diff --git a/components/esp_hw_support/clk_ctrl_os.c b/components/esp_hw_support/clk_ctrl_os.c index add12e4bb2..76b683e300 100644 --- a/components/esp_hw_support/clk_ctrl_os.c +++ b/components/esp_hw_support/clk_ctrl_os.c @@ -193,7 +193,7 @@ esp_err_t IRAM_ATTR periph_rtc_mpll_freq_set(uint32_t expt_freq_hz, uint32_t *re * But when more than one peripheral refers MPLL, its frequency is not allowed to change once it is set */ if (s_cur_mpll_freq_hz == 0 || s_mpll_ref_cnt < 2) { uint32_t xtal_freq_mhz = clk_ll_xtal_load_freq_mhz(); - rtc_clk_mpll_configure(xtal_freq_mhz, expt_freq_hz / MHZ); + rtc_clk_mpll_configure(xtal_freq_mhz, expt_freq_hz / MHZ, false); s_cur_mpll_freq_hz = clk_ll_mpll_get_freq_mhz(xtal_freq_mhz) * MHZ; } else { ret = ESP_ERR_INVALID_STATE; diff --git a/components/esp_hw_support/include/esp_private/esp_sleep_internal.h b/components/esp_hw_support/include/esp_private/esp_sleep_internal.h index 19f02f7ae3..bb5e1899ee 100644 --- a/components/esp_hw_support/include/esp_private/esp_sleep_internal.h +++ b/components/esp_hw_support/include/esp_private/esp_sleep_internal.h @@ -161,6 +161,15 @@ void esp_sleep_mmu_retention(bool backup_or_restore); bool mmu_domain_pd_allowed(void); #endif +/** + * @brief Notify the sleep process that `sleep_time_overhead_out` needs to be remeasured, which must be called + * in the following scenarios: + * 1. When the CPU frequency changes to below the crystal oscillator frequency. + * 2. When a new callback function is registered in the sleep process. + * 3. Other events occur that affect the execution time of the CPU sleep process. + */ +void esp_sleep_overhead_out_time_refresh(void); + #ifdef __cplusplus } #endif diff --git a/components/esp_hw_support/include/esp_private/regi2c_ctrl.h b/components/esp_hw_support/include/esp_private/regi2c_ctrl.h index 6be822733b..fcdebb41ac 100644 --- a/components/esp_hw_support/include/esp_private/regi2c_ctrl.h +++ b/components/esp_hw_support/include/esp_private/regi2c_ctrl.h @@ -1,5 +1,5 @@ /* - * SPDX-FileCopyrightText: 2015-2024 Espressif Systems (Shanghai) CO LTD + * SPDX-FileCopyrightText: 2015-2025 Espressif Systems (Shanghai) CO LTD * * SPDX-License-Identifier: Apache-2.0 */ @@ -70,7 +70,8 @@ extern "C" { #define regi2c_ctrl_read_reg_mask regi2c_read_reg_mask_raw #define regi2c_ctrl_write_reg regi2c_write_reg_raw #define regi2c_ctrl_write_reg_mask regi2c_write_reg_mask_raw - +#define REGI2C_ENTER_CRITICAL() +#define REGI2C_EXIT_CRITICAL() #else /* Access internal registers, don't use in application */ @@ -82,7 +83,8 @@ void regi2c_ctrl_write_reg_mask(uint8_t block, uint8_t host_id, uint8_t reg_add, /* enter the critical section that protects internal registers. Don't use it in SDK. Use the functions above. */ void regi2c_enter_critical(void); void regi2c_exit_critical(void); - +#define REGI2C_ENTER_CRITICAL() regi2c_enter_critical() +#define REGI2C_EXIT_CRITICAL() regi2c_exit_critical() #endif // NON_OS_BUILD /* Convenience macros for the above functions, these use register definitions diff --git a/components/esp_hw_support/include/esp_private/rtc_clk.h b/components/esp_hw_support/include/esp_private/rtc_clk.h index 4480780217..9a27820d96 100644 --- a/components/esp_hw_support/include/esp_private/rtc_clk.h +++ b/components/esp_hw_support/include/esp_private/rtc_clk.h @@ -1,5 +1,5 @@ /* - * SPDX-FileCopyrightText: 2022-2024 Espressif Systems (Shanghai) CO LTD + * SPDX-FileCopyrightText: 2022-2025 Espressif Systems (Shanghai) CO LTD * * SPDX-License-Identifier: Apache-2.0 */ @@ -22,11 +22,24 @@ extern "C" { * source to XTAL (except for S2). * * Currently, this function is only called in `esp_restart_noos` and `esp_restart_noos_dig` to switch the CPU - * clock source back to XTAL (by default) before reset, and in `esp_sleep_start` to switch CPU clock source to XTAL - * before entering sleep for PMU supported chips. + * clock source back to XTAL (by default) before reset. */ void rtc_clk_cpu_set_to_default_config(void); +/** + * @brief Switch CPU clock source to XTAL, the PLL has different processing methods for different chips. + * 1. For earlier chips without PMU, there is no PMU module that can turn off the CPU's PLL, so it has to be + * disabled at here to save the power consumption. Though ESP32C3/S3 has USB CDC device, it can not function + * properly during sleep due to the lack of APB clock (before C6, USJ relies on APB clock to work). Therefore, + * we will always disable CPU's PLL (i.e. BBPLL). + * 2. For PMU supported chips, CPU's PLL power can be turned off by PMU, so no need to disable the PLL at here. + * Leaving PLL on at this stage also helps USJ keep connection and retention operation (if they rely on this PLL). + * For ESP32P4, if the APB frequency is configured as the hardware default value (10MHz), this will cause the + * regdma backup/restore to not achieve optimal performance. The MEM/APB frequency divider needs to be configured + * to 40MHz to speed up the retention speed. + */ +void rtc_clk_cpu_freq_set_xtal_for_sleep(void); + /** * @brief Notify that the BBPLL has a new in-use consumer * @@ -58,8 +71,9 @@ void rtc_clk_mpll_disable(void); * * @param[in] xtal_freq XTAL frequency * @param[in] mpll_freq MPLL frequency + * @param[in] thread_safe Set true if called from thread safe context, which will save the time of taking spin lock. */ -void rtc_clk_mpll_configure(uint32_t xtal_freq, uint32_t mpll_freq); +void rtc_clk_mpll_configure(uint32_t xtal_freq, uint32_t mpll_freq, bool thread_safe); /** * Get the MPLL frequency diff --git a/components/esp_hw_support/lowpower/port/esp32p4/sleep_clock.c b/components/esp_hw_support/lowpower/port/esp32p4/sleep_clock.c index bae30d29d5..36c06306b9 100644 --- a/components/esp_hw_support/lowpower/port/esp32p4/sleep_clock.c +++ b/components/esp_hw_support/lowpower/port/esp32p4/sleep_clock.c @@ -18,9 +18,9 @@ esp_err_t sleep_clock_system_retention_init(void *arg) const static sleep_retention_entries_config_t pcr_regs_retention[] = { /* Enable i2c master clock */ - [0] = { .config = REGDMA_LINK_WRITE_INIT (REGDMA_PCR_LINK(0), LPPERI_CLK_EN_REG, LPPERI_CK_EN_LP_I2CMST, LPPERI_CK_EN_LP_I2CMST_M, 1, 1), .owner = ENTRY(0) }, + [0] = { .config = REGDMA_LINK_WRITE_INIT (REGDMA_PCR_LINK(0), LPPERI_CLK_EN_REG, LPPERI_CK_EN_LP_I2CMST, LPPERI_CK_EN_LP_I2CMST_M, 1, 0), .owner = ENTRY(0) }, /* Start SYSPLL self-calibration */ - [1] = { .config = REGDMA_LINK_WRITE_INIT (REGDMA_PCR_LINK(1), HP_SYS_CLKRST_ANA_PLL_CTRL0_REG, 0, HP_SYS_CLKRST_REG_SYS_PLL_CAL_STOP_M, 1, 1), .owner = ENTRY(0) }, + [1] = { .config = REGDMA_LINK_WRITE_INIT (REGDMA_PCR_LINK(1), HP_SYS_CLKRST_ANA_PLL_CTRL0_REG, 0, HP_SYS_CLKRST_REG_SYS_PLL_CAL_STOP_M, 1, 0), .owner = ENTRY(0) }, /* Wait calibration done */ [2] = { .config = REGDMA_LINK_WAIT_INIT (REGDMA_PCR_LINK(2), HP_SYS_CLKRST_ANA_PLL_CTRL0_REG, HP_SYS_CLKRST_REG_SYS_PLL_CAL_END, HP_SYS_CLKRST_REG_SYS_PLL_CAL_END_M, 1, 0), .owner = ENTRY(0) }, /* Stop SYSPLL self-calibration */ diff --git a/components/esp_hw_support/port/esp32/rtc_clk.c b/components/esp_hw_support/port/esp32/rtc_clk.c index 7646c98891..85219d6a5a 100644 --- a/components/esp_hw_support/port/esp32/rtc_clk.c +++ b/components/esp_hw_support/port/esp32/rtc_clk.c @@ -426,6 +426,11 @@ void rtc_clk_cpu_set_to_default_config(void) rtc_clk_wait_for_slow_cycle(); } +void rtc_clk_cpu_freq_set_xtal_for_sleep(void) +{ + rtc_clk_cpu_freq_set_xtal(); +} + bool rtc_clk_cpu_freq_mhz_to_config(uint32_t freq_mhz, rtc_cpu_freq_config_t* out_config) { uint32_t source_freq_mhz; diff --git a/components/esp_hw_support/port/esp32c2/rtc_clk.c b/components/esp_hw_support/port/esp32c2/rtc_clk.c index 40fd737c3f..044f3a4ed5 100644 --- a/components/esp_hw_support/port/esp32c2/rtc_clk.c +++ b/components/esp_hw_support/port/esp32c2/rtc_clk.c @@ -296,6 +296,11 @@ void rtc_clk_cpu_set_to_default_config(void) rtc_clk_cpu_freq_to_xtal(freq_mhz, 1); } +void rtc_clk_cpu_freq_set_xtal_for_sleep(void) +{ + rtc_clk_cpu_freq_set_xtal(); +} + /** * Switch to use XTAL as the CPU clock source. * Must satisfy: cpu_freq = XTAL_FREQ / div. diff --git a/components/esp_hw_support/port/esp32c3/rtc_clk.c b/components/esp_hw_support/port/esp32c3/rtc_clk.c index ed9aaf2b1d..4328ab28d5 100644 --- a/components/esp_hw_support/port/esp32c3/rtc_clk.c +++ b/components/esp_hw_support/port/esp32c3/rtc_clk.c @@ -325,6 +325,11 @@ void rtc_clk_cpu_set_to_default_config(void) rtc_clk_cpu_freq_to_xtal(freq_mhz, 1); } +void rtc_clk_cpu_freq_set_xtal_for_sleep(void) +{ + rtc_clk_cpu_freq_set_xtal(); +} + /** * Switch to use XTAL as the CPU clock source. * Must satisfy: cpu_freq = XTAL_FREQ / div. diff --git a/components/esp_hw_support/port/esp32c5/rtc_clk.c b/components/esp_hw_support/port/esp32c5/rtc_clk.c index 341dc58ca6..b585c29867 100644 --- a/components/esp_hw_support/port/esp32c5/rtc_clk.c +++ b/components/esp_hw_support/port/esp32c5/rtc_clk.c @@ -386,6 +386,11 @@ void rtc_clk_cpu_set_to_default_config(void) s_cur_pll_freq = 0; // no disable PLL, but set freq to 0 to trigger a PLL calibration after wake-up from sleep } +void rtc_clk_cpu_freq_set_xtal_for_sleep(void) +{ + rtc_clk_cpu_set_to_default_config(); +} + void rtc_clk_cpu_freq_to_pll_and_pll_lock_release(int cpu_freq_mhz) { // TODO: IDF-8641 CPU_MAX_FREQ don't know what to do... pll_240 or pll_160... diff --git a/components/esp_hw_support/port/esp32c6/rtc_clk.c b/components/esp_hw_support/port/esp32c6/rtc_clk.c index 1ddf54eda3..20027f936d 100644 --- a/components/esp_hw_support/port/esp32c6/rtc_clk.c +++ b/components/esp_hw_support/port/esp32c6/rtc_clk.c @@ -340,6 +340,11 @@ void rtc_clk_cpu_set_to_default_config(void) s_cur_pll_freq = 0; // no disable PLL, but set freq to 0 to trigger a PLL calibration after wake-up from sleep } +void rtc_clk_cpu_freq_set_xtal_for_sleep(void) +{ + rtc_clk_cpu_set_to_default_config(); +} + void rtc_clk_cpu_freq_to_pll_and_pll_lock_release(int cpu_freq_mhz) { rtc_clk_cpu_freq_to_pll_mhz(cpu_freq_mhz); diff --git a/components/esp_hw_support/port/esp32c61/rtc_clk.c b/components/esp_hw_support/port/esp32c61/rtc_clk.c index ab577f46b5..400a6f2cde 100644 --- a/components/esp_hw_support/port/esp32c61/rtc_clk.c +++ b/components/esp_hw_support/port/esp32c61/rtc_clk.c @@ -329,6 +329,11 @@ void rtc_clk_cpu_set_to_default_config(void) s_cur_pll_freq = 0; // no disable PLL, but set freq to 0 to trigger a PLL calibration after wake-up from sleep } +void rtc_clk_cpu_freq_set_xtal_for_sleep(void) +{ + rtc_clk_cpu_set_to_default_config(); +} + void rtc_clk_cpu_freq_to_pll_and_pll_lock_release(int cpu_freq_mhz) { rtc_clk_cpu_freq_to_pll_160_mhz(cpu_freq_mhz); diff --git a/components/esp_hw_support/port/esp32h2/rtc_clk.c b/components/esp_hw_support/port/esp32h2/rtc_clk.c index 0260707ec5..a089b29a8b 100644 --- a/components/esp_hw_support/port/esp32h2/rtc_clk.c +++ b/components/esp_hw_support/port/esp32h2/rtc_clk.c @@ -400,6 +400,11 @@ void rtc_clk_cpu_set_to_default_config(void) s_cur_pll_freq = 0; // no disable PLL, but set freq to 0 to trigger a PLL calibration after wake-up from sleep } +void rtc_clk_cpu_freq_set_xtal_for_sleep(void) +{ + rtc_clk_cpu_set_to_default_config(); +} + soc_xtal_freq_t rtc_clk_xtal_freq_get(void) { uint32_t xtal_freq_mhz = clk_ll_xtal_load_freq_mhz(); diff --git a/components/esp_hw_support/port/esp32h21/rtc_clk.c b/components/esp_hw_support/port/esp32h21/rtc_clk.c index 2a4632e399..9e15e2d7af 100644 --- a/components/esp_hw_support/port/esp32h21/rtc_clk.c +++ b/components/esp_hw_support/port/esp32h21/rtc_clk.c @@ -400,6 +400,11 @@ void rtc_clk_cpu_set_to_default_config(void) s_cur_pll_freq = 0; // no disable PLL, but set freq to 0 to trigger a PLL calibration after wake-up from sleep } +void rtc_clk_cpu_freq_set_xtal_for_sleep(void) +{ + rtc_clk_cpu_set_to_default_config(); +} + soc_xtal_freq_t rtc_clk_xtal_freq_get(void) { uint32_t xtal_freq_mhz = clk_ll_xtal_load_freq_mhz(); diff --git a/components/esp_hw_support/port/esp32h4/rtc_clk.c b/components/esp_hw_support/port/esp32h4/rtc_clk.c index 3f65e49f4e..586e000ea1 100644 --- a/components/esp_hw_support/port/esp32h4/rtc_clk.c +++ b/components/esp_hw_support/port/esp32h4/rtc_clk.c @@ -338,6 +338,11 @@ void rtc_clk_cpu_set_to_default_config(void) rtc_clk_cpu_freq_to_xtal(freq_mhz, 1); } +void rtc_clk_cpu_freq_set_xtal_for_sleep(void) +{ + rtc_clk_cpu_set_to_default_config(); +} + void rtc_clk_cpu_freq_to_pll_and_pll_lock_release(int cpu_freq_mhz) { rtc_clk_cpu_freq_to_pll_mhz(cpu_freq_mhz); diff --git a/components/esp_hw_support/port/esp32p4/pmu_sleep.c b/components/esp_hw_support/port/esp32p4/pmu_sleep.c index 444117441b..a2351d85ee 100644 --- a/components/esp_hw_support/port/esp32p4/pmu_sleep.c +++ b/components/esp_hw_support/port/esp32p4/pmu_sleep.c @@ -370,17 +370,17 @@ FORCE_INLINE_ATTR void pmu_sleep_cache_sync_items(uint32_t gid, uint32_t type, u static TCM_DRAM_ATTR uint32_t s_mpll_freq_mhz_before_sleep = 0; +__attribute__((optimize("-O2"))) TCM_IRAM_ATTR uint32_t pmu_sleep_start(uint32_t wakeup_opt, uint32_t reject_opt, uint32_t lslp_mem_inf_fpu, bool dslp) { lp_aon_hal_inform_wakeup_type(dslp); - assert(PMU_instance()->hal); - pmu_ll_hp_set_wakeup_enable(PMU_instance()->hal->dev, wakeup_opt); - pmu_ll_hp_set_reject_enable(PMU_instance()->hal->dev, reject_opt); + pmu_ll_hp_set_wakeup_enable(&PMU, wakeup_opt); + pmu_ll_hp_set_reject_enable(&PMU, reject_opt); - pmu_ll_hp_clear_wakeup_intr_status(PMU_instance()->hal->dev); - pmu_ll_hp_clear_reject_intr_status(PMU_instance()->hal->dev); - pmu_ll_hp_clear_reject_cause(PMU_instance()->hal->dev); + pmu_ll_hp_clear_wakeup_intr_status(&PMU); + pmu_ll_hp_clear_reject_intr_status(&PMU); + pmu_ll_hp_clear_reject_cause(&PMU); // 1. For the sleep where powered down the TOP domain, the L1 cache data memory will be lost and needs to be written back here. // 2. For the sleep without power down the TOP domain, regdma retention may still be enabled, and dirty data in the L1 cache needs @@ -421,13 +421,13 @@ TCM_IRAM_ATTR uint32_t pmu_sleep_start(uint32_t wakeup_opt, uint32_t reject_opt, // The PMU state machine will switch PAD to sleep setting and do IO holding at the same stage. // IO may be held to an indeterminate state, so the software needs to trigger the PAD to switch // to the sleep setting before starting the PMU state machine. - pmu_ll_imm_set_pad_slp_sel(PMU_instance()->hal->dev, true); + pmu_ll_imm_set_pad_slp_sel(&PMU, true); /* Start entry into sleep mode */ - pmu_ll_hp_set_sleep_enable(PMU_instance()->hal->dev); + pmu_ll_hp_set_sleep_enable(&PMU); - while (!pmu_ll_hp_is_sleep_wakeup(PMU_instance()->hal->dev) && - !pmu_ll_hp_is_sleep_reject(PMU_instance()->hal->dev)) { + while (!pmu_ll_hp_is_sleep_wakeup(&PMU) && + !pmu_ll_hp_is_sleep_reject(&PMU)) { ; } @@ -451,7 +451,7 @@ TCM_IRAM_ATTR bool pmu_sleep_finish(bool dslp) { pmu_ll_hp_set_dcm_vset(&PMU, PMU_MODE_HP_ACTIVE, HP_CALI_ACTIVE_DCM_VSET_DEFAULT); pmu_sleep_enable_dcdc(); - if (pmu_ll_hp_is_sleep_reject(PMU_instance()->hal->dev)) { + if (pmu_ll_hp_is_sleep_reject(&PMU)) { // If sleep is rejected, the hardware wake-up process that turns on DCDC // is skipped, and wait DCDC volt rise up by software here. esp_rom_delay_us(950); @@ -459,14 +459,14 @@ TCM_IRAM_ATTR bool pmu_sleep_finish(bool dslp) pmu_sleep_shutdown_ldo(); } - pmu_ll_imm_set_pad_slp_sel(PMU_instance()->hal->dev, false); + pmu_ll_imm_set_pad_slp_sel(&PMU, false); // Wait eFuse memory update done. while(efuse_ll_get_controller_state() != EFUSE_CONTROLLER_STATE_IDLE); if (s_mpll_freq_mhz_before_sleep && !dslp) { rtc_clk_mpll_enable(); - rtc_clk_mpll_configure(clk_hal_xtal_get_freq_mhz(), s_mpll_freq_mhz_before_sleep); + rtc_clk_mpll_configure(clk_hal_xtal_get_freq_mhz(), s_mpll_freq_mhz_before_sleep, true); #if CONFIG_SPIRAM if (!s_pmu_sleep_regdma_backup_enabled) { // MSPI2 and MSPI3 share the register for core clock. So we only set MSPI2 here. @@ -484,7 +484,7 @@ TCM_IRAM_ATTR bool pmu_sleep_finish(bool dslp) REGI2C_WRITE_MASK(I2C_CPLL, I2C_CPLL_OC_DIV_7_0, 6); // lower default cpu_pll freq to 400M REGI2C_WRITE_MASK(I2C_SYSPLL, I2C_SYSPLL_OC_DIV_7_0, 8); // lower default sys_pll freq to 480M } - return pmu_ll_hp_is_sleep_reject(PMU_instance()->hal->dev); + return pmu_ll_hp_is_sleep_reject(&PMU); } uint32_t pmu_sleep_get_wakup_retention_cost(void) diff --git a/components/esp_hw_support/port/esp32p4/rtc_clk.c b/components/esp_hw_support/port/esp32p4/rtc_clk.c index 951beac948..d6e1db2779 100644 --- a/components/esp_hw_support/port/esp32p4/rtc_clk.c +++ b/components/esp_hw_support/port/esp32p4/rtc_clk.c @@ -1,5 +1,5 @@ /* - * SPDX-FileCopyrightText: 2022-2024 Espressif Systems (Shanghai) CO LTD + * SPDX-FileCopyrightText: 2022-2025 Espressif Systems (Shanghai) CO LTD * * SPDX-License-Identifier: Apache-2.0 */ @@ -438,6 +438,13 @@ void rtc_clk_cpu_set_to_default_config(void) int freq_mhz = (int)rtc_clk_xtal_freq_get(); rtc_clk_cpu_freq_to_xtal(freq_mhz, 1, true); +} + +void rtc_clk_cpu_freq_set_xtal_for_sleep(void) +{ + int freq_mhz = (int)rtc_clk_xtal_freq_get(); + + rtc_clk_cpu_freq_to_xtal(freq_mhz, 1, false); s_cur_cpll_freq = 0; // no disable PLL, but set freq to 0 to trigger a PLL calibration after wake-up from sleep } @@ -585,10 +592,17 @@ TCM_IRAM_ATTR void rtc_clk_mpll_enable(void) clk_ll_mpll_enable(); } -void rtc_clk_mpll_configure(uint32_t xtal_freq, uint32_t mpll_freq) +void rtc_clk_mpll_configure(uint32_t xtal_freq, uint32_t mpll_freq, bool thread_safe) { /* Analog part */ - ANALOG_CLOCK_ENABLE(); + if (thread_safe) { + _regi2c_ctrl_ll_master_enable_clock(true); + } else { + ANALOG_CLOCK_ENABLE(); +#if !BOOTLOADER_BUILD + regi2c_enter_critical(); +#endif + } /* MPLL calibration start */ regi2c_ctrl_ll_mpll_calibration_start(); clk_ll_mpll_set_config(mpll_freq, xtal_freq); @@ -596,8 +610,15 @@ void rtc_clk_mpll_configure(uint32_t xtal_freq, uint32_t mpll_freq) while(!regi2c_ctrl_ll_mpll_calibration_is_done()); /* MPLL calibration stop */ regi2c_ctrl_ll_mpll_calibration_stop(); - ANALOG_CLOCK_DISABLE(); + if (thread_safe) { + _regi2c_ctrl_ll_master_enable_clock(false); + } else { +#if !BOOTLOADER_BUILD + regi2c_exit_critical(); +#endif + ANALOG_CLOCK_DISABLE(); + } s_cur_mpll_freq = mpll_freq; } diff --git a/components/esp_hw_support/port/esp32s2/rtc_clk.c b/components/esp_hw_support/port/esp32s2/rtc_clk.c index a284c043ba..f2111b73e7 100644 --- a/components/esp_hw_support/port/esp32s2/rtc_clk.c +++ b/components/esp_hw_support/port/esp32s2/rtc_clk.c @@ -446,6 +446,11 @@ void rtc_clk_cpu_set_to_default_config(void) rtc_clk_cpu_freq_to_xtal(CLK_LL_XTAL_FREQ_MHZ, 1); } +void rtc_clk_cpu_freq_set_xtal_for_sleep(void) +{ + rtc_clk_cpu_freq_set_xtal(); +} + /** * Switch to use XTAL as the CPU clock source. * Must satisfy: cpu_freq = XTAL_FREQ / div. diff --git a/components/esp_hw_support/port/esp32s3/rtc_clk.c b/components/esp_hw_support/port/esp32s3/rtc_clk.c index 22dbbafa49..ba9986d3cc 100644 --- a/components/esp_hw_support/port/esp32s3/rtc_clk.c +++ b/components/esp_hw_support/port/esp32s3/rtc_clk.c @@ -387,6 +387,11 @@ void rtc_clk_cpu_set_to_default_config(void) rtc_clk_cpu_freq_to_xtal(freq_mhz, 1); } +void rtc_clk_cpu_freq_set_xtal_for_sleep(void) +{ + rtc_clk_cpu_freq_set_xtal(); +} + /** * Switch to use XTAL as the CPU clock source. * Must satisfy: cpu_freq = XTAL_FREQ / div. diff --git a/components/esp_hw_support/sleep_gpio.c b/components/esp_hw_support/sleep_gpio.c index 8d9d1b1417..efea60ec37 100644 --- a/components/esp_hw_support/sleep_gpio.c +++ b/components/esp_hw_support/sleep_gpio.c @@ -64,7 +64,7 @@ void esp_sleep_config_gpio_isolate(void) } } -#if CONFIG_ESP_SLEEP_MSPI_NEED_ALL_IO_PU +#if CONFIG_ESP_SLEEP_MSPI_NEED_ALL_IO_PU && !SOC_MSPI_HAS_INDEPENT_IOMUX gpio_sleep_set_pull_mode(esp_mspi_get_io(ESP_MSPI_IO_CLK), GPIO_PULLUP_ONLY); gpio_sleep_set_pull_mode(esp_mspi_get_io(ESP_MSPI_IO_Q), GPIO_PULLUP_ONLY); gpio_sleep_set_pull_mode(esp_mspi_get_io(ESP_MSPI_IO_D), GPIO_PULLUP_ONLY); @@ -104,14 +104,14 @@ void esp_sleep_enable_gpio_switch(bool enable) } #endif /* If the PSRAM is disable in ESP32xx chips equipped with PSRAM, there will be a large current leakage. */ -#if CONFIG_ESP_SLEEP_PSRAM_LEAKAGE_WORKAROUND && CONFIG_SPIRAM +#if CONFIG_ESP_SLEEP_PSRAM_LEAKAGE_WORKAROUND && CONFIG_SPIRAM & !SOC_MSPI_HAS_INDEPENT_IOMUX if (gpio_num == esp_mspi_get_io(ESP_MSPI_IO_CS1)) { gpio_sleep_sel_dis(gpio_num); continue; } #endif // CONFIG_ESP_SLEEP_PSRAM_LEAKAGE_WORKAROUND && CONFIG_SPIRAM -#if CONFIG_ESP_SLEEP_FLASH_LEAKAGE_WORKAROUND +#if CONFIG_ESP_SLEEP_FLASH_LEAKAGE_WORKAROUND & !SOC_MSPI_HAS_INDEPENT_IOMUX if (gpio_num == esp_mspi_get_io(ESP_MSPI_IO_CS0)) { gpio_sleep_sel_dis(gpio_num); continue; diff --git a/components/esp_hw_support/sleep_modes.c b/components/esp_hw_support/sleep_modes.c index b0155b294a..1417f5d59a 100644 --- a/components/esp_hw_support/sleep_modes.c +++ b/components/esp_hw_support/sleep_modes.c @@ -271,6 +271,7 @@ typedef struct { #if SOC_DCDC_SUPPORTED uint64_t rtc_ticks_at_ldo_prepare; #endif + bool overhead_out_need_remeasure; } sleep_config_t; @@ -302,7 +303,8 @@ static sleep_config_t s_config = { .lock = portMUX_INITIALIZER_UNLOCKED, .ccount_ticks_record = 0, .sleep_time_overhead_out = DEFAULT_SLEEP_OUT_OVERHEAD_US, - .wakeup_triggers = 0 + .wakeup_triggers = 0, + .overhead_out_need_remeasure = true }; /* Internal variable used to track if light sleep wakeup sources are to be @@ -317,6 +319,12 @@ static const char *TAG = "sleep"; static RTC_FAST_ATTR int32_t s_sleep_sub_mode_ref_cnt[ESP_SLEEP_MODE_MAX] = { 0 }; //in this mode, 2uA is saved, but RTC memory can't use at high temperature, and RTCIO can't be used as INPUT. +void esp_sleep_overhead_out_time_refresh(void) +{ + portENTER_CRITICAL(&s_config.lock); + s_config.overhead_out_need_remeasure = true; + portEXIT_CRITICAL(&s_config.lock); +} static uint32_t get_power_down_flags(void); static uint32_t get_sleep_flags(uint32_t pd_flags, bool deepsleep); @@ -983,16 +991,7 @@ static esp_err_t SLEEP_FN_ATTR esp_sleep_start(uint32_t sleep_flags, uint32_t cl // Save current frequency and switch to XTAL rtc_cpu_freq_config_t cpu_freq_config; rtc_clk_cpu_freq_get_config(&cpu_freq_config); -#if SOC_PMU_SUPPORTED - // For PMU supported chips, CPU's PLL power can be turned off by PMU, so no need to disable the PLL at here. - // Leaving PLL on at this stage also helps USJ keep connection and retention operation (if they rely on this PLL). - rtc_clk_cpu_set_to_default_config(); -#else - // For earlier chips, there is no PMU module that can turn off the CPU's PLL, so it has to be disabled at here to save the power consumption. - // Though ESP32C3/S3 has USB CDC device, it can not function properly during sleep due to the lack of APB clock (before C6, USJ relies on APB clock to work). - // Therefore, we will always disable CPU's PLL (i.e. BBPLL). - rtc_clk_cpu_freq_set_xtal(); -#endif + rtc_clk_cpu_freq_set_xtal_for_sleep(); #if SOC_PM_SUPPORT_EXT0_WAKEUP // Configure pins for external wakeup @@ -1446,6 +1445,16 @@ esp_err_t esp_light_sleep_start(void) // Re-calibrate the RTC clock sleep_low_power_clock_calibration(false); + if (s_config.overhead_out_need_remeasure) { + uint32_t cur_cpu_freq = esp_clk_cpu_freq() / MHZ; + uint32_t xtal_freq = rtc_clk_xtal_freq_get(); + if (cur_cpu_freq < xtal_freq) { + s_config.sleep_time_overhead_out = DEFAULT_SLEEP_OUT_OVERHEAD_US * xtal_freq / cur_cpu_freq; + } else { + s_config.sleep_time_overhead_out = DEFAULT_SLEEP_OUT_OVERHEAD_US; + } + } + /* * Adjustment time consists of parts below: * 1. Hardware time waiting for internal 8M oscillate clock and XTAL; @@ -1610,6 +1619,7 @@ esp_err_t esp_light_sleep_start(void) if (s_light_sleep_wakeup) { s_config.sleep_time_overhead_out = (esp_cpu_get_cycle_count() - s_config.ccount_ticks_record) / (esp_clk_cpu_freq() / 1000000ULL); + s_config.overhead_out_need_remeasure = false; } portEXIT_CRITICAL(&s_config.lock); @@ -1704,8 +1714,10 @@ esp_err_t esp_sleep_enable_timer_wakeup(uint64_t time_in_us) return ESP_ERR_INVALID_ARG; } #endif + portENTER_CRITICAL(&s_config.lock); s_config.wakeup_triggers |= RTC_TIMER_TRIG_EN; s_config.sleep_duration = time_in_us; + portEXIT_CRITICAL(&s_config.lock); return ESP_OK; } @@ -1747,9 +1759,10 @@ static SLEEP_FN_ATTR esp_err_t timer_wakeup_prepare(int64_t sleep_duration) #if SOC_LP_TIMER_SUPPORTED #if CONFIG_PM_POWER_DOWN_PERIPHERAL_IN_LIGHT_SLEEP + int64_t backup_cost_ticks = rtc_time_us_to_slowclk(((pmu_sleep_machine_constant_t *)PMU_instance()->mc)->hp.regdma_a2s_work_time_us, s_config.rtc_clk_cal_period); // Last timer wake-up validity check if ((sleep_duration == 0) || \ - (target_wakeup_tick < rtc_time_get() + SLEEP_TIMER_ALARM_TO_SLEEP_TICKS)) { + (target_wakeup_tick < rtc_time_get() + SLEEP_TIMER_ALARM_TO_SLEEP_TICKS + backup_cost_ticks)) { // Treat too short sleep duration setting as timer reject return ESP_ERR_SLEEP_REJECT; } diff --git a/components/esp_pm/pm_impl.c b/components/esp_pm/pm_impl.c index d320bd150b..7eac897c8c 100644 --- a/components/esp_pm/pm_impl.c +++ b/components/esp_pm/pm_impl.c @@ -20,6 +20,7 @@ #include "esp_clk_tree.h" #include "soc/soc_caps.h" +#include "esp_private/esp_sleep_internal.h" #include "esp_private/crosscore_int.h" #include "esp_private/periph_ctrl.h" @@ -480,6 +481,7 @@ esp_err_t esp_pm_configure(const void* vconfig) // Enable the wakeup source here because the `esp_sleep_disable_wakeup_source` in the `else` // branch must be called if corresponding wakeup source is already enabled. esp_sleep_enable_timer_wakeup(0); + esp_sleep_overhead_out_time_refresh(); } else if (s_light_sleep_en) { // Since auto light-sleep will enable the timer wakeup source, to avoid affecting subsequent possible // deepsleep requests, disable the timer wakeup source here. diff --git a/components/hal/esp32p4/include/hal/clk_tree_ll.h b/components/hal/esp32p4/include/hal/clk_tree_ll.h index bb0273c8f8..7ecf2068bf 100644 --- a/components/hal/esp32p4/include/hal/clk_tree_ll.h +++ b/components/hal/esp32p4/include/hal/clk_tree_ll.h @@ -1,5 +1,5 @@ /* - * SPDX-FileCopyrightText: 2022-2024 Espressif Systems (Shanghai) CO LTD + * SPDX-FileCopyrightText: 2022-2025 Espressif Systems (Shanghai) CO LTD * * SPDX-License-Identifier: Apache-2.0 */ @@ -26,7 +26,7 @@ #include "esp32p4/rom/rtc.h" #include "hal/misc.h" #include "hal/efuse_hal.h" - +#include "esp_private/regi2c_ctrl.h" #ifdef __cplusplus extern "C" { @@ -414,9 +414,12 @@ static inline __attribute__((always_inline)) void clk_ll_cpll_set_config(uint32_ uint8_t i2c_cpll_lref = (oc_enb_fcal << I2C_CPLL_OC_ENB_FCAL_LSB) | (dchgp << I2C_CPLL_OC_DCHGP_LSB) | (div_ref); uint8_t i2c_cpll_div_7_0 = div7_0; uint8_t i2c_cpll_dcur = (1 << I2C_CPLL_OC_DLREF_SEL_LSB ) | (3 << I2C_CPLL_OC_DHREF_SEL_LSB) | dcur; - REGI2C_WRITE(I2C_CPLL, I2C_CPLL_OC_REF_DIV, i2c_cpll_lref); - REGI2C_WRITE(I2C_CPLL, I2C_CPLL_OC_DIV_7_0, i2c_cpll_div_7_0); - REGI2C_WRITE(I2C_CPLL, I2C_CPLL_OC_DCUR, i2c_cpll_dcur); + // There are sequential regi2c operations in `clk_ll_cpll_set_config`, use the raw regi2c API with one lock wrapper to save time. + REGI2C_ENTER_CRITICAL(); + esp_rom_regi2c_write(I2C_CPLL, I2C_CPLL_HOSTID, I2C_CPLL_OC_REF_DIV, i2c_cpll_lref); + esp_rom_regi2c_write(I2C_CPLL, I2C_CPLL_HOSTID, I2C_CPLL_OC_DIV_7_0, i2c_cpll_div_7_0); + esp_rom_regi2c_write(I2C_CPLL, I2C_CPLL_HOSTID, I2C_CPLL_OC_DCUR, i2c_cpll_dcur); + REGI2C_EXIT_CRITICAL(); } /** @@ -443,17 +446,17 @@ static inline __attribute__((always_inline)) void clk_ll_mpll_set_config(uint32_ { HAL_ASSERT(xtal_freq_mhz == SOC_XTAL_FREQ_40M); - uint8_t mpll_dhref_val = REGI2C_READ(I2C_MPLL, I2C_MPLL_DHREF); - REGI2C_WRITE(I2C_MPLL, I2C_MPLL_DHREF, mpll_dhref_val | (3 << I2C_MPLL_DHREF_LSB)); - uint8_t mpll_rstb_val = REGI2C_READ(I2C_MPLL, I2C_MPLL_IR_CAL_RSTB); - REGI2C_WRITE(I2C_MPLL, I2C_MPLL_IR_CAL_RSTB, mpll_rstb_val & 0xdf); - REGI2C_WRITE(I2C_MPLL, I2C_MPLL_IR_CAL_RSTB, mpll_rstb_val | (1 << I2C_MPLL_IR_CAL_RSTB_lSB)); + uint8_t mpll_dhref_val = esp_rom_regi2c_read(I2C_MPLL, I2C_MPLL_HOSTID, I2C_MPLL_DHREF); + esp_rom_regi2c_write(I2C_MPLL, I2C_MPLL_HOSTID, I2C_MPLL_DHREF, mpll_dhref_val | (3 << I2C_MPLL_DHREF_LSB)); + uint8_t mpll_rstb_val = esp_rom_regi2c_read(I2C_MPLL, I2C_MPLL_HOSTID, I2C_MPLL_IR_CAL_RSTB); + esp_rom_regi2c_write(I2C_MPLL, I2C_MPLL_HOSTID, I2C_MPLL_IR_CAL_RSTB, mpll_rstb_val & 0xdf); + esp_rom_regi2c_write(I2C_MPLL, I2C_MPLL_HOSTID, I2C_MPLL_IR_CAL_RSTB, mpll_rstb_val | (1 << I2C_MPLL_IR_CAL_RSTB_lSB)); // MPLL_Freq = XTAL_Freq * (div + 1) / (ref_div + 1) uint8_t ref_div = 1; uint8_t div = mpll_freq_mhz / 20 - 1; uint8_t val = ((div << 3) | ref_div); - REGI2C_WRITE(I2C_MPLL, I2C_MPLL_DIV_REG_ADDR, val); + esp_rom_regi2c_write(I2C_MPLL, I2C_MPLL_HOSTID, I2C_MPLL_DIV_REG_ADDR, val); } /** diff --git a/components/hal/platform_port/include/hal/regi2c_ctrl.h b/components/hal/platform_port/include/hal/regi2c_ctrl.h index b87fdb3108..dea715874d 100644 --- a/components/hal/platform_port/include/hal/regi2c_ctrl.h +++ b/components/hal/platform_port/include/hal/regi2c_ctrl.h @@ -29,4 +29,7 @@ #define REGI2C_READ(block, reg_add) \ esp_rom_regi2c_read(block, block##_HOSTID, reg_add) + + #define REGI2C_ENTER_CRITICAL() + #define REGI2C_EXIT_CRITICAL() #endif diff --git a/components/soc/esp32p4/include/soc/Kconfig.soc_caps.in b/components/soc/esp32p4/include/soc/Kconfig.soc_caps.in index 388b455194..7b19d4262c 100644 --- a/components/soc/esp32p4/include/soc/Kconfig.soc_caps.in +++ b/components/soc/esp32p4/include/soc/Kconfig.soc_caps.in @@ -1551,6 +1551,10 @@ config SOC_SPI_SUPPORT_CLK_SPLL bool default y +config SOC_MSPI_HAS_INDEPENT_IOMUX + bool + default y + config SOC_MEMSPI_IS_INDEPENDENT bool default y diff --git a/components/soc/esp32p4/include/soc/soc_caps.h b/components/soc/esp32p4/include/soc/soc_caps.h index 21752e65ae..3cb56d9322 100644 --- a/components/soc/esp32p4/include/soc/soc_caps.h +++ b/components/soc/esp32p4/include/soc/soc_caps.h @@ -573,6 +573,7 @@ // host_id = 0 -> SPI0/SPI1, host_id = 1 -> SPI2, #define SOC_SPI_PERIPH_SUPPORT_MULTILINE_MODE(host_id) ({(void)host_id; 1;}) +#define SOC_MSPI_HAS_INDEPENT_IOMUX 1 #define SOC_MEMSPI_IS_INDEPENDENT 1 #define SOC_SPI_MAX_PRE_DIVIDER 16 diff --git a/components/soc/esp32p4/include/soc/system_periph_retention.h b/components/soc/esp32p4/include/soc/system_periph_retention.h index 77fd2c3d77..56b867e487 100644 --- a/components/soc/esp32p4/include/soc/system_periph_retention.h +++ b/components/soc/esp32p4/include/soc/system_periph_retention.h @@ -52,7 +52,7 @@ extern const regdma_entries_config_t hp_system_regs_retention[HP_SYSTEM_RETENTIO * This is an internal function of the sleep retention driver, and is not * useful for external use. */ -#define IOMUX_RETENTION_LINK_LEN 3 +#define IOMUX_RETENTION_LINK_LEN 6 extern const regdma_entries_config_t iomux_regs_retention[IOMUX_RETENTION_LINK_LEN]; /** diff --git a/components/soc/esp32p4/system_retention_periph.c b/components/soc/esp32p4/system_retention_periph.c index 6f2be205a8..acbae1c86d 100644 --- a/components/soc/esp32p4/system_retention_periph.c +++ b/components/soc/esp32p4/system_retention_periph.c @@ -1,5 +1,5 @@ /* - * SPDX-FileCopyrightText: 2024 Espressif Systems (Shanghai) CO LTD + * SPDX-FileCopyrightText: 2024-2025 Espressif Systems (Shanghai) CO LTD * * SPDX-License-Identifier: Apache-2.0 */ @@ -80,13 +80,37 @@ const regdma_entries_config_t hp_system_regs_retention[] = { _Static_assert(ARRAY_SIZE(hp_system_regs_retention) == HP_SYSTEM_RETENTION_LINK_LEN, "Inconsistent HP_SYSTEM retention link length definitions"); /* IO MUX Registers Context */ -#define N_REGS_IOMUX_0() (((IO_MUX_GPIO54_REG - REG_IO_MUX_BASE) / 4) + 1) -#define N_REGS_GPIO_MTX() (((GPIO_ZERO_DET1_FILTER_CNT_REG - DR_REG_GPIO_BASE) / 4) + 1) +#define N_REGS_IOMUX() (((IO_MUX_GPIO54_REG - REG_IO_MUX_BASE) / 4) + 1) #define N_REGS_MSPI_IOMUX() (((IOMUX_MSPI_PIN_PSRAM_DQS_1_PIN0_REG - IOMUX_MSPI_PIN_CLK_EN0_REG) / 4) + 1) +#define N_REGS_GPIO_PINx() (((GPIO_PIN56_REG - GPIO_PIN0_REG) / 4) + 1) +#define N_REGS_GPIO_FUNCx() (((GPIO_FUNC56_OUT_SEL_CFG_REG - GPIO_FUNC1_IN_SEL_CFG_REG) / 4) + 1) + +#define GPIO_RETENTION_REGS_CNT0 6 +#define GPIO_RETENTION_REGS_CNT1 9 +#define GPIO_RETENTION_REGS_BASE0 (GPIO_OUT_REG) +#define GPIO_RETENTION_REGS_BASE1 (GPIO_CLOCK_GATE_REG) +static const uint32_t gpio_regs_map0[4] = {0x90489, 0x0, 0x0, 0x0}; +static const uint32_t gpio_regs_map1[4] = {0x1, 0x6fa000, 0x0, 0x0}; + const regdma_entries_config_t iomux_regs_retention[] = { - [0] = { .config = REGDMA_LINK_CONTINUOUS_INIT(REGDMA_IOMUX_LINK(0x00), REG_IO_MUX_BASE, REG_IO_MUX_BASE, N_REGS_IOMUX_0(), 0, 0), .owner = ENTRY(0) }, /* io_mux */ - [1] = { .config = REGDMA_LINK_CONTINUOUS_INIT(REGDMA_IOMUX_LINK(0x01), DR_REG_GPIO_BASE, DR_REG_GPIO_BASE, N_REGS_GPIO_MTX(), 0, 0), .owner = ENTRY(0) }, - [2] = { .config = REGDMA_LINK_CONTINUOUS_INIT(REGDMA_IOMUX_LINK(0x01), IOMUX_MSPI_PIN_CLK_EN0_REG, IOMUX_MSPI_PIN_CLK_EN0_REG, N_REGS_GPIO_MTX(), 0, 0), .owner = ENTRY(0) }, + /* IO_MUX */ + [0] = { .config = REGDMA_LINK_CONTINUOUS_INIT(REGDMA_IOMUX_LINK(0x00), REG_IO_MUX_BASE, REG_IO_MUX_BASE, N_REGS_IOMUX(), 0, 0), .owner = ENTRY(0) }, + /* MSPI IOMUX */ + [1] = { .config = REGDMA_LINK_CONTINUOUS_INIT(REGDMA_IOMUX_LINK(0x01), IOMUX_MSPI_PIN_FLASH_CS_PIN0_REG, IOMUX_MSPI_PIN_FLASH_CS_PIN0_REG, N_REGS_MSPI_IOMUX(), 0, 0), .owner = ENTRY(0) }, + /* GPIO_OUT_REG / GPIO_OUT1_REG / GPIO_ENABLE_REG / GPIO_ENABLE1_REG / GPIO_STATUS_REG / GPIO_STATUS1_REG*/ + [2] = { + .config = REGDMA_LINK_ADDR_MAP_INIT(REGDMA_IOMUX_LINK(0x02), GPIO_RETENTION_REGS_BASE0, GPIO_RETENTION_REGS_BASE0, GPIO_RETENTION_REGS_CNT0, 0, 0, \ + gpio_regs_map0[0], gpio_regs_map0[1], gpio_regs_map0[2], gpio_regs_map0[3]), .owner = ENTRY(0) + }, + /* GPIO_PIN0_REG ~ GPIO_PIN56_REG*/ + [3] = { .config = REGDMA_LINK_CONTINUOUS_INIT(REGDMA_IOMUX_LINK(0x03), GPIO_PIN0_REG, GPIO_PIN0_REG, N_REGS_GPIO_PINx(), 0, 0), .owner = ENTRY(0) }, + /* GPIO_FUNC1_IN_SEL_CFG_REG ~ GPIO_FUNC255_IN_SEL_CFG_REG & GPIO_FUNC0_OUT_SEL_CFG_REG ~ GPIO_FUNC56_OUT_SEL_CFG_REG */ + [4] = { .config = REGDMA_LINK_CONTINUOUS_INIT(REGDMA_IOMUX_LINK(0x04), GPIO_FUNC1_IN_SEL_CFG_REG, GPIO_FUNC1_IN_SEL_CFG_REG, N_REGS_GPIO_FUNCx(), 0, 0), .owner = ENTRY(0) }, + + [5] = { + .config = REGDMA_LINK_ADDR_MAP_INIT(REGDMA_IOMUX_LINK(0x05), GPIO_RETENTION_REGS_BASE1, GPIO_RETENTION_REGS_BASE1, GPIO_RETENTION_REGS_CNT1, 0, 0, \ + gpio_regs_map1[0], gpio_regs_map1[1], gpio_regs_map1[2], gpio_regs_map1[3]), .owner = ENTRY(0) + }, }; _Static_assert(ARRAY_SIZE(iomux_regs_retention) == IOMUX_RETENTION_LINK_LEN, "Inconsistent IOMUX retention link length definitions");