Branch data Line data Source code
1 : : /*
2 : : * Copyright (c) 2016-2020 Nordic Semiconductor ASA
3 : : * Copyright (c) 2016 Vinayak Kariappa Chettimada
4 : : *
5 : : * SPDX-License-Identifier: Apache-2.0
6 : : */
7 : :
8 : : #include <soc.h>
9 : : #include <sys/onoff.h>
10 : : #include <drivers/clock_control.h>
11 : : #include <drivers/clock_control/nrf_clock_control.h>
12 : : #include "nrf_clock_calibration.h"
13 : : #include <nrfx_clock.h>
14 : : #include <logging/log.h>
15 : : #include <shell/shell.h>
16 : :
17 : : LOG_MODULE_REGISTER(clock_control, CONFIG_CLOCK_CONTROL_LOG_LEVEL);
18 : :
19 : : #define DT_DRV_COMPAT nordic_nrf_clock
20 : :
21 : :
22 : : #define CTX_ONOFF BIT(6)
23 : : #define CTX_API BIT(7)
24 : : #define CTX_MASK (CTX_ONOFF | CTX_API)
25 : :
26 : : #define STATUS_MASK 0x7
27 : : #define GET_STATUS(flags) (flags & STATUS_MASK)
28 : : #define GET_CTX(flags) (flags & CTX_MASK)
29 : :
30 : : /* Used only by HF clock */
31 : : #define HF_USER_BT BIT(0)
32 : : #define HF_USER_GENERIC BIT(1)
33 : :
34 : : /* Helper logging macros which prepends subsys name to the log. */
35 : : #ifdef CONFIG_LOG
36 : : #define CLOCK_LOG(lvl, dev, subsys, ...) \
37 : : LOG_##lvl("%s: " GET_ARG_N(1, __VA_ARGS__), \
38 : : get_sub_config(dev, (enum clock_control_nrf_type)subsys)->name \
39 : : COND_CODE_0(NUM_VA_ARGS_LESS_1(__VA_ARGS__),\
40 : : (), (, GET_ARGS_LESS_N(1, __VA_ARGS__))))
41 : : #else
42 : : #define CLOCK_LOG(...)
43 : : #endif
44 : :
45 : : #define ERR(dev, subsys, ...) CLOCK_LOG(ERR, dev, subsys, __VA_ARGS__)
46 : : #define WRN(dev, subsys, ...) CLOCK_LOG(WRN, dev, subsys, __VA_ARGS__)
47 : : #define INF(dev, subsys, ...) CLOCK_LOG(INF, dev, subsys, __VA_ARGS__)
48 : : #define DBG(dev, subsys, ...) CLOCK_LOG(DBG, dev, subsys, __VA_ARGS__)
49 : :
50 : : /* Clock subsys structure */
51 : : struct nrf_clock_control_sub_data {
52 : : clock_control_cb_t cb;
53 : : void *user_data;
54 : : uint32_t flags;
55 : : };
56 : :
57 : : typedef void (*clk_ctrl_func_t)(void);
58 : :
59 : : /* Clock subsys static configuration */
60 : : struct nrf_clock_control_sub_config {
61 : : clk_ctrl_func_t start; /* Clock start function */
62 : : clk_ctrl_func_t stop; /* Clock stop function */
63 : : #ifdef CONFIG_LOG
64 : : const char *name;
65 : : #endif
66 : : };
67 : :
68 : : struct nrf_clock_control_data {
69 : : struct onoff_manager mgr[CLOCK_CONTROL_NRF_TYPE_COUNT];
70 : : struct nrf_clock_control_sub_data subsys[CLOCK_CONTROL_NRF_TYPE_COUNT];
71 : : };
72 : :
73 : : struct nrf_clock_control_config {
74 : : struct nrf_clock_control_sub_config
75 : : subsys[CLOCK_CONTROL_NRF_TYPE_COUNT];
76 : : };
77 : :
78 : : static atomic_t hfclk_users;
79 : : static uint64_t hf_start_tstamp;
80 : : static uint64_t hf_stop_tstamp;
81 : :
82 : 6 : static struct nrf_clock_control_sub_data *get_sub_data(const struct device *dev,
83 : : enum clock_control_nrf_type type)
84 : : {
85 : 6 : struct nrf_clock_control_data *data = dev->data;
86 : :
87 : 6 : return &data->subsys[type];
88 : : }
89 : :
90 : 1 : static const struct nrf_clock_control_sub_config *get_sub_config(const struct device *dev,
91 : : enum clock_control_nrf_type type)
92 : : {
93 : 1 : const struct nrf_clock_control_config *config =
94 : : dev->config;
95 : :
96 : 1 : return &config->subsys[type];
97 : : }
98 : :
99 : 6 : static struct onoff_manager *get_onoff_manager(const struct device *dev,
100 : : enum clock_control_nrf_type type)
101 : : {
102 : 6 : struct nrf_clock_control_data *data = dev->data;
103 : :
104 : 6 : return &data->mgr[type];
105 : : }
106 : :
107 : :
108 : : #define CLOCK_DEVICE DEVICE_DT_GET(DT_NODELABEL(clock))
109 : :
110 : 0 : struct onoff_manager *z_nrf_clock_control_get_onoff(clock_control_subsys_t sys)
111 : : {
112 : 0 : return get_onoff_manager(CLOCK_DEVICE,
113 : : (enum clock_control_nrf_type)sys);
114 : : }
115 : :
116 : 0 : static enum clock_control_status get_status(const struct device *dev,
117 : : clock_control_subsys_t subsys)
118 : : {
119 : 0 : enum clock_control_nrf_type type = (enum clock_control_nrf_type)subsys;
120 : :
121 [ # # ]: 0 : __ASSERT_NO_MSG(type < CLOCK_CONTROL_NRF_TYPE_COUNT);
122 : :
123 : 0 : return GET_STATUS(get_sub_data(dev, type)->flags);
124 : : }
125 : :
126 : 0 : static int set_off_state(uint32_t *flags, uint32_t ctx)
127 : : {
128 : 0 : int err = 0;
129 : 0 : int key = irq_lock();
130 : 0 : uint32_t current_ctx = GET_CTX(*flags);
131 : :
132 [ # # # # ]: 0 : if ((current_ctx != 0) && (current_ctx != ctx)) {
133 : 0 : err = -EPERM;
134 : : } else {
135 : 0 : *flags = CLOCK_CONTROL_STATUS_OFF;
136 : : }
137 : :
138 : 0 : irq_unlock(key);
139 : :
140 : 0 : return err;
141 : : }
142 : :
143 : 1 : static int set_starting_state(uint32_t *flags, uint32_t ctx)
144 : : {
145 : 1 : int err = 0;
146 : 1 : int key = irq_lock();
147 : 1 : uint32_t current_ctx = GET_CTX(*flags);
148 : :
149 [ + - ]: 1 : if ((*flags & (STATUS_MASK)) == CLOCK_CONTROL_STATUS_OFF) {
150 : 1 : *flags = CLOCK_CONTROL_STATUS_STARTING | ctx;
151 [ # # ]: 0 : } else if (current_ctx != ctx) {
152 : 0 : err = -EPERM;
153 : : } else {
154 : 0 : err = -EALREADY;
155 : : }
156 : :
157 : 1 : irq_unlock(key);
158 : :
159 : 1 : return err;
160 : : }
161 : :
162 : 1 : static void set_on_state(uint32_t *flags)
163 : : {
164 : 1 : int key = irq_lock();
165 : :
166 : 1 : *flags = CLOCK_CONTROL_STATUS_ON | GET_CTX(*flags);
167 : 1 : irq_unlock(key);
168 : 1 : }
169 : :
170 : 1 : static void clkstarted_handle(const struct device *dev,
171 : : enum clock_control_nrf_type type)
172 : : {
173 : 1 : struct nrf_clock_control_sub_data *sub_data = get_sub_data(dev, type);
174 : 1 : clock_control_cb_t callback = sub_data->cb;
175 : 1 : void *user_data = sub_data->user_data;
176 : :
177 : 1 : sub_data->cb = NULL;
178 : 1 : set_on_state(&sub_data->flags);
179 [ + - ]: 1 : DBG(dev, type, "Clock started");
180 : :
181 [ + - ]: 1 : if (callback) {
182 : 1 : callback(dev, (clock_control_subsys_t)type, user_data);
183 : : }
184 : 1 : }
185 : :
186 : : static inline void anomaly_132_workaround(void)
187 : : {
188 : : #if (CONFIG_NRF52_ANOMALY_132_DELAY_US - 0)
189 : : static bool once;
190 : :
191 : : if (!once) {
192 : : k_busy_wait(CONFIG_NRF52_ANOMALY_132_DELAY_US);
193 : : once = true;
194 : : }
195 : : #endif
196 : : }
197 : :
198 : 1 : static void lfclk_start(void)
199 : : {
200 : : if (IS_ENABLED(CONFIG_NRF52_ANOMALY_132_WORKAROUND)) {
201 : : anomaly_132_workaround();
202 : : }
203 : :
204 : 1 : nrfx_clock_lfclk_start();
205 : 1 : }
206 : :
207 : 0 : static void lfclk_stop(void)
208 : : {
209 : : if (IS_ENABLED(CONFIG_CLOCK_CONTROL_NRF_DRIVER_CALIBRATION)) {
210 : : z_nrf_clock_calibration_lfclk_stopped();
211 : : }
212 : :
213 : 0 : nrfx_clock_lfclk_stop();
214 : 0 : }
215 : :
216 : 0 : static void hfclk_start(void)
217 : : {
218 : : if (IS_ENABLED(CONFIG_CLOCK_CONTROL_NRF_SHELL)) {
219 : : hf_start_tstamp = k_uptime_get();
220 : : }
221 : :
222 : 0 : nrfx_clock_hfclk_start();
223 : 0 : }
224 : :
225 : 0 : static void hfclk_stop(void)
226 : : {
227 : : if (IS_ENABLED(CONFIG_CLOCK_CONTROL_NRF_SHELL)) {
228 : : hf_stop_tstamp = k_uptime_get();
229 : : }
230 : :
231 : 0 : nrfx_clock_hfclk_stop();
232 : 0 : }
233 : :
234 : : #if NRF_CLOCK_HAS_HFCLK192M
235 : 0 : static void hfclk192m_start(void)
236 : : {
237 : 0 : nrfx_clock_start(NRF_CLOCK_DOMAIN_HFCLK192M);
238 : 0 : }
239 : :
240 : 0 : static void hfclk192m_stop(void)
241 : : {
242 : 0 : nrfx_clock_stop(NRF_CLOCK_DOMAIN_HFCLK192M);
243 : 0 : }
244 : : #endif
245 : :
246 : : #if NRF_CLOCK_HAS_HFCLKAUDIO
247 : 0 : static void hfclkaudio_start(void)
248 : : {
249 : 0 : nrfx_clock_start(NRF_CLOCK_DOMAIN_HFCLKAUDIO);
250 : 0 : }
251 : :
252 : 0 : static void hfclkaudio_stop(void)
253 : : {
254 : 0 : nrfx_clock_stop(NRF_CLOCK_DOMAIN_HFCLKAUDIO);
255 : 0 : }
256 : : #endif
257 : :
258 : 0 : static uint32_t *get_hf_flags(void)
259 : : {
260 : 0 : struct nrf_clock_control_data *data = CLOCK_DEVICE->data;
261 : :
262 : 0 : return &data->subsys[CLOCK_CONTROL_NRF_TYPE_HFCLK].flags;
263 : : }
264 : :
265 : 0 : static void generic_hfclk_start(void)
266 : : {
267 : : nrf_clock_hfclk_t type;
268 : 0 : bool already_started = false;
269 : 0 : int key = irq_lock();
270 : :
271 : 0 : hfclk_users |= HF_USER_GENERIC;
272 [ # # ]: 0 : if (hfclk_users & HF_USER_BT) {
273 : 0 : (void)nrfx_clock_is_running(NRF_CLOCK_DOMAIN_HFCLK, &type);
274 [ # # ]: 0 : if (type == NRF_CLOCK_HFCLK_HIGH_ACCURACY) {
275 : 0 : already_started = true;
276 : : /* Set on state in case clock interrupt comes and we
277 : : * want to avoid handling that.
278 : : */
279 : 0 : set_on_state(get_hf_flags());
280 : : }
281 : : }
282 : :
283 : 0 : irq_unlock(key);
284 : :
285 [ # # ]: 0 : if (already_started) {
286 : : /* Clock already started by z_nrf_clock_bt_ctlr_hf_request */
287 : 0 : clkstarted_handle(CLOCK_DEVICE,
288 : : CLOCK_CONTROL_NRF_TYPE_HFCLK);
289 : 0 : return;
290 : : }
291 : :
292 : 0 : hfclk_start();
293 : : }
294 : :
295 : 0 : static void generic_hfclk_stop(void)
296 : : {
297 [ # # ]: 0 : if (atomic_and(&hfclk_users, ~HF_USER_GENERIC) & HF_USER_BT) {
298 : : /* bt still requesting the clock. */
299 : 0 : return;
300 : : }
301 : :
302 : 0 : hfclk_stop();
303 : : }
304 : :
305 : :
306 : 0 : void z_nrf_clock_bt_ctlr_hf_request(void)
307 : : {
308 [ # # ]: 0 : if (atomic_or(&hfclk_users, HF_USER_BT) & HF_USER_GENERIC) {
309 : : /* generic request already activated clock. */
310 : 0 : return;
311 : : }
312 : :
313 : 0 : hfclk_start();
314 : : }
315 : :
316 : 0 : void z_nrf_clock_bt_ctlr_hf_release(void)
317 : : {
318 [ # # ]: 0 : if (atomic_and(&hfclk_users, ~HF_USER_BT) & HF_USER_GENERIC) {
319 : : /* generic still requesting the clock. */
320 : 0 : return;
321 : : }
322 : :
323 : 0 : hfclk_stop();
324 : : }
325 : :
326 : 0 : static int stop(const struct device *dev, clock_control_subsys_t subsys,
327 : : uint32_t ctx)
328 : : {
329 : 0 : enum clock_control_nrf_type type = (enum clock_control_nrf_type)subsys;
330 : 0 : struct nrf_clock_control_sub_data *subdata = get_sub_data(dev, type);
331 : : int err;
332 : :
333 [ # # ]: 0 : __ASSERT_NO_MSG(type < CLOCK_CONTROL_NRF_TYPE_COUNT);
334 : :
335 : 0 : err = set_off_state(&subdata->flags, ctx);
336 [ # # ]: 0 : if (err < 0) {
337 : 0 : return err;
338 : : }
339 : :
340 : 0 : get_sub_config(dev, type)->stop();
341 : :
342 : 0 : return 0;
343 : : }
344 : :
345 : 0 : static int api_stop(const struct device *dev, clock_control_subsys_t subsys)
346 : : {
347 : 0 : return stop(dev, subsys, CTX_API);
348 : : }
349 : :
350 : 1 : static int async_start(const struct device *dev, clock_control_subsys_t subsys,
351 : : clock_control_cb_t cb, void *user_data, uint32_t ctx)
352 : : {
353 : 1 : enum clock_control_nrf_type type = (enum clock_control_nrf_type)subsys;
354 : 1 : struct nrf_clock_control_sub_data *subdata = get_sub_data(dev, type);
355 : : int err;
356 : :
357 : 1 : err = set_starting_state(&subdata->flags, ctx);
358 [ - + ]: 1 : if (err < 0) {
359 : 0 : return err;
360 : : }
361 : :
362 : 1 : subdata->cb = cb;
363 : 1 : subdata->user_data = user_data;
364 : :
365 : 1 : get_sub_config(dev, type)->start();
366 : :
367 : 1 : return 0;
368 : : }
369 : :
370 : 0 : static int api_start(const struct device *dev, clock_control_subsys_t subsys,
371 : : clock_control_cb_t cb, void *user_data)
372 : : {
373 : 0 : return async_start(dev, subsys, cb, user_data, CTX_API);
374 : : }
375 : :
376 : 0 : static void blocking_start_callback(const struct device *dev,
377 : : clock_control_subsys_t subsys,
378 : : void *user_data)
379 : : {
380 : 0 : struct k_sem *sem = user_data;
381 : :
382 : 0 : k_sem_give(sem);
383 : 0 : }
384 : :
385 : 0 : static int api_blocking_start(const struct device *dev,
386 : : clock_control_subsys_t subsys)
387 : : {
388 : 0 : struct k_sem sem = Z_SEM_INITIALIZER(sem, 0, 1);
389 : : int err;
390 : :
391 : : if (!IS_ENABLED(CONFIG_MULTITHREADING)) {
392 : : return -ENOTSUP;
393 : : }
394 : :
395 : 0 : err = api_start(dev, subsys, blocking_start_callback, &sem);
396 [ # # ]: 0 : if (err < 0) {
397 : 0 : return err;
398 : : }
399 : :
400 : 0 : return k_sem_take(&sem, K_MSEC(500));
401 : : }
402 : :
403 : 1 : static clock_control_subsys_t get_subsys(struct onoff_manager *mgr)
404 : : {
405 : 1 : struct nrf_clock_control_data *data = CLOCK_DEVICE->data;
406 : 1 : size_t offset = (size_t)(mgr - data->mgr);
407 : :
408 : 1 : return (clock_control_subsys_t)offset;
409 : : }
410 : :
411 : 0 : static void onoff_stop(struct onoff_manager *mgr,
412 : : onoff_notify_fn notify)
413 : : {
414 : : int res;
415 : :
416 : 0 : res = stop(CLOCK_DEVICE, get_subsys(mgr), CTX_ONOFF);
417 : 0 : notify(mgr, res);
418 : 0 : }
419 : :
420 : 1 : static void onoff_started_callback(const struct device *dev,
421 : : clock_control_subsys_t sys,
422 : : void *user_data)
423 : : {
424 : 1 : enum clock_control_nrf_type type = (enum clock_control_nrf_type)sys;
425 : 1 : struct onoff_manager *mgr = get_onoff_manager(dev, type);
426 : 1 : onoff_notify_fn notify = user_data;
427 : :
428 : 1 : notify(mgr, 0);
429 : 1 : }
430 : :
431 : 1 : static void onoff_start(struct onoff_manager *mgr,
432 : : onoff_notify_fn notify)
433 : : {
434 : : int err;
435 : :
436 : 1 : err = async_start(CLOCK_DEVICE, get_subsys(mgr),
437 : : onoff_started_callback, notify, CTX_ONOFF);
438 [ - + ]: 1 : if (err < 0) {
439 : 0 : notify(mgr, err);
440 : : }
441 : 1 : }
442 : :
443 : : /** @brief Wait for LF clock availability or stability.
444 : : *
445 : : * If LF clock source is SYNTH or RC then there is no distinction between
446 : : * availability and stability. In case of XTAL source clock, system is initially
447 : : * starting RC and then seamlessly switches to XTAL. Running RC means clock
448 : : * availability and running target source means stability, That is because
449 : : * significant difference in startup time (<1ms vs >200ms).
450 : : *
451 : : * In order to get event/interrupt when RC is ready (allowing CPU sleeping) two
452 : : * stage startup sequence is used. Initially, LF source is set to RC and when
453 : : * LFSTARTED event is handled it is reconfigured to the target source clock.
454 : : * This approach is implemented in nrfx_clock driver and utilized here.
455 : : *
456 : : * @param mode Start mode.
457 : : */
458 : 1 : static void lfclk_spinwait(enum nrf_lfclk_start_mode mode)
459 : : {
460 : : static const nrf_clock_domain_t d = NRF_CLOCK_DOMAIN_LFCLK;
461 : : static const nrf_clock_lfclk_t target_type =
462 : : /* For sources XTAL, EXT_LOW_SWING, and EXT_FULL_SWING,
463 : : * NRF_CLOCK_LFCLK_Xtal is returned as the type of running clock.
464 : : */
465 : : (IS_ENABLED(CONFIG_CLOCK_CONTROL_NRF_K32SRC_XTAL) ||
466 : : IS_ENABLED(CONFIG_CLOCK_CONTROL_NRF_K32SRC_EXT_LOW_SWING) ||
467 : : IS_ENABLED(CONFIG_CLOCK_CONTROL_NRF_K32SRC_EXT_FULL_SWING))
468 : : ? NRF_CLOCK_LFCLK_Xtal
469 : : : CLOCK_CONTROL_NRF_K32SRC;
470 : : nrf_clock_lfclk_t type;
471 : :
472 [ - + ]: 1 : if ((mode == CLOCK_CONTROL_NRF_LF_START_AVAILABLE) &&
473 [ # # # # ]: 0 : (target_type == NRF_CLOCK_LFCLK_Xtal) &&
474 : 0 : (nrf_clock_lf_srccopy_get(NRF_CLOCK) == CLOCK_CONTROL_NRF_K32SRC)) {
475 : : /* If target clock source is using XTAL then due to two-stage
476 : : * clock startup sequence, RC might already be running.
477 : : * It can be determined by checking current LFCLK source. If it
478 : : * is set to the target clock source then it means that RC was
479 : : * started.
480 : : */
481 : 0 : return;
482 : : }
483 : :
484 [ + - + - ]: 1 : bool isr_mode = k_is_in_isr() || k_is_pre_kernel();
485 [ + - ]: 1 : int key = isr_mode ? irq_lock() : 0;
486 : :
487 [ - + ]: 1 : if (!isr_mode) {
488 : 0 : nrf_clock_int_disable(NRF_CLOCK, NRF_CLOCK_INT_LF_STARTED_MASK);
489 : : }
490 : :
491 [ + + ]: 3 : while (!(nrfx_clock_is_running(d, (void *)&type)
492 [ - + ]: 1 : && ((type == target_type)
493 [ # # ]: 0 : || (mode == CLOCK_CONTROL_NRF_LF_START_AVAILABLE)))) {
494 : : /* Synth source start is almost instant and LFCLKSTARTED may
495 : : * happen before calling idle. That would lead to deadlock.
496 : : */
497 : : if (!IS_ENABLED(CONFIG_CLOCK_CONTROL_NRF_K32SRC_SYNTH)) {
498 [ + - ]: 2 : if (isr_mode || !IS_ENABLED(CONFIG_MULTITHREADING)) {
499 : 2 : k_cpu_atomic_idle(key);
500 : : } else {
501 : 0 : k_msleep(1);
502 : : }
503 : : }
504 : :
505 : : /* Clock interrupt is locked, LFCLKSTARTED is handled here. */
506 [ + - ]: 2 : if ((target_type == NRF_CLOCK_LFCLK_Xtal)
507 [ + + ]: 2 : && (nrf_clock_lf_src_get(NRF_CLOCK) == NRF_CLOCK_LFCLK_RC)
508 [ + - ]: 1 : && nrf_clock_event_check(NRF_CLOCK,
509 : : NRF_CLOCK_EVENT_LFCLKSTARTED)) {
510 : 1 : nrf_clock_event_clear(NRF_CLOCK,
511 : : NRF_CLOCK_EVENT_LFCLKSTARTED);
512 : 1 : nrf_clock_lf_src_set(NRF_CLOCK,
513 : : CLOCK_CONTROL_NRF_K32SRC);
514 : :
515 : : /* Clear pending interrupt, otherwise new clock event
516 : : * would not wake up from idle.
517 : : */
518 : 1 : NVIC_ClearPendingIRQ(DT_INST_IRQN(0));
519 : 1 : nrf_clock_task_trigger(NRF_CLOCK,
520 : : NRF_CLOCK_TASK_LFCLKSTART);
521 : : }
522 : : }
523 : :
524 [ + - ]: 1 : if (isr_mode) {
525 : 1 : irq_unlock(key);
526 : : } else {
527 : 0 : nrf_clock_int_enable(NRF_CLOCK, NRF_CLOCK_INT_LF_STARTED_MASK);
528 : : }
529 : : }
530 : :
531 : 1 : void z_nrf_clock_control_lf_on(enum nrf_lfclk_start_mode start_mode)
532 : : {
533 : : static atomic_t on;
534 : : static struct onoff_client cli;
535 : :
536 [ + - ]: 1 : if (atomic_set(&on, 1) == 0) {
537 : : int err;
538 : : struct onoff_manager *mgr =
539 : 1 : get_onoff_manager(CLOCK_DEVICE,
540 : : CLOCK_CONTROL_NRF_TYPE_LFCLK);
541 : :
542 : 1 : sys_notify_init_spinwait(&cli.notify);
543 : 1 : err = onoff_request(mgr, &cli);
544 [ - + ]: 1 : __ASSERT_NO_MSG(err >= 0);
545 : : }
546 : :
547 : : /* In case of simulated board leave immediately. */
548 : : if (IS_ENABLED(CONFIG_SOC_SERIES_BSIM_NRFXX)) {
549 : : return;
550 : : }
551 : :
552 [ + - - ]: 1 : switch (start_mode) {
553 : 1 : case CLOCK_CONTROL_NRF_LF_START_AVAILABLE:
554 : : case CLOCK_CONTROL_NRF_LF_START_STABLE:
555 : 1 : lfclk_spinwait(start_mode);
556 : 1 : break;
557 : :
558 : 0 : case CLOCK_CONTROL_NRF_LF_START_NOWAIT:
559 : 0 : break;
560 : :
561 : 0 : default:
562 : 0 : __ASSERT_NO_MSG(false);
563 : : }
564 : : }
565 : :
566 : 1 : static void clock_event_handler(nrfx_clock_evt_type_t event)
567 : : {
568 : 1 : const struct device *dev = CLOCK_DEVICE;
569 : :
570 [ - - - + : 1 : switch (event) {
- - ]
571 : 0 : case NRFX_CLOCK_EVT_HFCLK_STARTED:
572 : : {
573 : : struct nrf_clock_control_sub_data *data =
574 : 0 : get_sub_data(dev, CLOCK_CONTROL_NRF_TYPE_HFCLK);
575 : :
576 : : /* Check needed due to anomaly 201:
577 : : * HFCLKSTARTED may be generated twice.
578 : : */
579 [ # # ]: 0 : if (GET_STATUS(data->flags) == CLOCK_CONTROL_STATUS_STARTING) {
580 : 0 : clkstarted_handle(dev, CLOCK_CONTROL_NRF_TYPE_HFCLK);
581 : : }
582 : :
583 : 0 : break;
584 : : }
585 : : #if NRF_CLOCK_HAS_HFCLK192M
586 : 0 : case NRFX_CLOCK_EVT_HFCLK192M_STARTED:
587 : 0 : clkstarted_handle(dev, CLOCK_CONTROL_NRF_TYPE_HFCLK192M);
588 : 0 : break;
589 : : #endif
590 : : #if NRF_CLOCK_HAS_HFCLKAUDIO
591 : 0 : case NRFX_CLOCK_EVT_HFCLKAUDIO_STARTED:
592 : 0 : clkstarted_handle(dev, CLOCK_CONTROL_NRF_TYPE_HFCLKAUDIO);
593 : 0 : break;
594 : : #endif
595 : 1 : case NRFX_CLOCK_EVT_LFCLK_STARTED:
596 : : if (IS_ENABLED(CONFIG_CLOCK_CONTROL_NRF_DRIVER_CALIBRATION)) {
597 : : z_nrf_clock_calibration_lfclk_started();
598 : : }
599 : 1 : clkstarted_handle(dev, CLOCK_CONTROL_NRF_TYPE_LFCLK);
600 : 1 : break;
601 : 0 : case NRFX_CLOCK_EVT_CAL_DONE:
602 : : if (IS_ENABLED(CONFIG_CLOCK_CONTROL_NRF_DRIVER_CALIBRATION)) {
603 : : z_nrf_clock_calibration_done_handler();
604 : : } else {
605 : : /* Should not happen when calibration is disabled. */
606 : 0 : __ASSERT_NO_MSG(false);
607 : : }
608 : 0 : break;
609 : 0 : default:
610 : 0 : __ASSERT_NO_MSG(0);
611 : 0 : break;
612 : : }
613 : 1 : }
614 : :
615 : 1 : static void hfclkaudio_init(void)
616 : : {
617 : : #if DT_NODE_HAS_PROP(DT_NODELABEL(clock), hfclkaudio_frequency)
618 : : const uint32_t frequency =
619 : : DT_PROP(DT_NODELABEL(clock), hfclkaudio_frequency);
620 : : /* As specified in the nRF5340 PS:
621 : : *
622 : : * FREQ_VALUE = 2^16 * ((12 * f_out / 32M) - 4)
623 : : */
624 : : const uint32_t freq_value =
625 : : (uint32_t)((384ULL * frequency) / 15625) - 262144;
626 : :
627 : : #if NRF_CLOCK_HAS_HFCLKAUDIO
628 : : nrf_clock_hfclkaudio_config_set(NRF_CLOCK, freq_value);
629 : : #else
630 : : #error "hfclkaudio-frequency specified but HFCLKAUDIO clock is not present."
631 : : #endif /* NRF_CLOCK_HAS_HFCLKAUDIO */
632 : : #endif
633 : 1 : }
634 : :
635 : 1 : static int clk_init(const struct device *dev)
636 : : {
637 : : nrfx_err_t nrfx_err;
638 : : int err;
639 : : static const struct onoff_transitions transitions = {
640 : : .start = onoff_start,
641 : : .stop = onoff_stop
642 : : };
643 : :
644 : 1 : IRQ_CONNECT(DT_INST_IRQN(0), DT_INST_IRQ(0, priority),
645 : : nrfx_isr, nrfx_power_clock_irq_handler, 0);
646 : :
647 : 1 : nrfx_err = nrfx_clock_init(clock_event_handler);
648 [ - + ]: 1 : if (nrfx_err != NRFX_SUCCESS) {
649 : 0 : return -EIO;
650 : : }
651 : :
652 : 1 : hfclkaudio_init();
653 : :
654 : : if (IS_ENABLED(CONFIG_CLOCK_CONTROL_NRF_DRIVER_CALIBRATION)) {
655 : : struct nrf_clock_control_data *data = dev->data;
656 : :
657 : : z_nrf_clock_calibration_init(data->mgr);
658 : : }
659 : :
660 : 1 : nrfx_clock_enable();
661 : :
662 [ + + ]: 5 : for (enum clock_control_nrf_type i = 0;
663 : 4 : i < CLOCK_CONTROL_NRF_TYPE_COUNT; i++) {
664 : : struct nrf_clock_control_sub_data *subdata =
665 : 4 : get_sub_data(dev, i);
666 : :
667 : 4 : err = onoff_manager_init(get_onoff_manager(dev, i),
668 : : &transitions);
669 [ - + ]: 4 : if (err < 0) {
670 : 0 : return err;
671 : : }
672 : :
673 : 4 : subdata->flags = CLOCK_CONTROL_STATUS_OFF;
674 : : }
675 : :
676 : 1 : return 0;
677 : : }
678 : :
679 : : static const struct clock_control_driver_api clock_control_api = {
680 : : .on = api_blocking_start,
681 : : .off = api_stop,
682 : : .async_on = api_start,
683 : : .get_status = get_status,
684 : : };
685 : :
686 : : static struct nrf_clock_control_data data;
687 : :
688 : : static const struct nrf_clock_control_config config = {
689 : : .subsys = {
690 : : [CLOCK_CONTROL_NRF_TYPE_HFCLK] = {
691 : : .start = generic_hfclk_start,
692 : : .stop = generic_hfclk_stop,
693 : : IF_ENABLED(CONFIG_LOG, (.name = "hfclk",))
694 : : },
695 : : [CLOCK_CONTROL_NRF_TYPE_LFCLK] = {
696 : : .start = lfclk_start,
697 : : .stop = lfclk_stop,
698 : : IF_ENABLED(CONFIG_LOG, (.name = "lfclk",))
699 : : },
700 : : #if NRF_CLOCK_HAS_HFCLK192M
701 : : [CLOCK_CONTROL_NRF_TYPE_HFCLK192M] = {
702 : : .start = hfclk192m_start,
703 : : .stop = hfclk192m_stop,
704 : : IF_ENABLED(CONFIG_LOG, (.name = "hfclk192m",))
705 : : },
706 : : #endif
707 : : #if NRF_CLOCK_HAS_HFCLKAUDIO
708 : : [CLOCK_CONTROL_NRF_TYPE_HFCLKAUDIO] = {
709 : : .start = hfclkaudio_start,
710 : : .stop = hfclkaudio_stop,
711 : : IF_ENABLED(CONFIG_LOG, (.name = "hfclkaudio",))
712 : : },
713 : : #endif
714 : : }
715 : : };
716 : :
717 : : DEVICE_DT_DEFINE(DT_NODELABEL(clock), clk_init, NULL,
718 : : &data, &config,
719 : : PRE_KERNEL_1, CONFIG_CLOCK_CONTROL_INIT_PRIORITY,
720 : : &clock_control_api);
721 : :
722 : 0 : static int cmd_status(const struct shell *shell, size_t argc, char **argv)
723 : : {
724 : : nrf_clock_hfclk_t hfclk_src;
725 : : bool hf_status;
726 : 0 : bool lf_status = nrfx_clock_is_running(NRF_CLOCK_DOMAIN_LFCLK, NULL);
727 : : struct onoff_manager *hf_mgr =
728 : 0 : get_onoff_manager(CLOCK_DEVICE,
729 : : CLOCK_CONTROL_NRF_TYPE_HFCLK);
730 : : struct onoff_manager *lf_mgr =
731 : 0 : get_onoff_manager(CLOCK_DEVICE,
732 : : CLOCK_CONTROL_NRF_TYPE_LFCLK);
733 : : uint32_t abs_start, abs_stop;
734 : 0 : int key = irq_lock();
735 : 0 : uint64_t now = k_uptime_get();
736 : :
737 : 0 : (void)nrfx_clock_is_running(NRF_CLOCK_DOMAIN_HFCLK, (void *)&hfclk_src);
738 : 0 : hf_status = (hfclk_src == NRF_CLOCK_HFCLK_HIGH_ACCURACY);
739 : :
740 : 0 : abs_start = hf_start_tstamp;
741 : 0 : abs_stop = hf_stop_tstamp;
742 : 0 : irq_unlock(key);
743 : :
744 : 0 : shell_print(shell, "HF clock:");
745 [ # # ]: 0 : shell_print(shell, "\t- %srunning (users: %u)",
746 : : hf_status ? "" : "not ", hf_mgr->refs);
747 : 0 : shell_print(shell, "\t- last start: %u ms (%u ms ago)",
748 : : (uint32_t)abs_start, (uint32_t)(now - abs_start));
749 : 0 : shell_print(shell, "\t- last stop: %u ms (%u ms ago)",
750 : : (uint32_t)abs_stop, (uint32_t)(now - abs_stop));
751 : 0 : shell_print(shell, "LF clock:");
752 [ # # ]: 0 : shell_print(shell, "\t- %srunning (users: %u)",
753 : : lf_status ? "" : "not ", lf_mgr->refs);
754 : :
755 : 0 : return 0;
756 : : }
757 : :
758 : : SHELL_STATIC_SUBCMD_SET_CREATE(subcmds,
759 : : SHELL_CMD_ARG(status, NULL, "Status", cmd_status, 1, 0),
760 : : SHELL_SUBCMD_SET_END
761 : : );
762 : :
763 : : SHELL_COND_CMD_REGISTER(CONFIG_CLOCK_CONTROL_NRF_SHELL,
764 : : nrf_clock_control, &subcmds,
765 : : "Clock control commands",
766 : : cmd_status);
|