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_LFLPRC_ACCURACY DT_INST_PROP(0, lflprc_accuracy_ppm)
23 #define LFCLK_LFRC_ACCURACY DT_INST_PROP(0, lfrc_accuracy_ppm)
24 #define LFCLK_HFXO_ACCURACY DT_PROP(LFCLK_HFXO_NODE, accuracy_ppm)
25
26 #define LFCLK_MAX_OPTS 5
27 #define LFCLK_DEF_OPTS 3
28
29 #define NRFS_CLOCK_TIMEOUT K_MSEC(CONFIG_CLOCK_CONTROL_NRF2_NRFS_CLOCK_TIMEOUT_MS)
30
31 #define BICR (NRF_BICR_Type *)DT_REG_ADDR(DT_NODELABEL(bicr))
32
33 /* Clock options sorted from lowest to highest accuracy/precision */
34 static struct clock_options {
35 uint16_t accuracy : 15;
36 uint16_t precision : 1;
37 nrfs_clock_src_t src;
38 } clock_options[LFCLK_MAX_OPTS] = {
39 {
40 .accuracy = LFCLK_LFLPRC_ACCURACY,
41 .precision = 0,
42 .src = NRFS_CLOCK_SRC_LFCLK_LFLPRC,
43 },
44 {
45 .accuracy = LFCLK_LFRC_ACCURACY,
46 .precision = 0,
47 .src = NRFS_CLOCK_SRC_LFCLK_LFRC,
48 },
49 {
50 /* NRFS will request FLL16M use HFXO in bypass mode if SYNTH src is used */
51 .accuracy = LFCLK_HFXO_ACCURACY,
52 .precision = 1,
53 .src = NRFS_CLOCK_SRC_LFCLK_SYNTH,
54 },
55 };
56
57 struct lfclk_dev_data {
58 STRUCT_CLOCK_CONFIG(lfclk, ARRAY_SIZE(clock_options)) clk_cfg;
59 struct k_timer timer;
60 uint16_t max_accuracy;
61 uint8_t clock_options_cnt;
62 };
63
64 struct lfclk_dev_config {
65 uint32_t fixed_frequency;
66 };
67
clock_evt_handler(nrfs_clock_evt_t const * p_evt,void * context)68 static void clock_evt_handler(nrfs_clock_evt_t const *p_evt, void *context)
69 {
70 struct lfclk_dev_data *dev_data = context;
71 int status = 0;
72
73 k_timer_stop(&dev_data->timer);
74
75 if (p_evt->type == NRFS_CLOCK_EVT_REJECT) {
76 status = -ENXIO;
77 }
78
79 clock_config_update_end(&dev_data->clk_cfg, status);
80 }
81
lfclk_update_timeout_handler(struct k_timer * timer)82 static void lfclk_update_timeout_handler(struct k_timer *timer)
83 {
84 struct lfclk_dev_data *dev_data =
85 CONTAINER_OF(timer, struct lfclk_dev_data, timer);
86
87 clock_config_update_end(&dev_data->clk_cfg, -ETIMEDOUT);
88 }
89
lfclk_work_handler(struct k_work * work)90 static void lfclk_work_handler(struct k_work *work)
91 {
92 struct lfclk_dev_data *dev_data =
93 CONTAINER_OF(work, struct lfclk_dev_data, clk_cfg.work);
94 uint8_t to_activate_idx;
95 nrfs_err_t err;
96
97 to_activate_idx = clock_config_update_begin(work);
98
99 err = nrfs_clock_lfclk_src_set(clock_options[to_activate_idx].src,
100 dev_data);
101 if (err != NRFS_SUCCESS) {
102 clock_config_update_end(&dev_data->clk_cfg, -EIO);
103 } else {
104 k_timer_start(&dev_data->timer, NRFS_CLOCK_TIMEOUT, K_NO_WAIT);
105 }
106 }
107
lfclk_find_mgr(const struct device * dev,const struct nrf_clock_spec * spec)108 static struct onoff_manager *lfclk_find_mgr(const struct device *dev,
109 const struct nrf_clock_spec *spec)
110 {
111 struct lfclk_dev_data *dev_data = dev->data;
112 const struct lfclk_dev_config *dev_config = dev->config;
113 uint16_t accuracy;
114
115 if (!spec) {
116 return &dev_data->clk_cfg.onoff[0].mgr;
117 }
118
119 if (spec->frequency > dev_config->fixed_frequency) {
120 LOG_ERR("invalid frequency");
121 return NULL;
122 }
123
124 accuracy = spec->accuracy == NRF_CLOCK_CONTROL_ACCURACY_MAX
125 ? dev_data->max_accuracy
126 : spec->accuracy;
127
128 for (int i = 0; i < dev_data->clock_options_cnt; ++i) {
129 if ((accuracy &&
130 accuracy < clock_options[i].accuracy) ||
131 spec->precision > clock_options[i].precision) {
132 continue;
133 }
134
135 return &dev_data->clk_cfg.onoff[i].mgr;
136 }
137
138 LOG_ERR("invalid accuracy or precision");
139 return NULL;
140 }
141
api_request_lfclk(const struct device * dev,const struct nrf_clock_spec * spec,struct onoff_client * cli)142 static int api_request_lfclk(const struct device *dev,
143 const struct nrf_clock_spec *spec,
144 struct onoff_client *cli)
145 {
146 struct onoff_manager *mgr = lfclk_find_mgr(dev, spec);
147
148 if (mgr) {
149 return onoff_request(mgr, cli);
150 }
151
152 return -EINVAL;
153 }
154
api_release_lfclk(const struct device * dev,const struct nrf_clock_spec * spec)155 static int api_release_lfclk(const struct device *dev,
156 const struct nrf_clock_spec *spec)
157 {
158 struct onoff_manager *mgr = lfclk_find_mgr(dev, spec);
159
160 if (mgr) {
161 return onoff_release(mgr);
162 }
163
164 return -EINVAL;
165 }
166
api_cancel_or_release_lfclk(const struct device * dev,const struct nrf_clock_spec * spec,struct onoff_client * cli)167 static int api_cancel_or_release_lfclk(const struct device *dev,
168 const struct nrf_clock_spec *spec,
169 struct onoff_client *cli)
170 {
171 struct onoff_manager *mgr = lfclk_find_mgr(dev, spec);
172
173 if (mgr) {
174 return onoff_cancel_or_release(mgr, cli);
175 }
176
177 return -EINVAL;
178 }
179
api_get_rate_lfclk(const struct device * dev,clock_control_subsys_t sys,uint32_t * rate)180 static int api_get_rate_lfclk(const struct device *dev,
181 clock_control_subsys_t sys,
182 uint32_t *rate)
183 {
184 ARG_UNUSED(sys);
185
186 const struct lfclk_dev_config *dev_config = dev->config;
187
188 *rate = dev_config->fixed_frequency;
189
190 return 0;
191 }
192
lfclk_init(const struct device * dev)193 static int lfclk_init(const struct device *dev)
194 {
195 struct lfclk_dev_data *dev_data = dev->data;
196 nrf_bicr_lfosc_mode_t lfosc_mode;
197 nrfs_err_t res;
198
199 res = nrfs_clock_init(clock_evt_handler);
200 if (res != NRFS_SUCCESS) {
201 return -EIO;
202 }
203
204 dev_data->clock_options_cnt = LFCLK_DEF_OPTS;
205
206 lfosc_mode = nrf_bicr_lfosc_mode_get(BICR);
207
208 if (lfosc_mode == NRF_BICR_LFOSC_MODE_UNCONFIGURED ||
209 lfosc_mode == NRF_BICR_LFOSC_MODE_DISABLED) {
210 dev_data->max_accuracy = LFCLK_HFXO_ACCURACY;
211 } else {
212 int ret;
213
214 ret = lfosc_get_accuracy(&dev_data->max_accuracy);
215 if (ret < 0) {
216 LOG_ERR("LFOSC enabled with invalid accuracy");
217 return ret;
218 }
219
220 switch (lfosc_mode) {
221 case NRF_BICR_LFOSC_MODE_CRYSTAL:
222 clock_options[LFCLK_MAX_OPTS - 2].accuracy = dev_data->max_accuracy;
223 clock_options[LFCLK_MAX_OPTS - 2].precision = 0;
224 clock_options[LFCLK_MAX_OPTS - 2].src = NRFS_CLOCK_SRC_LFCLK_XO_PIERCE;
225
226 clock_options[LFCLK_MAX_OPTS - 1].accuracy = dev_data->max_accuracy;
227 clock_options[LFCLK_MAX_OPTS - 1].precision = 1;
228 clock_options[LFCLK_MAX_OPTS - 1].src = NRFS_CLOCK_SRC_LFCLK_XO_PIERCE_HP;
229
230 dev_data->clock_options_cnt += 2;
231 break;
232 case NRF_BICR_LFOSC_MODE_EXTSINE:
233 clock_options[LFCLK_MAX_OPTS - 2].accuracy = dev_data->max_accuracy;
234 clock_options[LFCLK_MAX_OPTS - 2].precision = 0;
235 clock_options[LFCLK_MAX_OPTS - 2].src = NRFS_CLOCK_SRC_LFCLK_XO_EXT_SINE;
236
237 clock_options[LFCLK_MAX_OPTS - 1].accuracy = dev_data->max_accuracy;
238 clock_options[LFCLK_MAX_OPTS - 1].precision = 1;
239 clock_options[LFCLK_MAX_OPTS - 1].src = NRFS_CLOCK_SRC_LFCLK_XO_EXT_SINE_HP;
240
241 dev_data->clock_options_cnt += 2;
242 break;
243 case NRF_BICR_LFOSC_MODE_EXTSQUARE:
244 clock_options[LFCLK_MAX_OPTS - 2].accuracy = dev_data->max_accuracy;
245 clock_options[LFCLK_MAX_OPTS - 2].precision = 0;
246 clock_options[LFCLK_MAX_OPTS - 2].src = NRFS_CLOCK_SRC_LFCLK_XO_EXT_SQUARE;
247
248 dev_data->clock_options_cnt += 1;
249 break;
250 default:
251 LOG_ERR("Unexpected LFOSC mode");
252 return -EINVAL;
253 }
254 }
255
256 k_timer_init(&dev_data->timer, lfclk_update_timeout_handler, NULL);
257
258 return clock_config_init(&dev_data->clk_cfg,
259 ARRAY_SIZE(dev_data->clk_cfg.onoff),
260 lfclk_work_handler);
261 }
262
263 static DEVICE_API(nrf_clock_control, lfclk_drv_api) = {
264 .std_api = {
265 .on = api_nosys_on_off,
266 .off = api_nosys_on_off,
267 .get_rate = api_get_rate_lfclk,
268 },
269 .request = api_request_lfclk,
270 .release = api_release_lfclk,
271 .cancel_or_release = api_cancel_or_release_lfclk,
272 };
273
274 static struct lfclk_dev_data lfclk_data;
275
276 static const struct lfclk_dev_config lfclk_config = {
277 .fixed_frequency = DT_INST_PROP(0, clock_frequency),
278 };
279
280 DEVICE_DT_INST_DEFINE(0, lfclk_init, NULL,
281 &lfclk_data, &lfclk_config,
282 PRE_KERNEL_1, CONFIG_CLOCK_CONTROL_INIT_PRIORITY,
283 &lfclk_drv_api);
284