1 /*
2  * SPDX-FileCopyrightText: 2015-2022 Espressif Systems (Shanghai) CO LTD
3  *
4  * SPDX-License-Identifier: Apache-2.0
5  */
6 
7 #pragma once
8 
9 #include <stdint.h>
10 #include "soc/soc.h"
11 #include "soc/clk_tree_defs.h"
12 #include "soc/rtc.h"
13 #include "soc/rtc_cntl_reg.h"
14 #include "soc/dport_reg.h"
15 #include "soc/syscon_reg.h"
16 #include "hal/regi2c_ctrl.h"
17 #include "soc/regi2c_bbpll.h"
18 #include "soc/regi2c_apll.h"
19 #include "hal/assert.h"
20 #include "esp32s2/rom/rtc.h"
21 
22 #ifdef __cplusplus
23 extern "C" {
24 #endif
25 
26 #undef MHZ
27 #define MHZ                 (1000000)
28 
29 #define CLK_LL_PLL_80M_FREQ_MHZ    (80)
30 #define CLK_LL_PLL_160M_FREQ_MHZ   (160)
31 #define CLK_LL_PLL_240M_FREQ_MHZ   (240)
32 
33 #define CLK_LL_PLL_320M_FREQ_MHZ   (320)
34 #define CLK_LL_PLL_480M_FREQ_MHZ   (480)
35 
36 #define CLK_LL_AHB_MAX_FREQ_MHZ    CLK_LL_PLL_80M_FREQ_MHZ
37 
38 // ESP32S2 only supports 40MHz crystal
39 #define CLK_LL_XTAL_FREQ_MHZ       (40)
40 
41 /* APLL configuration parameters */
42 #define CLK_LL_APLL_SDM_STOP_VAL_1         0x09
43 #define CLK_LL_APLL_SDM_STOP_VAL_2_REV0    0x69
44 #define CLK_LL_APLL_SDM_STOP_VAL_2_REV1    0x49
45 
46 /* APLL calibration parameters */
47 #define CLK_LL_APLL_CAL_DELAY_1            0x0f
48 #define CLK_LL_APLL_CAL_DELAY_2            0x3f
49 #define CLK_LL_APLL_CAL_DELAY_3            0x1f
50 
51 #define CLK_LL_XTAL32K_CONFIG_DEFAULT() { \
52     .dac = 3, \
53     .dres = 3, \
54     .dgm = 3, \
55     .dbuf = 1, \
56 }
57 
58 /**
59  * @brief XTAL32K_CLK enable modes
60  */
61 typedef enum {
62     CLK_LL_XTAL32K_ENABLE_MODE_CRYSTAL,       //!< Enable the external 32kHz crystal for XTAL32K_CLK
63     CLK_LL_XTAL32K_ENABLE_MODE_EXTERNAL,      //!< Enable the external clock signal for XTAL32K_CLK
64     CLK_LL_XTAL32K_ENABLE_MODE_BOOTSTRAP,     //!< Bootstrap the crystal oscillator for faster XTAL32K_CLK start up */
65 } clk_ll_xtal32k_enable_mode_t;
66 
67 /**
68  * @brief XTAL32K_CLK configuration structure
69  */
70 typedef struct {
71     uint32_t dac : 6;
72     uint32_t dres : 3;
73     uint32_t dgm : 3;
74     uint32_t dbuf: 1;
75 } clk_ll_xtal32k_config_t;
76 
77 /**
78  * @brief Power up BBPLL circuit
79  */
clk_ll_bbpll_enable(void)80 static inline __attribute__((always_inline)) void clk_ll_bbpll_enable(void)
81 {
82     CLEAR_PERI_REG_MASK(RTC_CNTL_OPTIONS0_REG, RTC_CNTL_BB_I2C_FORCE_PD |
83                         RTC_CNTL_BBPLL_FORCE_PD | RTC_CNTL_BBPLL_I2C_FORCE_PD);
84 }
85 
86 /**
87  * @brief Power down BBPLL circuit
88  */
clk_ll_bbpll_disable(void)89 static inline __attribute__((always_inline)) void clk_ll_bbpll_disable(void)
90 {
91     SET_PERI_REG_MASK(RTC_CNTL_OPTIONS0_REG, RTC_CNTL_BB_I2C_FORCE_PD |
92                       RTC_CNTL_BBPLL_FORCE_PD | RTC_CNTL_BBPLL_I2C_FORCE_PD);
93 }
94 
95 /**
96  * @brief Power up APLL circuit
97  */
clk_ll_apll_enable(void)98 static inline __attribute__((always_inline)) void clk_ll_apll_enable(void)
99 {
100     CLEAR_PERI_REG_MASK(RTC_CNTL_ANA_CONF_REG, RTC_CNTL_PLLA_FORCE_PD);
101     SET_PERI_REG_MASK(RTC_CNTL_ANA_CONF_REG, RTC_CNTL_PLLA_FORCE_PU);
102 }
103 
104 /**
105  * @brief Power down APLL circuit
106  */
clk_ll_apll_disable(void)107 static inline __attribute__((always_inline)) void clk_ll_apll_disable(void)
108 {
109     SET_PERI_REG_MASK(RTC_CNTL_ANA_CONF_REG, RTC_CNTL_PLLA_FORCE_PD);
110     CLEAR_PERI_REG_MASK(RTC_CNTL_ANA_CONF_REG, RTC_CNTL_PLLA_FORCE_PU);
111 }
112 
113 /**
114  * @brief Get APLL configuration which can be used to calculate APLL frequency
115  *
116  * @param[out] o_div  Frequency divider, 0..31
117  * @param[out] sdm0  Frequency adjustment parameter, 0..255
118  * @param[out] sdm1  Frequency adjustment parameter, 0..255
119  * @param[out] sdm2  Frequency adjustment parameter, 0..63
120  */
clk_ll_apll_get_config(uint32_t * o_div,uint32_t * sdm0,uint32_t * sdm1,uint32_t * sdm2)121 static inline __attribute__((always_inline)) void clk_ll_apll_get_config(uint32_t *o_div, uint32_t *sdm0, uint32_t *sdm1, uint32_t *sdm2)
122 {
123     *o_div = REGI2C_READ_MASK(I2C_APLL, I2C_APLL_OR_OUTPUT_DIV);
124     *sdm0 = REGI2C_READ_MASK(I2C_APLL, I2C_APLL_DSDM0);
125     *sdm1 = REGI2C_READ_MASK(I2C_APLL, I2C_APLL_DSDM1);
126     *sdm2 = REGI2C_READ_MASK(I2C_APLL, I2C_APLL_DSDM2);
127 }
128 
129 /**
130  * @brief Set APLL configuration
131  *
132  * @param o_div  Frequency divider, 0..31
133  * @param sdm0  Frequency adjustment parameter, 0..255
134  * @param sdm1  Frequency adjustment parameter, 0..255
135  * @param sdm2  Frequency adjustment parameter, 0..63
136  */
clk_ll_apll_set_config(uint32_t o_div,uint32_t sdm0,uint32_t sdm1,uint32_t sdm2)137 static inline __attribute__((always_inline)) void clk_ll_apll_set_config(uint32_t o_div, uint32_t sdm0, uint32_t sdm1, uint32_t sdm2)
138 {
139     REGI2C_WRITE_MASK(I2C_APLL, I2C_APLL_DSDM2, sdm2);
140     REGI2C_WRITE_MASK(I2C_APLL, I2C_APLL_DSDM0, sdm0);
141     REGI2C_WRITE_MASK(I2C_APLL, I2C_APLL_DSDM1, sdm1);
142     REGI2C_WRITE(I2C_APLL, I2C_APLL_SDM_STOP, CLK_LL_APLL_SDM_STOP_VAL_1);
143     REGI2C_WRITE(I2C_APLL, I2C_APLL_SDM_STOP, CLK_LL_APLL_SDM_STOP_VAL_2_REV1);
144     REGI2C_WRITE_MASK(I2C_APLL, I2C_APLL_OR_OUTPUT_DIV, o_div);
145 }
146 
147 /**
148  * @brief Set APLL calibration parameters
149  */
clk_ll_apll_set_calibration(void)150 static inline __attribute__((always_inline)) void clk_ll_apll_set_calibration(void)
151 {
152     REGI2C_WRITE(I2C_APLL, I2C_APLL_IR_CAL_DELAY, CLK_LL_APLL_CAL_DELAY_1);
153     REGI2C_WRITE(I2C_APLL, I2C_APLL_IR_CAL_DELAY, CLK_LL_APLL_CAL_DELAY_2);
154     REGI2C_WRITE(I2C_APLL, I2C_APLL_IR_CAL_DELAY, CLK_LL_APLL_CAL_DELAY_3);
155 }
156 
157 /**
158  * @brief Check whether APLL calibration is done
159  *
160  * @return True if calibration is done; otherwise false
161  */
clk_ll_apll_calibration_is_done(void)162 static inline __attribute__((always_inline)) bool clk_ll_apll_calibration_is_done(void)
163 {
164     return REGI2C_READ_MASK(I2C_APLL, I2C_APLL_OR_CAL_END);
165 }
166 
167 /**
168  * @brief Enable the 32kHz crystal oscillator
169  *
170  * @param mode Used to determine the xtal32k configuration parameters
171  */
clk_ll_xtal32k_enable(clk_ll_xtal32k_enable_mode_t mode)172 static inline __attribute__((always_inline)) void clk_ll_xtal32k_enable(clk_ll_xtal32k_enable_mode_t mode)
173 {
174     // Configure xtal32k
175     clk_ll_xtal32k_config_t cfg = CLK_LL_XTAL32K_CONFIG_DEFAULT();
176     REG_SET_FIELD(RTC_CNTL_EXT_XTL_CONF_REG, RTC_CNTL_DAC_XTAL_32K, cfg.dac);
177     REG_SET_FIELD(RTC_CNTL_EXT_XTL_CONF_REG, RTC_CNTL_DRES_XTAL_32K, cfg.dres);
178     REG_SET_FIELD(RTC_CNTL_EXT_XTL_CONF_REG, RTC_CNTL_DGM_XTAL_32K, cfg.dgm);
179     REG_SET_FIELD(RTC_CNTL_EXT_XTL_CONF_REG, RTC_CNTL_DBUF_XTAL_32K, cfg.dbuf);
180     // Enable xtal32k xpd status
181     SET_PERI_REG_MASK(RTC_CNTL_EXT_XTL_CONF_REG, RTC_CNTL_XPD_XTAL_32K);
182     if (mode == CLK_LL_XTAL32K_ENABLE_MODE_EXTERNAL) {
183         /* TODO: external 32k source may need different settings */
184         ;
185     }
186 }
187 
188 /**
189  * @brief Disable the 32kHz crystal oscillator
190  */
clk_ll_xtal32k_disable(void)191 static inline __attribute__((always_inline)) void clk_ll_xtal32k_disable(void)
192 {
193     // Set xtal32k xpd to be controlled by software
194     SET_PERI_REG_MASK(RTC_CNTL_EXT_XTL_CONF_REG, RTC_CNTL_XTAL32K_XPD_FORCE);
195     // Disable xtal32k xpd status
196     CLEAR_PERI_REG_MASK(RTC_CNTL_EXT_XTL_CONF_REG, RTC_CNTL_XPD_XTAL_32K);
197 }
198 
199 /**
200  * @brief Get the state of the 32kHz crystal clock
201  *
202  * @return True if the 32kHz XTAL is enabled
203  */
clk_ll_xtal32k_is_enabled(void)204 static inline __attribute__((always_inline)) bool clk_ll_xtal32k_is_enabled(void)
205 {
206     uint32_t xtal_conf = READ_PERI_REG(RTC_CNTL_EXT_XTL_CONF_REG);
207     /* If xtal xpd is controlled by software */
208     bool xtal_xpd_sw = (xtal_conf & RTC_CNTL_XTAL32K_XPD_FORCE) >> RTC_CNTL_XTAL32K_XPD_FORCE_S;
209     /* If xtal xpd software control is on */
210     bool xtal_xpd_st = (xtal_conf & RTC_CNTL_XPD_XTAL_32K) >> RTC_CNTL_XPD_XTAL_32K_S;
211     // disabled = xtal_xpd_sw && !xtal_xpd_st; enabled = !disbaled
212     bool enabled = !xtal_xpd_sw || xtal_xpd_st;
213     return enabled;
214 }
215 
216 /**
217  * @brief Enable the internal oscillator output for RC_FAST_CLK
218  */
clk_ll_rc_fast_enable(void)219 static inline __attribute__((always_inline)) void clk_ll_rc_fast_enable(void)
220 {
221     CLEAR_PERI_REG_MASK(RTC_CNTL_CLK_CONF_REG, RTC_CNTL_ENB_CK8M);
222     REG_SET_FIELD(RTC_CNTL_TIMER1_REG, RTC_CNTL_CK8M_WAIT, RTC_CK8M_ENABLE_WAIT_DEFAULT);
223 }
224 
225 /**
226  * @brief Disable the internal oscillator output for RC_FAST_CLK
227  */
clk_ll_rc_fast_disable(void)228 static inline __attribute__((always_inline)) void clk_ll_rc_fast_disable(void)
229 {
230     SET_PERI_REG_MASK(RTC_CNTL_CLK_CONF_REG, RTC_CNTL_ENB_CK8M);
231     REG_SET_FIELD(RTC_CNTL_TIMER1_REG, RTC_CNTL_CK8M_WAIT, RTC_CNTL_CK8M_WAIT_DEFAULT);
232 }
233 
234 /**
235  * @brief Get the state of the internal oscillator for RC_FAST_CLK
236  *
237  * @return True if the oscillator is enabled
238  */
clk_ll_rc_fast_is_enabled(void)239 static inline __attribute__((always_inline)) bool clk_ll_rc_fast_is_enabled(void)
240 {
241     return GET_PERI_REG_MASK(RTC_CNTL_CLK_CONF_REG, RTC_CNTL_ENB_CK8M) == 0;
242 }
243 
244 /**
245  * @brief Enable the output from the internal oscillator to be passed into a configurable divider,
246  * which by default divides the input clock frequency by 256. i.e. RC_FAST_D256_CLK = RC_FAST_CLK / 256
247  *
248  * Divider values other than 256 may be configured, but this facility is not currently needed,
249  * so is not exposed in the code.
250  * The output of the divider, RC_FAST_D256_CLK, is referred as 8md256 or simply d256 in reg. descriptions.
251  */
clk_ll_rc_fast_d256_enable(void)252 static inline __attribute__((always_inline)) void clk_ll_rc_fast_d256_enable(void)
253 {
254     CLEAR_PERI_REG_MASK(RTC_CNTL_CLK_CONF_REG, RTC_CNTL_ENB_CK8M_DIV);
255 }
256 
257 /**
258  * @brief Disable the output from the internal oscillator to be passed into a configurable divider.
259  * i.e. RC_FAST_D256_CLK = RC_FAST_CLK / 256
260  *
261  * Disabling this divider could reduce power consumption.
262  */
clk_ll_rc_fast_d256_disable(void)263 static inline __attribute__((always_inline)) void clk_ll_rc_fast_d256_disable(void)
264 {
265     SET_PERI_REG_MASK(RTC_CNTL_CLK_CONF_REG, RTC_CNTL_ENB_CK8M_DIV);
266 }
267 
268 /**
269  * @brief Get the state of the divider which is applied to the output from the internal oscillator (RC_FAST_CLK)
270  *
271  * @return True if the divided output is enabled
272  */
clk_ll_rc_fast_d256_is_enabled(void)273 static inline __attribute__((always_inline)) bool clk_ll_rc_fast_d256_is_enabled(void)
274 {
275     return GET_PERI_REG_MASK(RTC_CNTL_CLK_CONF_REG, RTC_CNTL_ENB_CK8M_DIV) == 0;
276 }
277 
278 /**
279  * @brief Enable the digital RC_FAST_CLK, which is used to support peripherals.
280  */
clk_ll_rc_fast_digi_enable(void)281 static inline __attribute__((always_inline)) void clk_ll_rc_fast_digi_enable(void)
282 {
283     SET_PERI_REG_MASK(RTC_CNTL_CLK_CONF_REG, RTC_CNTL_DIG_CLK8M_EN_M);
284 }
285 
286 /**
287  * @brief Disable the digital RC_FAST_CLK, which is used to support peripherals.
288  */
clk_ll_rc_fast_digi_disable(void)289 static inline __attribute__((always_inline)) void clk_ll_rc_fast_digi_disable(void)
290 {
291     CLEAR_PERI_REG_MASK(RTC_CNTL_CLK_CONF_REG, RTC_CNTL_DIG_CLK8M_EN_M);
292 }
293 
294 /**
295  * @brief Get the state of the digital RC_FAST_CLK
296  *
297  * @return True if the digital RC_FAST_CLK is enabled
298  */
clk_ll_rc_fast_digi_is_enabled(void)299 static inline __attribute__((always_inline)) bool clk_ll_rc_fast_digi_is_enabled(void)
300 {
301     return GET_PERI_REG_MASK(RTC_CNTL_CLK_CONF_REG, RTC_CNTL_DIG_CLK8M_EN_M);
302 }
303 
304 /**
305  * @brief Enable the digital RC_FAST_D256_CLK, which is used to support peripherals.
306  */
clk_ll_rc_fast_d256_digi_enable(void)307 static inline __attribute__((always_inline)) void clk_ll_rc_fast_d256_digi_enable(void)
308 {
309     SET_PERI_REG_MASK(RTC_CNTL_CLK_CONF_REG, RTC_CNTL_DIG_CLK8M_D256_EN_M);
310 }
311 
312 /**
313  * @brief Disable the digital RC_FAST_D256_CLK, which is used to support peripherals.
314  */
clk_ll_rc_fast_d256_digi_disable(void)315 static inline __attribute__((always_inline)) void clk_ll_rc_fast_d256_digi_disable(void)
316 {
317     CLEAR_PERI_REG_MASK(RTC_CNTL_CLK_CONF_REG, RTC_CNTL_DIG_CLK8M_D256_EN_M);
318 }
319 
320 /**
321  * @brief Enable the digital XTAL32K_CLK, which is used to support peripherals.
322  */
clk_ll_xtal32k_digi_enable(void)323 static inline __attribute__((always_inline)) void clk_ll_xtal32k_digi_enable(void)
324 {
325     SET_PERI_REG_MASK(RTC_CNTL_CLK_CONF_REG, RTC_CNTL_DIG_XTAL32K_EN_M);
326 }
327 
328 /**
329  * @brief Disable the digital XTAL32K_CLK, which is used to support peripherals.
330  */
clk_ll_xtal32k_digi_disable(void)331 static inline __attribute__((always_inline)) void clk_ll_xtal32k_digi_disable(void)
332 {
333     CLEAR_PERI_REG_MASK(RTC_CNTL_CLK_CONF_REG, RTC_CNTL_DIG_XTAL32K_EN_M);
334 }
335 
336 /**
337  * @brief Get the state of the digital XTAL32K_CLK
338  *
339  * @return True if the digital XTAL32K_CLK is enabled
340  */
clk_ll_xtal32k_digi_is_enabled(void)341 static inline __attribute__((always_inline)) bool clk_ll_xtal32k_digi_is_enabled(void)
342 {
343     return REG_GET_FIELD(RTC_CNTL_CLK_CONF_REG, RTC_CNTL_DIG_XTAL32K_EN);
344 }
345 
346 /**
347  * @brief Get PLL_CLK frequency
348  *
349  * @return PLL clock frequency, in MHz. Returns 0 if register field value is invalid.
350  */
clk_ll_bbpll_get_freq_mhz(void)351 static inline __attribute__((always_inline)) uint32_t clk_ll_bbpll_get_freq_mhz(void)
352 {
353     uint32_t pll_freq_sel = DPORT_REG_GET_FIELD(DPORT_CPU_PER_CONF_REG, DPORT_PLL_FREQ_SEL);
354     switch (pll_freq_sel) {
355     case 0: // PLL_320M
356         return CLK_LL_PLL_320M_FREQ_MHZ;
357     case 1: // PLL_480M
358         return CLK_LL_PLL_480M_FREQ_MHZ;
359     default:
360         return 0;
361     }
362 }
363 
364 /**
365  * @brief Set BBPLL frequency from XTAL source (digital part)
366  *
367  * @param pll_freq_mhz PLL frequency, in MHz
368  */
clk_ll_bbpll_set_freq_mhz(uint32_t pll_freq_mhz)369 static inline __attribute__((always_inline)) void clk_ll_bbpll_set_freq_mhz(uint32_t pll_freq_mhz)
370 {
371     switch (pll_freq_mhz) {
372     case CLK_LL_PLL_320M_FREQ_MHZ: // PLL_320M
373         CLEAR_PERI_REG_MASK(DPORT_CPU_PER_CONF_REG, DPORT_PLL_FREQ_SEL);
374         break;
375     case CLK_LL_PLL_480M_FREQ_MHZ: // PLL_480M
376         SET_PERI_REG_MASK(DPORT_CPU_PER_CONF_REG, DPORT_PLL_FREQ_SEL);
377         break;
378     default:
379         abort();
380     }
381 }
382 
383 /**
384  * @brief Set BBPLL frequency from XTAL source (Analog part)
385  *
386  * @param pll_freq_mhz PLL frequency, in MHz
387  * @param xtal_freq_mhz XTAL frequency, in MHz
388  */
clk_ll_bbpll_set_config(uint32_t pll_freq_mhz,uint32_t xtal_freq_mhz)389 static inline __attribute__((always_inline)) void clk_ll_bbpll_set_config(uint32_t pll_freq_mhz, uint32_t xtal_freq_mhz)
390 {
391     (void)xtal_freq_mhz;
392     uint8_t div_ref;
393     uint8_t div7_0;
394     uint8_t dr1;
395     uint8_t dr3;
396     uint8_t dchgp;
397     uint8_t dcur;
398 
399     if (pll_freq_mhz == CLK_LL_PLL_480M_FREQ_MHZ) {
400         /* Configure 480M PLL */
401         div_ref = 0;
402         div7_0 = 8;
403         dr1 = 0;
404         dr3 = 0;
405         dchgp = 5;
406         dcur = 4;
407         REGI2C_WRITE(I2C_BBPLL, I2C_BBPLL_MODE_HF, 0x6B);
408     } else {
409         /* Configure 320M PLL */
410         div_ref = 0;
411         div7_0 = 4;
412         dr1 = 0;
413         dr3 = 0;
414         dchgp = 5;
415         dcur = 5;
416         REGI2C_WRITE(I2C_BBPLL, I2C_BBPLL_MODE_HF, 0x69);
417     }
418     uint8_t i2c_bbpll_lref  = (dchgp << I2C_BBPLL_OC_DCHGP_LSB) | (div_ref);
419     uint8_t i2c_bbpll_div_7_0 = div7_0;
420     uint8_t i2c_bbpll_dcur = (2 << I2C_BBPLL_OC_DLREF_SEL_LSB ) | (1 << I2C_BBPLL_OC_DHREF_SEL_LSB) | dcur;
421     REGI2C_WRITE(I2C_BBPLL, I2C_BBPLL_OC_REF_DIV, i2c_bbpll_lref);
422     REGI2C_WRITE(I2C_BBPLL, I2C_BBPLL_OC_DIV_7_0, i2c_bbpll_div_7_0);
423     REGI2C_WRITE_MASK(I2C_BBPLL, I2C_BBPLL_OC_DR1, dr1);
424     REGI2C_WRITE_MASK(I2C_BBPLL, I2C_BBPLL_OC_DR3, dr3);
425     REGI2C_WRITE(I2C_BBPLL, I2C_BBPLL_OC_DCUR, i2c_bbpll_dcur);
426 }
427 
428 /**
429  * @brief Enable BBPLL self-calibration
430  */
clk_ll_bbpll_calibration_enable(void)431 static inline __attribute__((always_inline)) void clk_ll_bbpll_calibration_enable(void)
432 {
433     REGI2C_WRITE_MASK(I2C_BBPLL, I2C_BBPLL_IR_CAL_ENX_CAP, 1);
434 }
435 
436 /**
437  * @brief Check whether BBPLL calibration is done
438  *
439  * @param ext_cap Steps write to I2C_BBPLL_IR_CAL_EXT_CAP
440  *
441  * @return True if calibration is done; otherwise false
442  */
clk_ll_bbpll_calibration_is_done(uint32_t ext_cap)443 static inline __attribute__((always_inline)) bool clk_ll_bbpll_calibration_is_done(uint32_t ext_cap)
444 {
445     REGI2C_WRITE_MASK(I2C_BBPLL, I2C_BBPLL_IR_CAL_EXT_CAP, ext_cap);
446     return REGI2C_READ_MASK(I2C_BBPLL, I2C_BBPLL_OR_CAL_CAP) == 0;
447 }
448 
449 /**
450  * @brief Select the clock source for CPU_CLK
451  *
452  * @param in_sel One of the clock sources in soc_cpu_clk_src_t
453  */
clk_ll_cpu_set_src(soc_cpu_clk_src_t in_sel)454 static inline __attribute__((always_inline)) void clk_ll_cpu_set_src(soc_cpu_clk_src_t in_sel)
455 {
456     switch (in_sel) {
457     case SOC_CPU_CLK_SRC_XTAL:
458         REG_SET_FIELD(DPORT_SYSCLK_CONF_REG, DPORT_SOC_CLK_SEL, 0);
459         break;
460     case SOC_CPU_CLK_SRC_PLL:
461         REG_SET_FIELD(DPORT_SYSCLK_CONF_REG, DPORT_SOC_CLK_SEL, 1);
462         break;
463     case SOC_CPU_CLK_SRC_RC_FAST:
464         REG_SET_FIELD(DPORT_SYSCLK_CONF_REG, DPORT_SOC_CLK_SEL, 2);
465         break;
466     case SOC_CPU_CLK_SRC_APLL:
467         REG_SET_FIELD(DPORT_SYSCLK_CONF_REG, DPORT_SOC_CLK_SEL, 3);
468         break;
469     default:
470         // Unsupported CPU_CLK mux input sel
471         abort();
472     }
473 }
474 
475 /**
476  * @brief Get the clock source for CPU_CLK
477  *
478  * @return Currently selected clock source (one of soc_cpu_clk_src_t values)
479  */
clk_ll_cpu_get_src(void)480 static inline __attribute__((always_inline)) soc_cpu_clk_src_t clk_ll_cpu_get_src(void)
481 {
482     uint32_t clk_sel = REG_GET_FIELD(DPORT_SYSCLK_CONF_REG, DPORT_SOC_CLK_SEL);
483     switch (clk_sel) {
484     case 0:
485         return SOC_CPU_CLK_SRC_XTAL;
486     case 1:
487         return SOC_CPU_CLK_SRC_PLL;
488     case 2:
489         return SOC_CPU_CLK_SRC_RC_FAST;
490     case 3:
491         return SOC_CPU_CLK_SRC_APLL;
492     default:
493         return SOC_CPU_CLK_SRC_INVALID;
494     }
495 }
496 
497 /**
498  * @brief Set CPU frequency from PLL clock
499  *
500  * @param cpu_mhz CPU frequency value, in MHz
501  */
clk_ll_cpu_set_freq_mhz_from_pll(uint32_t cpu_mhz)502 static inline __attribute__((always_inline)) void clk_ll_cpu_set_freq_mhz_from_pll(uint32_t cpu_mhz)
503 {
504     switch (cpu_mhz) {
505     case CLK_LL_PLL_80M_FREQ_MHZ:
506         REG_SET_FIELD(DPORT_CPU_PER_CONF_REG, DPORT_CPUPERIOD_SEL, 0);
507         break;
508     case CLK_LL_PLL_160M_FREQ_MHZ:
509         REG_SET_FIELD(DPORT_CPU_PER_CONF_REG, DPORT_CPUPERIOD_SEL, 1);
510         break;
511     case CLK_LL_PLL_240M_FREQ_MHZ:
512         REG_SET_FIELD(DPORT_CPU_PER_CONF_REG, DPORT_CPUPERIOD_SEL, 2);
513         break;
514     default:
515         // Unsupported CPU_CLK freq from PLL
516         abort();
517     }
518 }
519 
520 /**
521  * @brief Get CPU_CLK frequency from PLL_CLK source
522  *
523  * @return CPU clock frequency, in MHz. Returns 0 if register field value is invalid.
524  */
clk_ll_cpu_get_freq_mhz_from_pll(void)525 static inline __attribute__((always_inline)) uint32_t clk_ll_cpu_get_freq_mhz_from_pll(void)
526 {
527     uint32_t cpu_freq_sel = DPORT_REG_GET_FIELD(DPORT_CPU_PER_CONF_REG, DPORT_CPUPERIOD_SEL);
528     switch (cpu_freq_sel) {
529     case 0:
530         return CLK_LL_PLL_80M_FREQ_MHZ;
531     case 1:
532         return CLK_LL_PLL_160M_FREQ_MHZ;
533     case 2:
534         // When PLL frequency selection is 320MHz but CPU frequency selection is 240MHz, it is an undetermined state.
535         // It is checked in the upper layer.
536         return CLK_LL_PLL_240M_FREQ_MHZ;
537     default:
538         // Invalid CPUPERIOD_SEL value
539         return 0;
540     }
541 }
542 
543 /**
544  * @brief Set CPU_CLK's XTAL/FAST_RC clock source path divider
545  *
546  * @param divider Divider. Usually this divider is set to 1 in bootloader stage. PRE_DIV_CNT = divider - 1.
547  */
clk_ll_cpu_set_divider(uint32_t divider)548 static inline __attribute__((always_inline)) void clk_ll_cpu_set_divider(uint32_t divider)
549 {
550     HAL_ASSERT(divider > 0);
551     REG_SET_FIELD(DPORT_SYSCLK_CONF_REG, DPORT_PRE_DIV_CNT, divider - 1);
552 }
553 
554 /**
555  * @brief Get CPU_CLK's XTAL/FAST_RC clock source path divider
556  *
557  * @return Divider. Divider = (PRE_DIV_CNT + 1).
558  */
clk_ll_cpu_get_divider(void)559 static inline __attribute__((always_inline)) uint32_t clk_ll_cpu_get_divider(void)
560 {
561     return REG_GET_FIELD(DPORT_SYSCLK_CONF_REG, DPORT_PRE_DIV_CNT) + 1;
562 }
563 
564 /**
565  * @brief Get CPU_CLK's APLL clock source path divider
566  *
567  * @return Divider. Returns 0 means invalid.
568  */
clk_ll_cpu_get_divider_from_apll(void)569 static inline __attribute__((always_inline)) uint32_t clk_ll_cpu_get_divider_from_apll(void)
570 {
571     // APLL path divider choice depends on PLL_FREQ_SEL and CPUPERIOD_SEL
572     uint32_t pll_freq_sel = DPORT_REG_GET_FIELD(DPORT_CPU_PER_CONF_REG, DPORT_PLL_FREQ_SEL);
573     uint32_t cpu_freq_sel = DPORT_REG_GET_FIELD(DPORT_CPU_PER_CONF_REG, DPORT_CPUPERIOD_SEL);
574     if (pll_freq_sel == 0 && cpu_freq_sel == 0) {
575         return 4;
576     } else if (pll_freq_sel == 0 && cpu_freq_sel == 1) {
577         return 2;
578     } else {
579         // Invalid configuration if APLL is the clock source
580         return 0;
581     }
582 }
583 
584 /**
585  * @brief Set REF_TICK divider to make REF_TICK frequency at 1MHz
586  *
587  * @param cpu_clk_src Selected CPU clock source (one of soc_cpu_clk_src_t values)
588  *
589  * When PLL, APLL, or XTAL is set as CPU clock source, divider = XTAL_CLK freq in Hz / 1MHz.
590  * When RC_FAST is set as CPU clock source, divider = RC_FAST_CLK freq in Hz / 1MHz.
591  * Value in register = divider - 1.
592  */
clk_ll_ref_tick_set_divider(soc_cpu_clk_src_t cpu_clk_src)593 static inline __attribute__((always_inline)) void clk_ll_ref_tick_set_divider(soc_cpu_clk_src_t cpu_clk_src)
594 {
595     switch (cpu_clk_src) {
596     case SOC_CPU_CLK_SRC_XTAL:
597     case SOC_CPU_CLK_SRC_PLL:
598     case SOC_CPU_CLK_SRC_APLL:
599         REG_SET_FIELD(SYSCON_TICK_CONF_REG, SYSCON_XTAL_TICK_NUM, CLK_LL_XTAL_FREQ_MHZ - 1);
600         break;
601     case SOC_CPU_CLK_SRC_RC_FAST:
602         REG_SET_FIELD(SYSCON_TICK_CONF_REG, SYSCON_CK8M_TICK_NUM, 8 - 1);
603         break;
604     default:
605         // Unsupported CPU_CLK mux input sel
606         abort();
607     }
608 }
609 
610 /**
611  * @brief Select the clock source for RTC_SLOW_CLK
612  *
613  * @param in_sel One of the clock sources in soc_rtc_slow_clk_src_t
614  */
clk_ll_rtc_slow_set_src(soc_rtc_slow_clk_src_t in_sel)615 static inline __attribute__((always_inline)) void clk_ll_rtc_slow_set_src(soc_rtc_slow_clk_src_t in_sel)
616 {
617     switch (in_sel) {
618     case SOC_RTC_SLOW_CLK_SRC_RC_SLOW:
619         REG_SET_FIELD(RTC_CNTL_CLK_CONF_REG, RTC_CNTL_ANA_CLK_RTC_SEL, 0);
620         break;
621     case SOC_RTC_SLOW_CLK_SRC_XTAL32K:
622         REG_SET_FIELD(RTC_CNTL_CLK_CONF_REG, RTC_CNTL_ANA_CLK_RTC_SEL, 1);
623         break;
624     case SOC_RTC_SLOW_CLK_SRC_RC_FAST_D256:
625         REG_SET_FIELD(RTC_CNTL_CLK_CONF_REG, RTC_CNTL_ANA_CLK_RTC_SEL, 2);
626         break;
627     default:
628         // Unsupported RTC_SLOW_CLK mux input sel
629         abort();
630     }
631 }
632 
633 /**
634  * @brief Get the clock source for RTC_SLOW_CLK
635  *
636  * @return Currently selected clock source (one of soc_rtc_slow_clk_src_t values)
637  */
clk_ll_rtc_slow_get_src(void)638 static inline __attribute__((always_inline)) soc_rtc_slow_clk_src_t clk_ll_rtc_slow_get_src(void)
639 {
640     uint32_t clk_sel = REG_GET_FIELD(RTC_CNTL_CLK_CONF_REG, RTC_CNTL_ANA_CLK_RTC_SEL);
641     switch (clk_sel) {
642     case 0:
643         return SOC_RTC_SLOW_CLK_SRC_RC_SLOW;
644     case 1:
645         return SOC_RTC_SLOW_CLK_SRC_XTAL32K;
646     case 2:
647         return SOC_RTC_SLOW_CLK_SRC_RC_FAST_D256;
648     default:
649         // Invalid ANA_CLK_RTC_SEL value
650         return SOC_RTC_SLOW_CLK_SRC_INVALID;
651     }
652 }
653 
654 /**
655  * @brief Select the clock source for RTC_FAST_CLK
656  *
657  * @param in_sel One of the clock sources in soc_rtc_fast_clk_src_t
658  */
clk_ll_rtc_fast_set_src(soc_rtc_fast_clk_src_t in_sel)659 static inline __attribute__((always_inline)) void clk_ll_rtc_fast_set_src(soc_rtc_fast_clk_src_t in_sel)
660 {
661     switch (in_sel) {
662     case SOC_RTC_FAST_CLK_SRC_XTAL_D4:
663         REG_SET_FIELD(RTC_CNTL_CLK_CONF_REG, RTC_CNTL_FAST_CLK_RTC_SEL, 0);
664         break;
665     case SOC_RTC_FAST_CLK_SRC_RC_FAST:
666         REG_SET_FIELD(RTC_CNTL_CLK_CONF_REG, RTC_CNTL_FAST_CLK_RTC_SEL, 1);
667         break;
668     default:
669         // Unsupported RTC_FAST_CLK mux input sel
670         abort();
671     }
672 }
673 
674 /**
675  * @brief Get the clock source for RTC_FAST_CLK
676  *
677  * @return Currently selected clock source (one of soc_rtc_fast_clk_src_t values)
678  */
clk_ll_rtc_fast_get_src(void)679 static inline __attribute__((always_inline)) soc_rtc_fast_clk_src_t clk_ll_rtc_fast_get_src(void)
680 {
681     uint32_t clk_sel = REG_GET_FIELD(RTC_CNTL_CLK_CONF_REG, RTC_CNTL_FAST_CLK_RTC_SEL);
682     switch (clk_sel) {
683     case 0:
684         return SOC_RTC_FAST_CLK_SRC_XTAL_D4;
685     case 1:
686         return SOC_RTC_FAST_CLK_SRC_RC_FAST;
687     default:
688         return SOC_RTC_FAST_CLK_SRC_INVALID;
689     }
690 }
691 
692 /**
693  * @brief Set RC_FAST_CLK divider. The output from the divider is passed into rtc_fast_clk MUX.
694  *
695  * @param divider Divider of RC_FAST_CLK. Usually this divider is set to 1 (reg. value is 0) in bootloader stage.
696  */
clk_ll_rc_fast_set_divider(uint32_t divider)697 static inline __attribute__((always_inline)) void clk_ll_rc_fast_set_divider(uint32_t divider)
698 {
699     HAL_ASSERT(divider > 0);
700     CLEAR_PERI_REG_MASK(RTC_CNTL_CLK_CONF_REG, RTC_CNTL_CK8M_DIV_SEL_VLD);
701     REG_SET_FIELD(RTC_CNTL_CLK_CONF_REG, RTC_CNTL_CK8M_DIV_SEL, divider - 1);
702     SET_PERI_REG_MASK(RTC_CNTL_CLK_CONF_REG, RTC_CNTL_CK8M_DIV_SEL_VLD);
703 }
704 
705 /**
706  * @brief Get RC_FAST_CLK divider
707  *
708  * @return Divider. Divider = (CK8M_DIV_SEL + 1).
709  */
clk_ll_rc_fast_get_divider(void)710 static inline __attribute__((always_inline)) uint32_t clk_ll_rc_fast_get_divider(void)
711 {
712     return REG_GET_FIELD(RTC_CNTL_CLK_CONF_REG, RTC_CNTL_CK8M_DIV_SEL) + 1;
713 }
714 
715 /**
716  * @brief Set RC_SLOW_CLK divider
717  *
718  * @param divider Divider of RC_SLOW_CLK. Usually this divider is set to 1 (reg. value is 0) in bootloader stage.
719  */
clk_ll_rc_slow_set_divider(uint32_t divider)720 static inline __attribute__((always_inline)) void clk_ll_rc_slow_set_divider(uint32_t divider)
721 {
722     HAL_ASSERT(divider > 0);
723     CLEAR_PERI_REG_MASK(RTC_CNTL_SLOW_CLK_CONF_REG, RTC_CNTL_ANA_CLK_DIV_VLD);
724     REG_SET_FIELD(RTC_CNTL_SLOW_CLK_CONF_REG, RTC_CNTL_ANA_CLK_DIV, divider - 1);
725     SET_PERI_REG_MASK(RTC_CNTL_SLOW_CLK_CONF_REG, RTC_CNTL_ANA_CLK_DIV_VLD);
726 }
727 
728 /************************* RTC STORAGE REGISTER STORE/LOAD **************************/
729 /**
730  * @brief Store APB_CLK frequency in RTC storage register
731  *
732  * Value of RTC_APB_FREQ_REG is stored as two copies in lower and upper 16-bit
733  * halves. These are the routines to work with that representation.
734  *
735  * @param apb_freq_hz APB frequency, in Hz
736  */
clk_ll_apb_store_freq_hz(uint32_t apb_freq_hz)737 static inline __attribute__((always_inline)) void clk_ll_apb_store_freq_hz(uint32_t apb_freq_hz)
738 {
739     uint32_t val = apb_freq_hz >> 12;
740     WRITE_PERI_REG(RTC_APB_FREQ_REG, (val & UINT16_MAX) | ((val & UINT16_MAX) << 16));
741 }
742 
743 /**
744  * @brief Load APB_CLK frequency from RTC storage register
745  *
746  * Value of RTC_APB_FREQ_REG is stored as two copies in lower and upper 16-bit
747  * halves. These are the routines to work with that representation.
748  *
749  * @return The stored APB frequency, in Hz
750  */
clk_ll_apb_load_freq_hz(void)751 static inline __attribute__((always_inline)) uint32_t clk_ll_apb_load_freq_hz(void)
752 {
753     // Read from RTC storage register
754     uint32_t apb_freq_hz = (READ_PERI_REG(RTC_APB_FREQ_REG) & UINT16_MAX) << 12;
755     // Round to the nearest MHz
756     apb_freq_hz += MHZ / 2;
757     uint32_t remainder = apb_freq_hz % MHZ;
758     return apb_freq_hz - remainder;
759 }
760 
761 /**
762  * @brief Store RTC_SLOW_CLK calibration value in RTC storage register
763  *
764  * Value of RTC_SLOW_CLK_CAL_REG has to be in the same format as returned by rtc_clk_cal (microseconds,
765  * in Q13.19 fixed-point format).
766  *
767  * @param cal_value The calibration value of slow clock period in microseconds, in Q13.19 fixed point format
768  */
clk_ll_rtc_slow_store_cal(uint32_t cal_value)769 static inline __attribute__((always_inline)) void clk_ll_rtc_slow_store_cal(uint32_t cal_value)
770 {
771     REG_WRITE(RTC_SLOW_CLK_CAL_REG, cal_value);
772 }
773 
774 /**
775  * @brief Load the calibration value of RTC_SLOW_CLK frequency from RTC storage register
776  *
777  * This value gets updated (i.e. rtc slow clock gets calibrated) every time RTC_SLOW_CLK source switches
778  *
779  * @return The calibration value of slow clock period in microseconds, in Q13.19 fixed point format
780  */
clk_ll_rtc_slow_load_cal(void)781 static inline __attribute__((always_inline)) uint32_t clk_ll_rtc_slow_load_cal(void)
782 {
783     return REG_READ(RTC_SLOW_CLK_CAL_REG);
784 }
785 
786 #ifdef __cplusplus
787 }
788 #endif
789