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