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