xtensa/esp32: Support to select different clock source for RTC controller and close TODOs.

This commit is contained in:
Sara Souza 2021-06-07 16:47:55 -03:00 committed by Xiang Xiao
parent a8cbcd084f
commit 87fabb2bc7
3 changed files with 300 additions and 47 deletions

View File

@ -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

View File

@ -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);
}
}

View File

@ -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);