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