1 /*
2 * SPDX-FileCopyrightText: 2015-2024 Espressif Systems (Shanghai) CO LTD
3 *
4 * SPDX-License-Identifier: Apache-2.0
5 */
6
7 #include <stdbool.h>
8 #include <stdint.h>
9 #include <stddef.h>
10 #include <assert.h>
11 #include <stdlib.h>
12 #include "sdkconfig.h"
13 #include "esp32s3/rom/rtc.h"
14 #include "soc/rtc.h"
15 #include "soc/io_mux_reg.h"
16 #include "esp_private/rtc_clk.h"
17 #include "soc/rtc_io_reg.h"
18 #include "esp_rom_sys.h"
19 #include "esp_hw_log.h"
20 #include "hal/clk_tree_ll.h"
21 #include "hal/regi2c_ctrl_ll.h"
22 #include "esp_private/regi2c_ctrl.h"
23 #include "soc/regi2c_dig_reg.h"
24 #include "soc/sens_reg.h"
25 #include "sdkconfig.h"
26
27 static const char *TAG = "rtc_clk";
28
29 // Current PLL frequency, in MHZ (320 or 480). Zero if PLL is not enabled.
30 static uint32_t s_cur_pll_freq;
31
32 static uint32_t s_apb_freq;
33
34 void rtc_clk_cpu_freq_to_xtal(int freq, int div);
35 static void rtc_clk_cpu_freq_to_8m(void);
36
37 extern uint32_t g_dig_dbias_pvt_240m;
38 extern uint32_t g_rtc_dbias_pvt_240m;
39 extern uint32_t g_dig_dbias_pvt_non_240m;
40 extern uint32_t g_rtc_dbias_pvt_non_240m;
41
42 static uint32_t s_bbpll_digi_consumers_ref_count = 0; // Currently, it only tracks whether the 48MHz PHY clock is in-use by USB Serial/JTAG
43
rtc_clk_bbpll_add_consumer(void)44 void rtc_clk_bbpll_add_consumer(void)
45 {
46 s_bbpll_digi_consumers_ref_count += 1;
47 }
48
rtc_clk_bbpll_remove_consumer(void)49 void rtc_clk_bbpll_remove_consumer(void)
50 {
51 s_bbpll_digi_consumers_ref_count -= 1;
52 }
53
rtc_clk_32k_enable(bool enable)54 void rtc_clk_32k_enable(bool enable)
55 {
56 if (enable) {
57 SET_PERI_REG_MASK(RTC_IO_XTAL_32P_PAD_REG, RTC_IO_X32P_MUX_SEL);
58 SET_PERI_REG_MASK(RTC_IO_XTAL_32N_PAD_REG, RTC_IO_X32N_MUX_SEL);
59 clk_ll_xtal32k_enable(CLK_LL_XTAL32K_ENABLE_MODE_CRYSTAL);
60 } else {
61 clk_ll_xtal32k_disable();
62 }
63 }
64
rtc_clk_32k_enable_external(void)65 void rtc_clk_32k_enable_external(void)
66 {
67 PIN_INPUT_ENABLE(IO_MUX_GPIO15_REG);
68 SET_PERI_REG_MASK(SENS_SAR_PERI_CLK_GATE_CONF_REG, SENS_IOMUX_CLK_EN);
69 SET_PERI_REG_MASK(RTC_CNTL_PAD_HOLD_REG, RTC_CNTL_X32P_HOLD);
70 clk_ll_xtal32k_enable(CLK_LL_XTAL32K_ENABLE_MODE_EXTERNAL);
71 }
72
rtc_clk_32k_bootstrap(uint32_t cycle)73 void rtc_clk_32k_bootstrap(uint32_t cycle)
74 {
75 /* No special bootstrapping needed for ESP32-S3, 'cycle' argument is to keep the signature
76 * same as for the ESP32. Just enable the XTAL here.
77 */
78 (void)cycle;
79 rtc_clk_32k_enable(true);
80 }
81
rtc_clk_32k_enabled(void)82 bool rtc_clk_32k_enabled(void)
83 {
84 return clk_ll_xtal32k_is_enabled();
85 }
86
rtc_clk_8m_enable(bool clk_8m_en,bool d256_en)87 void rtc_clk_8m_enable(bool clk_8m_en, bool d256_en)
88 {
89 if (clk_8m_en) {
90 clk_ll_rc_fast_enable();
91 esp_rom_delay_us(SOC_DELAY_RC_FAST_ENABLE);
92 } else {
93 clk_ll_rc_fast_disable();
94 }
95 /* d256 should be independent configured with 8M
96 * Maybe we can split this function into 8m and dmd256
97 */
98 if (d256_en) {
99 clk_ll_rc_fast_d256_enable();
100 } else {
101 clk_ll_rc_fast_d256_disable();
102 }
103 }
104
rtc_clk_8m_enabled(void)105 bool rtc_clk_8m_enabled(void)
106 {
107 return clk_ll_rc_fast_is_enabled();
108 }
109
rtc_clk_8md256_enabled(void)110 bool rtc_clk_8md256_enabled(void)
111 {
112 return clk_ll_rc_fast_d256_is_enabled();
113 }
114
rtc_clk_slow_src_set(soc_rtc_slow_clk_src_t clk_src)115 void rtc_clk_slow_src_set(soc_rtc_slow_clk_src_t clk_src)
116 {
117 clk_ll_rtc_slow_set_src(clk_src);
118
119 /* Why we need to connect this clock to digital?
120 * Or maybe this clock should be connected to digital when xtal 32k clock is enabled instead?
121 */
122 if (clk_src == SOC_RTC_SLOW_CLK_SRC_XTAL32K) {
123 clk_ll_xtal32k_digi_enable();
124 } else {
125 clk_ll_xtal32k_digi_disable();
126 }
127
128 esp_rom_delay_us(SOC_DELAY_RTC_SLOW_CLK_SWITCH);
129 }
130
rtc_clk_slow_src_get(void)131 soc_rtc_slow_clk_src_t rtc_clk_slow_src_get(void)
132 {
133 return clk_ll_rtc_slow_get_src();
134 }
135
rtc_clk_slow_freq_get_hz(void)136 uint32_t rtc_clk_slow_freq_get_hz(void)
137 {
138 switch (rtc_clk_slow_src_get()) {
139 case SOC_RTC_SLOW_CLK_SRC_RC_SLOW: return SOC_CLK_RC_SLOW_FREQ_APPROX;
140 case SOC_RTC_SLOW_CLK_SRC_XTAL32K: return SOC_CLK_XTAL32K_FREQ_APPROX;
141 case SOC_RTC_SLOW_CLK_SRC_RC_FAST_D256: return SOC_CLK_RC_FAST_D256_FREQ_APPROX;
142 default: return 0;
143 }
144 }
145
rtc_clk_fast_src_set(soc_rtc_fast_clk_src_t clk_src)146 void rtc_clk_fast_src_set(soc_rtc_fast_clk_src_t clk_src)
147 {
148 clk_ll_rtc_fast_set_src(clk_src);
149 esp_rom_delay_us(SOC_DELAY_RTC_FAST_CLK_SWITCH);
150 }
151
rtc_clk_fast_src_get(void)152 soc_rtc_fast_clk_src_t rtc_clk_fast_src_get(void)
153 {
154 return clk_ll_rtc_fast_get_src();
155 }
156
rtc_clk_bbpll_disable(void)157 static void rtc_clk_bbpll_disable(void)
158 {
159 clk_ll_bbpll_disable();
160 s_cur_pll_freq = 0;
161 }
162
rtc_clk_bbpll_enable(void)163 static void rtc_clk_bbpll_enable(void)
164 {
165 clk_ll_bbpll_enable();
166 }
167
rtc_clk_bbpll_configure(rtc_xtal_freq_t xtal_freq,int pll_freq)168 static void rtc_clk_bbpll_configure(rtc_xtal_freq_t xtal_freq, int pll_freq)
169 {
170 /* Digital part */
171 clk_ll_bbpll_set_freq_mhz(pll_freq);
172 /* Analog part */
173 /* BBPLL CALIBRATION START */
174 regi2c_ctrl_ll_bbpll_calibration_start();
175 clk_ll_bbpll_set_config(pll_freq, xtal_freq);
176 /* WAIT CALIBRATION DONE */
177 while(!regi2c_ctrl_ll_bbpll_calibration_is_done());
178 esp_rom_delay_us(10);
179 /* BBPLL CALIBRATION STOP */
180 regi2c_ctrl_ll_bbpll_calibration_stop();
181
182 s_cur_pll_freq = pll_freq;
183 }
184
185 /**
186 * Switch to one of PLL-based frequencies. Current frequency can be XTAL or PLL.
187 * PLL must already be enabled.
188 * @param cpu_freq new CPU frequency
189 */
rtc_clk_cpu_freq_to_pll_mhz(int cpu_freq_mhz)190 static void rtc_clk_cpu_freq_to_pll_mhz(int cpu_freq_mhz)
191 {
192 /* There are totally 6 LDO slaves(all on by default). At the moment of switching LDO slave, LDO voltage will also change instantaneously.
193 * LDO slave can reduce the voltage change caused by switching frequency.
194 * CPU frequency <= 40M : just open 3 LDO slaves; CPU frequency = 80M : open 4 LDO slaves; CPU frequency = 160M : open 5 LDO slaves; CPU frequency = 240M : open 6 LDO slaves;
195 *
196 * LDO voltage will decrease at the moment of switching from low frequency to high frequency; otherwise, LDO voltage will increase.
197 * In order to reduce LDO voltage drop, LDO voltage should rise first then fall.
198 */
199 int pd_slave = cpu_freq_mhz / 80;
200 rtc_cpu_freq_config_t cur_config;
201 rtc_clk_cpu_freq_get_config(&cur_config);
202 /* cpu_frequency < 240M: dbias = pvt-dig + 2;
203 * cpu_frequency = 240M: dbias = pvt-dig + 3;
204 */
205 if (cpu_freq_mhz > cur_config.freq_mhz) {
206 if (cpu_freq_mhz == 240) {
207 REGI2C_WRITE_MASK(I2C_DIG_REG, I2C_DIG_REG_EXT_RTC_DREG, g_rtc_dbias_pvt_240m);
208 REGI2C_WRITE_MASK(I2C_DIG_REG, I2C_DIG_REG_EXT_DIG_DREG, g_dig_dbias_pvt_240m);
209 esp_rom_delay_us(40);
210 }
211 REG_SET_FIELD(RTC_CNTL_DATE_REG, RTC_CNTL_SLAVE_PD, DEFAULT_LDO_SLAVE >> pd_slave);
212 }
213
214 clk_ll_cpu_set_freq_mhz_from_pll(cpu_freq_mhz);
215 clk_ll_cpu_set_divider(1);
216 /* switch clock source */
217 clk_ll_cpu_set_src(SOC_CPU_CLK_SRC_PLL);
218 rtc_clk_apb_freq_update(80 * MHZ);
219 esp_rom_set_cpu_ticks_per_us(cpu_freq_mhz);
220
221 if (cpu_freq_mhz < cur_config.freq_mhz) {
222 if (cur_config.freq_mhz == 240) {
223 REGI2C_WRITE_MASK(I2C_DIG_REG, I2C_DIG_REG_EXT_RTC_DREG, g_rtc_dbias_pvt_non_240m);
224 REGI2C_WRITE_MASK(I2C_DIG_REG, I2C_DIG_REG_EXT_DIG_DREG, g_dig_dbias_pvt_non_240m);
225 esp_rom_delay_us(40);
226 }
227
228 REG_SET_FIELD(RTC_CNTL_DATE_REG, RTC_CNTL_SLAVE_PD, DEFAULT_LDO_SLAVE >> pd_slave);
229 }
230 }
231
rtc_clk_cpu_freq_mhz_to_config(uint32_t freq_mhz,rtc_cpu_freq_config_t * out_config)232 bool rtc_clk_cpu_freq_mhz_to_config(uint32_t freq_mhz, rtc_cpu_freq_config_t *out_config)
233 {
234 uint32_t source_freq_mhz;
235 soc_cpu_clk_src_t source;
236 uint32_t divider;
237 uint32_t real_freq_mhz;
238
239 uint32_t xtal_freq = (uint32_t)rtc_clk_xtal_freq_get();
240 if (freq_mhz <= xtal_freq && freq_mhz != 0) {
241 divider = xtal_freq / freq_mhz;
242 real_freq_mhz = (xtal_freq + divider / 2) / divider; /* round */
243 if (real_freq_mhz != freq_mhz) {
244 // no suitable divider
245 return false;
246 }
247
248 source_freq_mhz = xtal_freq;
249 source = SOC_CPU_CLK_SRC_XTAL;
250 } else if (freq_mhz == 80) {
251 real_freq_mhz = freq_mhz;
252 source = SOC_CPU_CLK_SRC_PLL;
253 source_freq_mhz = CLK_LL_PLL_480M_FREQ_MHZ;
254 divider = 6;
255 } else if (freq_mhz == 160) {
256 real_freq_mhz = freq_mhz;
257 source = SOC_CPU_CLK_SRC_PLL;
258 source_freq_mhz = CLK_LL_PLL_480M_FREQ_MHZ;
259 divider = 3;
260 } else if (freq_mhz == 240) {
261 real_freq_mhz = freq_mhz;
262 source = SOC_CPU_CLK_SRC_PLL;
263 source_freq_mhz = CLK_LL_PLL_480M_FREQ_MHZ;
264 divider = 2;
265 } else {
266 // unsupported frequency
267 return false;
268 }
269 *out_config = (rtc_cpu_freq_config_t) {
270 .source = source,
271 .div = divider,
272 .source_freq_mhz = source_freq_mhz,
273 .freq_mhz = real_freq_mhz
274 };
275 return true;
276 }
277
rtc_clk_cpu_freq_set_config(const rtc_cpu_freq_config_t * config)278 void rtc_clk_cpu_freq_set_config(const rtc_cpu_freq_config_t *config)
279 {
280 soc_cpu_clk_src_t old_cpu_clk_src = clk_ll_cpu_get_src();
281 if (config->source == SOC_CPU_CLK_SRC_XTAL) {
282 rtc_clk_cpu_freq_to_xtal(config->freq_mhz, config->div);
283 if ((old_cpu_clk_src == SOC_CPU_CLK_SRC_PLL) && !s_bbpll_digi_consumers_ref_count) {
284 // We don't turn off the bbpll if some consumers depend on bbpll
285 rtc_clk_bbpll_disable();
286 }
287 } else if (config->source == SOC_CPU_CLK_SRC_PLL) {
288 if (old_cpu_clk_src != SOC_CPU_CLK_SRC_PLL) {
289 rtc_clk_bbpll_enable();
290 rtc_clk_bbpll_configure(rtc_clk_xtal_freq_get(), config->source_freq_mhz);
291 }
292 rtc_clk_cpu_freq_to_pll_mhz(config->freq_mhz);
293 } else if (config->source == SOC_CPU_CLK_SRC_RC_FAST) {
294 rtc_clk_cpu_freq_to_8m();
295 if ((old_cpu_clk_src == SOC_CPU_CLK_SRC_PLL) && !s_bbpll_digi_consumers_ref_count) {
296 // We don't turn off the bbpll if some consumers depend on bbpll
297 rtc_clk_bbpll_disable();
298 }
299 }
300 }
301
rtc_clk_cpu_freq_get_config(rtc_cpu_freq_config_t * out_config)302 void rtc_clk_cpu_freq_get_config(rtc_cpu_freq_config_t *out_config)
303 {
304 soc_cpu_clk_src_t source = clk_ll_cpu_get_src();
305 uint32_t source_freq_mhz;
306 uint32_t div;
307 uint32_t freq_mhz;
308 switch (source) {
309 case SOC_CPU_CLK_SRC_XTAL: {
310 div = clk_ll_cpu_get_divider();
311 source_freq_mhz = (uint32_t)rtc_clk_xtal_freq_get();
312 freq_mhz = source_freq_mhz / div;
313 }
314 break;
315 case SOC_CPU_CLK_SRC_PLL: {
316 freq_mhz = clk_ll_cpu_get_freq_mhz_from_pll();
317 source_freq_mhz = clk_ll_bbpll_get_freq_mhz();
318 if (freq_mhz == CLK_LL_PLL_80M_FREQ_MHZ) {
319 div = (source_freq_mhz == CLK_LL_PLL_480M_FREQ_MHZ) ? 6 : 4;
320 } else if (freq_mhz == CLK_LL_PLL_160M_FREQ_MHZ) {
321 div = (source_freq_mhz == CLK_LL_PLL_480M_FREQ_MHZ) ? 3 : 2;
322 } else if (freq_mhz == CLK_LL_PLL_240M_FREQ_MHZ && source_freq_mhz == CLK_LL_PLL_480M_FREQ_MHZ) {
323 div = 2;
324 } else {
325 ESP_HW_LOGE(TAG, "unsupported frequency configuration");
326 return;
327 }
328 break;
329 }
330 case SOC_CPU_CLK_SRC_RC_FAST:
331 source_freq_mhz = 20;
332 div = 1;
333 freq_mhz = source_freq_mhz;
334 break;
335 default:
336 ESP_HW_LOGE(TAG, "unsupported frequency configuration");
337 return;
338 }
339 *out_config = (rtc_cpu_freq_config_t) {
340 .source = source,
341 .source_freq_mhz = source_freq_mhz,
342 .div = div,
343 .freq_mhz = freq_mhz
344 };
345 }
346
rtc_clk_cpu_freq_set_config_fast(const rtc_cpu_freq_config_t * config)347 void rtc_clk_cpu_freq_set_config_fast(const rtc_cpu_freq_config_t *config)
348 {
349 if (config->source == SOC_CPU_CLK_SRC_XTAL) {
350 rtc_clk_cpu_freq_to_xtal(config->freq_mhz, config->div);
351 } else if (config->source == SOC_CPU_CLK_SRC_PLL &&
352 s_cur_pll_freq == config->source_freq_mhz) {
353 rtc_clk_cpu_freq_to_pll_mhz(config->freq_mhz);
354 } else {
355 /* fallback */
356 rtc_clk_cpu_freq_set_config(config);
357 }
358 }
359
rtc_clk_cpu_freq_set_xtal(void)360 void rtc_clk_cpu_freq_set_xtal(void)
361 {
362 rtc_clk_cpu_set_to_default_config();
363 rtc_clk_bbpll_disable();
364 }
365
rtc_clk_cpu_set_to_default_config(void)366 void rtc_clk_cpu_set_to_default_config(void)
367 {
368 int freq_mhz = (int)rtc_clk_xtal_freq_get();
369
370 rtc_clk_cpu_freq_to_xtal(freq_mhz, 1);
371 }
372
373 /**
374 * Switch to use XTAL as the CPU clock source.
375 * Must satisfy: cpu_freq = XTAL_FREQ / div.
376 * Does not disable the PLL.
377 *
378 * Public function for testing only.
379 */
rtc_clk_cpu_freq_to_xtal(int cpu_freq,int div)380 void rtc_clk_cpu_freq_to_xtal(int cpu_freq, int div)
381 {
382 rtc_cpu_freq_config_t cur_config;
383 rtc_clk_cpu_freq_get_config(&cur_config);
384
385 esp_rom_set_cpu_ticks_per_us(cpu_freq);
386 /* Set divider from XTAL to APB clock. Need to set divider to 1 (reg. value 0) first. */
387 clk_ll_cpu_set_divider(1);
388 clk_ll_cpu_set_divider(div);
389 /* switch clock source */
390 clk_ll_cpu_set_src(SOC_CPU_CLK_SRC_XTAL);
391 rtc_clk_apb_freq_update(cpu_freq * MHZ);
392
393 if (cur_config.freq_mhz == 240) {
394 REGI2C_WRITE_MASK(I2C_DIG_REG, I2C_DIG_REG_EXT_RTC_DREG, g_rtc_dbias_pvt_non_240m);
395 REGI2C_WRITE_MASK(I2C_DIG_REG, I2C_DIG_REG_EXT_DIG_DREG, g_dig_dbias_pvt_non_240m);
396 esp_rom_delay_us(40);
397 }
398
399 REG_SET_FIELD(RTC_CNTL_DATE_REG, RTC_CNTL_SLAVE_PD, DEFAULT_LDO_SLAVE);
400 }
401
rtc_clk_cpu_freq_to_8m(void)402 static void rtc_clk_cpu_freq_to_8m(void)
403 {
404 assert(0 && "LDO dbias need to modified");
405 esp_rom_set_cpu_ticks_per_us(20);
406 clk_ll_cpu_set_divider(1);
407 clk_ll_cpu_set_src(SOC_CPU_CLK_SRC_RC_FAST);
408 rtc_clk_apb_freq_update(SOC_CLK_RC_FAST_FREQ_APPROX);
409 REGI2C_WRITE_MASK(I2C_DIG_REG, I2C_DIG_REG_EXT_RTC_DREG, g_rtc_dbias_pvt_non_240m);
410 REGI2C_WRITE_MASK(I2C_DIG_REG, I2C_DIG_REG_EXT_DIG_DREG, g_dig_dbias_pvt_non_240m);
411 REG_SET_FIELD(RTC_CNTL_DATE_REG, RTC_CNTL_SLAVE_PD, DEFAULT_LDO_SLAVE);
412 }
413
rtc_clk_xtal_freq_get(void)414 rtc_xtal_freq_t rtc_clk_xtal_freq_get(void)
415 {
416 uint32_t xtal_freq_mhz = clk_ll_xtal_load_freq_mhz();
417 if (xtal_freq_mhz == 0) {
418 ESP_HW_LOGW(TAG, "invalid RTC_XTAL_FREQ_REG value, assume 40MHz");
419 return RTC_XTAL_FREQ_40M;
420 }
421 return (rtc_xtal_freq_t)xtal_freq_mhz;
422 }
423
rtc_clk_xtal_freq_update(rtc_xtal_freq_t xtal_freq)424 void rtc_clk_xtal_freq_update(rtc_xtal_freq_t xtal_freq)
425 {
426 clk_ll_xtal_store_freq_mhz(xtal_freq);
427 }
428
rtc_clk_apb_freq_update(uint32_t apb_freq)429 void rtc_clk_apb_freq_update(uint32_t apb_freq)
430 {
431 s_apb_freq = apb_freq;
432 }
433
rtc_clk_apb_freq_get(void)434 uint32_t rtc_clk_apb_freq_get(void)
435 {
436 return s_apb_freq;
437 }
438
rtc_clk_divider_set(uint32_t div)439 void rtc_clk_divider_set(uint32_t div)
440 {
441 clk_ll_rc_slow_set_divider(div + 1);
442 }
443
rtc_clk_8m_divider_set(uint32_t div)444 void rtc_clk_8m_divider_set(uint32_t div)
445 {
446 clk_ll_rc_fast_set_divider(div + 1);
447 }
448
rtc_dig_clk8m_enable(void)449 void rtc_dig_clk8m_enable(void)
450 {
451 clk_ll_rc_fast_digi_enable();
452 esp_rom_delay_us(SOC_DELAY_RC_FAST_DIGI_SWITCH);
453 }
454
rtc_dig_clk8m_disable(void)455 void rtc_dig_clk8m_disable(void)
456 {
457 clk_ll_rc_fast_digi_disable();
458 esp_rom_delay_us(SOC_DELAY_RC_FAST_DIGI_SWITCH);
459 }
460
rtc_dig_8m_enabled(void)461 bool rtc_dig_8m_enabled(void)
462 {
463 return clk_ll_rc_fast_digi_is_enabled();
464 }
465
466 /* Name used in libphy.a:phy_chip_v7.o
467 * TODO: update the library to use rtc_clk_xtal_freq_get
468 */
469 rtc_xtal_freq_t rtc_get_xtal(void) __attribute__((alias("rtc_clk_xtal_freq_get")));
470