1 /*
2  * SPDX-FileCopyrightText: 2022-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 "esp32c6/rom/rtc.h"
14 #include "soc/rtc.h"
15 #include "esp_private/rtc_clk.h"
16 #include "esp_hw_log.h"
17 #include "esp_rom_sys.h"
18 #include "hal/clk_tree_ll.h"
19 #include "hal/regi2c_ctrl_ll.h"
20 #include "soc/io_mux_reg.h"
21 #include "soc/lp_aon_reg.h"
22 #include "esp_private/sleep_event.h"
23 
24 #ifdef BOOTLOADER_BUILD
25 #include "hal/modem_lpcon_ll.h"
26 #else
27 #include "esp_private/esp_modem_clock.h"
28 #endif
29 
30 static const char *TAG = "rtc_clk";
31 
32 // Current PLL frequency, in 480MHz. Zero if PLL is not enabled.
33 static int s_cur_pll_freq;
34 
35 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
36 
rtc_clk_bbpll_add_consumer(void)37 void rtc_clk_bbpll_add_consumer(void)
38 {
39     s_bbpll_digi_consumers_ref_count += 1;
40 }
41 
rtc_clk_bbpll_remove_consumer(void)42 void rtc_clk_bbpll_remove_consumer(void)
43 {
44     s_bbpll_digi_consumers_ref_count -= 1;
45 }
46 
rtc_clk_32k_enable(bool enable)47 void rtc_clk_32k_enable(bool enable)
48 {
49     if (enable) {
50         clk_ll_xtal32k_enable(CLK_LL_XTAL32K_ENABLE_MODE_CRYSTAL);
51     } else {
52         clk_ll_xtal32k_disable();
53     }
54 }
55 
rtc_clk_32k_enable_external(void)56 void rtc_clk_32k_enable_external(void)
57 {
58     // EXT_OSC_SLOW_GPIO_NUM == GPIO_NUM_0
59     PIN_INPUT_ENABLE(IO_MUX_GPIO0_REG);
60     REG_SET_BIT(LP_AON_GPIO_HOLD0_REG, BIT(EXT_OSC_SLOW_GPIO_NUM));
61     clk_ll_xtal32k_enable(CLK_LL_XTAL32K_ENABLE_MODE_EXTERNAL);
62 }
63 
rtc_clk_32k_bootstrap(uint32_t cycle)64 void rtc_clk_32k_bootstrap(uint32_t cycle)
65 {
66     /* No special bootstrapping needed for ESP32-C6, 'cycle' argument is to keep the signature
67      * same as for the ESP32. Just enable the XTAL here.
68      */
69     (void)cycle;
70     rtc_clk_32k_enable(true);
71 }
72 
rtc_clk_32k_enabled(void)73 bool rtc_clk_32k_enabled(void)
74 {
75     return clk_ll_xtal32k_is_enabled();
76 }
77 
rtc_clk_rc32k_enable(bool enable)78 void rtc_clk_rc32k_enable(bool enable)
79 {
80     if (enable) {
81         clk_ll_rc32k_enable();
82         esp_rom_delay_us(SOC_DELAY_RC32K_ENABLE);
83     } else {
84         clk_ll_rc32k_disable();
85     }
86 }
87 
rtc_clk_8m_enable(bool clk_8m_en)88 void rtc_clk_8m_enable(bool clk_8m_en)
89 {
90     if (clk_8m_en) {
91         clk_ll_rc_fast_enable();
92         esp_rom_delay_us(SOC_DELAY_RC_FAST_ENABLE);
93     } else {
94         clk_ll_rc_fast_disable();
95     }
96 }
97 
rtc_clk_8m_enabled(void)98 bool rtc_clk_8m_enabled(void)
99 {
100     return clk_ll_rc_fast_is_enabled();
101 }
102 
rtc_clk_slow_src_set(soc_rtc_slow_clk_src_t clk_src)103 void rtc_clk_slow_src_set(soc_rtc_slow_clk_src_t clk_src)
104 {
105     clk_ll_rtc_slow_set_src(clk_src);
106     esp_rom_delay_us(SOC_DELAY_RTC_SLOW_CLK_SWITCH);
107 }
108 
rtc_clk_slow_src_get(void)109 soc_rtc_slow_clk_src_t rtc_clk_slow_src_get(void)
110 {
111     return clk_ll_rtc_slow_get_src();
112 }
113 
rtc_clk_slow_freq_get_hz(void)114 uint32_t rtc_clk_slow_freq_get_hz(void)
115 {
116     switch (rtc_clk_slow_src_get()) {
117     case SOC_RTC_SLOW_CLK_SRC_RC_SLOW: return SOC_CLK_RC_SLOW_FREQ_APPROX;
118     case SOC_RTC_SLOW_CLK_SRC_XTAL32K: return SOC_CLK_XTAL32K_FREQ_APPROX;
119     case SOC_RTC_SLOW_CLK_SRC_RC32K: return SOC_CLK_RC32K_FREQ_APPROX;
120     case SOC_RTC_SLOW_CLK_SRC_OSC_SLOW: return SOC_CLK_OSC_SLOW_FREQ_APPROX;
121     default: return 0;
122     }
123 }
124 
rtc_clk_fast_src_set(soc_rtc_fast_clk_src_t clk_src)125 void rtc_clk_fast_src_set(soc_rtc_fast_clk_src_t clk_src)
126 {
127     clk_ll_rtc_fast_set_src(clk_src);
128     esp_rom_delay_us(SOC_DELAY_RTC_FAST_CLK_SWITCH);
129 }
130 
rtc_clk_fast_src_get(void)131 soc_rtc_fast_clk_src_t rtc_clk_fast_src_get(void)
132 {
133     return clk_ll_rtc_fast_get_src();
134 }
135 
rtc_clk_bbpll_disable(void)136 static void rtc_clk_bbpll_disable(void)
137 {
138 #if defined(CLK_BBPLL_DISABLE)
139     /* Disabling BBPLL is commented due to issues when transitioning
140      * from PLL to XTAL clock sources. Only peripheral using BBPLL on C6
141      * should be USB Serial/JTAG, so investigation is necessary */
142     clk_ll_bbpll_disable();
143 #endif
144     s_cur_pll_freq = 0;
145 }
146 
rtc_clk_bbpll_enable(void)147 static void rtc_clk_bbpll_enable(void)
148 {
149     clk_ll_bbpll_enable();
150 }
151 
rtc_clk_enable_i2c_ana_master_clock(bool enable)152 static void rtc_clk_enable_i2c_ana_master_clock(bool enable)
153 {
154 #ifdef BOOTLOADER_BUILD
155     modem_lpcon_ll_enable_i2c_master_clock(&MODEM_LPCON, enable);
156 #else
157     if (enable) {
158         modem_clock_module_enable(PERIPH_ANA_I2C_MASTER_MODULE);
159     } else {
160         modem_clock_module_disable(PERIPH_ANA_I2C_MASTER_MODULE);
161     }
162 #endif
163 }
164 
rtc_clk_bbpll_configure(rtc_xtal_freq_t xtal_freq,int pll_freq)165 static void rtc_clk_bbpll_configure(rtc_xtal_freq_t xtal_freq, int pll_freq)
166 {
167     /* Digital part */
168     clk_ll_bbpll_set_freq_mhz(pll_freq);
169     /* Analog part */
170     rtc_clk_enable_i2c_ana_master_clock(true);
171     /* BBPLL CALIBRATION START */
172     regi2c_ctrl_ll_bbpll_calibration_start();
173     clk_ll_bbpll_set_config(pll_freq, xtal_freq);
174     /* WAIT CALIBRATION DONE */
175     while(!regi2c_ctrl_ll_bbpll_calibration_is_done());
176     esp_rom_delay_us(10);
177     /* BBPLL CALIBRATION STOP */
178     regi2c_ctrl_ll_bbpll_calibration_stop();
179     rtc_clk_enable_i2c_ana_master_clock(false);
180     s_cur_pll_freq = pll_freq;
181 }
182 
183 /**
184  * Switch to use XTAL as the CPU clock source.
185  * Must satisfy: cpu_freq = XTAL_FREQ / div.
186  * Does not disable the PLL.
187  */
rtc_clk_cpu_freq_to_xtal(int cpu_freq,int div)188 static void rtc_clk_cpu_freq_to_xtal(int cpu_freq, int div)
189 {
190     clk_ll_ahb_set_ls_divider(div);
191     clk_ll_cpu_set_ls_divider(div);
192     clk_ll_cpu_set_src(SOC_CPU_CLK_SRC_XTAL);
193     esp_rom_set_cpu_ticks_per_us(cpu_freq);
194 }
195 
rtc_clk_cpu_freq_to_8m(void)196 static void rtc_clk_cpu_freq_to_8m(void)
197 {
198     clk_ll_ahb_set_ls_divider(1);
199     clk_ll_cpu_set_ls_divider(1);
200     clk_ll_cpu_set_src(SOC_CPU_CLK_SRC_RC_FAST);
201     esp_rom_set_cpu_ticks_per_us(20);
202 }
203 
204 /**
205  * Switch to one of PLL-based frequencies. Current frequency can be XTAL or PLL.
206  * PLL must already be enabled.
207  * @param cpu_freq new CPU frequency
208  */
rtc_clk_cpu_freq_to_pll_mhz(int cpu_freq_mhz)209 static void rtc_clk_cpu_freq_to_pll_mhz(int cpu_freq_mhz)
210 {
211     clk_ll_cpu_set_hs_divider(CLK_LL_PLL_480M_FREQ_MHZ / cpu_freq_mhz);
212     clk_ll_cpu_set_src(SOC_CPU_CLK_SRC_PLL);
213     esp_rom_set_cpu_ticks_per_us(cpu_freq_mhz);
214 }
215 
rtc_clk_cpu_freq_mhz_to_config(uint32_t freq_mhz,rtc_cpu_freq_config_t * out_config)216 bool rtc_clk_cpu_freq_mhz_to_config(uint32_t freq_mhz, rtc_cpu_freq_config_t *out_config)
217 {
218     uint32_t source_freq_mhz;
219     soc_cpu_clk_src_t source;
220     uint32_t divider; // divider = freq of SOC_ROOT_CLK / freq of CPU_CLK
221     uint32_t real_freq_mhz;
222 
223     uint32_t xtal_freq = (uint32_t)rtc_clk_xtal_freq_get();
224     if (freq_mhz <= xtal_freq && freq_mhz != 0) {
225         divider = xtal_freq / freq_mhz;
226         real_freq_mhz = (xtal_freq + divider / 2) / divider; /* round */
227         if (real_freq_mhz != freq_mhz) {
228             // no suitable divider
229             return false;
230         }
231 
232         source_freq_mhz = xtal_freq;
233         source = SOC_CPU_CLK_SRC_XTAL;
234     } else if (freq_mhz == 80) {
235         real_freq_mhz = freq_mhz;
236         source = SOC_CPU_CLK_SRC_PLL;
237         source_freq_mhz = CLK_LL_PLL_480M_FREQ_MHZ;
238         divider = 6;
239     } else if (freq_mhz == 120) {
240         real_freq_mhz = freq_mhz;
241         source = SOC_CPU_CLK_SRC_PLL;
242         source_freq_mhz = CLK_LL_PLL_480M_FREQ_MHZ;
243         divider = 4;
244     } else if (freq_mhz == 160) {
245         real_freq_mhz = freq_mhz;
246         source = SOC_CPU_CLK_SRC_PLL;
247         source_freq_mhz = CLK_LL_PLL_480M_FREQ_MHZ;
248         divider = 3;
249     } else {
250         // unsupported frequency
251         return false;
252     }
253     *out_config = (rtc_cpu_freq_config_t) {
254         .source = source,
255         .div = divider,
256         .source_freq_mhz = source_freq_mhz,
257         .freq_mhz = real_freq_mhz
258     };
259     return true;
260 }
261 
rtc_clk_set_cpu_switch_to_bbpll(int event_id)262 __attribute__((weak)) void rtc_clk_set_cpu_switch_to_bbpll(int event_id)
263 {
264 }
265 
rtc_clk_cpu_freq_set_config(const rtc_cpu_freq_config_t * config)266 void rtc_clk_cpu_freq_set_config(const rtc_cpu_freq_config_t *config)
267 {
268     soc_cpu_clk_src_t old_cpu_clk_src = clk_ll_cpu_get_src();
269     if (config->source == SOC_CPU_CLK_SRC_XTAL) {
270         rtc_clk_cpu_freq_to_xtal(config->freq_mhz, config->div);
271         if ((old_cpu_clk_src == SOC_CPU_CLK_SRC_PLL) && !s_bbpll_digi_consumers_ref_count) {
272             // We don't turn off the bbpll if some consumers depend on bbpll
273             rtc_clk_bbpll_disable();
274         }
275     } else if (config->source == SOC_CPU_CLK_SRC_PLL) {
276         if (old_cpu_clk_src != SOC_CPU_CLK_SRC_PLL) {
277             rtc_clk_set_cpu_switch_to_bbpll(SLEEP_EVENT_HW_BBPLL_EN_START);
278             rtc_clk_bbpll_enable();
279             rtc_clk_bbpll_configure(rtc_clk_xtal_freq_get(), config->source_freq_mhz);
280         }
281         rtc_clk_cpu_freq_to_pll_mhz(config->freq_mhz);
282         rtc_clk_set_cpu_switch_to_bbpll(SLEEP_EVENT_HW_BBPLL_EN_STOP);
283     } else if (config->source == SOC_CPU_CLK_SRC_RC_FAST) {
284         rtc_clk_cpu_freq_to_8m();
285         if ((old_cpu_clk_src == SOC_CPU_CLK_SRC_PLL) && !s_bbpll_digi_consumers_ref_count) {
286             // We don't turn off the bbpll if some consumers depend on bbpll
287             rtc_clk_bbpll_disable();
288         }
289     }
290 }
291 
rtc_clk_cpu_freq_get_config(rtc_cpu_freq_config_t * out_config)292 void rtc_clk_cpu_freq_get_config(rtc_cpu_freq_config_t *out_config)
293 {
294     soc_cpu_clk_src_t source = clk_ll_cpu_get_src();
295     uint32_t source_freq_mhz;
296     uint32_t div; // div = freq of SOC_ROOT_CLK / freq of CPU_CLK
297     uint32_t freq_mhz;
298     switch (source) {
299     case SOC_CPU_CLK_SRC_XTAL: {
300         div = clk_ll_cpu_get_ls_divider();
301         source_freq_mhz = (uint32_t)rtc_clk_xtal_freq_get();
302         freq_mhz = source_freq_mhz / div;
303         break;
304     }
305     case SOC_CPU_CLK_SRC_PLL: {
306         div = clk_ll_cpu_get_hs_divider();
307         source_freq_mhz = clk_ll_bbpll_get_freq_mhz();
308         freq_mhz = source_freq_mhz / div;
309         break;
310     }
311     case SOC_CPU_CLK_SRC_RC_FAST:
312         div = clk_ll_cpu_get_ls_divider();
313         source_freq_mhz = 20;
314         freq_mhz = source_freq_mhz / div;
315         break;
316     default:
317         ESP_HW_LOGE(TAG, "unsupported frequency configuration");
318         abort();
319     }
320     *out_config = (rtc_cpu_freq_config_t) {
321         .source = source,
322         .source_freq_mhz = source_freq_mhz,
323         .div = div,
324         .freq_mhz = freq_mhz
325     };
326 }
327 
rtc_clk_cpu_freq_set_config_fast(const rtc_cpu_freq_config_t * config)328 void rtc_clk_cpu_freq_set_config_fast(const rtc_cpu_freq_config_t *config)
329 {
330     if (config->source == SOC_CPU_CLK_SRC_XTAL) {
331         rtc_clk_cpu_freq_to_xtal(config->freq_mhz, config->div);
332     } else if (config->source == SOC_CPU_CLK_SRC_PLL &&
333                s_cur_pll_freq == config->source_freq_mhz) {
334         rtc_clk_cpu_freq_to_pll_mhz(config->freq_mhz);
335     } else if (config->source == SOC_CPU_CLK_SRC_RC_FAST) {
336         rtc_clk_cpu_freq_to_8m();
337     } else {
338         /* fallback */
339         rtc_clk_cpu_freq_set_config(config);
340     }
341 }
342 
rtc_clk_cpu_freq_set_xtal(void)343 void rtc_clk_cpu_freq_set_xtal(void)
344 {
345     rtc_clk_cpu_set_to_default_config();
346     rtc_clk_bbpll_disable();
347 }
348 
rtc_clk_cpu_set_to_default_config(void)349 void rtc_clk_cpu_set_to_default_config(void)
350 {
351     int freq_mhz = (int)rtc_clk_xtal_freq_get();
352 
353     rtc_clk_cpu_freq_to_xtal(freq_mhz, 1);
354     s_cur_pll_freq = 0; // no disable PLL, but set freq to 0 to trigger a PLL calibration after wake-up from sleep
355 }
356 
rtc_clk_cpu_freq_to_pll_and_pll_lock_release(int cpu_freq_mhz)357 void rtc_clk_cpu_freq_to_pll_and_pll_lock_release(int cpu_freq_mhz)
358 {
359     rtc_clk_cpu_freq_to_pll_mhz(cpu_freq_mhz);
360     clk_ll_cpu_clk_src_lock_release();
361 }
362 
rtc_clk_xtal_freq_get(void)363 rtc_xtal_freq_t rtc_clk_xtal_freq_get(void)
364 {
365     uint32_t xtal_freq_mhz = clk_ll_xtal_load_freq_mhz();
366     if (xtal_freq_mhz == 0) {
367         ESP_HW_LOGW(TAG, "invalid RTC_XTAL_FREQ_REG value, assume 40MHz");
368         clk_ll_xtal_store_freq_mhz(RTC_XTAL_FREQ_40M);
369         return RTC_XTAL_FREQ_40M;
370     }
371     return (rtc_xtal_freq_t)xtal_freq_mhz;
372 }
373 
rtc_clk_xtal_freq_update(rtc_xtal_freq_t xtal_freq)374 void rtc_clk_xtal_freq_update(rtc_xtal_freq_t xtal_freq)
375 {
376     clk_ll_xtal_store_freq_mhz(xtal_freq);
377 }
378 
rtc_clk_ahb_freq_get(void)379 static uint32_t rtc_clk_ahb_freq_get(void)
380 {
381     soc_cpu_clk_src_t source = clk_ll_cpu_get_src();
382     uint32_t soc_root_freq_mhz;
383     uint32_t divider;
384     switch (source) {
385     case SOC_CPU_CLK_SRC_XTAL:
386         soc_root_freq_mhz = rtc_clk_xtal_freq_get();
387         divider = clk_ll_ahb_get_ls_divider();
388         break;
389     case SOC_CPU_CLK_SRC_PLL:
390         soc_root_freq_mhz = clk_ll_bbpll_get_freq_mhz();
391         divider = clk_ll_ahb_get_hs_divider();
392         break;
393     case SOC_CPU_CLK_SRC_RC_FAST:
394         soc_root_freq_mhz = 20;
395         divider = clk_ll_ahb_get_ls_divider();
396         break;
397     default:
398         // Unknown SOC_ROOT clock source
399         soc_root_freq_mhz = 0;
400         divider = 1;
401         ESP_HW_LOGE(TAG, "Invalid SOC_ROOT_CLK");
402         break;
403     }
404     return soc_root_freq_mhz / divider;
405 }
406 
rtc_clk_apb_freq_get(void)407 uint32_t rtc_clk_apb_freq_get(void)
408 {
409     return rtc_clk_ahb_freq_get() / clk_ll_apb_get_divider() * MHZ;
410 }
411 
rtc_dig_clk8m_enable(void)412 void rtc_dig_clk8m_enable(void)
413 {
414     clk_ll_rc_fast_digi_enable();
415     esp_rom_delay_us(SOC_DELAY_RC_FAST_DIGI_SWITCH);
416 }
417 
rtc_dig_clk8m_disable(void)418 void rtc_dig_clk8m_disable(void)
419 {
420     clk_ll_rc_fast_digi_disable();
421     esp_rom_delay_us(SOC_DELAY_RC_FAST_DIGI_SWITCH);
422 }
423 
rtc_dig_8m_enabled(void)424 bool rtc_dig_8m_enabled(void)
425 {
426     return clk_ll_rc_fast_digi_is_enabled();
427 }
428 
429 /* Name used in libphy.a:phy_chip_v7.o
430  * TODO: update the library to use rtc_clk_xtal_freq_get
431  */
432 rtc_xtal_freq_t rtc_get_xtal(void) __attribute__((alias("rtc_clk_xtal_freq_get")));
433