1 /*
2 * Copyright (c) 2024 Nordic Semiconductor ASA
3 * SPDX-License-Identifier: Apache-2.0
4 */
5
6 #define DT_DRV_COMPAT nordic_nrf_lfclk
7
8 #include "clock_control_nrf2_common.h"
9 #include <zephyr/devicetree.h>
10 #include <zephyr/drivers/clock_control/nrf_clock_control.h>
11 #include <hal/nrf_bicr.h>
12 #include <nrfs_clock.h>
13
14 #include <zephyr/logging/log.h>
15 LOG_MODULE_DECLARE(clock_control_nrf2, CONFIG_CLOCK_CONTROL_LOG_LEVEL);
16
17 BUILD_ASSERT(DT_NUM_INST_STATUS_OKAY(DT_DRV_COMPAT) == 1,
18 "multiple instances not supported");
19
20 #define LFCLK_HFXO_NODE DT_INST_PHANDLE_BY_NAME(0, clocks, hfxo)
21
22 #define LFCLK_LFRC_ACCURACY DT_INST_PROP(0, lfrc_accuracy_ppm)
23 #define LFCLK_HFXO_ACCURACY DT_PROP(LFCLK_HFXO_NODE, accuracy_ppm)
24 #define LFCLK_LFLPRC_STARTUP_TIME_US DT_INST_PROP(0, lflprc_startup_time_us)
25 #define LFCLK_LFRC_STARTUP_TIME_US DT_INST_PROP(0, lfrc_startup_time_us)
26
27 #define LFCLK_MAX_OPTS 4
28 #define LFCLK_DEF_OPTS 2
29
30 #define NRFS_CLOCK_TIMEOUT K_MSEC(CONFIG_CLOCK_CONTROL_NRF_LFCLK_CLOCK_TIMEOUT_MS)
31
32 #define BICR (NRF_BICR_Type *)DT_REG_ADDR(DT_NODELABEL(bicr))
33
34 /* Clock options sorted from highest to lowest power consumption.
35 * - Clock synthesized from a high frequency clock
36 * - Internal RC oscillator
37 * - External clock. These are inserted into the list at driver initialization.
38 * Set to one of the following:
39 * - XTAL. Low or High precision
40 * - External sine or square wave
41 */
42 static struct clock_options {
43 uint16_t accuracy : 15;
44 uint16_t precision : 1;
45 nrfs_clock_src_t src;
46 } clock_options[LFCLK_MAX_OPTS] = {
47 {
48 /* NRFS will request FLL16M use HFXO in bypass mode if SYNTH src is used */
49 .accuracy = LFCLK_HFXO_ACCURACY,
50 .precision = 1,
51 .src = NRFS_CLOCK_SRC_LFCLK_SYNTH,
52 },
53 {
54 .accuracy = LFCLK_LFRC_ACCURACY,
55 .precision = 0,
56 .src = NRFS_CLOCK_SRC_LFCLK_LFRC,
57 },
58 /* Remaining options are populated on lfclk_init */
59 };
60
61 struct lfclk_dev_data {
62 STRUCT_CLOCK_CONFIG(lfclk, ARRAY_SIZE(clock_options)) clk_cfg;
63 struct k_timer timer;
64 uint16_t max_accuracy;
65 uint8_t clock_options_cnt;
66 uint32_t hfxo_startup_time_us;
67 uint32_t lfxo_startup_time_us;
68 };
69
70 struct lfclk_dev_config {
71 uint32_t fixed_frequency;
72 };
73
lfosc_get_accuracy(uint16_t * accuracy)74 static int lfosc_get_accuracy(uint16_t *accuracy)
75 {
76 switch (nrf_bicr_lfosc_accuracy_get(BICR)) {
77 case NRF_BICR_LFOSC_ACCURACY_500PPM:
78 *accuracy = 500U;
79 break;
80 case NRF_BICR_LFOSC_ACCURACY_250PPM:
81 *accuracy = 250U;
82 break;
83 case NRF_BICR_LFOSC_ACCURACY_150PPM:
84 *accuracy = 150U;
85 break;
86 case NRF_BICR_LFOSC_ACCURACY_100PPM:
87 *accuracy = 100U;
88 break;
89 case NRF_BICR_LFOSC_ACCURACY_75PPM:
90 *accuracy = 75U;
91 break;
92 case NRF_BICR_LFOSC_ACCURACY_50PPM:
93 *accuracy = 50U;
94 break;
95 case NRF_BICR_LFOSC_ACCURACY_30PPM:
96 *accuracy = 30U;
97 break;
98 case NRF_BICR_LFOSC_ACCURACY_20PPM:
99 *accuracy = 20U;
100 break;
101 default:
102 return -EINVAL;
103 }
104
105 return 0;
106 }
107
clock_evt_handler(nrfs_clock_evt_t const * p_evt,void * context)108 static void clock_evt_handler(nrfs_clock_evt_t const *p_evt, void *context)
109 {
110 struct lfclk_dev_data *dev_data = context;
111 int status = 0;
112
113 k_timer_stop(&dev_data->timer);
114
115 if (p_evt->type == NRFS_CLOCK_EVT_REJECT) {
116 status = -ENXIO;
117 }
118
119 clock_config_update_end(&dev_data->clk_cfg, status);
120 }
121
lfclk_update_timeout_handler(struct k_timer * timer)122 static void lfclk_update_timeout_handler(struct k_timer *timer)
123 {
124 struct lfclk_dev_data *dev_data =
125 CONTAINER_OF(timer, struct lfclk_dev_data, timer);
126
127 clock_config_update_end(&dev_data->clk_cfg, -ETIMEDOUT);
128 }
129
lfclk_work_handler(struct k_work * work)130 static void lfclk_work_handler(struct k_work *work)
131 {
132 struct lfclk_dev_data *dev_data =
133 CONTAINER_OF(work, struct lfclk_dev_data, clk_cfg.work);
134 uint8_t to_activate_idx;
135 nrfs_err_t err;
136
137 to_activate_idx = clock_config_update_begin(work);
138
139 err = nrfs_clock_lfclk_src_set(clock_options[to_activate_idx].src,
140 dev_data);
141 if (err != NRFS_SUCCESS) {
142 clock_config_update_end(&dev_data->clk_cfg, -EIO);
143 } else {
144 k_timer_start(&dev_data->timer, NRFS_CLOCK_TIMEOUT, K_NO_WAIT);
145 }
146 }
147
lfclk_resolve_spec_to_idx(const struct device * dev,const struct nrf_clock_spec * req_spec)148 static int lfclk_resolve_spec_to_idx(const struct device *dev,
149 const struct nrf_clock_spec *req_spec)
150 {
151 struct lfclk_dev_data *dev_data = dev->data;
152 const struct lfclk_dev_config *dev_config = dev->config;
153 uint16_t req_accuracy;
154
155 if (req_spec->frequency > dev_config->fixed_frequency) {
156 LOG_ERR("invalid frequency");
157 return -EINVAL;
158 }
159
160 req_accuracy = req_spec->accuracy == NRF_CLOCK_CONTROL_ACCURACY_MAX
161 ? dev_data->max_accuracy
162 : req_spec->accuracy;
163
164 for (int i = dev_data->clock_options_cnt - 1; i >= 0; --i) {
165 /* Iterate to a more power hungry and accurate clock source
166 * If the requested accuracy is higher (lower ppm) than what
167 * the clock source can provide.
168 *
169 * In case of an accuracy of 0 (don't care), do not check accuracy.
170 */
171 if ((req_accuracy != 0 && req_accuracy < clock_options[i].accuracy) ||
172 (req_spec->precision > clock_options[i].precision)) {
173 continue;
174 }
175
176 return i;
177 }
178
179 LOG_ERR("invalid accuracy or precision");
180 return -EINVAL;
181 }
182
lfclk_get_spec_by_idx(const struct device * dev,uint8_t idx,struct nrf_clock_spec * spec)183 static void lfclk_get_spec_by_idx(const struct device *dev,
184 uint8_t idx,
185 struct nrf_clock_spec *spec)
186 {
187 const struct lfclk_dev_config *dev_config = dev->config;
188
189 spec->frequency = dev_config->fixed_frequency;
190 spec->accuracy = clock_options[idx].accuracy;
191 spec->precision = clock_options[idx].precision;
192 }
193
lfclk_get_mgr_by_idx(const struct device * dev,uint8_t idx)194 static struct onoff_manager *lfclk_get_mgr_by_idx(const struct device *dev, uint8_t idx)
195 {
196 struct lfclk_dev_data *dev_data = dev->data;
197
198 return &dev_data->clk_cfg.onoff[idx].mgr;
199 }
200
lfclk_get_startup_time_by_idx(const struct device * dev,uint8_t idx,uint32_t * startup_time_us)201 static int lfclk_get_startup_time_by_idx(const struct device *dev,
202 uint8_t idx,
203 uint32_t *startup_time_us)
204 {
205 struct lfclk_dev_data *dev_data = dev->data;
206 nrfs_clock_src_t src = clock_options[idx].src;
207
208 switch (src) {
209 case NRFS_CLOCK_SRC_LFCLK_LFLPRC:
210 *startup_time_us = LFCLK_LFLPRC_STARTUP_TIME_US;
211 return 0;
212
213 case NRFS_CLOCK_SRC_LFCLK_LFRC:
214 *startup_time_us = LFCLK_LFRC_STARTUP_TIME_US;
215 return 0;
216
217 case NRFS_CLOCK_SRC_LFCLK_XO_PIXO:
218 case NRFS_CLOCK_SRC_LFCLK_XO_PIERCE:
219 case NRFS_CLOCK_SRC_LFCLK_XO_EXT_SINE:
220 case NRFS_CLOCK_SRC_LFCLK_XO_EXT_SQUARE:
221 case NRFS_CLOCK_SRC_LFCLK_XO_PIERCE_HP:
222 case NRFS_CLOCK_SRC_LFCLK_XO_EXT_SINE_HP:
223 *startup_time_us = dev_data->lfxo_startup_time_us;
224 return 0;
225
226 case NRFS_CLOCK_SRC_LFCLK_SYNTH:
227 *startup_time_us = dev_data->hfxo_startup_time_us;
228 return 0;
229
230 default:
231 break;
232 }
233
234 return -EINVAL;
235 }
236
lfclk_find_mgr_by_spec(const struct device * dev,const struct nrf_clock_spec * spec)237 static struct onoff_manager *lfclk_find_mgr_by_spec(const struct device *dev,
238 const struct nrf_clock_spec *spec)
239 {
240 int idx;
241
242 if (!spec) {
243 return lfclk_get_mgr_by_idx(dev, 0);
244 }
245
246 idx = lfclk_resolve_spec_to_idx(dev, spec);
247 return idx < 0 ? NULL : lfclk_get_mgr_by_idx(dev, idx);
248 }
249
api_request_lfclk(const struct device * dev,const struct nrf_clock_spec * spec,struct onoff_client * cli)250 static int api_request_lfclk(const struct device *dev,
251 const struct nrf_clock_spec *spec,
252 struct onoff_client *cli)
253 {
254 struct onoff_manager *mgr = lfclk_find_mgr_by_spec(dev, spec);
255
256 if (mgr) {
257 return clock_config_request(mgr, cli);
258 }
259
260 return -EINVAL;
261 }
262
api_release_lfclk(const struct device * dev,const struct nrf_clock_spec * spec)263 static int api_release_lfclk(const struct device *dev,
264 const struct nrf_clock_spec *spec)
265 {
266 struct onoff_manager *mgr = lfclk_find_mgr_by_spec(dev, spec);
267
268 if (mgr) {
269 return onoff_release(mgr);
270 }
271
272 return -EINVAL;
273 }
274
api_cancel_or_release_lfclk(const struct device * dev,const struct nrf_clock_spec * spec,struct onoff_client * cli)275 static int api_cancel_or_release_lfclk(const struct device *dev,
276 const struct nrf_clock_spec *spec,
277 struct onoff_client *cli)
278 {
279 struct onoff_manager *mgr = lfclk_find_mgr_by_spec(dev, spec);
280
281 if (mgr) {
282 return onoff_cancel_or_release(mgr, cli);
283 }
284
285 return -EINVAL;
286 }
287
288
api_resolve(const struct device * dev,const struct nrf_clock_spec * req_spec,struct nrf_clock_spec * res_spec)289 static int api_resolve(const struct device *dev,
290 const struct nrf_clock_spec *req_spec,
291 struct nrf_clock_spec *res_spec)
292 {
293 int idx;
294
295 idx = lfclk_resolve_spec_to_idx(dev, req_spec);
296 if (idx < 0) {
297 return -EINVAL;
298 }
299
300 lfclk_get_spec_by_idx(dev, idx, res_spec);
301 return 0;
302 }
303
api_get_startup_time(const struct device * dev,const struct nrf_clock_spec * spec,uint32_t * startup_time_us)304 static int api_get_startup_time(const struct device *dev,
305 const struct nrf_clock_spec *spec,
306 uint32_t *startup_time_us)
307 {
308 int idx;
309
310 idx = lfclk_resolve_spec_to_idx(dev, spec);
311 if (idx < 0) {
312 return -EINVAL;
313 }
314
315 return lfclk_get_startup_time_by_idx(dev, idx, startup_time_us);
316 }
317
api_get_rate_lfclk(const struct device * dev,clock_control_subsys_t sys,uint32_t * rate)318 static int api_get_rate_lfclk(const struct device *dev,
319 clock_control_subsys_t sys,
320 uint32_t *rate)
321 {
322 ARG_UNUSED(sys);
323
324 const struct lfclk_dev_config *dev_config = dev->config;
325
326 *rate = dev_config->fixed_frequency;
327
328 return 0;
329 }
330
lfclk_init(const struct device * dev)331 static int lfclk_init(const struct device *dev)
332 {
333 struct lfclk_dev_data *dev_data = dev->data;
334 nrf_bicr_lfosc_mode_t lfosc_mode;
335 nrfs_err_t res;
336
337 res = nrfs_clock_init(clock_evt_handler);
338 if (res != NRFS_SUCCESS) {
339 return -EIO;
340 }
341
342 dev_data->clock_options_cnt = LFCLK_DEF_OPTS;
343
344 lfosc_mode = nrf_bicr_lfosc_mode_get(BICR);
345
346 if (lfosc_mode == NRF_BICR_LFOSC_MODE_UNCONFIGURED ||
347 lfosc_mode == NRF_BICR_LFOSC_MODE_DISABLED) {
348 dev_data->max_accuracy = LFCLK_HFXO_ACCURACY;
349 } else {
350 int ret;
351
352 ret = lfosc_get_accuracy(&dev_data->max_accuracy);
353 if (ret < 0) {
354 LOG_ERR("LFOSC enabled with invalid accuracy");
355 return ret;
356 }
357
358 switch (lfosc_mode) {
359 case NRF_BICR_LFOSC_MODE_CRYSTAL:
360 clock_options[LFCLK_MAX_OPTS - 1].accuracy = dev_data->max_accuracy;
361 clock_options[LFCLK_MAX_OPTS - 1].precision = 0;
362 clock_options[LFCLK_MAX_OPTS - 1].src = NRFS_CLOCK_SRC_LFCLK_XO_PIERCE;
363
364 clock_options[LFCLK_MAX_OPTS - 2].accuracy = dev_data->max_accuracy;
365 clock_options[LFCLK_MAX_OPTS - 2].precision = 1;
366 clock_options[LFCLK_MAX_OPTS - 2].src = NRFS_CLOCK_SRC_LFCLK_XO_PIERCE_HP;
367
368 dev_data->clock_options_cnt += 2;
369 break;
370 case NRF_BICR_LFOSC_MODE_EXTSINE:
371 clock_options[LFCLK_MAX_OPTS - 1].accuracy = dev_data->max_accuracy;
372 clock_options[LFCLK_MAX_OPTS - 1].precision = 0;
373 clock_options[LFCLK_MAX_OPTS - 1].src = NRFS_CLOCK_SRC_LFCLK_XO_EXT_SINE;
374
375 clock_options[LFCLK_MAX_OPTS - 2].accuracy = dev_data->max_accuracy;
376 clock_options[LFCLK_MAX_OPTS - 2].precision = 1;
377 clock_options[LFCLK_MAX_OPTS - 2].src = NRFS_CLOCK_SRC_LFCLK_XO_EXT_SINE_HP;
378
379 dev_data->clock_options_cnt += 2;
380 break;
381 case NRF_BICR_LFOSC_MODE_EXTSQUARE:
382 clock_options[LFCLK_MAX_OPTS - 2].accuracy = dev_data->max_accuracy;
383 clock_options[LFCLK_MAX_OPTS - 2].precision = 0;
384 clock_options[LFCLK_MAX_OPTS - 2].src = NRFS_CLOCK_SRC_LFCLK_XO_EXT_SQUARE;
385
386 dev_data->clock_options_cnt += 1;
387 break;
388 default:
389 LOG_ERR("Unexpected LFOSC mode");
390 return -EINVAL;
391 }
392
393 dev_data->lfxo_startup_time_us = nrf_bicr_lfosc_startup_time_ms_get(BICR)
394 * USEC_PER_MSEC;
395 if (dev_data->lfxo_startup_time_us == NRF_BICR_LFOSC_STARTUP_TIME_UNCONFIGURED) {
396 LOG_ERR("BICR LFXO startup time invalid");
397 return -ENODEV;
398 }
399 }
400
401 dev_data->hfxo_startup_time_us = nrf_bicr_hfxo_startup_time_us_get(BICR);
402 if (dev_data->hfxo_startup_time_us == NRF_BICR_HFXO_STARTUP_TIME_UNCONFIGURED) {
403 LOG_ERR("BICR HFXO startup time invalid");
404 return -ENODEV;
405 }
406
407 k_timer_init(&dev_data->timer, lfclk_update_timeout_handler, NULL);
408
409 return clock_config_init(&dev_data->clk_cfg,
410 ARRAY_SIZE(dev_data->clk_cfg.onoff),
411 lfclk_work_handler);
412 }
413
414 static DEVICE_API(nrf_clock_control, lfclk_drv_api) = {
415 .std_api = {
416 .on = api_nosys_on_off,
417 .off = api_nosys_on_off,
418 .get_rate = api_get_rate_lfclk,
419 },
420 .request = api_request_lfclk,
421 .release = api_release_lfclk,
422 .cancel_or_release = api_cancel_or_release_lfclk,
423 .resolve = api_resolve,
424 .get_startup_time = api_get_startup_time,
425 };
426
427 static struct lfclk_dev_data lfclk_data;
428
429 static const struct lfclk_dev_config lfclk_config = {
430 .fixed_frequency = DT_INST_PROP(0, clock_frequency),
431 };
432
433 DEVICE_DT_INST_DEFINE(0, lfclk_init, NULL,
434 &lfclk_data, &lfclk_config,
435 PRE_KERNEL_1, CONFIG_CLOCK_CONTROL_INIT_PRIORITY,
436 &lfclk_drv_api);
437