1 /*
2  * Copyright (c) 2018-2020 Nordic Semiconductor ASA
3  *
4  * SPDX-License-Identifier: Apache-2.0
5  */
6 
7 #include <soc.h>
8 #include <zephyr/device.h>
9 
10 #include <zephyr/drivers/clock_control.h>
11 #include <zephyr/drivers/clock_control/nrf_clock_control.h>
12 
13 #include "hal/debug.h"
14 
15 /* Clock setup timeouts are unlikely, below values are experimental */
16 #define LFCLOCK_TIMEOUT_MS 500
17 #define HFCLOCK_TIMEOUT_MS 2
18 
19 static uint16_t const sca_ppm_lut[] = {500, 250, 150, 100, 75, 50, 30, 20};
20 
21 struct lll_clock_state {
22 	struct onoff_client cli;
23 	struct k_sem sem;
24 };
25 
26 static struct onoff_client lf_cli;
27 static atomic_val_t hf_refcnt;
28 
clock_ready(struct onoff_manager * mgr,struct onoff_client * cli,uint32_t state,int res)29 static void clock_ready(struct onoff_manager *mgr, struct onoff_client *cli,
30 			uint32_t state, int res)
31 {
32 	struct lll_clock_state *clk_state =
33 		CONTAINER_OF(cli, struct lll_clock_state, cli);
34 
35 	k_sem_give(&clk_state->sem);
36 }
37 
blocking_on(struct onoff_manager * mgr,uint32_t timeout)38 static int blocking_on(struct onoff_manager *mgr, uint32_t timeout)
39 {
40 
41 	struct lll_clock_state state;
42 	int err;
43 
44 	k_sem_init(&state.sem, 0, 1);
45 	sys_notify_init_callback(&state.cli.notify, clock_ready);
46 	err = onoff_request(mgr, &state.cli);
47 	if (err < 0) {
48 		return err;
49 	}
50 
51 	return k_sem_take(&state.sem, K_MSEC(timeout));
52 }
53 
lll_clock_init(void)54 int lll_clock_init(void)
55 {
56 	struct onoff_manager *mgr =
57 		z_nrf_clock_control_get_onoff(CLOCK_CONTROL_NRF_SUBSYS_LF);
58 
59 	sys_notify_init_spinwait(&lf_cli.notify);
60 
61 	return onoff_request(mgr, &lf_cli);
62 }
63 
lll_clock_deinit(void)64 int lll_clock_deinit(void)
65 {
66 	struct onoff_manager *mgr =
67 		z_nrf_clock_control_get_onoff(CLOCK_CONTROL_NRF_SUBSYS_LF);
68 
69 	/* Cancel any ongoing request */
70 	(void)onoff_cancel(mgr, &lf_cli);
71 
72 	return onoff_release(mgr);
73 }
74 
lll_clock_wait(void)75 int lll_clock_wait(void)
76 {
77 	struct onoff_manager *mgr;
78 	static bool done;
79 	int err;
80 
81 	if (done) {
82 		return 0;
83 	}
84 	done = true;
85 
86 	mgr = z_nrf_clock_control_get_onoff(CLOCK_CONTROL_NRF_SUBSYS_LF);
87 	err = blocking_on(mgr, LFCLOCK_TIMEOUT_MS);
88 	if (err) {
89 		return err;
90 	}
91 
92 	err = onoff_release(mgr);
93 	if (err != ONOFF_STATE_ON) {
94 		return -EIO;
95 	}
96 
97 	return 0;
98 }
99 
lll_hfclock_on(void)100 int lll_hfclock_on(void)
101 {
102 	if (atomic_inc(&hf_refcnt) > 0) {
103 		return 0;
104 	}
105 
106 	z_nrf_clock_bt_ctlr_hf_request();
107 	DEBUG_RADIO_XTAL(1);
108 
109 	return 0;
110 }
111 
lll_hfclock_on_wait(void)112 int lll_hfclock_on_wait(void)
113 {
114 	struct onoff_manager *mgr =
115 		z_nrf_clock_control_get_onoff(CLOCK_CONTROL_NRF_SUBSYS_HF);
116 	int err;
117 
118 	atomic_inc(&hf_refcnt);
119 
120 	err = blocking_on(mgr, HFCLOCK_TIMEOUT_MS);
121 	if (err >= 0) {
122 		DEBUG_RADIO_XTAL(1);
123 	}
124 
125 	return err;
126 }
127 
lll_hfclock_off(void)128 int lll_hfclock_off(void)
129 {
130 	if (hf_refcnt < 1) {
131 		return -EALREADY;
132 	}
133 
134 	if (atomic_dec(&hf_refcnt) > 1) {
135 		return 0;
136 	}
137 
138 	z_nrf_clock_bt_ctlr_hf_release();
139 	DEBUG_RADIO_XTAL(0);
140 
141 	return 0;
142 }
143 
lll_clock_sca_local_get(void)144 uint8_t lll_clock_sca_local_get(void)
145 {
146 	return CLOCK_CONTROL_NRF_K32SRC_ACCURACY;
147 }
148 
lll_clock_ppm_local_get(void)149 uint32_t lll_clock_ppm_local_get(void)
150 {
151 	return sca_ppm_lut[CLOCK_CONTROL_NRF_K32SRC_ACCURACY];
152 }
153 
lll_clock_ppm_get(uint8_t sca)154 uint32_t lll_clock_ppm_get(uint8_t sca)
155 {
156 	return sca_ppm_lut[sca];
157 }
158