diff --git a/arch/xtensa/src/esp32/Kconfig b/arch/xtensa/src/esp32/Kconfig index 29e0f980f2..e5b5298e83 100644 --- a/arch/xtensa/src/esp32/Kconfig +++ b/arch/xtensa/src/esp32/Kconfig @@ -211,6 +211,44 @@ config ESP32_XTAL_26MHz endchoice # On-board Crystal Frequency +choice ESP32_RTC_CLK_SRC + prompt "RTC clock source" + default ESP32_RTC_CLK_SRC_INT_RC + ---help--- + Choose which clock is used as RTC clock source. + + - "Internal 150kHz 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_XN 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_XP pin and + ground. 32K_XP pin can not be used as a GPIO in this case. + - "Internal 8.5MHz oscillator divided by 256" option results in higher + deep sleep current (by 5uA) but has better frequency stability than + the internal 150kHz oscillator. It does not require external components. + + config ESP32_RTC_CLK_SRC_INT_RC + bool "Internal 150kHz RC oscillator" + + config ESP32_RTC_CLK_SRC_EXT_XTAL + bool "External 32kHz crystal" + select ESP_SYSTEM_RTC_EXT_XTAL + + config ESP32_RTC_CLK_SRC_EXT_OSC + bool "External 32kHz oscillator at 32K_XN pin" + + config ESP32_RTC_CLK_SRC_INT_8MD256 + bool "Internal 8.5MHz oscillator, divided by 256 (~33kHz)" + +endchoice + config ESP32_RT_TIMER bool "Real-time Timer" default n diff --git a/arch/xtensa/src/esp32/esp32_rtc.c b/arch/xtensa/src/esp32/esp32_rtc.c index 47cd168f9a..b02c6d462e 100644 --- a/arch/xtensa/src/esp32/esp32_rtc.c +++ b/arch/xtensa/src/esp32/esp32_rtc.c @@ -70,12 +70,36 @@ #define DELAY_FAST_CLK_SWITCH 3 -#define XTAL_32K_DAC_VAL 3 +#define XTAL_32K_DAC_VAL 1 #define XTAL_32K_DRES_VAL 3 #define XTAL_32K_DBIAS_VAL 0 +#define XTAL_32K_EXT_DAC_VAL 2 +#define XTAL_32K_EXT_DRES_VAL 3 +#define XTAL_32K_EXT_DBIAS_VAL 1 + #define DELAY_SLOW_CLK_SWITCH 300 +#define DELAY_8M_ENABLE 50 + +#define RETRY_CAL_EXT 1 + +/* 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 + +/* 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 + /* Number of fractional bits in values returned by rtc_clk_cal */ #define RTC_CLK_CAL_FRACT 19 @@ -237,6 +261,9 @@ static uint32_t IRAM_ATTR esp32_rtc_clk_cal_internal( static void IRAM_ATTR esp32_rtc_clk_slow_freq_set( enum esp32_rtc_slow_freq_e slow_freq); static void esp32_select_rtc_slow_clk(enum esp32_slow_clk_sel_e slow_clk); +static void esp32_rtc_clk_32k_enable(int ac, int res, int bias); +static void IRAM_ATTR esp32_rtc_clk_8m_enable(bool clk_8m_en, bool d256_en); +static uint32_t IRAM_ATTR esp32_rtc_clk_slow_freq_get_hz(void); #ifdef CONFIG_RTC_DRIVER static void IRAM_ATTR esp32_rt_cb_handler(FAR void *arg); @@ -285,8 +312,6 @@ volatile bool g_rtc_enabled = false; * Private Functions ****************************************************************************/ -extern void ets_delay_us(uint32_t us); - /**************************************************************************** * Name: esp32_rtc_sleep_pd * @@ -338,7 +363,7 @@ static void IRAM_ATTR esp32_rtc_clk_fast_freq_set( enum esp32_rtc_fast_freq_e fast_freq) { REG_SET_FIELD(RTC_CNTL_CLK_CONF_REG, RTC_CNTL_FAST_CLK_RTC_SEL, fast_freq); - ets_delay_us(DELAY_FAST_CLK_SWITCH); + up_udelay(DELAY_FAST_CLK_SWITCH); } /**************************************************************************** @@ -375,7 +400,9 @@ static inline bool esp32_clk_val_is_valid(uint32_t val) * slowclk_cycles - number of slow clock cycles to count. * * Returned Value: - * Number of XTAL clock cycles within the given number of slow clock cycles + * Number of XTAL clock cycles within the given number of slow clock + * cycles. + * In case of error, return 0 cycle. * ****************************************************************************/ @@ -385,21 +412,26 @@ static uint32_t IRAM_ATTR esp32_rtc_clk_cal_internal( uint32_t expected_freq; uint32_t us_time_estimate; uint32_t us_timer_max; + uint32_t clks_state; + uint32_t clks_mask; int timeout_us; enum esp32_rtc_slow_freq_e slow_freq; enum esp32_rtc_xtal_freq_e xtal_freq; + /* Get the current state */ + + clks_mask = (RTC_CNTL_DIG_XTAL32K_EN_M | RTC_CNTL_DIG_CLK8M_D256_EN_M); + clks_state = getreg32(RTC_CNTL_CLK_CONF_REG); + clks_state &= clks_mask; + /* Enable requested clock (150k clock is always on) */ - int dig_32k_xtal_state = REG_GET_FIELD(RTC_CNTL_CLK_CONF_REG, - RTC_CNTL_DIG_XTAL32K_EN); - - if (cal_clk == RTC_CAL_32K_XTAL && !dig_32k_xtal_state) + if (cal_clk == RTC_CAL_32K_XTAL && !(clks_state & RTC_CNTL_DIG_XTAL32K_EN)) { REG_SET_FIELD(RTC_CNTL_CLK_CONF_REG, RTC_CNTL_DIG_XTAL32K_EN, 1); } - - if (cal_clk == RTC_CAL_8MD256) + else if (cal_clk == RTC_CAL_8MD256 && + !(clks_state & RTC_CNTL_DIG_CLK8M_D256_EN)) { modifyreg32(RTC_CNTL_CLK_CONF_REG, 0, RTC_CNTL_DIG_CLK8M_D256_EN); } @@ -414,13 +446,11 @@ static uint32_t IRAM_ATTR esp32_rtc_clk_cal_internal( slow_freq = REG_GET_FIELD(RTC_CNTL_CLK_CONF_REG, RTC_CNTL_ANA_CLK_RTC_SEL); - if (cal_clk == RTC_CAL_32K_XTAL || - (cal_clk == RTC_CAL_RTC_MUX && slow_freq == RTC_SLOW_FREQ_32K_XTAL)) + if (cal_clk == RTC_CAL_32K_XTAL || slow_freq == RTC_SLOW_FREQ_32K_XTAL) { expected_freq = 32768; /* standard 32k XTAL */ } - else if (cal_clk == RTC_CAL_8MD256 || - (cal_clk == RTC_CAL_RTC_MUX && slow_freq == RTC_SLOW_FREQ_8MD256)) + else if (cal_clk == RTC_CAL_8MD256 || slow_freq == RTC_SLOW_FREQ_8MD256) { expected_freq = RTC_FAST_CLK_FREQ_APPROX / 256; } @@ -448,6 +478,7 @@ static uint32_t IRAM_ATTR esp32_rtc_clk_cal_internal( if (us_time_estimate >= us_timer_max) { + rtcerr("Estimated time overflows TIMG_RTC_CALI_VALUE\n"); return 0; } @@ -456,12 +487,9 @@ static uint32_t IRAM_ATTR esp32_rtc_clk_cal_internal( modifyreg32(TIMG_RTCCALICFG_REG(0), TIMG_RTC_CALI_START, 0); modifyreg32(TIMG_RTCCALICFG_REG(0), 0, TIMG_RTC_CALI_START); - /* Wait the expected time calibration should take. - * TODO: if running under RTOS, and us_time_estimate > RTOS tick, use the - * RTOS delay function. - */ + /* Wait the expected time calibration should take */ - ets_delay_us(us_time_estimate); + up_udelay(us_time_estimate); /* Wait for calibration to finish up to another us_time_estimate */ @@ -470,21 +498,20 @@ static uint32_t IRAM_ATTR esp32_rtc_clk_cal_internal( TIMG_RTC_CALI_RDY) && (timeout_us > 0)) { timeout_us--; - ets_delay_us(1); + up_udelay(1); } - REG_SET_FIELD(RTC_CNTL_CLK_CONF_REG, RTC_CNTL_DIG_XTAL32K_EN, - dig_32k_xtal_state); + /* Restore the previous clocks states */ - if (cal_clk == RTC_CAL_8MD256) - { - modifyreg32(RTC_CNTL_CLK_CONF_REG, RTC_CNTL_DIG_CLK8M_D256_EN, 0); - } + modifyreg32(RTC_CNTL_CLK_CONF_REG, clks_mask, clks_state); + + /* Verify if this calibration occured within the timeout */ if (timeout_us == 0) { - /* timed out waiting for calibration */ + /* Timed out waiting for calibration */ + rtcerr("Timed out waiting for calibration\n"); return 0; } @@ -513,7 +540,116 @@ static void IRAM_ATTR esp32_rtc_clk_slow_freq_set( REG_SET_FIELD(RTC_CNTL_CLK_CONF_REG, RTC_CNTL_DIG_XTAL32K_EN, (slow_freq == RTC_SLOW_FREQ_32K_XTAL) ? 1 : 0); - ets_delay_us(DELAY_SLOW_CLK_SWITCH); + up_udelay(DELAY_SLOW_CLK_SWITCH); +} + +/**************************************************************************** + * Name: esp32_rtc_clk_32k_enable + * + * Description: + * Enable 32 kHz XTAL oscillator + * + * Input Parameters: + * ac - The current of XTAL oscillator. + * res - The resistance of XTAL oscillator. + * bias - The bias voltage of XTAL oscillator. + * + * Returned Value: + * None + * + ****************************************************************************/ + +static void esp32_rtc_clk_32k_enable(int ac, int res, int bias) +{ + modifyreg32(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_FUN_IE | + RTC_IO_X32P_FUN_IE, RTC_IO_X32N_MUX_SEL | RTC_IO_X32P_MUX_SEL); + + /* Set the parameters of xtal */ + + REG_SET_FIELD(RTC_IO_XTAL_32K_PAD_REG, RTC_IO_DAC_XTAL_32K, ac); + REG_SET_FIELD(RTC_IO_XTAL_32K_PAD_REG, RTC_IO_DRES_XTAL_32K, res); + REG_SET_FIELD(RTC_IO_XTAL_32K_PAD_REG, RTC_IO_DBIAS_XTAL_32K, bias); + + /* Power up external xtal */ + + modifyreg32(RTC_IO_XTAL_32K_PAD_REG, 0, RTC_IO_XPD_XTAL_32K_M); +} + +/**************************************************************************** + * Name: esp32_rtc_clk_8m_enable + * + * Description: + * Enable or disable 8 MHz internal oscillator + * + * Input Parameters: + * clk_8m_en - true to enable 8MHz generator, false to disable + * d256_en - true to enable /256 divider, false to disable + * + * Returned Value: + * None + * + ****************************************************************************/ + +static void IRAM_ATTR esp32_rtc_clk_8m_enable(bool clk_8m_en, bool d256_en) +{ + if (clk_8m_en) + { + modifyreg32(RTC_CNTL_CLK_CONF_REG, RTC_CNTL_ENB_CK8M, 0); + + /* no need to wait once enabled by software */ + + REG_SET_FIELD(RTC_CNTL_TIMER1_REG, RTC_CNTL_CK8M_WAIT, 1); + if (d256_en) + { + modifyreg32(RTC_CNTL_CLK_CONF_REG, RTC_CNTL_ENB_CK8M_DIV, 0); + } + else + { + modifyreg32(RTC_CNTL_CLK_CONF_REG, 0, RTC_CNTL_ENB_CK8M_DIV); + } + + up_udelay(DELAY_8M_ENABLE); + } + else + { + modifyreg32(RTC_CNTL_CLK_CONF_REG, 0, RTC_CNTL_ENB_CK8M); + REG_SET_FIELD(RTC_CNTL_TIMER1_REG, RTC_CNTL_CK8M_WAIT, + RTC_CNTL_CK8M_WAIT_DEFAULT); + } +} + +/**************************************************************************** + * Name: esp32_rtc_clk_slow_freq_get_hz + * + * Description: + * Get the approximate frequency of RTC_SLOW_CLK, in Hz + * + * Input Parameters: + * None + * + * Returned Value: + * slow_clk_freq - RTC_SLOW_CLK frequency, in Hz + * + ****************************************************************************/ + +static uint32_t IRAM_ATTR esp32_rtc_clk_slow_freq_get_hz(void) +{ + enum esp32_rtc_slow_freq_e slow_clk_freq = + REG_GET_FIELD(RTC_CNTL_CLK_CONF_REG, RTC_CNTL_ANA_CLK_RTC_SEL); + switch (slow_clk_freq) + { + 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 OK; } /**************************************************************************** @@ -532,22 +668,79 @@ static void IRAM_ATTR esp32_rtc_clk_slow_freq_set( static void esp32_select_rtc_slow_clk(enum esp32_slow_clk_sel_e slow_clk) { + /* Number of times to repeat 32k XTAL calibration before giving up and + * switching to the internal RC. + */ + + int retry_32k_xtal = RETRY_CAL_EXT; uint32_t cal_val = 0; + uint64_t cal_dividend; enum esp32_rtc_slow_freq_e rtc_slow_freq = slow_clk & RTC_CNTL_ANA_CLK_RTC_SEL_V; do { + 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 the number of main XTAL cycles in the given number of + * 32k XTAL oscillator cycles. If the 32k XTAL has not started up, + * calibration will time out, returning 0. + */ + + rtcinfo("Waiting for 32k oscillator to start up\n"); + if (slow_clk == SLOW_CLK_32K_XTAL) + { + esp32_rtc_clk_32k_enable(XTAL_32K_DAC_VAL, XTAL_32K_DRES_VAL, + XTAL_32K_DBIAS_VAL); + } + else if (slow_clk == SLOW_CLK_32K_EXT_OSC) + { + esp32_rtc_clk_32k_enable(XTAL_32K_EXT_DAC_VAL, + XTAL_32K_EXT_DRES_VAL, XTAL_32K_EXT_DBIAS_VAL); + } + + if (SLOW_CLK_CAL_CYCLES > 0) + { + cal_val = esp32_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; + } + + rtc_slow_freq = RTC_SLOW_FREQ_RTC; + } + } + } + else if (rtc_slow_freq == RTC_SLOW_FREQ_8MD256) + { + esp32_rtc_clk_8m_enable(true, true); + } + esp32_rtc_clk_slow_freq_set(rtc_slow_freq); + if (SLOW_CLK_CAL_CYCLES > 0) + { + /* 32k XTAL oscillator has some frequency drift at startup. Improve + * calibration routine to wait until the frequency is stable. + */ - /* TODO: 32k XTAL oscillator has some frequency drift at startup. - * Improve calibration routine to wait until the frequency is stable. - */ - - cal_val = esp32_rtc_clk_cal(RTC_CAL_RTC_MUX, SLOW_CLK_CAL_CYCLES); + cal_val = esp32_rtc_clk_cal(RTC_CAL_RTC_MUX, + SLOW_CLK_CAL_CYCLES); + } + else + { + cal_dividend = (1ULL << RTC_CLK_CAL_FRACT) * 1000000ULL; + cal_val = (uint32_t) (cal_dividend / + esp32_rtc_clk_slow_freq_get_hz()); + } } while (cal_val == 0); - + rtcinfo("RTC_SLOW_CLK calibration value: %d\n", cal_val); putreg32((uint32_t)cal_val, RTC_SLOW_CLK_CAL_REG); } @@ -867,7 +1060,7 @@ void IRAM_ATTR esp32_rtc_bbpll_configure( /* Raise the voltage */ REG_SET_FIELD(RTC_CNTL_REG, RTC_CNTL_DIG_DBIAS_WAK, DIG_DBIAS_240M); - ets_delay_us(DELAY_PLL_DBIAS_RAISE); + up_udelay(DELAY_PLL_DBIAS_RAISE); /* Configure 480M PLL */ @@ -948,7 +1141,16 @@ void IRAM_ATTR esp32_rtc_bbpll_configure( void esp32_rtc_clk_set(void) { enum esp32_rtc_fast_freq_e fast_freq = RTC_FAST_FREQ_8M; - enum esp32_slow_clk_sel_e slow_clk = RTC_SLOW_FREQ_RTC; + enum esp32_slow_clk_sel_e slow_clk = SLOW_CLK_150K; + +#if defined(CONFIG_ESP32_RTC_CLK_SRC_EXT_CRYS) + slow_clk = SLOW_CLK_32K_XTAL; +#elif defined(CONFIG_ESP32_RTC_CLK_SRC_EXT_OSC) + slow_clk = SLOW_CLK_32K_EXT_OSC; +#elif defined(CONFIG_ESP32_RTC_CLK_SRC_INT_8MD256) + slow_clk = SLOW_CLK_8MD256; +#endif + esp32_rtc_clk_fast_freq_set(fast_freq); esp32_select_rtc_slow_clk(slow_clk); } @@ -1094,7 +1296,7 @@ uint64_t IRAM_ATTR esp32_rtc_time_get(void) while ((getreg32(RTC_CNTL_TIME_UPDATE_REG) & RTC_CNTL_TIME_VALID) == 0) { - ets_delay_us(1); + up_udelay(1); } modifyreg32(RTC_CNTL_INT_CLR_REG, 0, RTC_CNTL_TIME_VALID_INT_CLR); @@ -1115,18 +1317,27 @@ uint64_t IRAM_ATTR esp32_rtc_time_get(void) * slow_clk_period - Period of slow clock in microseconds * * Returned Value: - * number of slow clock cycles + * Number of slow clock cycles * ****************************************************************************/ uint64_t IRAM_ATTR esp32_rtc_time_us_to_slowclk(uint64_t time_in_us, uint32_t period) { - /* Overflow will happen in this function if time_in_us >= 2^45, - * which is about 400 days. TODO: fix overflow. - */ + uint64_t slow_clk_cycles = 0; + uint64_t max_time_in_us = (UINT64_C(1) << 45) - 1; - return (time_in_us << RTC_CLK_CAL_FRACT) / period; + /* Handle overflow that would happen if time_in_us >= 2^45 */ + + while (time_in_us > max_time_in_us) + { + time_in_us -= max_time_in_us; + slow_clk_cycles += ((max_time_in_us << RTC_CLK_CAL_FRACT) / period); + } + + slow_clk_cycles += ((time_in_us << RTC_CLK_CAL_FRACT) / period); + + return slow_clk_cycles; } /**************************************************************************** @@ -1252,11 +1463,11 @@ void IRAM_ATTR esp32_rtc_wait_for_slow_cycle(void) /* RDY needs some time to go low */ - ets_delay_us(1); + up_udelay(1); while (!(getreg32(TIMG_RTCCALICFG_REG(0)) & TIMG_RTC_CALI_RDY)) { - ets_delay_us(1); + up_udelay(1); } } diff --git a/arch/xtensa/src/esp32/esp32_wdt.c b/arch/xtensa/src/esp32/esp32_wdt.c index 73b3c9fea6..1fff28787f 100644 --- a/arch/xtensa/src/esp32/esp32_wdt.c +++ b/arch/xtensa/src/esp32/esp32_wdt.c @@ -505,8 +505,8 @@ static int esp32_wdt_pre(FAR struct esp32_wdt_dev_s *dev, uint16_t pre) * Name: esp32_rtc_clk * * Description: - * Check the RTC clock source and return the necessary cycles to complete - * 1 ms. NOTE: TODO. + * Check the RTC clock source and return the necessary cycles to complete + * 1 ms. * ****************************************************************************/ @@ -528,6 +528,10 @@ static uint16_t esp32_rtc_clk(FAR struct esp32_wdt_dev_s *dev) period_13q19 = esp32_rtc_clk_cal(slow_clk_rtc, SLOW_CLK_CAL_CYCLES); + /* Assert no error happened during the calibration */ + + DEBUGASSERT(period_13q19 != 0); + /* Convert from Q13.19 format to float */ period = Q_TO_FLOAT(period_13q19);