1 /*
2 * Licensed to the Apache Software Foundation (ASF) under one
3 * or more contributor license agreements. See the NOTICE file
4 * distributed with this work for additional information
5 * regarding copyright ownership. The ASF licenses this file
6 * to you under the Apache License, Version 2.0 (the
7 * "License"); you may not use this file except in compliance
8 * with the License. You may obtain a copy of the License at
9 *
10 * http://www.apache.org/licenses/LICENSE-2.0
11 *
12 * Unless required by applicable law or agreed to in writing,
13 * software distributed under the License is distributed on an
14 * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
15 * KIND, either express or implied. See the License for the
16 * specific language governing permissions and limitations
17 * under the License.
18 */
19
20 #include <assert.h>
21 #include <stdbool.h>
22 #include <stdint.h>
23 #include <DA1469xAB.h>
24 #include <da1469x_config.h>
25 #include <da1469x_clock.h>
26 #include <da1469x_pd.h>
27 #include <da1469x_pdc.h>
28 #include <da1469x_otp.h>
29 #include <da1469x_trimv.h>
30
31 #define XTAL32M_FREQ 32000000
32 #define RC32M_FREQ 32000000
33 #define PLL_FREQ 96000000
34 #define XTAL32K_FREQ 32768
35
36 #define ARRAY_COUNT(_arr) (sizeof(_arr) / sizeof(_arr[0]))
37
38 static uint32_t g_mcu_clock_rcx_freq;
39 static uint32_t g_mcu_clock_rc32k_freq;
40 static uint32_t g_mcu_clock_rc32m_freq;
41
42 uint32_t SystemCoreClock = RC32M_FREQ;
43
44 enum {
45 XTALRDY_CLK_SEL_32KHz = 0x0,
46 XTALRDY_CLK_SEL_256KHz
47 } XTALRDY_CLK_SEL;
48
49 #define CLK_FREQ_TRIM_REG_SET(_field, _val) \
50 CRG_XTAL->CLK_FREQ_TRIM_REG = \
51 ((CRG_XTAL->CLK_FREQ_TRIM_REG & ~CRG_XTAL_CLK_FREQ_TRIM_REG_ ## _field ## _Msk) | \
52 (((_val) << CRG_XTAL_CLK_FREQ_TRIM_REG_ ## _field ## _Pos) & \
53 CRG_XTAL_CLK_FREQ_TRIM_REG_ ## _field ## _Msk))
54
55 #define CLK_FREQ_TRIM_REG_GET(_field) \
56 ((CRG_XTAL->CLK_FREQ_TRIM_REG & CRG_XTAL_CLK_FREQ_TRIM_REG_ ## _field ## _Msk) >> \
57 CRG_XTAL_CLK_FREQ_TRIM_REG_ ## _field ## _Pos)
58
59 #define XTAL32M_CTRL2_REG_SET(_field, _val) \
60 CRG_XTAL->XTAL32M_CTRL2_REG = \
61 ((CRG_XTAL->XTAL32M_CTRL2_REG & ~CRG_XTAL_XTAL32M_CTRL2_REG_ ## _field ## _Msk) | \
62 (((_val) << CRG_XTAL_XTAL32M_CTRL2_REG_ ## _field ## _Pos) & \
63 CRG_XTAL_XTAL32M_CTRL2_REG_ ## _field ## _Msk))
64
65 #define XTAL32M_CTRL2_REG_GET(_field) \
66 ((CRG_XTAL->XTAL32M_CTRL2_REG & CRG_XTAL_XTAL32M_CTRL2_REG_ ## _field ## _Msk) >> \
67 CRG_XTAL_XTAL32M_CTRL2_REG_ ## _field ## _Pos)
68
69 #define XTALRDY_CTRL_REG_SET(_field, _val) \
70 CRG_XTAL->XTALRDY_CTRL_REG = \
71 ((CRG_XTAL->XTALRDY_CTRL_REG & ~CRG_XTAL_XTALRDY_CTRL_REG_ ## _field ## _Msk) | \
72 (((_val) << CRG_XTAL_XTALRDY_CTRL_REG_ ## _field ## _Pos) & \
73 CRG_XTAL_XTALRDY_CTRL_REG_ ## _field ## _Msk))
74
75 #define XTALRDY_CTRL_REG_GET(_field) \
76 ((CRG_XTAL->XTALRDY_CTRL_REG & CRG_XTAL_XTALRDY_CTRL_REG_ ## _field ## _Msk) >> \
77 CRG_XTAL_XTALRDY_CTRL_REG_ ## _field ## _Pos)
78
79 #define XTALRDY_STAT_REG_GET(_field) \
80 ((CRG_XTAL->XTALRDY_STAT_REG & CRG_XTAL_XTALRDY_STAT_REG_ ## _field ## _Msk) >> \
81 CRG_XTAL_XTALRDY_STAT_REG_ ## _field ## _Pos)
82
83 #define COM_DEV_CHECK_DIV1_CLK(_val, _dev) ((_val) & CRG_COM_CLK_COM_REG_ ## _dev ## _CLK_SEL_Msk) && \
84 ((_val) & CRG_COM_CLK_COM_REG_ ## _dev ## _ENABLE_Msk)
85
86 #define SYS_DEV_CHECK_DIV1_CLK(_val, _dev) ((_val) & CRG_SYS_CLK_SYS_REG_ ## _dev ## _CLK_SEL_Msk) && \
87 ((_val) & CRG_SYS_CLK_SYS_REG_ ## _dev ## _ENABLE_Msk)
88
89
90 bool
da1469x_clock_check_device_div1_clock(void)91 da1469x_clock_check_device_div1_clock(void)
92 {
93 uint32_t sys_stat_reg = CRG_TOP->SYS_STAT_REG;
94
95 /*
96 * Exercisie if any block instance powered by PD_COM is enabled
97 * and clocked by DIV1 path (reflects the system clock);
98 */
99 if (sys_stat_reg & CRG_TOP_SYS_STAT_REG_COM_IS_UP_Msk) {
100 uint32_t clk_com_reg = CRG_COM->CLK_COM_REG;
101
102 if (COM_DEV_CHECK_DIV1_CLK(clk_com_reg, I2C)) {
103 return true;
104 }
105
106 if (COM_DEV_CHECK_DIV1_CLK(clk_com_reg, I2C2)) {
107 return true;
108 }
109
110 if (COM_DEV_CHECK_DIV1_CLK(clk_com_reg, SPI)){
111 return true;
112 }
113
114 if (COM_DEV_CHECK_DIV1_CLK(clk_com_reg, SPI2)) {
115 return true;
116 }
117
118 if (COM_DEV_CHECK_DIV1_CLK(clk_com_reg, UART2)) {
119 return true;
120 }
121
122 if (COM_DEV_CHECK_DIV1_CLK(clk_com_reg, UART3)) {
123 return true;
124 }
125 }
126
127 /*
128 * Check if GPADC, which is powered by PD_PER, is enabled and
129 * clocked by DIV1 path.
130 */
131 if (sys_stat_reg & CRG_TOP_SYS_STAT_REG_PER_IS_UP_Msk) {
132 if ((CRG_PER->CLK_PER_REG & CRG_PER_CLK_PER_REG_GPADC_CLK_SEL_Msk) &&
133 (GPADC->GP_ADC_CTRL_REG & GPADC_GP_ADC_CTRL_REG_GP_ADC_EN_Msk)) {
134 return true;
135 }
136 }
137
138 /* Exercise if LCDC is enabled and clocked by DIV1 path */
139 if (SYS_DEV_CHECK_DIV1_CLK(CRG_SYS-> CLK_SYS_REG, LCD)) {
140 return true;
141 }
142
143 return false;
144 }
145
146 /*
147 * OTPC is clocked by the system clock. Therefore, its timing settings
148 * should be adjusted upon system clock update.
149 */
150 static inline void
da1469x_clock_adjust_otp_access_timings(void)151 da1469x_clock_adjust_otp_access_timings(void)
152 {
153 /* Get the high-speed clock divider (AHB bus) */
154 uint8_t hclk_div = (CRG_TOP->CLK_AMBA_REG & CRG_TOP_CLK_AMBA_REG_HCLK_DIV_Msk);
155
156 /* Compute the actual core speed */
157 uint32_t core_speed = (SystemCoreClock >> ((hclk_div < 4) ? hclk_div : 4/*1xx = divide by 16*/));
158
159 da1469x_otp_set_speed(core_speed);
160 }
161
162 static inline bool
da1469x_clock_is_xtal32m_settled(void)163 da1469x_clock_is_xtal32m_settled(void)
164 {
165 return !!((CRG_XTAL->XTALRDY_STAT_REG & CRG_XTAL_XTALRDY_STAT_REG_XTALRDY_COUNT_Msk) == 0 &&
166 (CRG_XTAL->XTAL32M_STAT1_REG & CRG_XTAL_XTAL32M_STAT1_REG_XTAL32M_STATE_Msk) == 0x8 /*XTAL_RUN state*/);
167 }
168
169 static inline bool
da1469x_clock_sys_pll_switch_check_restrictions(void)170 da1469x_clock_sys_pll_switch_check_restrictions(void)
171 {
172 bool ret = 0;
173
174 /* HDIV and PDIV should both be 0 */
175 ret |= !((CRG_TOP->CLK_AMBA_REG & CRG_TOP_CLK_AMBA_REG_HCLK_DIV_Msk) == 0 &&
176 (CRG_TOP->CLK_AMBA_REG & CRG_TOP_CLK_AMBA_REG_PCLK_DIV_Msk) == 0);
177
178
179 /* Switch to PLL is only allowed when the current system clock is XTAL32M */
180 ret |= !((CRG_TOP->CLK_CTRL_REG & CRG_TOP_CLK_CTRL_REG_RUNNING_AT_XTAL32M_Msk) ||
181 (CRG_TOP->CLK_CTRL_REG & CRG_TOP_CLK_CTRL_REG_RUNNING_AT_PLL96M_Msk));
182
183 /* 0 for success and 1 for failure. */
184 return ret;
185 }
186
187 static inline bool
da1469x_clock_sys_xtal32m_switch_check_restrictions(void)188 da1469x_clock_sys_xtal32m_switch_check_restrictions(void)
189 {
190 bool ret = 0;
191
192 /* XTAL32M should be enabled by PDC */
193 ret |= !((CRG_TOP->POWER_CTRL_REG & CRG_TOP_POWER_CTRL_REG_LDO_RADIO_ENABLE_Msk) ||
194 ((DCDC->DCDC_CTRL1_REG & DCDC_DCDC_CTRL1_REG_DCDC_ENABLE_Msk) &&
195 (DCDC->DCDC_V14_REG & DCDC_DCDC_V14_REG_DCDC_V14_ENABLE_HV_Msk) &&
196 (DCDC->DCDC_V14_REG & DCDC_DCDC_V14_REG_DCDC_V14_ENABLE_LV_Msk)));
197
198
199 /* An attemp to switch to an unsteeled crystal might end up in hanging the system clock */
200 ret |= !da1469x_clock_is_xtal32m_settled();
201
202 /* 0 for success and 1 for failure. */
203 return ret;
204 }
205
206 static void
da1469x_clock_sys_xtal32m_configure(void)207 da1469x_clock_sys_xtal32m_configure(void)
208 {
209 assert(CRG_TOP->SYS_STAT_REG & CRG_TOP_SYS_STAT_REG_TIM_IS_UP_Msk);
210
211 /* Apply optimum values for XTAL32M registers not defined in CS section in OTP */
212 static uint32_t regs_buf[] = {
213 (uint32_t)&CRG_XTAL->CLK_FREQ_TRIM_REG
214 };
215
216 bool status_buf[ARRAY_COUNT(regs_buf)];
217
218 da1469x_trimv_is_reg_pairs_in_otp(regs_buf, ARRAY_COUNT(regs_buf), status_buf);
219
220 if (!status_buf[0]) {
221 CLK_FREQ_TRIM_REG_SET(XTAL32M_TRIM, CLK_FREQ_TRIM_REG__XTAL32M_TRIM__DEFAULT);
222 }
223
224 /* Configure OSF BOOST */
225 uint8_t cxcomp_phi_trim = 0;
226 uint8_t cxcomp_trim_cap = XTAL32M_CTRL2_REG_GET(XTAL32M_CXCOMP_TRIM_CAP);
227
228 if (cxcomp_trim_cap < 37) {
229 cxcomp_phi_trim = 3;
230 } else {
231 if (cxcomp_trim_cap < 123) {
232 cxcomp_phi_trim = 2;
233 }
234 else {
235 if (cxcomp_trim_cap < 170) {
236 cxcomp_phi_trim = 1;
237 }
238 else {
239 cxcomp_phi_trim = 0;
240 }
241 }
242 }
243 XTAL32M_CTRL2_REG_SET(XTAL32M_CXCOMP_PHI_TRIM, cxcomp_phi_trim);
244 }
245
246 static void
da1469x_clock_sys_xtal32m_rdy_cnt_finetune(void)247 da1469x_clock_sys_xtal32m_rdy_cnt_finetune(void)
248 {
249 #define XTALRDY_CTRL_REG_XTALRDY_CNT_MIN_LIMIT ( 4 )
250 #define XTALRDY_CTRL_REG_XTALRDY_CNT_OFFSET ( 3 )
251
252 if (XTALRDY_CTRL_REG_GET(XTALRDY_CLK_SEL) == XTALRDY_CLK_SEL_32KHz) {
253 if (CRG_XTAL->XTAL32M_STAT1_REG & 0x100UL) {
254 int16_t xtalrdy_cnt = XTALRDY_CTRL_REG_GET(XTALRDY_CNT);
255 int16_t xtalrdy_stat = XTALRDY_CTRL_REG_XTALRDY_CNT_OFFSET - XTALRDY_STAT_REG_GET(XTALRDY_STAT);
256 xtalrdy_cnt += xtalrdy_stat;
257
258 if (xtalrdy_cnt < XTALRDY_CTRL_REG_XTALRDY_CNT_MIN_LIMIT) {
259 xtalrdy_cnt = XTALRDY_CTRL_REG_XTALRDY_CNT_MIN_LIMIT;
260 }
261 XTALRDY_CTRL_REG_SET(XTALRDY_CNT, xtalrdy_cnt);
262 }
263 }
264 }
265
266 void
da1469x_clock_sys_xtal32m_init(void)267 da1469x_clock_sys_xtal32m_init(void)
268 {
269 da1469x_clock_sys_xtal32m_configure();
270
271 /* Select normal XTAL32M start-up */
272 XTALRDY_CTRL_REG_SET(XTALRDY_CLK_SEL, XTALRDY_CLK_SEL_32KHz);
273 }
274
275 void
da1469x_clock_sys_xtal32m_enable(void)276 da1469x_clock_sys_xtal32m_enable(void)
277 {
278 int idx;
279
280 idx = da1469x_pdc_find(MCU_PDC_TRIGGER_SW_TRIGGER, MCU_PDC_MASTER_M33,
281 MCU_PDC_EN_XTAL);
282 if (idx < 0) {
283 idx = da1469x_pdc_add(MCU_PDC_TRIGGER_SW_TRIGGER, MCU_PDC_MASTER_M33,
284 MCU_PDC_EN_XTAL);
285 }
286 assert(idx >= 0);
287
288 da1469x_pdc_set(idx);
289 da1469x_pdc_ack(idx);
290
291 /* PDC will now take care of XTAL32M. Make sure that PDC is able to turn it off when going to sleep */
292 CRG_XTAL->XTAL32M_CTRL1_REG &= ~(CRG_XTAL_XTAL32M_CTRL1_REG_XTAL32M_XTAL_ENABLE_Msk);
293 }
294
295 void
da1469x_clock_sys_xtal32m_switch(void)296 da1469x_clock_sys_xtal32m_switch(void)
297 {
298 if (CRG_TOP->CLK_CTRL_REG & CRG_TOP_CLK_CTRL_REG_RUNNING_AT_RC32M_Msk) {
299 assert(da1469x_clock_sys_xtal32m_switch_check_restrictions() == 0);
300 CRG_TOP->CLK_SWITCH2XTAL_REG = CRG_TOP_CLK_SWITCH2XTAL_REG_SWITCH2XTAL_Msk;
301 } else {
302 /* ~CLK_SEL_Msk == 0 means XTAL32M */
303 CRG_TOP->CLK_CTRL_REG &= ~CRG_TOP_CLK_CTRL_REG_SYS_CLK_SEL_Msk;
304 }
305
306 while (!(CRG_TOP->CLK_CTRL_REG & CRG_TOP_CLK_CTRL_REG_RUNNING_AT_XTAL32M_Msk));
307
308 SystemCoreClock = XTAL32M_FREQ;
309
310 da1469x_clock_adjust_otp_access_timings();
311 }
312
313 void
da1469x_clock_sys_xtal32m_wait_to_settle(void)314 da1469x_clock_sys_xtal32m_wait_to_settle(void)
315 {
316 uint32_t primask;
317
318 primask = DA1469X_IRQ_DISABLE();
319
320 NVIC_ClearPendingIRQ(XTAL32M_RDY_IRQn);
321
322 if (!da1469x_clock_is_xtal32m_settled()) {
323 NVIC_EnableIRQ(XTAL32M_RDY_IRQn);
324 while (!NVIC_GetPendingIRQ(XTAL32M_RDY_IRQn)) {
325 __WFE();
326 __SEV();
327 __WFE();
328 }
329 NVIC_DisableIRQ(XTAL32M_RDY_IRQn);
330 }
331
332 /* XTALM32M_RDY_IRQn should be fired. The XTAL32M ready counter can be fine tuned. */
333 da1469x_clock_sys_xtal32m_rdy_cnt_finetune();
334
335 DA1469X_IRQ_ENABLE(primask);
336 }
337
338 void
da1469x_clock_sys_xtal32m_switch_safe(void)339 da1469x_clock_sys_xtal32m_switch_safe(void)
340 {
341 da1469x_clock_sys_xtal32m_wait_to_settle();
342
343 da1469x_clock_sys_xtal32m_switch();
344 }
345
346 void
da1469x_clock_sys_rc32m_disable(void)347 da1469x_clock_sys_rc32m_disable(void)
348 {
349 CRG_TOP->CLK_RC32M_REG &= ~CRG_TOP_CLK_RC32M_REG_RC32M_ENABLE_Msk;
350 }
351
352 void
da1469x_clock_lp_xtal32k_enable(void)353 da1469x_clock_lp_xtal32k_enable(void)
354 {
355 CRG_TOP->CLK_XTAL32K_REG |= CRG_TOP_CLK_XTAL32K_REG_XTAL32K_ENABLE_Msk;
356 }
357
358 void
da1469x_clock_lp_xtal32k_switch(void)359 da1469x_clock_lp_xtal32k_switch(void)
360 {
361 CRG_TOP->CLK_CTRL_REG = (CRG_TOP->CLK_CTRL_REG &
362 ~CRG_TOP_CLK_CTRL_REG_LP_CLK_SEL_Msk) |
363 (2 << CRG_TOP_CLK_CTRL_REG_LP_CLK_SEL_Pos);
364 }
365
366 void
da1469x_clock_lp_rcx_enable(void)367 da1469x_clock_lp_rcx_enable(void)
368 {
369 CRG_TOP->CLK_RCX_REG |= CRG_TOP_CLK_RCX_REG_RCX_ENABLE_Msk;
370 }
371
372 void
da1469x_clock_lp_rcx_switch(void)373 da1469x_clock_lp_rcx_switch(void)
374 {
375 CRG_TOP->CLK_CTRL_REG = (CRG_TOP->CLK_CTRL_REG &
376 ~CRG_TOP_CLK_CTRL_REG_LP_CLK_SEL_Msk) |
377 (1 << CRG_TOP_CLK_CTRL_REG_LP_CLK_SEL_Pos);
378 }
379
380 /**
381 * Measure clock frequency
382 *
383 * @param clock_sel - clock to measure
384 * 0 - RC32K
385 * 1 - RC32M
386 * 2 - XTAL32K
387 * 3 - RCX
388 * 4 - RCOSC
389 * @param ref_cnt - number of cycles used for measurement.
390 * @return clock frequency
391 */
392 static uint32_t
da1469x_clock_calibrate(uint8_t clock_sel,uint16_t ref_cnt)393 da1469x_clock_calibrate(uint8_t clock_sel, uint16_t ref_cnt)
394 {
395 uint32_t ref_val;
396
397 da1469x_pd_acquire(MCU_PD_DOMAIN_PER);
398
399 /* Clock reference is hardcoded to DIVN so make sure that XTAL32M is settled. */
400 assert(da1469x_clock_is_xtal32m_settled());
401 /*
402 * Make sure calibration is not employed by multiple calibrations tasks at the same time.
403 * Developers are responsible to ensure that two or more calibration tasks do not overlap
404 * each other.
405 */
406 assert(!(ANAMISC_BIF->CLK_REF_SEL_REG & ANAMISC_BIF_CLK_REF_SEL_REG_REF_CAL_START_Msk));
407
408 ANAMISC_BIF->CLK_REF_CNT_REG = ref_cnt;
409 ANAMISC_BIF->CLK_REF_SEL_REG = (clock_sel << ANAMISC_BIF_CLK_REF_SEL_REG_REF_CLK_SEL_Pos) |
410 ANAMISC_BIF_CLK_REF_SEL_REG_REF_CAL_START_Msk;
411
412 while (ANAMISC_BIF->CLK_REF_SEL_REG & ANAMISC_BIF_CLK_REF_SEL_REG_REF_CAL_START_Msk);
413
414 ref_val = ANAMISC_BIF->CLK_REF_VAL_REG;
415
416 da1469x_pd_release(MCU_PD_DOMAIN_PER);
417
418 return 32000000 * ref_cnt / ref_val;
419 }
420
421 void
da1469x_clock_lp_rcx_calibrate(void)422 da1469x_clock_lp_rcx_calibrate(void)
423 {
424 g_mcu_clock_rcx_freq = da1469x_clock_calibrate(3, MCU_RCX_CAL_REF_CNT);
425 }
426
427 #define RC32K_TARGET_FREQ 32000
428 #define RC32K_TRIM_MIN 0
429 #define RC32K_TRIM_MAX 15
430
431 static inline uint32_t
rc32k_trim_get(void)432 rc32k_trim_get(void)
433 {
434 return (CRG_TOP->CLK_RC32K_REG & CRG_TOP_CLK_RC32K_REG_RC32K_TRIM_Msk) >>
435 CRG_TOP_CLK_RC32K_REG_RC32K_TRIM_Pos;
436 }
437
438 static inline void
rc32k_trim_set(uint32_t trim)439 rc32k_trim_set(uint32_t trim)
440 {
441 CRG_TOP->CLK_RC32K_REG =
442 (CRG_TOP->CLK_RC32K_REG & ~CRG_TOP_CLK_RC32K_REG_RC32K_TRIM_Msk) |
443 (trim << CRG_TOP_CLK_RC32K_REG_RC32K_TRIM_Pos);
444 }
445
446 void
da1469x_clock_lp_rc32k_calibrate(void)447 da1469x_clock_lp_rc32k_calibrate(void)
448 {
449 uint32_t trim;
450 uint32_t trim_prev;
451 uint32_t freq;
452 uint32_t freq_prev;
453 uint32_t freq_delta;
454 uint32_t freq_delta_prev;
455 bool trim_ok;
456
457 if (!(CRG_TOP->CLK_RC32K_REG & CRG_TOP_CLK_RC32K_REG_RC32K_ENABLE_Msk)) {
458 return;
459 }
460
461 freq = 0;
462 freq_delta = INT32_MAX;
463
464 trim = rc32k_trim_get();
465 trim_prev = trim;
466 trim_ok = false;
467
468 do {
469 freq_prev = freq;
470 freq_delta_prev = freq_delta;
471
472 freq = da1469x_clock_calibrate(0, 1);
473
474 freq_delta = freq - RC32K_TARGET_FREQ;
475 freq_delta = (int32_t)freq_delta < 0 ? -freq_delta : freq_delta;
476
477 if (freq_delta > freq_delta_prev) {
478 /* Previous trim value was closer to target frequency, use it */
479 freq = freq_prev;
480 rc32k_trim_set(trim_prev);
481 trim_ok = true;
482 } else if (freq > RC32K_TARGET_FREQ) {
483 /* Decrease trim value if possible */
484 if (trim > RC32K_TRIM_MIN) {
485 trim_prev = trim;
486 rc32k_trim_set(--trim);
487 } else {
488 trim_ok = true;
489 }
490 } else if (freq < RC32K_TARGET_FREQ) {
491 /* Increase trim value if possible */
492 if (trim < RC32K_TRIM_MAX) {
493 trim_prev = trim;
494 rc32k_trim_set(++trim);
495 } else {
496 trim_ok = true;
497 }
498 } else {
499 trim_ok = true;
500 }
501 } while (!trim_ok);
502
503 g_mcu_clock_rc32k_freq = freq;
504 }
505
506 void
da1469x_clock_lp_rc32m_calibrate(void)507 da1469x_clock_lp_rc32m_calibrate(void)
508 {
509 g_mcu_clock_rc32m_freq = da1469x_clock_calibrate(1, 100);
510 }
511
512 uint32_t
da1469x_clock_lp_rcx_freq_get(void)513 da1469x_clock_lp_rcx_freq_get(void)
514 {
515 assert(g_mcu_clock_rcx_freq);
516
517 return g_mcu_clock_rcx_freq;
518 }
519
520 uint32_t
da1469x_clock_lp_rc32k_freq_get(void)521 da1469x_clock_lp_rc32k_freq_get(void)
522 {
523 assert(g_mcu_clock_rc32k_freq);
524
525 return g_mcu_clock_rc32k_freq;
526 }
527
528 uint32_t
da1469x_clock_lp_rc32m_freq_get(void)529 da1469x_clock_lp_rc32m_freq_get(void)
530 {
531 assert(g_mcu_clock_rc32m_freq);
532
533 return g_mcu_clock_rc32m_freq;
534 }
535
536 void
da1469x_clock_lp_rcx_disable(void)537 da1469x_clock_lp_rcx_disable(void)
538 {
539 CRG_TOP->CLK_RCX_REG &= ~CRG_TOP_CLK_RCX_REG_RCX_ENABLE_Msk;
540 }
541
542 static void
da1469x_delay_us(uint32_t delay_us)543 da1469x_delay_us(uint32_t delay_us)
544 {
545 /*
546 * SysTick runs on ~32 MHz clock while PLL is not started.
547 * so multiplying by 32 to convert from us to SysTicks.
548 */
549 SysTick->LOAD = delay_us * 32;
550 SysTick->VAL = 0UL;
551 SysTick->CTRL = SysTick_CTRL_CLKSOURCE_Msk | SysTick_CTRL_ENABLE_Msk;
552 while (0 == (SysTick->CTRL & SysTick_CTRL_COUNTFLAG_Msk));
553 SysTick->CTRL = 0;
554 }
555
556 /**
557 * Enable PLL96
558 */
559 void
da1469x_clock_sys_pll_enable(void)560 da1469x_clock_sys_pll_enable(void)
561 {
562 /* VDD voltage should be to 1.2V prior to enabling PLL (VDD_LEVEL_Msk means 1.2V) */
563 assert((CRG_TOP->POWER_CTRL_REG & CRG_TOP_POWER_CTRL_REG_VDD_LEVEL_Msk)
564 == CRG_TOP_POWER_CTRL_REG_VDD_LEVEL_Msk);
565
566 /* Start PLL LDO if not done yet */
567 if ((CRG_XTAL->PLL_SYS_STATUS_REG & CRG_XTAL_PLL_SYS_STATUS_REG_LDO_PLL_OK_Msk) == 0) {
568 CRG_XTAL->PLL_SYS_CTRL1_REG |= CRG_XTAL_PLL_SYS_CTRL1_REG_LDO_PLL_ENABLE_Msk;
569 /* Wait for XTAL LDO to settle */
570 da1469x_delay_us(20);
571 }
572 if ((CRG_XTAL->PLL_SYS_STATUS_REG & CRG_XTAL_PLL_SYS_STATUS_REG_PLL_LOCK_FINE_Msk) == 0) {
573 /* Enables DXTAL for the system PLL */
574 CRG_XTAL->XTAL32M_CTRL0_REG |= CRG_XTAL_XTAL32M_CTRL0_REG_XTAL32M_DXTAL_SYSPLL_ENABLE_Msk;
575 /* Use internal VCO current setting to enable precharge */
576 CRG_XTAL->PLL_SYS_CTRL1_REG |= CRG_XTAL_PLL_SYS_CTRL1_REG_PLL_SEL_MIN_CUR_INT_Msk;
577 /* Enable precharge */
578 CRG_XTAL->PLL_SYS_CTRL2_REG |= CRG_XTAL_PLL_SYS_CTRL2_REG_PLL_RECALIB_Msk;
579 /* Start the SYSPLL */
580 CRG_XTAL->PLL_SYS_CTRL1_REG |= CRG_XTAL_PLL_SYS_CTRL1_REG_PLL_EN_Msk;
581 /* Precharge loopfilter (Vtune) */
582 da1469x_delay_us(10);
583 /* Disable precharge */
584 CRG_XTAL->PLL_SYS_CTRL2_REG &= ~CRG_XTAL_PLL_SYS_CTRL2_REG_PLL_RECALIB_Msk;
585 /* Extra wait time */
586 da1469x_delay_us(5);
587 /* Take external VCO current setting */
588 CRG_XTAL->PLL_SYS_CTRL1_REG &= ~CRG_XTAL_PLL_SYS_CTRL1_REG_PLL_SEL_MIN_CUR_INT_Msk;
589 }
590 }
591
592 void
da1469x_clock_sys_pll_disable(void)593 da1469x_clock_sys_pll_disable(void)
594 {
595 /* Switch from PLL is only allowed when the new system clock is XTAL32M */
596 da1469x_clock_sys_xtal32m_switch();
597
598 CRG_XTAL->PLL_SYS_CTRL1_REG &= ~(CRG_XTAL_PLL_SYS_CTRL1_REG_PLL_EN_Msk |
599 CRG_XTAL_PLL_SYS_CTRL1_REG_LDO_PLL_ENABLE_Msk);
600
601 CRG_XTAL->XTAL32M_CTRL0_REG &= ~(CRG_XTAL_XTAL32M_CTRL0_REG_XTAL32M_DXTAL_SYSPLL_ENABLE_Msk);
602 }
603
604 static void
da1469x_clock_pll_wait_to_lock(void)605 da1469x_clock_pll_wait_to_lock(void)
606 {
607 uint32_t primask;
608
609 primask = DA1469X_IRQ_DISABLE();
610
611 NVIC_ClearPendingIRQ(PLL_LOCK_IRQn);
612
613 if (!da1469x_clock_is_pll_locked()) {
614 NVIC_EnableIRQ(PLL_LOCK_IRQn);
615 while (!NVIC_GetPendingIRQ(PLL_LOCK_IRQn)) {
616 /*
617 * Wait for event and not for interrupt as the PLL lock interrupt
618 * might fire right after checking its pending flag.
619 * In such a case, and if there are no other interrupts expected
620 * the SoC will not exit the idle state.
621 */
622 __WFE();
623 /* Clear ARM's internal event register. */
624 __SEV();
625 __WFE();
626 }
627 NVIC_DisableIRQ(PLL_LOCK_IRQn);
628
629 /*
630 * Due to a metastability issue, the PLL lock interrupt may be fired earlier
631 * and before the PLL is actually locked. Therefore, an attempt to switch to
632 * an unlocked PLL might end up hanging the system clock.
633 * Make sure that PLL is locked by polling the corresponding status register
634 * as the actual time interval between two successive PLL lock interrupts is
635 * uncertain.
636 */
637 while (!da1469x_clock_is_pll_locked());
638 }
639
640 DA1469X_IRQ_ENABLE(primask);
641 }
642
643 void
da1469x_clock_sys_pll_switch(void)644 da1469x_clock_sys_pll_switch(void)
645 {
646 assert(da1469x_clock_sys_pll_switch_check_restrictions() == 0);
647
648 da1469x_clock_pll_wait_to_lock();
649
650 /* CLK_SEL_Msk == 3 means PLL */
651 CRG_TOP->CLK_CTRL_REG |= CRG_TOP_CLK_CTRL_REG_SYS_CLK_SEL_Msk;
652
653 while (!(CRG_TOP->CLK_CTRL_REG & CRG_TOP_CLK_CTRL_REG_RUNNING_AT_PLL96M_Msk));
654
655 SystemCoreClock = PLL_FREQ;
656
657 da1469x_clock_adjust_otp_access_timings();
658 }
659