From 05a5410033d898fb3946efdd415a2ed9ba0f2fd5 Mon Sep 17 00:00:00 2001 From: Ivan Grokhotkov Date: Sun, 29 Jul 2018 09:34:49 +0300 Subject: [PATCH 1/9] esp_clk.h: add esp_clk_xtal_freq This adds a user API for getting the XTAL frequency --- components/esp32/clk.c | 9 +++++++-- components/esp32/include/esp_clk.h | 11 +++++++++++ 2 files changed, 18 insertions(+), 2 deletions(-) diff --git a/components/esp32/clk.c b/components/esp32/clk.c index 4b1f0369c..9b0097774 100644 --- a/components/esp32/clk.c +++ b/components/esp32/clk.c @@ -109,12 +109,17 @@ void esp_clk_init(void) int IRAM_ATTR esp_clk_cpu_freq(void) { - return g_ticks_per_us_pro * 1000000; + return g_ticks_per_us_pro * MHZ; } int IRAM_ATTR esp_clk_apb_freq(void) { - return MIN(g_ticks_per_us_pro, 80) * 1000000; + return MIN(g_ticks_per_us_pro, 80) * MHZ; +} + +int IRAM_ATTR esp_clk_xtal_freq(void) +{ + return rtc_clk_xtal_freq_get() * MHZ; } void IRAM_ATTR ets_update_cpu_frequency(uint32_t ticks_per_us) diff --git a/components/esp32/include/esp_clk.h b/components/esp32/include/esp_clk.h index 6526aa927..1a91d26f9 100644 --- a/components/esp32/include/esp_clk.h +++ b/components/esp32/include/esp_clk.h @@ -62,6 +62,17 @@ int esp_clk_cpu_freq(void); */ int esp_clk_apb_freq(void); +/** + * @brief Return frequency of the main XTAL + * + * Frequency of the main XTAL can be either auto-detected or set at compile + * time (see CONFIG_ESP32_XTAL_FREQ_SEL sdkconfig option). In both cases, this + * function returns the actual value at run time. + * + * @return XTAL frequency, in Hz + */ +int esp_clk_xtal_freq(void); + /** * @brief Read value of RTC counter, converting it to microseconds From 2e31cce3902bbf73c8e7192d85cf89b62c25bae9 Mon Sep 17 00:00:00 2001 From: Ivan Grokhotkov Date: Sun, 29 Jul 2018 10:47:58 +0300 Subject: [PATCH 2/9] soc/rtc: CPU frequency settings refactoring Previous APIs used to set CPU frequency used CPU frequencies listed in rtc_cpu_freq_t enumeration. This was problematic for two reasons. First, supporting many possible frequency values obtained by dividing XTAL frequency was hard, as every value would have to be listed in the enumeration. Since different base XTAL frequencies are supported, this further complicated things, since not all of these divided frequencies would be valid for any given XTAL frequency. Second, having to deal with enumeration values often involved switch statements to convert between enumeration and MHz values, handle PLL/XTAL frequencies separately, etc. This change introduces rtc_cpu_freq_config_t structure, which contains CPU frequency (in MHz) and information on how this frequency has to be generated: clock source (XTAL/PLL), source frequency, clock divider value. More fields can be added to this structure in the future. This structure simplifies many parts of the code, since both frequency value and frequency generation settings can be accessed in any place in code without the need for conversions. Additionally, this change adds setting of REF_TICK dividers to support frequencies lower then XTAL with DFS. --- components/soc/esp32/include/soc/rtc.h | 126 ++++++- components/soc/esp32/rtc_clk.c | 470 +++++++++++++++++-------- 2 files changed, 427 insertions(+), 169 deletions(-) diff --git a/components/soc/esp32/include/soc/rtc.h b/components/soc/esp32/include/soc/rtc.h index 1ece26c97..e7e1c1d14 100644 --- a/components/soc/esp32/include/soc/rtc.h +++ b/components/soc/esp32/include/soc/rtc.h @@ -75,6 +75,26 @@ typedef enum { RTC_CPU_FREQ_2M = 4, //!< 2 MHz } 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_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 */ @@ -108,13 +128,13 @@ typedef enum { * Initialization parameters for rtc_clk_init */ typedef struct { - rtc_xtal_freq_t xtal_freq : 8; //!< Main XTAL frequency - rtc_cpu_freq_t cpu_freq : 3; //!< CPU frequency to set - 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) - uint32_t slow_clk_dcap : 8; //!< RTC 150k clock adjustment parameter (higher value leads to lower frequency) - uint32_t clk_8m_dfreq : 8; //!< RTC 8m clock adjustment parameter (higher value leads to higher frequency) + rtc_xtal_freq_t xtal_freq : 8; //!< Main XTAL frequency + rtc_cpu_freq_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) + uint32_t slow_clk_dcap : 8; //!< RTC 150k clock adjustment parameter (higher value leads to lower frequency) + uint32_t clk_8m_dfreq : 8; //!< RTC 8m clock adjustment parameter (higher value leads to higher frequency) } rtc_clk_config_t; /** @@ -122,7 +142,7 @@ typedef struct { */ #define RTC_CLK_CONFIG_DEFAULT() { \ .xtal_freq = RTC_XTAL_FREQ_AUTO, \ - .cpu_freq = RTC_CPU_FREQ_80M, \ + .cpu_freq_mhz = 80, \ .fast_freq = RTC_FAST_FREQ_8M, \ .slow_freq = RTC_SLOW_FREQ_RTC, \ .clk_8m_div = 0, \ @@ -281,6 +301,9 @@ rtc_fast_freq_t rtc_clk_fast_freq_get(); /** * @brief Switch CPU frequency * + * @note This function is deprecated and will be removed. + * See rtc_clk_cpu_freq_config_set instead. + * * 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 @@ -288,11 +311,14 @@ rtc_fast_freq_t rtc_clk_fast_freq_get(); * * @param cpu_freq new CPU frequency */ -void rtc_clk_cpu_freq_set(rtc_cpu_freq_t cpu_freq); +void rtc_clk_cpu_freq_set(rtc_cpu_freq_t cpu_freq) __attribute__((deprecated)); /** * @brief Switch CPU frequency * + * @note This function is deprecated and will be removed. + * See rtc_clk_cpu_freq_set_config_fast instead. + * * 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). @@ -307,11 +333,14 @@ void rtc_clk_cpu_freq_set(rtc_cpu_freq_t cpu_freq); * * @param cpu_freq new CPU frequency */ -void rtc_clk_cpu_freq_set_fast(rtc_cpu_freq_t cpu_freq); +void rtc_clk_cpu_freq_set_fast(rtc_cpu_freq_t cpu_freq) __attribute__((deprecated)); /** * @brief Get the currently selected CPU frequency * + * @note This function is deprecated and will be removed. + * See rtc_clk_cpu_freq_get_config instead. + * * 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 @@ -320,22 +349,93 @@ void rtc_clk_cpu_freq_set_fast(rtc_cpu_freq_t cpu_freq); * * @return CPU frequency (one of rtc_cpu_freq_t values) */ -rtc_cpu_freq_t rtc_clk_cpu_freq_get(); +rtc_cpu_freq_t rtc_clk_cpu_freq_get() __attribute__((deprecated)); /** * @brief Get corresponding frequency value for rtc_cpu_freq_t enum value + * + * @note This function is deprecated and will be removed. + * See rtc_clk_cpu_freq_get/set_config instead. + * * @param cpu_freq CPU frequency, on of rtc_cpu_freq_t values * @return CPU frequency, in HZ */ -uint32_t rtc_clk_cpu_freq_value(rtc_cpu_freq_t cpu_freq); +uint32_t rtc_clk_cpu_freq_value(rtc_cpu_freq_t cpu_freq) __attribute__((deprecated)); /** * @brief Get rtc_cpu_freq_t enum value for given CPU frequency + * + * @note This function is deprecated and will be removed. + * See rtc_clk_cpu_freq_mhz_to_config instead. + * * @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 */ - bool rtc_clk_cpu_freq_from_mhz(int cpu_freq_mhz, rtc_cpu_freq_t* out_val); + bool rtc_clk_cpu_freq_from_mhz(int cpu_freq_mhz, rtc_cpu_freq_t* out_val) __attribute__((deprecated)); + +/** + * @brief Get CPU frequency config corresponding to a rtc_cpu_freq_t value + * @param cpu_freq CPU frequency enumeration value + * @param[out] out_config Output, CPU frequency configuration structure + */ + void rtc_clk_cpu_freq_to_config(rtc_cpu_freq_t cpu_freq, rtc_cpu_freq_config_t* out_config); + + /** + * @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 + */ + bool rtc_clk_cpu_freq_mhz_to_config(uint32_t freq_mhz, rtc_cpu_freq_config_t* out_config); + + /** + * @brief Switch CPU frequency + * + * This function sets CPU frequency according to the given configuration + * structure. It enables PLLs, if necessary. + * + * @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 + */ + void rtc_clk_cpu_freq_set_config(const rtc_cpu_freq_config_t* config); + + /** + * @brief Switch CPU frequency (optimized for speed) + * + * 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. + * + * 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). + * + * @param config CPU frequency configuration structure + */ + void rtc_clk_cpu_freq_set_config_fast(const rtc_cpu_freq_config_t* config); + + /** + * @brief Get the currently used CPU frequency configuration + * @param[out] out_config Output, CPU frequency configuration structure + */ + void rtc_clk_cpu_freq_get_config(rtc_cpu_freq_config_t* out_config); + + /** + * @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. + */ + void rtc_clk_cpu_freq_set_xtal(); + /** * @brief Store new APB frequency value into RTC_APB_FREQ_REG diff --git a/components/soc/esp32/rtc_clk.c b/components/soc/esp32/rtc_clk.c index d919bb822..ea899db3f 100644 --- a/components/soc/esp32/rtc_clk.c +++ b/components/soc/esp32/rtc_clk.c @@ -16,6 +16,7 @@ #include #include #include +#include #include "rom/ets_sys.h" #include "rom/rtc.h" #include "rom/uart.h" @@ -98,17 +99,19 @@ static const char* TAG = "rtc_clk"; #define DIG_DBIAS_XTAL RTC_CNTL_DBIAS_1V10 #define DIG_DBIAS_2M RTC_CNTL_DBIAS_1V00 -/* 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; +static void rtc_clk_cpu_freq_to_xtal(int freq, int div); +static void rtc_clk_cpu_freq_to_8m(); +static void rtc_clk_bbpll_disable(); +static void rtc_clk_bbpll_enable(); +static void rtc_clk_cpu_freq_to_pll_mhz(int cpu_freq_mhz); +static bool rtc_clk_cpu_freq_from_mhz_internal(int mhz, rtc_cpu_freq_t* out_val); + +// Current PLL frequency, in MHZ (320 or 480). Zero if PLL is not enabled. +static int s_cur_pll_freq; static void rtc_clk_32k_enable_internal(int dac, int dres, int dbias) { @@ -275,7 +278,7 @@ rtc_fast_freq_t rtc_clk_fast_freq_get() return REG_GET_FIELD(RTC_CNTL_CLK_CONF_REG, RTC_CNTL_FAST_CLK_RTC_SEL); } -void rtc_clk_bbpll_set(rtc_xtal_freq_t xtal_freq, rtc_cpu_freq_t cpu_freq) +void rtc_clk_bbpll_configure(rtc_xtal_freq_t xtal_freq, int pll_freq) { uint8_t div_ref; uint8_t div7_0; @@ -284,7 +287,7 @@ void rtc_clk_bbpll_set(rtc_xtal_freq_t xtal_freq, rtc_cpu_freq_t cpu_freq) uint8_t dcur; uint8_t bw; - if (cpu_freq != RTC_CPU_FREQ_240M) { + if (pll_freq == RTC_PLL_FREQ_320M) { /* Raise the voltage, if needed */ REG_SET_FIELD(RTC_CNTL_REG, RTC_CNTL_DIG_DBIAS_WAK, DIG_DBIAS_80M_160M); /* Configure 320M PLL */ @@ -376,96 +379,47 @@ void rtc_clk_bbpll_set(rtc_xtal_freq_t xtal_freq, rtc_cpu_freq_t cpu_freq) 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); + s_cur_pll_freq = pll_freq; } /** * Switch to XTAL frequency. Does not disable the PLL. */ -static void rtc_clk_cpu_freq_to_xtal() +static void rtc_clk_cpu_freq_to_xtal(int freq, int div) { - 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(APB_CTRL_SYSCLK_CONF_REG, APB_CTRL_PRE_DIV_CNT, 0); - REG_SET_FIELD(RTC_CNTL_CLK_CONF_REG, RTC_CNTL_SOC_CLK_SEL, RTC_CNTL_SOC_CLK_SEL_XTL); - 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; -} - -/** - * 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) -{ - int freq = 0; - if (s_cur_pll == RTC_PLL_NONE || - (cpu_freq == RTC_CPU_FREQ_240M && s_cur_pll == RTC_PLL_320M) || - (cpu_freq != RTC_CPU_FREQ_240M && s_cur_pll == RTC_PLL_480M)) { - /* need to switch PLLs, fall back to full implementation */ - rtc_clk_cpu_freq_set(cpu_freq); - return; - } - - if (cpu_freq == RTC_CPU_FREQ_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) { - 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(RTC_CNTL_CLK_CONF_REG, RTC_CNTL_SOC_CLK_SEL, RTC_CNTL_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); - rtc_clk_wait_for_slow_cycle(); + /* set divider from XTAL to APB clock */ + REG_SET_FIELD(APB_CTRL_SYSCLK_CONF_REG, APB_CTRL_PRE_DIV_CNT, div - 1); + /* adjust ref_tick */ + REG_WRITE(APB_CTRL_XTAL_TICK_CONF_REG, freq * MHZ / REF_CLK_FREQ - 1); + /* switch clock source */ + REG_SET_FIELD(RTC_CNTL_CLK_CONF_REG, RTC_CNTL_SOC_CLK_SEL, RTC_CNTL_SOC_CLK_SEL_XTL); + DPORT_REG_WRITE(DPORT_CPU_PER_CONF_REG, 0); /* clear DPORT_CPUPERIOD_SEL */ + 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); } } -void rtc_clk_cpu_freq_set(rtc_cpu_freq_t cpu_freq) +static void rtc_clk_cpu_freq_to_8m() { - rtc_xtal_freq_t xtal_freq = rtc_clk_xtal_freq_get(); - /* Switch CPU to XTAL frequency first */ + ets_update_cpu_frequency(8); REG_SET_FIELD(RTC_CNTL_REG, RTC_CNTL_DIG_DBIAS_WAK, DIG_DBIAS_XTAL); - REG_SET_FIELD(RTC_CNTL_CLK_CONF_REG, RTC_CNTL_SOC_CLK_SEL, RTC_CNTL_SOC_CLK_SEL_XTL); REG_SET_FIELD(APB_CTRL_SYSCLK_CONF_REG, APB_CTRL_PRE_DIV_CNT, 0); - ets_update_cpu_frequency(xtal_freq); + REG_SET_FIELD(RTC_CNTL_CLK_CONF_REG, RTC_CNTL_SOC_CLK_SEL, RTC_CNTL_SOC_CLK_SEL_8M); + DPORT_REG_WRITE(DPORT_CPU_PER_CONF_REG, 0); // clear DPORT_CPUPERIOD_SEL + rtc_clk_apb_freq_update(RTC_FAST_CLK_FREQ_8M); +} - /* Frequency switch is synchronized to SLOW_CLK cycle. Wait until the switch - * is complete before disabling the PLL. - */ - rtc_clk_wait_for_slow_cycle(); - - DPORT_REG_SET_FIELD(DPORT_CPU_PER_CONF_REG, DPORT_CPUPERIOD_SEL, 0); +static void rtc_clk_bbpll_disable() +{ 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); + s_cur_pll_freq = 0; /* is APLL under force power down? */ uint32_t apll_fpd = REG_GET_FIELD(RTC_CNTL_ANA_CONF_REG, RTC_CNTL_PLLA_FORCE_PD); @@ -473,76 +427,73 @@ void rtc_clk_cpu_freq_set(rtc_cpu_freq_t cpu_freq) /* then also power down the internal I2C bus */ 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(APB_CTRL_SYSCLK_CONF_REG, APB_CTRL_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); +} + +static void rtc_clk_bbpll_enable() +{ + CLEAR_PERI_REG_MASK(RTC_CNTL_OPTIONS0_REG, + RTC_CNTL_BIAS_I2C_FORCE_PD | RTC_CNTL_BB_I2C_FORCE_PD | + RTC_CNTL_BBPLL_FORCE_PD | RTC_CNTL_BBPLL_I2C_FORCE_PD); +} + +/** + * Switch to one of PLL-based frequencies. Current frequency can be XTAL or PLL. + * PLL must already be enabled. + * @param cpu_freq new CPU frequency + */ +static void rtc_clk_cpu_freq_to_pll_mhz(int cpu_freq_mhz) +{ + int dbias = DIG_DBIAS_80M_160M; + int per_conf = 0; + if (cpu_freq_mhz == 80) { + /* nothing to do */ + } else if (cpu_freq_mhz == 160) { + per_conf = 1; + } else if (cpu_freq_mhz == 240) { + dbias = DIG_DBIAS_240M; + per_conf = 2; } else { - /* use PLL as clock source */ - CLEAR_PERI_REG_MASK(RTC_CNTL_OPTIONS0_REG, - RTC_CNTL_BIAS_I2C_FORCE_PD | RTC_CNTL_BB_I2C_FORCE_PD | - RTC_CNTL_BBPLL_FORCE_PD | RTC_CNTL_BBPLL_I2C_FORCE_PD); - rtc_clk_bbpll_set(xtal_freq, cpu_freq); - if (cpu_freq == RTC_CPU_FREQ_80M) { - DPORT_REG_SET_FIELD(DPORT_CPU_PER_CONF_REG, DPORT_CPUPERIOD_SEL, 0); - ets_update_cpu_frequency(80); - s_cur_pll = RTC_PLL_320M; - } else if (cpu_freq == RTC_CPU_FREQ_160M) { - DPORT_REG_SET_FIELD(DPORT_CPU_PER_CONF_REG, DPORT_CPUPERIOD_SEL, 1); - ets_update_cpu_frequency(160); - s_cur_pll = RTC_PLL_320M; - } else if (cpu_freq == RTC_CPU_FREQ_240M) { - DPORT_REG_SET_FIELD(DPORT_CPU_PER_CONF_REG, DPORT_CPUPERIOD_SEL, 2); - ets_update_cpu_frequency(240); - s_cur_pll = RTC_PLL_480M; - } - REG_SET_FIELD(RTC_CNTL_CLK_CONF_REG, RTC_CNTL_SOC_CLK_SEL, RTC_CNTL_SOC_CLK_SEL_PLL); - rtc_clk_wait_for_slow_cycle(); - rtc_clk_apb_freq_update(80 * MHZ); + assert(false && "invalid frequency"); } - s_cur_freq = cpu_freq; + DPORT_REG_WRITE(DPORT_CPU_PER_CONF_REG, per_conf); + REG_SET_FIELD(RTC_CNTL_REG, RTC_CNTL_DIG_DBIAS_WAK, dbias); + REG_SET_FIELD(RTC_CNTL_CLK_CONF_REG, RTC_CNTL_SOC_CLK_SEL, RTC_CNTL_SOC_CLK_SEL_PLL); + rtc_clk_apb_freq_update(80 * MHZ); + ets_update_cpu_frequency(cpu_freq_mhz); + rtc_clk_wait_for_slow_cycle(); +} + + +void rtc_clk_cpu_freq_set(rtc_cpu_freq_t cpu_freq) +{ + rtc_cpu_freq_config_t config; + rtc_clk_cpu_freq_to_config(cpu_freq, &config); + rtc_clk_cpu_freq_set_config(&config); +} + +void rtc_clk_cpu_freq_set_fast(rtc_cpu_freq_t cpu_freq) +{ + rtc_cpu_freq_config_t config; + rtc_clk_cpu_freq_to_config(cpu_freq, &config); + rtc_clk_cpu_freq_set_config_fast(&config); +} + +void rtc_clk_cpu_freq_set_xtal() +{ + int freq_mhz = (int) rtc_clk_xtal_freq_get(); + + rtc_clk_cpu_freq_to_xtal(freq_mhz, 1); + rtc_clk_wait_for_slow_cycle(); + rtc_clk_bbpll_disable(); } rtc_cpu_freq_t rtc_clk_cpu_freq_get() { - uint32_t soc_clk_sel = REG_GET_FIELD(RTC_CNTL_CLK_CONF_REG, RTC_CNTL_SOC_CLK_SEL); - switch (soc_clk_sel) { - case RTC_CNTL_SOC_CLK_SEL_XTL: { - uint32_t pre_div = REG_GET_FIELD(APB_CTRL_SYSCLK_CONF_REG, APB_CTRL_PRE_DIV_CNT); - if (pre_div == 0) { - return RTC_CPU_FREQ_XTAL; - } else if (pre_div == rtc_clk_xtal_freq_get() / 2 - 1) { - return RTC_CPU_FREQ_2M; - } else { - assert(false && "unsupported frequency"); - } - break; - } - case RTC_CNTL_SOC_CLK_SEL_PLL: { - uint32_t cpuperiod_sel = DPORT_REG_GET_FIELD(DPORT_CPU_PER_CONF_REG, DPORT_CPUPERIOD_SEL); - if (cpuperiod_sel == 0) { - return RTC_CPU_FREQ_80M; - } else if (cpuperiod_sel == 1) { - return RTC_CPU_FREQ_160M; - } else if (cpuperiod_sel == 2) { - return RTC_CPU_FREQ_240M; - } else { - assert(false && "unsupported frequency"); - } - break; - } - case RTC_CNTL_SOC_CLK_SEL_APLL: - case RTC_CNTL_SOC_CLK_SEL_8M: - default: - assert(false && "unsupported frequency"); - } - return RTC_CNTL_SOC_CLK_SEL_XTL; + rtc_cpu_freq_config_t config; + rtc_clk_cpu_freq_get_config(&config); + rtc_cpu_freq_t freq; + rtc_clk_cpu_freq_from_mhz_internal(config.freq_mhz, &freq); + return freq; } uint32_t rtc_clk_cpu_freq_value(rtc_cpu_freq_t cpu_freq) @@ -564,7 +515,7 @@ uint32_t rtc_clk_cpu_freq_value(rtc_cpu_freq_t cpu_freq) } } -bool rtc_clk_cpu_freq_from_mhz(int mhz, rtc_cpu_freq_t* out_val) +static bool rtc_clk_cpu_freq_from_mhz_internal(int mhz, rtc_cpu_freq_t* out_val) { if (mhz == 240) { *out_val = RTC_CPU_FREQ_240M; @@ -582,6 +533,200 @@ bool rtc_clk_cpu_freq_from_mhz(int mhz, rtc_cpu_freq_t* out_val) return true; } +bool rtc_clk_cpu_freq_from_mhz(int mhz, rtc_cpu_freq_t* out_val) +{ + return rtc_clk_cpu_freq_from_mhz_internal(mhz, out_val); +} + +void rtc_clk_cpu_freq_to_config(rtc_cpu_freq_t cpu_freq, rtc_cpu_freq_config_t* out_config) +{ + uint32_t source_freq_mhz; + rtc_cpu_freq_src_t source; + uint32_t freq_mhz; + uint32_t divider; + + switch (cpu_freq) { + case RTC_CPU_FREQ_XTAL: + case RTC_CPU_FREQ_2M: + source_freq_mhz = rtc_clk_xtal_freq_get(); + source = RTC_CPU_FREQ_SRC_XTAL; + if (cpu_freq == RTC_CPU_FREQ_2M) { + freq_mhz = 2; + divider = out_config->source_freq_mhz / 2; + } else { + freq_mhz = source_freq_mhz; + divider = 1; + } + break; + case RTC_CPU_FREQ_80M: + source = RTC_CPU_FREQ_SRC_PLL; + source_freq_mhz = RTC_PLL_FREQ_320M; + divider = 4; + freq_mhz = 80; + break; + case RTC_CPU_FREQ_160M: + source = RTC_CPU_FREQ_SRC_PLL; + source_freq_mhz = RTC_PLL_FREQ_320M; + divider = 2; + freq_mhz = 160; + break; + case RTC_CPU_FREQ_240M: + source = RTC_CPU_FREQ_SRC_PLL; + source_freq_mhz = RTC_PLL_FREQ_480M; + divider = 2; + freq_mhz = 240; + break; + default: + assert(false && "invalid rtc_cpu_freq_t value"); + abort(); + } + + *out_config = (rtc_cpu_freq_config_t) { + .source = source, + .source_freq_mhz = source_freq_mhz, + .div = divider, + .freq_mhz = freq_mhz + }; +} + +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; + + 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) { + SOC_LOGW(TAG, "can't find divider to generate %d MHz from %d MHz XTAL", + freq_mhz, xtal_freq); + return false; + } + + 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_320M; + divider = 4; + } else if (freq_mhz == 160) { + real_freq_mhz = freq_mhz; + source = RTC_CPU_FREQ_SRC_PLL; + source_freq_mhz = RTC_PLL_FREQ_320M; + divider = 2; + } 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 { + SOC_LOGW(TAG, "unsupported frequency: %d", freq_mhz); + 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; +} + +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(RTC_CNTL_CLK_CONF_REG, RTC_CNTL_SOC_CLK_SEL); + if (soc_clk_sel != RTC_CNTL_SOC_CLK_SEL_XTL) { + rtc_clk_cpu_freq_to_xtal(xtal_freq, 1); + rtc_clk_wait_for_slow_cycle(); + } + if (soc_clk_sel == RTC_CNTL_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_wait_for_slow_cycle(); + 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(RTC_CNTL_CLK_CONF_REG, RTC_CNTL_SOC_CLK_SEL); + switch (soc_clk_sel) { + case RTC_CNTL_SOC_CLK_SEL_XTL: { + source = RTC_CPU_FREQ_SRC_XTAL; + div = REG_GET_FIELD(APB_CTRL_SYSCLK_CONF_REG, APB_CTRL_PRE_DIV_CNT) + 1; + source_freq_mhz = (uint32_t) rtc_clk_xtal_freq_get(); + freq_mhz = source_freq_mhz / div; + } + break; + case RTC_CNTL_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); + if (cpuperiod_sel == 0) { + source_freq_mhz = RTC_PLL_FREQ_320M; + div = 4; + freq_mhz = 80; + } else if (cpuperiod_sel == 1) { + source_freq_mhz = RTC_PLL_FREQ_320M; + div = 2; + freq_mhz = 160; + } else if (cpuperiod_sel == 2) { + source_freq_mhz = RTC_PLL_FREQ_480M; + div = 2; + freq_mhz = 240; + } else { + assert(false && "unsupported frequency configuration"); + } + break; + } + case RTC_CNTL_SOC_CLK_SEL_8M: + source = RTC_CPU_FREQ_SRC_8M; + source_freq_mhz = 8; + div = 1; + freq_mhz = source_freq_mhz; + break; + case RTC_CNTL_SOC_CLK_SEL_APLL: + default: + assert(false && "unsupported frequency configuration"); + } + *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); + } +} + /* 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. @@ -669,8 +814,8 @@ uint32_t rtc_clk_apb_freq_get() void rtc_clk_init(rtc_clk_config_t cfg) { - rtc_cpu_freq_t cpu_source_before = rtc_clk_cpu_freq_get(); - + rtc_cpu_freq_config_t old_config, new_config; + /* 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 @@ -681,7 +826,11 @@ void rtc_clk_init(rtc_clk_config_t cfg) * run the following code than querying the PLL does. */ if (REG_GET_FIELD(RTC_CNTL_CLK_CONF_REG, RTC_CNTL_SOC_CLK_SEL) == RTC_CNTL_SOC_CLK_SEL_PLL) { - rtc_clk_cpu_freq_set(RTC_CPU_FREQ_XTAL); + /* We don't know actual XTAL frequency yet, assume 40MHz. + * REF_TICK divider will be corrected below, one XTAL frequency is + * determined. + */ + rtc_clk_cpu_freq_to_xtal(40, 1); } /* Set tuning parameters for 8M and 150k clocks. @@ -721,6 +870,7 @@ void rtc_clk_init(rtc_clk_config_t cfg) * frequency is different. If autodetection failed, worst case we get a * bit of garbage output. */ + rtc_xtal_freq_t est_xtal_freq = rtc_clk_xtal_freq_estimate(); if (est_xtal_freq != xtal_freq) { SOC_LOGW(TAG, "Possibly invalid CONFIG_ESP32_XTAL_FREQ setting (%dMHz). Detected %d MHz.", @@ -730,13 +880,21 @@ void rtc_clk_init(rtc_clk_config_t cfg) 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); + + 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); + assert(res && "invalid CPU frequency value"); + /* Configure REF_TICK */ + REG_WRITE(APB_CTRL_XTAL_TICK_CONF_REG, xtal_freq - 1); + REG_WRITE(APB_CTRL_PLL_TICK_CONF_REG, APB_CLK_FREQ / MHZ - 1); /* Under PLL, APB frequency is always 80MHz */ + /* 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 ); + XTHAL_SET_CCOUNT( XTHAL_GET_CCOUNT() * cfg.cpu_freq_mhz / freq_before ); /* Slow & fast clocks setup */ if (cfg.slow_freq == RTC_SLOW_FREQ_32K_XTAL) { From bec70ce2986b79a28ed1269760fa79da9603c90b Mon Sep 17 00:00:00 2001 From: Ivan Grokhotkov Date: Sun, 29 Jul 2018 10:50:49 +0300 Subject: [PATCH 3/9] esp32: use new CPU frequency setting API --- components/esp32/clk.c | 30 ++--- components/esp32/include/esp32/pm.h | 6 +- components/esp32/panic.c | 2 +- components/esp32/pm_esp32.c | 148 +++++++++++-------------- components/esp32/sleep_modes.c | 7 +- components/esp32/system_api.c | 2 +- components/soc/esp32/include/soc/rtc.h | 4 + 7 files changed, 90 insertions(+), 109 deletions(-) diff --git a/components/esp32/clk.c b/components/esp32/clk.c index 9b0097774..bb348fd67 100644 --- a/components/esp32/clk.c +++ b/components/esp32/clk.c @@ -77,34 +77,22 @@ void esp_clk_init(void) select_rtc_slow_clk(RTC_SLOW_FREQ_RTC); #endif - uint32_t freq_mhz = CONFIG_ESP32_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; - default: - freq_mhz = 80; - /* falls through */ - case 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_ESP32_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_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_ESP32_DEFAULT_CPU_FREQ_MHZ; - XTHAL_SET_CCOUNT( XTHAL_GET_CCOUNT() * freq_after / freq_before ); + XTHAL_SET_CCOUNT( XTHAL_GET_CCOUNT() * new_freq_mhz / old_freq_mhz ); } int IRAM_ATTR esp_clk_cpu_freq(void) diff --git a/components/esp32/include/esp32/pm.h b/components/esp32/include/esp32/pm.h index a7cbf0eac..f64045fb3 100644 --- a/components/esp32/include/esp32/pm.h +++ b/components/esp32/include/esp32/pm.h @@ -31,8 +31,10 @@ extern "C" { * Pass a pointer to this structure as an argument to esp_pm_configure function. */ typedef struct { - rtc_cpu_freq_t max_cpu_freq; /*!< Maximum CPU frequency to use */ - rtc_cpu_freq_t min_cpu_freq; /*!< Minimum CPU frequency to use when no frequency locks are taken */ + rtc_cpu_freq_t max_cpu_freq __attribute__((deprecated)); /*!< Maximum CPU frequency to use. Deprecated, use max_freq_mhz instead. */ + int max_freq_mhz; /*!< Maximum CPU frequency, in MHz */ + rtc_cpu_freq_t min_cpu_freq __attribute__((deprecated)); /*!< Minimum CPU frequency to use when no frequency locks are taken. Deprecated, use min_freq_mhz instead. */ + int min_freq_mhz; /*!< Minimum CPU frequency to use when no locks are taken, in MHz */ bool light_sleep_enable; /*!< Enter light sleep when no locks are taken */ } esp_pm_config_esp32_t; diff --git a/components/esp32/panic.c b/components/esp32/panic.c index 5a000574b..bbb359c9f 100644 --- a/components/esp32/panic.c +++ b/components/esp32/panic.c @@ -382,7 +382,7 @@ static void esp_panic_dig_reset() // make sure all the panic handler output is sent from UART FIFO uart_tx_wait_idle(CONFIG_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/esp32/pm_esp32.c b/components/esp32/pm_esp32.c index f75b3fe81..5d77dd8db 100644 --- a/components/esp32/pm_esp32.c +++ b/components/esp32/pm_esp32.c @@ -51,6 +51,11 @@ */ #define LIGHT_SLEEP_EARLY_WAKEUP_US 100 +/* Minimal divider at which REF_CLK_FREQ can be obtained */ +#define REF_CLK_DIV_MIN 10 + +#define MHZ 1000000 + #ifdef CONFIG_PM_PROFILING #define WITH_PROFILING #endif @@ -80,44 +85,20 @@ static uint32_t s_ccount_mul; */ static volatile bool s_need_update_ccompare[portNUM_PROCESSORS]; -/* When no RTOS tasks are active, these locks are released to allow going into - * a lower power mode. Used by ISR hook and idle hook. - */ -static esp_pm_lock_handle_t s_rtos_lock_handle[portNUM_PROCESSORS]; - /* A flag indicating that Idle hook has run on a given CPU; * Next interrupt on the same CPU will take s_rtos_lock_handle. */ static bool s_core_idle[portNUM_PROCESSORS]; -/* g_ticks_us defined in ROM for PRO CPU */ -extern uint32_t g_ticks_per_us_pro; +/* When no RTOS tasks are active, these locks are released to allow going into + * a lower power mode. Used by ISR hook and idle hook. + */ +static esp_pm_lock_handle_t s_rtos_lock_handle[portNUM_PROCESSORS]; -/* Lookup table of CPU frequencies to be used in each mode. +/* Lookup table of CPU frequency configs to be used in each mode. * Initialized by esp_pm_impl_init and modified by esp_pm_configure. */ -rtc_cpu_freq_t s_cpu_freq_by_mode[PM_MODE_COUNT]; - -/* Lookup table of CPU ticks per microsecond for each RTC_CPU_FREQ_ value. - * Essentially the same as returned by rtc_clk_cpu_freq_value(), but without - * the function call. Not const because XTAL frequency is only known at run time. - */ -static uint32_t s_cpu_freq_to_ticks[] = { - [RTC_CPU_FREQ_XTAL] = 0, /* This is set by esp_pm_impl_init */ - [RTC_CPU_FREQ_80M] = 80, - [RTC_CPU_FREQ_160M] = 160, - [RTC_CPU_FREQ_240M] = 240, - [RTC_CPU_FREQ_2M] = 2 -}; - -/* Lookup table of names for each RTC_CPU_FREQ_ value. Used for logging only. */ -static const char* s_freq_names[] __attribute__((unused)) = { - [RTC_CPU_FREQ_XTAL] = "XTAL", - [RTC_CPU_FREQ_80M] = "80", - [RTC_CPU_FREQ_160M] = "160", - [RTC_CPU_FREQ_240M] = "240", - [RTC_CPU_FREQ_2M] = "2" -}; +rtc_cpu_freq_config_t s_cpu_freq_by_mode[PM_MODE_COUNT]; /* Whether automatic light sleep is enabled */ static bool s_light_sleep_en = false; @@ -167,21 +148,6 @@ pm_mode_t esp_pm_impl_get_mode(esp_pm_lock_type_t type, int arg) } } -/* rtc_cpu_freq_t enum is not ordered by frequency, so convert to MHz, - * figure out the maximum value, then convert back to rtc_cpu_freq_t. - */ -static rtc_cpu_freq_t max_freq_of(rtc_cpu_freq_t f1, rtc_cpu_freq_t f2) -{ - int f1_hz = rtc_clk_cpu_freq_value(f1); - int f2_hz = rtc_clk_cpu_freq_value(f2); - int f_max_hz = MAX(f1_hz, f2_hz); - rtc_cpu_freq_t result = RTC_CPU_FREQ_XTAL; - if (!rtc_clk_cpu_freq_from_mhz(f_max_hz/1000000, &result)) { - assert(false && "unsupported frequency"); - } - return result; -} - esp_err_t esp_pm_configure(const void* vconfig) { #ifndef CONFIG_PM_ENABLE @@ -195,46 +161,66 @@ esp_err_t esp_pm_configure(const void* vconfig) } #endif - if (config->min_cpu_freq == RTC_CPU_FREQ_2M) { - /* Minimal APB frequency to achieve 1MHz REF_TICK frequency is 5 MHz */ - return ESP_ERR_NOT_SUPPORTED; + int min_freq_mhz = config->min_freq_mhz; + int max_freq_mhz = config->max_freq_mhz; + + if (min_freq_mhz == 0 && max_freq_mhz == 0) { + /* For compatibility, handle deprecated fields, min_cpu_freq and max_cpu_freq. */ +#pragma GCC diagnostic push +#pragma GCC diagnostic ignored "-Wdeprecated-declarations" + min_freq_mhz = rtc_clk_cpu_freq_value(config->min_cpu_freq) / MHZ; + max_freq_mhz = rtc_clk_cpu_freq_value(config->max_cpu_freq) / MHZ; +#pragma GCC diagnostic pop } - rtc_cpu_freq_t min_freq = config->min_cpu_freq; - rtc_cpu_freq_t max_freq = config->max_cpu_freq; - int min_freq_mhz = rtc_clk_cpu_freq_value(min_freq); - int max_freq_mhz = rtc_clk_cpu_freq_value(max_freq); if (min_freq_mhz > max_freq_mhz) { return ESP_ERR_INVALID_ARG; } - rtc_cpu_freq_t apb_max_freq = max_freq; /* CPU frequency in APB_MAX mode */ - if (max_freq == RTC_CPU_FREQ_240M) { + rtc_cpu_freq_config_t freq_config; + if (!rtc_clk_cpu_freq_mhz_to_config(min_freq_mhz, &freq_config)) { + ESP_LOGW(TAG, "invalid min_freq_mhz value (%d)", min_freq_mhz); + return ESP_ERR_INVALID_ARG; + } + + int xtal_freq_mhz = (int) rtc_clk_xtal_freq_get(); + if (min_freq_mhz < xtal_freq_mhz && min_freq_mhz * MHZ / REF_CLK_FREQ < REF_CLK_DIV_MIN) { + ESP_LOGW(TAG, "min_freq_mhz should be >= %d", REF_CLK_FREQ * REF_CLK_DIV_MIN / MHZ); + return ESP_ERR_INVALID_ARG; + } + + if (!rtc_clk_cpu_freq_mhz_to_config(max_freq_mhz, &freq_config)) { + ESP_LOGW(TAG, "invalid max_freq_mhz value (%d)", max_freq_mhz); + return ESP_ERR_INVALID_ARG; + } + + int apb_max_freq = max_freq_mhz; /* CPU frequency in APB_MAX mode */ + if (max_freq_mhz == 240) { /* We can't switch between 240 and 80/160 without disabling PLL, * so use 240MHz CPU frequency when 80MHz APB frequency is requested. */ - apb_max_freq = RTC_CPU_FREQ_240M; - } else if (max_freq == RTC_CPU_FREQ_160M || max_freq == RTC_CPU_FREQ_80M) { + apb_max_freq = 240; + } else if (max_freq_mhz == 160 || max_freq_mhz == 80) { /* Otherwise, can use 80MHz * CPU frequency when 80MHz APB frequency is requested. */ - apb_max_freq = RTC_CPU_FREQ_80M; + apb_max_freq = 80; } - apb_max_freq = max_freq_of(apb_max_freq, min_freq); + apb_max_freq = MAX(apb_max_freq, min_freq_mhz); ESP_LOGI(TAG, "Frequency switching config: " - "CPU_MAX: %s, APB_MAX: %s, APB_MIN: %s, Light sleep: %s", - s_freq_names[max_freq], - s_freq_names[apb_max_freq], - s_freq_names[min_freq], + "CPU_MAX: %d, APB_MAX: %d, APB_MIN: %d, Light sleep: %s", + max_freq_mhz, + apb_max_freq, + min_freq_mhz, config->light_sleep_enable ? "ENABLED" : "DISABLED"); portENTER_CRITICAL(&s_switch_lock); - s_cpu_freq_by_mode[PM_MODE_CPU_MAX] = max_freq; - s_cpu_freq_by_mode[PM_MODE_APB_MAX] = apb_max_freq; - s_cpu_freq_by_mode[PM_MODE_APB_MIN] = min_freq; - s_cpu_freq_by_mode[PM_MODE_LIGHT_SLEEP] = min_freq; + rtc_clk_cpu_freq_mhz_to_config(max_freq_mhz, &s_cpu_freq_by_mode[PM_MODE_CPU_MAX]); + rtc_clk_cpu_freq_mhz_to_config(apb_max_freq, &s_cpu_freq_by_mode[PM_MODE_APB_MAX]); + rtc_clk_cpu_freq_mhz_to_config(min_freq_mhz, &s_cpu_freq_by_mode[PM_MODE_APB_MIN]); + s_cpu_freq_by_mode[PM_MODE_LIGHT_SLEEP] = s_cpu_freq_by_mode[PM_MODE_APB_MIN]; s_light_sleep_en = config->light_sleep_enable; s_config_changed = true; portEXIT_CRITICAL(&s_switch_lock); @@ -310,7 +296,7 @@ static void IRAM_ATTR on_freq_update(uint32_t old_ticks_per_us, uint32_t ticks_p } /* Calculate new tick divisor */ - _xt_tick_divisor = ticks_per_us * 1000000 / XT_TICK_PER_SEC; + _xt_tick_divisor = ticks_per_us * MHZ / XT_TICK_PER_SEC; int core_id = xPortGetCoreID(); if (s_rtos_lock_handle[core_id] != NULL) { @@ -375,17 +361,18 @@ static void IRAM_ATTR do_switch(pm_mode_t new_mode) s_config_changed = false; portEXIT_CRITICAL_ISR(&s_switch_lock); - rtc_cpu_freq_t new_freq = s_cpu_freq_by_mode[new_mode]; - rtc_cpu_freq_t old_freq; + rtc_cpu_freq_config_t new_config = s_cpu_freq_by_mode[new_mode]; + rtc_cpu_freq_config_t old_config; + if (!config_changed) { - old_freq = s_cpu_freq_by_mode[s_mode]; + old_config = s_cpu_freq_by_mode[s_mode]; } else { - old_freq = rtc_clk_cpu_freq_get(); + rtc_clk_cpu_freq_get_config(&old_config); } - if (new_freq != old_freq) { - uint32_t old_ticks_per_us = g_ticks_per_us_pro; - uint32_t new_ticks_per_us = s_cpu_freq_to_ticks[new_freq]; + if (new_config.freq_mhz != old_config.freq_mhz) { + uint32_t old_ticks_per_us = old_config.freq_mhz; + uint32_t new_ticks_per_us = new_config.freq_mhz; bool switch_down = new_ticks_per_us < old_ticks_per_us; @@ -393,7 +380,7 @@ static void IRAM_ATTR do_switch(pm_mode_t new_mode) if (switch_down) { on_freq_update(old_ticks_per_us, new_ticks_per_us); } - rtc_clk_cpu_freq_set_fast(new_freq); + rtc_clk_cpu_freq_set_config_fast(&new_config); if (!switch_down) { on_freq_update(old_ticks_per_us, new_ticks_per_us); } @@ -536,9 +523,9 @@ void esp_pm_impl_dump_stats(FILE* out) /* don't display light sleep mode if it's not enabled */ continue; } - fprintf(out, "%8s %6s %12lld %2d%%\n", + fprintf(out, "%8s %3dM %12lld %2d%%\n", s_mode_names[i], - s_freq_names[s_cpu_freq_by_mode[i]], + s_cpu_freq_by_mode[i].freq_mhz, time_in_mode[i], (int) (time_in_mode[i] * 100 / now)); } @@ -547,7 +534,6 @@ void esp_pm_impl_dump_stats(FILE* out) void esp_pm_impl_init() { - s_cpu_freq_to_ticks[RTC_CPU_FREQ_XTAL] = rtc_clk_xtal_freq_get(); #ifdef CONFIG_PM_TRACE esp_pm_trace_init(); #endif @@ -563,11 +549,11 @@ void esp_pm_impl_init() /* Configure all modes to use the default CPU frequency. * This will be modified later by a call to esp_pm_configure. */ - rtc_cpu_freq_t default_freq; - if (!rtc_clk_cpu_freq_from_mhz(CONFIG_ESP32_DEFAULT_CPU_FREQ_MHZ, &default_freq)) { + rtc_cpu_freq_config_t default_config; + if (!rtc_clk_cpu_freq_mhz_to_config(CONFIG_ESP32_DEFAULT_CPU_FREQ_MHZ, &default_config)) { assert(false && "unsupported frequency"); } for (size_t i = 0; i < PM_MODE_COUNT; ++i) { - s_cpu_freq_by_mode[i] = default_freq; + s_cpu_freq_by_mode[i] = default_config; } } diff --git a/components/esp32/sleep_modes.c b/components/esp32/sleep_modes.c index 91713ad6b..2fda37a5d 100644 --- a/components/esp32/sleep_modes.c +++ b/components/esp32/sleep_modes.c @@ -178,8 +178,9 @@ 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); + 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) { @@ -205,7 +206,7 @@ static uint32_t IRAM_ATTR esp_sleep_start(uint32_t pd_flags) uint32_t result = rtc_sleep_start(s_config.wakeup_triggers, 0); // 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/esp32/system_api.c b/components/esp32/system_api.c index f56bf622e..1208de483 100644 --- a/components/esp32/system_api.c +++ b/components/esp32/system_api.c @@ -333,7 +333,7 @@ void IRAM_ATTR esp_restart_noos() 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(); // Clear entry point for APP CPU DPORT_REG_WRITE(DPORT_APPCPU_CTRL_D_REG, 0); diff --git a/components/soc/esp32/include/soc/rtc.h b/components/soc/esp32/include/soc/rtc.h index e7e1c1d14..b02d31eac 100644 --- a/components/soc/esp32/include/soc/rtc.h +++ b/components/soc/esp32/include/soc/rtc.h @@ -417,6 +417,10 @@ uint32_t rtc_clk_cpu_freq_value(rtc_cpu_freq_t cpu_freq) __attribute__((depreca * 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 */ void rtc_clk_cpu_freq_set_config_fast(const rtc_cpu_freq_config_t* config); From c722cf3e06a6a546abf94acd2c54ff2b3146fe1b Mon Sep 17 00:00:00 2001 From: Ivan Grokhotkov Date: Sun, 29 Jul 2018 10:51:02 +0300 Subject: [PATCH 4/9] bootloader: use new CPU frequency setting API --- components/bootloader_support/src/bootloader_clock.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/components/bootloader_support/src/bootloader_clock.c b/components/bootloader_support/src/bootloader_clock.c index 64ef9c233..d5a1e52ab 100644 --- a/components/bootloader_support/src/bootloader_clock.c +++ b/components/bootloader_support/src/bootloader_clock.c @@ -29,7 +29,7 @@ void bootloader_clock_configure() uart_tx_wait_idle(0); /* Set CPU to 80MHz. Keep other clocks unmodified. */ - rtc_cpu_freq_t cpu_freq = RTC_CPU_FREQ_80M; + int cpu_freq_mhz = 80; /* On ESP32 rev 0, switching to 80MHz if clock was previously set to * 240 MHz may cause the chip to lock up (see section 3.5 of the errata @@ -39,12 +39,12 @@ void bootloader_clock_configure() uint32_t chip_ver_reg = REG_READ(EFUSE_BLK0_RDATA3_REG); if ((chip_ver_reg & EFUSE_RD_CHIP_VER_REV1_M) == 0 && CONFIG_ESP32_DEFAULT_CPU_FREQ_MHZ == 240) { - cpu_freq = RTC_CPU_FREQ_240M; + cpu_freq_mhz = 240; } rtc_clk_config_t clk_cfg = RTC_CLK_CONFIG_DEFAULT(); clk_cfg.xtal_freq = CONFIG_ESP32_XTAL_FREQ; - clk_cfg.cpu_freq = cpu_freq; + 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); From bcf79e5cf23592c1453d910d5afb9bb119426bd7 Mon Sep 17 00:00:00 2001 From: Ivan Grokhotkov Date: Sun, 29 Jul 2018 10:51:19 +0300 Subject: [PATCH 5/9] tests: use new CPU frequency setting API --- components/esp32/test/test_dport.c | 64 +++++++++++++----------- components/esp32/test/test_pm.c | 19 ++++--- components/esp32/test/test_sleep.c | 9 ++-- components/soc/esp32/test/test_rtc_clk.c | 17 ++++--- 4 files changed, 62 insertions(+), 47 deletions(-) diff --git a/components/esp32/test/test_dport.c b/components/esp32/test/test_dport.c index 6b5960bf4..96fa0b109 100644 --- a/components/esp32/test/test_dport.c +++ b/components/esp32/test/test_dport.c @@ -1,11 +1,12 @@ - -#include #include #include +#include "esp_types.h" +#include "esp_clk.h" #include "freertos/FreeRTOS.h" #include "freertos/task.h" #include "freertos/semphr.h" +#include "freertos/xtensa_timer.h" #include "soc/cpu.h" #include "unity.h" #include "rom/uart.h" @@ -99,49 +100,52 @@ TEST_CASE("access DPORT and APB at same time", "[esp32]") { dport_test_result = false; apb_test_result = false; - printf("CPU_FREQ = %d MHz\n", rtc_clk_cpu_freq_value(rtc_clk_cpu_freq_get()) / MHZ); + printf("CPU_FREQ = %d MHz\n", esp_clk_cpu_freq()); run_tasks("accessDPORT", accessDPORT, "accessAPB", accessAPB, 10000); } -void run_tasks_with_change_freq_cpu (rtc_cpu_freq_t cpu_freq) +void run_tasks_with_change_freq_cpu(int cpu_freq_mhz) { - dport_test_result = false; - apb_test_result = false; - rtc_cpu_freq_t cur_freq = rtc_clk_cpu_freq_get(); - uint32_t freq_before_changed = rtc_clk_cpu_freq_value(cur_freq) / MHZ; - uint32_t freq_changed = freq_before_changed; - printf("CPU_FREQ = %d MHz\n", freq_before_changed); - - if (cur_freq != cpu_freq) { - uart_tx_wait_idle(CONFIG_CONSOLE_UART_NUM); - - rtc_clk_cpu_freq_set(cpu_freq); - - const int uart_num = CONFIG_CONSOLE_UART_NUM; - const int uart_baud = CONFIG_CONSOLE_UART_BAUDRATE; - uart_div_modify(uart_num, (rtc_clk_apb_freq_get() << 4) / uart_baud); - - freq_changed = rtc_clk_cpu_freq_value(rtc_clk_cpu_freq_get()) / MHZ; - printf("CPU_FREQ switching to %d MHz\n", freq_changed); - } - run_tasks("accessDPORT", accessDPORT, "accessAPB", accessAPB, 10000 / ((freq_before_changed <= freq_changed) ? 1 : (freq_before_changed / freq_changed))); - - // return old freq. - uart_tx_wait_idle(CONFIG_CONSOLE_UART_NUM); - rtc_clk_cpu_freq_set(cur_freq); const int uart_num = CONFIG_CONSOLE_UART_NUM; const int uart_baud = CONFIG_CONSOLE_UART_BAUDRATE; + dport_test_result = false; + apb_test_result = false; + rtc_cpu_freq_config_t old_config; + rtc_clk_cpu_freq_get_config(&old_config); + + printf("CPU_FREQ = %d MHz\n", old_config.freq_mhz); + + if (cpu_freq_mhz != old_config.freq_mhz) { + rtc_cpu_freq_config_t new_config; + bool res = rtc_clk_cpu_freq_mhz_to_config(cpu_freq_mhz, &new_config); + assert(res && "invalid frequency value"); + + uart_tx_wait_idle(uart_num); + rtc_clk_cpu_freq_set_config(&new_config); + uart_div_modify(uart_num, (rtc_clk_apb_freq_get() << 4) / uart_baud); + /* adjust RTOS ticks */ + _xt_tick_divisor = cpu_freq_mhz * 1000000 / XT_TICK_PER_SEC; + vTaskDelay(2); + + printf("CPU_FREQ switched to %d MHz\n", cpu_freq_mhz); + } + run_tasks("accessDPORT", accessDPORT, "accessAPB", accessAPB, 10000); + + // return old freq. + uart_tx_wait_idle(uart_num); + rtc_clk_cpu_freq_set_config(&old_config); uart_div_modify(uart_num, (rtc_clk_apb_freq_get() << 4) / uart_baud); + _xt_tick_divisor = old_config.freq_mhz * 1000000 / XT_TICK_PER_SEC; } TEST_CASE("access DPORT and APB at same time (Freq CPU and APB = 80 MHz)", "[esp32] [ignore]") { - run_tasks_with_change_freq_cpu(RTC_CPU_FREQ_80M); + run_tasks_with_change_freq_cpu(80); } TEST_CASE("access DPORT and APB at same time (Freq CPU and APB = 40 MHz (XTAL))", "[esp32]") { - run_tasks_with_change_freq_cpu(RTC_CPU_FREQ_XTAL); + run_tasks_with_change_freq_cpu((int) rtc_clk_xtal_freq_get()); } static uint32_t stall_other_cpu_counter; diff --git a/components/esp32/test/test_pm.c b/components/esp32/test/test_pm.c index fa432eddc..e34658b7e 100644 --- a/components/esp32/test/test_pm.c +++ b/components/esp32/test/test_pm.c @@ -2,6 +2,7 @@ #include #include #include +#include #include "unity.h" #include "esp_pm.h" #include "esp_clk.h" @@ -25,19 +26,17 @@ TEST_CASE("Can dump power management lock stats", "[pm]") static void switch_freq(int mhz) { - rtc_cpu_freq_t max_freq; - assert(rtc_clk_cpu_freq_from_mhz(mhz, &max_freq)); + int xtal_freq = rtc_clk_xtal_freq_get(); esp_pm_config_esp32_t pm_config = { - .max_cpu_freq = max_freq, - .min_cpu_freq = RTC_CPU_FREQ_XTAL, + .max_freq_mhz = mhz, + .min_freq_mhz = MIN(mhz, xtal_freq), }; ESP_ERROR_CHECK( esp_pm_configure(&pm_config) ); - printf("Waiting for frequency to be set to %d (%d MHz)...\n", max_freq, mhz); + printf("Waiting for frequency to be set to %d MHz...\n", mhz); while (esp_clk_cpu_freq() / 1000000 != mhz) { - vTaskDelay(pdMS_TO_TICKS(1000)); - printf("Frequency is %d MHz\n", esp_clk_cpu_freq()); + vTaskDelay(pdMS_TO_TICKS(200)); + printf("Frequency is %d MHz\n", esp_clk_cpu_freq() / 1000000); } - printf("Frequency is set to %d MHz\n", mhz); } TEST_CASE("Can switch frequency using esp_pm_configure", "[pm]") @@ -52,6 +51,10 @@ TEST_CASE("Can switch frequency using esp_pm_configure", "[pm]") switch_freq(240); switch_freq(40); switch_freq(80); + switch_freq(10); + switch_freq(80); + switch_freq(20); + switch_freq(40); switch_freq(orig_freq_mhz); } diff --git a/components/esp32/test/test_sleep.c b/components/esp32/test/test_sleep.c index abd5a2080..2212de3c4 100644 --- a/components/esp32/test/test_sleep.c +++ b/components/esp32/test/test_sleep.c @@ -177,13 +177,16 @@ TEST_CASE("light sleep and frequency switching", "[deepsleep]") uart_div_modify(CONFIG_CONSOLE_UART_NUM, (uart_clk_freq << 4) / CONFIG_CONSOLE_UART_BAUDRATE); #endif + rtc_cpu_freq_config_t config_xtal, config_default; + rtc_clk_cpu_freq_get_config(&config_default); + rtc_clk_cpu_freq_mhz_to_config((int) rtc_clk_xtal_freq_get(), &config_xtal); + esp_sleep_enable_timer_wakeup(1000); - rtc_cpu_freq_t default_freq = rtc_clk_cpu_freq_get(); for (int i = 0; i < 1000; ++i) { if (i % 2 == 0) { - rtc_clk_cpu_freq_set_fast(RTC_CPU_FREQ_XTAL); + rtc_clk_cpu_freq_set_config_fast(&config_xtal); } else { - rtc_clk_cpu_freq_set_fast(default_freq); + rtc_clk_cpu_freq_set_config_fast(&config_default); } printf("%d\n", i); fflush(stdout); diff --git a/components/soc/esp32/test/test_rtc_clk.c b/components/soc/esp32/test/test_rtc_clk.c index 39c3ac7fd..ee05a302b 100644 --- a/components/soc/esp32/test/test_rtc_clk.c +++ b/components/soc/esp32/test/test_rtc_clk.c @@ -95,7 +95,7 @@ TEST_CASE("Output 8M XTAL clock to GPIO25", "[rtc_clk][ignore]") pull_out_clk(RTC_IO_DEBUG_SEL0_8M); } -static void test_clock_switching(void (*switch_func)(rtc_cpu_freq_t)) +static void test_clock_switching(void (*switch_func)(const rtc_cpu_freq_config_t* config)) { uart_tx_wait_idle(CONFIG_CONSOLE_UART_NUM); @@ -103,11 +103,16 @@ static void test_clock_switching(void (*switch_func)(rtc_cpu_freq_t)) ref_clock_init(); uint64_t t_start = ref_clock_get(); - rtc_cpu_freq_t cur_freq = rtc_clk_cpu_freq_get(); + rtc_cpu_freq_config_t cur_config; + rtc_clk_cpu_freq_get_config(&cur_config); + + rtc_cpu_freq_config_t xtal_config; + rtc_clk_cpu_freq_mhz_to_config((uint32_t) rtc_clk_xtal_freq_get(), &xtal_config); + int count = 0; while (ref_clock_get() - t_start < test_duration_sec * 1000000) { - switch_func(RTC_CPU_FREQ_XTAL); - switch_func(cur_freq); + switch_func(&xtal_config); + switch_func(&cur_config); ++count; } uint64_t t_end = ref_clock_get(); @@ -126,12 +131,12 @@ TEST_CASE("Calculate 8M clock frequency", "[rtc_clk]") TEST_CASE("Test switching between PLL and XTAL", "[rtc_clk]") { - test_clock_switching(rtc_clk_cpu_freq_set); + test_clock_switching(rtc_clk_cpu_freq_set_config); } TEST_CASE("Test fast switching between PLL and XTAL", "[rtc_clk]") { - test_clock_switching(rtc_clk_cpu_freq_set_fast); + test_clock_switching(rtc_clk_cpu_freq_set_config_fast); } #define COUNT_TEST 3 From 3cec686803663b1ca8c3d5cdbd84e08d6569a5b9 Mon Sep 17 00:00:00 2001 From: Ivan Grokhotkov Date: Thu, 26 Jul 2018 02:02:31 +0300 Subject: [PATCH 6/9] examples/power_save: set CPU frequencies in sdkconfig --- .../wifi/power_save/main/Kconfig.projbuild | 109 +++++++++++++----- examples/wifi/power_save/main/power_save.c | 11 +- 2 files changed, 86 insertions(+), 34 deletions(-) diff --git a/examples/wifi/power_save/main/Kconfig.projbuild b/examples/wifi/power_save/main/Kconfig.projbuild index 974474d33..ea2b0aa26 100644 --- a/examples/wifi/power_save/main/Kconfig.projbuild +++ b/examples/wifi/power_save/main/Kconfig.projbuild @@ -1,43 +1,96 @@ menu "Example Configuration" config WIFI_SSID - string "WiFi SSID" - default "myssid" - help - SSID (network name) for the example to connect to. + string "WiFi SSID" + default "myssid" + help + SSID (network name) for the example to connect to. config WIFI_PASSWORD - string "WiFi Password" - default "mypassword" - help - WiFi password (WPA or WPA2) for the example to use. - + string "WiFi Password" + default "mypassword" + help + WiFi password (WPA or WPA2) for the example to use. + config WIFI_LISTEN_INTERVAL - int "WiFi listen interval" - default 3 - help - Interval for station to listen to beacon from AP. The unit of listen interval is one beacon interval. - For example, if beacon interval is 100 ms and listen interval is 3, the interval for station to listen - to beacon is 300 ms. + int "WiFi listen interval" + default 3 + help + Interval for station to listen to beacon from AP. The unit of listen interval is one beacon interval. + For example, if beacon interval is 100 ms and listen interval is 3, the interval for station to listen + to beacon is 300 ms. choice POWER_SAVE_MODE - prompt "power save mode" - default POWER_SAVE_MIN_MODEM - help - Power save mode for the esp32 to use. Modem sleep mode includes minimum and maximum power save modes. - In minimum power save mode, station wakes up every DTIM to receive beacon. Broadcast data will not be - lost because it is transmitted after DTIM. However, it can not save much more power if DTIM is short - for DTIM is determined by AP. - In maximum power save mode, station wakes up every listen interval to receive beacon. Broadcast data - may be lost because station may be in sleep state at DTIM time. If listen interval is longer, more power - is saved but broadcast data is more easy to lose. + prompt "power save mode" + default POWER_SAVE_MIN_MODEM + help + Power save mode for the esp32 to use. Modem sleep mode includes minimum and maximum power save modes. + In minimum power save mode, station wakes up every DTIM to receive beacon. Broadcast data will not be + lost because it is transmitted after DTIM. However, it can not save much more power if DTIM is short + for DTIM is determined by AP. + In maximum power save mode, station wakes up every listen interval to receive beacon. Broadcast data + may be lost because station may be in sleep state at DTIM time. If listen interval is longer, more power + is saved but broadcast data is more easy to lose. config POWER_SAVE_NONE - bool "none" + bool "none" config POWER_SAVE_MIN_MODEM - bool "minimum modem" + bool "minimum modem" config POWER_SAVE_MAX_MODEM - bool "maximum modem" + bool "maximum modem" endchoice +choice EXAMPLE_MAX_CPU_FREQ + prompt "Maximum CPU frequency" + default EXAMPLE_MAX_CPU_FREQ_80 + help + Maximum CPU frequency to use for dynamic frequency scaling. + +config EXAMPLE_MAX_CPU_FREQ_80 + bool "80 MHz" +config EXAMPLE_MAX_CPU_FREQ_160 + bool "160 MHz" +config EXAMPLE_MAX_CPU_FREQ_240 + bool "240 MHz" +endchoice + +config EXAMPLE_MAX_CPU_FREQ_MHZ + int + default 80 if EXAMPLE_MAX_CPU_FREQ_80 + default 160 if EXAMPLE_MAX_CPU_FREQ_160 + default 240 if EXAMPLE_MAX_CPU_FREQ_240 + + +choice EXAMPLE_MIN_CPU_FREQ + prompt "Minimum CPU frequency" + default EXAMPLE_MIN_CPU_FREQ_10M + help + Minimum CPU frequency to use for dynamic frequency scaling. + Should be set to XTAL frequency or XTAL frequency divided by integer. + +config EXAMPLE_MIN_CPU_FREQ_40M + bool "40 MHz (use with 40MHz XTAL)" + depends on ESP32_XTAL_FREQ_40 || ESP32_XTAL_FREQ_AUTO +config EXAMPLE_MIN_CPU_FREQ_20M + bool "20 MHz (use with 40MHz XTAL)" + depends on ESP32_XTAL_FREQ_40 || ESP32_XTAL_FREQ_AUTO +config EXAMPLE_MIN_CPU_FREQ_10M + bool "10 MHz (use with 40MHz XTAL)" + depends on ESP32_XTAL_FREQ_40 || ESP32_XTAL_FREQ_AUTO +config EXAMPLE_MIN_CPU_FREQ_26M + bool "26 MHz (use with 26MHz XTAL)" + depends on ESP32_XTAL_FREQ_26 || ESP32_XTAL_FREQ_AUTO +config EXAMPLE_MIN_CPU_FREQ_13M + bool "13 MHz (use with 26MHz XTAL)" + depends on ESP32_XTAL_FREQ_26 || ESP32_XTAL_FREQ_AUTO +endchoice + +config EXAMPLE_MIN_CPU_FREQ_MHZ + int + default 40 if EXAMPLE_MIN_CPU_FREQ_40M + default 20 if EXAMPLE_MIN_CPU_FREQ_20M + default 10 if EXAMPLE_MIN_CPU_FREQ_10M + default 26 if EXAMPLE_MIN_CPU_FREQ_26M + default 13 if EXAMPLE_MIN_CPU_FREQ_13M + endmenu diff --git a/examples/wifi/power_save/main/power_save.c b/examples/wifi/power_save/main/power_save.c index d2e60fdfd..c9576a9aa 100644 --- a/examples/wifi/power_save/main/power_save.c +++ b/examples/wifi/power_save/main/power_save.c @@ -96,13 +96,12 @@ void app_main() ESP_ERROR_CHECK( ret ); #if CONFIG_PM_ENABLE - // Configure dynamic frequency scaling: maximum frequency is set in sdkconfig, - // minimum frequency is XTAL. - rtc_cpu_freq_t max_freq; - rtc_clk_cpu_freq_from_mhz(CONFIG_ESP32_DEFAULT_CPU_FREQ_MHZ, &max_freq); + // Configure dynamic frequency scaling: + // maximum and minimum frequencies are set in sdkconfig, + // automatic light sleep is enabled if tickless idle support is enabled. esp_pm_config_esp32_t pm_config = { - .max_cpu_freq = max_freq, - .min_cpu_freq = RTC_CPU_FREQ_XTAL, + .max_freq_mhz = CONFIG_EXAMPLE_MAX_CPU_FREQ_MHZ, + .min_freq_mhz = CONFIG_EXAMPLE_MIN_CPU_FREQ_MHZ, #if CONFIG_FREERTOS_USE_TICKLESS_IDLE .light_sleep_enable = true #endif From db2f0f45becb7ce79c17d27c6ae962b9b5ab576f Mon Sep 17 00:00:00 2001 From: Ivan Grokhotkov Date: Sun, 29 Jul 2018 08:24:20 +0300 Subject: [PATCH 7/9] soc/rtc_clk: split rtc_clk_init into separate object file MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit rtc_clk_init and related functions don’t need to be in IRAM/DRAM. --- components/soc/esp32/rtc_clk.c | 176 +------------------------- components/soc/esp32/rtc_clk_common.h | 38 ++++++ components/soc/esp32/rtc_clk_init.c | 176 ++++++++++++++++++++++++++ 3 files changed, 218 insertions(+), 172 deletions(-) create mode 100644 components/soc/esp32/rtc_clk_common.h create mode 100644 components/soc/esp32/rtc_clk_init.c diff --git a/components/soc/esp32/rtc_clk.c b/components/soc/esp32/rtc_clk.c index ea899db3f..38737fbaa 100644 --- a/components/soc/esp32/rtc_clk.c +++ b/components/soc/esp32/rtc_clk.c @@ -32,9 +32,7 @@ #include "soc_log.h" #include "sdkconfig.h" #include "xtensa/core-macros.h" - - -#define MHZ (1000000) +#include "rtc_clk_common.h" /* Frequency of the 8M oscillator is 8.5MHz +/- 5%, at the default DCAP setting */ #define RTC_FAST_CLK_FREQ_8M 8500000 @@ -42,12 +40,6 @@ #define RTC_SLOW_CLK_FREQ_8MD256 (RTC_FAST_CLK_FREQ_8M / 256) #define RTC_SLOW_CLK_FREQ_32K 32768 -static const char* TAG = "rtc_clk"; - -/* Various constants related to the analog internals of the chip. - * Defined here because they don't have any use outside of this file. - */ - #define BBPLL_ENDIV5_VAL_320M 0x43 #define BBPLL_BBADC_DSMP_VAL_320M 0x84 #define BBPLL_ENDIV5_VAL_480M 0xc3 @@ -81,11 +73,6 @@ static const char* TAG = "rtc_clk"; #define DELAY_SLOW_CLK_SWITCH 300 #define DELAY_8M_ENABLE 50 -/* Number of 8M/256 clock cycles to use for XTAL frequency estimation. - * 10 cycles will take approximately 300 microseconds. - */ -#define XTAL_FREQ_EST_CYCLES 10 - /* Core voltage needs to be increased in two cases: * 1. running at 240 MHz * 2. running with 80MHz Flash frequency @@ -102,8 +89,6 @@ static const char* TAG = "rtc_clk"; #define RTC_PLL_FREQ_320M 320 #define RTC_PLL_FREQ_480M 480 - -static void rtc_clk_cpu_freq_to_xtal(int freq, int div); static void rtc_clk_cpu_freq_to_8m(); static void rtc_clk_bbpll_disable(); static void rtc_clk_bbpll_enable(); @@ -385,7 +370,7 @@ void rtc_clk_bbpll_configure(rtc_xtal_freq_t xtal_freq, int pll_freq) /** * Switch to XTAL frequency. Does not disable the PLL. */ -static void rtc_clk_cpu_freq_to_xtal(int freq, int div) +void rtc_clk_cpu_freq_to_xtal(int freq, int div) { ets_update_cpu_frequency(freq); /* set divider from XTAL to APB clock */ @@ -601,8 +586,7 @@ bool rtc_clk_cpu_freq_mhz_to_config(uint32_t freq_mhz, rtc_cpu_freq_config_t* ou divider = xtal_freq / freq_mhz; real_freq_mhz = (xtal_freq + divider / 2) / divider; /* round */ if (real_freq_mhz != freq_mhz) { - SOC_LOGW(TAG, "can't find divider to generate %d MHz from %d MHz XTAL", - freq_mhz, xtal_freq); + // no suitable divider return false; } @@ -624,7 +608,7 @@ bool rtc_clk_cpu_freq_mhz_to_config(uint32_t freq_mhz, rtc_cpu_freq_config_t* ou source_freq_mhz = RTC_PLL_FREQ_480M; divider = 2; } else { - SOC_LOGW(TAG, "unsupported frequency: %d", freq_mhz); + // unsupported frequency return false; } *out_config = (rtc_cpu_freq_config_t) { @@ -727,30 +711,11 @@ void rtc_clk_cpu_freq_set_config_fast(const rtc_cpu_freq_config_t* config) } } -/* 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 bool clk_val_is_valid(uint32_t val) { - return (val & 0xffff) == ((val >> 16) & 0xffff) && - val != 0 && - val != UINT32_MAX; -} - -static uint32_t reg_val_to_clk_val(uint32_t val) { - return val & UINT16_MAX; -} - -static uint32_t clk_val_to_reg_val(uint32_t val) { - return (val & UINT16_MAX) | ((val & UINT16_MAX) << 16); -} - rtc_xtal_freq_t rtc_clk_xtal_freq_get() { /* We may have already written XTAL value into RTC_XTAL_FREQ_REG */ uint32_t xtal_freq_reg = READ_PERI_REG(RTC_XTAL_FREQ_REG); if (!clk_val_is_valid(xtal_freq_reg)) { - SOC_LOGW(TAG, "invalid RTC_XTAL_FREQ_REG value: 0x%08x", xtal_freq_reg); return RTC_XTAL_FREQ_AUTO; } return reg_val_to_clk_val(xtal_freq_reg); @@ -761,42 +726,6 @@ void rtc_clk_xtal_freq_update(rtc_xtal_freq_t xtal_freq) WRITE_PERI_REG(RTC_XTAL_FREQ_REG, clk_val_to_reg_val(xtal_freq)); } -static rtc_xtal_freq_t rtc_clk_xtal_freq_estimate() -{ - /* Enable 8M/256 clock if needed */ - const bool clk_8m_enabled = rtc_clk_8m_enabled(); - const bool clk_8md256_enabled = rtc_clk_8md256_enabled(); - if (!clk_8md256_enabled) { - rtc_clk_8m_enable(true, true); - } - - uint64_t cal_val = rtc_clk_cal_ratio(RTC_CAL_8MD256, XTAL_FREQ_EST_CYCLES); - /* cal_val contains period of 8M/256 clock in XTAL clock cycles - * (shifted by RTC_CLK_CAL_FRACT bits). - * Xtal frequency will be (cal_val * 8M / 256) / 2^19 - */ - uint32_t freq_mhz = (cal_val * RTC_FAST_CLK_FREQ_APPROX / MHZ / 256 ) >> RTC_CLK_CAL_FRACT; - /* Guess the XTAL type. For now, only 40 and 26MHz are supported. - */ - switch (freq_mhz) { - case 21 ... 31: - return RTC_XTAL_FREQ_26M; - case 32 ... 33: - SOC_LOGW(TAG, "Potentially bogus XTAL frequency: %d MHz, guessing 26 MHz", freq_mhz); - return RTC_XTAL_FREQ_26M; - case 34 ... 35: - SOC_LOGW(TAG, "Potentially bogus XTAL frequency: %d MHz, guessing 40 MHz", freq_mhz); - return RTC_XTAL_FREQ_40M; - case 36 ... 45: - return RTC_XTAL_FREQ_40M; - default: - SOC_LOGW(TAG, "Bogus XTAL frequency: %d MHz", freq_mhz); - return RTC_XTAL_FREQ_AUTO; - } - /* Restore 8M and 8md256 clocks to original state */ - rtc_clk_8m_enable(clk_8m_enabled, clk_8md256_enabled); -} - void rtc_clk_apb_freq_update(uint32_t apb_freq) { WRITE_PERI_REG(RTC_APB_FREQ_REG, clk_val_to_reg_val(apb_freq >> 12)); @@ -811,103 +740,6 @@ uint32_t rtc_clk_apb_freq_get() return freq_hz - remainder; } - -void rtc_clk_init(rtc_clk_config_t cfg) -{ - rtc_cpu_freq_config_t old_config, new_config; - - /* 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 RTC_CNTL_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(RTC_CNTL_CLK_CONF_REG, RTC_CNTL_SOC_CLK_SEL) == RTC_CNTL_SOC_CLK_SEL_PLL) { - /* We don't know actual XTAL frequency yet, assume 40MHz. - * REF_TICK divider will be corrected below, one XTAL frequency is - * determined. - */ - rtc_clk_cpu_freq_to_xtal(40, 1); - } - - /* 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 8M clock division */ - REG_SET_FIELD(RTC_CNTL_CLK_CONF_REG, RTC_CNTL_CK8M_DIV_SEL, cfg.clk_8m_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); - - /* Estimate XTAL frequency */ - rtc_xtal_freq_t xtal_freq = cfg.xtal_freq; - if (xtal_freq == RTC_XTAL_FREQ_AUTO) { - if (clk_val_is_valid(READ_PERI_REG(RTC_XTAL_FREQ_REG))) { - /* XTAL frequency has already been set, use existing value */ - xtal_freq = rtc_clk_xtal_freq_get(); - } else { - /* Not set yet, estimate XTAL frequency based on RTC_FAST_CLK */ - xtal_freq = rtc_clk_xtal_freq_estimate(); - if (xtal_freq == RTC_XTAL_FREQ_AUTO) { - SOC_LOGW(TAG, "Can't estimate XTAL frequency, assuming 26MHz"); - xtal_freq = RTC_XTAL_FREQ_26M; - } - } - } else if (!clk_val_is_valid(READ_PERI_REG(RTC_XTAL_FREQ_REG))) { - /* Exact frequency was set in sdkconfig, but still warn if autodetected - * frequency is different. If autodetection failed, worst case we get a - * bit of garbage output. - */ - - rtc_xtal_freq_t est_xtal_freq = rtc_clk_xtal_freq_estimate(); - if (est_xtal_freq != xtal_freq) { - SOC_LOGW(TAG, "Possibly invalid CONFIG_ESP32_XTAL_FREQ setting (%dMHz). Detected %d MHz.", - xtal_freq, est_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); - assert(res && "invalid CPU frequency value"); - - /* Configure REF_TICK */ - REG_WRITE(APB_CTRL_XTAL_TICK_CONF_REG, xtal_freq - 1); - REG_WRITE(APB_CTRL_PLL_TICK_CONF_REG, APB_CLK_FREQ / MHZ - 1); /* Under PLL, APB frequency is always 80MHz */ - - /* Re-calculate the ccount to make time calculation correct. */ - XTHAL_SET_CCOUNT( 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); -} - /* 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/esp32/rtc_clk_common.h b/components/soc/esp32/rtc_clk_common.h new file mode 100644 index 000000000..b1efa175d --- /dev/null +++ b/components/soc/esp32/rtc_clk_common.h @@ -0,0 +1,38 @@ +// Copyright 2015-2018 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) + +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); +} + diff --git a/components/soc/esp32/rtc_clk_init.c b/components/soc/esp32/rtc_clk_init.c new file mode 100644 index 000000000..6672f460b --- /dev/null +++ b/components/soc/esp32/rtc_clk_init.c @@ -0,0 +1,176 @@ +// Copyright 2015-2018 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 +#include "rom/ets_sys.h" +#include "rom/rtc.h" +#include "rom/uart.h" +#include "rom/gpio.h" +#include "soc/rtc.h" +#include "soc/rtc_cntl_reg.h" +#include "soc/rtc_io_reg.h" +#include "soc/sens_reg.h" +#include "soc/dport_reg.h" +#include "soc/efuse_reg.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" + +/* Number of 8M/256 clock cycles to use for XTAL frequency estimation. + * 10 cycles will take approximately 300 microseconds. + */ +#define XTAL_FREQ_EST_CYCLES 10 + +static rtc_xtal_freq_t rtc_clk_xtal_freq_estimate(); + +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; + + /* 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 RTC_CNTL_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(RTC_CNTL_CLK_CONF_REG, RTC_CNTL_SOC_CLK_SEL) == RTC_CNTL_SOC_CLK_SEL_PLL) { + /* We don't know actual XTAL frequency yet, assume 40MHz. + * REF_TICK divider will be corrected below, once XTAL frequency is + * determined. + */ + rtc_clk_cpu_freq_to_xtal(40, 1); + } + + /* 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 8M clock division */ + REG_SET_FIELD(RTC_CNTL_CLK_CONF_REG, RTC_CNTL_CK8M_DIV_SEL, cfg.clk_8m_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); + + /* Estimate XTAL frequency */ + rtc_xtal_freq_t xtal_freq = cfg.xtal_freq; + if (xtal_freq == RTC_XTAL_FREQ_AUTO) { + if (clk_val_is_valid(READ_PERI_REG(RTC_XTAL_FREQ_REG))) { + /* XTAL frequency has already been set, use existing value */ + xtal_freq = rtc_clk_xtal_freq_get(); + } else { + /* Not set yet, estimate XTAL frequency based on RTC_FAST_CLK */ + xtal_freq = rtc_clk_xtal_freq_estimate(); + if (xtal_freq == RTC_XTAL_FREQ_AUTO) { + SOC_LOGW(TAG, "Can't estimate XTAL frequency, assuming 26MHz"); + xtal_freq = RTC_XTAL_FREQ_26M; + } + } + } else if (!clk_val_is_valid(READ_PERI_REG(RTC_XTAL_FREQ_REG))) { + /* Exact frequency was set in sdkconfig, but still warn if autodetected + * frequency is different. If autodetection failed, worst case we get a + * bit of garbage output. + */ + + rtc_xtal_freq_t est_xtal_freq = rtc_clk_xtal_freq_estimate(); + if (est_xtal_freq != xtal_freq) { + SOC_LOGW(TAG, "Possibly invalid CONFIG_ESP32_XTAL_FREQ setting (%dMHz). Detected %d MHz.", + xtal_freq, est_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); + assert(res && "invalid CPU frequency value"); + + /* Configure REF_TICK */ + REG_WRITE(APB_CTRL_XTAL_TICK_CONF_REG, xtal_freq - 1); + REG_WRITE(APB_CTRL_PLL_TICK_CONF_REG, APB_CLK_FREQ / MHZ - 1); /* Under PLL, APB frequency is always 80MHz */ + + /* Re-calculate the ccount to make time calculation correct. */ + XTHAL_SET_CCOUNT( 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); +} + +static rtc_xtal_freq_t rtc_clk_xtal_freq_estimate() +{ + /* Enable 8M/256 clock if needed */ + const bool clk_8m_enabled = rtc_clk_8m_enabled(); + const bool clk_8md256_enabled = rtc_clk_8md256_enabled(); + if (!clk_8md256_enabled) { + rtc_clk_8m_enable(true, true); + } + + uint64_t cal_val = rtc_clk_cal_ratio(RTC_CAL_8MD256, XTAL_FREQ_EST_CYCLES); + /* cal_val contains period of 8M/256 clock in XTAL clock cycles + * (shifted by RTC_CLK_CAL_FRACT bits). + * Xtal frequency will be (cal_val * 8M / 256) / 2^19 + */ + uint32_t freq_mhz = (cal_val * RTC_FAST_CLK_FREQ_APPROX / MHZ / 256 ) >> RTC_CLK_CAL_FRACT; + /* Guess the XTAL type. For now, only 40 and 26MHz are supported. + */ + switch (freq_mhz) { + case 21 ... 31: + return RTC_XTAL_FREQ_26M; + case 32 ... 33: + SOC_LOGW(TAG, "Potentially bogus XTAL frequency: %d MHz, guessing 26 MHz", freq_mhz); + return RTC_XTAL_FREQ_26M; + case 34 ... 35: + SOC_LOGW(TAG, "Potentially bogus XTAL frequency: %d MHz, guessing 40 MHz", freq_mhz); + return RTC_XTAL_FREQ_40M; + case 36 ... 45: + return RTC_XTAL_FREQ_40M; + default: + SOC_LOGW(TAG, "Bogus XTAL frequency: %d MHz", freq_mhz); + return RTC_XTAL_FREQ_AUTO; + } + /* Restore 8M and 8md256 clocks to original state */ + rtc_clk_8m_enable(clk_8m_enabled, clk_8md256_enabled); +} From e59571eece0cf232d08ca22fd3b74530f94e6038 Mon Sep 17 00:00:00 2001 From: Ivan Grokhotkov Date: Sun, 29 Jul 2018 08:27:06 +0300 Subject: [PATCH 8/9] soc/rtc_clk: reduce data size by disabling CSWTCH generation Saves about 200 bytes of DRAM at the expense of 4 bytes in IRAM. --- components/soc/component.mk | 2 ++ components/soc/esp32/component.mk | 1 + 2 files changed, 3 insertions(+) create mode 100644 components/soc/esp32/component.mk diff --git a/components/soc/component.mk b/components/soc/component.mk index 1c0f3d421..6dfb407f1 100644 --- a/components/soc/component.mk +++ b/components/soc/component.mk @@ -4,3 +4,5 @@ SOC_NAME := esp32 COMPONENT_SRCDIRS := $(SOC_NAME) src/ COMPONENT_ADD_INCLUDEDIRS := $(SOC_NAME)/include include + +-include $(COMPONENT_PATH)/$(SOC_NAME)/component.mk diff --git a/components/soc/esp32/component.mk b/components/soc/esp32/component.mk new file mode 100644 index 000000000..83a2ef722 --- /dev/null +++ b/components/soc/esp32/component.mk @@ -0,0 +1 @@ +esp32/rtc_clk.o: CFLAGS += -fno-jump-tables -fno-tree-switch-conversion From e3d76d13b4adf8a1083800f89677c321aca9a67d Mon Sep 17 00:00:00 2001 From: Ivan Grokhotkov Date: Sun, 29 Jul 2018 09:36:07 +0300 Subject: [PATCH 9/9] docs: update power management section on esp_pm_configure --- .../api-reference/system/power_management.rst | 19 ++++++++++++++----- 1 file changed, 14 insertions(+), 5 deletions(-) diff --git a/docs/en/api-reference/system/power_management.rst b/docs/en/api-reference/system/power_management.rst index 80fcda64b..618482441 100644 --- a/docs/en/api-reference/system/power_management.rst +++ b/docs/en/api-reference/system/power_management.rst @@ -20,16 +20,25 @@ Power management can be enabled at compile time, using :envvar:`CONFIG_PM_ENABLE Enabling power management features comes at the cost of increased interrupt latency. Extra latency depends on a number of factors, among which are CPU frequency, single/dual core mode, whether frequency switch needs to be performed or not. Minimal extra latency is 0.2us (when CPU frequency is 240MHz, and frequency scaling is not enabled), maximum extra latency is 40us (when frequency scaling is enabled, and a switch from 40MHz to 80MHz is performed on interrupt entry). -Dynamic frequency scaling (DFS) and automatic light sleep can be enabled in the application by calling :cpp:func:`esp_pm_configure` function. Its argument is a structure defining frequency scaling settings (for ESP32, minimum and maximum CPU frequencies). Alternatively, :envvar:`CONFIG_PM_DFS_INIT_AUTO` option can be enabled in menuconfig. If enabled, maximal CPU frequency is determined by :envvar:`CONFIG_ESP32_DEFAULT_CPU_FREQ_MHZ` setting, and minimal CPU frequency is set to the XTAL frequency. +Dynamic frequency scaling (DFS) and automatic light sleep can be enabled in the application by calling :cpp:func:`esp_pm_configure` function. Its argument is a structure defining frequency scaling settings, cpp:class:`esp_pm_config_esp32_t`. In this structure, 3 fields need to be initialized: + +* ``max_freq_mhz`` - Maximal CPU frequency, in MHZ (i.e. frequency used when ``ESP_PM_CPU_FREQ_MAX`` lock is taken). This will usually be set to :envvar:`CONFIG_ESP32_DEFAULT_CPU_FREQ_MHZ`. + +* ``min_freq_mhz`` — Minimal CPU frequency, in MHz (i.e. frequency used when only ``ESP_PM_APB_FREQ_MAX`` locks are taken). This can be set to XTAL frequency, or XTAL frequency divided by integer. Note that 10MHz is the lowest frequency at which the default REF_TICK clock of 1MHz can be generated. + +* ``light_sleep_enable`` — Whether system should automatically enter light sleep when no locks are taken (``true``/``false``). .. note:: - Automatic light sleep is based on FreeRTOS Tickless Idle functionality. :cpp:func:`esp_pm_configure` will return an `ESP_ERR_NOT_SUPPORTED` error if :envvar:`CONFIG_FREERTOS_USE_TICKLESS_IDLE` option is not enabled in menuconfig, but automatic light sleep is requested. + Automatic light sleep is based on FreeRTOS Tickless Idle functionality. :cpp:func:`esp_pm_configure` will return an `ESP_ERR_NOT_SUPPORTED` error if :envvar:`CONFIG_FREERTOS_USE_TICKLESS_IDLE` option is not enabled in menuconfig, but automatic light sleep is requested. .. note:: In light sleep, peripherals are clock gated, and interrupts (from GPIOs and internal peripherals) will not be generated. Wakeup source described in :doc:`Sleep Modes ` documentation can be used to wake from light sleep state. For example, EXT0 and EXT1 wakeup source can be used to wake up from a GPIO. +Alternatively, :envvar:`CONFIG_PM_DFS_INIT_AUTO` option can be enabled in menuconfig. If enabled, maximal CPU frequency is determined by :envvar:`CONFIG_ESP32_DEFAULT_CPU_FREQ_MHZ` setting, and minimal CPU frequency is set to the XTAL frequency. + + Power Management Locks ---------------------- @@ -58,7 +67,7 @@ When dynamic frequency scaling is enabled, CPU frequency will be switched as fol 1. When ``ESP_PM_CPU_FREQ_MAX`` or ``ESP_PM_APB_FREQ_MAX`` locks are acquired, CPU frequency will be 240 MHz, and APB frequency will be 80 MHz. - 2. Otherwise, frequency will be switched to the minimal value set using :cpp:func:`esp_pm_configure` (usually, XTAL). + 2. Otherwise, frequency will be switched to the minimal value set using :cpp:func:`esp_pm_configure`. - If maximal CPU frequency is 160 MHz: @@ -66,13 +75,13 @@ When dynamic frequency scaling is enabled, CPU frequency will be switched as fol 2. When ``ESP_PM_CPU_FREQ_MAX`` is not acquired, but ``ESP_PM_APB_FREQ_MAX`` is, CPU and APB frequencies are set to 80 MHz. - 3. Otherwise, frequency will be switched to the minimal value set using :cpp:func:`esp_pm_configure` (usually, XTAL). + 3. Otherwise, frequency will be switched to the minimal value set using :cpp:func:`esp_pm_configure`. - If maximal CPU frequency is 80 MHz: 1. When ``ESP_PM_CPU_FREQ_MAX`` or ``ESP_PM_APB_FREQ_MAX`` locks are acquired, CPU and APB frequencies will be 80 MHz. - 2. Otherwise, frequency will be switched to the minimal value set using :cpp:func:`esp_pm_configure` (usually, XTAL). + 2. Otherwise, frequency will be switched to the minimal value set using :cpp:func:`esp_pm_configure`. - When none of the locks are aquired, and light sleep is enabled in a call to :cpp:func:`esp_pm_configure`, the system will go into light sleep mode. The duration of light sleep will be determined by: