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