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