1 /*
2  * Copyright (c) 2016 ARM Ltd.
3  * Copyright (c) 2019 Nordic Semiconductor ASA
4  *
5  * SPDX-License-Identifier: Apache-2.0
6  */
7 
8 #define DT_DRV_COMPAT nordic_nrf_temp
9 
10 #include <zephyr/device.h>
11 #include <zephyr/drivers/sensor.h>
12 #include <zephyr/drivers/clock_control.h>
13 #include <zephyr/drivers/clock_control/nrf_clock_control.h>
14 #include <zephyr/logging/log.h>
15 #include <hal/nrf_temp.h>
16 #include <zephyr/irq.h>
17 
18 LOG_MODULE_REGISTER(temp_nrf5, CONFIG_SENSOR_LOG_LEVEL);
19 
20 
21 /* The nRF5 temperature device returns measurements in 0.25C
22  * increments.  Scale to mDegrees C.
23  */
24 #define TEMP_NRF5_TEMP_SCALE (1000000 / 4)
25 
26 struct temp_nrf5_data {
27 	struct k_sem device_sync_sem;
28 	struct k_mutex mutex;
29 	int32_t sample;
30 	struct onoff_manager *clk_mgr;
31 };
32 
hfclk_on_callback(struct onoff_manager * mgr,struct onoff_client * cli,uint32_t state,int res)33 static void hfclk_on_callback(struct onoff_manager *mgr,
34 			      struct onoff_client *cli,
35 			      uint32_t state,
36 			      int res)
37 {
38 	nrf_temp_task_trigger(NRF_TEMP, NRF_TEMP_TASK_START);
39 }
40 
temp_nrf5_sample_fetch(const struct device * dev,enum sensor_channel chan)41 static int temp_nrf5_sample_fetch(const struct device *dev,
42 				  enum sensor_channel chan)
43 {
44 	struct temp_nrf5_data *data = dev->data;
45 	struct onoff_client cli;
46 	int r;
47 
48 	/* Error if before sensor initialized */
49 	if (data->clk_mgr == NULL) {
50 		return -EAGAIN;
51 	}
52 
53 	if (chan != SENSOR_CHAN_ALL && chan != SENSOR_CHAN_DIE_TEMP) {
54 		return -ENOTSUP;
55 	}
56 
57 	k_mutex_lock(&data->mutex, K_FOREVER);
58 
59 	sys_notify_init_callback(&cli.notify, hfclk_on_callback);
60 	r = onoff_request(data->clk_mgr, &cli);
61 	__ASSERT_NO_MSG(r >= 0);
62 
63 	k_sem_take(&data->device_sync_sem, K_FOREVER);
64 
65 	r = onoff_release(data->clk_mgr);
66 	__ASSERT_NO_MSG(r >= 0);
67 
68 	data->sample = nrf_temp_result_get(NRF_TEMP);
69 	LOG_DBG("sample: %d", data->sample);
70 	nrf_temp_task_trigger(NRF_TEMP, NRF_TEMP_TASK_STOP);
71 
72 	k_mutex_unlock(&data->mutex);
73 
74 	return 0;
75 }
76 
temp_nrf5_channel_get(const struct device * dev,enum sensor_channel chan,struct sensor_value * val)77 static int temp_nrf5_channel_get(const struct device *dev,
78 				 enum sensor_channel chan,
79 				 struct sensor_value *val)
80 {
81 	struct temp_nrf5_data *data = dev->data;
82 	int32_t uval;
83 
84 
85 	if (chan != SENSOR_CHAN_DIE_TEMP) {
86 		return -ENOTSUP;
87 	}
88 
89 	uval = data->sample * TEMP_NRF5_TEMP_SCALE;
90 	val->val1 = uval / 1000000;
91 	val->val2 = uval % 1000000;
92 
93 	LOG_DBG("Temperature:%d,%d", val->val1, val->val2);
94 
95 	return 0;
96 }
97 
temp_nrf5_isr(const void * arg)98 static void temp_nrf5_isr(const void *arg)
99 {
100 	const struct device *dev = (const struct device *)arg;
101 	struct temp_nrf5_data *data = dev->data;
102 
103 	nrf_temp_event_clear(NRF_TEMP, NRF_TEMP_EVENT_DATARDY);
104 	k_sem_give(&data->device_sync_sem);
105 }
106 
107 static DEVICE_API(sensor, temp_nrf5_driver_api) = {
108 	.sample_fetch = temp_nrf5_sample_fetch,
109 	.channel_get = temp_nrf5_channel_get,
110 };
111 
temp_nrf5_init(const struct device * dev)112 static int temp_nrf5_init(const struct device *dev)
113 {
114 	struct temp_nrf5_data *data = dev->data;
115 
116 	/* A null clk_mgr indicates sensor has not been initialized */
117 	data->clk_mgr =
118 		z_nrf_clock_control_get_onoff(CLOCK_CONTROL_NRF_SUBSYS_HF);
119 	__ASSERT_NO_MSG(data->clk_mgr);
120 
121 	k_sem_init(&data->device_sync_sem, 0, K_SEM_MAX_LIMIT);
122 	k_mutex_init(&data->mutex);
123 
124 	IRQ_CONNECT(
125 		DT_INST_IRQN(0),
126 		DT_INST_IRQ(0, priority),
127 		temp_nrf5_isr,
128 		DEVICE_DT_INST_GET(0),
129 		0);
130 	irq_enable(DT_INST_IRQN(0));
131 
132 	nrf_temp_int_enable(NRF_TEMP, NRF_TEMP_INT_DATARDY_MASK);
133 
134 	return 0;
135 }
136 
137 #define NRF_TEMP_DEFINE(inst)								\
138 	static struct temp_nrf5_data temp_nrf5_data_##inst;				\
139 											\
140 	SENSOR_DEVICE_DT_INST_DEFINE(inst, temp_nrf5_init, NULL,			\
141 			      &temp_nrf5_data_##inst, NULL, POST_KERNEL,		\
142 			      CONFIG_SENSOR_INIT_PRIORITY, &temp_nrf5_driver_api);	\
143 
144 DT_INST_FOREACH_STATUS_OKAY(NRF_TEMP_DEFINE)
145