]> granicus.if.org Git - esp-idf/blobdiff - components/soc/esp32/rtc_clk.c
fix(global, log): fix `esp_log(_early)_timestamp` readings after startup by correct...
[esp-idf] / components / soc / esp32 / rtc_clk.c
index 79b56f993651b4a773d212df2a5131f665bf6ab4..e3e007898df55e69efa2cb1ec240a077ce7a84a9 100644 (file)
 #include "i2c_rtc_clk.h"
 #include "soc_log.h"
 #include "sdkconfig.h"
+#include "xtensa/core-macros.h"
+
 
 #define MHZ (1000000)
 
+/* Frequency of the 8M oscillator is 8.5MHz +/- 5%, at the default DCAP setting */
+#define RTC_FAST_CLK_FREQ_8M        8500000
+#define RTC_SLOW_CLK_FREQ_150K      150000
+#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.
@@ -55,35 +63,61 @@ static const char* TAG = "rtc_clk";
 #define XTAL_32K_DRES_VAL   3
 #define XTAL_32K_DBIAS_VAL  0
 
+#define XTAL_32K_BOOTSTRAP_DAC_VAL      3
+#define XTAL_32K_BOOTSTRAP_DRES_VAL     3
+#define XTAL_32K_BOOTSTRAP_DBIAS_VAL    0
+#define XTAL_32K_BOOTSTRAP_TIME_US      7
+
 /* Delays for various clock sources to be enabled/switched.
  * All values are in microseconds.
  * TODO: some of these are excessive, and should be reduced.
  */
-#define DELAY_CPU_FREQ_SWITCH_TO_XTAL   80
+#define DELAY_CPU_FREQ_SWITCH_TO_XTAL_WITH_150K  80
+#define DELAY_CPU_FREQ_SWITCH_TO_XTAL_WITH_32K   160
 #define DELAY_CPU_FREQ_SWITCH_TO_PLL    10
 #define DELAY_PLL_DBIAS_RAISE           3
-#define DELAY_PLL_ENABLE                80
+#define DELAY_PLL_ENABLE_WITH_150K      80
+#define DELAY_PLL_ENABLE_WITH_32K       160
 #define DELAY_FAST_CLK_SWITCH           3
 #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
+
+
+static void rtc_clk_32k_enable_internal(int dac, int dres, int dbias)
+{
+    SET_PERI_REG_MASK(RTC_IO_XTAL_32K_PAD_REG, RTC_IO_X32N_MUX_SEL | RTC_IO_X32P_MUX_SEL);
+    CLEAR_PERI_REG_MASK(RTC_IO_XTAL_32K_PAD_REG,
+            RTC_IO_X32P_RDE | RTC_IO_X32P_RUE | RTC_IO_X32N_RUE |
+            RTC_IO_X32N_RDE | RTC_IO_X32N_MUX_SEL | RTC_IO_X32P_MUX_SEL);
+    REG_SET_FIELD(RTC_IO_XTAL_32K_PAD_REG, RTC_IO_DAC_XTAL_32K, dac);
+    REG_SET_FIELD(RTC_IO_XTAL_32K_PAD_REG, RTC_IO_DRES_XTAL_32K, dres);
+    REG_SET_FIELD(RTC_IO_XTAL_32K_PAD_REG, RTC_IO_DBIAS_XTAL_32K, dbias);
+    SET_PERI_REG_MASK(RTC_IO_XTAL_32K_PAD_REG, RTC_IO_XPD_XTAL_32K);
+}
 
 void rtc_clk_32k_enable(bool enable)
 {
     if (enable) {
-        SET_PERI_REG_MASK(RTC_IO_XTAL_32K_PAD_REG, RTC_IO_X32N_MUX_SEL | RTC_IO_X32P_MUX_SEL);
-        CLEAR_PERI_REG_MASK(RTC_IO_XTAL_32K_PAD_REG,
-                RTC_IO_X32P_RDE | RTC_IO_X32P_RUE | RTC_IO_X32N_RUE |
-                RTC_IO_X32N_RDE | RTC_IO_X32N_MUX_SEL | RTC_IO_X32P_MUX_SEL);
-        REG_SET_FIELD(RTC_IO_XTAL_32K_PAD_REG, RTC_IO_DAC_XTAL_32K, XTAL_32K_DAC_VAL);
-        REG_SET_FIELD(RTC_IO_XTAL_32K_PAD_REG, RTC_IO_DRES_XTAL_32K, XTAL_32K_DRES_VAL);
-        REG_SET_FIELD(RTC_IO_XTAL_32K_PAD_REG, RTC_IO_DBIAS_XTAL_32K, XTAL_32K_DBIAS_VAL);
-        SET_PERI_REG_MASK(RTC_IO_XTAL_32K_PAD_REG, RTC_IO_XPD_XTAL_32K);
+        rtc_clk_32k_enable_internal(XTAL_32K_DAC_VAL, XTAL_32K_DRES_VAL, XTAL_32K_DBIAS_VAL);
     } else {
         CLEAR_PERI_REG_MASK(RTC_IO_XTAL_32K_PAD_REG, RTC_IO_XPD_XTAL_32K);
     }
 }
 
