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