]> granicus.if.org Git - esp-idf/commitdiff
soc/rtc: add fast paths for switching between PLL and XTAL
authorIvan Grokhotkov <ivan@espressif.com>
Mon, 21 Aug 2017 14:34:42 +0000 (22:34 +0800)
committerIvan Grokhotkov <ivan@espressif.com>
Wed, 18 Oct 2017 06:19:14 +0000 (14:19 +0800)
components/soc/esp32/include/soc/rtc.h
components/soc/esp32/rtc_clk.c

index b47e39e1e7bdd4e4565d2be6c1f05c0e1c82572e..2c4b4d320cc4fad971f42086f0ef5ff0ed132944 100644 (file)
@@ -276,6 +276,25 @@ rtc_fast_freq_t rtc_clk_fast_freq_get();
  */
 void rtc_clk_cpu_freq_set(rtc_cpu_freq_t cpu_freq);
 
+/**
+ * @brief Switch CPU frequency
+ *
+ * This is a faster version of rtc_clk_cpu_freq_set, which can handle some of
+ * the frequency switch paths (XTAL -> PLL, PLL -> XTAL).
+ * When switching from PLL to XTAL, PLL is not disabled (unlike rtc_clk_cpu_freq_set).
+ * When switching back from XTAL to PLL, only the same PLL can be used.
+ * Therefore it is not possible to switch 240 -> XTAL -> (80 or 160) using this
+ * function.
+ *
+ * For unsupported cases, this function falls back to rtc_clk_cpu_freq_set.
+ *
+ * Unlike rtc_clk_cpu_freq_set, this function relies on static data, so it is
+ * less safe to use it e.g. from a panic handler (when memory might be corrupted).
+ *
+ * @param cpu_freq  new CPU frequency
+ */
+void rtc_clk_cpu_freq_set_fast(rtc_cpu_freq_t cpu_freq);
+
 /**
  * @brief Get the currently selected CPU frequency
  *
index e3e007898df55e69efa2cb1ec240a077ce7a84a9..0b1f90251774df13be055de45b347dd6da136057 100644 (file)
@@ -72,9 +72,9 @@ static const char* TAG = "rtc_clk";
  * All values are in microseconds.
  * TODO: some of these are excessive, and should be reduced.
  */
-#define DELAY_CPU_FREQ_SWITCH_TO_XTAL_WITH_150K  80
+#define DELAY_CPU_FREQ_SWITCH_TO_XTAL_WITH_150K  20
 #define DELAY_CPU_FREQ_SWITCH_TO_XTAL_WITH_32K   160
-#define DELAY_CPU_FREQ_SWITCH_TO_PLL    10
+#define DELAY_CPU_FREQ_SWITCH_TO_PLL    20
 #define DELAY_PLL_DBIAS_RAISE           3
 #define DELAY_PLL_ENABLE_WITH_150K      80
 #define DELAY_PLL_ENABLE_WITH_32K       160
@@ -87,6 +87,8 @@ static const char* TAG = "rtc_clk";
  */
 #define XTAL_FREQ_EST_CYCLES            10
 
+static rtc_cpu_freq_t s_cur_freq = RTC_CPU_FREQ_XTAL;
+static int s_pll_freq = 0;
 
 static void rtc_clk_32k_enable_internal(int dac, int dres, int dbias)
 {
@@ -323,6 +325,70 @@ void rtc_clk_bbpll_set(rtc_xtal_freq_t xtal_freq, rtc_cpu_freq_t cpu_freq)
     ets_delay_us(delay_pll_en);
 }
 
+/**
+ * Switch to XTAL frequency. Does not disable the PLL.
+ */
+static void rtc_clk_cpu_freq_to_xtal()
+{
+    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, RTC_CNTL_DBIAS_1V10);
+    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 ((cpu_freq == RTC_CPU_FREQ_240M && s_pll_freq == 320) ||
+        (cpu_freq != RTC_CPU_FREQ_240M && s_pll_freq == 240)) {
+        /* need to switch PLLs, fall back to full implementation */
+        rtc_clk_cpu_freq_set(cpu_freq);
+        return;
+    }
+
+    if (cpu_freq == RTC_CPU_FREQ_80M) {
+        DPORT_REG_WRITE(DPORT_CPU_PER_CONF_REG, 0);
+        freq = 80;
+    } else if (cpu_freq == RTC_CPU_FREQ_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, RTC_CNTL_DBIAS_1V25);
+        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);
+    }
+}
+
 void rtc_clk_cpu_freq_set(rtc_cpu_freq_t cpu_freq)
 {
     rtc_xtal_freq_t xtal_freq = rtc_clk_xtal_freq_get();
@@ -338,6 +404,7 @@ void rtc_clk_cpu_freq_set(rtc_cpu_freq_t cpu_freq)
     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_pll_freq = 0;
     rtc_clk_apb_freq_update(xtal_freq * MHZ);
 
     /* is APLL under force power down? */
@@ -365,17 +432,21 @@ void rtc_clk_cpu_freq_set(rtc_cpu_freq_t 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_pll_freq = 320;
         } 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_pll_freq = 320;
         } 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_pll_freq = 480;
         }
         REG_SET_FIELD(RTC_CNTL_CLK_CONF_REG, RTC_CNTL_SOC_CLK_SEL, RTC_CNTL_SOC_CLK_SEL_PLL);
         ets_delay_us(DELAY_CPU_FREQ_SWITCH_TO_PLL);
         rtc_clk_apb_freq_update(80 * MHZ);
     }
+    s_cur_freq = cpu_freq;
 }
 
 rtc_cpu_freq_t rtc_clk_cpu_freq_get()