#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.
#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;
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)
{
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)
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);
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);
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) {
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) {
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.
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) {
if (clk_val_is_valid(READ_PERI_REG(RTC_XTAL_FREQ_REG))) {
xtal_freq = rtc_clk_xtal_freq_get();
} else {
/* Not set yet, estimate XTAL frequency based on RTC_FAST_CLK */
- xtal_freq = rtc_clk_xtal_freq_estimate();
+ 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;
* 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)
-{
-
-}