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 /*
84 OTPC is clocked by the system clock. Therefore, its timing settings
85 should be adjusted upon system clock update.
86 */
87 static inline void
da1469x_clock_adjust_otp_access_timings(void)88 da1469x_clock_adjust_otp_access_timings(void)
89 {
90 /* Get the high-speed clock divider (AHB bus) */
91 uint8_t hclk_div = (CRG_TOP->CLK_AMBA_REG & CRG_TOP_CLK_AMBA_REG_HCLK_DIV_Msk);
92
93 /* Compute the actual core speed */
94 uint32_t core_speed = (SystemCoreClock >> ((hclk_div < 4) ? hclk_div : 4/*1xx = divide by 16*/));
95
96 da1469x_otp_set_speed(core_speed);
97 }
98
99 static inline bool
da1469x_clock_is_xtal32m_settled(void)100 da1469x_clock_is_xtal32m_settled(void)
101 {
102 return !!((CRG_XTAL->XTALRDY_STAT_REG & CRG_XTAL_XTALRDY_STAT_REG_XTALRDY_COUNT_Msk) == 0 &&
103 (CRG_XTAL->XTAL32M_STAT1_REG & CRG_XTAL_XTAL32M_STAT1_REG_XTAL32M_STATE_Msk) == 0x8 /*XTAL_RUN state*/);
104 }
105
106 static inline bool
da1469x_clock_sys_pll_switch_check_restrictions(void)107 da1469x_clock_sys_pll_switch_check_restrictions(void)
108 {
109 bool ret = 0;
110
111 /* HDIV and PDIV should both be 0 */
112 ret |= !((CRG_TOP->CLK_AMBA_REG & CRG_TOP_CLK_AMBA_REG_HCLK_DIV_Msk) == 0 &&
113 (CRG_TOP->CLK_AMBA_REG & CRG_TOP_CLK_AMBA_REG_PCLK_DIV_Msk) == 0);
114
115
116 /* Switch to PLL is only allowed when the current system clock is XTAL32M */
117 ret |= !((CRG_TOP->CLK_CTRL_REG & CRG_TOP_CLK_CTRL_REG_RUNNING_AT_XTAL32M_Msk) ||
118 (CRG_TOP->CLK_CTRL_REG & CRG_TOP_CLK_CTRL_REG_RUNNING_AT_PLL96M_Msk));
119
120 /* 0 for success and 1 for failure. */
121 return ret;
122 }
123
124 static inline bool
da1469x_clock_sys_xtal32m_switch_check_restrictions(void)125 da1469x_clock_sys_xtal32m_switch_check_restrictions(void)
126 {
127 bool ret = 0;
128
129 /* XTAL32M should be enabled by PDC */
130 ret |= !((CRG_TOP->POWER_CTRL_REG & CRG_TOP_POWER_CTRL_REG_LDO_RADIO_ENABLE_Msk) ||
131 ((DCDC->DCDC_CTRL1_REG & DCDC_DCDC_CTRL1_REG_DCDC_ENABLE_Msk) &&
132 (DCDC->DCDC_V14_REG & DCDC_DCDC_V14_REG_DCDC_V14_ENABLE_HV_Msk) &&
133 (DCDC->DCDC_V14_REG & DCDC_DCDC_V14_REG_DCDC_V14_ENABLE_LV_Msk)));
134
135
136 /* An attemp to switch to an unsteeled crystal might end up in hanging the system clock */
137 ret |= !da1469x_clock_is_xtal32m_settled();
138
139 /* 0 for success and 1 for failure. */
140 return ret;
141 }
142
143 static void
da1469x_clock_sys_xtal32m_configure(void)144 da1469x_clock_sys_xtal32m_configure(void)
145 {
146 assert(CRG_TOP->SYS_STAT_REG & CRG_TOP_SYS_STAT_REG_TIM_IS_UP_Msk);
147
148 /* Apply optimum values for XTAL32M registers not defined in CS section in OTP */
149 static uint32_t regs_buf[] = {
150 (uint32_t)&CRG_XTAL->CLK_FREQ_TRIM_REG
151 };
152
153 bool status_buf[ARRAY_COUNT(regs_buf)];
154
155 da1469x_trimv_is_reg_pairs_in_otp(regs_buf, ARRAY_COUNT(regs_buf), status_buf);
156
157 if (!status_buf[0]) {
158 CLK_FREQ_TRIM_REG_SET(XTAL32M_TRIM, CLK_FREQ_TRIM_REG__XTAL32M_TRIM__DEFAULT);
159 }
160
161 /* Configure OSF BOOST */
162 uint8_t cxcomp_phi_trim = 0;
163 uint8_t cxcomp_trim_cap = XTAL32M_CTRL2_REG_GET(XTAL32M_CXCOMP_TRIM_CAP);
164
165 if (cxcomp_trim_cap < 37) {
166 cxcomp_phi_trim = 3;
167 } else {
168 if (cxcomp_trim_cap < 123) {
169 cxcomp_phi_trim = 2;
170 }
171 else {
172 if (cxcomp_trim_cap < 170) {
173 cxcomp_phi_trim = 1;
174 }
175 else {
176 cxcomp_phi_trim = 0;
177 }
178 }
179 }
180 XTAL32M_CTRL2_REG_SET(XTAL32M_CXCOMP_PHI_TRIM, cxcomp_phi_trim);
181 }
182
183 static void
da1469x_clock_sys_xtal32m_rdy_cnt_finetune(void)184 da1469x_clock_sys_xtal32m_rdy_cnt_finetune(void)
185 {
186 #define XTALRDY_CTRL_REG_XTALRDY_CNT_MIN_LIMIT ( 4 )
187 #define XTALRDY_CTRL_REG_XTALRDY_CNT_OFFSET ( 3 )
188
189 if (XTALRDY_CTRL_REG_GET(XTALRDY_CLK_SEL) == XTALRDY_CLK_SEL_32KHz) {
190 if (CRG_XTAL->XTAL32M_STAT1_REG & 0x100UL) {
191 int16_t xtalrdy_cnt = XTALRDY_CTRL_REG_GET(XTALRDY_CNT);
192 int16_t xtalrdy_stat = XTALRDY_CTRL_REG_XTALRDY_CNT_OFFSET - XTALRDY_STAT_REG_GET(XTALRDY_STAT);
193 xtalrdy_cnt += xtalrdy_stat;
194
195 if (xtalrdy_cnt < XTALRDY_CTRL_REG_XTALRDY_CNT_MIN_LIMIT) {
196 xtalrdy_cnt = XTALRDY_CTRL_REG_XTALRDY_CNT_MIN_LIMIT;
197 }
198 XTALRDY_CTRL_REG_SET(XTALRDY_CNT, xtalrdy_cnt);
199 }
200 }
201 }
202
203 void
da1469x_clock_sys_xtal32m_init(void)204 da1469x_clock_sys_xtal32m_init(void)
205 {
206 da1469x_clock_sys_xtal32m_configure();
207
208 /* Select normal XTAL32M start-up */
209 XTALRDY_CTRL_REG_SET(XTALRDY_CLK_SEL, XTALRDY_CLK_SEL_32KHz);
210 }
211
212 void
da1469x_clock_sys_xtal32m_enable(void)213 da1469x_clock_sys_xtal32m_enable(void)
214 {
215 int idx;
216
217 idx = da1469x_pdc_find(MCU_PDC_TRIGGER_SW_TRIGGER, MCU_PDC_MASTER_M33,
218 MCU_PDC_EN_XTAL);
219 if (idx < 0) {
220 idx = da1469x_pdc_add(MCU_PDC_TRIGGER_SW_TRIGGER, MCU_PDC_MASTER_M33,
221 MCU_PDC_EN_XTAL);
222 }
223 assert(idx >= 0);
224
225 da1469x_pdc_set(idx);
226 da1469x_pdc_ack(idx);
227
228 /* PDC will now take care of XTAL32M. Make sure that PDC is able to turn it off when going to sleep */
229 CRG_XTAL->XTAL32M_CTRL1_REG &= ~(CRG_XTAL_XTAL32M_CTRL1_REG_XTAL32M_XTAL_ENABLE_Msk);
230 }
231
232 void
da1469x_clock_sys_xtal32m_switch(void)233 da1469x_clock_sys_xtal32m_switch(void)
234 {
235 if (CRG_TOP->CLK_CTRL_REG & CRG_TOP_CLK_CTRL_REG_RUNNING_AT_RC32M_Msk) {
236 assert(da1469x_clock_sys_xtal32m_switch_check_restrictions() == 0);
237
238 CRG_TOP->CLK_SWITCH2XTAL_REG = CRG_TOP_CLK_SWITCH2XTAL_REG_SWITCH2XTAL_Msk;
239 } else {
240 /* ~CLK_SEL_Msk == 0 means XTAL32M */
241 CRG_TOP->CLK_CTRL_REG &= ~CRG_TOP_CLK_CTRL_REG_SYS_CLK_SEL_Msk;
242 }
243
244 while (!(CRG_TOP->CLK_CTRL_REG & CRG_TOP_CLK_CTRL_REG_RUNNING_AT_XTAL32M_Msk));
245
246 SystemCoreClock = XTAL32M_FREQ;
247
248 da1469x_clock_adjust_otp_access_timings();
249 }
250
251 void
da1469x_clock_sys_xtal32m_wait_to_settle(void)252 da1469x_clock_sys_xtal32m_wait_to_settle(void)
253 {
254 uint32_t primask;
255
256 primask = DA1469X_IRQ_DISABLE();
257
258 NVIC_ClearPendingIRQ(XTAL32M_RDY_IRQn);
259
260 if (!da1469x_clock_is_xtal32m_settled()) {
261 NVIC_EnableIRQ(XTAL32M_RDY_IRQn);
262 while (!NVIC_GetPendingIRQ(XTAL32M_RDY_IRQn)) {
263 __WFE();
264 __SEV();
265 __WFE();
266 }
267 NVIC_DisableIRQ(XTAL32M_RDY_IRQn);
268 }
269
270 /* XTALM32M_RDY_IRQn should be fired. The XTAL32M ready counter can be fine tuned. */
271 da1469x_clock_sys_xtal32m_rdy_cnt_finetune();
272
273 DA1469X_IRQ_ENABLE(primask);
274 }
275
276 void
da1469x_clock_sys_xtal32m_switch_safe(void)277 da1469x_clock_sys_xtal32m_switch_safe(void)
278 {
279 da1469x_clock_sys_xtal32m_wait_to_settle();
280
281 da1469x_clock_sys_xtal32m_switch();
282 }
283
284 void
da1469x_clock_sys_rc32m_disable(void)285 da1469x_clock_sys_rc32m_disable(void)
286 {
287 CRG_TOP->CLK_RC32M_REG &= ~CRG_TOP_CLK_RC32M_REG_RC32M_ENABLE_Msk;
288 }
289
290 void
da1469x_clock_lp_xtal32k_enable(void)291 da1469x_clock_lp_xtal32k_enable(void)
292 {
293 CRG_TOP->CLK_XTAL32K_REG |= CRG_TOP_CLK_XTAL32K_REG_XTAL32K_ENABLE_Msk;
294 }
295
296 void
da1469x_clock_lp_xtal32k_switch(void)297 da1469x_clock_lp_xtal32k_switch(void)
298 {
299 CRG_TOP->CLK_CTRL_REG = (CRG_TOP->CLK_CTRL_REG &
300 ~CRG_TOP_CLK_CTRL_REG_LP_CLK_SEL_Msk) |
301 (2 << CRG_TOP_CLK_CTRL_REG_LP_CLK_SEL_Pos);
302 }
303
304 void
da1469x_clock_lp_rcx_enable(void)305 da1469x_clock_lp_rcx_enable(void)
306 {
307 CRG_TOP->CLK_RCX_REG |= CRG_TOP_CLK_RCX_REG_RCX_ENABLE_Msk;
308 }
309
310 void
da1469x_clock_lp_rcx_switch(void)311 da1469x_clock_lp_rcx_switch(void)
312 {
313 CRG_TOP->CLK_CTRL_REG = (CRG_TOP->CLK_CTRL_REG &
314 ~CRG_TOP_CLK_CTRL_REG_LP_CLK_SEL_Msk) |
315 (1 << CRG_TOP_CLK_CTRL_REG_LP_CLK_SEL_Pos);
316 }
317
318 /**
319 * Measure clock frequency
320 *
321 * @param clock_sel - clock to measure
322 * 0 - RC32K
323 * 1 - RC32M
324 * 2 - XTAL32K
325 * 3 - RCX
326 * 4 - RCOSC
327 * @param ref_cnt - number of cycles used for measurement.
328 * @return clock frequency
329 */
330 static uint32_t
da1469x_clock_calibrate(uint8_t clock_sel,uint16_t ref_cnt)331 da1469x_clock_calibrate(uint8_t clock_sel, uint16_t ref_cnt)
332 {
333 uint32_t ref_val;
334
335 da1469x_pd_acquire(MCU_PD_DOMAIN_PER);
336
337 /* Clock reference is hardcoded to DIVN so make sure that XTAL32M is settled. */
338 assert(da1469x_clock_is_xtal32m_settled());
339 /*
340 * Make sure calibration is not employed by multiple calibrations tasks at the same time.
341 * Developers are responsible to ensure that two or more calibration tasks do not overlap
342 * each other.
343 */
344 assert(!(ANAMISC_BIF->CLK_REF_SEL_REG & ANAMISC_BIF_CLK_REF_SEL_REG_REF_CAL_START_Msk));
345
346 ANAMISC_BIF->CLK_REF_CNT_REG = ref_cnt;
347 ANAMISC_BIF->CLK_REF_SEL_REG = (clock_sel << ANAMISC_BIF_CLK_REF_SEL_REG_REF_CLK_SEL_Pos) |
348 ANAMISC_BIF_CLK_REF_SEL_REG_REF_CAL_START_Msk;
349
350 while (ANAMISC_BIF->CLK_REF_SEL_REG & ANAMISC_BIF_CLK_REF_SEL_REG_REF_CAL_START_Msk);
351
352 ref_val = ANAMISC_BIF->CLK_REF_VAL_REG;
353
354 da1469x_pd_release(MCU_PD_DOMAIN_PER);
355
356 return 32000000 * ref_cnt / ref_val;
357 }
358
359 void
da1469x_clock_lp_rcx_calibrate(void)360 da1469x_clock_lp_rcx_calibrate(void)
361 {
362 g_mcu_clock_rcx_freq = da1469x_clock_calibrate(3, MCU_RCX_CAL_REF_CNT);
363 }
364
365 #define RC32K_TARGET_FREQ 32000
366 #define RC32K_TRIM_MIN 0
367 #define RC32K_TRIM_MAX 15
368
369 static inline uint32_t
rc32k_trim_get(void)370 rc32k_trim_get(void)
371 {
372 return (CRG_TOP->CLK_RC32K_REG & CRG_TOP_CLK_RC32K_REG_RC32K_TRIM_Msk) >>
373 CRG_TOP_CLK_RC32K_REG_RC32K_TRIM_Pos;
374 }
375
376 static inline void
rc32k_trim_set(uint32_t trim)377 rc32k_trim_set(uint32_t trim)
378 {
379 CRG_TOP->CLK_RC32K_REG =
380 (CRG_TOP->CLK_RC32K_REG & ~CRG_TOP_CLK_RC32K_REG_RC32K_TRIM_Msk) |
381 (trim << CRG_TOP_CLK_RC32K_REG_RC32K_TRIM_Pos);
382 }
383
384 void
da1469x_clock_lp_rc32k_calibrate(void)385 da1469x_clock_lp_rc32k_calibrate(void)
386 {
387 uint32_t trim;
388 uint32_t trim_prev;
389 uint32_t freq;
390 uint32_t freq_prev;
391 uint32_t freq_delta;
392 uint32_t freq_delta_prev;
393 bool trim_ok;
394
395 if (!(CRG_TOP->CLK_RC32K_REG & CRG_TOP_CLK_RC32K_REG_RC32K_ENABLE_Msk)) {
396 return;
397 }
398
399 freq = 0;
400 freq_delta = INT32_MAX;
401
402 trim = rc32k_trim_get();
403 trim_prev = trim;
404 trim_ok = false;
405
406 do {
407 freq_prev = freq;
408 freq_delta_prev = freq_delta;
409
410 freq = da1469x_clock_calibrate(0, 1);
411
412 freq_delta = freq - RC32K_TARGET_FREQ;
413 freq_delta = (int32_t)freq_delta < 0 ? -freq_delta : freq_delta;
414
415 if (freq_delta > freq_delta_prev) {
416 /* Previous trim value was closer to target frequency, use it */
417 freq = freq_prev;
418 rc32k_trim_set(trim_prev);
419 trim_ok = true;
420 } else if (freq > RC32K_TARGET_FREQ) {
421 /* Decrease trim value if possible */
422 if (trim > RC32K_TRIM_MIN) {
423 trim_prev = trim;
424 rc32k_trim_set(--trim);
425 } else {
426 trim_ok = true;
427 }
428 } else if (freq < RC32K_TARGET_FREQ) {
429 /* Increase trim value if possible */
430 if (trim < RC32K_TRIM_MAX) {
431 trim_prev = trim;
432 rc32k_trim_set(++trim);
433 } else {
434 trim_ok = true;
435 }
436 } else {
437 trim_ok = true;
438 }
439 } while (!trim_ok);
440
441 g_mcu_clock_rc32k_freq = freq;
442 }
443
444 void
da1469x_clock_lp_rc32m_calibrate(void)445 da1469x_clock_lp_rc32m_calibrate(void)
446 {
447 g_mcu_clock_rc32m_freq = da1469x_clock_calibrate(1, 100);
448 }
449
450 uint32_t
da1469x_clock_lp_rcx_freq_get(void)451 da1469x_clock_lp_rcx_freq_get(void)
452 {
453 assert(g_mcu_clock_rcx_freq);
454
455 return g_mcu_clock_rcx_freq;
456 }
457
458 uint32_t
da1469x_clock_lp_rc32k_freq_get(void)459 da1469x_clock_lp_rc32k_freq_get(void)
460 {
461 assert(g_mcu_clock_rc32k_freq);
462
463 return g_mcu_clock_rc32k_freq;
464 }
465
466 uint32_t
da1469x_clock_lp_rc32m_freq_get(void)467 da1469x_clock_lp_rc32m_freq_get(void)
468 {
469 assert(g_mcu_clock_rc32m_freq);
470
471 return g_mcu_clock_rc32m_freq;
472 }
473
474 void
da1469x_clock_lp_rcx_disable(void)475 da1469x_clock_lp_rcx_disable(void)
476 {
477 CRG_TOP->CLK_RCX_REG &= ~CRG_TOP_CLK_RCX_REG_RCX_ENABLE_Msk;
478 }
479
480 static void
da1469x_delay_us(uint32_t delay_us)481 da1469x_delay_us(uint32_t delay_us)
482 {
483 /*
484 * SysTick runs on ~32 MHz clock while PLL is not started.
485 * so multiplying by 32 to convert from us to SysTicks.
486 */
487 SysTick->LOAD = delay_us * 32;
488 SysTick->VAL = 0UL;
489 SysTick->CTRL = SysTick_CTRL_CLKSOURCE_Msk | SysTick_CTRL_ENABLE_Msk;
490 while (0 == (SysTick->CTRL & SysTick_CTRL_COUNTFLAG_Msk));
491 SysTick->CTRL = 0;
492 }
493
494 /**
495 * Enable PLL96
496 */
497 void
da1469x_clock_sys_pll_enable(void)498 da1469x_clock_sys_pll_enable(void)
499 {
500 /* VDD voltage should be to 1.2V prior to enabling PLL (VDD_LEVEL_Msk means 1.2V) */
501 assert((CRG_TOP->POWER_CTRL_REG & CRG_TOP_POWER_CTRL_REG_VDD_LEVEL_Msk)
502 == CRG_TOP_POWER_CTRL_REG_VDD_LEVEL_Msk);
503
504 /* Start PLL LDO if not done yet */
505 if ((CRG_XTAL->PLL_SYS_STATUS_REG & CRG_XTAL_PLL_SYS_STATUS_REG_LDO_PLL_OK_Msk) == 0) {
506 CRG_XTAL->PLL_SYS_CTRL1_REG |= CRG_XTAL_PLL_SYS_CTRL1_REG_LDO_PLL_ENABLE_Msk;
507 /* Wait for XTAL LDO to settle */
508 da1469x_delay_us(20);
509 }
510 if ((CRG_XTAL->PLL_SYS_STATUS_REG & CRG_XTAL_PLL_SYS_STATUS_REG_PLL_LOCK_FINE_Msk) == 0) {
511 /* Enables DXTAL for the system PLL */
512 CRG_XTAL->XTAL32M_CTRL0_REG |= CRG_XTAL_XTAL32M_CTRL0_REG_XTAL32M_DXTAL_SYSPLL_ENABLE_Msk;
513 /* Use internal VCO current setting to enable precharge */
514 CRG_XTAL->PLL_SYS_CTRL1_REG |= CRG_XTAL_PLL_SYS_CTRL1_REG_PLL_SEL_MIN_CUR_INT_Msk;
515 /* Enable precharge */
516 CRG_XTAL->PLL_SYS_CTRL2_REG |= CRG_XTAL_PLL_SYS_CTRL2_REG_PLL_RECALIB_Msk;
517 /* Start the SYSPLL */
518 CRG_XTAL->PLL_SYS_CTRL1_REG |= CRG_XTAL_PLL_SYS_CTRL1_REG_PLL_EN_Msk;
519 /* Precharge loopfilter (Vtune) */
520 da1469x_delay_us(10);
521 /* Disable precharge */
522 CRG_XTAL->PLL_SYS_CTRL2_REG &= ~CRG_XTAL_PLL_SYS_CTRL2_REG_PLL_RECALIB_Msk;
523 /* Extra wait time */
524 da1469x_delay_us(5);
525 /* Take external VCO current setting */
526 CRG_XTAL->PLL_SYS_CTRL1_REG &= ~CRG_XTAL_PLL_SYS_CTRL1_REG_PLL_SEL_MIN_CUR_INT_Msk;
527 }
528 }
529
530 void
da1469x_clock_sys_pll_disable(void)531 da1469x_clock_sys_pll_disable(void)
532 {
533 /* Switch from PLL is only allowed when the new system clock is XTAL32M */
534 da1469x_clock_sys_xtal32m_switch();
535
536 CRG_XTAL->PLL_SYS_CTRL1_REG &= ~(CRG_XTAL_PLL_SYS_CTRL1_REG_PLL_EN_Msk |
537 CRG_XTAL_PLL_SYS_CTRL1_REG_LDO_PLL_ENABLE_Msk);
538
539 CRG_XTAL->XTAL32M_CTRL0_REG &= ~(CRG_XTAL_XTAL32M_CTRL0_REG_XTAL32M_DXTAL_SYSPLL_ENABLE_Msk);
540 }
541
542 void
da1469x_clock_pll_wait_to_lock(void)543 da1469x_clock_pll_wait_to_lock(void)
544 {
545 uint32_t primask;
546
547 primask = DA1469X_IRQ_DISABLE();
548
549 NVIC_ClearPendingIRQ(PLL_LOCK_IRQn);
550
551 if (!da1469x_clock_is_pll_locked()) {
552 NVIC_EnableIRQ(PLL_LOCK_IRQn);
553 while (!NVIC_GetPendingIRQ(PLL_LOCK_IRQn)) {
554 __WFI();
555 }
556 NVIC_DisableIRQ(PLL_LOCK_IRQn);
557
558 /*
559 * Due to a metastability issue, the PLL lock interrupt may be fired earlier
560 * and before the PLL is actually locked. Therefore, an attempt to switch to
561 * an unlocked PLL might end up hanging the system clock.
562 * Make sure that PLL is locked by polling the corresponding status register
563 * as the actual time interval between two successive PLL lock interrupts is
564 * uncertain.
565 */
566 while (!da1469x_clock_is_pll_locked());
567 }
568
569 DA1469X_IRQ_ENABLE(primask);
570 }
571
572 void
da1469x_clock_sys_pll_switch(void)573 da1469x_clock_sys_pll_switch(void)
574 {
575 assert(da1469x_clock_sys_pll_switch_check_restrictions() == 0);
576
577 /* CLK_SEL_Msk == 3 means PLL */
578 CRG_TOP->CLK_CTRL_REG |= CRG_TOP_CLK_CTRL_REG_SYS_CLK_SEL_Msk;
579
580 while (!(CRG_TOP->CLK_CTRL_REG & CRG_TOP_CLK_CTRL_REG_RUNNING_AT_PLL96M_Msk));
581
582 SystemCoreClock = PLL_FREQ;
583
584 da1469x_clock_adjust_otp_access_timings();
585 }
586