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