From 490bf29767ce057bc4944db23f26b8a9376bfa16 Mon Sep 17 00:00:00 2001 From: Ivan Grokhotkov Date: Wed, 12 Feb 2020 18:09:17 +0100 Subject: [PATCH] esp32s2: fix enabling 32k XTAL clock On the ESP32S2, rtc_clk_cal(RTC_CAL_RTC_MUX) measures the frequency of the 90kHz RTC clock regardless of the selected slow clock frequency. Keep track which clock is selected and pass the argument to rtc_clk_cal accordingly. fix clock choices update rtc 32k xtal code for s2 missed api in rtc.h bootloader_clock: update for S2 --- .../bootloader_support/src/bootloader_clock.c | 14 ++- components/esp32s2/Kconfig | 23 ++++- components/esp32s2/clk.c | 95 ++++++++++++------- components/soc/soc/esp32s2/include/soc/rtc.h | 5 + components/soc/src/esp32s2/rtc_clk.c | 23 ++++- 5 files changed, 116 insertions(+), 44 deletions(-) diff --git a/components/bootloader_support/src/bootloader_clock.c b/components/bootloader_support/src/bootloader_clock.c index d3305dfe7..09186402b 100644 --- a/components/bootloader_support/src/bootloader_clock.c +++ b/components/bootloader_support/src/bootloader_clock.c @@ -54,9 +54,8 @@ 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; -#elif CONFIG_IDF_TARGET_ESP32S2 - clk_cfg.xtal_freq = RTC_XTAL_FREQ_40M; #endif + /* ESP32-S2 doesn't have XTAL_FREQ choice, always 40MHz */ 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(); @@ -66,11 +65,20 @@ void bootloader_clock_configure(void) * part of the start up time by enabling 32k XTAL early. * App startup code will wait until the oscillator has started up. */ -#ifdef CONFIG_ESP32_RTC_CLK_SRC_EXT_CRYS + + /* TODO: move the clock option into esp_system, so that this doesn't have + * to continue: + */ +#if CONFIG_ESP32_RTC_CLK_SRC_EXT_CRYS if (!rtc_clk_32k_enabled()) { rtc_clk_32k_bootstrap(CONFIG_ESP32_RTC_XTAL_BOOTSTRAP_CYCLES); } #endif +#if CONFIG_ESP32S2_RTC_CLK_SRC_EXT_CRYS + if (!rtc_clk_32k_enabled()) { + rtc_clk_32k_bootstrap(0); + } +#endif } #ifdef BOOTLOADER_BUILD diff --git a/components/esp32s2/Kconfig b/components/esp32s2/Kconfig index 62c3ab42c..a166c2cdc 100644 --- a/components/esp32s2/Kconfig +++ b/components/esp32s2/Kconfig @@ -431,10 +431,31 @@ menu "ESP32S2-specific" help Choose which clock is used as RTC clock source. + - "Internal 90kHz oscillator" option provides lowest deep sleep current + consumption, and does not require extra external components. However + frequency stability with respect to temperature is poor, so time may + drift in deep/light sleep modes. + - "External 32kHz crystal" provides better frequency stability, at the + expense of slightly higher (1uA) deep sleep current consumption. + - "External 32kHz oscillator" allows using 32kHz clock generated by an + external circuit. In this case, external clock signal must be connected + to 32K_XP pin. Amplitude should be <1.2V in case of sine wave signal, + and <1V in case of square wave signal. Common mode voltage should be + 0.1 < Vcm < 0.5Vamp, where Vamp is the signal amplitude. + Additionally, 1nF capacitor must be connected between 32K_XN pin and + ground. 32K_XN pin can not be used as a GPIO in this case. + - "Internal 8MHz oscillator divided by 256" option results in higher + deep sleep current (by 5uA) but has better frequency stability than + the internal 90kHz oscillator. It does not require external components. + config ESP32S2_RTC_CLK_SRC_INT_RC - bool "Internal 150kHz RC oscillator" + bool "Internal 90kHz RC oscillator" config ESP32S2_RTC_CLK_SRC_EXT_CRYS bool "External 32kHz crystal" + config ESP32S2_RTC_CLK_SRC_EXT_OSC + bool "External 32kHz oscillator at 32K_XP pin" + config ESP32S2_RTC_CLK_SRC_INT_8MD256 + bool "Internal 8MHz oscillator, divided by 256 (~32kHz)" endchoice config ESP32S2_RTC_CLK_CAL_CYCLES diff --git a/components/esp32s2/clk.c b/components/esp32s2/clk.c index 7304a6f1e..270d69b35 100644 --- a/components/esp32s2/clk.c +++ b/components/esp32s2/clk.c @@ -44,10 +44,29 @@ #define MHZ (1000000) -static void select_rtc_slow_clk(rtc_slow_freq_t slow_clk); +/* Lower threshold for a reasonably-looking calibration value for a 32k XTAL. + * The ideal value (assuming 32768 Hz frequency) is 1000000/32768*(2**19) = 16*10^6. + */ +#define MIN_32K_XTAL_CAL_VAL 15000000L + +/* Indicates that this 32k oscillator gets input from external oscillator, rather + * than a crystal. + */ +#define EXT_OSC_FLAG BIT(3) + +/* This is almost the same as rtc_slow_freq_t, except that we define + * an extra enum member for the external 32k oscillator. + * For convenience, lower 2 bits should correspond to rtc_slow_freq_t values. + */ +typedef enum { + SLOW_CLK_RTC = RTC_SLOW_FREQ_RTC, //!< Internal 90 kHz RC oscillator + SLOW_CLK_32K_XTAL = RTC_SLOW_FREQ_32K_XTAL, //!< External 32 kHz XTAL + SLOW_CLK_8MD256 = RTC_SLOW_FREQ_8MD256, //!< Internal 8 MHz RC oscillator, divided by 256 + SLOW_CLK_32K_EXT_OSC = RTC_SLOW_FREQ_32K_XTAL | EXT_OSC_FLAG //!< External 32k oscillator connected to 32K_XP pin +} slow_clk_sel_t; + +static void select_rtc_slow_clk(slow_clk_sel_t slow_clk); -// g_ticks_us defined in ROMs for PRO CPU -extern uint32_t g_ticks_per_us_pro; static const char *TAG = "clk"; @@ -72,8 +91,12 @@ void esp_clk_init(void) rtc_wdt_protect_on(); #endif -#ifdef CONFIG_ESP32S2_RTC_CLK_SRC_EXT_CRYS - select_rtc_slow_clk(RTC_SLOW_FREQ_32K_XTAL); +#if defined(CONFIG_ESP32S2_RTC_CLK_SRC_EXT_CRYS) + select_rtc_slow_clk(SLOW_CLK_32K_XTAL); +#elif defined(CONFIG_ESP32S2_RTC_CLK_SRC_EXT_OSC) + select_rtc_slow_clk(SLOW_CLK_32K_EXT_OSC); +#elif defined(CONFIG_ESP32S2_RTC_CLK_SRC_INT_8MD256) + select_rtc_slow_clk(SLOW_CLK_8MD256); #else select_rtc_slow_clk(RTC_SLOW_FREQ_RTC); #endif @@ -106,28 +129,29 @@ void esp_clk_init(void) int IRAM_ATTR esp_clk_cpu_freq(void) { - return g_ticks_per_us_pro * 1000000; + return ets_get_cpu_frequency() * 1000000; } int IRAM_ATTR esp_clk_apb_freq(void) { - return MIN(g_ticks_per_us_pro, 80) * 1000000; + return MIN(ets_get_cpu_frequency(), 80) * 1000000; } -void IRAM_ATTR ets_update_cpu_frequency(uint32_t ticks_per_us) -{ - /* Update scale factors used by ets_delay_us */ - g_ticks_per_us_pro = ticks_per_us; -} - -static void select_rtc_slow_clk(rtc_slow_freq_t slow_clk) +static void select_rtc_slow_clk(slow_clk_sel_t slow_clk) { + rtc_slow_freq_t rtc_slow_freq = slow_clk & RTC_CNTL_ANA_CLK_RTC_SEL_V; uint32_t cal_val = 0; - uint32_t wait = 0; - const uint32_t warning_timeout = 3 /* sec */ * 32768 /* Hz */ / (2 * SLOW_CLK_CAL_CYCLES); - bool changing_clock_to_150k = false; + /* number of times to repeat 32k XTAL calibration + * before giving up and switching to the internal RC + */ +#ifdef CONFIG_IDF_TARGET_ESP32 + int retry_32k_xtal = 1; /* don't change the behavior for the ESP32 */ +#else + int retry_32k_xtal = 3; +#endif + do { - if (slow_clk == RTC_SLOW_FREQ_32K_XTAL) { + if (rtc_slow_freq == RTC_SLOW_FREQ_32K_XTAL) { /* 32k XTAL oscillator needs to be enabled and running before it can * be used. Hardware doesn't have a direct way of checking if the * oscillator is running. Here we use rtc_clk_cal function to count @@ -136,22 +160,26 @@ static void select_rtc_slow_clk(rtc_slow_freq_t slow_clk) * will time out, returning 0. */ ESP_EARLY_LOGD(TAG, "waiting for 32k oscillator to start up"); - rtc_clk_32k_enable(true); - cal_val = rtc_clk_cal(RTC_CAL_32K_XTAL, SLOW_CLK_CAL_CYCLES); - if (cal_val == 0 || cal_val < 15000000L) { - ESP_EARLY_LOGE(TAG, "RTC: Not found External 32 kHz XTAL. Switching to Internal 150 kHz RC chain"); - slow_clk = RTC_SLOW_FREQ_RTC; - changing_clock_to_150k = true; + if (slow_clk == SLOW_CLK_32K_XTAL) { + rtc_clk_32k_enable(true); + } else if (slow_clk == SLOW_CLK_32K_EXT_OSC) { + rtc_clk_32k_enable_external(); } + // When SLOW_CLK_CAL_CYCLES is set to 0, clock calibration will not be performed at startup. + if (SLOW_CLK_CAL_CYCLES > 0) { + cal_val = rtc_clk_cal(RTC_CAL_32K_XTAL, SLOW_CLK_CAL_CYCLES); + if (cal_val == 0 || cal_val < MIN_32K_XTAL_CAL_VAL) { + if (retry_32k_xtal-- > 0) { + continue; + } + ESP_EARLY_LOGW(TAG, "32 kHz XTAL not found, switching to internal 90 kHz oscillator"); + rtc_slow_freq = RTC_SLOW_FREQ_RTC; + } + } + } else if (rtc_slow_freq == RTC_SLOW_FREQ_8MD256) { + rtc_clk_8m_enable(true, true); } - rtc_clk_slow_freq_set(slow_clk); - if (changing_clock_to_150k == true && wait > 1) { - // This helps when there are errors when switching the clock from External 32 kHz XTAL to Internal 150 kHz RC chain. - rtc_clk_32k_enable(false); - uint32_t min_bootstrap = 5; // Min bootstrapping for continue switching the clock. - rtc_clk_32k_bootstrap(min_bootstrap); - rtc_clk_32k_enable(true); - } + rtc_clk_slow_freq_set(rtc_slow_freq); if (SLOW_CLK_CAL_CYCLES > 0) { /* TODO: 32k XTAL oscillator has some frequency drift at startup. @@ -162,9 +190,6 @@ static void select_rtc_slow_clk(rtc_slow_freq_t slow_clk) const uint64_t cal_dividend = (1ULL << RTC_CLK_CAL_FRACT) * 1000000ULL; cal_val = (uint32_t) (cal_dividend / rtc_clk_slow_freq_get_hz()); } - if (++wait % warning_timeout == 0) { - ESP_EARLY_LOGW(TAG, "still waiting for source selection RTC"); - } } while (cal_val == 0); ESP_EARLY_LOGD(TAG, "RTC_SLOW_CLK calibration value: %d", cal_val); esp_clk_slowclk_cal_set(cal_val); diff --git a/components/soc/soc/esp32s2/include/soc/rtc.h b/components/soc/soc/esp32s2/include/soc/rtc.h index 7953510ed..2c27bd670 100644 --- a/components/soc/soc/esp32s2/include/soc/rtc.h +++ b/components/soc/soc/esp32s2/include/soc/rtc.h @@ -325,6 +325,11 @@ void rtc_clk_xtal_freq_update(rtc_xtal_freq_t xtal_freq); */ void rtc_clk_32k_enable(bool en); +/** + * @brief Configure 32 kHz XTAL oscillator to accept external clock signal + */ +void rtc_clk_32k_enable_external(void); + /** * @brief Get the state of 32k XTAL oscillator * @return true if 32k XTAL oscillator has been enabled diff --git a/components/soc/src/esp32s2/rtc_clk.c b/components/soc/src/esp32s2/rtc_clk.c index 3e9234258..75969a804 100644 --- a/components/soc/src/esp32s2/rtc_clk.c +++ b/components/soc/src/esp32s2/rtc_clk.c @@ -57,6 +57,8 @@ void rtc_clk_32k_enable_internal(x32k_config_t cfg) void rtc_clk_32k_enable(bool enable) { if (enable) { + SET_PERI_REG_MASK(RTC_IO_XTAL_32P_PAD_REG, RTC_IO_X32P_MUX_SEL); + SET_PERI_REG_MASK(RTC_IO_XTAL_32N_PAD_REG, RTC_IO_X32N_MUX_SEL); x32k_config_t cfg = X32K_CONFIG_DEFAULT(); rtc_clk_32k_enable_internal(cfg); } else { @@ -65,8 +67,22 @@ void rtc_clk_32k_enable(bool enable) } } +void rtc_clk_32k_enable_external(void) +{ + SET_PERI_REG_MASK(RTC_IO_XTAL_32P_PAD_REG, RTC_IO_X32P_MUX_SEL); + SET_PERI_REG_MASK(RTC_IO_XTAL_32N_PAD_REG, RTC_IO_X32N_MUX_SEL); + /* TODO: external 32k source may need different settings */ + x32k_config_t cfg = X32K_CONFIG_DEFAULT(); + rtc_clk_32k_enable_internal(cfg); +} + void rtc_clk_32k_bootstrap(uint32_t cycle) { + /* No special bootstrapping needed for ESP32-S2, 'cycle' argument is to keep the signature + * same as for the ESP32. Just enable the XTAL here. + */ + (void) cycle; + rtc_clk_32k_enable(true); } bool rtc_clk_32k_enabled(void) @@ -76,11 +92,8 @@ bool rtc_clk_32k_enabled(void) bool xtal_xpd_sw = (xtal_conf & RTC_CNTL_XTAL32K_XPD_FORCE) >> RTC_CNTL_XTAL32K_XPD_FORCE_S; /* If xtal xpd software control is on */ bool xtal_xpd_st = (xtal_conf & RTC_CNTL_XPD_XTAL_32K) >> RTC_CNTL_XPD_XTAL_32K_S; - if (xtal_xpd_sw & !xtal_xpd_st) { - return false; - } else { - return true; - } + bool disabled = xtal_xpd_sw && !xtal_xpd_st; + return !disabled; } void rtc_clk_8m_enable(bool clk_8m_en, bool d256_en)