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