Kaynağa Gözat

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
Ivan Grokhotkov 6 yıl önce
ebeveyn
işleme
490bf29767

+ 11 - 3
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

+ 22 - 1
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

+ 60 - 35
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);

+ 5 - 0
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

+ 18 - 5
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)