1 /*
2  * Copyright (c) 2019 Nordic Semiconductor ASA
3  *
4  * SPDX-License-Identifier: Apache-2.0
5  */
6 #include <zephyr/drivers/sensor.h>
7 #include <zephyr/drivers/clock_control.h>
8 #include "nrf_clock_calibration.h"
9 #include <zephyr/drivers/clock_control/nrf_clock_control.h>
10 #include <nrfx_clock.h>
11 #include <zephyr/logging/log.h>
12 #include <stdlib.h>
13 
14 LOG_MODULE_DECLARE(clock_control, CONFIG_CLOCK_CONTROL_LOG_LEVEL);
15 
16 /**
17  * Terms:
18  * - calibration - overall process of LFRC clock calibration which is performed
19  *   periodically, calibration may include temperature monitoring, hf XTAL
20  *   starting and stopping.
21  * - cycle - all calibration phases (waiting, temperature monitoring,
22  *   calibration).
23  * - process - calibration process which may consists of hf XTAL clock
24  *   requesting, performing hw calibration and releasing hf clock.
25  * - hw_cal - calibration action performed by the hardware.
26  *
27  * Those terms are later on used in function names.
28  *
29  * In order to ensure that low frequency clock is not released when calibration
30  * is ongoing, it is requested by the calibration process and released when
31  * calibration is done.
32  */
33 
34 static atomic_t cal_process_in_progress;
35 static uint8_t calib_skip_cnt; /* Counting down skipped calibrations. */
36 static volatile int total_cnt; /* Total number of calibrations. */
37 static volatile int total_skips_cnt; /* Total number of skipped calibrations. */
38 
39 
40 static void cal_hf_callback(struct onoff_manager *mgr,
41 			    struct onoff_client *cli,
42 			    uint32_t state, int res);
43 static void cal_lf_callback(struct onoff_manager *mgr,
44 			    struct onoff_client *cli,
45 			    uint32_t state, int res);
46 
47 static struct onoff_client client;
48 static struct onoff_manager *mgrs;
49 
50 /* Temperature sensor is only needed if
51  * CONFIG_CLOCK_CONTROL_NRF_CALIBRATION_MAX_SKIP > 0, since a value of 0
52  * indicates performing calibration periodically regardless of temperature
53  * change.
54  */
55 #define USE_TEMP_SENSOR							\
56 	(CONFIG_CLOCK_CONTROL_NRF_CALIBRATION_MAX_SKIP > 0)
57 
58 #if USE_TEMP_SENSOR
59 static const struct device *const temp_sensor =
60 	DEVICE_DT_GET_OR_NULL(DT_INST(0, nordic_nrf_temp));
61 
62 static void measure_temperature(struct k_work *work);
63 static K_WORK_DEFINE(temp_measure_work, measure_temperature);
64 static int16_t prev_temperature; /* Previous temperature measurement. */
65 #endif /* USE_TEMP_SENSOR */
66 
67 static void timeout_handler(struct k_timer *timer);
68 static K_TIMER_DEFINE(backoff_timer, timeout_handler, NULL);
69 
clk_request(struct onoff_manager * mgr,struct onoff_client * cli,onoff_client_callback callback)70 static void clk_request(struct onoff_manager *mgr, struct onoff_client *cli,
71 			onoff_client_callback callback)
72 {
73 	int err;
74 
75 	sys_notify_init_callback(&cli->notify, callback);
76 	err = onoff_request(mgr, cli);
77 	__ASSERT_NO_MSG(err >= 0);
78 }
79 
clk_release(struct onoff_manager * mgr)80 static void clk_release(struct onoff_manager *mgr)
81 {
82 	int err;
83 
84 	err = onoff_release(mgr);
85 	__ASSERT_NO_MSG(err >= 0);
86 }
87 
hf_request(void)88 static void hf_request(void)
89 {
90 	clk_request(&mgrs[CLOCK_CONTROL_NRF_TYPE_HFCLK], &client, cal_hf_callback);
91 }
92 
lf_request(void)93 static void lf_request(void)
94 {
95 	clk_request(&mgrs[CLOCK_CONTROL_NRF_TYPE_LFCLK], &client, cal_lf_callback);
96 }
97 
hf_release(void)98 static void hf_release(void)
99 {
100 	clk_release(&mgrs[CLOCK_CONTROL_NRF_TYPE_HFCLK]);
101 }
102 
lf_release(void)103 static void lf_release(void)
104 {
105 	clk_release(&mgrs[CLOCK_CONTROL_NRF_TYPE_LFCLK]);
106 }
107 
cal_lf_callback(struct onoff_manager * mgr,struct onoff_client * cli,uint32_t state,int res)108 static void cal_lf_callback(struct onoff_manager *mgr,
109 			    struct onoff_client *cli,
110 			    uint32_t state, int res)
111 {
112 	hf_request();
113 }
114 
115 /* Start actual HW calibration assuming that HFCLK XTAL is on. */
start_hw_cal(void)116 static void start_hw_cal(void)
117 {
118 	nrfx_clock_calibration_start();
119 	calib_skip_cnt = CONFIG_CLOCK_CONTROL_NRF_CALIBRATION_MAX_SKIP;
120 }
121 
122 /* Start cycle by starting backoff timer and releasing HFCLK XTAL. */
start_cycle(void)123 static void start_cycle(void)
124 {
125 	k_timer_start(&backoff_timer,
126 		      K_MSEC(CONFIG_CLOCK_CONTROL_NRF_CALIBRATION_PERIOD),
127 		      K_NO_WAIT);
128 	hf_release();
129 
130 	if (!IS_ENABLED(CONFIG_CLOCK_CONTROL_NRF_CALIBRATION_LF_ALWAYS_ON)) {
131 		lf_release();
132 	}
133 
134 	cal_process_in_progress = 0;
135 }
136 
start_cal_process(void)137 static void start_cal_process(void)
138 {
139 	if (atomic_cas(&cal_process_in_progress, 0, 1) == false) {
140 		return;
141 	}
142 
143 	if (IS_ENABLED(CONFIG_CLOCK_CONTROL_NRF_CALIBRATION_LF_ALWAYS_ON)) {
144 		hf_request();
145 	} else {
146 		/* LF clock is probably running but it is requested to ensure
147 		 * that it is not released while calibration process in ongoing.
148 		 * If system releases the clock during calibration process it
149 		 * will be released at the end of calibration process and
150 		 * stopped in consequence.
151 		 */
152 		lf_request();
153 	}
154 }
155 
timeout_handler(struct k_timer * timer)156 static void timeout_handler(struct k_timer *timer)
157 {
158 	start_cal_process();
159 }
160 
161 /* Called when HFCLK XTAL is on. Schedules temperature measurement or triggers
162  * calibration.
163  */
cal_hf_callback(struct onoff_manager * mgr,struct onoff_client * cli,uint32_t state,int res)164 static void cal_hf_callback(struct onoff_manager *mgr,
165 			    struct onoff_client *cli,
166 			    uint32_t state, int res)
167 {
168 #if USE_TEMP_SENSOR
169 	if (!device_is_ready(temp_sensor)) {
170 		start_hw_cal();
171 	} else {
172 		k_work_submit(&temp_measure_work);
173 	}
174 #else
175 	start_hw_cal();
176 #endif /* USE_TEMP_SENSOR */
177 }
178 
179 #if USE_TEMP_SENSOR
180 /* Convert sensor value to 0.25'C units. */
sensor_value_to_temp_unit(struct sensor_value * val)181 static inline int16_t sensor_value_to_temp_unit(struct sensor_value *val)
182 {
183 	return (int16_t)(4 * val->val1 + val->val2 / 250000);
184 }
185 
186 /* Function reads from temperature sensor and converts to 0.25'C units. */
get_temperature(int16_t * tvp)187 static int get_temperature(int16_t *tvp)
188 {
189 	struct sensor_value sensor_val;
190 	int rc = sensor_sample_fetch(temp_sensor);
191 
192 	if (rc == 0) {
193 		rc = sensor_channel_get(temp_sensor, SENSOR_CHAN_DIE_TEMP,
194 					&sensor_val);
195 	}
196 	if (rc == 0) {
197 		*tvp = sensor_value_to_temp_unit(&sensor_val);
198 	}
199 	return rc;
200 }
201 
202 /* Function determines if calibration should be performed based on temperature
203  * measurement. Function is called from system work queue context. It is
204  * reading temperature from TEMP sensor and compares with last measurement.
205  */
measure_temperature(struct k_work * work)206 static void measure_temperature(struct k_work *work)
207 {
208 	int16_t temperature = 0;
209 	int16_t diff = 0;
210 	bool started = false;
211 	int rc;
212 
213 	rc = get_temperature(&temperature);
214 
215 	if (rc != 0) {
216 		/* Temperature read failed, force calibration. */
217 		calib_skip_cnt = 0;
218 	} else {
219 		diff = abs(temperature - prev_temperature);
220 	}
221 
222 	if ((calib_skip_cnt == 0) ||
223 		(diff >= CONFIG_CLOCK_CONTROL_NRF_CALIBRATION_TEMP_DIFF)) {
224 		prev_temperature = temperature;
225 		started = true;
226 		start_hw_cal();
227 	} else {
228 		calib_skip_cnt--;
229 		total_skips_cnt++;
230 		start_cycle();
231 	}
232 
233 	LOG_DBG("Calibration %s. Temperature diff: %d (in 0.25'C units).",
234 			started ? "started" : "skipped", diff);
235 }
236 #endif /* USE_TEMP_SENSOR */
237 
z_nrf_clock_calibration_init(struct onoff_manager * onoff_mgrs)238 void z_nrf_clock_calibration_init(struct onoff_manager *onoff_mgrs)
239 {
240 	mgrs = onoff_mgrs;
241 	total_cnt = 0;
242 	total_skips_cnt = 0;
243 }
244 
start_unconditional_cal_process(void)245 static void start_unconditional_cal_process(void)
246 {
247 	calib_skip_cnt = 0;
248 	start_cal_process();
249 }
250 
z_nrf_clock_calibration_force_start(void)251 void z_nrf_clock_calibration_force_start(void)
252 {
253 	/* if it's already in progress that is good enough. */
254 	if (cal_process_in_progress) {
255 		return;
256 	}
257 
258 	start_unconditional_cal_process();
259 }
260 
z_nrf_clock_calibration_lfclk_started(void)261 void z_nrf_clock_calibration_lfclk_started(void)
262 {
263 	start_unconditional_cal_process();
264 }
265 
z_nrf_clock_calibration_lfclk_stopped(void)266 void z_nrf_clock_calibration_lfclk_stopped(void)
267 {
268 	k_timer_stop(&backoff_timer);
269 	LOG_DBG("Calibration stopped");
270 }
271 
z_nrf_clock_calibration_done_handler(void)272 void z_nrf_clock_calibration_done_handler(void)
273 {
274 	total_cnt++;
275 	LOG_DBG("Calibration done.");
276 
277 	start_cycle();
278 }
279 
z_nrf_clock_calibration_count(void)280 int z_nrf_clock_calibration_count(void)
281 {
282 	if (!IS_ENABLED(CONFIG_CLOCK_CONTROL_NRF_CALIBRATION_DEBUG)) {
283 		return -1;
284 	}
285 
286 	return total_cnt;
287 }
288 
z_nrf_clock_calibration_skips_count(void)289 int z_nrf_clock_calibration_skips_count(void)
290 {
291 	if (!IS_ENABLED(CONFIG_CLOCK_CONTROL_NRF_CALIBRATION_DEBUG)) {
292 		return -1;
293 	}
294 
295 	return total_skips_cnt;
296 }
297 
z_nrf_clock_calibration_is_in_progress(void)298 bool z_nrf_clock_calibration_is_in_progress(void)
299 {
300 	return cal_process_in_progress ? true : false;
301 }
302