diff --git a/components/bootloader_support/src/bootloader_clock.c b/components/bootloader_support/src/bootloader_clock.c index 40630a7f7..d3305dfe7 100644 --- a/components/bootloader_support/src/bootloader_clock.c +++ b/components/bootloader_support/src/bootloader_clock.c @@ -35,15 +35,15 @@ void bootloader_clock_configure(void) // and will be done with the bootloader much earlier than UART FIFO is empty. uart_tx_wait_idle(0); + /* Set CPU to 80MHz. Keep other clocks unmodified. */ + int cpu_freq_mhz = 80; + +#if CONFIG_IDF_TARGET_ESP32 /* On ESP32 rev 0, switching to 80/160 MHz if clock was previously set to * 240 MHz may cause the chip to lock up (see section 3.5 of the errata * document). For rev. 0, switch to 240 instead if it has been enabled * previously. */ -#if CONFIG_IDF_TARGET_ESP32 - /* Set CPU to 80MHz. Keep other clocks unmodified. */ - int cpu_freq_mhz = 80; - uint32_t chip_ver_reg = REG_READ(EFUSE_BLK0_RDATA3_REG); if ((chip_ver_reg & EFUSE_RD_CHIP_VER_REV1_M) == 0 && DPORT_REG_GET_FIELD(DPORT_CPU_PER_CONF_REG, DPORT_CPUPERIOD_SEL) == DPORT_CPUPERIOD_SEL_240) { @@ -54,11 +54,10 @@ void bootloader_clock_configure(void) rtc_clk_config_t clk_cfg = RTC_CLK_CONFIG_DEFAULT(); #if CONFIG_IDF_TARGET_ESP32 clk_cfg.xtal_freq = CONFIG_ESP32_XTAL_FREQ; - clk_cfg.cpu_freq_mhz = cpu_freq_mhz; #elif CONFIG_IDF_TARGET_ESP32S2 clk_cfg.xtal_freq = RTC_XTAL_FREQ_40M; - clk_cfg.cpu_freq = RTC_CPU_FREQ_80M; #endif + clk_cfg.cpu_freq_mhz = cpu_freq_mhz; clk_cfg.slow_freq = rtc_clk_slow_freq_get(); clk_cfg.fast_freq = rtc_clk_fast_freq_get(); rtc_clk_init(clk_cfg); diff --git a/components/esp32s2/clk.c b/components/esp32s2/clk.c index 5b579db22..7304a6f1e 100644 --- a/components/esp32s2/clk.c +++ b/components/esp32s2/clk.c @@ -86,35 +86,22 @@ void esp_clk_init(void) rtc_wdt_protect_on(); #endif - uint32_t freq_mhz = CONFIG_ESP32S2_DEFAULT_CPU_FREQ_MHZ; - rtc_cpu_freq_t freq = RTC_CPU_FREQ_80M; - switch (freq_mhz) { - case 240: - freq = RTC_CPU_FREQ_240M; - break; - case 160: - freq = RTC_CPU_FREQ_160M; - break; - case 80: - freq = RTC_CPU_FREQ_80M; - break; - default: - freq_mhz = 80; - freq = RTC_CPU_FREQ_80M; - break; - } + rtc_cpu_freq_config_t old_config, new_config; + rtc_clk_cpu_freq_get_config(&old_config); + const uint32_t old_freq_mhz = old_config.freq_mhz; + const uint32_t new_freq_mhz = CONFIG_ESP32S2_DEFAULT_CPU_FREQ_MHZ; + + bool res = rtc_clk_cpu_freq_mhz_to_config(new_freq_mhz, &new_config); + assert(res); // Wait for UART TX to finish, otherwise some UART output will be lost // when switching APB frequency uart_tx_wait_idle(CONFIG_ESP_CONSOLE_UART_NUM); - uint32_t freq_before = rtc_clk_cpu_freq_value(rtc_clk_cpu_freq_get()) / MHZ ; - - rtc_clk_cpu_freq_set(freq); + rtc_clk_cpu_freq_set_config(&new_config); // Re calculate the ccount to make time calculation correct. - uint32_t freq_after = CONFIG_ESP32S2_DEFAULT_CPU_FREQ_MHZ; - XTHAL_SET_CCOUNT( XTHAL_GET_CCOUNT() * freq_after / freq_before ); + XTHAL_SET_CCOUNT( (uint64_t)XTHAL_GET_CCOUNT() * new_freq_mhz / old_freq_mhz ); } int IRAM_ATTR esp_clk_cpu_freq(void) diff --git a/components/esp32s2/panic.c b/components/esp32s2/panic.c index dccfe0467..1209974f8 100644 --- a/components/esp32s2/panic.c +++ b/components/esp32s2/panic.c @@ -476,7 +476,7 @@ static void esp_panic_dig_reset(void) // make sure all the panic handler output is sent from UART FIFO uart_tx_wait_idle(CONFIG_ESP_CONSOLE_UART_NUM); // switch to XTAL (otherwise we will keep running from the PLL) - rtc_clk_cpu_freq_set(RTC_CPU_FREQ_XTAL); + rtc_clk_cpu_freq_set_xtal(); // reset the digital part esp_cpu_unstall(PRO_CPU_NUM); SET_PERI_REG_MASK(RTC_CNTL_OPTIONS0_REG, RTC_CNTL_SW_SYS_RST); diff --git a/components/esp32s2/sleep_modes.c b/components/esp32s2/sleep_modes.c index 529b14085..17d831f06 100644 --- a/components/esp32s2/sleep_modes.c +++ b/components/esp32s2/sleep_modes.c @@ -172,8 +172,10 @@ static uint32_t IRAM_ATTR esp_sleep_start(uint32_t pd_flags) } // Save current frequency and switch to XTAL - rtc_cpu_freq_t cpu_freq = rtc_clk_cpu_freq_get(); - rtc_clk_cpu_freq_set(RTC_CPU_FREQ_XTAL); + // Save current frequency and switch to XTAL + rtc_cpu_freq_config_t cpu_freq_config; + rtc_clk_cpu_freq_get_config(&cpu_freq_config); + rtc_clk_cpu_freq_set_xtal(); // Configure pins for external wakeup if (s_config.wakeup_triggers & RTC_EXT0_TRIG_EN) { @@ -199,7 +201,7 @@ static uint32_t IRAM_ATTR esp_sleep_start(uint32_t pd_flags) uint32_t result = rtc_sleep_start(s_config.wakeup_triggers, 0, 1); // Restore CPU frequency - rtc_clk_cpu_freq_set(cpu_freq); + rtc_clk_cpu_freq_set_config(&cpu_freq_config); // re-enable UART output resume_uarts(); diff --git a/components/esp32s2/system_api_esp32s2.c b/components/esp32s2/system_api_esp32s2.c index 096ea860f..e4c17b978 100644 --- a/components/esp32s2/system_api_esp32s2.c +++ b/components/esp32s2/system_api_esp32s2.c @@ -94,7 +94,7 @@ void IRAM_ATTR esp_restart_noos(void) DPORT_REG_WRITE(DPORT_PERIP_RST_EN_REG, 0); // Set CPU back to XTAL source, no PLL, same as hard reset - rtc_clk_cpu_freq_set(RTC_CPU_FREQ_XTAL); + rtc_clk_cpu_freq_set_xtal(); // Reset CPUs if (core_id == 0) { diff --git a/components/soc/soc/esp32/include/soc/rtc.h b/components/soc/soc/esp32/include/soc/rtc.h index cb999ae1c..96982a038 100644 --- a/components/soc/soc/esp32/include/soc/rtc.h +++ b/components/soc/soc/esp32/include/soc/rtc.h @@ -130,7 +130,7 @@ typedef enum { */ typedef struct rtc_clk_config_s { rtc_xtal_freq_t xtal_freq : 8; //!< Main XTAL frequency - rtc_cpu_freq_t cpu_freq_mhz : 10; //!< CPU frequency to set, in MHz + uint32_t cpu_freq_mhz : 10; //!< CPU frequency to set, in MHz rtc_fast_freq_t fast_freq : 1; //!< RTC_FAST_CLK frequency to set rtc_slow_freq_t slow_freq : 2; //!< RTC_SLOW_CLK frequency to set uint32_t clk_8m_div : 3; //!< RTC 8M clock divider (division is by clk_8m_div+1, i.e. 0 means 8MHz frequency) diff --git a/components/soc/soc/esp32s2/include/soc/rtc.h b/components/soc/soc/esp32s2/include/soc/rtc.h index 15156da85..7953510ed 100644 --- a/components/soc/soc/esp32s2/include/soc/rtc.h +++ b/components/soc/soc/esp32s2/include/soc/rtc.h @@ -148,6 +148,26 @@ typedef enum { RTC_CPU_FREQ_XTAL_DIV2 = 7, //!< XTAL/2 after reset } rtc_cpu_freq_t; +/** + * @brief CPU clock source + */ +typedef enum { + RTC_CPU_FREQ_SRC_XTAL, //!< XTAL + RTC_CPU_FREQ_SRC_PLL, //!< PLL (480M or 320M) + RTC_CPU_FREQ_SRC_8M, //!< Internal 8M RTC oscillator + RTC_CPU_FREQ_SRC_APLL //!< APLL +} rtc_cpu_freq_src_t; + +/** + * @brief CPU clock configuration structure + */ +typedef struct rtc_cpu_freq_config_s { + rtc_cpu_freq_src_t source; //!< The clock from which CPU clock is derived + uint32_t source_freq_mhz; //!< Source clock frequency + uint32_t div; //!< Divider, freq_mhz = source_freq_mhz / div + uint32_t freq_mhz; //!< CPU clock frequency +} rtc_cpu_freq_config_t; + /** * @brief RTC SLOW_CLK frequency values */ @@ -187,7 +207,7 @@ typedef enum { */ typedef struct { rtc_xtal_freq_t xtal_freq : 8; //!< Main XTAL frequency - rtc_cpu_freq_t cpu_freq : 3; //!< CPU frequency to set + uint32_t cpu_freq_mhz : 10; //!< CPU frequency to set, in MHz rtc_fast_freq_t fast_freq : 1; //!< RTC_FAST_CLK frequency to set rtc_slow_freq_t slow_freq : 2; //!< RTC_SLOW_CLK frequency to set uint32_t clk_rtc_clk_div : 8; @@ -201,7 +221,7 @@ typedef struct { */ #define RTC_CLK_CONFIG_DEFAULT() { \ .xtal_freq = RTC_XTAL_FREQ_40M, \ - .cpu_freq = RTC_CPU_FREQ_80M, \ + .cpu_freq_mhz = 80, \ .fast_freq = RTC_FAST_FREQ_8M, \ .slow_freq = RTC_SLOW_FREQ_RTC, \ .clk_rtc_clk_div = 0, \ @@ -415,63 +435,63 @@ void rtc_clk_fast_freq_set(rtc_fast_freq_t fast_freq); rtc_fast_freq_t rtc_clk_fast_freq_get(void); /** - * @brief Switch CPU frequency - * - * If a PLL-derived frequency is requested (80, 160, 240 MHz), this function - * will enable the PLL. Otherwise, PLL will be disabled. - * Note: this function is not optimized for switching speed. It may take several - * hundred microseconds to perform frequency switch. - * - * @param cpu_freq new CPU frequency + * @brief Get CPU frequency config for a given frequency + * @param freq_mhz Frequency in MHz + * @param[out] out_config Output, CPU frequency configuration structure + * @return true if frequency can be obtained, false otherwise */ -void rtc_clk_cpu_freq_set(rtc_cpu_freq_t cpu_freq); +bool rtc_clk_cpu_freq_mhz_to_config(uint32_t freq_mhz, rtc_cpu_freq_config_t* out_config); /** * @brief Switch CPU frequency * - * This is a faster version of rtc_clk_cpu_freq_set, which can handle some of - * the frequency switch paths (XTAL -> PLL, PLL -> XTAL). - * When switching from PLL to XTAL, PLL is not disabled (unlike rtc_clk_cpu_freq_set). - * When switching back from XTAL to PLL, only the same PLL can be used. - * Therefore it is not possible to switch 240 -> XTAL -> (80 or 160) using this - * function. + * This function sets CPU frequency according to the given configuration + * structure. It enables PLLs, if necessary. * - * For unsupported cases, this function falls back to rtc_clk_cpu_freq_set. + * @note This function in not intended to be called by applications in FreeRTOS + * environment. This is because it does not adjust various timers based on the + * new CPU frequency. * - * Unlike rtc_clk_cpu_freq_set, this function relies on static data, so it is - * less safe to use it e.g. from a panic handler (when memory might be corrupted). - * - * @param cpu_freq new CPU frequency + * @param config CPU frequency configuration structure */ -void rtc_clk_cpu_freq_set_fast(rtc_cpu_freq_t cpu_freq); +void rtc_clk_cpu_freq_set_config(const rtc_cpu_freq_config_t* config); /** - * @brief Get the currently selected CPU frequency + * @brief Switch CPU frequency (optimized for speed) * - * Although CPU can be clocked by APLL and RTC 8M sources, such support is not - * exposed through this library. As such, this function will not return - * meaningful values when these clock sources are configured (e.g. using direct - * access to clock selection registers). In debug builds, it will assert; in - * release builds, it will return RTC_CPU_FREQ_XTAL. + * This function is a faster equivalent of rtc_clk_cpu_freq_set_config. + * It works faster because it does not disable PLLs when switching from PLL to + * XTAL and does not enabled them when switching back. If PLL is not already + * enabled when this function is called to switch from XTAL to PLL frequency, + * or the PLL which is enabled is the wrong one, this function will fall back + * to calling rtc_clk_cpu_freq_set_config. * - * @return CPU frequency (one of rtc_cpu_freq_t values) + * Unlike rtc_clk_cpu_freq_set_config, this function relies on static data, + * so it is less safe to use it e.g. from a panic handler (when memory might + * be corrupted). + * + * @note This function in not intended to be called by applications in FreeRTOS + * environment. This is because it does not adjust various timers based on the + * new CPU frequency. + * + * @param config CPU frequency configuration structure */ -rtc_cpu_freq_t rtc_clk_cpu_freq_get(void); +void rtc_clk_cpu_freq_set_config_fast(const rtc_cpu_freq_config_t* config); /** - * @brief Get corresponding frequency value for rtc_cpu_freq_t enum value - * @param cpu_freq CPU frequency, on of rtc_cpu_freq_t values - * @return CPU frequency, in HZ + * @brief Get the currently used CPU frequency configuration + * @param[out] out_config Output, CPU frequency configuration structure */ -uint32_t rtc_clk_cpu_freq_value(rtc_cpu_freq_t cpu_freq); +void rtc_clk_cpu_freq_get_config(rtc_cpu_freq_config_t* out_config); /** - * @brief Get rtc_cpu_freq_t enum value for given CPU frequency - * @param cpu_freq_mhz CPU frequency, one of 80, 160, 240, 2, and XTAL frequency - * @param[out] out_val output, rtc_cpu_freq_t value corresponding to the frequency - * @return true if the given frequency value matches one of enum values + * @brief Switch CPU clock source to XTAL + * + * Short form for filling in rtc_cpu_freq_config_t structure and calling + * rtc_clk_cpu_freq_set_config when a switch to XTAL is needed. + * Assumes that XTAL frequency has been determined — don't call in startup code. */ - bool rtc_clk_cpu_freq_from_mhz(int cpu_freq_mhz, rtc_cpu_freq_t* out_val); +void rtc_clk_cpu_freq_set_xtal(void); /** * @brief Store new APB frequency value into RTC_APB_FREQ_REG diff --git a/components/soc/src/esp32s2/CMakeLists.txt b/components/soc/src/esp32s2/CMakeLists.txt index 86cf1c3d3..4fc1eb04c 100644 --- a/components/soc/src/esp32s2/CMakeLists.txt +++ b/components/soc/src/esp32s2/CMakeLists.txt @@ -1,6 +1,7 @@ set(srcs "brownout_hal.c" "cpu_util.c" "rtc_clk.c" + "rtc_clk_init.c" "rtc_init.c" "rtc_pm.c" "rtc_sleep.c" diff --git a/components/soc/src/esp32s2/rtc_clk.c b/components/soc/src/esp32s2/rtc_clk.c index 0d0ce595c..3e9234258 100644 --- a/components/soc/src/esp32s2/rtc_clk.c +++ b/components/soc/src/esp32s2/rtc_clk.c @@ -16,6 +16,7 @@ #include #include #include +#include #include "sdkconfig.h" #include "esp32s2/rom/ets_sys.h" #include "esp32s2/rom/rtc.h" @@ -30,21 +31,19 @@ #include "soc/syscon_reg.h" #include "i2c_rtc_clk.h" #include "soc_log.h" +#include "rtc_clk_common.h" #include "sdkconfig.h" #include "xtensa/core-macros.h" static const char *TAG = "rtc_clk"; -/* PLL currently enabled, if any */ -typedef enum { - RTC_PLL_NONE, - RTC_PLL_320M, - RTC_PLL_480M -} rtc_pll_t; -static rtc_pll_t s_cur_pll = RTC_PLL_NONE; +#define RTC_PLL_FREQ_320M 320 +#define RTC_PLL_FREQ_480M 480 -/* Current CPU frequency; saved in a variable for faster freq. switching */ -static rtc_cpu_freq_t s_cur_freq = RTC_CPU_FREQ_XTAL; +// Current PLL frequency, in MHZ (320 or 480). Zero if PLL is not enabled. +static int s_cur_pll_freq; + +static void rtc_clk_cpu_freq_to_8m(void); void rtc_clk_32k_enable_internal(x32k_config_t cfg) { @@ -120,25 +119,7 @@ void rtc_clk_apll_enable(bool enable, uint32_t sdm0, uint32_t sdm1, uint32_t sdm REG_SET_FIELD(RTC_CNTL_ANA_CONF_REG, RTC_CNTL_PLLA_FORCE_PD, enable ? 0 : 1); REG_SET_FIELD(RTC_CNTL_ANA_CONF_REG, RTC_CNTL_PLLA_FORCE_PU, enable ? 1 : 0); - /* BIAS I2C not exist any more, but not sure how to get the same effect yet... - * if (!enable && - * REG_GET_FIELD(DPORT_SYSCLK_CONF_REG, DPORT_SOC_CLK_SEL) != DPORT_SOC_CLK_SEL_PLL) { - * REG_SET_BIT(RTC_CNTL_OPTIONS0_REG, RTC_CNTL_BIAS_I2C_FORCE_PD); - * } else { - * REG_CLR_BIT(RTC_CNTL_OPTIONS0_REG, RTC_CNTL_BIAS_I2C_FORCE_PD); - * } - */ - if (enable) { - /* no need to differentiate ECO chip any more - uint8_t sdm_stop_val_2 = APLL_SDM_STOP_VAL_2_REV1; - uint32_t is_rev0 = (GET_PERI_REG_BITS2(EFUSE_BLK0_RDATA3_REG, 1, 15) == 0); - if (is_rev0) { - sdm0 = 0; - sdm1 = 0; - sdm_stop_val_2 = APLL_SDM_STOP_VAL_2_REV0; - } - */ I2C_WRITEREG_MASK_RTC(I2C_APLL, I2C_APLL_DSDM2, sdm2); I2C_WRITEREG_MASK_RTC(I2C_APLL, I2C_APLL_DSDM0, sdm0); I2C_WRITEREG_MASK_RTC(I2C_APLL, I2C_APLL_DSDM1, sdm1); @@ -226,17 +207,20 @@ rtc_fast_freq_t rtc_clk_fast_freq_get(void) return REG_GET_FIELD(RTC_CNTL_CLK_CONF_REG, RTC_CNTL_FAST_CLK_RTC_SEL); } -/* In 7.2.2, cpu can run at 80M/160M/240M if PLL is 480M - * pll can run 80M/160M is PLL is 320M - */ -#define DR_REG_I2C_MST_BASE 0x3f40E000 -#define I2C_MST_ANA_STATE_REG (DR_REG_I2C_MST_BASE + 0x040) -#define I2C_MST_BBPLL_CAL_END (BIT(24)) -#define I2C_MST_BBPLL_CAL_END_M (BIT(24)) -#define I2C_MST_BBPLL_CAL_END_V 0x1 -#define I2C_MST_BBPLL_CAL_END_S 24 +static void rtc_clk_bbpll_disable(void) +{ + SET_PERI_REG_MASK(RTC_CNTL_OPTIONS0_REG, RTC_CNTL_BB_I2C_FORCE_PD | + RTC_CNTL_BBPLL_FORCE_PD | RTC_CNTL_BBPLL_I2C_FORCE_PD); + s_cur_pll_freq = 0; +} -void rtc_clk_bbpll_set(rtc_xtal_freq_t xtal_freq, rtc_pll_t pll_freq) +static void rtc_clk_bbpll_enable(void) +{ + CLEAR_PERI_REG_MASK(RTC_CNTL_OPTIONS0_REG, RTC_CNTL_BB_I2C_FORCE_PD | + RTC_CNTL_BBPLL_FORCE_PD | RTC_CNTL_BBPLL_I2C_FORCE_PD); +} + +void rtc_clk_bbpll_configure(rtc_xtal_freq_t xtal_freq, int pll_freq) { uint8_t div_ref; uint8_t div7_0; @@ -247,11 +231,8 @@ void rtc_clk_bbpll_set(rtc_xtal_freq_t xtal_freq, rtc_pll_t pll_freq) assert(xtal_freq == RTC_XTAL_FREQ_40M); - if (pll_freq == RTC_PLL_480M) { - /* Raise the voltage, if needed */ - /* move to 240M logic */ - //REG_SET_FIELD(RTC_CNTL_REG, RTC_CNTL_DIG_DBIAS_WAK, DIG_DBIAS_80M_160M); - /* Set this register to let digital know pll is 480M */ + if (pll_freq == RTC_PLL_FREQ_480M) { + /* Clear this register to let the digital part know 480M PLL is used */ SET_PERI_REG_MASK(DPORT_CPU_PER_CONF_REG, DPORT_PLL_FREQ_SEL); /* Configure 480M PLL */ div_ref = 0; @@ -262,11 +243,9 @@ void rtc_clk_bbpll_set(rtc_xtal_freq_t xtal_freq, rtc_pll_t pll_freq) dcur = 4; I2C_WRITEREG_RTC(I2C_BBPLL, I2C_BBPLL_MODE_HF, 0x6B); } else { - /* Raise the voltage */ - //REG_SET_FIELD(RTC_CNTL_REG, RTC_CNTL_DIG_DBIAS_WAK, DIG_DBIAS_240M); - //ets_delay_us(DELAY_PLL_DBIAS_RAISE); + /* Clear this register to let the digital part know 320M PLL is used */ CLEAR_PERI_REG_MASK(DPORT_CPU_PER_CONF_REG, DPORT_PLL_FREQ_SEL); - /* Configure 480M PLL */ + /* Configure 320M PLL */ div_ref = 0; div7_0 = 4; dr1 = 0; @@ -295,280 +274,213 @@ void rtc_clk_bbpll_set(rtc_xtal_freq_t xtal_freq, rtc_pll_t pll_freq) } if (ext_cap == 15) { SOC_LOGE(TAG, "BBPLL SOFTWARE CAL FAIL"); + abort(); } } - - /* this delay is replaced by polling Pll calibration end flag - * uint32_t delay_pll_en = (rtc_clk_slow_freq_get() == RTC_SLOW_FREQ_RTC) ? - * DELAY_PLL_ENABLE_WITH_150K : DELAY_PLL_ENABLE_WITH_32K; - * ets_delay_us(delay_pll_en); - */ - /* this calibration didn't work on 480M - need to test exact delay according to 320M - while (!GET_PERI_REG_MASK(I2C_MST_ANA_STATE_REG, I2C_MST_BBPLL_CAL_END)) { - ets_delay_us(1); - } - */ -} - -/** - * Switch to XTAL frequency. Does not disable the PLL. - */ -static void rtc_clk_cpu_freq_to_xtal(void) -{ - rtc_xtal_freq_t xtal_freq = rtc_clk_xtal_freq_get(); - ets_update_cpu_frequency(xtal_freq); - REG_SET_FIELD(RTC_CNTL_REG, RTC_CNTL_DIG_DBIAS_WAK, DIG_DBIAS_XTAL); - REG_SET_FIELD(DPORT_SYSCLK_CONF_REG, DPORT_PRE_DIV_CNT, 0); - REG_SET_FIELD(DPORT_SYSCLK_CONF_REG, DPORT_SOC_CLK_SEL, 0); - /* Why we need to do this ? */ - //DPORT_REG_WRITE(DPORT_CPU_PER_CONF_REG, 0); // clear DPORT_CPUPERIOD_SEL - - rtc_clk_apb_freq_update(xtal_freq * MHZ); - s_cur_freq = RTC_CPU_FREQ_XTAL; - s_cur_pll = RTC_PLL_NONE; + s_cur_pll_freq = pll_freq; } /** * Switch to one of PLL-based frequencies. Current frequency can be XTAL or PLL. * PLL must already be enabled. - * If switching between frequencies derived from different PLLs (320M and 480M), - * fall back to rtc_clk_cpu_freq_set. * @param cpu_freq new CPU frequency */ -static void rtc_clk_cpu_freq_to_pll(rtc_cpu_freq_t cpu_freq) +static void rtc_clk_cpu_freq_to_pll_mhz(int cpu_freq_mhz) { - int freq = 0; - if ((s_cur_pll == RTC_PLL_NONE) || ((s_cur_pll == RTC_PLL_320M) && (cpu_freq == RTC_CPU_FREQ_240M))) { - /* - * if switch from non-pll or switch from PLL 320M to 480M - * need to switch PLLs, fall back to full implementation - */ - rtc_clk_cpu_freq_set(cpu_freq); - return; + int dbias = DIG_DBIAS_80M_160M; + int per_conf = DPORT_CPUPERIOD_SEL_80; + if (cpu_freq_mhz == 80) { + /* nothing to do */ + } else if (cpu_freq_mhz == 160) { + per_conf = DPORT_CPUPERIOD_SEL_160; + } else if (cpu_freq_mhz == 240) { + dbias = DIG_DBIAS_240M; + per_conf = DPORT_CPUPERIOD_SEL_240; + } else { + SOC_LOGE(TAG, "invalid frequency"); + abort(); } - - if ((cpu_freq == RTC_CPU_FREQ_80M) || (cpu_freq == RTC_CPU_320M_80M)) { - REG_SET_FIELD(RTC_CNTL_REG, RTC_CNTL_DIG_DBIAS_WAK, DIG_DBIAS_80M_160M); - DPORT_REG_WRITE(DPORT_CPU_PER_CONF_REG, 0); - freq = 80; - } else if ((cpu_freq == RTC_CPU_FREQ_160M) || (cpu_freq == RTC_CPU_320M_160M)) { - REG_SET_FIELD(RTC_CNTL_REG, RTC_CNTL_DIG_DBIAS_WAK, DIG_DBIAS_80M_160M); - DPORT_REG_WRITE(DPORT_CPU_PER_CONF_REG, 1); - freq = 160; - } else if (cpu_freq == RTC_CPU_FREQ_240M) { - REG_SET_FIELD(RTC_CNTL_REG, RTC_CNTL_DIG_DBIAS_WAK, DIG_DBIAS_240M); - DPORT_REG_WRITE(DPORT_CPU_PER_CONF_REG, 2); - freq = 240; - } - // REG_SET_FIELD(DPORT_SYSCLK_CONF_REG, DPORT_SOC_CLK_SEL, DPORT_SOC_CLK_SEL_PLL); - rtc_clk_apb_freq_update(80 * MHZ); - ets_update_cpu_frequency(freq); - s_cur_freq = cpu_freq; -} - -void rtc_clk_cpu_freq_set_fast(rtc_cpu_freq_t cpu_freq) -{ - if (cpu_freq == s_cur_freq) { - return; - } else if (cpu_freq == RTC_CPU_FREQ_2M || s_cur_freq == RTC_CPU_FREQ_2M) { - /* fall back to full implementation if switch to/from 2M is needed */ - rtc_clk_cpu_freq_set(cpu_freq); - } else if (cpu_freq == RTC_CPU_FREQ_XTAL) { - rtc_clk_cpu_freq_to_xtal(); - } else if (cpu_freq > RTC_CPU_FREQ_XTAL) { - rtc_clk_cpu_freq_to_pll(cpu_freq); - /* Not neccessary any more */ - //rtc_clk_wait_for_slow_cycle(); - } -} - -void rtc_clk_cpu_freq_set(rtc_cpu_freq_t cpu_freq) -{ - rtc_xtal_freq_t xtal_freq = rtc_clk_xtal_freq_get(); - /* Switch CPU to XTAL frequency first */ - REG_SET_FIELD(RTC_CNTL_REG, RTC_CNTL_DIG_DBIAS_WAK, DIG_DBIAS_XTAL); - REG_SET_FIELD(DPORT_SYSCLK_CONF_REG, DPORT_SOC_CLK_SEL, 0); + REG_SET_FIELD(DPORT_CPU_PER_CONF_REG, DPORT_CPUPERIOD_SEL, per_conf); REG_SET_FIELD(DPORT_SYSCLK_CONF_REG, DPORT_PRE_DIV_CNT, 0); - ets_update_cpu_frequency(xtal_freq); - /* Frequency switch is synchronized to SLOW_CLK cycle. Wait until the switch - * is complete before disabling the PLL. - */ - /* register SOC_CLK_SEL is moved to APB domain, so this delay is not neccessary any more */ - //rtc_clk_wait_for_slow_cycle(); + REG_SET_FIELD(RTC_CNTL_REG, RTC_CNTL_DIG_DBIAS_WAK, dbias); + REG_SET_FIELD(DPORT_SYSCLK_CONF_REG, DPORT_SOC_CLK_SEL, DPORT_SOC_CLK_SEL_PLL); + rtc_clk_apb_freq_update(80 * MHZ); + ets_update_cpu_frequency(cpu_freq_mhz); +} - DPORT_REG_SET_FIELD(DPORT_CPU_PER_CONF_REG, DPORT_CPUPERIOD_SEL, 0); +bool rtc_clk_cpu_freq_mhz_to_config(uint32_t freq_mhz, rtc_cpu_freq_config_t* out_config) +{ + uint32_t source_freq_mhz; + rtc_cpu_freq_src_t source; + uint32_t divider; + uint32_t real_freq_mhz; - /* BBPLL force power down won't affect force power up setting */ - SET_PERI_REG_MASK(RTC_CNTL_OPTIONS0_REG, - RTC_CNTL_BB_I2C_FORCE_PD | RTC_CNTL_BBPLL_FORCE_PD | - RTC_CNTL_BBPLL_I2C_FORCE_PD); - s_cur_pll = RTC_PLL_NONE; - rtc_clk_apb_freq_update(xtal_freq * MHZ); + uint32_t xtal_freq = (uint32_t) rtc_clk_xtal_freq_get(); + if (freq_mhz <= xtal_freq) { + divider = xtal_freq / freq_mhz; + real_freq_mhz = (xtal_freq + divider / 2) / divider; /* round */ + if (real_freq_mhz != freq_mhz) { + // no suitable divider + return false; + } - /* is APLL under force power down? */ - /* may need equivalent function - uint32_t apll_fpd = REG_GET_FIELD(RTC_CNTL_ANA_CONF_REG, RTC_CNTL_PLLA_FORCE_PD); - - * if (apll_fpd) { - * SET_PERI_REG_MASK(RTC_CNTL_OPTIONS0_REG, RTC_CNTL_BIAS_I2C_FORCE_PD); - * } - */ - - /* now switch to the desired frequency */ - if (cpu_freq == RTC_CPU_FREQ_XTAL) { - /* already at XTAL, nothing to do */ - } else if (cpu_freq == RTC_CPU_FREQ_2M) { - /* set up divider to produce 2MHz from XTAL */ - REG_SET_FIELD(DPORT_SYSCLK_CONF_REG, DPORT_PRE_DIV_CNT, (xtal_freq / 2) - 1); - ets_update_cpu_frequency(2); - rtc_clk_apb_freq_update(2 * MHZ); - /* lower the voltage */ - REG_SET_FIELD(RTC_CNTL_REG, RTC_CNTL_DIG_DBIAS_WAK, DIG_DBIAS_2M); - } else { - /* use PLL as clock source */ - CLEAR_PERI_REG_MASK(RTC_CNTL_OPTIONS0_REG, - RTC_CNTL_BB_I2C_FORCE_PD | - RTC_CNTL_BBPLL_FORCE_PD | RTC_CNTL_BBPLL_I2C_FORCE_PD); - if (cpu_freq > RTC_CPU_FREQ_2M) { - rtc_clk_bbpll_set(xtal_freq, RTC_PLL_320M); - s_cur_pll = RTC_PLL_320M; - } else { - rtc_clk_bbpll_set(xtal_freq, RTC_PLL_480M); - s_cur_pll = RTC_PLL_480M; - } - - if ((cpu_freq == RTC_CPU_FREQ_80M) || (cpu_freq == RTC_CPU_320M_80M)) { - REG_SET_FIELD(RTC_CNTL_REG, RTC_CNTL_DIG_DBIAS_WAK, DIG_DBIAS_80M_160M); - DPORT_REG_SET_FIELD(DPORT_CPU_PER_CONF_REG, DPORT_CPUPERIOD_SEL, 0); - ets_update_cpu_frequency(80); - } else if ((cpu_freq == RTC_CPU_FREQ_160M) || (cpu_freq == RTC_CPU_320M_160M)) { - REG_SET_FIELD(RTC_CNTL_REG, RTC_CNTL_DIG_DBIAS_WAK, DIG_DBIAS_80M_160M); - DPORT_REG_SET_FIELD(DPORT_CPU_PER_CONF_REG, DPORT_CPUPERIOD_SEL, 1); - ets_update_cpu_frequency(160); - } else if (cpu_freq == RTC_CPU_FREQ_240M) { - REG_SET_FIELD(RTC_CNTL_REG, RTC_CNTL_DIG_DBIAS_WAK, DIG_DBIAS_240M); - DPORT_REG_SET_FIELD(DPORT_CPU_PER_CONF_REG, DPORT_CPUPERIOD_SEL, 2); - ets_update_cpu_frequency(240); - } - REG_SET_FIELD(DPORT_SYSCLK_CONF_REG, DPORT_SOC_CLK_SEL, 1); - //rtc_clk_wait_for_slow_cycle(); - rtc_clk_apb_freq_update(80 * MHZ); - } - s_cur_freq = cpu_freq; -} - -rtc_cpu_freq_t rtc_clk_cpu_freq_get(void) -{ - uint32_t soc_clk_sel = REG_GET_FIELD(DPORT_SYSCLK_CONF_REG, DPORT_SOC_CLK_SEL); - switch (soc_clk_sel) { - case 0: { - uint32_t pre_div = REG_GET_FIELD(DPORT_SYSCLK_CONF_REG, DPORT_PRE_DIV_CNT); - if (pre_div == 0) { - return RTC_CPU_FREQ_XTAL; - } else if (pre_div == 1) { - return RTC_CPU_FREQ_XTAL_DIV2; - } else if (pre_div == rtc_clk_xtal_freq_get() / 2 - 1) { - return RTC_CPU_FREQ_2M; - } else { - assert(false && "unsupported frequency"); - } - break; - } - case 1: { - uint32_t cpuperiod_sel = DPORT_REG_GET_FIELD(DPORT_CPU_PER_CONF_REG, DPORT_CPUPERIOD_SEL); - uint32_t pllfreq_sel = DPORT_REG_GET_FIELD(DPORT_CPU_PER_CONF_REG, DPORT_PLL_FREQ_SEL); - if (cpuperiod_sel == 0) { - if (pllfreq_sel == 1) { - return RTC_CPU_FREQ_80M; - } else { - return RTC_CPU_320M_80M; - } - } else if (cpuperiod_sel == 1) { - if (pllfreq_sel == 1) { - return RTC_CPU_FREQ_160M; - } else { - return RTC_CPU_320M_160M; - } - } else if (cpuperiod_sel == 2) { - return RTC_CPU_FREQ_240M; - } else { - assert(false && "unsupported frequency"); - } - break; - } - case 2: - case 3: - default: - assert(false && "unsupported frequency"); - } - return 0; -} - -uint32_t rtc_clk_cpu_freq_value(rtc_cpu_freq_t cpu_freq) -{ - switch (cpu_freq) { - case RTC_CPU_FREQ_XTAL: - return ((uint32_t) rtc_clk_xtal_freq_get()) * MHZ; - case RTC_CPU_FREQ_XTAL_DIV2: - return ((uint32_t) rtc_clk_xtal_freq_get()) / 2 * MHZ; - case RTC_CPU_FREQ_2M: - return 2 * MHZ; - case RTC_CPU_FREQ_80M: - return 80 * MHZ; - case RTC_CPU_FREQ_160M: - return 160 * MHZ; - case RTC_CPU_FREQ_240M: - return 240 * MHZ; - case RTC_CPU_320M_80M: - return 80 * MHZ; - case RTC_CPU_320M_160M: - return 160 * MHZ; - default: - assert(false && "invalid rtc_cpu_freq_t value"); - return 0; - } -} - -bool rtc_clk_cpu_freq_from_mhz(int mhz, rtc_cpu_freq_t *out_val) -{ - if (mhz == 240) { - *out_val = RTC_CPU_FREQ_240M; - } else if (mhz == 160) { - *out_val = RTC_CPU_FREQ_160M; - } else if (mhz == 80) { - *out_val = RTC_CPU_FREQ_80M; - } else if (mhz == (int) rtc_clk_xtal_freq_get()) { - *out_val = RTC_CPU_FREQ_XTAL; - } else if (mhz == (int) rtc_clk_xtal_freq_get() / 2) { - *out_val = RTC_CPU_FREQ_XTAL_DIV2; - } else if (mhz == 2) { - *out_val = RTC_CPU_FREQ_2M; + source_freq_mhz = xtal_freq; + source = RTC_CPU_FREQ_SRC_XTAL; + } else if (freq_mhz == 80) { + real_freq_mhz = freq_mhz; + source = RTC_CPU_FREQ_SRC_PLL; + source_freq_mhz = RTC_PLL_FREQ_480M; + divider = 6; + } else if (freq_mhz == 160) { + real_freq_mhz = freq_mhz; + source = RTC_CPU_FREQ_SRC_PLL; + source_freq_mhz = RTC_PLL_FREQ_480M; + divider = 3; + } else if (freq_mhz == 240) { + real_freq_mhz = freq_mhz; + source = RTC_CPU_FREQ_SRC_PLL; + source_freq_mhz = RTC_PLL_FREQ_480M; + divider = 2; } else { + // unsupported frequency return false; } + *out_config = (rtc_cpu_freq_config_t) { + .source = source, + .div = divider, + .source_freq_mhz = source_freq_mhz, + .freq_mhz = real_freq_mhz + }; return true; } -/* Values of RTC_XTAL_FREQ_REG and RTC_APB_FREQ_REG are stored as two copies in - * lower and upper 16-bit halves. These are the routines to work with such a - * representation. +void rtc_clk_cpu_freq_set_config(const rtc_cpu_freq_config_t* config) +{ + rtc_xtal_freq_t xtal_freq = rtc_clk_xtal_freq_get(); + uint32_t soc_clk_sel = REG_GET_FIELD(DPORT_SYSCLK_CONF_REG, DPORT_SOC_CLK_SEL); + if (soc_clk_sel != DPORT_SOC_CLK_SEL_XTAL) { + rtc_clk_cpu_freq_to_xtal(xtal_freq, 1); + } + if (soc_clk_sel == DPORT_SOC_CLK_SEL_PLL) { + rtc_clk_bbpll_disable(); + } + if (config->source == RTC_CPU_FREQ_SRC_XTAL) { + if (config->div > 1) { + rtc_clk_cpu_freq_to_xtal(config->freq_mhz, config->div); + } + } else if (config->source == RTC_CPU_FREQ_SRC_PLL) { + rtc_clk_bbpll_enable(); + rtc_clk_bbpll_configure(rtc_clk_xtal_freq_get(), config->source_freq_mhz); + rtc_clk_cpu_freq_to_pll_mhz(config->freq_mhz); + } else if (config->source == RTC_CPU_FREQ_SRC_8M) { + rtc_clk_cpu_freq_to_8m(); + } +} + +void rtc_clk_cpu_freq_get_config(rtc_cpu_freq_config_t* out_config) +{ + rtc_cpu_freq_src_t source; + uint32_t source_freq_mhz; + uint32_t div; + uint32_t freq_mhz; + uint32_t soc_clk_sel = REG_GET_FIELD(DPORT_SYSCLK_CONF_REG, DPORT_SOC_CLK_SEL); + switch (soc_clk_sel) { + case DPORT_SOC_CLK_SEL_XTAL: { + source = RTC_CPU_FREQ_SRC_XTAL; + div = REG_GET_FIELD(DPORT_SYSCLK_CONF_REG, DPORT_PRE_DIV_CNT) + 1; + source_freq_mhz = (uint32_t) rtc_clk_xtal_freq_get(); + freq_mhz = source_freq_mhz / div; + } + break; + case DPORT_SOC_CLK_SEL_PLL: { + source = RTC_CPU_FREQ_SRC_PLL; + uint32_t cpuperiod_sel = DPORT_REG_GET_FIELD(DPORT_CPU_PER_CONF_REG, DPORT_CPUPERIOD_SEL); + uint32_t pllfreq_sel = DPORT_REG_GET_FIELD(DPORT_CPU_PER_CONF_REG, DPORT_PLL_FREQ_SEL); + source_freq_mhz = (pllfreq_sel) ? RTC_PLL_FREQ_480M : RTC_PLL_FREQ_320M; + if (cpuperiod_sel == DPORT_CPUPERIOD_SEL_80) { + div = (source_freq_mhz == RTC_PLL_FREQ_480M) ? 6 : 4; + freq_mhz = 80; + } else if (cpuperiod_sel == DPORT_CPUPERIOD_SEL_160) { + div = (source_freq_mhz == RTC_PLL_FREQ_480M) ? 3 : 2; + div = 3; + freq_mhz = 160; + } else if (cpuperiod_sel == DPORT_CPUPERIOD_SEL_240) { + div = 2; + freq_mhz = 240; + } else { + SOC_LOGE(TAG, "unsupported frequency configuration"); + abort(); + } + break; + } + case DPORT_SOC_CLK_SEL_8M: + source = RTC_CPU_FREQ_SRC_8M; + source_freq_mhz = 8; + div = 1; + freq_mhz = source_freq_mhz; + break; + case DPORT_SOC_CLK_SEL_APLL: + default: + SOC_LOGE(TAG, "unsupported frequency configuration"); + abort(); + } + *out_config = (rtc_cpu_freq_config_t) { + .source = source, + .source_freq_mhz = source_freq_mhz, + .div = div, + .freq_mhz = freq_mhz + }; +} + +void rtc_clk_cpu_freq_set_config_fast(const rtc_cpu_freq_config_t* config) +{ + if (config->source == RTC_CPU_FREQ_SRC_XTAL) { + rtc_clk_cpu_freq_to_xtal(config->freq_mhz, config->div); + } else if (config->source == RTC_CPU_FREQ_SRC_PLL && + s_cur_pll_freq == config->source_freq_mhz) { + rtc_clk_cpu_freq_to_pll_mhz(config->freq_mhz); + } else { + /* fallback */ + rtc_clk_cpu_freq_set_config(config); + } +} + +void rtc_clk_cpu_freq_set_xtal(void) +{ + int freq_mhz = (int) rtc_clk_xtal_freq_get(); + + rtc_clk_cpu_freq_to_xtal(freq_mhz, 1); + rtc_clk_bbpll_disable(); +} + +/** + * Switch to XTAL frequency. Does not disable the PLL. */ -static bool clk_val_is_valid(uint32_t val) +void rtc_clk_cpu_freq_to_xtal(int freq, int div) { - return (val & 0xffff) == ((val >> 16) & 0xffff) && - val != 0 && - val != UINT32_MAX; + ets_update_cpu_frequency(freq); + /* Set divider from XTAL to APB clock. Need to set divider to 1 (reg. value 0) first. */ + REG_SET_FIELD(DPORT_SYSCLK_CONF_REG, DPORT_PRE_DIV_CNT, 0); + REG_SET_FIELD(DPORT_SYSCLK_CONF_REG, DPORT_PRE_DIV_CNT, div - 1); + /* no need to adjust the REF_TICK */ + /* switch clock source */ + REG_SET_FIELD(DPORT_SYSCLK_CONF_REG, DPORT_SOC_CLK_SEL, DPORT_SOC_CLK_SEL_XTAL); + rtc_clk_apb_freq_update(freq * MHZ); + /* lower the voltage */ + if (freq <= 2) { + REG_SET_FIELD(RTC_CNTL_REG, RTC_CNTL_DIG_DBIAS_WAK, DIG_DBIAS_2M); + } else { + REG_SET_FIELD(RTC_CNTL_REG, RTC_CNTL_DIG_DBIAS_WAK, DIG_DBIAS_XTAL); + } } -static uint32_t reg_val_to_clk_val(uint32_t val) +static void rtc_clk_cpu_freq_to_8m(void) { - return val & UINT16_MAX; -} - -static uint32_t clk_val_to_reg_val(uint32_t val) -{ - return (val & UINT16_MAX) | ((val & UINT16_MAX) << 16); + ets_update_cpu_frequency(8); + REG_SET_FIELD(RTC_CNTL_REG, RTC_CNTL_DIG_DBIAS_WAK, DIG_DBIAS_XTAL); + REG_SET_FIELD(DPORT_SYSCLK_CONF_REG, DPORT_PRE_DIV_CNT, 0); + REG_SET_FIELD(DPORT_SYSCLK_CONF_REG, DPORT_SOC_CLK_SEL, DPORT_SOC_CLK_SEL_8M); + rtc_clk_apb_freq_update(RTC_FAST_CLK_FREQ_8M); } rtc_xtal_freq_t rtc_clk_xtal_freq_get(void) @@ -614,66 +526,6 @@ void rtc_clk_8m_divider_set(uint32_t div) SET_PERI_REG_MASK(RTC_CNTL_CLK_CONF_REG, RTC_CNTL_CK8M_DIV_SEL_VLD); } -void rtc_clk_init(rtc_clk_config_t cfg) -{ - rtc_cpu_freq_t cpu_source_before = rtc_clk_cpu_freq_get(); - /* If we get a TG WDT system reset while running at 240MHz, - * DPORT_CPUPERIOD_SEL register will be reset to 0 resulting in 120MHz - * APB and CPU frequencies after reset. This will cause issues with XTAL - * frequency estimation, so we switch to XTAL frequency first. - * - * Ideally we would only do this if SYSCON_SOC_CLK_SEL == PLL and - * PLL is configured for 480M, but it takes less time to switch to 40M and - * run the following code than querying the PLL does. - */ - if (REG_GET_FIELD(DPORT_SYSCLK_CONF_REG, DPORT_SOC_CLK_SEL) == 1) { - rtc_clk_cpu_freq_set(RTC_CPU_FREQ_XTAL); - } - - /* Set tuning parameters for 8M and 150k clocks. - * Note: this doesn't attempt to set the clocks to precise frequencies. - * Instead, we calibrate these clocks against XTAL frequency later, when necessary. - * - SCK_DCAP value controls tuning of 150k clock. - * The higher the value of DCAP is, the lower is the frequency. - * - CK8M_DFREQ value controls tuning of 8M clock. - * CLK_8M_DFREQ constant gives the best temperature characteristics. - */ - REG_SET_FIELD(RTC_CNTL_REG, RTC_CNTL_SCK_DCAP, cfg.slow_clk_dcap); - REG_SET_FIELD(RTC_CNTL_CLK_CONF_REG, RTC_CNTL_CK8M_DFREQ, cfg.clk_8m_dfreq); - - /* Configure 150k clock division */ - rtc_clk_divider_set(cfg.clk_rtc_clk_div); - /* Configure 8M clock division */ - rtc_clk_8m_divider_set(cfg.clk_8m_clk_div); - - /* Enable the internal bus used to configure PLLs */ - SET_PERI_REG_BITS(ANA_CONFIG_REG, ANA_CONFIG_M, ANA_CONFIG_M, ANA_CONFIG_S); - CLEAR_PERI_REG_MASK(ANA_CONFIG_REG, I2C_APLL_M | I2C_BBPLL_M); - - rtc_xtal_freq_t xtal_freq = cfg.xtal_freq; - uart_tx_wait_idle(0); - rtc_clk_xtal_freq_update(xtal_freq); - rtc_clk_apb_freq_update(xtal_freq * MHZ); - /* Set CPU frequency */ - rtc_clk_cpu_freq_set(cfg.cpu_freq); - - /* Re-calculate the ccount to make time calculation correct. */ - uint32_t freq_before = rtc_clk_cpu_freq_value(cpu_source_before) / MHZ; - uint32_t freq_after = rtc_clk_cpu_freq_value(cfg.cpu_freq) / MHZ; - XTHAL_SET_CCOUNT( XTHAL_GET_CCOUNT() * freq_after / freq_before ); - - /* Slow & fast clocks setup */ - if (cfg.slow_freq == RTC_SLOW_FREQ_32K_XTAL) { - rtc_clk_32k_enable(true); - } - if (cfg.fast_freq == RTC_FAST_FREQ_8M) { - bool need_8md256 = cfg.slow_freq == RTC_SLOW_FREQ_8MD256; - rtc_clk_8m_enable(true, need_8md256); - } - rtc_clk_fast_freq_set(cfg.fast_freq); - rtc_clk_slow_freq_set(cfg.slow_freq); -} - /* Name used in libphy.a:phy_chip_v7.o * TODO: update the library to use rtc_clk_xtal_freq_get */ diff --git a/components/soc/src/esp32s2/rtc_clk_common.h b/components/soc/src/esp32s2/rtc_clk_common.h new file mode 100644 index 000000000..cb92f67ac --- /dev/null +++ b/components/soc/src/esp32s2/rtc_clk_common.h @@ -0,0 +1,56 @@ +// Copyright 2015-2020 Espressif Systems (Shanghai) PTE LTD +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#pragma once + +#define MHZ (1000000) + +#define DPORT_CPUPERIOD_SEL_80 0 +#define DPORT_CPUPERIOD_SEL_160 1 +#define DPORT_CPUPERIOD_SEL_240 2 + +#define DPORT_SOC_CLK_SEL_XTAL 0 +#define DPORT_SOC_CLK_SEL_PLL 1 +#define DPORT_SOC_CLK_SEL_8M 2 +#define DPORT_SOC_CLK_SEL_APLL 3 + +#define RTC_FAST_CLK_FREQ_8M 8500000 + +#ifdef __cplusplus +extern "C" { +#endif + +void rtc_clk_cpu_freq_to_xtal(int freq, int div); + +/* Values of RTC_XTAL_FREQ_REG and RTC_APB_FREQ_REG are stored as two copies in + * lower and upper 16-bit halves. These are the routines to work with such a + * representation. + */ +static inline bool clk_val_is_valid(uint32_t val) { + return (val & 0xffff) == ((val >> 16) & 0xffff) && + val != 0 && + val != UINT32_MAX; +} + +static inline uint32_t reg_val_to_clk_val(uint32_t val) { + return val & UINT16_MAX; +} + +static inline uint32_t clk_val_to_reg_val(uint32_t val) { + return (val & UINT16_MAX) | ((val & UINT16_MAX) << 16); +} + +#ifdef __cplusplus +} +#endif diff --git a/components/soc/src/esp32s2/rtc_clk_init.c b/components/soc/src/esp32s2/rtc_clk_init.c new file mode 100644 index 000000000..6480e593b --- /dev/null +++ b/components/soc/src/esp32s2/rtc_clk_init.c @@ -0,0 +1,88 @@ +// Copyright 2015-2020 Espressif Systems (Shanghai) PTE LTD +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include +#include +#include +#include +#include "esp32s2/rom/ets_sys.h" +#include "esp32s2/rom/rtc.h" +#include "esp32s2/rom/uart.h" +#include "soc/rtc.h" +#include "soc/rtc_periph.h" +#include "soc/sens_periph.h" +#include "soc/efuse_periph.h" +#include "soc/apb_ctrl_reg.h" +#include "i2c_rtc_clk.h" +#include "soc_log.h" +#include "sdkconfig.h" +#include "xtensa/core-macros.h" +#include "rtc_clk_common.h" + +static const char* TAG = "rtc_clk_init"; + +void rtc_clk_init(rtc_clk_config_t cfg) +{ + rtc_cpu_freq_config_t old_config, new_config; + + /* Set tuning parameters for 8M and 90k clocks. + * Note: this doesn't attempt to set the clocks to precise frequencies. + * Instead, we calibrate these clocks against XTAL frequency later, when necessary. + * - SCK_DCAP value controls tuning of 90k clock. + * The higher the value of DCAP is, the lower is the frequency. + * - CK8M_DFREQ value controls tuning of 8M clock. + * CLK_8M_DFREQ constant gives the best temperature characteristics. + */ + REG_SET_FIELD(RTC_CNTL_REG, RTC_CNTL_SCK_DCAP, cfg.slow_clk_dcap); + REG_SET_FIELD(RTC_CNTL_CLK_CONF_REG, RTC_CNTL_CK8M_DFREQ, cfg.clk_8m_dfreq); + + /* Configure 90k clock division */ + rtc_clk_divider_set(cfg.clk_rtc_clk_div); + + /* Configure 8M clock division */ + rtc_clk_8m_divider_set(cfg.clk_8m_clk_div); + + /* Enable the internal bus used to configure PLLs */ + SET_PERI_REG_BITS(ANA_CONFIG_REG, ANA_CONFIG_M, ANA_CONFIG_M, ANA_CONFIG_S); + CLEAR_PERI_REG_MASK(ANA_CONFIG_REG, I2C_APLL_M | I2C_BBPLL_M); + + rtc_xtal_freq_t xtal_freq = cfg.xtal_freq; + uart_tx_wait_idle(0); + rtc_clk_xtal_freq_update(xtal_freq); + rtc_clk_apb_freq_update(xtal_freq * MHZ); + + /* Set CPU frequency */ + rtc_clk_cpu_freq_get_config(&old_config); + uint32_t freq_before = old_config.freq_mhz; + bool res = rtc_clk_cpu_freq_mhz_to_config(cfg.cpu_freq_mhz, &new_config); + if (!res) { + SOC_LOGE(TAG, "invalid CPU frequency value"); + abort(); + } + rtc_clk_cpu_freq_set_config(&new_config); + + /* Re-calculate the ccount to make time calculation correct. */ + XTHAL_SET_CCOUNT( (uint64_t)XTHAL_GET_CCOUNT() * cfg.cpu_freq_mhz / freq_before ); + + /* Slow & fast clocks setup */ + if (cfg.slow_freq == RTC_SLOW_FREQ_32K_XTAL) { + rtc_clk_32k_enable(true); + } + if (cfg.fast_freq == RTC_FAST_FREQ_8M) { + bool need_8md256 = cfg.slow_freq == RTC_SLOW_FREQ_8MD256; + rtc_clk_8m_enable(true, need_8md256); + } + rtc_clk_fast_freq_set(cfg.fast_freq); + rtc_clk_slow_freq_set(cfg.slow_freq); +}