/* * Copyright (c) 2024 Nordic Semiconductor ASA * * SPDX-License-Identifier: Apache-2.0 */ #define DT_DRV_COMPAT nordic_nrf_temp_nrfs #include #include #include #include #include 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 DEVICE_API(sensor, 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);