+void rtc_clk_32k_bootstrap()
+{
+    CLEAR_PERI_REG_MASK(RTC_IO_XTAL_32K_PAD_REG, RTC_IO_XPD_XTAL_32K);
+    SET_PERI_REG_MASK(RTC_IO_XTAL_32K_PAD_REG, RTC_IO_X32P_RUE | RTC_IO_X32N_RDE);
+    ets_delay_us(XTAL_32K_BOOTSTRAP_TIME_US);
+    rtc_clk_32k_enable_internal(XTAL_32K_BOOTSTRAP_DAC_VAL,
+            XTAL_32K_BOOTSTRAP_DRES_VAL, XTAL_32K_BOOTSTRAP_DBIAS_VAL);
+}
+
 bool rtc_clk_32k_enabled()
 {
     return GET_PERI_REG_MASK(RTC_IO_XTAL_32K_PAD_REG, RTC_IO_XPD_XTAL_32K) != 0;
@@ -167,6 +201,15 @@ rtc_slow_freq_t rtc_clk_slow_freq_get()
     return REG_GET_FIELD(RTC_CNTL_CLK_CONF_REG, RTC_CNTL_ANA_CLK_RTC_SEL);
 }
 
+uint32_t rtc_clk_slow_freq_get_hz()
+{
+    switch(rtc_clk_slow_freq_get()) {
+        case RTC_SLOW_FREQ_RTC: return RTC_SLOW_CLK_FREQ_150K;
+        case RTC_SLOW_FREQ_32K_XTAL: return RTC_SLOW_CLK_FREQ_32K;
+        case RTC_SLOW_FREQ_8MD256: return RTC_SLOW_CLK_FREQ_8MD256;
+    }
+    return 0;
+}
 
 void rtc_clk_fast_freq_set(rtc_fast_freq_t fast_freq)
 {
@@ -275,7 +318,9 @@ void rtc_clk_bbpll_set(rtc_xtal_freq_t xtal_freq, rtc_cpu_freq_t cpu_freq)
     I2C_WRITEREG_RTC(I2C_BBPLL, I2C_BBPLL_OC_LREF, i2c_bbpll_lref);
     I2C_WRITEREG_RTC(I2C_BBPLL, I2C_BBPLL_OC_DIV_7_0, i2c_bbpll_div_7_0);
     I2C_WRITEREG_RTC(I2C_BBPLL, I2C_BBPLL_OC_DCUR, i2c_bbpll_dcur);
-    ets_delay_us(DELAY_PLL_ENABLE);
+    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);
 }
 
 void rtc_clk_cpu_freq_set(rtc_cpu_freq_t cpu_freq)
@@ -286,8 +331,10 @@ void rtc_clk_cpu_freq_set(rtc_cpu_freq_t cpu_freq)
     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);
-    ets_delay_us(DELAY_CPU_FREQ_SWITCH_TO_XTAL);
-    REG_SET_FIELD(DPORT_CPU_PER_CONF_REG, DPORT_CPUPERIOD_SEL, 0);
+    uint32_t delay_xtal_switch = (rtc_clk_slow_freq_get() == RTC_SLOW_FREQ_RTC) ?
+            DELAY_CPU_FREQ_SWITCH_TO_XTAL_WITH_150K : DELAY_CPU_FREQ_SWITCH_TO_XTAL_WITH_32K;
+    ets_delay_us(delay_xtal_switch);
+    DPORT_REG_SET_FIELD(DPORT_CPU_PER_CONF_REG, DPORT_CPUPERIOD_SEL, 0);
     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);
@@ -316,13 +363,13 @@ void rtc_clk_cpu_freq_set(rtc_cpu_freq_t cpu_freq)
                 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) {
-            REG_SET_FIELD(DPORT_CPU_PER_CONF_REG, DPORT_CPUPERIOD_SEL, 0);
+            DPORT_REG_SET_FIELD(DPORT_CPU_PER_CONF_REG, DPORT_CPUPERIOD_SEL, 0);
             ets_update_cpu_frequency(80);
         } else if (cpu_freq == RTC_CPU_FREQ_160M) {
-            REG_SET_FIELD(DPORT_CPU_PER_CONF_REG, DPORT_CPUPERIOD_SEL, 1);
+            DPORT_REG_SET_FIELD(DPORT_CPU_PER_CONF_REG, DPORT_CPUPERIOD_SEL, 1);
             ets_update_cpu_frequency(160);
         } else if (cpu_freq == RTC_CPU_FREQ_240M) {
-            REG_SET_FIELD(DPORT_CPU_PER_CONF_REG, DPORT_CPUPERIOD_SEL, 2);
+            DPORT_REG_SET_FIELD(DPORT_CPU_PER_CONF_REG, DPORT_CPUPERIOD_SEL, 2);
             ets_update_cpu_frequency(240);
         }
         REG_SET_FIELD(RTC_CNTL_CLK_CONF_REG, RTC_CNTL_SOC_CLK_SEL, RTC_CNTL_SOC_CLK_SEL_PLL);
