drivers: sensor: Add driver for nRF temperature sensor accessed via nrfs

Add driver, together with the corresponding dts binding and node in
the nRF54H20 SoC definiton, for the nRF temperature sensor that cannot
be accessed directly but only through nRF Services (nrfs) layer.

Signed-off-by: Andrzej Głąbek <andrzej.glabek@nordicsemi.no>
This commit is contained in:
Andrzej Głąbek 2024-04-26 12:55:21 +02:00 committed by Fabio Baltieri
commit 41786a6477
9 changed files with 369 additions and 22 deletions

View file

@ -2,7 +2,7 @@
# SPDX-License-Identifier: Apache-2.0
# zephyr-keep-sorted-start
add_subdirectory(temp)
add_subdirectory_ifdef(CONFIG_NPM1300_CHARGER npm1300_charger)
add_subdirectory_ifdef(CONFIG_QDEC_NRFX qdec_nrfx)
add_subdirectory_ifdef(CONFIG_TEMP_NRF5 nrf5)
# zephyr-keep-sorted-stop

View file

@ -3,6 +3,6 @@
# zephyr-keep-sorted-start
source "drivers/sensor/nordic/npm1300_charger/Kconfig"
source "drivers/sensor/nordic/nrf5/Kconfig"
source "drivers/sensor/nordic/qdec_nrfx/Kconfig"
source "drivers/sensor/nordic/temp/Kconfig"
# zephyr-keep-sorted-stop

View file

@ -1,20 +0,0 @@
# nRF5 temperature sensor configuration options
# Copyright (c) 2016 ARM Ltd.
# SPDX-License-Identifier: Apache-2.0
config TEMP_NRF5_FORCE_ALT
bool
depends on SOC_COMPATIBLE_NRF
help
This option can be enabled to force an alternative implementation
of the temperature sensor driver.
config TEMP_NRF5
bool "nRF5 Temperature Sensor"
default y
depends on DT_HAS_NORDIC_NRF_TEMP_ENABLED
depends on HAS_HW_NRF_TEMP
depends on MULTITHREADING || TEMP_NRF5_FORCE_ALT
help
Enable driver for nRF5 temperature sensor.

View file

@ -3,4 +3,7 @@
if(CONFIG_TEMP_NRF5 AND NOT CONFIG_TEMP_NRF5_FORCE_ALT)
zephyr_library()
zephyr_library_sources(temp_nrf5.c)
elseif(CONFIG_TEMP_NRFS)
zephyr_library()
zephyr_library_sources(temp_nrfs.c)
endif()

View file

@ -0,0 +1,38 @@
# nRF temperature sensor configuration options
# Copyright (c) 2016 ARM Ltd.
# Copyright (c) 2024 Nordic Semiconductor ASA
# SPDX-License-Identifier: Apache-2.0
config TEMP_NRF5_FORCE_ALT
bool
depends on SOC_COMPATIBLE_NRF
help
This option can be enabled to force an alternative implementation
of the temperature sensor driver.
config TEMP_NRF5
bool "nRF5 Temperature Sensor"
default y
depends on DT_HAS_NORDIC_NRF_TEMP_ENABLED
depends on MULTITHREADING || TEMP_NRF5_FORCE_ALT
help
Enable driver for nRF5 temperature sensor.
config TEMP_NRFS
bool "nRF Temperature Sensor accessed via nrfs"
default y
depends on DT_HAS_NORDIC_NRF_TEMP_NRFS_ENABLED
depends on NRFS
help
Enable driver for nRF temperature sensor accessed through the nRF
Services (nrfs) layer.
if TEMP_NRFS
module = TEMP_NRFS
thread_priority = 10
thread_stack_size = 1024
source "drivers/sensor/Kconfig.trigger_template"
endif # TEMP_NRFS

View file

