/**************************************************************************** * arch/xtensa/src/esp32s2/esp32s2_rtc.c * * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. The * ASF licenses this file to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance with the * License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the * License for the specific language governing permissions and limitations * under the License. * ****************************************************************************/ /**************************************************************************** * Included Files ****************************************************************************/ #include #include #include #include #include #include "clock/clock.h" #include "esp32s2_clockconfig.h" #include "esp32s2_rt_timer.h" #include "hardware/esp32s2_rtccntl.h" #include "hardware/esp32s2_rtc_io.h" #include "hardware/esp32s2_system.h" #include "hardware/esp32s2_i2s.h" #include "hardware/esp32s2_rtccntl.h" #include "hardware/esp32s2_rtc_io.h" #include "hardware/esp32s2_system.h" #include "hardware/esp32s2_tim.h" #include "hardware/regi2c_ctrl.h" #include "hardware/esp32s2_spi_mem_reg.h" #include "hardware/esp32s2_extmem.h" #include "hardware/esp32s2_syscon.h" #include "hardware/regi2c_bbpll.h" #include "hardware/regi2c_lp_bias.h" #include "xtensa.h" #include "xtensa_attr.h" #include "esp32s2_rtc.h" /**************************************************************************** * Pre-processor Definitions ****************************************************************************/ /* Various delays to be programmed into power control state machines */ #define RTC_CNTL_XTL_BUF_WAIT_SLP 2 #define RTC_CNTL_CK8M_WAIT_SLP 4 #define OTHER_BLOCKS_POWERUP 1 #define OTHER_BLOCKS_WAIT 1 #define ROM_RAM_POWERUP_CYCLES OTHER_BLOCKS_POWERUP #define ROM_RAM_WAIT_CYCLES OTHER_BLOCKS_WAIT #define WIFI_POWERUP_CYCLES OTHER_BLOCKS_POWERUP #define WIFI_WAIT_CYCLES OTHER_BLOCKS_WAIT #define RTC_POWERUP_CYCLES OTHER_BLOCKS_POWERUP #define RTC_WAIT_CYCLES OTHER_BLOCKS_WAIT #define DG_WRAP_POWERUP_CYCLES OTHER_BLOCKS_POWERUP #define DG_WRAP_WAIT_CYCLES OTHER_BLOCKS_WAIT #define RTC_MEM_POWERUP_CYCLES OTHER_BLOCKS_POWERUP #define RTC_MEM_WAIT_CYCLES OTHER_BLOCKS_WAIT #define RTC_CNTL_PLL_BUF_WAIT_SLP 2 #define DELAY_FAST_CLK_SWITCH 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 32KHz 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_90K 90000 #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 /* With the default value of CK8M_DFREQ, * 8M clock frequency is 8.5 MHz +/- 7% */ #define RTC_FAST_CLK_FREQ_APPROX 8500000 #define RCT_FAST_D256_FREQ_APPROX (RTC_FAST_CLK_FREQ_APPROX / 256) #define RTC_SLOW_CLK_FREQ_APPROX 32768 /* Disable logging from the ROM code. */ #define RTC_DISABLE_ROM_LOG ((1 << 0) | (1 << 16)) #define RTC_SLEEP_PD_DIG BIT(0) /* Deep sleep (power down digital domain) */ #define RTC_SLEEP_PD_RTC_PERIPH BIT(1) /* Power down RTC peripherals */ #define RTC_SLEEP_PD_RTC_SLOW_MEM BIT(2) /* Power down RTC SLOW memory */ #define RTC_SLEEP_PD_RTC_FAST_MEM BIT(3) /* Power down RTC FAST memory */ #define RTC_SLEEP_PD_RTC_MEM_FOLLOW_CPU BIT(4) /* RTC FAST and SLOW memories are automatically powered up and down along with the CPU */ #define RTC_SLEEP_PD_VDDSDIO BIT(5) /* Power down VDDSDIO regulator */ #define RTC_SLEEP_PD_WIFI BIT(6) /* Power down WIFI */ #define RTC_SLEEP_PD_INT_8M BIT(7) /* Power down Internal 8M oscillator */ #define RTC_SLEEP_PD_XTAL BIT(8) /* Power down main XTAL */ /* These flags are not power domains, but will affect some sleep parameters */ #define RTC_SLEEP_DIG_USE_8M BIT(16) #define RTC_SLEEP_USE_ADC_TESEN_MONITOR BIT(17) #define RTC_SLEEP_NO_ULTRA_LOW BIT(18) /* Avoid using ultra low power in deep sleep, * in which RTCIO cannot be used as input, * and RTCMEM can't work under high temperature */ #define is_dslp(pd_flags) ((pd_flags) & RTC_SLEEP_PD_DIG) /* set sleep_init default param. */ #define RTC_CNTL_DBG_ATTEN_LIGHTSLEEP_DEFAULT 6 #define RTC_CNTL_DBG_ATTEN_LIGHTSLEEP_NODROP 0 #define RTC_CNTL_DBG_ATTEN_DEEPSLEEP_DEFAULT 15 #define RTC_CNTL_DBG_ATTEN_DEEPSLEEP_NODROP 0 #define RTC_CNTL_BIASSLP_SLEEP_DEFAULT 1 #define RTC_CNTL_BIASSLP_SLEEP_ON 0 #define RTC_CNTL_PD_CUR_SLEEP_DEFAULT 1 #define RTC_CNTL_PD_CUR_SLEEP_ON 0 #define RTC_CNTL_DBG_ATTEN_MONITOR_DEFAULT 0 #define RTC_CNTL_BIASSLP_MONITOR_DEFAULT 1 #define RTC_CNTL_BIASSLP_MONITOR_ON 0 #define RTC_CNTL_PD_CUR_MONITOR_DEFAULT 1 #define RTC_CNTL_PD_CUR_MONITOR_ON 0 /* Approximate mapping of voltages to RTC_CNTL_DBIAS_WAK, RTC_CNTL_DBIAS_SLP, * RTC_CNTL_DIG_DBIAS_WAK, RTC_CNTL_DIG_DBIAS_SLP values. * Valid if RTC_CNTL_DBG_ATTEN is 0. */ #define RTC_CNTL_DBIAS_0V90 0 /* sleep dig_dbias & rtc_dbias */ #define RTC_CNTL_DBIAS_0V95 1 /* digital voltage */ #define RTC_CNTL_DBIAS_1V00 2 #define RTC_CNTL_DBIAS_1V05 3 #define RTC_CNTL_DBIAS_1V10 4 #define RTC_CNTL_DBIAS_1V15 5 #define RTC_CNTL_DBIAS_1V20 6 #define RTC_CNTL_DBIAS_1V25 7 /* voltage is about 1.34v in fact */ /* Default initializer for esp32s2_rtc_sleep_config_t * This initializer sets all fields to "reasonable" values * (e.g. suggested for production use) based on a combination * of RTC_SLEEP_PD_x flags. */ #define RTC_SLEEP_CONFIG_DEFAULT(sleep_flags) { \ .lslp_mem_inf_fpu = 0, \ .rtc_mem_inf_follow_cpu = ((sleep_flags) & \ RTC_SLEEP_PD_RTC_MEM_FOLLOW_CPU) ? 1 : 0, \ .rtc_fastmem_pd_en = ((sleep_flags) & RTC_SLEEP_PD_RTC_FAST_MEM) ? 1 : 0, \ .rtc_slowmem_pd_en = ((sleep_flags) & RTC_SLEEP_PD_RTC_SLOW_MEM) ? 1 : 0, \ .rtc_peri_pd_en = ((sleep_flags) & RTC_SLEEP_PD_RTC_PERIPH) ? 1 : 0, \ .wifi_pd_en = (sleep_flags & RTC_SLEEP_PD_WIFI) ? 1 : 0, \ .int_8m_pd_en = (sleep_flags & RTC_SLEEP_PD_INT_8M) ? 1 : 0, \ .deep_slp = ((sleep_flags) & RTC_SLEEP_PD_DIG) ? 1 : 0, \ .wdt_flashboot_mod_en = 0, \ .dig_dbias_wak = RTC_CNTL_DBIAS_1V10, \ .dig_dbias_slp = is_dslp(sleep_flags) ? RTC_CNTL_DBIAS_SLP \ : !((sleep_flags) & RTC_SLEEP_PD_INT_8M) ? \ RTC_CNTL_DBIAS_1V10 : RTC_CNTL_DBIAS_SLP, \ .rtc_dbias_slp = is_dslp(sleep_flags) ? RTC_CNTL_DBIAS_SLP \ : !((sleep_flags) & RTC_SLEEP_PD_INT_8M) ? \ RTC_CNTL_DBIAS_1V10 : RTC_CNTL_DBIAS_SLP, \ .bias_sleep_monitor = 0, \ .dbg_atten_slp = 0, \ .pd_cur_monitor = 0, \ .pd_cur_slp = 0, \ .rtc_regulator_fpu = 0, \ .rtc_dbias_wak = RTC_CNTL_DBIAS_1V10, \ .vddsdio_pd_en = ((sleep_flags) & RTC_SLEEP_PD_VDDSDIO) ? 1 : 0, \ .xtal_fpu = ((sleep_flags) & RTC_SLEEP_PD_XTAL) ? 0 : 1, \ .deep_slp_reject = 1, \ .light_slp_reject = 1, \ } #define X32K_CONFIG_DEFAULT() { \ .dac = 3, \ .dres = 3, \ .dgm = 3, \ .dbuf = 1, \ } /* Initializer for rtc_sleep_pd_config_t which * sets all flags to the same value */ #define RTC_SLEEP_PD_CONFIG_ALL(val) { \ .dig_fpu = (val), \ .rtc_fpu = (val), \ .cpu_fpu = (val), \ .i2s_fpu = (val), \ .bb_fpu = (val), \ .nrx_fpu = (val), \ .fe_fpu = (val), \ } /* Default initializer of struct esp32s2_rtc_config_s. * This initializer sets all fields to "reasonable" values * (e.g. suggested for production use). */ #define RTC_CONFIG_DEFAULT() { \ .ck8m_wait = RTC_CNTL_CK8M_WAIT_DEFAULT, \ .xtal_wait = RTC_CNTL_XTL_BUF_WAIT_DEFAULT, \ .pll_wait = RTC_CNTL_PLL_BUF_WAIT_DEFAULT, \ .clkctl_init = 1, \ .pwrctl_init = 1, \ .rtc_dboost_fpd = 1, \ .xtal_fpu = 0, \ .bbpll_fpu = 0, \ .cpu_waiti_clk_gate = 1, \ .cali_ocode = 0 \ } /* The magic data for the struct esp32s2_rtc_backup_s that is in RTC slow * memory. */ #define MAGIC_RTC_SAVE UINT64_C(0x11223344556677) /* RTC Memory & Store Register usage */ #define RTC_SLOW_CLK_CAL_REG RTC_CNTL_STORE1_REG /* RTC_SLOW_CLK calibration value */ #define RTC_BOOT_TIME_LOW_REG RTC_CNTL_STORE2_REG /* Boot time, low word */ #define RTC_BOOT_TIME_HIGH_REG RTC_CNTL_STORE3_REG /* Boot time, high word */ #define RTC_XTAL_FREQ_REG RTC_CNTL_STORE4_REG /* External XTAL frequency */ #define RTC_APB_FREQ_REG RTC_CNTL_STORE5_REG /* APB bus frequency */ #define RTC_ENTRY_ADDR_REG RTC_CNTL_STORE6_REG /* FAST_RTC_MEMORY_ENTRY */ #define RTC_RESET_CAUSE_REG RTC_CNTL_STORE6_REG #define RTC_MEMORY_CRC_REG RTC_CNTL_STORE7_REG /* FAST_RTC_MEMORY_CRC */ /**************************************************************************** * Private Types ****************************************************************************/ /* RTC power and clock control initialization settings */ struct esp32s2_rtc_priv_s { uint32_t ck8m_wait : 8; /* Number of rtc_fast_clk cycles to wait for 8M clock to be ready */ uint32_t xtal_wait : 8; /* Number of rtc_fast_clk cycles to wait for XTAL clock to be ready */ uint32_t pll_wait : 8; /* Number of rtc_fast_clk cycles to wait for PLL to be ready */ uint32_t clkctl_init : 1; /* Perform clock control related initialization */ uint32_t pwrctl_init : 1; /* Perform power control related initialization */ uint32_t rtc_dboost_fpd : 1; /* Force power down RTC_DBOOST */ uint32_t xtal_fpu : 1; uint32_t bbpll_fpu : 1; uint32_t cpu_waiti_clk_gate : 1; uint32_t cali_ocode : 1; /* Calibrate Ocode to make bangap voltage more precise. */ }; /* sleep configuration for rtc_sleep_init function */ struct esp32s2_rtc_sleep_config_s { uint32_t lslp_mem_inf_fpu : 1; /* force normal voltage in sleep mode (digital domain memory) */ uint32_t rtc_mem_inf_follow_cpu : 1; /* keep low voltage in sleep mode (even if ULP/touch is used) */ uint32_t rtc_fastmem_pd_en : 1; /* power down RTC fast memory */ uint32_t rtc_slowmem_pd_en : 1; /* power down RTC slow memory */ uint32_t rtc_peri_pd_en : 1; /* power down RTC peripherals */ uint32_t wifi_pd_en : 1; /* power down WiFi */ uint32_t int_8m_pd_en : 1; /* Power down Internal 8M oscillator */ uint32_t deep_slp : 1; /* power down digital domain */ uint32_t wdt_flashboot_mod_en : 1; /* enable WDT flashboot mode */ uint32_t dig_dbias_wak : 3; /* set bias for digital domain, in active mode */ uint32_t dig_dbias_slp : 3; /* set bias for digital domain, in sleep mode */ uint32_t rtc_dbias_wak : 3; /* set bias for RTC domain, in active mode */ uint32_t rtc_dbias_slp : 3; /* set bias for RTC domain, in sleep mode */ uint32_t bias_sleep_monitor : 1; /* circuit control parameter, in monitor mode */ uint32_t dbg_atten_slp : 4; /* voltage parameter, in sleep mode */ uint32_t bias_sleep_slp : 1; /* circuit control parameter, in sleep mode */ uint32_t pd_cur_monitor : 1; /* circuit control parameter, in monitor mode */ uint32_t pd_cur_slp : 1; /* circuit control parameter, in sleep mode */ uint32_t vddsdio_pd_en : 1; /* power down VDDSDIO regulator */ uint32_t xtal_fpu : 1; /* keep main XTAL powered up in sleep */ uint32_t rtc_regulator_fpu : 1; /* keep rtc regulator powered up in sleep */ uint32_t deep_slp_reject : 1; /* enable deep sleep reject */ uint32_t light_slp_reject : 1; /* enable light sleep reject */ }; /* Power down flags for rtc_sleep_pd function */ struct esp32s2_rtc_sleep_pd_config_s { uint32_t dig_fpu : 1; /* Set to 1 to power down digital part in sleep */ uint32_t rtc_fpu : 1; /* Set to 1 to power down RTC memories in sleep */ uint32_t cpu_fpu : 1; /* Set to 1 to power down digital memories and CPU in sleep */ uint32_t i2s_fpu : 1; /* Set to 1 to power down I2S in sleep */ uint32_t bb_fpu : 1; /* Set to 1 to power down Wi-Fi in sleep */ uint32_t nrx_fpu : 1; /* Set to 1 to power down Wi-Fi in sleep */ uint32_t fe_fpu : 1; /* Set to 1 to power down Wi-Fi in sleep */ }; #ifdef CONFIG_RTC_ALARM struct alm_cbinfo_s { struct rt_timer_s *alarm_hdl; /* Timer id point to here */ volatile alm_callback_t ac_cb; /* Client callback function */ volatile void *ac_arg; /* Argument to pass with the callback function */ uint64_t deadline_us; uint8_t index; }; #endif /* crystal configuration */ struct esp32s2_rtc_x32k_config_s { uint32_t dac : 6; uint32_t dres : 3; uint32_t dgm : 3; uint32_t dbuf: 1; }; struct esp32s2_rtc_backup_s { uint64_t magic; int64_t offset; /* Offset time from RTC HW value */ int64_t reserved0; }; /**************************************************************************** * Private Data ****************************************************************************/ /* APB Frequency */ static uint32_t g_apb_freq; /* Callback to use when the alarm expires */ #ifdef CONFIG_RTC_ALARM static struct alm_cbinfo_s g_alarmcb[RTC_ALARM_LAST]; #endif static RTC_DATA_ATTR struct esp32s2_rtc_backup_s rtc_saved_data; /* Saved data for persistent RTC time */ static struct esp32s2_rtc_backup_s *g_rtc_save; static bool g_rt_timer_enabled = false; /**************************************************************************** * Private Function Prototypes ****************************************************************************/ static void IRAM_ATTR esp32s2_rtc_sleep_pd( struct esp32s2_rtc_sleep_pd_config_s cfg); static inline bool esp32s2_clk_val_is_valid(uint32_t val); static void IRAM_ATTR esp32s2_rtc_clk_fast_freq_set( enum esp32s2_rtc_fast_freq_e fast_freq); static uint32_t IRAM_ATTR esp32s2_rtc_clk_cal_internal( enum esp32s2_rtc_cal_sel_e cal_clk, uint32_t slowclk_cycles); static int IRAM_ATTR esp32s2_rtc_clk_slow_freq_get(void); static void IRAM_ATTR esp32s2_rtc_clk_slow_freq_set( enum esp32s2_rtc_slow_freq_e slow_freq); static void esp32s2_select_rtc_slow_clk(enum esp32s2_slow_clk_sel_e slow_clk); static void esp32s2_rtc_clk_32k_enable(bool enable); static void IRAM_ATTR esp32s2_rtc_clk_8m_enable(bool clk_8m_en, bool d256_en); static void esp32s2_rtc_calibrate_ocode(void); static void IRAM_ATTR esp32s2_rtc_clk_bbpll_disable(void); static void IRAM_ATTR esp32s2_rtc_bbpll_configure( enum esp32s2_rtc_xtal_freq_e xtal_freq, int pll_freq); static void IRAM_ATTR esp32s2_rtc_clk_cpu_freq_to_8m(void); static void IRAM_ATTR esp32s2_rtc_clk_cpu_freq_to_pll_mhz(int cpu_freq_mhz); void IRAM_ATTR esp32s2_rtc_bbpll_disable(void); void esp32s2_rtc_clk_apb_freq_update(uint32_t apb_freq); void IRAM_ATTR esp32s2_rtc_update_to_xtal(int freq, int div); static void esp32s2_wait_dig_dbias_valid(uint64_t rtc_cycles); uint32_t esp32s2_rtc_clk_apb_freq_get(void); #ifdef CONFIG_RTC_ALARM static void IRAM_ATTR esp32s2_rt_cb_handler(void *arg); #endif /**************************************************************************** * Public Data ****************************************************************************/ volatile bool g_rtc_enabled = false; /**************************************************************************** * Private Functions ****************************************************************************/ /* Set the real CPU ticks per us to the ets, so that ets_delay_us * will be accurate. Call this function when CPU frequency is changed. */ extern void ets_update_cpu_frequency(uint32_t ticks_per_us); /**************************************************************************** * Name: esp32s2_rtc_sleep_pd * * Description: * Configure whether certain peripherals are powered up in deep sleep. * * Input Parameters: * cfg - power down flags as rtc_sleep_pu_config_t structure * * Returned Value: * None * ****************************************************************************/ static void IRAM_ATTR esp32s2_rtc_sleep_pd(struct esp32s2_rtc_sleep_pd_config_s cfg) { REG_SET_FIELD(RTC_CNTL_DIG_PWC_REG, RTC_CNTL_LSLP_MEM_FORCE_PU, cfg.dig_fpu); REG_SET_FIELD(RTC_CNTL_PWC_REG, RTC_CNTL_FASTMEM_FORCE_LPU, cfg.rtc_fpu); REG_SET_FIELD(RTC_CNTL_PWC_REG, RTC_CNTL_SLOWMEM_FORCE_LPU, cfg.rtc_fpu); REG_SET_FIELD(SYSCON_FRONT_END_MEM_PD_REG, SYSCON_DC_MEM_FORCE_PU, cfg.fe_fpu); REG_SET_FIELD(SYSCON_FRONT_END_MEM_PD_REG, SYSCON_PBUS_MEM_FORCE_PU, cfg.fe_fpu); REG_SET_FIELD(SYSCON_FRONT_END_MEM_PD_REG, SYSCON_AGC_MEM_FORCE_PU, cfg.fe_fpu); REG_SET_FIELD(BBPD_CTRL, BB_FFT_FORCE_PU, cfg.bb_fpu); REG_SET_FIELD(BBPD_CTRL, BB_DC_EST_FORCE_PU, cfg.bb_fpu); REG_SET_FIELD(NRXPD_CTRL, NRX_RX_ROT_FORCE_PU, cfg.nrx_fpu); REG_SET_FIELD(NRXPD_CTRL, NRX_VIT_FORCE_PU, cfg.nrx_fpu); REG_SET_FIELD(NRXPD_CTRL, NRX_DEMAP_FORCE_PU, cfg.nrx_fpu); REG_SET_FIELD(FE_GEN_CTRL, FE_IQ_EST_FORCE_PU, cfg.fe_fpu); REG_SET_FIELD(FE2_TX_INTERP_CTRL, FE2_TX_INF_FORCE_PU, cfg.fe_fpu); } /**************************************************************************** * Name: esp32s2_rtc_clk_fast_freq_set * * Description: * Select source for RTC_FAST_CLK. * * Input Parameters: * fast_freq - Clock source (one of enum esp32s2_rtc_fast_freq_e values) * * Returned Value: * None * ****************************************************************************/ static void IRAM_ATTR esp32s2_rtc_clk_fast_freq_set( enum esp32s2_rtc_fast_freq_e fast_freq) { REG_SET_FIELD(RTC_CNTL_CLK_CONF_REG, RTC_CNTL_FAST_CLK_RTC_SEL, fast_freq); up_udelay(DELAY_FAST_CLK_SWITCH); } /**************************************************************************** * Name: esp32s2_clk_val_is_valid * * Description: * Values of RTC_XTAL_FREQ_REG and RTC_APB_FREQ_REG are * stored as two copies in lower and upper 16-bit halves. * These are the routines to work with such a representation. * * Input Parameters: * val - register value * * Returned Value: * true: Valid register value. * false: Invalid register value. * ****************************************************************************/ static inline bool esp32s2_clk_val_is_valid(uint32_t val) { return (val & 0xffff) == ((val >> 16) & 0xffff) && val != 0 && val != UINT32_MAX; } /**************************************************************************** * Name: esp32s2_rtc_clk_cal_internal * * Description: * Clock calibration function used by rtc_clk_cal and rtc_clk_cal_ratio * * Input Parameters: * cal_clk - which clock to calibrate * slowclk_cycles - number of slow clock cycles to count. * * Returned Value: * Number of XTAL clock cycles within the given number of slow clock * cycles. * In case of error, return 0 cycle. * ****************************************************************************/ static uint32_t IRAM_ATTR esp32s2_rtc_clk_cal_internal( enum esp32s2_rtc_cal_sel_e cal_clk, uint32_t slowclk_cycles) { uint32_t expected_freq; uint32_t us_time_estimate; uint32_t clks_state; uint32_t clks_mask; uint32_t cal_val; enum esp32s2_rtc_slow_freq_e slow_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; /* On ESP32S2, choosing RTC_CAL_RTC_MUX results in calibration of * the 150k RTC clock regardless of the currenlty selected SLOW_CLK. * The following code emulates ESP32 behavior */ if (cal_clk == RTC_CAL_RTC_MUX) { slow_freq = esp32s2_rtc_clk_slow_freq_get(); if (slow_freq == RTC_SLOW_FREQ_32K_XTAL) { cal_clk = RTC_CAL_32K_XTAL; } else if (slow_freq == RTC_SLOW_FREQ_8MD256) { cal_clk = RTC_CAL_8MD256; } } else if (cal_clk == RTC_CAL_INTERNAL_OSC) { cal_clk = RTC_CAL_RTC_MUX; } /* Enable requested clock (150k clock is always on) */ 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); } 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); } /* Prepare calibration */ REG_SET_FIELD(TIMG_RTCCALICFG_REG(0), TIMG_RTC_CALI_CLK_SEL, cal_clk); modifyreg32(TIMG_RTCCALICFG_REG(0), TIMG_RTC_CALI_START_CYCLING, 0); REG_SET_FIELD(TIMG_RTCCALICFG_REG(0), TIMG_RTC_CALI_MAX, slowclk_cycles); /* Figure out how long to wait for calibration to finish */ slow_freq = REG_GET_FIELD(RTC_CNTL_CLK_CONF_REG, RTC_CNTL_ANA_CLK_RTC_SEL); if (cal_clk == RTC_CAL_32K_XTAL || slow_freq == RTC_SLOW_FREQ_32K_XTAL) { expected_freq = 32768; /* standard 32KHz XTAL */ } else if (cal_clk == RTC_CAL_8MD256 || slow_freq == RTC_SLOW_FREQ_8MD256) { expected_freq = RTC_FAST_CLK_FREQ_APPROX / 256; } else { expected_freq = 150000; /* 150k internal oscillator */ } us_time_estimate = (uint32_t) (((uint64_t)slowclk_cycles) * MHZ / expected_freq); /* Start calibration */ 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 */ up_udelay(us_time_estimate); /* Wait for calibration to finish up to another us_time_estimate */ while (true) { if (getreg32(TIMG_RTCCALICFG_REG(0)) & TIMG_RTC_CALI_RDY) { cal_val = REG_GET_FIELD(TIMG_RTCCALICFG1_REG(0), TIMG_RTC_CALI_VALUE); break; } if (GET_PERI_REG_MASK(TIMG_RTCCALICFG2_REG(0), TIMG_RTC_CALI_TIMEOUT)) { cal_val = 0; break; } } CLEAR_PERI_REG_MASK(TIMG_RTCCALICFG_REG(0), TIMG_RTC_CALI_START); /* Restore the previous clocks states */ modifyreg32(RTC_CNTL_CLK_CONF_REG, clks_mask, clks_state); return cal_val; } static void esp32s2_wait_dig_dbias_valid(uint64_t rtc_cycles) { int slow_clk_freq = esp32s2_rtc_clk_slow_freq_get(); int cal_clk = RTC_CAL_RTC_MUX; if (slow_clk_freq == RTC_SLOW_FREQ_32K_XTAL) { cal_clk = RTC_CAL_32K_XTAL; } else if (slow_clk_freq == RTC_SLOW_FREQ_8MD256) { cal_clk = RTC_CAL_8MD256; } esp32s2_rtc_clk_cal(cal_clk, rtc_cycles); } /**************************************************************************** * Name: esp32s2_rtc_update_to_xtal * * Description: * Switch to XTAL frequency, does not disable the PLL * * Input Parameters: * freq - XTAL frequency * div - REF_TICK divider * * Returned Value: * none * ****************************************************************************/ void IRAM_ATTR esp32s2_rtc_update_to_xtal(int freq, int div) { ets_update_cpu_frequency(freq); esp32s2_wait_dig_dbias_valid(2); /* Set divider from XTAL to APB clock. * Need to set divider to 1 (reg. value 0) first. */ REG_SET_FIELD(SYSTEM_SYSCLK_CONF_REG, SYSTEM_PRE_DIV_CNT, 0); REG_SET_FIELD(SYSTEM_SYSCLK_CONF_REG, SYSTEM_PRE_DIV_CNT, div - 1); /* No need to adjust the REF_TICK. * Switch clock source. */ REG_SET_FIELD(SYSTEM_SYSCLK_CONF_REG, SYSTEM_SOC_CLK_SEL, DPORT_SOC_CLK_SEL_XTAL); esp32s2_rtc_clk_apb_freq_update(freq * MHZ); } /**************************************************************************** * Name: esp32s2_rtc_clk_slow_freq_set * * Description: * Select source for RTC_SLOW_CLK * * Input Parameters: * slow_freq - Select source for RTC_SLOW_CLK * * Returned Value: * None * ****************************************************************************/ static void IRAM_ATTR esp32s2_rtc_clk_slow_freq_set( enum esp32s2_rtc_slow_freq_e slow_freq) { REG_SET_FIELD(RTC_CNTL_CLK_CONF_REG, RTC_CNTL_ANA_CLK_RTC_SEL, slow_freq); REG_SET_FIELD(RTC_CNTL_CLK_CONF_REG, RTC_CNTL_DIG_XTAL32K_EN, (slow_freq == RTC_SLOW_FREQ_32K_XTAL) ? 1 : 0); up_udelay(DELAY_SLOW_CLK_SWITCH); } /**************************************************************************** * Name: esp32s2_rtc_clk_32k_enable * * Description: * Enable 32 KHz XTAL oscillator * * Input Parameters: * enable - boolean Enable/Disable * * Returned Value: * None * ****************************************************************************/ static void IRAM_ATTR esp32s2_rtc_clk_32k_enable(bool enable) { if (enable) { struct esp32s2_rtc_x32k_config_s cfg = X32K_CONFIG_DEFAULT(); modifyreg32(RTCIO_XTAL_32P_PAD_REG, 0, RTCIO_X32P_MUX_SEL); modifyreg32(RTCIO_XTAL_32N_PAD_REG, 0, RTCIO_X32N_MUX_SEL); REG_SET_FIELD(RTC_CNTL_EXT_XTL_CONF_REG, RTC_CNTL_DAC_XTAL_32K, cfg.dac); REG_SET_FIELD(RTC_CNTL_EXT_XTL_CONF_REG, RTC_CNTL_DRES_XTAL_32K, cfg.dres); REG_SET_FIELD(RTC_CNTL_EXT_XTL_CONF_REG, RTC_CNTL_DGM_XTAL_32K, cfg.dgm); REG_SET_FIELD(RTC_CNTL_EXT_XTL_CONF_REG, RTC_CNTL_DBUF_XTAL_32K, cfg.dbuf); modifyreg32(RTC_CNTL_EXT_XTL_CONF_REG, 0, RTC_CNTL_XPD_XTAL_32K); } else { modifyreg32(RTC_CNTL_EXT_XTL_CONF_REG, RTC_CNTL_XPD_XTAL_32K, RTC_CNTL_XTAL32K_XPD_FORCE); } } /**************************************************************************** * Name: esp32s2_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 esp32s2_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: esp32s2_select_rtc_slow_clk * * Description: * Selects an clock source for RTC. * * Input Parameters: * slow_clk - RTC SLOW_CLK frequency values * * Returned Value: * None * ****************************************************************************/ static void esp32s2_select_rtc_slow_clk(enum esp32s2_slow_clk_sel_e slow_clk) { /* Number of times to repeat 32KHz XTAL calibration before giving up and * switching to the internal RC. */ int retry_32k_xtal = 0; uint32_t cal_val = 0; uint64_t cal_dividend; enum esp32s2_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) { /* 32KHz 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 * 32KHz XTAL oscillator cycles. If the 32KHz XTAL has not * started up, calibration will time out, returning 0. */ rtcinfo("Waiting for 32KHz oscillator to start up\n"); if (slow_clk == SLOW_CLK_32K_XTAL || slow_clk == SLOW_CLK_32K_EXT_OSC) { esp32s2_rtc_clk_32k_enable(true); } if (SLOW_CLK_CAL_CYCLES > 0) { cal_val = esp32s2_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) { esp32s2_rtc_clk_8m_enable(true, true); } esp32s2_rtc_clk_slow_freq_set(rtc_slow_freq); if (SLOW_CLK_CAL_CYCLES > 0) { /* 32KHz XTAL oscillator has some frequency drift at startup. * Improve calibration routine to wait until * the frequency is stable. */ cal_val = esp32s2_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 / esp32s2_rtc_clk_slow_freq_get_hz()); } retry_32k_xtal++; } while (cal_val == 0 && retry_32k_xtal < RETRY_CAL_EXT); rtcinfo("RTC_SLOW_CLK calibration value: %d\n", cal_val); putreg32((uint32_t)cal_val, RTC_SLOW_CLK_CAL_REG); } /**************************************************************************** * Name: esp32s2_rtc_clk_cpu_freq_to_8m * * Description: * Switch CPU frequency to 8 Mhz. * * Input Parameters: * None * * Returned Value: * None * ****************************************************************************/ static void IRAM_ATTR esp32s2_rtc_clk_cpu_freq_to_8m(void) { int origin_soc_clk = REG_GET_FIELD(SYSTEM_SYSCLK_CONF_REG, SYSTEM_SOC_CLK_SEL); int origin_div_cnt = REG_GET_FIELD(SYSTEM_SYSCLK_CONF_REG, SYSTEM_PRE_DIV_CNT); ets_update_cpu_frequency(8); REG_SET_FIELD(RTC_CNTL_REG, RTC_CNTL_DIG_DBIAS_WAK, DIG_DBIAS_XTAL); esp32s2_wait_dig_dbias_valid(2); if ((DPORT_SOC_CLK_SEL_XTAL == origin_soc_clk) && (origin_div_cnt > 4)) { esp32s2_wait_dig_dbias_valid(2); } REG_SET_FIELD(SYSTEM_SYSCLK_CONF_REG, SYSTEM_PRE_DIV_CNT, 0); REG_SET_FIELD(SYSTEM_SYSCLK_CONF_REG, SYSTEM_SOC_CLK_SEL, DPORT_SOC_CLK_SEL_8M); esp32s2_rtc_clk_apb_freq_update(RTC_FAST_CLK_FREQ_APPROX); } /**************************************************************************** * Name: esp32s2_rtc_clk_cpu_freq_to_pll_mhz * * Description: * Switch to one of PLL-based frequencies. * * Input Parameters: * cpu_freq_mhz - CPU frequency * * Returned Value: * None * ****************************************************************************/ static void IRAM_ATTR esp32s2_rtc_clk_cpu_freq_to_pll_mhz(int cpu_freq_mhz) { int origin_soc_clk = REG_GET_FIELD(SYSTEM_SYSCLK_CONF_REG, SYSTEM_SOC_CLK_SEL); int origin_cpuperiod_sel = REG_GET_FIELD(SYSTEM_CPU_PER_CONF_REG, SYSTEM_CPUPERIOD_SEL); int dbias = DIG_DBIAS_80M_160M; int per_conf = DPORT_CPUPERIOD_SEL_80; if (cpu_freq_mhz == 80) { /* Nothing to do */ } else if (cpu_freq_mhz == 160) { dbias = DIG_DBIAS_80M_160M; per_conf = DPORT_CPUPERIOD_SEL_160; } else if (cpu_freq_mhz == 240) { dbias = DIG_DBIAS_240M; per_conf = DPORT_CPUPERIOD_SEL_240; } else { rtcerr("Invalid frequency\n"); } REG_SET_FIELD(RTC_CNTL_REG, RTC_CNTL_DIG_DBIAS_WAK, dbias); REG_SET_FIELD(RTC_CNTL_REG, RTC_CNTL_DBIAS_WAK, dbias); if ((origin_soc_clk == DPORT_SOC_CLK_SEL_XTAL) || (origin_soc_clk == DPORT_SOC_CLK_SEL_8M) || (((origin_soc_clk == DPORT_SOC_CLK_SEL_PLL) && (0 == origin_cpuperiod_sel)))) { esp32s2_wait_dig_dbias_valid(2); } REG_SET_FIELD(SYSTEM_CPU_PER_CONF_REG, SYSTEM_CPUPERIOD_SEL, per_conf); REG_SET_FIELD(SYSTEM_SYSCLK_CONF_REG, SYSTEM_PRE_DIV_CNT, 0); REG_SET_FIELD(SYSTEM_SYSCLK_CONF_REG, SYSTEM_SOC_CLK_SEL, DPORT_SOC_CLK_SEL_PLL); esp32s2_rtc_clk_apb_freq_update(80 * MHZ); ets_update_cpu_frequency(cpu_freq_mhz); } #ifdef CONFIG_RTC_ALARM /**************************************************************************** * Name: esp32s2_rt_cb_handler * * Description: * RT-Timer service routine * * Input Parameters: * arg - Information about the RT-Timer configuration. * * Returned Value: * None * ****************************************************************************/ static void IRAM_ATTR esp32s2_rt_cb_handler(void *arg) { struct alm_cbinfo_s *cbinfo = (struct alm_cbinfo_s *)arg; alm_callback_t cb; void *cb_arg; int alminfo_id; DEBUGASSERT(cbinfo != NULL); alminfo_id = cbinfo->index; DEBUGASSERT((RTC_ALARM0 <= alminfo_id) && (alminfo_id < RTC_ALARM_LAST)); if (cbinfo->ac_cb != NULL) { /* Alarm callback */ cb = cbinfo->ac_cb; cb_arg = (void *)cbinfo->ac_arg; cbinfo->ac_cb = NULL; cbinfo->ac_arg = NULL; cbinfo->deadline_us = 0; cb(cb_arg, alminfo_id); } } #endif /* CONFIG_RTC_ALARM */ /**************************************************************************** * Name: esp32s2_rtc_calibrate_ocode * * Description: * Calibrate o-code by software * * Input Parameters: * None * * Returned Value: * None * ****************************************************************************/ static void esp32s2_rtc_calibrate_ocode(void) { uint64_t cycle0; uint64_t timeout_cycle; uint32_t slow_clk_period; uint64_t max_delay_cycle; bool odone_flag = 0; bool bg_odone_flag = 0; uint64_t cycle1 = 0; uint64_t max_delay_time_us = 10000; struct esp32s2_cpu_freq_config_s freq_config; /* Bandgap output voltage is not precise when calibrate o-code by hardware * sometimes, so need software o-code calibration (must turn off PLL). * Method: * 1. read current cpu config, save in old_config * 2. switch cpu to xtal because PLL will be closed when o-code calibration * 3. begin o-code calibration * 4. wait o-code calibration done flag or timeout * 5. set cpu to old-config */ enum esp32s2_rtc_slow_freq_e slow_clk_freq = esp32s2_rtc_clk_slow_freq_get(); enum esp32s2_rtc_slow_freq_e rtc_slow_freq_x32k = RTC_SLOW_FREQ_32K_XTAL; enum esp32s2_rtc_slow_freq_e rtc_slow_freq_8md256 = RTC_SLOW_FREQ_8MD256; enum esp32s2_rtc_cal_sel_e cal_clk = RTC_CAL_RTC_MUX; if (slow_clk_freq == rtc_slow_freq_x32k) { cal_clk = RTC_CAL_32K_XTAL; } else if (slow_clk_freq == rtc_slow_freq_8md256) { cal_clk = RTC_CAL_8MD256; } slow_clk_period = esp32s2_rtc_clk_cal(cal_clk, 100); max_delay_cycle = esp32s2_rtc_time_us_to_slowclk(max_delay_time_us, slow_clk_period); cycle0 = esp32s2_rtc_time_get(); timeout_cycle = cycle0 + max_delay_cycle; esp32s2_rtc_clk_cpu_freq_get_config(&freq_config); esp32s2_rtc_cpu_freq_set_xtal(); REGI2C_WRITE_MASK(I2C_ULP, I2C_ULP_IR_RESETB, 0); REGI2C_WRITE_MASK(I2C_ULP, I2C_ULP_IR_RESETB, 1); while (1) { odone_flag = REGI2C_READ_MASK(I2C_ULP, I2C_ULP_O_DONE_FLAG); bg_odone_flag = REGI2C_READ_MASK(I2C_ULP, I2C_ULP_BG_O_DONE_FLAG); cycle1 = esp32s2_rtc_time_get(); if (odone_flag && bg_odone_flag) { break; } if (cycle1 >= timeout_cycle) { break; } } esp32s2_rtc_clk_cpu_freq_set_config(&freq_config); } /**************************************************************************** * Public Functions ****************************************************************************/ static int IRAM_ATTR esp32s2_rtc_clk_slow_freq_get(void) { return REG_GET_FIELD(RTC_CNTL_CLK_CONF_REG, RTC_CNTL_ANA_CLK_RTC_SEL); } /**************************************************************************** * Name: esp32s2_rtc_clk_slow_freq_get_hz * * Description: * Get the approximate frequency of RTC_SLOW_CLK, in Hz * * Input Parameters: * None * * Returned Value: * RTC_SLOW_CLK frequency, in Hz * ****************************************************************************/ uint32_t IRAM_ATTR esp32s2_rtc_clk_slow_freq_get_hz(void) { enum esp32s2_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_APPROX; case RTC_SLOW_FREQ_32K_XTAL: return RTC_SLOW_CLK_FREQ_APPROX; case RTC_SLOW_FREQ_8MD256: return RCT_FAST_D256_FREQ_APPROX; } return OK; } /**************************************************************************** * Name: esp32s2_rtc_clk_fast_freq_get_hz * * Description: * Get fast_clk_rtc source in Hz. * * Input Parameters: * None * * Returned Value: * The clock source in Hz. * ****************************************************************************/ uint32_t IRAM_ATTR esp32s2_rtc_clk_fast_freq_get_hz(void) { return RTC_FAST_CLK_FREQ_APPROX; } /**************************************************************************** * Name: esp32s2_rtc_get_slow_clk_rtc * * Description: * Get slow_clk_rtc source. * * Input Parameters: * None * * Returned Value: * The clock source: * - SLOW_CK * - CK_XTAL_32K * - CK8M_D256_OUT * ****************************************************************************/ enum esp32s2_rtc_slow_freq_e IRAM_ATTR esp32s2_rtc_get_slow_clk(void) { enum esp32s2_rtc_slow_freq_e slow_freq; /* Get the clock source for slow_clk_rtc */ slow_freq = REG_GET_FIELD(RTC_CNTL_CLK_CONF_REG, RTC_CNTL_ANA_CLK_RTC_SEL); return slow_freq; } /**************************************************************************** * Name: esp32s2_rtc_clk_cal * * Description: * Measure RTC slow clock's period, based on main XTAL frequency * * Input Parameters: * cal_clk - clock to be measured * slowclk_cycles - number of slow clock cycles to average * * Returned Value: * Average slow clock period in microseconds, Q13.19 fixed point format * or 0 if calibration has timed out * ****************************************************************************/ uint32_t IRAM_ATTR esp32s2_rtc_clk_cal(enum esp32s2_rtc_cal_sel_e cal_clk, uint32_t slowclk_cycles) { enum esp32s2_rtc_xtal_freq_e xtal_freq; uint64_t xtal_cycles; uint64_t divider; uint64_t period_64; uint32_t period; xtal_freq = esp32s2_rtc_clk_xtal_freq_get(); xtal_cycles = esp32s2_rtc_clk_cal_internal(cal_clk, slowclk_cycles); divider = ((uint64_t)xtal_freq) * slowclk_cycles; period_64 = ((xtal_cycles << RTC_CLK_CAL_FRACT) + divider / 2 - 1) / divider; period = (uint32_t)(period_64 & UINT32_MAX); return period; } enum esp32s2_rtc_xtal_freq_e rtc_get_xtal(void) __attribute__((alias("esp32s2_rtc_clk_xtal_freq_get"))); /**************************************************************************** * Name: esp32s2_rtc_clk_xtal_freq_get * * Description: * Get main XTAL frequency * * Input Parameters: * None * * Returned Value: * XTAL frequency (one of enum esp32s2_rtc_xtal_freq_e values) * ****************************************************************************/ enum esp32s2_rtc_xtal_freq_e IRAM_ATTR esp32s2_rtc_clk_xtal_freq_get(void) { /* We may have already written XTAL value into RTC_XTAL_FREQ_REG */ uint32_t xtal_freq_reg = getreg32(RTC_XTAL_FREQ_REG); if (!esp32s2_clk_val_is_valid(xtal_freq_reg)) { return RTC_XTAL_FREQ_40M; } return (xtal_freq_reg & ~RTC_DISABLE_ROM_LOG) & UINT16_MAX; } /**************************************************************************** * Name: esp32s2_rtc_clk_bbpll_disable * * Description: * disable BBPLL. * * Input Parameters: * None * * Returned Value: * None * ****************************************************************************/ static void IRAM_ATTR esp32s2_rtc_clk_bbpll_disable(void) { modifyreg32(RTC_CNTL_OPTIONS0_REG, 0, RTC_CNTL_BB_I2C_FORCE_PD | RTC_CNTL_BBPLL_FORCE_PD | RTC_CNTL_BBPLL_I2C_FORCE_PD); } /**************************************************************************** * Name: esp32s2_rtc_bbpll_configure * * Description: * Configure main XTAL frequency values according to pll_freq. * * Input Parameters: * xtal_freq - XTAL frequency values * pll_freq - PLL frequency values * * Returned Value: * None * ****************************************************************************/ static void IRAM_ATTR esp32s2_rtc_bbpll_configure( enum esp32s2_rtc_xtal_freq_e xtal_freq, int pll_freq) { static uint8_t div_ref = 0; static uint8_t div7_0 = 0; static uint8_t dr1 = 0 ; static uint8_t dr3 = 0 ; static uint8_t dchgp = 0; static uint8_t dcur = 0; uint8_t i2c_bbpll_lref = 0; uint8_t i2c_bbpll_div_7_0 = 0; uint8_t i2c_bbpll_dcur = 0; if (pll_freq == RTC_PLL_FREQ_480M) { /* Set this register to let the digital part know 480M PLL is used */ modifyreg32(SYSTEM_CPU_PER_CONF_REG, 0, SYSTEM_PLL_FREQ_SEL); /* Configure 480M PLL */ div_ref = 0; div7_0 = 8; dr1 = 0; dr3 = 0; dchgp = 5; dcur = 4; REGI2C_WRITE(I2C_BBPLL, I2C_BBPLL_MODE_HF, 0x6b); } else { /* Clear this register to let the digital part know 320M PLL is used */ modifyreg32(SYSTEM_CPU_PER_CONF_REG, SYSTEM_PLL_FREQ_SEL, 0); /* Configure 320M PLL */ div_ref = 0; div7_0 = 4; dr1 = 0; dr3 = 0; dchgp = 5; dcur = 5; REGI2C_WRITE(I2C_BBPLL, I2C_BBPLL_MODE_HF, 0x69); } i2c_bbpll_lref = (dchgp << I2C_BBPLL_OC_DCHGP_LSB) | (div_ref); i2c_bbpll_div_7_0 = div7_0; i2c_bbpll_dcur = (1 << I2C_BBPLL_OC_DLREF_SEL_LSB) | (2 << I2C_BBPLL_OC_DHREF_SEL_LSB) | dcur; REGI2C_WRITE(I2C_BBPLL, I2C_BBPLL_OC_REF_DIV, i2c_bbpll_lref); REGI2C_WRITE(I2C_BBPLL, I2C_BBPLL_OC_DIV_7_0, i2c_bbpll_div_7_0); REGI2C_WRITE_MASK(I2C_BBPLL, I2C_BBPLL_OC_DR1, dr1); REGI2C_WRITE_MASK(I2C_BBPLL, I2C_BBPLL_OC_DR3, dr3); REGI2C_WRITE(I2C_BBPLL, I2C_BBPLL_OC_DCUR, i2c_bbpll_dcur); REGI2C_WRITE_MASK(I2C_BBPLL, I2C_BBPLL_OC_DHREF_SEL, 2); REGI2C_WRITE_MASK(I2C_BBPLL, I2C_BBPLL_OC_DLREF_SEL, 1); } /**************************************************************************** * Name: esp32s2_rtc_clk_set * * Description: * Set RTC CLK frequency. * * Input Parameters: * None * * Returned Value: * None * ****************************************************************************/ void esp32s2_rtc_clk_set(void) { enum esp32s2_rtc_fast_freq_e fast_freq = RTC_FAST_FREQ_8M; enum esp32s2_slow_clk_sel_e slow_clk = SLOW_CLK_90K; #if defined(CONFIG_ESP32S2_RTC_CLK_EXT_XTAL) slow_clk = SLOW_CLK_32K_XTAL; #elif defined(CONFIG_ESP32S2_RTC_CLK_EXT_OSC) slow_clk = SLOW_CLK_32K_EXT_OSC; #elif defined(CONFIG_ESP32S2_RTC_CLK_INT_8MD256) slow_clk = SLOW_CLK_8MD256; #endif esp32s2_rtc_clk_fast_freq_set(fast_freq); esp32s2_select_rtc_slow_clk(slow_clk); } /**************************************************************************** * Name: esp32s2_rtc_init * * Description: * Initialize RTC clock and power control related functions. * * Input Parameters: * None * * Returned Value: * None * ****************************************************************************/ void IRAM_ATTR esp32s2_rtc_init(void) { struct esp32s2_rtc_priv_s cfg = RTC_CONFIG_DEFAULT(); modifyreg32(RTC_CNTL_ANA_CONF_REG, RTC_CNTL_PVTMON_PU, 0); modifyreg32(RTC_CNTL_TIMER1_REG, 0, cfg.pll_wait ? RTC_CNTL_PLL_BUF_WAIT : 0); modifyreg32(RTC_CNTL_TIMER1_REG, 0, cfg.ck8m_wait ? RTC_CNTL_CK8M_WAIT : 0); /* Moved from rtc sleep to rtc init to save sleep function running time */ /* set shortest possible sleep time limit */ REG_SET_FIELD(RTC_CNTL_TIMER5_REG, RTC_CNTL_MIN_SLP_VAL, RTC_CNTL_MIN_SLP_VAL_MIN); /* set wifi timer */ REG_SET_FIELD(RTC_CNTL_TIMER3_REG, RTC_CNTL_WIFI_POWERUP_TIMER, 1); REG_SET_FIELD(RTC_CNTL_TIMER3_REG, RTC_CNTL_WIFI_WAIT_TIMER, 1); if (cfg.cali_ocode) { /* TODO: Use calibration from efuse if configured */ esp32s2_rtc_calibrate_ocode(); } if (cfg.clkctl_init) { /* clear CMMU clock force on */ modifyreg32(EXTMEM_PRO_CACHE_MMU_POWER_CTRL_REG, EXTMEM_PRO_CACHE_MMU_MEM_FORCE_ON, 0); /* clear rom clock force on */ REG_SET_FIELD(SYSTEM_ROM_CTRL_0_REG, SYSTEM_ROM_FO, 0); /* clear tag clock force on */ REG_SET_FIELD(SYSTEM_SRAM_CTRL_0_REG, SYSTEM_SRAM_FO, 0); /* clear tag clock force on */ modifyreg32(EXTMEM_PRO_DCACHE_TAG_POWER_CTRL_REG, EXTMEM_PRO_DCACHE_TAG_MEM_FORCE_ON, 0); modifyreg32(EXTMEM_PRO_ICACHE_TAG_POWER_CTRL_REG, EXTMEM_PRO_ICACHE_TAG_MEM_FORCE_ON, 0); /* clear register clock force on */ modifyreg32(SPI_MEM_CLOCK_GATE_REG(0), SPI_MEM_CLK_EN, 0); modifyreg32(SPI_MEM_CLOCK_GATE_REG(1), SPI_MEM_CLK_EN, 0); } if (cfg.pwrctl_init) { modifyreg32(RTC_CNTL_CLK_CONF_REG, RTC_CNTL_CK8M_FORCE_PU, 0); /* Cancel xtal force pu if no need to force power up * Cannot cancel xtal force pu if pll is force power on */ if (!(cfg.xtal_fpu || cfg.bbpll_fpu)) { modifyreg32(RTC_CNTL_OPTIONS0_REG, RTC_CNTL_XTL_FORCE_PU, 0); } else { modifyreg32(RTC_CNTL_OPTIONS0_REG, 0, RTC_CNTL_XTL_FORCE_PU); } /* CLEAR APLL close */ CLEAR_PERI_REG_MASK(RTC_CNTL_ANA_CONF_REG, RTC_CNTL_PLLA_FORCE_PU); SET_PERI_REG_MASK(RTC_CNTL_ANA_CONF_REG, RTC_CNTL_PLLA_FORCE_PD); /* Cancel bbpll force pu if setting no force power up */ if (!cfg.bbpll_fpu) { modifyreg32(RTC_CNTL_OPTIONS0_REG, RTC_CNTL_BBPLL_FORCE_PU | RTC_CNTL_BBPLL_I2C_FORCE_PU | RTC_CNTL_BB_I2C_FORCE_PU, 0); } else { modifyreg32(RTC_CNTL_OPTIONS0_REG, 0, RTC_CNTL_BBPLL_FORCE_PU | RTC_CNTL_BBPLL_I2C_FORCE_PU | RTC_CNTL_BB_I2C_FORCE_PU); } /* Cancel RTC REG force PU */ modifyreg32(RTC_CNTL_PWC_REG, RTC_CNTL_FORCE_PU, 0); modifyreg32(RTC_CNTL_REG, RTC_CNTL_REGULATOR_FORCE_PU | RTC_CNTL_DBOOST_FORCE_PU, 0); modifyreg32(RTC_CNTL_PWC_REG, RTC_CNTL_SLOWMEM_FORCE_PU | RTC_CNTL_FASTMEM_FORCE_PU | RTC_CNTL_SLOWMEM_FORCE_NOISO | RTC_CNTL_FASTMEM_FORCE_NOISO, 0); if (cfg.rtc_dboost_fpd) { modifyreg32(RTC_CNTL_REG, 0, RTC_CNTL_DBOOST_FORCE_PD); } else { modifyreg32(RTC_CNTL_REG, RTC_CNTL_DBOOST_FORCE_PD, 0); } /* cancel digital pu force */ modifyreg32(RTC_CNTL_PWC_REG, RTC_CNTL_SLOWMEM_FORCE_PU | RTC_CNTL_FASTMEM_FORCE_PU, 0); /* If this mask is enabled, all soc mem cannot enter power down mode * We should control soc memory power down mode from RTC, so we will * not touch this register any more */ modifyreg32(SYSTEM_MEM_PD_MASK_REG, SYSTEM_LSLP_MEM_PD_MASK, 0); /* If this pd_cfg is set to 1, all memory won't enter low power mode * during light sleep. * If this pd_cfg is set to 0, all memory will enter low power mode * during light sleep. */ struct esp32s2_rtc_sleep_pd_config_s pu_cfg = RTC_SLEEP_PD_CONFIG_ALL(0); esp32s2_rtc_sleep_pd(pu_cfg); modifyreg32(RTC_CNTL_PWC_REG, RTC_CNTL_DG_WRAP_FORCE_PU, 0); modifyreg32(RTC_CNTL_PWC_REG, RTC_CNTL_WIFI_FORCE_PU, 0); modifyreg32(RTC_CNTL_DIG_ISO_REG, RTC_CNTL_DG_WRAP_FORCE_NOISO | RTC_CNTL_DG_WRAP_FORCE_ISO, 0); modifyreg32(RTC_CNTL_DIG_ISO_REG, RTC_CNTL_WIFI_FORCE_NOISO | RTC_CNTL_DG_WRAP_FORCE_NOISO, 0); modifyreg32(RTC_CNTL_PWC_REG, RTC_CNTL_FORCE_NOISO, 0); /* cancel digital PADS force no iso */ if (cfg.cpu_waiti_clk_gate) { modifyreg32(SYSTEM_CPU_PER_CONF_REG, SYSTEM_CPU_WAIT_MODE_FORCE_ON, 0); } else { modifyreg32(SYSTEM_CPU_PER_CONF_REG, 0, SYSTEM_CPU_WAIT_MODE_FORCE_ON); } /* If SYSTEM_CPU_WAIT_MODE_FORCE_ON == 0, the cpu clk will be closed * when cpu enter WAITI mode */ modifyreg32(RTC_CNTL_DIG_ISO_REG, RTC_CNTL_DG_PAD_FORCE_UNHOLD | RTC_CNTL_DG_PAD_FORCE_NOISO, 0); } } /**************************************************************************** * Name: esp32s2_rtc_time_get * * Description: * Get current value of RTC counter. * * Input Parameters: * None * * Returned Value: * Current value of RTC counter * ****************************************************************************/ uint64_t IRAM_ATTR esp32s2_rtc_time_get(void) { uint64_t rtc_time; modifyreg32(RTC_CNTL_TIME_UPDATE_REG, 0, RTC_CNTL_TIME_UPDATE); rtc_time = getreg32(RTC_CNTL_TIME0_REG); rtc_time |= ((uint64_t) getreg32(RTC_CNTL_TIME1_REG)) << 32; return rtc_time; } /**************************************************************************** * Name: esp32s2_rtc_time_us_to_slowclk * * Description: * Convert time interval from microseconds to RTC_SLOW_CLK cycles. * * Input Parameters: * time_in_us - Time interval in microseconds * period - Period of slow clock in microseconds * * Returned Value: * Number of slow clock cycles * ****************************************************************************/ uint64_t IRAM_ATTR esp32s2_rtc_time_us_to_slowclk(uint64_t time_in_us, uint32_t period) { uint64_t slow_clk_cycles = 0; uint64_t max_time_in_us = (UINT64_C(1) << 45) - 1; /* 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; } /**************************************************************************** * Name: esp32s2_rtc_time_slowclk_to_us * * Description: * Convert time interval from RTC_SLOW_CLK to microseconds * * Input Parameters: * rtc_cycles - Time interval in RTC_SLOW_CLK cycles * period - Period of slow clock in microseconds * * Returned Value: * Time interval in microseconds * ****************************************************************************/ uint64_t IRAM_ATTR esp32s2_rtc_time_slowclk_to_us(uint64_t rtc_cycles, uint32_t period) { return (rtc_cycles * period) >> RTC_CLK_CAL_FRACT; } /**************************************************************************** * Name: esp32s2_clk_slowclk_cal_get * * Description: * Get the calibration value of RTC slow clock. * * Input Parameters: * None * * Returned Value: * The calibration value obtained using rtc_clk_cal * ****************************************************************************/ uint32_t IRAM_ATTR esp32s2_clk_slowclk_cal_get(void) { return getreg32(RTC_SLOW_CLK_CAL_REG); } /**************************************************************************** * Name: esp32s2_rtc_sleep_set_wakeup_time * * Description: * Set target value of RTC counter for RTC_TIMER_TRIG_EN wakeup source. * * Input Parameters: * t - value of RTC counter at which wakeup from sleep will happen. * * Returned Value: * None * ****************************************************************************/ void IRAM_ATTR esp32s2_rtc_sleep_set_wakeup_time(uint64_t t) { putreg32(t & UINT32_MAX, RTC_CNTL_SLP_TIMER0_REG); putreg32((uint32_t)(t >> 32), RTC_CNTL_SLP_TIMER1_REG); } /**************************************************************************** * Name: esp32s2_rtc_wait_for_slow_cycle * * Description: * Busy loop until next RTC_SLOW_CLK cycle. * * Input Parameters: * None * * Returned Value: * none * ****************************************************************************/ void IRAM_ATTR esp32s2_rtc_wait_for_slow_cycle(void) { modifyreg32(TIMG_RTCCALICFG_REG(0), TIMG_RTC_CALI_START_CYCLING | TIMG_RTC_CALI_START, 0); modifyreg32(TIMG_RTCCALICFG_REG(0), TIMG_RTC_CALI_RDY, 0); REG_SET_FIELD(TIMG_RTCCALICFG_REG(0), TIMG_RTC_CALI_CLK_SEL, RTC_CAL_RTC_MUX); /* Request to run calibration for 0 slow clock cycles. * RDY bit will be set on the nearest slow clock cycle. */ REG_SET_FIELD(TIMG_RTCCALICFG_REG(0), TIMG_RTC_CALI_MAX, 0); modifyreg32(TIMG_RTCCALICFG_REG(0), 0, TIMG_RTC_CALI_START); /* RDY needs some time to go low */ up_udelay(1); while (!(getreg32(TIMG_RTCCALICFG_REG(0)) & TIMG_RTC_CALI_RDY)) { up_udelay(1); } } void esp32s2_rtc_clk_apb_freq_update(uint32_t apb_freq) { g_apb_freq = apb_freq; } uint32_t esp32s2_rtc_clk_apb_freq_get(void) { return g_apb_freq; } /**************************************************************************** * Name: esp32s2_rtc_cpu_freq_set_xtal * * Description: * Switch CPU clock source to XTAL * * Input Parameters: * None * * Returned Value: * None * ****************************************************************************/ void IRAM_ATTR esp32s2_rtc_cpu_freq_set_xtal(void) { int freq_mhz = (int) esp32s2_rtc_clk_xtal_freq_get(); esp32s2_rtc_update_to_xtal(freq_mhz, 1); esp32s2_rtc_wait_for_slow_cycle(); } /**************************************************************************** * Name: esp_rtc_clk_get_cpu_freq * * Description: * Get the currently used CPU frequency configuration. * * Input Parameters: * None * * Returned Value: * CPU frequency * ****************************************************************************/ int IRAM_ATTR esp_rtc_clk_get_cpu_freq(void) { uint32_t source_freq_mhz; uint32_t div; uint32_t soc_clk_sel; uint32_t cpuperiod_sel; int freq_mhz = 0; soc_clk_sel = REG_GET_FIELD(SYSTEM_SYSCLK_CONF_REG, SYSTEM_SOC_CLK_SEL); switch (soc_clk_sel) { case DPORT_SOC_CLK_SEL_XTAL: { div = REG_GET_FIELD(SYSTEM_SYSCLK_CONF_REG, SYSTEM_PRE_DIV_CNT) + 1; source_freq_mhz = (uint32_t) esp32s2_rtc_clk_xtal_freq_get(); freq_mhz = source_freq_mhz / div; } break; case DPORT_SOC_CLK_SEL_PLL: { cpuperiod_sel = REG_GET_FIELD(SYSTEM_CPU_PER_CONF_REG, SYSTEM_CPUPERIOD_SEL); uint32_t pllfreq_sel = REG_GET_FIELD(SYSTEM_CPU_PER_CONF_REG, SYSTEM_PLL_FREQ_SEL); source_freq_mhz = (pllfreq_sel) ? RTC_PLL_FREQ_480M : RTC_PLL_FREQ_320M; if (cpuperiod_sel == DPORT_CPUPERIOD_SEL_80) { div = (source_freq_mhz == RTC_PLL_FREQ_480M) ? 6 : 4; freq_mhz = 480 / div; } else if (cpuperiod_sel == DPORT_CPUPERIOD_SEL_160) { div = 3; freq_mhz = 480 / div; } else if (cpuperiod_sel == DPORT_CPUPERIOD_SEL_240) { div = 2; freq_mhz = 480 / div; } else { rtcerr("unsupported frequency configuration"); return -ENODEV; } } break; case DPORT_SOC_CLK_SEL_8M: { source_freq_mhz = 8; div = 1; freq_mhz = source_freq_mhz / div; } break; default: { rtcerr("unsupported frequency configuration"); return -ENODEV; } } return freq_mhz; } /**************************************************************************** * Name: esp32s2_rtc_sleep_init * * Description: * Prepare the chip to enter sleep mode * * Input Parameters: * flags - sleep mode configuration * * Returned Value: * None * ****************************************************************************/ void IRAM_ATTR esp32s2_rtc_sleep_init(uint32_t flags) { struct esp32s2_rtc_sleep_config_s cfg = RTC_SLEEP_CONFIG_DEFAULT(flags); /* Starts here */ if (cfg.lslp_mem_inf_fpu) { struct esp32s2_rtc_sleep_pd_config_s pu_cfg = RTC_SLEEP_PD_CONFIG_ALL(1); esp32s2_rtc_sleep_pd(pu_cfg); } if (cfg.rtc_mem_inf_follow_cpu) { modifyreg32(RTC_CNTL_PWC_REG, 0, RTC_CNTL_SLOWMEM_FOLW_CPU | RTC_CNTL_FASTMEM_FOLW_CPU); } else { modifyreg32(RTC_CNTL_PWC_REG, RTC_CNTL_SLOWMEM_FOLW_CPU | RTC_CNTL_FASTMEM_FOLW_CPU, 0); } if (cfg.rtc_fastmem_pd_en) { modifyreg32(RTC_CNTL_PWC_REG, 0, RTC_CNTL_FASTMEM_PD_EN); modifyreg32(RTC_CNTL_PWC_REG, RTC_CNTL_FASTMEM_FORCE_PU, 0); modifyreg32(RTC_CNTL_PWC_REG, RTC_CNTL_FASTMEM_FORCE_NOISO, 0); } else { modifyreg32(RTC_CNTL_PWC_REG, RTC_CNTL_FASTMEM_PD_EN, 0); modifyreg32(RTC_CNTL_PWC_REG, 0, RTC_CNTL_FASTMEM_FORCE_PU); modifyreg32(RTC_CNTL_PWC_REG, 0, RTC_CNTL_FASTMEM_FORCE_NOISO); } if (cfg.rtc_slowmem_pd_en) { modifyreg32(RTC_CNTL_PWC_REG, 0, RTC_CNTL_SLOWMEM_PD_EN); modifyreg32(RTC_CNTL_PWC_REG, RTC_CNTL_SLOWMEM_FORCE_PU, 0); modifyreg32(RTC_CNTL_PWC_REG, RTC_CNTL_SLOWMEM_FORCE_NOISO, 0); } else { modifyreg32(RTC_CNTL_PWC_REG, RTC_CNTL_SLOWMEM_PD_EN, 0); modifyreg32(RTC_CNTL_PWC_REG, 0, RTC_CNTL_SLOWMEM_FORCE_PU); modifyreg32(RTC_CNTL_PWC_REG, 0, RTC_CNTL_SLOWMEM_FORCE_NOISO); } if (cfg.wifi_pd_en) { modifyreg32(RTC_CNTL_DIG_ISO_REG, RTC_CNTL_WIFI_FORCE_NOISO | RTC_CNTL_WIFI_FORCE_ISO, 0); modifyreg32(RTC_CNTL_DIG_PWC_REG, RTC_CNTL_WIFI_FORCE_PU, 0); modifyreg32(RTC_CNTL_DIG_PWC_REG, 0, RTC_CNTL_WIFI_PD_EN); } else { modifyreg32(RTC_CNTL_DIG_PWC_REG, RTC_CNTL_WIFI_PD_EN, 0); } if (cfg.rtc_peri_pd_en) { modifyreg32(RTC_CNTL_PWC_REG, 0, RTC_CNTL_PD_EN); } else { modifyreg32(RTC_CNTL_PWC_REG, RTC_CNTL_PD_EN, 0); } REG_SET_FIELD(RTC_CNTL_REG, RTC_CNTL_DBIAS_SLP, cfg.rtc_dbias_slp); REG_SET_FIELD(RTC_CNTL_REG, RTC_CNTL_DIG_DBIAS_SLP, cfg.dig_dbias_slp); REG_SET_FIELD(RTC_CNTL_BIAS_CONF_REG, RTC_CNTL_DBG_ATTEN_MONITOR, RTC_CNTL_DBG_ATTEN_MONITOR_DEFAULT); REG_SET_FIELD(RTC_CNTL_BIAS_CONF_REG, RTC_CNTL_BIAS_SLEEP_MONITOR, RTC_CNTL_BIASSLP_MONITOR_DEFAULT); REG_SET_FIELD(RTC_CNTL_BIAS_CONF_REG, RTC_CNTL_BIAS_SLEEP_DEEP_SLP, (!cfg.deep_slp && cfg.xtal_fpu) ? RTC_CNTL_BIASSLP_SLEEP_ON : RTC_CNTL_BIASSLP_SLEEP_DEFAULT); REG_SET_FIELD(RTC_CNTL_BIAS_CONF_REG, RTC_CNTL_PD_CUR_MONITOR, RTC_CNTL_PD_CUR_MONITOR_DEFAULT); REG_SET_FIELD(RTC_CNTL_BIAS_CONF_REG, RTC_CNTL_PD_CUR_DEEP_SLP, (!cfg.deep_slp && cfg.xtal_fpu) ? RTC_CNTL_PD_CUR_SLEEP_ON : RTC_CNTL_PD_CUR_SLEEP_DEFAULT); REG_SET_FIELD(RTC_CNTL_BIAS_CONF_REG, RTC_CNTL_DBG_ATTEN_DEEP_SLP, cfg.dbg_atten_slp); if (cfg.deep_slp) { modifyreg32(RTC_CNTL_DIG_ISO_REG, RTC_CNTL_DG_PAD_FORCE_ISO | RTC_CNTL_DG_PAD_FORCE_NOISO, 0); /* Shut down parts of RTC which may have been left * enabled by the wireless drivers. */ modifyreg32(RTC_CNTL_ANA_CONF_REG, RTC_CNTL_CKGEN_I2C_PU | RTC_CNTL_PLL_I2C_PU | RTC_CNTL_RFRX_PBUS_PU | RTC_CNTL_TXRF_I2C_PU, 0); } else { modifyreg32(RTC_CNTL_DIG_PWC_REG, RTC_CNTL_DG_WRAP_PD_EN, 0); REG_SET_FIELD(RTC_CNTL_BIAS_CONF_REG, RTC_CNTL_DBG_ATTEN_DEEP_SLP, 0); } REG_SET_FIELD(RTC_CNTL_OPTIONS0_REG, RTC_CNTL_XTL_FORCE_PU, cfg.xtal_fpu); if (REG_GET_FIELD(RTC_CNTL_CLK_CONF_REG, RTC_CNTL_ANA_CLK_RTC_SEL) == RTC_SLOW_FREQ_8MD256) { modifyreg32(RTC_CNTL_CLK_CONF_REG, 0, RTC_CNTL_CK8M_FORCE_PU); } else { modifyreg32(RTC_CNTL_CLK_CONF_REG, RTC_CNTL_CK8M_FORCE_PU, 0); } /* Keep the RTC8M_CLK on in light_sleep mode if the * ledc low-speed channel is clocked by RTC8M_CLK. */ if (!cfg.deep_slp && GET_PERI_REG_MASK(RTC_CNTL_CLK_CONF_REG, RTC_CNTL_DIG_CLK8M_EN_M)) { REG_CLR_BIT(RTC_CNTL_CLK_CONF_REG, RTC_CNTL_CK8M_FORCE_PD); REG_SET_BIT(RTC_CNTL_CLK_CONF_REG, RTC_CNTL_CK8M_FORCE_PU); } /* enable VDDSDIO control by state machine */ modifyreg32(RTC_CNTL_SDIO_CONF_REG, RTC_CNTL_SDIO_FORCE, 0); REG_SET_FIELD(RTC_CNTL_SDIO_CONF_REG, RTC_CNTL_SDIO_REG_PD_EN, cfg.vddsdio_pd_en); REG_SET_FIELD(RTC_CNTL_SLP_REJECT_CONF_REG, RTC_CNTL_DEEP_SLP_REJECT_EN, cfg.deep_slp_reject); REG_SET_FIELD(RTC_CNTL_SLP_REJECT_CONF_REG, RTC_CNTL_LIGHT_SLP_REJECT_EN, cfg.light_slp_reject); REG_SET_FIELD(RTC_CNTL_OPTIONS0_REG, RTC_CNTL_XTL_FORCE_PU, cfg.xtal_fpu); } /**************************************************************************** * Name: esp32s2_rtc_sleep_start * * Description: * Enter force sleep mode. * * Input Parameters: * wakeup_opt - bit mask wake up reasons to enable * reject_opt - bit mask of sleep reject reasons. * * Returned Value: * Non-zero if sleep was rejected by hardware * ****************************************************************************/ int IRAM_ATTR esp32s2_rtc_sleep_start(uint32_t wakeup_opt, uint32_t reject_opt) { int reject; REG_SET_FIELD(RTC_CNTL_WAKEUP_STATE_REG, RTC_CNTL_WAKEUP_ENA, wakeup_opt); putreg32((uint32_t)reject_opt, RTC_CNTL_SLP_REJECT_CONF_REG); /* Start entry into sleep mode */ modifyreg32(RTC_CNTL_STATE0_REG, 0, RTC_CNTL_SLEEP_EN); while ((getreg32(RTC_CNTL_INT_RAW_RTC_REG) & (RTC_CNTL_SLP_REJECT_INT_RAW | RTC_CNTL_SLP_WAKEUP_INT_RAW)) == 0); /* In deep sleep mode, we never get here */ reject = REG_GET_FIELD(RTC_CNTL_INT_RAW_RTC_REG, RTC_CNTL_SLP_REJECT_INT_RAW); modifyreg32(RTC_CNTL_INT_CLR_RTC_REG, 0, RTC_CNTL_SLP_REJECT_INT_CLR | RTC_CNTL_SLP_WAKEUP_INT_CLR); /* restore DBG_ATTEN to the default value */ REG_SET_FIELD(RTC_CNTL_BIAS_CONF_REG, RTC_CNTL_DBG_ATTEN_DEEP_SLP, RTC_CNTL_DBG_ATTEN_LIGHTSLEEP_DEFAULT); return reject; } /**************************************************************************** * Name: esp32s2_rtc_clk_cpu_freq_set_config * * Description: * Set CPU frequency configuration. * * Input Parameters: * config - CPU frequency configuration * * Returned Value: * None * ****************************************************************************/ void IRAM_ATTR esp32s2_rtc_clk_cpu_freq_set_config( const struct esp32s2_cpu_freq_config_s *config) { uint32_t soc_clk_sel = REG_GET_FIELD(SYSTEM_SYSCLK_CONF_REG, SYSTEM_SOC_CLK_SEL); if (config->source == RTC_CPU_FREQ_SRC_XTAL) { esp32s2_rtc_update_to_xtal(config->freq_mhz, config->div); if (soc_clk_sel == DPORT_SOC_CLK_SEL_PLL) { esp32s2_rtc_clk_bbpll_disable(); } } else if (config->source == RTC_CPU_FREQ_SRC_PLL) { if (soc_clk_sel != DPORT_SOC_CLK_SEL_PLL) { modifyreg32(RTC_CNTL_OPTIONS0_REG, RTC_CNTL_BB_I2C_FORCE_PD | RTC_CNTL_BBPLL_FORCE_PD | RTC_CNTL_BBPLL_I2C_FORCE_PD, 0); esp32s2_rtc_bbpll_configure(esp32s2_rtc_clk_xtal_freq_get(), config->source_freq_mhz); } esp32s2_rtc_clk_cpu_freq_to_pll_mhz(config->freq_mhz); } else if (config->source == RTC_CPU_FREQ_SRC_8M) { esp32s2_rtc_clk_cpu_freq_to_8m(); if (soc_clk_sel == DPORT_SOC_CLK_SEL_PLL) { esp32s2_rtc_clk_bbpll_disable(); } } } /**************************************************************************** * Name: esp32s2_rtc_clk_cpu_freq_get_config * * Description: * Get the currently used CPU frequency configuration. * * Input Parameters: * out_config - CPU frequency configuration * * Returned Value: * None * ****************************************************************************/ void IRAM_ATTR esp32s2_rtc_clk_cpu_freq_get_config( struct esp32s2_cpu_freq_config_s *out_config) { uint32_t div = 3; uint32_t freq_mhz = 160; uint32_t source_freq_mhz = RTC_PLL_FREQ_480M; enum esp32s2_rtc_cpu_freq_src_e source = RTC_CPU_FREQ_SRC_PLL; uint32_t soc_clk_sel = REG_GET_FIELD(SYSTEM_SYSCLK_CONF_REG, SYSTEM_SOC_CLK_SEL); switch (soc_clk_sel) { case DPORT_SOC_CLK_SEL_XTAL: { source = RTC_CPU_FREQ_SRC_XTAL; div = REG_GET_FIELD(SYSTEM_SYSCLK_CONF_REG, SYSTEM_PRE_DIV_CNT) + 1; source_freq_mhz = (uint32_t) esp32s2_rtc_clk_xtal_freq_get(); freq_mhz = source_freq_mhz / div; } break; case DPORT_SOC_CLK_SEL_PLL: { uint32_t cpuperiod_sel = REG_GET_FIELD(SYSTEM_CPU_PER_CONF_REG, SYSTEM_CPUPERIOD_SEL); uint32_t pllfreq_sel = REG_GET_FIELD(SYSTEM_CPU_PER_CONF_REG, SYSTEM_PLL_FREQ_SEL); source = RTC_CPU_FREQ_SRC_PLL; source_freq_mhz = (pllfreq_sel) ? RTC_PLL_FREQ_480M : RTC_PLL_FREQ_320M; if (cpuperiod_sel == DPORT_CPUPERIOD_SEL_80) { div = (source_freq_mhz == RTC_PLL_FREQ_480M) ? 6 : 4; freq_mhz = 80; } else if (cpuperiod_sel == DPORT_CPUPERIOD_SEL_160) { div = (source_freq_mhz == RTC_PLL_FREQ_480M) ? 3 : 2; div = 3; freq_mhz = 160; } else if (cpuperiod_sel == DPORT_CPUPERIOD_SEL_240) { div = 2; freq_mhz = 240; } else { return; } } break; case DPORT_SOC_CLK_SEL_8M: { source = RTC_CPU_FREQ_SRC_8M; source_freq_mhz = 8; div = 1; freq_mhz = source_freq_mhz; } break; default: PANIC(); break; } *out_config = (struct esp32s2_cpu_freq_config_s) { .source = source, .source_freq_mhz = source_freq_mhz, .div = div, .freq_mhz = freq_mhz }; } /**************************************************************************** * Name: esp32s2_rtc_get_time_us * * Description: * Get current value of RTC counter in microseconds * * Input Parameters: * None * * Returned Value: * Current value of RTC counter in microseconds * ****************************************************************************/ uint64_t esp32s2_rtc_get_time_us(void) { const uint32_t cal = getreg32(RTC_SLOW_CLK_CAL_REG); const uint64_t rtc_this_ticks = esp32s2_rtc_time_get(); /* RTC counter result is up to 2^48, calibration factor is up to 2^24, * for a 32KHz clock. We need to calculate (assuming no overflow): * (ticks * cal) >> RTC_CLK_CAL_FRACT. An overflow in the (ticks * cal) * multiplication would cause time to wrap around after approximately * 13 days, which is probably not enough for some applications. * Therefore multiplication is split into two terms, for the lower 32-bit * and the upper 16-bit parts of "ticks", i.e.: * ((ticks_low + 2^32 * ticks_high) * cal) >> RTC_CLK_CAL_FRACT */ const uint64_t ticks_low = rtc_this_ticks & UINT32_MAX; const uint64_t ticks_high = rtc_this_ticks >> 32; const uint64_t delta_time_us = ((ticks_low * cal) >> RTC_CLK_CAL_FRACT) + ((ticks_high * cal) << (32 - RTC_CLK_CAL_FRACT)); return delta_time_us; } /**************************************************************************** * Name: esp32_rtc_bbpll_disable * * Description: * Disable BBPLL. * * Input Parameters: * None * * Returned Value: * None * ****************************************************************************/ void IRAM_ATTR esp32s2_rtc_bbpll_disable(void) { modifyreg32(RTC_CNTL_OPTIONS0_REG, 0, RTC_CNTL_BB_I2C_FORCE_PD | RTC_CNTL_BBPLL_FORCE_PD | RTC_CNTL_BBPLL_I2C_FORCE_PD); } /**************************************************************************** * Name: esp32s2_rtc_set_boot_time * * Description: * Set time to RTC register to replace the original boot time. * * Input Parameters: * time_us - set time in microseconds. * * Returned Value: * None * ****************************************************************************/ void IRAM_ATTR esp32s2_rtc_set_boot_time(uint64_t time_us) { putreg32((uint32_t)(time_us & UINT32_MAX), RTC_BOOT_TIME_LOW_REG); putreg32((uint32_t)(time_us >> 32), RTC_BOOT_TIME_HIGH_REG); } /**************************************************************************** * Name: esp32s2_rtc_get_boot_time * * Description: * Get time of RTC register to indicate the original boot time. * * Input Parameters: * None * * Returned Value: * Get time in microseconds. * ****************************************************************************/ uint64_t IRAM_ATTR esp32s2_rtc_get_boot_time(void) { return ((uint64_t)getreg32(RTC_BOOT_TIME_LOW_REG)) + (((uint64_t)getreg32(RTC_BOOT_TIME_HIGH_REG)) << 32); } /**************************************************************************** * Name: up_rtc_time * * Description: * Get the current time in seconds. This is similar to the standard time() * function. This interface is only required if the low-resolution * RTC/counter hardware implementation is selected. It is only used by the * RTOS during initialization to set up the system time when CONFIG_RTC is * set but CONFIG_RTC_HIRES is not set. * * Input Parameters: * None * * Returned Value: * The current time in seconds * ****************************************************************************/ #ifndef CONFIG_RTC_HIRES time_t up_rtc_time(void) { uint64_t time_us; irqstate_t flags; flags = spin_lock_irqsave(NULL); /* NOTE: RT-Timer starts to work after the board is initialized, and the * RTC controller starts works after up_rtc_initialize is initialized. * Since the system clock starts to work before the board is initialized, * if CONFIG_RTC is enabled, the system time must be matched by the time * of the RTC controller (up_rtc_initialize has already been initialized, * and RT-Timer cannot work). */ /* Determine if RT-Timer is started */ if (g_rt_timer_enabled == true) { /* Get the time from RT-Timer, the time interval between RTC * controller and RT-Timer is stored in g_rtc_save->offset. */ time_us = rt_timer_time_us() + g_rtc_save->offset + esp32s2_rtc_get_boot_time(); } else { /* Get the time from RTC controller. */ time_us = esp32s2_rtc_get_time_us() + esp32s2_rtc_get_boot_time(); } spin_unlock_irqrestore(NULL, flags); return (time_t)(time_us / USEC_PER_SEC); } #endif /* !CONFIG_RTC_HIRES */ /**************************************************************************** * Name: up_rtc_settime * * Description: * Set the RTC to the provided time. All RTC implementations must be * able to set their time based on a standard timespec. * * Input Parameters: * ts - the time to use * * Returned Value: * Zero (OK) on success; a negated errno on failure * ****************************************************************************/ int up_rtc_settime(const struct timespec *ts) { irqstate_t flags; uint64_t now_us; uint64_t rtc_offset_us; DEBUGASSERT(ts != NULL && ts->tv_nsec < NSEC_PER_SEC); flags = spin_lock_irqsave(NULL); now_us = ((uint64_t) ts->tv_sec) * USEC_PER_SEC + ts->tv_nsec / NSEC_PER_USEC; if (g_rt_timer_enabled == true) { /* Set based on RT-Timer offset value. */ rtc_offset_us = now_us - rt_timer_time_us(); } else { /* Set based on the offset value of the RT controller. */ rtc_offset_us = now_us - esp32s2_rtc_get_time_us(); } g_rtc_save->offset = 0; esp32s2_rtc_set_boot_time(rtc_offset_us); spin_unlock_irqrestore(NULL, flags); return OK; } /**************************************************************************** * Name: up_rtc_initialize * * Description: * Initialize the hardware RTC per the selected configuration. * This function is called once during the OS initialization sequence * * Input Parameters: * None * * Returned Value: * Zero (OK) on success; a negated errno on failure * ****************************************************************************/ int up_rtc_initialize(void) { g_rtc_save = &rtc_saved_data; /* If saved data is invalid, clear offset information */ if (g_rtc_save->magic != MAGIC_RTC_SAVE) { g_rtc_save->magic = MAGIC_RTC_SAVE; g_rtc_save->offset = 0; esp32s2_rtc_set_boot_time(0); } #ifdef CONFIG_RTC_HIRES /* Synchronize the base time to the RTC time */ up_rtc_gettime(&g_basetime); #endif g_rtc_enabled = true; return OK; } /**************************************************************************** * Name: up_rtc_gettime * * Description: * Get the current time from the high resolution RTC time or RT-Timer. This * interface is only supported by the high-resolution RTC/counter hardware * implementation. It is used to replace the system timer. * * Input Parameters: * tp - The location to return the RTC time or RT-Timer value. * * Returned Value: * Zero (OK) on success; a negated errno on failure * ****************************************************************************/ #ifdef CONFIG_RTC_HIRES int up_rtc_gettime(struct timespec *tp) { irqstate_t flags; uint64_t time_us; flags = spin_lock_irqsave(NULL); if (g_rt_timer_enabled == true) { time_us = rt_timer_time_us() + g_rtc_save->offset + esp32s2_rtc_get_boot_time(); } else { time_us = esp32s2_rtc_get_time_us() + esp32s2_rtc_get_boot_time(); } tp->tv_sec = time_us / USEC_PER_SEC; tp->tv_nsec = (time_us % USEC_PER_SEC) * NSEC_PER_USEC; spin_unlock_irqrestore(NULL, flags); return OK; } #endif /* CONFIG_RTC_HIRES */ #ifdef CONFIG_RTC_ALARM /**************************************************************************** * Name: up_rtc_setalarm * * Description: * Set up an alarm. * * Input Parameters: * alminfo - Information about the alarm configuration. * * Returned Value: * Zero (OK) on success; a negated errno on failure * ****************************************************************************/ int up_rtc_setalarm(struct alm_setalarm_s *alminfo) { struct rt_timer_args_s rt_timer_args; struct alm_cbinfo_s *cbinfo; irqstate_t flags; int ret = -EBUSY; int id; DEBUGASSERT(alminfo != NULL); DEBUGASSERT((RTC_ALARM0 <= alminfo->as_id) && (alminfo->as_id < RTC_ALARM_LAST)); /* Set the alarm in RT-Timer */ id = alminfo->as_id; cbinfo = &g_alarmcb[id]; if (cbinfo->ac_cb == NULL) { /* Create the RT-Timer alarm */ flags = spin_lock_irqsave(NULL); if (cbinfo->alarm_hdl == NULL) { cbinfo->index = id; rt_timer_args.arg = cbinfo; rt_timer_args.callback = esp32s2_rt_cb_handler; ret = rt_timer_create(&rt_timer_args, &cbinfo->alarm_hdl); if (ret < 0) { rtcerr("ERROR: Failed to create rt_timer error=%d\n", ret); spin_unlock_irqrestore(NULL, flags); return ret; } } cbinfo->ac_cb = alminfo->as_cb; cbinfo->ac_arg = alminfo->as_arg; cbinfo->deadline_us = alminfo->as_time.tv_sec * USEC_PER_SEC + alminfo->as_time.tv_nsec / NSEC_PER_USEC; if (cbinfo->alarm_hdl == NULL) { rtcerr("ERROR: failed to create alarm timer\n"); } else { rtcinfo("Start RTC alarm.\n"); rt_timer_start(cbinfo->alarm_hdl, cbinfo->deadline_us, false); ret = OK; } spin_unlock_irqrestore(NULL, flags); } return ret; } /**************************************************************************** * Name: up_rtc_cancelalarm * * Description: * Cancel an alarm. * * Input Parameters: * alarmid - Identifies the alarm to be cancelled * * Returned Value: * Zero (OK) on success; a negated errno on failure * ****************************************************************************/ int up_rtc_cancelalarm(enum alm_id_e alarmid) { struct alm_cbinfo_s *cbinfo; irqstate_t flags; int ret = -ENODATA; DEBUGASSERT((RTC_ALARM0 <= alarmid) && (alarmid < RTC_ALARM_LAST)); /* Set the alarm in hardware and enable interrupts */ cbinfo = &g_alarmcb[alarmid]; if (cbinfo->ac_cb != NULL) { flags = spin_lock_irqsave(NULL); /* Stop and delete the alarm */ rtcinfo("Cancel RTC alarm.\n"); rt_timer_stop(cbinfo->alarm_hdl); rt_timer_delete(cbinfo->alarm_hdl); cbinfo->ac_cb = NULL; cbinfo->deadline_us = 0; cbinfo->alarm_hdl = NULL; spin_unlock_irqrestore(NULL, flags); ret = OK; } return ret; } /**************************************************************************** * Name: up_rtc_rdalarm * * Description: * Query an alarm configured in hardware. * * Input Parameters: * tp - Location to return the timer match register. * alarmid - Identifies the alarm to get. * * Returned Value: * Zero (OK) on success; a negated errno on failure * ****************************************************************************/ int up_rtc_rdalarm(struct timespec *tp, uint32_t alarmid) { irqstate_t flags; struct alm_cbinfo_s *cbinfo; DEBUGASSERT(tp != NULL); DEBUGASSERT((RTC_ALARM0 <= alarmid) && (alarmid < RTC_ALARM_LAST)); flags = spin_lock_irqsave(NULL); /* Get the alarm according to the alarmid */ cbinfo = &g_alarmcb[alarmid]; tp->tv_sec = (rt_timer_time_us() + g_rtc_save->offset + cbinfo->deadline_us) / USEC_PER_SEC; tp->tv_nsec = ((rt_timer_time_us() + g_rtc_save->offset + cbinfo->deadline_us) % USEC_PER_SEC) * NSEC_PER_USEC; spin_unlock_irqrestore(NULL, flags); return OK; } #endif /* CONFIG_RTC_ALARM */ /**************************************************************************** * Name: up_rtc_timer_init * * Description: * Init RTC timer. * * Input Parameters: * None * * Returned Value: * Zero (OK) on success; a negated errno on failure * ****************************************************************************/ int up_rtc_timer_init(void) { /* RT-Timer enabled */ g_rt_timer_enabled = true; /* Get the time difference between rt_timer and RTC timer */ g_rtc_save->offset = esp32s2_rtc_get_time_us() - rt_timer_time_us(); return OK; }