@@ -347,7 +394,7 @@ rtc_cpu_freq_t rtc_clk_cpu_freq_get()
             break;
         }
         case RTC_CNTL_SOC_CLK_SEL_PLL: {
-            uint32_t cpuperiod_sel = REG_GET_FIELD(DPORT_CPU_PER_CONF_REG, DPORT_CPUPERIOD_SEL);
+            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) {
@@ -422,19 +469,12 @@ void rtc_clk_xtal_freq_update(rtc_xtal_freq_t xtal_freq)
 
 static rtc_xtal_freq_t rtc_clk_xtal_freq_estimate()
 {
-    /* ROM startup code estimates XTAL frequency using an 8MD256 clock and stores
-     * the value into RTC_APB_FREQ_REG. The value is in Hz, right shifted by 12.
-     * Use this value to guess the real XTAL frequency.
-     *
-     * TODO: make this more robust by calibrating again after setting
-     * RTC_CNTL_CK8M_DFREQ.
+    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 apb_freq_reg = READ_PERI_REG(RTC_APB_FREQ_REG);
-    if (!clk_val_is_valid(apb_freq_reg)) {
-        SOC_LOGW(TAG, "invalid RTC_APB_FREQ_REG value: 0x%08x", apb_freq_reg);
-        return RTC_XTAL_FREQ_AUTO;
-    }
-    uint32_t freq_mhz = (reg_val_to_clk_val(apb_freq_reg) << 12) / MHZ;
+    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) {
@@ -461,12 +501,31 @@ void rtc_clk_apb_freq_update(uint32_t apb_freq)
 
 uint32_t rtc_clk_apb_freq_get()
 {
-    return reg_val_to_clk_val(READ_PERI_REG(RTC_APB_FREQ_REG)) << 12;
+    uint32_t freq_hz = reg_val_to_clk_val(READ_PERI_REG(RTC_APB_FREQ_REG)) << 12;
+    // round to the nearest MHz
+    freq_hz += MHZ / 2;
+    uint32_t remainder = freq_hz % MHZ;
+    return freq_hz - remainder;
 }
 
 
 void rtc_clk_init(rtc_clk_config_t cfg)
 {
+    rtc_cpu_freq_t cpu_source_before = rtc_clk_cpu_freq_get();
+    
+    /* If we get a TG WDT system reset while running at 240MHz,
+     * DPORT_CPUPERIOD_SEL register will be reset to 0 resulting in 120MHz
+     * APB and CPU frequencies after reset. This will cause issues with XTAL
+     * frequency estimation, so we switch to XTAL frequency first.
+     *
+     * Ideally we would only do this if 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) {
+        rtc_clk_cpu_freq_set(RTC_CPU_FREQ_XTAL);
+    }
+
     /* Set tuning parameters for 8M and 150k clocks.
      * Note: this doesn't attempt to set the clocks to precise frequencies.
      * Instead, we calibrate these clocks against XTAL frequency later, when necessary.
@@ -485,23 +544,43 @@ void rtc_clk_init(rtc_clk_config_t cfg)
     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 if requested */
+    /* Estimate XTAL frequency */
+    rtc_xtal_freq_t est_xtal_freq = rtc_clk_xtal_freq_estimate();
     rtc_xtal_freq_t xtal_freq = cfg.xtal_freq;
     if (xtal_freq == RTC_XTAL_FREQ_AUTO) {
-        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;
+        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 = est_xtal_freq;
+            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.
+         */
+        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_set(cfg.cpu_freq);
+    
+    /* Re-calculate the ccount to make time calculation correct. */    
+    uint32_t freq_before = rtc_clk_cpu_freq_value(cpu_source_before) / MHZ;
+    uint32_t freq_after = rtc_clk_cpu_freq_value(cfg.cpu_freq) / MHZ;
+    XTHAL_SET_CCOUNT( XTHAL_GET_CCOUNT() * freq_after / freq_before );
 
     /* Slow & fast clocks setup */
     if (cfg.slow_freq == RTC_SLOW_FREQ_32K_XTAL) {
-        rtc_clk_32k_enable(false);
+        rtc_clk_32k_enable(true);
     }
     if (cfg.fast_freq == RTC_FAST_FREQ_8M) {
         bool need_8md256 = cfg.slow_freq == RTC_SLOW_FREQ_8MD256;
@@ -515,20 +594,3 @@ void rtc_clk_init(rtc_clk_config_t cfg)
  * TODO: update the library to use rtc_clk_xtal_freq_get
  */
 rtc_xtal_freq_t rtc_get_xtal() __attribute__((alias("rtc_clk_xtal_freq_get")));
-
-
-/* Referenced in librtc.a:rtc.o.
- * TODO: remove
- */
-void rtc_uart_div_modify(int latch)
-{
-
-}
-
-/* Referenced in librtc.a:rtc.o.
- * TODO: remove
- */
-void rtc_uart_tx_wait_idle(int uart)
-{
-
-}