@ -0,0 +1,312 @@
/*
* Copyright (c) 2024 Nordic Semiconductor ASA
*
* SPDX-License-Identifier: Apache-2.0
*/
#define DT_DRV_COMPAT nordic_nrf_temp_nrfs
#include <zephyr/kernel.h>
#include <zephyr/drivers/sensor.h>
#include <zephyr/logging/log.h>
#include <zephyr/sys/__assert.h>
#include <nrfs_temp.h>
LOG_MODULE_REGISTER(temp_nrfs, CONFIG_SENSOR_LOG_LEVEL);
struct temp_nrfs_data {
struct k_sem measure_sem;
struct k_mutex mutex;
int32_t raw_temp;
#ifdef CONFIG_TEMP_NRFS_TRIGGER
struct sensor_trigger trigger;
sensor_trigger_handler_t handler;
const struct device *dev;
struct sensor_value sampling_freq;
struct sensor_value up_threshold;
struct sensor_value low_threshold;
#endif
#if defined(CONFIG_TEMP_NRFS_TRIGGER_OWN_THREAD)
K_KERNEL_STACK_MEMBER(thread_stack, CONFIG_TEMP_NRFS_THREAD_STACK_SIZE);
struct k_thread thread;
struct k_sem event_sem;
#elif defined(CONFIG_TEMP_NRFS_TRIGGER_GLOBAL_THREAD)
struct k_work work;
#endif
};
#ifdef CONFIG_TEMP_NRFS_TRIGGER
#define DEFAULT_SAMPLING_FREQ { 1, 0 }
#define DEFAULT_UP_THRESHOLD { 25, 0 }
#define DEFAULT_LOW_THRESHOLD { 0, 0 }
static void temp_nrfs_handle_event(const struct device *dev)
{
struct temp_nrfs_data *data = dev->data;
struct sensor_trigger trigger;
sensor_trigger_handler_t handler;
k_mutex_lock(&data->mutex, K_FOREVER);
trigger = data->trigger;
handler = data->handler;
k_mutex_unlock(&data->mutex);
if (handler) {
handler(dev, &trigger);
}
}
#if defined(CONFIG_TEMP_NRFS_TRIGGER_OWN_THREAD)
static void temp_nrfs_thread(void *p1, void *p2, void *p3)
{
ARG_UNUSED(p2);
ARG_UNUSED(p3);
struct temp_nrfs_data *data = p1;
while (1) {
k_sem_take(&data->event_sem, K_FOREVER);
temp_nrfs_handle_event(data->dev);
}
}
#elif defined(CONFIG_TEMP_NRFS_TRIGGER_GLOBAL_THREAD)
static void temp_nrfs_work_handler(struct k_work *work)
{
struct temp_nrfs_data *data =
CONTAINER_OF(work, struct temp_nrfs_data, work);
temp_nrfs_handle_event(data->dev);
}
#endif
static uint16_t to_measure_rate_ms(const struct sensor_value *freq_val)
{
uint32_t measure_rate_ms = (MSEC_PER_SEC * 1000) /
(uint32_t)sensor_value_to_milli(freq_val);
return (uint16_t)MIN(measure_rate_ms, UINT16_MAX);
}
static int32_t to_raw_temp(const struct sensor_value *temp_val)
{
int32_t temp_mul_100 = (int32_t)(sensor_value_to_milli(temp_val) / 10);
return nrfs_temp_to_raw(temp_mul_100);
}
static int api_sensor_trigger_set(const struct device *dev,
const struct sensor_trigger *trig,
sensor_trigger_handler_t handler)
{
struct temp_nrfs_data *data = dev->data;
nrfs_err_t err;
if (trig->chan != SENSOR_CHAN_ALL &&
trig->chan != SENSOR_CHAN_DIE_TEMP) {
return -ENOTSUP;
}
switch (trig->type) {
case SENSOR_TRIG_THRESHOLD:
k_mutex_lock(&data->mutex, K_FOREVER);
data->trigger = *trig;
data->handler = handler;
k_mutex_unlock(&data->mutex);
if (handler) {
err = nrfs_temp_subscribe(
to_measure_rate_ms(&data->sampling_freq),
to_raw_temp(&data->low_threshold),
to_raw_temp(&data->up_threshold),
data);
} else {
err = nrfs_temp_unsubscribe();
}
switch (err) {
case NRFS_SUCCESS:
break;
case NRFS_ERR_INVALID_STATE:
return -EAGAIN;
default:
return -EIO;
}
break;
default:
return -ENOTSUP;
}
return 0;
}
static int api_sensor_attr_set(const struct device *dev,
enum sensor_channel chan,
enum sensor_attribute attr,
const struct sensor_value *val)
{
struct temp_nrfs_data *data = dev->data;
if (chan != SENSOR_CHAN_ALL && chan != SENSOR_CHAN_DIE_TEMP) {
return -ENOTSUP;
}
switch (attr) {
case SENSOR_ATTR_SAMPLING_FREQUENCY:
if (sensor_value_to_milli(val) <= 0) {
return -EINVAL;
}
data->sampling_freq = *val;
break;
case SENSOR_ATTR_UPPER_THRESH:
data->up_threshold = *val;
break;
case SENSOR_ATTR_LOWER_THRESH:
data->low_threshold = *val;
break;
default:
return -ENOTSUP;
}
return 0;
}
#endif /* CONFIG_TEMP_NRFS_TRIGGER */
static void sensor_handler(nrfs_temp_evt_t const *p_evt, void *context)
{
ARG_UNUSED(context);
struct temp_nrfs_data *data = context;
switch (p_evt->type) {
case NRFS_TEMP_EVT_MEASURE_DONE:
data->raw_temp = p_evt->raw_temp;
k_sem_give(&data->measure_sem);
break;
#ifdef CONFIG_TEMP_NRFS_TRIGGER
case NRFS_TEMP_EVT_CHANGE:
data->raw_temp = p_evt->raw_temp;
#if defined(CONFIG_TEMP_NRFS_TRIGGER_OWN_THREAD)
k_sem_give(&data->event_sem);
#elif defined(CONFIG_TEMP_NRFS_TRIGGER_GLOBAL_THREAD)
k_work_submit(&data->work);
#endif
break;
#endif /* CONFIG_TEMP_NRFS_TRIGGER */
default:
LOG_DBG("Temperature handler - unsupported event: 0x%x",
p_evt->type);
break;
}
}
static int api_sample_fetch(const struct device *dev,
enum sensor_channel chan)
{
struct temp_nrfs_data *data = dev->data;
int nrfs_rc;
int rc = 0;
if (chan != SENSOR_CHAN_ALL && chan != SENSOR_CHAN_DIE_TEMP) {
return -ENOTSUP;
}
k_mutex_lock(&data->mutex, K_FOREVER);
nrfs_rc = nrfs_temp_measure_request(data);
switch (nrfs_rc) {
case NRFS_SUCCESS:
k_sem_take(&data->measure_sem, K_FOREVER);
LOG_DBG("Temperature sample: %d", data->raw_temp);
break;
case NRFS_ERR_INVALID_STATE:
LOG_DBG("Backend is not ready, try again.");
rc = -EAGAIN;
break;
default:
LOG_DBG("Measure request failed: %d", nrfs_rc);
rc = -EIO;
break;
}
k_mutex_unlock(&data->mutex);
return rc;
}
static int api_channel_get(const struct device *dev,
enum sensor_channel chan,
struct sensor_value *val)
{
struct temp_nrfs_data *data = dev->data;
if (chan != SENSOR_CHAN_DIE_TEMP) {
return -ENOTSUP;
}
int32_t uval = nrfs_temp_from_raw(data->raw_temp);
val->val1 = uval / 100;
val->val2 = (abs(uval) % 100) * 10000;
LOG_DBG("Temperature: %d.%02u[C]", uval / 100, abs(uval) % 100);
return 0;
}
static int temp_nrfs_init(const struct device *dev)
{
int rc;
rc = nrfs_temp_init(sensor_handler);
if (rc < 0) {
return rc;
}
#if defined(CONFIG_TEMP_NRFS_TRIGGER_OWN_THREAD)
struct temp_nrfs_data *data = dev->data;
k_thread_create(&data->thread, data->thread_stack,
CONFIG_TEMP_NRFS_THREAD_STACK_SIZE,
temp_nrfs_thread,
data, NULL, NULL,
K_PRIO_COOP(CONFIG_TEMP_NRFS_THREAD_PRIORITY),
0, K_NO_WAIT);
k_thread_name_set(&data->thread, dev->name);
#endif
return 0;
}
static const struct sensor_driver_api temp_nrfs_drv_api = {
#ifdef CONFIG_TEMP_NRFS_TRIGGER
.attr_set = api_sensor_attr_set,
.trigger_set = api_sensor_trigger_set,
#endif
.sample_fetch = api_sample_fetch,
.channel_get = api_channel_get
};
static struct temp_nrfs_data temp_nrfs_drv_data = {
.mutex = Z_MUTEX_INITIALIZER(temp_nrfs_drv_data.mutex),
.measure_sem = Z_SEM_INITIALIZER(temp_nrfs_drv_data.measure_sem, 0, 1),
#ifdef CONFIG_TEMP_NRFS_TRIGGER
.dev = DEVICE_DT_INST_GET(0),
.sampling_freq = DEFAULT_SAMPLING_FREQ,
.up_threshold = DEFAULT_UP_THRESHOLD,
.low_threshold = DEFAULT_LOW_THRESHOLD,
#endif
#if defined(CONFIG_TEMP_NRFS_TRIGGER_OWN_THREAD)
.event_sem = Z_SEM_INITIALIZER(temp_nrfs_drv_data.event_sem, 0, 1),
#elif defined(CONFIG_TEMP_NRFS_TRIGGER_GLOBAL_THREAD)
.work = Z_WORK_INITIALIZER(temp_nrfs_work_handler),
#endif
};
DEVICE_DT_INST_DEFINE(0, temp_nrfs_init, NULL,
&temp_nrfs_drv_data, NULL,
POST_KERNEL, CONFIG_SENSOR_INIT_PRIORITY,
&temp_nrfs_drv_api);

View file

@ -0,0 +1,9 @@
# Copyright (c) 2024 Nordic Semiconductor ASA
# SPDX-License-Identifier: Apache-2.0
description: |
Nordic nRF family temperature sensor accessed via nRF Services (nrfs)
compatible: "nordic,nrf-temp-nrfs"
include: base.yaml

View file

@ -1076,4 +1076,9 @@
#address-cells = <1>;
};
};
temp_nrfs: temp {
compatible = "nordic,nrf-temp-nrfs";
status = "disabled";
};
};