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