1 /*
2  * SPDX-FileCopyrightText: 2022-2023 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/pcr_struct.h"
14 #include "soc/lp_clkrst_struct.h"
15 #include "soc/pmu_reg.h"
16 #include "hal/regi2c_ctrl.h"
17 #include "soc/regi2c_bbpll.h"
18 #include "hal/assert.h"
19 #include "hal/log.h"
20 #include "esp32c6/rom/rtc.h"
21 #include "hal/misc.h"
22 
23 #ifdef __cplusplus
24 extern "C" {
25 #endif
26 
27 #if !defined(DT_DRV_COMPAT)
28 #undef MHZ
29 #define MHZ                 (1000000)
30 #endif
31 
32 #define CLK_LL_PLL_80M_FREQ_MHZ    (80)
33 #define CLK_LL_PLL_120M_FREQ_MHZ   (120)
34 #define CLK_LL_PLL_160M_FREQ_MHZ   (160)
35 #define CLK_LL_PLL_240M_FREQ_MHZ   (240)
36 
37 #define CLK_LL_PLL_480M_FREQ_MHZ   (480)
38 
39 #define CLK_LL_XTAL32K_CONFIG_DEFAULT() { \
40     .dac = 3, \
41     .dres = 3, \
42     .dgm = 3, \
43     .dbuf = 1, \
44 }
45 
46 /*
47 Set the frequency division factor of ref_tick
48 The FOSC of rtc calibration uses the 32 frequency division clock for ECO1,
49 So the frequency division factor of ref_tick must be greater than or equal to 32
50 */
51 #define REG_FOSC_TICK_NUM  255
52 
53 /**
54  * @brief XTAL32K_CLK enable modes
55  */
56 typedef enum {
57     CLK_LL_XTAL32K_ENABLE_MODE_CRYSTAL,       //!< Enable the external 32kHz crystal for XTAL32K_CLK
58     CLK_LL_XTAL32K_ENABLE_MODE_EXTERNAL,      //!< Enable the external clock signal for OSC_SLOW_CLK
59     CLK_LL_XTAL32K_ENABLE_MODE_BOOTSTRAP,     //!< Bootstrap the crystal oscillator for faster XTAL32K_CLK start up */
60 } clk_ll_xtal32k_enable_mode_t;
61 
62 /**
63  * @brief XTAL32K_CLK configuration structure
64  */
65 typedef struct {
66     uint32_t dac : 6;
67     uint32_t dres : 3;
68     uint32_t dgm : 3;
69     uint32_t dbuf: 1;
70 } clk_ll_xtal32k_config_t;
71 
72 /**
73  * @brief Power up BBPLL circuit
74  */
clk_ll_bbpll_enable(void)75 static inline __attribute__((always_inline)) void clk_ll_bbpll_enable(void)
76 {
77     SET_PERI_REG_MASK(PMU_IMM_HP_CK_POWER_REG, PMU_TIE_HIGH_XPD_BB_I2C |
78                       PMU_TIE_HIGH_XPD_BBPLL | PMU_TIE_HIGH_XPD_BBPLL_I2C);
79     SET_PERI_REG_MASK(PMU_IMM_HP_CK_POWER_REG, PMU_TIE_HIGH_GLOBAL_BBPLL_ICG);
80 }
81 
82 /**
83  * @brief Power down BBPLL circuit
84  */
clk_ll_bbpll_disable(void)85 static inline __attribute__((always_inline)) void clk_ll_bbpll_disable(void)
86 {
87     SET_PERI_REG_MASK(PMU_IMM_HP_CK_POWER_REG, PMU_TIE_LOW_GLOBAL_BBPLL_ICG) ;
88     SET_PERI_REG_MASK(PMU_IMM_HP_CK_POWER_REG, PMU_TIE_LOW_XPD_BBPLL | PMU_TIE_LOW_XPD_BBPLL_I2C);
89 }
90 
91 /**
92  * @brief Release the root clock source locked by PMU
93  */
clk_ll_cpu_clk_src_lock_release(void)94 static inline __attribute__((always_inline)) void clk_ll_cpu_clk_src_lock_release(void)
95 {
96     SET_PERI_REG_MASK(PMU_IMM_SLEEP_SYSCLK_REG, PMU_UPDATE_DIG_SYS_CLK_SEL);
97 }
98 
99 /**
100  * @brief Enable the 32kHz crystal oscillator
101  *
102  * @param mode Used to determine the xtal32k configuration parameters
103  */
clk_ll_xtal32k_enable(clk_ll_xtal32k_enable_mode_t mode)104 static inline __attribute__((always_inline)) void clk_ll_xtal32k_enable(clk_ll_xtal32k_enable_mode_t mode)
105 {
106     if (mode == CLK_LL_XTAL32K_ENABLE_MODE_EXTERNAL) {
107         // No need to configure anything for OSC_SLOW_CLK
108         return;
109     }
110     // Configure xtal32k
111     clk_ll_xtal32k_config_t cfg = CLK_LL_XTAL32K_CONFIG_DEFAULT();
112     LP_CLKRST.xtal32k.dac_xtal32k = cfg.dac;
113     LP_CLKRST.xtal32k.dres_xtal32k = cfg.dres;
114     LP_CLKRST.xtal32k.dgm_xtal32k = cfg.dgm;
115     LP_CLKRST.xtal32k.dbuf_xtal32k = cfg.dbuf;
116     // Enable xtal32k xpd
117     SET_PERI_REG_MASK(PMU_HP_SLEEP_LP_CK_POWER_REG, PMU_HP_SLEEP_XPD_XTAL32K);
118 }
119 
120 /**
121  * @brief Disable the 32kHz crystal oscillator
122  */
clk_ll_xtal32k_disable(void)123 static inline __attribute__((always_inline)) void clk_ll_xtal32k_disable(void)
124 {
125     // Disable xtal32k xpd
126     CLEAR_PERI_REG_MASK(PMU_HP_SLEEP_LP_CK_POWER_REG, PMU_HP_SLEEP_XPD_XTAL32K);
127 }
128 
129 /**
130  * @brief Get the state of the 32kHz crystal clock
131  *
132  * @return True if the 32kHz XTAL is enabled
133  */
clk_ll_xtal32k_is_enabled(void)134 static inline __attribute__((always_inline)) bool clk_ll_xtal32k_is_enabled(void)
135 {
136     return REG_GET_FIELD(PMU_HP_SLEEP_LP_CK_POWER_REG, PMU_HP_SLEEP_XPD_XTAL32K) == 1;
137 }
138 
139 /**
140  * @brief Enable the internal oscillator output for RC32K_CLK
141  */
clk_ll_rc32k_enable(void)142 static inline __attribute__((always_inline)) void clk_ll_rc32k_enable(void)
143 {
144     // Enable rc32k xpd status
145     SET_PERI_REG_MASK(PMU_HP_SLEEP_LP_CK_POWER_REG, PMU_HP_SLEEP_XPD_RC32K);
146 }
147 
148 /**
149  * @brief Disable the internal oscillator output for RC32K_CLK
150  */
clk_ll_rc32k_disable(void)151 static inline __attribute__((always_inline)) void clk_ll_rc32k_disable(void)
152 {
153     // Disable rc32k xpd status
154     CLEAR_PERI_REG_MASK(PMU_HP_SLEEP_LP_CK_POWER_REG, PMU_HP_SLEEP_XPD_RC32K);
155 }
156 
157 /**
158  * @brief Get the state of the internal oscillator for RC32K_CLK
159  *
160  * @return True if the oscillator is enabled
161  */
clk_ll_rc32k_is_enabled(void)162 static inline __attribute__((always_inline)) bool clk_ll_rc32k_is_enabled(void)
163 {
164     return REG_GET_FIELD(PMU_HP_SLEEP_LP_CK_POWER_REG, PMU_HP_SLEEP_XPD_RC32K) == 1;
165 }
166 
167 /**
168  * @brief Enable the internal oscillator output for RC_FAST_CLK
169  */
clk_ll_rc_fast_enable(void)170 static inline __attribute__((always_inline)) void clk_ll_rc_fast_enable(void)
171 {
172     SET_PERI_REG_MASK(PMU_HP_SLEEP_LP_CK_POWER_REG, PMU_HP_SLEEP_XPD_FOSC_CLK);
173 }
174 
175 /**
176  * @brief Disable the internal oscillator output for RC_FAST_CLK
177  */
clk_ll_rc_fast_disable(void)178 static inline __attribute__((always_inline)) void clk_ll_rc_fast_disable(void)
179 {
180     CLEAR_PERI_REG_MASK(PMU_HP_SLEEP_LP_CK_POWER_REG, PMU_HP_SLEEP_XPD_FOSC_CLK);
181 }
182 
183 /**
184  * @brief Get the state of the internal oscillator for RC_FAST_CLK
185  *
186  * @return True if the oscillator is enabled
187  */
clk_ll_rc_fast_is_enabled(void)188 static inline __attribute__((always_inline)) bool clk_ll_rc_fast_is_enabled(void)
189 {
190     return REG_GET_FIELD(PMU_HP_SLEEP_LP_CK_POWER_REG, PMU_HP_SLEEP_XPD_FOSC_CLK) == 1;
191 }
192 
193 /**
194  * @brief Enable the digital RC_FAST_CLK, which is used to support peripherals.
195  */
clk_ll_rc_fast_digi_enable(void)196 static inline __attribute__((always_inline)) void clk_ll_rc_fast_digi_enable(void)
197 {
198     LP_CLKRST.clk_to_hp.icg_hp_fosc = 1;
199 }
200 
201 /**
202  * @brief Disable the digital RC_FAST_CLK, which is used to support peripherals.
203  */
clk_ll_rc_fast_digi_disable(void)204 static inline __attribute__((always_inline)) void clk_ll_rc_fast_digi_disable(void)
205 {
206     LP_CLKRST.clk_to_hp.icg_hp_fosc = 0;
207 }
208 
209 /**
210  * @brief Get the state of the digital RC_FAST_CLK
211  *
212  * @return True if the digital RC_FAST_CLK is enabled
213  */
clk_ll_rc_fast_digi_is_enabled(void)214 static inline __attribute__((always_inline)) bool clk_ll_rc_fast_digi_is_enabled(void)
215 {
216     return LP_CLKRST.clk_to_hp.icg_hp_fosc;
217 }
218 
219 /**
220  * @brief Enable the digital XTAL32K_CLK, which is used to support peripherals.
221  */
clk_ll_xtal32k_digi_enable(void)222 static inline __attribute__((always_inline)) void clk_ll_xtal32k_digi_enable(void)
223 {
224     LP_CLKRST.clk_to_hp.icg_hp_xtal32k = 1;
225 }
226 
227 /**
228  * @brief Disable the digital XTAL32K_CLK, which is used to support peripherals.
229  */
clk_ll_xtal32k_digi_disable(void)230 static inline __attribute__((always_inline)) void clk_ll_xtal32k_digi_disable(void)
231 {
232     LP_CLKRST.clk_to_hp.icg_hp_xtal32k = 0;
233 }
234 
235 /**
236  * @brief Get the state of the digital XTAL32K_CLK
237  *
238  * @return True if the digital XTAL32K_CLK is enabled
239  */
clk_ll_xtal32k_digi_is_enabled(void)240 static inline __attribute__((always_inline)) bool clk_ll_xtal32k_digi_is_enabled(void)
241 {
242     return LP_CLKRST.clk_to_hp.icg_hp_xtal32k;
243 }
244 
245 /**
246  * @brief Enable the digital RC32K_CLK, which is used to support peripherals.
247  */
clk_ll_rc32k_digi_enable(void)248 static inline __attribute__((always_inline)) void clk_ll_rc32k_digi_enable(void)
249 {
250     LP_CLKRST.clk_to_hp.icg_hp_osc32k = 1;
251 }
252 
253 /**
254  * @brief Disable the digital RC32K_CLK, which is used to support peripherals.
255  */
clk_ll_rc32k_digi_disable(void)256 static inline __attribute__((always_inline)) void clk_ll_rc32k_digi_disable(void)
257 {
258     LP_CLKRST.clk_to_hp.icg_hp_osc32k = 0;
259 }
260 
261 /**
262  * @brief Get the state of the digital RC32K_CLK
263  *
264  * @return True if the digital RC32K_CLK is enabled
265  */
clk_ll_rc32k_digi_is_enabled(void)266 static inline __attribute__((always_inline)) bool clk_ll_rc32k_digi_is_enabled(void)
267 {
268     return LP_CLKRST.clk_to_hp.icg_hp_osc32k;
269 }
270 
271 /**
272  * @brief Get PLL_CLK frequency
273  *
274  * @return PLL clock frequency, in MHz. Returns 0 if register field value is invalid.
275  */
clk_ll_bbpll_get_freq_mhz(void)276 static inline __attribute__((always_inline)) uint32_t clk_ll_bbpll_get_freq_mhz(void)
277 {
278     // The target has a fixed 480MHz SPLL
279     return CLK_LL_PLL_480M_FREQ_MHZ;
280 }
281 
282 /**
283  * @brief Set BBPLL frequency from XTAL source (Digital part)
284  *
285  * @param pll_freq_mhz PLL frequency, in MHz
286  */
clk_ll_bbpll_set_freq_mhz(uint32_t pll_freq_mhz)287 static inline __attribute__((always_inline)) void clk_ll_bbpll_set_freq_mhz(uint32_t pll_freq_mhz)
288 {
289     // The target SPLL is fixed to 480MHz
290     // Do nothing
291     HAL_ASSERT(pll_freq_mhz == CLK_LL_PLL_480M_FREQ_MHZ);
292 }
293 
294 /**
295  * @brief Set BBPLL frequency from XTAL source (Analog part)
296  *
297  * @param pll_freq_mhz PLL frequency, in MHz
298  * @param xtal_freq_mhz XTAL frequency, in MHz
299  */
clk_ll_bbpll_set_config(uint32_t pll_freq_mhz,uint32_t xtal_freq_mhz)300 static inline __attribute__((always_inline)) void clk_ll_bbpll_set_config(uint32_t pll_freq_mhz, uint32_t xtal_freq_mhz)
301 {
302     HAL_ASSERT(pll_freq_mhz == CLK_LL_PLL_480M_FREQ_MHZ);
303     uint8_t div_ref;
304     uint8_t div7_0;
305     uint8_t dr1;
306     uint8_t dr3;
307     uint8_t dchgp;
308     uint8_t dcur;
309     uint8_t dbias;
310 
311     /* Configure 480M PLL */
312     switch (xtal_freq_mhz) {
313     case RTC_XTAL_FREQ_40M:
314     default:
315         div_ref = 0;
316         div7_0 = 8;
317         dr1 = 0;
318         dr3 = 0;
319         dchgp = 5;
320         dcur = 3;
321         dbias = 2;
322         break;
323     }
324     uint8_t i2c_bbpll_lref  = (dchgp << I2C_BBPLL_OC_DCHGP_LSB) | (div_ref);
325     uint8_t i2c_bbpll_div_7_0 = div7_0;
326     uint8_t i2c_bbpll_dcur = (1 << I2C_BBPLL_OC_DLREF_SEL_LSB ) | (3 << I2C_BBPLL_OC_DHREF_SEL_LSB) | dcur;
327     REGI2C_WRITE(I2C_BBPLL, I2C_BBPLL_OC_REF_DIV, i2c_bbpll_lref);
328     REGI2C_WRITE(I2C_BBPLL, I2C_BBPLL_OC_DIV_7_0, i2c_bbpll_div_7_0);
329     REGI2C_WRITE_MASK(I2C_BBPLL, I2C_BBPLL_OC_DR1, dr1);
330     REGI2C_WRITE_MASK(I2C_BBPLL, I2C_BBPLL_OC_DR3, dr3);
331     REGI2C_WRITE(I2C_BBPLL, I2C_BBPLL_OC_DCUR, i2c_bbpll_dcur);
332     REGI2C_WRITE_MASK(I2C_BBPLL, I2C_BBPLL_OC_VCO_DBIAS, dbias);
333 }
334 
335 /**
336  * @brief Select the clock source for CPU_CLK (SOC Clock Root)
337  *
338  * @param in_sel One of the clock sources in soc_cpu_clk_src_t
339  */
clk_ll_cpu_set_src(soc_cpu_clk_src_t in_sel)340 static inline __attribute__((always_inline)) void clk_ll_cpu_set_src(soc_cpu_clk_src_t in_sel)
341 {
342     switch (in_sel) {
343     case SOC_CPU_CLK_SRC_XTAL:
344         PCR.sysclk_conf.soc_clk_sel = 0;
345         break;
346     case SOC_CPU_CLK_SRC_PLL:
347         PCR.sysclk_conf.soc_clk_sel = 1;
348         break;
349     case SOC_CPU_CLK_SRC_RC_FAST:
350         PCR.sysclk_conf.soc_clk_sel = 2;
351         break;
352     default:
353         // Unsupported SOC_CLK mux input sel
354         abort();
355     }
356 }
357 
358 /**
359  * @brief Get the clock source for CPU_CLK (SOC Clock Root)
360  *
361  * @return Currently selected clock source (one of soc_cpu_clk_src_t values)
362  */
clk_ll_cpu_get_src(void)363 static inline __attribute__((always_inline)) soc_cpu_clk_src_t clk_ll_cpu_get_src(void)
364 {
365     uint32_t clk_sel = PCR.sysclk_conf.soc_clk_sel;
366     switch (clk_sel) {
367     case 0:
368         return SOC_CPU_CLK_SRC_XTAL;
369     case 1:
370         return SOC_CPU_CLK_SRC_PLL;
371     case 2:
372         return SOC_CPU_CLK_SRC_RC_FAST;
373     default:
374         // Invalid SOC_CLK_SEL value
375         return SOC_CPU_CLK_SRC_INVALID;
376     }
377 }
378 
379 /**
380  * @brief Set CPU_CLK's high-speed divider (valid when SOC_ROOT clock source is PLL)
381  *
382  * @param divider Divider. (PCR_HS_DIV_NUM + 1) * (PCR_CPU_HS_DIV_NUM + 1) = divider.
383  */
clk_ll_cpu_set_hs_divider(uint32_t divider)384 static inline __attribute__((always_inline)) void clk_ll_cpu_set_hs_divider(uint32_t divider)
385 {
386     // SOC_ROOT_CLK ---(1)---> HP_ROOT_CLK ---(2)---> CPU_CLK
387     // (1) not configurable for the target (HRO register field: PCR_HS_DIV_NUM)
388     // Fixed at 3 for HS clock source
389     // Corresponding register field value is PCR_HS_DIV_NUM=2
390     // (2) configurable
391     // HS divider option: 1, 2, 4 (PCR_CPU_HS_DIV_NUM=0, 1, 3)
392 
393     HAL_ASSERT(divider == 3 || divider == 4 || divider == 6 || divider == 12);
394     HAL_FORCE_MODIFY_U32_REG_FIELD(PCR.cpu_freq_conf, cpu_hs_div_num, (divider / 3) - 1);
395 
396     // 120MHz CPU freq cannot be achieved through divider, need to set force_120m
397     // This field is only valid if PCR_CPU_HS_DIV_NUM=0 and PCR_SOC_CLK_SEL=SOC_CPU_CLK_SRC_PLL
398     bool force_120m = (divider == 4) ? 1 : 0;
399     PCR.cpu_freq_conf.cpu_hs_120m_force = force_120m;
400 }
401 
402 /**
403  * @brief Set CPU_CLK's low-speed divider (valid when SOC_ROOT clock source is XTAL/RC_FAST)
404  *
405  * @param divider Divider. (PCR_LS_DIV_NUM + 1) * (PCR_CPU_LS_DIV_NUM + 1) = divider.
406  */
clk_ll_cpu_set_ls_divider(uint32_t divider)407 static inline __attribute__((always_inline)) void clk_ll_cpu_set_ls_divider(uint32_t divider)
408 {
409     // SOC_ROOT_CLK ---(1)---> HP_ROOT_CLK ---(2)---> CPU_CLK
410     // (1) not configurable for the target (HRO register field: PCR_LS_DIV_NUM)
411     // Fixed at 1 for LS clock source
412     // Corresponding register field value is PCR_LS_DIV_NUM=0
413     // (2) configurable
414     // LS divider option: 1, 2, 4, 8, 16, 32 (PCR_CPU_LS_DIV_NUM=0, 1, 3, 7, 15, 31)
415     HAL_ASSERT((divider > 0) && ((divider & (divider - 1)) == 0));
416     HAL_FORCE_MODIFY_U32_REG_FIELD(PCR.cpu_freq_conf, cpu_ls_div_num, divider - 1);
417 }
418 
419 /**
420  * @brief Get CPU_CLK's high-speed divider
421  *
422  * @return Divider. Divider = (PCR_HS_DIV_NUM + 1) * (PCR_CPU_HS_DIV_NUM + 1).
423  */
clk_ll_cpu_get_hs_divider(void)424 static inline __attribute__((always_inline)) uint32_t clk_ll_cpu_get_hs_divider(void)
425 {
426     uint32_t force_120m = PCR.cpu_freq_conf.cpu_hs_120m_force;
427     uint32_t cpu_hs_div = HAL_FORCE_READ_U32_REG_FIELD(PCR.cpu_freq_conf, cpu_hs_div_num);
428     if (cpu_hs_div == 0 && force_120m) {
429         return 4;
430     }
431     uint32_t hp_root_hs_div = HAL_FORCE_READ_U32_REG_FIELD(PCR.sysclk_conf, hs_div_num);
432     return (hp_root_hs_div + 1) * (cpu_hs_div + 1);
433 }
434 
435 /**
436  * @brief Get CPU_CLK's low-speed divider
437  *
438  * @return Divider. Divider = (PCR_LS_DIV_NUM + 1) * (PCR_CPU_LS_DIV_NUM + 1).
439  */
clk_ll_cpu_get_ls_divider(void)440 static inline __attribute__((always_inline)) uint32_t clk_ll_cpu_get_ls_divider(void)
441 {
442     uint32_t cpu_ls_div = HAL_FORCE_READ_U32_REG_FIELD(PCR.cpu_freq_conf, cpu_ls_div_num);
443     uint32_t hp_root_ls_div = HAL_FORCE_READ_U32_REG_FIELD(PCR.sysclk_conf, ls_div_num);
444     return (hp_root_ls_div + 1) * (cpu_ls_div + 1);
445 }
446 
447 /**
448  * @brief Set AHB_CLK's high-speed divider (valid when SOC_ROOT clock source is PLL)
449  *
450  * @param divider Divider. (PCR_HS_DIV_NUM + 1) * (PCR_AHB_HS_DIV_NUM + 1) = divider.
451  */
clk_ll_ahb_set_hs_divider(uint32_t divider)452 static inline __attribute__((always_inline)) void clk_ll_ahb_set_hs_divider(uint32_t divider)
453 {
454     // SOC_ROOT_CLK ---(1)---> HP_ROOT_CLK ---(2)---> AHB_CLK
455     // (1) not configurable for the target (HRO register field: PCR_HS_DIV_NUM)
456     // Fixed at 3 for HS clock source
457     // Corresponding register field value is PCR_HS_DIV_NUM=2
458     // (2) configurable
459     // HS divider option: 4, 8, 16 (PCR_AHB_HS_DIV_NUM=3, 7, 15)
460     HAL_ASSERT(divider == 12 || divider == 24 || divider == 48);
461     HAL_FORCE_MODIFY_U32_REG_FIELD(PCR.ahb_freq_conf, ahb_hs_div_num, (divider / 3) - 1);
462 }
463 
464 /**
465  * @brief Set AHB_CLK's low-speed divider (valid when SOC_ROOT clock source is XTAL/RC_FAST)
466  *
467  * @param divider Divider. (PCR_LS_DIV_NUM + 1) * (PCR_AHB_LS_DIV_NUM + 1) = divider.
468  */
clk_ll_ahb_set_ls_divider(uint32_t divider)469 static inline __attribute__((always_inline)) void clk_ll_ahb_set_ls_divider(uint32_t divider)
470 {
471     // SOC_ROOT_CLK ---(1)---> HP_ROOT_CLK ---(2)---> AHB_CLK
472     // (1) not configurable for the target (HRO register field: PCR_LS_DIV_NUM)
473     // Fixed at 1 for LS clock source
474     // Corresponding register field value is PCR_LS_DIV_NUM=0
475     // (2) configurable
476     // LS divider option: 1, 2, 4, 8, 16, 32 (PCR_CPU_LS_DIV_NUM=0, 1, 3, 7, 15, 31)
477     HAL_ASSERT((divider > 0) && ((divider & (divider - 1)) == 0));
478     HAL_FORCE_MODIFY_U32_REG_FIELD(PCR.ahb_freq_conf, ahb_ls_div_num, divider - 1);
479 }
480 
481 /**
482  * @brief Get AHB_CLK's high-speed divider
483  *
484  * @return Divider. Divider = (PCR_HS_DIV_NUM + 1) * (PCR_AHB_HS_DIV_NUM + 1).
485  */
clk_ll_ahb_get_hs_divider(void)486 static inline __attribute__((always_inline)) uint32_t clk_ll_ahb_get_hs_divider(void)
487 {
488     uint32_t ahb_hs_div = HAL_FORCE_READ_U32_REG_FIELD(PCR.ahb_freq_conf, ahb_hs_div_num);
489     uint32_t hp_root_hs_div = HAL_FORCE_READ_U32_REG_FIELD(PCR.sysclk_conf, hs_div_num);
490     return (hp_root_hs_div + 1) * (ahb_hs_div + 1);
491 }
492 
493 /**
494  * @brief Get AHB_CLK's low-speed divider
495  *
496  * @return Divider. Divider = (PCR_LS_DIV_NUM + 1) * (PCR_AHB_LS_DIV_NUM + 1).
497  */
clk_ll_ahb_get_ls_divider(void)498 static inline __attribute__((always_inline)) uint32_t clk_ll_ahb_get_ls_divider(void)
499 {
500     uint32_t ahb_ls_div = HAL_FORCE_READ_U32_REG_FIELD(PCR.ahb_freq_conf, ahb_ls_div_num);
501     uint32_t hp_root_ls_div = HAL_FORCE_READ_U32_REG_FIELD(PCR.sysclk_conf, ls_div_num);
502     return (hp_root_ls_div + 1) * (ahb_ls_div + 1);
503 }
504 
505 /**
506  * @brief Set APB_CLK divider. freq of APB_CLK = freq of AHB_CLK / divider
507  *
508  * @param divider Divider. PCR_APB_DIV_NUM = divider - 1.
509  */
clk_ll_apb_set_divider(uint32_t divider)510 static inline __attribute__((always_inline)) void clk_ll_apb_set_divider(uint32_t divider)
511 {
512     // AHB ------> APB
513     // Divider option: 1, 2, 4 (PCR_APB_DIV_NUM=0, 1, 3)
514     HAL_ASSERT(divider == 1 || divider == 2 || divider == 4);
515     HAL_FORCE_MODIFY_U32_REG_FIELD(PCR.apb_freq_conf, apb_div_num, divider - 1);
516 }
517 
518 /**
519  * @brief Get APB_CLK divider
520  *
521  * @return Divider. Divider = (PCR_APB_DIV_NUM + 1).
522  */
clk_ll_apb_get_divider(void)523 static inline __attribute__((always_inline)) uint32_t clk_ll_apb_get_divider(void)
524 {
525     return HAL_FORCE_READ_U32_REG_FIELD(PCR.apb_freq_conf, apb_div_num) + 1;
526 }
527 
528 /**
529  * @brief Set MSPI_FAST_CLK's high-speed divider (valid when SOC_ROOT clock source is PLL)
530  *
531  * @param divider Divider.
532  */
clk_ll_mspi_fast_set_hs_divider(uint32_t divider)533 static inline __attribute__((always_inline)) void clk_ll_mspi_fast_set_hs_divider(uint32_t divider)
534 {
535     // SOC_ROOT_CLK ------> MSPI_FAST_CLK
536     // HS divider option: 4, 5, 6 (PCR_MSPI_FAST_HS_DIV_NUM=3, 4, 5)
537     uint32_t div_num = 0;
538     switch (divider) {
539     case 4:
540        div_num = 3;
541         break;
542     case 5:
543         div_num = 4;
544         break;
545     case 6:
546         div_num = 5;
547         break;
548     default:
549         // Unsupported HS MSPI_FAST divider
550         abort();
551     }
552     HAL_FORCE_MODIFY_U32_REG_FIELD(PCR.mspi_clk_conf, mspi_fast_hs_div_num, div_num);
553 }
554 
555 /**
556  * @brief Set MSPI_FAST_CLK's low-speed divider (valid when SOC_ROOT clock source is XTAL/RC_FAST)
557  *
558  * @param divider Divider.
559  */
clk_ll_mspi_fast_set_ls_divider(uint32_t divider)560 static inline __attribute__((always_inline)) void clk_ll_mspi_fast_set_ls_divider(uint32_t divider)
561 {
562     // SOC_ROOT_CLK ------> MSPI_FAST_CLK
563     // LS divider option: 1, 2, 4 (PCR_MSPI_FAST_LS_DIV_NUM=0, 1, 2)
564     uint32_t div_num = 0;
565     switch (divider) {
566     case 1:
567         div_num = 0;
568         break;
569     case 2:
570         div_num = 1;
571         break;
572     case 4:
573         div_num = 2;
574         break;
575     default:
576         // Unsupported LS MSPI_FAST divider
577         abort();
578     }
579     HAL_FORCE_MODIFY_U32_REG_FIELD(PCR.mspi_clk_conf, mspi_fast_ls_div_num, div_num);
580 }
581 
582 /**
583  * @brief Select the calibration 32kHz clock source for timergroup0
584  *
585  * @param in_sel One of the 32kHz clock sources (RC32K_CLK, XTAL32K_CLK, OSC_SLOW_CLK)
586  */
clk_ll_32k_calibration_set_target(soc_rtc_slow_clk_src_t in_sel)587 static inline __attribute__((always_inline)) void clk_ll_32k_calibration_set_target(soc_rtc_slow_clk_src_t in_sel)
588 {
589     switch (in_sel) {
590     case SOC_RTC_SLOW_CLK_SRC_RC32K:
591         PCR.ctrl_32k_conf.clk_32k_sel = 0;
592         break;
593     case SOC_RTC_SLOW_CLK_SRC_XTAL32K:
594         PCR.ctrl_32k_conf.clk_32k_sel = 1;
595         break;
596     case SOC_RTC_SLOW_CLK_SRC_OSC_SLOW:
597         PCR.ctrl_32k_conf.clk_32k_sel = 2;
598         break;
599     default:
600         // Unsupported 32K_SEL mux input
601         abort();
602     }
603 }
604 
605 /**
606  * @brief Get the calibration 32kHz clock source for timergroup0
607  *
608  * @return soc_rtc_slow_clk_src_t Currently selected calibration 32kHz clock (one of the 32kHz clocks)
609  */
clk_ll_32k_calibration_get_target(void)610 static inline __attribute__((always_inline)) soc_rtc_slow_clk_src_t clk_ll_32k_calibration_get_target(void)
611 {
612     uint32_t clk_sel = PCR.ctrl_32k_conf.clk_32k_sel;
613     switch (clk_sel) {
614     case 0:
615         return SOC_RTC_SLOW_CLK_SRC_RC32K;
616     case 1:
617         return SOC_RTC_SLOW_CLK_SRC_XTAL32K;
618     case 2:
619         return SOC_RTC_SLOW_CLK_SRC_OSC_SLOW;
620     default:
621         return SOC_RTC_SLOW_CLK_SRC_INVALID;
622     }
623 }
624 
625 /**
626  * @brief Select the clock source for RTC_SLOW_CLK
627  *
628  * @param in_sel One of the clock sources in soc_rtc_slow_clk_src_t
629  */
clk_ll_rtc_slow_set_src(soc_rtc_slow_clk_src_t in_sel)630 static inline __attribute__((always_inline)) void clk_ll_rtc_slow_set_src(soc_rtc_slow_clk_src_t in_sel)
631 {
632     switch (in_sel) {
633     case SOC_RTC_SLOW_CLK_SRC_RC_SLOW:
634         LP_CLKRST.lp_clk_conf.slow_clk_sel = 0;
635         break;
636     case SOC_RTC_SLOW_CLK_SRC_XTAL32K:
637         LP_CLKRST.lp_clk_conf.slow_clk_sel = 1;
638         break;
639     case SOC_RTC_SLOW_CLK_SRC_RC32K:
640         LP_CLKRST.lp_clk_conf.slow_clk_sel = 2;
641         break;
642     case SOC_RTC_SLOW_CLK_SRC_OSC_SLOW:
643         LP_CLKRST.lp_clk_conf.slow_clk_sel = 3;
644         break;
645     default:
646         // Unsupported RTC_SLOW_CLK mux input sel
647         abort();
648     }
649 }
650 
651 /**
652  * @brief Get the clock source for RTC_SLOW_CLK
653  *
654  * @return Currently selected clock source (one of soc_rtc_slow_clk_src_t values)
655  */
clk_ll_rtc_slow_get_src(void)656 static inline __attribute__((always_inline)) soc_rtc_slow_clk_src_t clk_ll_rtc_slow_get_src(void)
657 {
658     uint32_t clk_sel = LP_CLKRST.lp_clk_conf.slow_clk_sel;
659     switch (clk_sel) {
660     case 0:
661         return SOC_RTC_SLOW_CLK_SRC_RC_SLOW;
662     case 1:
663         return SOC_RTC_SLOW_CLK_SRC_XTAL32K;
664     case 2:
665         return SOC_RTC_SLOW_CLK_SRC_RC32K;
666     case 3:
667         return SOC_RTC_SLOW_CLK_SRC_OSC_SLOW;
668     default:
669         return SOC_RTC_SLOW_CLK_SRC_INVALID;
670     }
671 }
672 
673 /**
674  * @brief Select the clock source for RTC_FAST_CLK
675  *
676  * @param in_sel One of the clock sources in soc_rtc_fast_clk_src_t
677  */
clk_ll_rtc_fast_set_src(soc_rtc_fast_clk_src_t in_sel)678 static inline __attribute__((always_inline)) void clk_ll_rtc_fast_set_src(soc_rtc_fast_clk_src_t in_sel)
679 {
680     switch (in_sel) {
681     case SOC_RTC_FAST_CLK_SRC_RC_FAST:
682         LP_CLKRST.lp_clk_conf.fast_clk_sel = 0;
683         break;
684     case SOC_RTC_FAST_CLK_SRC_XTAL_D2:
685         LP_CLKRST.lp_clk_conf.fast_clk_sel = 1;
686         break;
687     default:
688         // Unsupported RTC_FAST_CLK mux input sel
689         abort();
690     }
691 }
692 
693 /**
694  * @brief Get the clock source for RTC_FAST_CLK
695  *
696  * @return Currently selected clock source (one of soc_rtc_fast_clk_src_t values)
697  */
clk_ll_rtc_fast_get_src(void)698 static inline __attribute__((always_inline)) soc_rtc_fast_clk_src_t clk_ll_rtc_fast_get_src(void)
699 {
700     uint32_t clk_sel = LP_CLKRST.lp_clk_conf.fast_clk_sel;
701     switch (clk_sel) {
702     case 0:
703         return SOC_RTC_FAST_CLK_SRC_RC_FAST;
704     case 1:
705         return SOC_RTC_FAST_CLK_SRC_XTAL_D2;
706     default:
707         return SOC_RTC_FAST_CLK_SRC_INVALID;
708     }
709 }
710 
711 /**
712  * @brief Set RC_FAST_CLK divider. The output from the divider is passed into rtc_fast_clk MUX.
713  *
714  * @param divider Divider of RC_FAST_CLK. Usually this divider is set to 1 (reg. value is 0) in bootloader stage.
715  */
clk_ll_rc_fast_set_divider(uint32_t divider)716 static inline __attribute__((always_inline)) void clk_ll_rc_fast_set_divider(uint32_t divider)
717 {
718     // No divider on the target
719     HAL_ASSERT(divider == 1);
720 }
721 
722 /**
723  * @brief Get RC_FAST_CLK divider
724  *
725  * @return Divider. Divider = (CK8M_DIV_SEL + 1).
726  */
clk_ll_rc_fast_get_divider(void)727 static inline __attribute__((always_inline)) uint32_t clk_ll_rc_fast_get_divider(void)
728 {
729     // No divider on the target, always return divider = 1
730     return 1;
731 }
732 
733 /**
734  * @brief Set RC_SLOW_CLK divider
735  *
736  * @param divider Divider of RC_SLOW_CLK. Usually this divider is set to 1 (reg. value is 0) in bootloader stage.
737  */
clk_ll_rc_slow_set_divider(uint32_t divider)738 static inline __attribute__((always_inline)) void clk_ll_rc_slow_set_divider(uint32_t divider)
739 {
740     // No divider on the target
741     HAL_ASSERT(divider == 1);
742 }
743 
744 /************************** LP STORAGE REGISTER STORE/LOAD **************************/
745 /**
746  * @brief Store XTAL_CLK frequency in RTC storage register
747  *
748  * Value of RTC_XTAL_FREQ_REG is stored as two copies in lower and upper 16-bit
749  * halves. These are the routines to work with that representation.
750  *
751  * @param xtal_freq_mhz XTAL frequency, in MHz. The frequency must necessarily be even,
752  * otherwise there will be a conflict with the low bit, which is used to disable logs
753  * in the ROM code.
754  */
clk_ll_xtal_store_freq_mhz(uint32_t xtal_freq_mhz)755 static inline __attribute__((always_inline)) void clk_ll_xtal_store_freq_mhz(uint32_t xtal_freq_mhz)
756 {
757     // Read the status of whether disabling logging from ROM code
758     uint32_t reg = READ_PERI_REG(RTC_XTAL_FREQ_REG) & RTC_DISABLE_ROM_LOG;
759     // If so, need to write back this setting
760     if (reg == RTC_DISABLE_ROM_LOG) {
761         xtal_freq_mhz |= 1;
762     }
763     WRITE_PERI_REG(RTC_XTAL_FREQ_REG, (xtal_freq_mhz & UINT16_MAX) | ((xtal_freq_mhz & UINT16_MAX) << 16));
764 }
765 
766 /**
767  * @brief Load XTAL_CLK frequency from RTC storage register
768  *
769  * Value of RTC_XTAL_FREQ_REG is stored as two copies in lower and upper 16-bit
770  * halves. These are the routines to work with that representation.
771  *
772  * @return XTAL frequency, in MHz. Returns 0 if value in reg is invalid.
773  */
clk_ll_xtal_load_freq_mhz(void)774 static inline __attribute__((always_inline)) uint32_t clk_ll_xtal_load_freq_mhz(void)
775 {
776     // Read from RTC storage register
777     uint32_t xtal_freq_reg = READ_PERI_REG(RTC_XTAL_FREQ_REG);
778     if ((xtal_freq_reg & 0xFFFF) == ((xtal_freq_reg >> 16) & 0xFFFF) &&
779         xtal_freq_reg != 0 && xtal_freq_reg != UINT32_MAX) {
780         return xtal_freq_reg & ~RTC_DISABLE_ROM_LOG & UINT16_MAX;
781     }
782     // If the format in reg is invalid
783     return 0;
784 }
785 
786 /**
787  * @brief Store RTC_SLOW_CLK calibration value in RTC storage register
788  *
789  * Value of RTC_SLOW_CLK_CAL_REG has to be in the same format as returned by rtc_clk_cal (microseconds,
790  * in Q13.19 fixed-point format).
791  *
792  * @param cal_value The calibration value of slow clock period in microseconds, in Q13.19 fixed point format
793  */
clk_ll_rtc_slow_store_cal(uint32_t cal_value)794 static inline __attribute__((always_inline)) void clk_ll_rtc_slow_store_cal(uint32_t cal_value)
795 {
796     REG_WRITE(RTC_SLOW_CLK_CAL_REG, cal_value);
797 }
798 
799 /**
800  * @brief Load the calibration value of RTC_SLOW_CLK frequency from RTC storage register
801  *
802  * This value gets updated (i.e. rtc slow clock gets calibrated) every time RTC_SLOW_CLK source switches
803  *
804  * @return The calibration value of slow clock period in microseconds, in Q13.19 fixed point format
805  */
clk_ll_rtc_slow_load_cal(void)806 static inline __attribute__((always_inline)) uint32_t clk_ll_rtc_slow_load_cal(void)
807 {
808     return REG_READ(RTC_SLOW_CLK_CAL_REG);
809 }
810 
811 /*
812 Set the frequency division factor of ref_tick
813 */
clk_ll_rc_fast_tick_conf(void)814 static inline void clk_ll_rc_fast_tick_conf(void)
815 {
816     PCR.ctrl_tick_conf.fosc_tick_num = REG_FOSC_TICK_NUM;
817 }
818 
819 #ifdef __cplusplus
820 }
821 #endif
822