1 /*
2  * SPDX-FileCopyrightText: 2020-2022 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 "esp32c3/rom/rtc.h"
14 #include "soc/rtc.h"
15 #include "soc/io_mux_reg.h"
16 #include "esp_private/rtc_clk.h"
17 #include "esp_hw_log.h"
18 #include "esp_rom_sys.h"
19 #include "hal/clk_tree_ll.h"
20 #include "hal/regi2c_ctrl_ll.h"
21 
22 static const char *TAG = "rtc_clk";
23 
24 // Current PLL frequency, in MHZ (320 or 480). Zero if PLL is not enabled.
25 static int s_cur_pll_freq;
26 
27 static void rtc_clk_cpu_freq_to_xtal(int freq, int div);
28 static void rtc_clk_cpu_freq_to_8m(void);
29 
30 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
31 
rtc_clk_bbpll_add_consumer(void)32 void rtc_clk_bbpll_add_consumer(void)
33 {
34     s_bbpll_digi_consumers_ref_count += 1;
35 }
36 
rtc_clk_bbpll_remove_consumer(void)37 void rtc_clk_bbpll_remove_consumer(void)
38 {
39     s_bbpll_digi_consumers_ref_count -= 1;
40 }
41 
rtc_clk_32k_enable(bool enable)42 void rtc_clk_32k_enable(bool enable)
43 {
44     if (enable) {
45         clk_ll_xtal32k_enable(CLK_LL_XTAL32K_ENABLE_MODE_CRYSTAL);
46     } else {
47         clk_ll_xtal32k_disable();
48     }
49 }
50 
rtc_clk_32k_enable_external(void)51 void rtc_clk_32k_enable_external(void)
52 {
53     PIN_INPUT_ENABLE(IO_MUX_GPIO0_REG);
54     SET_PERI_REG_MASK(RTC_CNTL_PAD_HOLD_REG, RTC_CNTL_GPIO_PIN0_HOLD);
55     clk_ll_xtal32k_enable(CLK_LL_XTAL32K_ENABLE_MODE_EXTERNAL);
56 }
57 
rtc_clk_32k_bootstrap(uint32_t cycle)58 void rtc_clk_32k_bootstrap(uint32_t cycle)
59 {
60     /* No special bootstrapping needed for ESP32-C3, 'cycle' argument is to keep the signature
61      * same as for the ESP32. Just enable the XTAL here.
62      */
63     (void)cycle;
64     rtc_clk_32k_enable(true);
65 }
66 
rtc_clk_32k_enabled(void)67 bool rtc_clk_32k_enabled(void)
68 {
69     return clk_ll_xtal32k_is_enabled();
70 }
71 
rtc_clk_8m_enable(bool clk_8m_en,bool d256_en)72 void rtc_clk_8m_enable(bool clk_8m_en, bool d256_en)
73 {
74     if (clk_8m_en) {
75         clk_ll_rc_fast_enable();
76         esp_rom_delay_us(SOC_DELAY_RC_FAST_ENABLE);
77     } else {
78         clk_ll_rc_fast_disable();
79     }
80     /* d256 should be independent configured with 8M
81      * Maybe we can split this function into 8m and dmd256
82      */
83     if (d256_en) {
84         clk_ll_rc_fast_d256_enable();
85     } else {
86         clk_ll_rc_fast_d256_disable();
87     }
88 }
89 
rtc_clk_8m_enabled(void)90 bool rtc_clk_8m_enabled(void)
91 {
92     return clk_ll_rc_fast_is_enabled();
93 }
94 
rtc_clk_8md256_enabled(void)95 bool rtc_clk_8md256_enabled(void)
96 {
97     return clk_ll_rc_fast_d256_is_enabled();
98 }
99 
rtc_clk_slow_src_set(soc_rtc_slow_clk_src_t clk_src)100 void rtc_clk_slow_src_set(soc_rtc_slow_clk_src_t clk_src)
101 {
102     clk_ll_rtc_slow_set_src(clk_src);
103 
104     /* Why we need to connect this clock to digital?
105      * Or maybe this clock should be connected to digital when xtal 32k clock is enabled instead?
106      */
107     if (clk_src == SOC_RTC_SLOW_CLK_SRC_XTAL32K) {
108         clk_ll_xtal32k_digi_enable();
109     } else {
110         clk_ll_xtal32k_digi_disable();
111     }
112 
113     esp_rom_delay_us(SOC_DELAY_RTC_SLOW_CLK_SWITCH);
114 }
115 
rtc_clk_slow_src_get(void)116 soc_rtc_slow_clk_src_t rtc_clk_slow_src_get(void)
117 {
118     return clk_ll_rtc_slow_get_src();
119 }
120 
rtc_clk_slow_freq_get_hz(void)121 uint32_t rtc_clk_slow_freq_get_hz(void)
122 {
123     switch (rtc_clk_slow_src_get()) {
124     case SOC_RTC_SLOW_CLK_SRC_RC_SLOW: return SOC_CLK_RC_SLOW_FREQ_APPROX;
125     case SOC_RTC_SLOW_CLK_SRC_XTAL32K: return SOC_CLK_XTAL32K_FREQ_APPROX;
126     case SOC_RTC_SLOW_CLK_SRC_RC_FAST_D256: return SOC_CLK_RC_FAST_D256_FREQ_APPROX;
127     default: return 0;
128     }
129 }
130 
rtc_clk_fast_src_set(soc_rtc_fast_clk_src_t clk_src)131 void rtc_clk_fast_src_set(soc_rtc_fast_clk_src_t clk_src)
132 {
133     clk_ll_rtc_fast_set_src(clk_src);
134     esp_rom_delay_us(SOC_DELAY_RTC_FAST_CLK_SWITCH);
135 }
136 
rtc_clk_fast_src_get(void)137 soc_rtc_fast_clk_src_t rtc_clk_fast_src_get(void)
138 {
139     return clk_ll_rtc_fast_get_src();
140 }
141 
rtc_clk_bbpll_disable(void)142 static void rtc_clk_bbpll_disable(void)
143 {
144     clk_ll_bbpll_disable();
145     s_cur_pll_freq = 0;
146 }
147 
rtc_clk_bbpll_enable(void)148 static void rtc_clk_bbpll_enable(void)
149 {
150     clk_ll_bbpll_enable();
151 }
152 
rtc_clk_bbpll_configure(rtc_xtal_freq_t xtal_freq,int pll_freq)153 static void rtc_clk_bbpll_configure(rtc_xtal_freq_t xtal_freq, int pll_freq)
154 {
155     /* Digital part */
156     clk_ll_bbpll_set_freq_mhz(pll_freq);
157     /* Analog part */
158     regi2c_ctrl_ll_bbpll_calibration_start();
159     clk_ll_bbpll_set_config(pll_freq, xtal_freq);
160 
161     s_cur_pll_freq = pll_freq;
162 }
163 
164 /**
165  * Switch to one of PLL-based frequencies. Current frequency can be XTAL or PLL.
166  * PLL must already be enabled.
167  * @param cpu_freq new CPU frequency
168  */
rtc_clk_cpu_freq_to_pll_mhz(int cpu_freq_mhz)169 static void rtc_clk_cpu_freq_to_pll_mhz(int cpu_freq_mhz)
170 {
171     clk_ll_cpu_set_freq_mhz_from_pll(cpu_freq_mhz);
172     clk_ll_cpu_set_divider(1);
173     clk_ll_cpu_set_src(SOC_CPU_CLK_SRC_PLL);
174     rtc_clk_apb_freq_update(80 * MHZ);
175     esp_rom_set_cpu_ticks_per_us(cpu_freq_mhz);
176 }
177 
rtc_clk_cpu_freq_mhz_to_config(uint32_t freq_mhz,rtc_cpu_freq_config_t * out_config)178 bool rtc_clk_cpu_freq_mhz_to_config(uint32_t freq_mhz, rtc_cpu_freq_config_t *out_config)
179 {
180     uint32_t source_freq_mhz;
181     soc_cpu_clk_src_t source;
182     uint32_t divider;
183     uint32_t real_freq_mhz;
184 
185     uint32_t xtal_freq = (uint32_t)rtc_clk_xtal_freq_get();
186     if (freq_mhz <= xtal_freq && freq_mhz != 0) {
187         divider = xtal_freq / freq_mhz;
188         real_freq_mhz = (xtal_freq + divider / 2) / divider; /* round */
189         if (real_freq_mhz != freq_mhz) {
190             // no suitable divider
191             return false;
192         }
193 
194         source_freq_mhz = xtal_freq;
195         source = SOC_CPU_CLK_SRC_XTAL;
196     } else if (freq_mhz == 80) {
197         real_freq_mhz = freq_mhz;
198         source = SOC_CPU_CLK_SRC_PLL;
199         source_freq_mhz = CLK_LL_PLL_480M_FREQ_MHZ;
200         divider = 6;
201     } else if (freq_mhz == 160) {
202         real_freq_mhz = freq_mhz;
203         source = SOC_CPU_CLK_SRC_PLL;
204         source_freq_mhz = CLK_LL_PLL_480M_FREQ_MHZ;
205         divider = 3;
206     } else {
207         // unsupported frequency
208         return false;
209     }
210     *out_config = (rtc_cpu_freq_config_t) {
211         .source = source,
212         .div = divider,
213         .source_freq_mhz = source_freq_mhz,
214         .freq_mhz = real_freq_mhz
215     };
216     return true;
217 }
218 
rtc_clk_cpu_freq_set_config(const rtc_cpu_freq_config_t * config)219 void rtc_clk_cpu_freq_set_config(const rtc_cpu_freq_config_t *config)
220 {
221     soc_cpu_clk_src_t old_cpu_clk_src = clk_ll_cpu_get_src();
222     if (config->source == SOC_CPU_CLK_SRC_XTAL) {
223         rtc_clk_cpu_freq_to_xtal(config->freq_mhz, config->div);
224         if ((old_cpu_clk_src == SOC_CPU_CLK_SRC_PLL) && !s_bbpll_digi_consumers_ref_count) {
225             // We don't turn off the bbpll if some consumers depend on bbpll
226             rtc_clk_bbpll_disable();
227         }
228     } else if (config->source == SOC_CPU_CLK_SRC_PLL) {
229         if (old_cpu_clk_src != SOC_CPU_CLK_SRC_PLL) {
230             rtc_clk_bbpll_enable();
231             rtc_clk_bbpll_configure(rtc_clk_xtal_freq_get(), config->source_freq_mhz);
232         }
233         rtc_clk_cpu_freq_to_pll_mhz(config->freq_mhz);
234     } else if (config->source == SOC_CPU_CLK_SRC_RC_FAST) {
235         rtc_clk_cpu_freq_to_8m();
236         if ((old_cpu_clk_src == SOC_CPU_CLK_SRC_PLL) && !s_bbpll_digi_consumers_ref_count) {
237             // We don't turn off the bbpll if some consumers depend on bbpll
238             rtc_clk_bbpll_disable();
239         }
240     }
241 }
242 
rtc_clk_cpu_freq_get_config(rtc_cpu_freq_config_t * out_config)243 void rtc_clk_cpu_freq_get_config(rtc_cpu_freq_config_t *out_config)
244 {
245     soc_cpu_clk_src_t source = clk_ll_cpu_get_src();
246     uint32_t source_freq_mhz;
247     uint32_t div;
248     uint32_t freq_mhz;
249     switch (source) {
250     case SOC_CPU_CLK_SRC_XTAL: {
251         div = clk_ll_cpu_get_divider();
252         source_freq_mhz = (uint32_t)rtc_clk_xtal_freq_get();
253         freq_mhz = source_freq_mhz / div;
254         break;
255     }
256     case SOC_CPU_CLK_SRC_PLL: {
257         freq_mhz = clk_ll_cpu_get_freq_mhz_from_pll();
258         source_freq_mhz = clk_ll_bbpll_get_freq_mhz();
259         if (freq_mhz == CLK_LL_PLL_80M_FREQ_MHZ) {
260             div = (source_freq_mhz == CLK_LL_PLL_480M_FREQ_MHZ) ? 6 : 4;
261         } else if (freq_mhz == CLK_LL_PLL_160M_FREQ_MHZ) {
262             div = (source_freq_mhz == CLK_LL_PLL_480M_FREQ_MHZ) ? 3 : 2;
263         } else {
264             ESP_HW_LOGE(TAG, "unsupported frequency configuration");
265             abort();
266         }
267         break;
268     }
269     case SOC_CPU_CLK_SRC_RC_FAST:
270         source_freq_mhz = 20;
271         div = 1;
272         freq_mhz = source_freq_mhz;
273         break;
274     default:
275         ESP_HW_LOGE(TAG, "unsupported frequency configuration");
276         abort();
277     }
278     *out_config = (rtc_cpu_freq_config_t) {
279         .source = source,
280         .source_freq_mhz = source_freq_mhz,
281         .div = div,
282         .freq_mhz = freq_mhz
283     };
284 }
285 
rtc_clk_cpu_freq_set_config_fast(const rtc_cpu_freq_config_t * config)286 void rtc_clk_cpu_freq_set_config_fast(const rtc_cpu_freq_config_t *config)
287 {
288     if (config->source == SOC_CPU_CLK_SRC_XTAL) {
289         rtc_clk_cpu_freq_to_xtal(config->freq_mhz, config->div);
290     } else if (config->source == SOC_CPU_CLK_SRC_PLL &&
291                s_cur_pll_freq == config->source_freq_mhz) {
292         rtc_clk_cpu_freq_to_pll_mhz(config->freq_mhz);
293     } else {
294         /* fallback */
295         rtc_clk_cpu_freq_set_config(config);
296     }
297 }
298 
rtc_clk_cpu_freq_set_xtal(void)299 void rtc_clk_cpu_freq_set_xtal(void)
300 {
301     rtc_clk_cpu_set_to_default_config();
302     rtc_clk_bbpll_disable();
303 }
304 
rtc_clk_cpu_set_to_default_config(void)305 void rtc_clk_cpu_set_to_default_config(void)
306 {
307     int freq_mhz = (int)rtc_clk_xtal_freq_get();
308 
309     rtc_clk_cpu_freq_to_xtal(freq_mhz, 1);
310 }
311 
312 /**
313  * Switch to use XTAL as the CPU clock source.
314  * Must satisfy: cpu_freq = XTAL_FREQ / div.
315  * Does not disable the PLL.
316  */
rtc_clk_cpu_freq_to_xtal(int cpu_freq,int div)317 static void rtc_clk_cpu_freq_to_xtal(int cpu_freq, int div)
318 {
319     esp_rom_set_cpu_ticks_per_us(cpu_freq);
320     /* Set divider from XTAL to APB clock. Need to set divider to 1 (reg. value 0) first. */
321     clk_ll_cpu_set_divider(1);
322     clk_ll_cpu_set_divider(div);
323     /* switch clock source */
324     clk_ll_cpu_set_src(SOC_CPU_CLK_SRC_XTAL);
325     rtc_clk_apb_freq_update(cpu_freq * MHZ);
326 }
327 
rtc_clk_cpu_freq_to_8m(void)328 static void rtc_clk_cpu_freq_to_8m(void)
329 {
330     esp_rom_set_cpu_ticks_per_us(20);
331     clk_ll_cpu_set_divider(1);
332     clk_ll_cpu_set_src(SOC_CPU_CLK_SRC_RC_FAST);
333     rtc_clk_apb_freq_update(SOC_CLK_RC_FAST_FREQ_APPROX);
334 }
335 
rtc_clk_xtal_freq_get(void)336 rtc_xtal_freq_t rtc_clk_xtal_freq_get(void)
337 {
338     uint32_t xtal_freq_mhz = clk_ll_xtal_load_freq_mhz();
339     if (xtal_freq_mhz == 0) {
340         ESP_HW_LOGW(TAG, "invalid RTC_XTAL_FREQ_REG value, assume 40MHz");
341         return RTC_XTAL_FREQ_40M;
342     }
343     return (rtc_xtal_freq_t)xtal_freq_mhz;
344 }
345 
rtc_clk_xtal_freq_update(rtc_xtal_freq_t xtal_freq)346 void rtc_clk_xtal_freq_update(rtc_xtal_freq_t xtal_freq)
347 {
348     clk_ll_xtal_store_freq_mhz(xtal_freq);
349 }
350 
rtc_clk_apb_freq_update(uint32_t apb_freq)351 void rtc_clk_apb_freq_update(uint32_t apb_freq)
352 {
353     clk_ll_apb_store_freq_hz(apb_freq);
354 }
355 
rtc_clk_apb_freq_get(void)356 uint32_t rtc_clk_apb_freq_get(void)
357 {
358     return clk_ll_apb_load_freq_hz();
359 }
360 
rtc_clk_divider_set(uint32_t div)361 void rtc_clk_divider_set(uint32_t div)
362 {
363     clk_ll_rc_slow_set_divider(div + 1);
364 }
365 
rtc_clk_8m_divider_set(uint32_t div)366 void rtc_clk_8m_divider_set(uint32_t div)
367 {
368     clk_ll_rc_fast_set_divider(div + 1);
369 }
370 
rtc_dig_clk8m_enable(void)371 void rtc_dig_clk8m_enable(void)
372 {
373     clk_ll_rc_fast_digi_enable();
374     esp_rom_delay_us(SOC_DELAY_RC_FAST_DIGI_SWITCH);
375 }
376 
rtc_dig_clk8m_disable(void)377 void rtc_dig_clk8m_disable(void)
378 {
379     clk_ll_rc_fast_digi_disable();
380     esp_rom_delay_us(SOC_DELAY_RC_FAST_DIGI_SWITCH);
381 }
382 
rtc_dig_8m_enabled(void)383 bool rtc_dig_8m_enabled(void)
384 {
385     return clk_ll_rc_fast_digi_is_enabled();
386 }
387 
388 /* Name used in libphy.a:phy_chip_v7.o
389  * TODO: update the library to use rtc_clk_xtal_freq_get
390  */
391 rtc_xtal_freq_t rtc_get_xtal(void) __attribute__((alias("rtc_clk_xtal_freq_get")));
392