From 54fed4c1c315c80eb8b750e88e65449811cd6bf6 Mon Sep 17 00:00:00 2001 From: wuzhenghui Date: Fri, 3 Apr 2026 17:06:30 +0800 Subject: [PATCH 01/13] feat(esp_hw_support): update esp32s31 PMU support --- .../src/esp32s31/bootloader_esp32s31.c | 3 - .../esp_hal_pmu/esp32s31/include/hal/pmu_ll.h | 34 ++++- .../include/esp_private/esp_pmu.h | 4 +- components/esp_hw_support/linker.lf | 2 +- .../esp32c5/private_include/pmu_bit_defs.h | 3 +- .../esp32c6/private_include/pmu_bit_defs.h | 4 +- .../esp32c61/private_include/pmu_bit_defs.h | 3 +- .../esp32p4/private_include/pmu_bit_defs.h | 6 +- .../esp_hw_support/port/esp32s31/pmu_init.c | 18 +-- .../esp_hw_support/port/esp32s31/pmu_param.c | 50 +++--- .../esp_hw_support/port/esp32s31/pmu_sleep.c | 34 +++-- .../esp32s31/private_include/pmu_bit_defs.h | 11 +- .../port/esp32s31/private_include/pmu_param.h | 36 ++--- components/esp_hw_support/sleep_modes.c | 2 +- .../soc/esp32s31/register/soc/pmu_reg.h | 142 ++++++++++++++---- .../soc/esp32s31/register/soc/pmu_struct.h | 22 ++- 16 files changed, 244 insertions(+), 130 deletions(-) diff --git a/components/bootloader_support/src/esp32s31/bootloader_esp32s31.c b/components/bootloader_support/src/esp32s31/bootloader_esp32s31.c index 2e8df43e161..c197eea497e 100644 --- a/components/bootloader_support/src/esp32s31/bootloader_esp32s31.c +++ b/components/bootloader_support/src/esp32s31/bootloader_esp32s31.c @@ -35,9 +35,6 @@ ESP_LOG_ATTR_TAG(TAG, "boot.esp32s31"); static inline void bootloader_hardware_init(void) { - /* Disable RF pll by default */ - REG_SET_FIELD(PMU_RF_PWC_REG, PMU_XPD_RF_CIRCUIT, 0xFFFF); - modem_lpcon_ll_enable_bus_clock(true); #if !CONFIG_IDF_ENV_FPGA diff --git a/components/esp_hal_pmu/esp32s31/include/hal/pmu_ll.h b/components/esp_hal_pmu/esp32s31/include/hal/pmu_ll.h index e631b3bbfff..15192392543 100644 --- a/components/esp_hal_pmu/esp32s31/include/hal/pmu_ll.h +++ b/components/esp_hal_pmu/esp32s31/include/hal/pmu_ll.h @@ -514,6 +514,25 @@ FORCE_INLINE_ATTR void pmu_ll_hp_set_memory_power_up(pmu_dev_t *hw, uint32_t fpu hw->power.mem_cntl.force_hp_mem_pu = fpu; } +FORCE_INLINE_ATTR void pmu_ll_hp_set_memory_power_on_mask(pmu_dev_t *hw, uint32_t mem_mask) +{ + hw->power.mem_mask.mem0_mask = (mem_mask & BIT(0)) ? 0x1F : 0; + hw->power.mem_mask.mem1_mask = (mem_mask & BIT(1)) ? 0x1F : 0; + hw->power.mem_mask.mem2_mask = (mem_mask & BIT(2)) ? 0x1F : 0; + if (mem_mask & BIT(3)) { + hw->power.mem_cntl.force_hp_mem_pu |= (mem_mask & BIT(3)); + } else { + hw->power.mem_cntl.force_hp_mem_pu &= ~(mem_mask & BIT(3)); + } +} + +FORCE_INLINE_ATTR void pmu_ll_hp_set_memory_power_off_mask(pmu_dev_t *hw, uint32_t mem0_pd_mask, uint32_t mem1_pd_mask, uint32_t mem2_pd_mask) +{ + hw->power.mem_mask.mem0_pd_mask = mem0_pd_mask; + hw->power.mem_mask.mem1_pd_mask = mem1_pd_mask; + hw->power.mem_mask.mem2_pd_mask = mem2_pd_mask; +} + FORCE_INLINE_ATTR void pmu_ll_hp_set_sleep_enable(pmu_dev_t *hw) { hw->wakeup.cntl0.sleep_req = 1; @@ -755,12 +774,17 @@ FORCE_INLINE_ATTR uint32_t pmu_ll_get_sysclk_sleep_select_state(pmu_dev_t *hw) return hw->clk_state0.sysclk_slp_sel; } +FORCE_INLINE_ATTR void pmu_ll_set_rf_power_control(pmu_dev_t *hw, uint16_t val) +{ + hw->hp_ext.rf_pwc.xpd_rf_circuit = val; +} + /** * @brief Get ext1 wakeup source status * @return The lower 8 bits of the returned value are the bitmap of * the wakeup source status, bit 0~7 corresponds to LP_IO 0~7 */ -static inline uint32_t pmu_ll_ext1_get_wakeup_status(void) +FORCE_INLINE_ATTR uint32_t pmu_ll_ext1_get_wakeup_status(void) { return REG_GET_FIELD(PMU_EXT_WAKEUP_ST_REG, PMU_EXT_WAKEUP_STATUS); } @@ -768,7 +792,7 @@ static inline uint32_t pmu_ll_ext1_get_wakeup_status(void) /** * @brief Clear the ext1 wakeup source status */ -static inline void pmu_ll_ext1_clear_wakeup_status(void) +FORCE_INLINE_ATTR void pmu_ll_ext1_clear_wakeup_status(void) { REG_SET_BIT(PMU_EXT_WAKEUP_CNTL_REG, PMU_EXT_WAKEUP_STATUS_CLR); } @@ -779,7 +803,7 @@ static inline void pmu_ll_ext1_clear_wakeup_status(void) * @param level_mask 0: Wake the chip when all selected GPIOs go low * 1: Wake the chip when any of the selected GPIOs go high */ -static inline void pmu_ll_ext1_set_wakeup_pins(uint32_t io_mask, int level_mask) +FORCE_INLINE_ATTR void pmu_ll_ext1_set_wakeup_pins(uint32_t io_mask, int level_mask) { REG_SET_FIELD(PMU_EXT_WAKEUP_SEL_REG, PMU_EXT_WAKEUP_SEL, io_mask); REG_SET_FIELD(PMU_EXT_WAKEUP_LV_REG, PMU_EXT_WAKEUP_LV, level_mask); @@ -788,7 +812,7 @@ static inline void pmu_ll_ext1_set_wakeup_pins(uint32_t io_mask, int level_mask /** * @brief Clear all ext1 wakup-source setting */ -static inline void pmu_ll_ext1_clear_wakeup_pins(void) +FORCE_INLINE_ATTR void pmu_ll_ext1_clear_wakeup_pins(void) { REG_SET_FIELD(PMU_EXT_WAKEUP_SEL_REG, PMU_EXT_WAKEUP_SEL, 0); } @@ -798,7 +822,7 @@ static inline void pmu_ll_ext1_clear_wakeup_pins(void) * @return The lower 8 bits of the returned value are the bitmap of * the wakeup source status, bit 0~7 corresponds to LP_IO 0~7 */ -static inline uint32_t pmu_ll_ext1_get_wakeup_pins(void) +FORCE_INLINE_ATTR uint32_t pmu_ll_ext1_get_wakeup_pins(void) { return REG_GET_FIELD(PMU_EXT_WAKEUP_SEL_REG, PMU_EXT_WAKEUP_SEL); } diff --git a/components/esp_hw_support/include/esp_private/esp_pmu.h b/components/esp_hw_support/include/esp_private/esp_pmu.h index 543b9c34c5a..af7cc9042cb 100644 --- a/components/esp_hw_support/include/esp_private/esp_pmu.h +++ b/components/esp_hw_support/include/esp_private/esp_pmu.h @@ -123,8 +123,8 @@ typedef enum { #define RTC_USB_TRIG_EN PMU_USB_WAKEUP_EN #if SOC_LP_CORE_SUPPORTED -#define RTC_LP_CORE_TRIG_EN PMU_LP_CORE_WAKEUP_EN //!< LP core wakeup -#define RTC_LP_CORE_TRAP_TRIG_EN PMU_LP_CORE_TRAP_WAKEUP_EN //!< LP core trap (exception) wakeup +#define RTC_LP_CORE_TRIG_EN PMU_LP_CORE_WAKEUP_HP_EN //!< LP core wakeup +#define RTC_LP_CORE_TRAP_TRIG_EN PMU_LP_CORE_TRAP_WAKEUP_EN //!< LP core trap (exception) wakeup #else #define RTC_LP_CORE_TRIG_EN 0 #define RTC_LP_CORE_TRAP_TRIG_EN 0 diff --git a/components/esp_hw_support/linker.lf b/components/esp_hw_support/linker.lf index 4b51f5b2078..6c091ac5f5e 100644 --- a/components/esp_hw_support/linker.lf +++ b/components/esp_hw_support/linker.lf @@ -46,7 +46,7 @@ entries: if SOC_PM_SLEEP_CLK_ICG_USE_REGDMA = y && PM_POWER_DOWN_PERIPHERAL_IN_LIGHT_SLEEP != y: pmu_sleep_clock_icg:pmu_sleep_clock_icg_config (noflash) sleep_mspi (noflash) - if PM_SLP_IRAM_OPT = y && IDF_TARGET_ESP32P4 != y: + if PM_SLP_IRAM_OPT = y && IDF_TARGET_ESP32S31 != y: pmu_param:get_act_lp_dbias (noflash) if IDF_TARGET_ESP32H4 || IDF_TARGET_ESP32H21 = y: pmu_param:get_act_hp_drvb (noflash) diff --git a/components/esp_hw_support/port/esp32c5/private_include/pmu_bit_defs.h b/components/esp_hw_support/port/esp32c5/private_include/pmu_bit_defs.h index b7876d6ffe2..7f6556bddea 100644 --- a/components/esp_hw_support/port/esp32c5/private_include/pmu_bit_defs.h +++ b/components/esp_hw_support/port/esp32c5/private_include/pmu_bit_defs.h @@ -19,9 +19,10 @@ extern "C" { #define PMU_UART0_WAKEUP_EN BIT(6) #define PMU_UART1_WAKEUP_EN BIT(7) #define PMU_BLE_SOC_WAKEUP_EN BIT(10) -#define PMU_LP_CORE_WAKEUP_EN BIT(11) +#define PMU_LP_CORE_WAKEUP_HP_EN BIT(11) #define PMU_LP_CORE_TRAP_WAKEUP_EN BIT(12) #define PMU_USB_WAKEUP_EN BIT(14) +#define PMU_MODEM_WAKEUP_PROTECT BIT(16) #ifdef __cplusplus } diff --git a/components/esp_hw_support/port/esp32c6/private_include/pmu_bit_defs.h b/components/esp_hw_support/port/esp32c6/private_include/pmu_bit_defs.h index fe9b958e7c2..2b8078d7ef9 100644 --- a/components/esp_hw_support/port/esp32c6/private_include/pmu_bit_defs.h +++ b/components/esp_hw_support/port/esp32c6/private_include/pmu_bit_defs.h @@ -20,10 +20,10 @@ extern "C" { #define PMU_UART1_WAKEUP_EN BIT(7) #define PMU_SDIO_WAKEUP_EN BIT(8) #define PMU_BLE_SOC_WAKEUP_EN BIT(10) -#define PMU_LP_CORE_WAKEUP_EN BIT(11) +#define PMU_LP_CORE_WAKEUP_HP_EN BIT(11) #define PMU_LP_CORE_TRAP_WAKEUP_EN BIT(12) #define PMU_USB_WAKEUP_EN BIT(14) - +#define PMU_MODEM_WAKEUP_PROTECT BIT(16) #ifdef __cplusplus } #endif diff --git a/components/esp_hw_support/port/esp32c61/private_include/pmu_bit_defs.h b/components/esp_hw_support/port/esp32c61/private_include/pmu_bit_defs.h index f3550538bf9..e29050c6b41 100644 --- a/components/esp_hw_support/port/esp32c61/private_include/pmu_bit_defs.h +++ b/components/esp_hw_support/port/esp32c61/private_include/pmu_bit_defs.h @@ -20,8 +20,9 @@ extern "C" { #define PMU_UART1_WAKEUP_EN BIT(7) #define PMU_UART2_WAKEUP_EN BIT(9) #define PMU_BLE_SOC_WAKEUP_EN BIT(10) -// #define PMU_LP_CORE_WAKEUP_EN BIT(11) +#define PMU_LP_CORE_WAKEUP_HP_EN BIT(11) #define PMU_USB_WAKEUP_EN BIT(14) +#define PMU_MODEM_WAKEUP_PROTECT BIT(16) #ifdef __cplusplus } diff --git a/components/esp_hw_support/port/esp32p4/private_include/pmu_bit_defs.h b/components/esp_hw_support/port/esp32p4/private_include/pmu_bit_defs.h index 72931274a7a..6d25676c483 100644 --- a/components/esp_hw_support/port/esp32p4/private_include/pmu_bit_defs.h +++ b/components/esp_hw_support/port/esp32p4/private_include/pmu_bit_defs.h @@ -1,5 +1,5 @@ /* - * SPDX-FileCopyrightText: 2023-2024 Espressif Systems (Shanghai) CO LTD + * SPDX-FileCopyrightText: 2023-2026 Espressif Systems (Shanghai) CO LTD * * SPDX-License-Identifier: Apache-2.0 */ @@ -11,7 +11,7 @@ extern "C" { #endif #define PMU_SDIO_WAKEUP_EN BIT(0) -#define PMU_LP_CORE_WAKEUP_EN BIT(1) +#define PMU_LP_CORE_WAKEUP_HP_EN BIT(1) #define PMU_GPIO_WAKEUP_EN BIT(2) #define PMU_USB_WAKEUP_EN BIT(3) #define PMU_UART4_WAKEUP_EN BIT(4) @@ -31,6 +31,8 @@ extern "C" { #define PMU_LP_TIMER1_WAKEUP_EN BIT(18) #define PMU_LP_I2S_WAKEUP_EN BIT(19) +#define PMU_INTERRUPT_WAKEUP BIT(23) + // BIT order in PLL control registers in PMU #define PMU_CPLL_CTRL BIT(0) #define PMU_SPLL_CTRL BIT(1) diff --git a/components/esp_hw_support/port/esp32s31/pmu_init.c b/components/esp_hw_support/port/esp32s31/pmu_init.c index a540ba0ec98..508c01e666f 100644 --- a/components/esp_hw_support/port/esp32s31/pmu_init.c +++ b/components/esp_hw_support/port/esp32s31/pmu_init.c @@ -90,14 +90,14 @@ void pmu_hp_system_init(pmu_context_t *ctx, pmu_hp_mode_t mode, pmu_hp_system_pa pmu_ll_hp_set_regulator_dbias_select (ctx->hal->dev, mode, anlg->regulator0.dbias_sel); pmu_ll_hp_set_regulator_dbias_init (ctx->hal->dev, mode, anlg->regulator0.dig_dbias_init); } - pmu_ll_hp_set_regulator_sleep_memory_xpd (ctx->hal->dev, mode, anlg->regulator0.slp_mem_xpd); - pmu_ll_hp_set_regulator_sleep_memory_dbias(ctx->hal->dev, mode, anlg->regulator0.slp_mem_dbias); - pmu_ll_hp_set_regulator_sleep_logic_xpd (ctx->hal->dev, mode, anlg->regulator0.slp_logic_xpd); - pmu_ll_hp_set_regulator_sleep_logic_dbias (ctx->hal->dev, mode, anlg->regulator0.slp_logic_dbias); - pmu_ll_hp_set_regulator_dbias (ctx->hal->dev, mode, anlg->regulator0.dbias); - pmu_ll_hp_set_regulator_xpd (ctx->hal->dev, mode, anlg->regulator0.xpd); + pmu_ll_hp_set_regulator_sleep_memory_xpd (ctx->hal->dev, mode, anlg->regulator0.slp_mem_xpd); + pmu_ll_hp_set_regulator_sleep_memory_dbias (ctx->hal->dev, mode, anlg->regulator0.slp_mem_dbias); + pmu_ll_hp_set_regulator_sleep_logic_xpd (ctx->hal->dev, mode, anlg->regulator0.slp_logic_xpd); + pmu_ll_hp_set_regulator_sleep_logic_dbias (ctx->hal->dev, mode, anlg->regulator0.slp_logic_dbias); + pmu_ll_hp_set_regulator_dbias (ctx->hal->dev, mode, anlg->regulator0.dbias); + pmu_ll_hp_set_regulator_xpd (ctx->hal->dev, mode, anlg->regulator0.xpd); pmu_ll_hp_set_regulator_sleep_connect_enable(ctx->hal->dev, mode, anlg->regulator0.slp_connect_en); - pmu_ll_hp_set_regulator_driver_bar (ctx->hal->dev, mode, anlg->regulator1.drv_b); + pmu_ll_hp_set_regulator_driver_bar (ctx->hal->dev, mode, anlg->regulator1.drv_b); /* Default configuration of hp-system retention sub-system in active, modem * and sleep modes */ @@ -158,6 +158,8 @@ static inline void pmu_power_domain_force_default(pmu_context_t *ctx) } /* Isolate all memory banks while sleeping, avoid memory leakage current */ pmu_ll_hp_set_memory_no_isolate (ctx->hal->dev, 0); + /* Disable memory force pu for memory pd during deep sleep */ + pmu_ll_hp_set_memory_power_up (ctx->hal->dev, 0); pmu_ll_lp_set_power_force_power_up (ctx->hal->dev, false); pmu_ll_lp_set_power_force_no_reset (ctx->hal->dev, false); @@ -210,6 +212,4 @@ void pmu_init(void) pmu_hp_system_init_default(PMU_instance()); pmu_lp_system_init_default(PMU_instance()); pmu_power_domain_force_default(PMU_instance()); - - WRITE_PERI_REG(PMU_POWER_PD_MEM_CNTL_REG, 0); } diff --git a/components/esp_hw_support/port/esp32s31/pmu_param.c b/components/esp_hw_support/port/esp32s31/pmu_param.c index b284bd20819..2fc17e5e225 100644 --- a/components/esp_hw_support/port/esp32s31/pmu_param.c +++ b/components/esp_hw_support/port/esp32s31/pmu_param.c @@ -38,9 +38,15 @@ ESP_HW_LOG_ATTR_TAG(TAG, "pmu_param"); .clk_power = { \ .i2c_iso_en = 0, \ .i2c_retention = 0, \ - .xpd_bb_i2c = 0, \ - .xpd_pll_i2c = 0x3, \ - .xpd_pll = 0x3 \ + .xpd_bb_i2c = 1, \ + .xpd_cpll_i2c = 1, \ + .xpd_bbpll_i2c = 1, \ + .xpd_apll_i2c = 0, \ + .xpd_mpll_i2c = 0, \ + .xpd_cpll = 1, \ + .xpd_bbpll = 1, \ + .xpd_apll = 0, \ + .xpd_mpll = 0, \ }, \ .xtal = { \ .xpd_xtal = 1 \ @@ -62,9 +68,15 @@ ESP_HW_LOG_ATTR_TAG(TAG, "pmu_param"); .clk_power = { \ .i2c_iso_en = 1, \ .i2c_retention = 1, \ - .xpd_bb_i2c = 0, \ - .xpd_pll_i2c = 0x3, \ - .xpd_pll = 0x3 \ + .xpd_bb_i2c = 1, \ + .xpd_cpll_i2c = 1, \ + .xpd_bbpll_i2c = 1, \ + .xpd_apll_i2c = 0, \ + .xpd_mpll_i2c = 0, \ + .xpd_cpll = 1, \ + .xpd_bbpll = 1, \ + .xpd_apll = 0, \ + .xpd_mpll = 0, \ }, \ .xtal = { \ .xpd_xtal = 1 \ @@ -87,8 +99,14 @@ ESP_HW_LOG_ATTR_TAG(TAG, "pmu_param"); .i2c_iso_en = 1, \ .i2c_retention = 1, \ .xpd_bb_i2c = 0, \ - .xpd_pll_i2c = 0, \ - .xpd_pll = 0 \ + .xpd_cpll_i2c = 0, \ + .xpd_bbpll_i2c = 0, \ + .xpd_apll_i2c = 0, \ + .xpd_mpll_i2c = 0, \ + .xpd_cpll = 0, \ + .xpd_bbpll = 0, \ + .xpd_apll = 0, \ + .xpd_mpll = 0, \ }, \ .xtal = { \ .xpd_xtal = 0 \ @@ -178,27 +196,21 @@ const pmu_hp_system_clock_param_t * pmu_hp_system_clock_param_default(pmu_hp_mod .syscntl = { \ .c_channel = 1, \ .uart_wakeup_en = 0, \ - .lp_pad_hold_all = 0, \ - .hp_pad_hold_all = 0, \ - .dig_pad_slp_sel = 1, \ + .lp_pad_hold_all = 1, \ + .hp_pad_hold_all = 1, \ + .dig_pad_slp_sel = 0, \ .dig_pause_wdt = 1, \ .dig_cpu_stall = 1 \ } \ } -/** - * - hp_pad_hold_all: - * - when top off: must set to 1; - * - when top on: 0 if use pad; 1 if not use pad - * - dig_pad_slp_sel: must set to 1 when sleep - */ #define PMU_HP_SLEEP_DIGITAL_CONFIG_DEFAULT() { \ .syscntl = { \ .c_channel = 0, \ .uart_wakeup_en = 1, \ - .lp_pad_hold_all = 0, \ + .lp_pad_hold_all = 1, \ .hp_pad_hold_all = 1, \ - .dig_pad_slp_sel = 1, \ + .dig_pad_slp_sel = 0, \ .dig_pause_wdt = 1, \ .dig_cpu_stall = 1 \ } \ diff --git a/components/esp_hw_support/port/esp32s31/pmu_sleep.c b/components/esp_hw_support/port/esp32s31/pmu_sleep.c index 8449304dcd7..29e61e36958 100644 --- a/components/esp_hw_support/port/esp32s31/pmu_sleep.c +++ b/components/esp_hw_support/port/esp32s31/pmu_sleep.c @@ -238,6 +238,13 @@ static void pmu_sleep_power_init(pmu_context_t *ctx, const pmu_sleep_power_confi pmu_ll_hp_set_clk_power(ctx->hal->dev, HP(SLEEP), power->hp_sys.clk_power.val); pmu_ll_hp_set_xtal_xpd (ctx->hal->dev, HP(SLEEP), power->hp_sys.xtal.xpd_xtal); + if (dslp) { + pmu_ll_hp_set_memory_power_on_mask(ctx->hal->dev, 0); + } else { + pmu_ll_hp_set_memory_power_on_mask(ctx->hal->dev, 0xf); + } + + pmu_ll_set_rf_power_control(ctx->hal->dev, 0); pmu_ll_lp_set_dig_power(ctx->hal->dev, LP(ACTIVE), power->lp_sys[LP(ACTIVE)].dig_power.val); pmu_ll_lp_set_clk_power(ctx->hal->dev, LP(ACTIVE), power->lp_sys[LP(ACTIVE)].clk_power.val); @@ -249,26 +256,24 @@ static void pmu_sleep_power_init(pmu_context_t *ctx, const pmu_sleep_power_confi static void pmu_sleep_digital_init(pmu_context_t *ctx, const pmu_sleep_digital_config_t *dig) { pmu_ll_hp_set_dig_pad_slp_sel (ctx->hal->dev, HP(SLEEP), dig->syscntl.dig_pad_slp_sel); + pmu_ll_hp_set_hold_all_hp_pad (ctx->hal->dev, HP(SLEEP), dig->syscntl.hp_pad_hold_all); pmu_ll_hp_set_hold_all_lp_pad (ctx->hal->dev, HP(SLEEP), dig->syscntl.lp_pad_hold_all); + pmu_ll_hp_set_pause_watchdog (ctx->hal->dev, HP(SLEEP), dig->syscntl.dig_pause_wdt); pmu_ll_hp_set_c_channel_enable (ctx->hal->dev, HP(SLEEP), dig->syscntl.c_channel); } static void pmu_sleep_analog_init(pmu_context_t *ctx, const pmu_sleep_analog_config_t *analog, bool dslp) { assert(ctx->hal); - pmu_ll_hp_set_current_power_off (ctx->hal->dev, HP(SLEEP), analog->hp_sys.analog.pd_cur); - pmu_ll_hp_set_bias_sleep_enable (ctx->hal->dev, HP(SLEEP), analog->hp_sys.analog.bias_sleep); - pmu_ll_hp_set_regulator_sleep_memory_xpd (ctx->hal->dev, HP(SLEEP), analog->hp_sys.analog.slp_mem_xpd); - pmu_ll_hp_set_regulator_sleep_logic_xpd (ctx->hal->dev, HP(SLEEP), analog->hp_sys.analog.slp_logic_xpd); - pmu_ll_hp_set_regulator_xpd (ctx->hal->dev, HP(SLEEP), analog->hp_sys.analog.xpd); - pmu_ll_hp_set_regulator_sleep_logic_dbias (ctx->hal->dev, HP(SLEEP), analog->hp_sys.analog.slp_logic_dbias); - pmu_ll_hp_set_dbg_atten (ctx->hal->dev, HP(SLEEP), analog->hp_sys.analog.dbg_atten); - pmu_ll_hp_set_regulator_dbias (ctx->hal->dev, HP(SLEEP), analog->hp_sys.analog.dbias); - pmu_ll_hp_set_regulator_driver_bar (ctx->hal->dev, HP(SLEEP), analog->hp_sys.analog.drv_b); - - pmu_ll_lp_set_regulator_sleep_dbias(ctx->hal->dev, LP(ACTIVE), analog->lp_sys[LP(ACTIVE)].analog.slp_dbias); - pmu_ll_lp_set_regulator_dbias (ctx->hal->dev, LP(ACTIVE), analog->lp_sys[LP(ACTIVE)].analog.dbias); - pmu_ll_lp_set_regulator_driver_bar (ctx->hal->dev, LP(ACTIVE), analog->lp_sys[LP(ACTIVE)].analog.drv_b); + pmu_ll_hp_set_current_power_off (ctx->hal->dev, HP(SLEEP), analog->hp_sys.analog.pd_cur); + pmu_ll_hp_set_bias_sleep_enable (ctx->hal->dev, HP(SLEEP), analog->hp_sys.analog.bias_sleep); + pmu_ll_hp_set_regulator_sleep_memory_xpd (ctx->hal->dev, HP(SLEEP), analog->hp_sys.analog.slp_mem_xpd); + pmu_ll_hp_set_regulator_sleep_logic_xpd (ctx->hal->dev, HP(SLEEP), analog->hp_sys.analog.slp_logic_xpd); + pmu_ll_hp_set_regulator_xpd (ctx->hal->dev, HP(SLEEP), analog->hp_sys.analog.xpd); + pmu_ll_hp_set_regulator_sleep_logic_dbias (ctx->hal->dev, HP(SLEEP), analog->hp_sys.analog.slp_logic_dbias); + pmu_ll_hp_set_dbg_atten (ctx->hal->dev, HP(SLEEP), analog->hp_sys.analog.dbg_atten); + pmu_ll_hp_set_regulator_dbias (ctx->hal->dev, HP(SLEEP), analog->hp_sys.analog.dbias); + pmu_ll_hp_set_regulator_driver_bar (ctx->hal->dev, HP(SLEEP), analog->hp_sys.analog.drv_b); pmu_ll_lp_set_current_power_off (ctx->hal->dev, LP(SLEEP), analog->lp_sys[LP(SLEEP)].analog.pd_cur); pmu_ll_lp_set_bias_sleep_enable (ctx->hal->dev, LP(SLEEP), analog->lp_sys[LP(SLEEP)].analog.bias_sleep); @@ -278,9 +283,6 @@ static void pmu_sleep_analog_init(pmu_context_t *ctx, const pmu_sleep_analog_con pmu_ll_lp_set_regulator_dbias (ctx->hal->dev, LP(SLEEP), analog->lp_sys[LP(SLEEP)].analog.dbias); pmu_ll_lp_set_dbg_atten (ctx->hal->dev, LP(SLEEP), analog->lp_sys[LP(SLEEP)].analog.dbg_atten); pmu_ll_lp_set_regulator_driver_bar (ctx->hal->dev, LP(SLEEP), analog->lp_sys[LP(SLEEP)].analog.drv_b); - - pmu_ll_lp_set_regulator_slp_xpd (ctx->hal->dev, LP(ACTIVE), analog->lp_sys[LP(ACTIVE)].analog.slp_xpd); - pmu_ll_lp_set_regulator_xpd (ctx->hal->dev, LP(ACTIVE), analog->lp_sys[LP(ACTIVE)].analog.xpd); } static void pmu_sleep_param_init(pmu_context_t *ctx, const pmu_sleep_param_config_t *param, bool dslp) diff --git a/components/esp_hw_support/port/esp32s31/private_include/pmu_bit_defs.h b/components/esp_hw_support/port/esp32s31/private_include/pmu_bit_defs.h index 470ec8872cd..6ea92b8e80a 100644 --- a/components/esp_hw_support/port/esp32s31/private_include/pmu_bit_defs.h +++ b/components/esp_hw_support/port/esp32s31/private_include/pmu_bit_defs.h @@ -10,11 +10,11 @@ extern "C" { #endif -#define PMU_SDIO_WAKEUP_EN BIT(0) -#define PMU_LP_CORE_WAKEUP_EN BIT(1) +#define PMU_LP_CORE_WAKEUP_HP_EN BIT(0) +#define PMU_HP_CORE_WAKEUP_LP_EN BIT(1) #define PMU_GPIO_WAKEUP_EN BIT(2) #define PMU_USB_WAKEUP_EN BIT(3) -#define PMU_UART4_WAKEUP_EN BIT(4) +#define PMU_INTERRUPT_WAKEUP BIT(4) #define PMU_UART3_WAKEUP_EN BIT(5) #define PMU_UART2_WAKEUP_EN BIT(6) #define PMU_UART1_WAKEUP_EN BIT(7) @@ -25,14 +25,13 @@ extern "C" { #define PMU_EXT1_WAKEUP_EN BIT(12) #define PMU_RTC_TIMER_WAKEUP_EN BIT(13) #define PMU_BOD_WAKEUP_EN BIT(14) -#define PMU_VBAT_UNDERVOLT_WAKEUP_EN BIT(15) #define PMU_LP_CORE_TRAP_WAKEUP_EN BIT(16) #define PMU_ETM_WAKEUP_EN BIT(17) #define PMU_LP_TIMER1_WAKEUP_EN BIT(18) -#define PMU_LP_I2S_WAKEUP_EN BIT(19) #define PMU_WIFI_SOC_WAKEUP_EN BIT(22) #define PMU_WIFI_BEACON_WAKEUP_EN BIT(23) -#define PMU_BLE_SOC_WAKEUP_EN BIT(24) +#define PMU_MODEM_WAKEUP_PROTECT BIT(24) +#define PMU_BLE_SOC_WAKEUP_EN BIT(25) // BIT order in PLL control registers in PMU diff --git a/components/esp_hw_support/port/esp32s31/private_include/pmu_param.h b/components/esp_hw_support/port/esp32s31/private_include/pmu_param.h index 652d9d82601..48b4555cd97 100644 --- a/components/esp_hw_support/port/esp32s31/private_include/pmu_param.h +++ b/components/esp_hw_support/port/esp32s31/private_include/pmu_param.h @@ -278,7 +278,7 @@ typedef struct { .pd_modem_top_pd_en = ((sleep_flags) & PMU_SLEEP_PD_MODEM) ? 1 : 0, \ .pd_hp_cnnt_pd_en = ((sleep_flags) & PMU_SLEEP_PD_CNNT) ? 1 : 0, \ .pd_hp_cpu_pd_en = ((sleep_flags) & PMU_SLEEP_PD_CPU) ? 1 : 0, \ - .pd_modem_pwr_pd_en = 0, \ + .pd_modem_pwr_pd_en = ((sleep_flags) & PMU_SLEEP_PD_MEM) ? 1 : 0, \ .pd_top_pd_en = ((sleep_flags) & PMU_SLEEP_PD_TOP) ? 1 : 0, \ .pd_hp_mem_pd_en = 0, \ .mem_dslp = 0, \ @@ -330,15 +330,19 @@ typedef struct { #define PMU_SLEEP_DIGITAL_DSLP_CONFIG_DEFAULT(sleep_flags) { \ .syscntl = { \ .dig_pad_slp_sel = 0, \ - .lp_pad_hold_all = (sleep_flags & PMU_SLEEP_PD_LP_PERIPH) ? 1 : 0, \ + .lp_pad_hold_all = 1, \ + .hp_pad_hold_all = 1, \ + .dig_pause_wdt = ((sleep_flags) & RTC_SLEEP_USE_RTC_WDT) ? 0 : 1, \ .c_channel = (sleep_flags & PMU_SLEEP_PD_TOP) ? 0 : 1 \ } \ } #define PMU_SLEEP_DIGITAL_LSLP_CONFIG_DEFAULT(sleep_flags) { \ .syscntl = { \ - .dig_pad_slp_sel = 0, \ - .lp_pad_hold_all = (sleep_flags & PMU_SLEEP_PD_LP_PERIPH) ? 1 : 0, \ + .dig_pad_slp_sel = ((sleep_flags) & PMU_SLEEP_PD_TOP) ? 0 : 1, \ + .lp_pad_hold_all = ((sleep_flags) & PMU_SLEEP_PD_TOP) ? 1 : 0, \ + .hp_pad_hold_all = ((sleep_flags) & PMU_SLEEP_PD_TOP) ? 1 : 0, \ + .dig_pause_wdt = ((sleep_flags) & RTC_SLEEP_USE_RTC_WDT) ? 0 : 1, \ .c_channel = (sleep_flags & PMU_SLEEP_PD_TOP) ? 0 : 1 \ } \ } @@ -363,15 +367,6 @@ typedef struct { .dbias = PMU_HP_DBIAS_LIGHTSLEEP_0V6 \ } \ }, \ - .lp_sys[PMU_MODE_LP_ACTIVE] = { \ - .analog = { \ - .slp_xpd = 0, \ - .slp_dbias = 0, \ - .xpd = 1, \ - .dbias = 29, \ - .drv_b = 0x0 \ - } \ - }, \ .lp_sys[PMU_MODE_LP_SLEEP] = { \ .analog = { \ .drv_b = PMU_LP_DRVB_LIGHTSLEEP, \ @@ -395,15 +390,6 @@ typedef struct { .dbg_atten = PMU_DBG_HP_DEEPSLEEP \ } \ }, \ - .lp_sys[PMU_MODE_LP_ACTIVE] = { \ - .analog = { \ - .xpd = 1, \ - .dbias = 0x1a, \ - .slp_xpd = 0, \ - .slp_dbias = 0, \ - .drv_b = 0x7 \ - } \ - }, \ .lp_sys[PMU_MODE_LP_SLEEP] = { \ .analog = { \ .drv_b = PMU_LP_DRVB_DEEPSLEEP, \ @@ -501,7 +487,7 @@ typedef struct pmu_sleep_machine_constant { }, \ .hp = { \ .min_slp_time_us = 450, \ - .clock_domain_sync_time_us = 150, \ + .clock_domain_sync_time_us = 2, \ .system_dfs_up_work_time_us = 124, \ .analog_wait_time_us = 154, \ .isolate_wait_time_us = 1, \ @@ -509,9 +495,9 @@ typedef struct pmu_sleep_machine_constant { .power_supply_wait_time_us = 2, \ .power_up_wait_time_us = 2, \ .regdma_s2m_work_time_us = 172, \ - .regdma_s2a_work_time_us = 685, \ + .regdma_s2a_work_time_us = 750, \ .regdma_m2a_work_time_us = 278, \ - .regdma_a2s_work_time_us = 382, \ + .regdma_a2s_work_time_us = 400, \ .regdma_rf_on_work_time_us = 70, \ .regdma_rf_off_work_time_us = 23, \ .xtal_wait_stable_time_us = 250, \ diff --git a/components/esp_hw_support/sleep_modes.c b/components/esp_hw_support/sleep_modes.c index c59964aa86c..3731b1e4097 100644 --- a/components/esp_hw_support/sleep_modes.c +++ b/components/esp_hw_support/sleep_modes.c @@ -288,7 +288,7 @@ typedef struct { #endif portMUX_TYPE lock; uint64_t sleep_duration; - uint32_t wakeup_triggers : 20; + uint32_t wakeup_triggers; #if SOC_PM_SUPPORT_EXT1_WAKEUP uint32_t ext1_trigger_mode : 22; // 22 is the maximum RTCIO number in all chips uint32_t ext1_rtc_gpio_mask : 22; diff --git a/components/soc/esp32s31/register/soc/pmu_reg.h b/components/soc/esp32s31/register/soc/pmu_reg.h index b90162301a7..b143653cc9f 100644 --- a/components/soc/esp32s31/register/soc/pmu_reg.h +++ b/components/soc/esp32s31/register/soc/pmu_reg.h @@ -227,56 +227,56 @@ extern "C" { /** PMU_HP_ACTIVE_XPD_CPLL_I2C : R/W; bitpos: [23]; default: 0; * need_des */ -#define PMU_HP_ACTIVE_XPD_CPLL_I2C 0x00000001U +#define PMU_HP_ACTIVE_XPD_CPLL_I2C (BIT(23)) #define PMU_HP_ACTIVE_XPD_CPLL_I2C_M (PMU_HP_ACTIVE_XPD_CPLL_I2C_V << PMU_HP_ACTIVE_XPD_CPLL_I2C_S) #define PMU_HP_ACTIVE_XPD_CPLL_I2C_V 0x00000001U #define PMU_HP_ACTIVE_XPD_CPLL_I2C_S 23 /** PMU_HP_ACTIVE_XPD_BBPLL_I2C : R/W; bitpos: [24]; default: 0; * need_des */ -#define PMU_HP_ACTIVE_XPD_BBPLL_I2C 0x00000001U +#define PMU_HP_ACTIVE_XPD_BBPLL_I2C (BIT(24)) #define PMU_HP_ACTIVE_XPD_BBPLL_I2C_M (PMU_HP_ACTIVE_XPD_BBPLL_I2C_V << PMU_HP_ACTIVE_XPD_BBPLL_I2C_S) #define PMU_HP_ACTIVE_XPD_BBPLL_I2C_V 0x00000001U #define PMU_HP_ACTIVE_XPD_BBPLL_I2C_S 24 /** PMU_HP_ACTIVE_XPD_APLL_I2C : R/W; bitpos: [25]; default: 0; * need_des */ -#define PMU_HP_ACTIVE_XPD_APLL_I2C 0x00000001U -#define PMU_HP_ACTIVE_XPD_APLL_I2C_M (PMU_HP_ACTIVE_XPD_APLL_I2C_V << PMU_HP_ACTIVE_XPD_ACPLL_I2C_S) +#define PMU_HP_ACTIVE_XPD_APLL_I2C (BIT(25)) +#define PMU_HP_ACTIVE_XPD_APLL_I2C_M (PMU_HP_ACTIVE_XPD_APLL_I2C_V << PMU_HP_ACTIVE_XPD_APLL_I2C_S) #define PMU_HP_ACTIVE_XPD_APLL_I2C_V 0x00000001U #define PMU_HP_ACTIVE_XPD_APLL_I2C_S 25 /** PMU_HP_ACTIVE_XPD_MPLL_I2C : R/W; bitpos: [26]; default: 0; * need_des */ -#define PMU_HP_ACTIVE_XPD_MLL_I2C 0x00000001U +#define PMU_HP_ACTIVE_XPD_MPLL_I2C (BIT(26)) #define PMU_HP_ACTIVE_XPD_MPLL_I2C_M (PMU_HP_ACTIVE_XPD_MPLL_I2C_V << PMU_HP_ACTIVE_XPD_MPLL_I2C_S) #define PMU_HP_ACTIVE_XPD_MPLL_I2C_V 0x00000001U #define PMU_HP_ACTIVE_XPD_MPLL_I2C_S 26 /** PMU_HP_ACTIVE_XPD_CPLL : R/W; bitpos: [27]; default: 0; * need_des */ -#define PMU_HP_ACTIVE_XPD_CPLL 0x00000001U +#define PMU_HP_ACTIVE_XPD_CPLL (BIT(27)) #define PMU_HP_ACTIVE_XPD_CPLL_M (PMU_HP_ACTIVE_XPD_CPLL_V << PMU_HP_ACTIVE_XPD_CPLL_S) #define PMU_HP_ACTIVE_XPD_CPLL_V 0x00000001U #define PMU_HP_ACTIVE_XPD_CPLL_S 27 /** PMU_HP_ACTIVE_XPD_BBPLL : R/W; bitpos: [28]; default: 0; * need_des */ -#define PMU_HP_ACTIVE_XPD_BBPLL 0x00000001U +#define PMU_HP_ACTIVE_XPD_BBPLL (BIT(28)) #define PMU_HP_ACTIVE_XPD_BBPLL_M (PMU_HP_ACTIVE_XPD_BBPLL_V << PMU_HP_ACTIVE_XPD_BBPLL_S) #define PMU_HP_ACTIVE_XPD_BBPLL_V 0x00000001U #define PMU_HP_ACTIVE_XPD_BBPLL_S 28 /** PMU_HP_ACTIVE_XPD_APLL : R/W; bitpos: [29]; default: 0; * need_des */ -#define PMU_HP_ACTIVE_XPD_APLL 0x00000001U +#define PMU_HP_ACTIVE_XPD_APLL (BIT(29)) #define PMU_HP_ACTIVE_XPD_APLL_M (PMU_HP_ACTIVE_XPD_APLL_V << PMU_HP_ACTIVE_XPD_APLL_S) #define PMU_HP_ACTIVE_XPD_APLL_V 0x00000001U #define PMU_HP_ACTIVE_XPD_APLL_S 29 /** PMU_HP_ACTIVE_XPD_MPLL : R/W; bitpos: [30]; default: 0; * need_des */ -#define PMU_HP_ACTIVE_XPD_MPLL 0x00000001U +#define PMU_HP_ACTIVE_XPD_MPLL (BIT(30)) #define PMU_HP_ACTIVE_XPD_MPLL_M (PMU_HP_ACTIVE_XPD_MPLL_V << PMU_HP_ACTIVE_XPD_MPLL_S) #define PMU_HP_ACTIVE_XPD_MPLL_V 0x00000001U #define PMU_HP_ACTIVE_XPD_MPLL_S 30 @@ -780,20 +780,62 @@ extern "C" { #define PMU_HP_MODEM_XPD_BB_I2C_M (PMU_HP_MODEM_XPD_BB_I2C_V << PMU_HP_MODEM_XPD_BB_I2C_S) #define PMU_HP_MODEM_XPD_BB_I2C_V 0x00000001U #define PMU_HP_MODEM_XPD_BB_I2C_S 22 -/** PMU_HP_MODEM_XPD_PLL_I2C : R/W; bitpos: [26:23]; default: 0; +/** PMU_HP_MODEM_XPD_CPLL_I2C : R/W; bitpos: [23]; default: 0; * need_des */ -#define PMU_HP_MODEM_XPD_PLL_I2C 0x0000000FU -#define PMU_HP_MODEM_XPD_PLL_I2C_M (PMU_HP_MODEM_XPD_PLL_I2C_V << PMU_HP_MODEM_XPD_PLL_I2C_S) -#define PMU_HP_MODEM_XPD_PLL_I2C_V 0x0000000FU -#define PMU_HP_MODEM_XPD_PLL_I2C_S 23 -/** PMU_HP_MODEM_XPD_PLL : R/W; bitpos: [30:27]; default: 0; +#define PMU_HP_MODEM_XPD_CPLL_I2C (BIT(23)) +#define PMU_HP_MODEM_XPD_CPLL_I2C_M (PMU_HP_MODEM_XPD_CPLL_I2C_V << PMU_HP_MODEM_XPD_CPLL_I2C_S) +#define PMU_HP_MODEM_XPD_CPLL_I2C_V 0x00000001U +#define PMU_HP_MODEM_XPD_CPLL_I2C_S 23 +/** PMU_HP_MODEM_XPD_BBPLL_I2C : R/W; bitpos: [24]; default: 0; * need_des */ -#define PMU_HP_MODEM_XPD_PLL 0x0000000FU -#define PMU_HP_MODEM_XPD_PLL_M (PMU_HP_MODEM_XPD_PLL_V << PMU_HP_MODEM_XPD_PLL_S) -#define PMU_HP_MODEM_XPD_PLL_V 0x0000000FU -#define PMU_HP_MODEM_XPD_PLL_S 27 +#define PMU_HP_MODEM_XPD_BBPLL_I2C (BIT(24)) +#define PMU_HP_MODEM_XPD_BBPLL_I2C_M (PMU_HP_MODEM_XPD_BBPLL_I2C_V << PMU_HP_MODEM_XPD_BBPLL_I2C_S) +#define PMU_HP_MODEM_XPD_BBPLL_I2C_V 0x00000001U +#define PMU_HP_MODEM_XPD_BBPLL_I2C_S 24 +/** PMU_HP_MODEM_XPD_APLL_I2C : R/W; bitpos: [25]; default: 0; + * need_des + */ +#define PMU_HP_MODEM_XPD_APLL_I2C (BIT(25)) +#define PMU_HP_MODEM_XPD_APLL_I2C_M (PMU_HP_MODEM_XPD_APLL_I2C_V << PMU_HP_MODEM_XPD_APLL_I2C_S) +#define PMU_HP_MODEM_XPD_APLL_I2C_V 0x00000001U +#define PMU_HP_MODEM_XPD_APLL_I2C_S 25 +/** PMU_HP_MODEM_XPD_MPLL_I2C : R/W; bitpos: [26]; default: 0; + * need_des + */ +#define PMU_HP_MODEM_XPD_MPLL_I2C (BIT(26)) +#define PMU_HP_MODEM_XPD_MPLL_I2C_M (PMU_HP_MODEM_XPD_MPLL_I2C_V << PMU_HP_MODEM_XPD_MPLL_I2C_S) +#define PMU_HP_MODEM_XPD_MPLL_I2C_V 0x00000001U +#define PMU_HP_MODEM_XPD_MPLL_I2C_S 26 +/** PMU_HP_MODEM_XPD_CPLL : R/W; bitpos: [27]; default: 0; + * need_des + */ +#define PMU_HP_MODEM_XPD_CPLL (BIT(27)) +#define PMU_HP_MODEM_XPD_CPLL_M (PMU_HP_MODEM_XPD_CPLL_V << PMU_HP_MODEM_XPD_CPLL_S) +#define PMU_HP_MODEM_XPD_CPLL_V 0x00000001U +#define PMU_HP_MODEM_XPD_CPLL_S 27 +/** PMU_HP_MODEM_XPD_BBPLL : R/W; bitpos: [28]; default: 0; + * need_des + */ +#define PMU_HP_MODEM_XPD_BBPLL (BIT(28)) +#define PMU_HP_MODEM_XPD_BBPLL_M (PMU_HP_MODEM_XPD_BBPLL_V << PMU_HP_MODEM_XPD_BBPLL_S) +#define PMU_HP_MODEM_XPD_BBPLL_V 0x00000001U +#define PMU_HP_MODEM_XPD_BBPLL_S 28 +/** PMU_HP_MODEM_XPD_APLL : R/W; bitpos: [29]; default: 0; + * need_des + */ +#define PMU_HP_MODEM_XPD_APLL (BIT(29)) +#define PMU_HP_MODEM_XPD_APLL_M (PMU_HP_MODEM_XPD_APLL_V << PMU_HP_MODEM_XPD_APLL_S) +#define PMU_HP_MODEM_XPD_APLL_V 0x00000001U +#define PMU_HP_MODEM_XPD_APLL_S 29 +/** PMU_HP_MODEM_XPD_MPLL : R/W; bitpos: [30]; default: 0; + * need_des + */ +#define PMU_HP_MODEM_XPD_MPLL (BIT(30)) +#define PMU_HP_MODEM_XPD_MPLL_M (PMU_HP_MODEM_XPD_MPLL_V << PMU_HP_MODEM_XPD_MPLL_S) +#define PMU_HP_MODEM_XPD_MPLL_V 0x00000001U +#define PMU_HP_MODEM_XPD_MPLL_S 30 /** PMU_HP_MODEM_BIAS_REG register * need_des @@ -1231,20 +1273,62 @@ extern "C" { #define PMU_HP_SLEEP_XPD_BB_I2C_M (PMU_HP_SLEEP_XPD_BB_I2C_V << PMU_HP_SLEEP_XPD_BB_I2C_S) #define PMU_HP_SLEEP_XPD_BB_I2C_V 0x00000001U #define PMU_HP_SLEEP_XPD_BB_I2C_S 22 -/** PMU_HP_SLEEP_XPD_PLL_I2C : R/W; bitpos: [26:23]; default: 0; +/** PMU_HP_SLEEP_XPD_CPLL_I2C : R/W; bitpos: [23]; default: 0; * need_des */ -#define PMU_HP_SLEEP_XPD_PLL_I2C 0x0000000FU -#define PMU_HP_SLEEP_XPD_PLL_I2C_M (PMU_HP_SLEEP_XPD_PLL_I2C_V << PMU_HP_SLEEP_XPD_PLL_I2C_S) -#define PMU_HP_SLEEP_XPD_PLL_I2C_V 0x0000000FU -#define PMU_HP_SLEEP_XPD_PLL_I2C_S 23 -/** PMU_HP_SLEEP_XPD_PLL : R/W; bitpos: [30:27]; default: 0; +#define PMU_HP_SLEEP_XPD_CPLL_I2C (BIT(23)) +#define PMU_HP_SLEEP_XPD_CPLL_I2C_M (PMU_HP_SLEEP_XPD_CPLL_I2C_V << PMU_HP_SLEEP_XPD_CPLL_I2C_S) +#define PMU_HP_SLEEP_XPD_CPLL_I2C_V 0x00000001U +#define PMU_HP_SLEEP_XPD_CPLL_I2C_S 23 +/** PMU_HP_SLEEP_XPD_BBPLL_I2C : R/W; bitpos: [24]; default: 0; * need_des */ -#define PMU_HP_SLEEP_XPD_PLL 0x0000000FU -#define PMU_HP_SLEEP_XPD_PLL_M (PMU_HP_SLEEP_XPD_PLL_V << PMU_HP_SLEEP_XPD_PLL_S) -#define PMU_HP_SLEEP_XPD_PLL_V 0x0000000FU -#define PMU_HP_SLEEP_XPD_PLL_S 27 +#define PMU_HP_SLEEP_XPD_BBPLL_I2C (BIT(24)) +#define PMU_HP_SLEEP_XPD_BBPLL_I2C_M (PMU_HP_SLEEP_XPD_BBPLL_I2C_V << PMU_HP_SLEEP_XPD_BBPLL_I2C_S) +#define PMU_HP_SLEEP_XPD_BBPLL_I2C_V 0x00000001U +#define PMU_HP_SLEEP_XPD_BBPLL_I2C_S 24 +/** PMU_HP_SLEEP_XPD_APLL_I2C : R/W; bitpos: [25]; default: 0; + * need_des + */ +#define PMU_HP_SLEEP_XPD_APLL_I2C (BIT(25)) +#define PMU_HP_SLEEP_XPD_APLL_I2C_M (PMU_HP_SLEEP_XPD_APLL_I2C_V << PMU_HP_SLEEP_XPD_APLL_I2C_S) +#define PMU_HP_SLEEP_XPD_APLL_I2C_V 0x00000001U +#define PMU_HP_SLEEP_XPD_APLL_I2C_S 25 +/** PMU_HP_SLEEP_XPD_MPLL_I2C : R/W; bitpos: [26]; default: 0; + * need_des + */ +#define PMU_HP_SLEEP_XPD_MPLL_I2C (BIT(26)) +#define PMU_HP_SLEEP_XPD_MPLL_I2C_M (PMU_HP_SLEEP_XPD_MPLL_I2C_V << PMU_HP_SLEEP_XPD_MPLL_I2C_S) +#define PMU_HP_SLEEP_XPD_MPLL_I2C_V 0x00000001U +#define PMU_HP_SLEEP_XPD_MPLL_I2C_S 26 +/** PMU_HP_SLEEP_XPD_CPLL : R/W; bitpos: [27]; default: 0; + * need_des + */ +#define PMU_HP_SLEEP_XPD_CPLL (BIT(27)) +#define PMU_HP_SLEEP_XPD_CPLL_M (PMU_HP_SLEEP_XPD_CPLL_V << PMU_HP_SLEEP_XPD_CPLL_S) +#define PMU_HP_SLEEP_XPD_CPLL_V 0x00000001U +#define PMU_HP_SLEEP_XPD_CPLL_S 27 +/** PMU_HP_SLEEP_XPD_BBPLL : R/W; bitpos: [28]; default: 0; + * need_des + */ +#define PMU_HP_SLEEP_XPD_BBPLL (BIT(28)) +#define PMU_HP_SLEEP_XPD_BBPLL_M (PMU_HP_SLEEP_XPD_BBPLL_V << PMU_HP_SLEEP_XPD_BBPLL_S) +#define PMU_HP_SLEEP_XPD_BBPLL_V 0x00000001U +#define PMU_HP_SLEEP_XPD_BBPLL_S 28 +/** PMU_HP_SLEEP_XPD_APLL : R/W; bitpos: [29]; default: 0; + * need_des + */ +#define PMU_HP_SLEEP_XPD_APLL (BIT(29)) +#define PMU_HP_SLEEP_XPD_APLL_M (PMU_HP_SLEEP_XPD_APLL_V << PMU_HP_SLEEP_XPD_APLL_S) +#define PMU_HP_SLEEP_XPD_APLL_V 0x00000001U +#define PMU_HP_SLEEP_XPD_APLL_S 29 +/** PMU_HP_SLEEP_XPD_MPLL : R/W; bitpos: [30]; default: 0; + * need_des + */ +#define PMU_HP_SLEEP_XPD_MPLL (BIT(30)) +#define PMU_HP_SLEEP_XPD_MPLL_M (PMU_HP_SLEEP_XPD_MPLL_V << PMU_HP_SLEEP_XPD_MPLL_S) +#define PMU_HP_SLEEP_XPD_MPLL_V 0x00000001U +#define PMU_HP_SLEEP_XPD_MPLL_S 30 /** PMU_HP_SLEEP_BIAS_REG register * need_des diff --git a/components/soc/esp32s31/register/soc/pmu_struct.h b/components/soc/esp32s31/register/soc/pmu_struct.h index 2d635a5aa6e..956811d5087 100644 --- a/components/soc/esp32s31/register/soc/pmu_struct.h +++ b/components/soc/esp32s31/register/soc/pmu_struct.h @@ -55,14 +55,20 @@ typedef union { typedef union { struct { - uint32_t reserved0 : 19; - uint32_t xpd_xtalx2 : 1; - uint32_t i2c_iso_en : 1; - uint32_t i2c_retention: 1; - uint32_t xpd_bb_i2c : 1; - uint32_t xpd_pll_i2c : 4; - uint32_t xpd_pll : 4; - uint32_t reserved1 : 1; + uint32_t reserved0 : 19; + uint32_t xpd_xtalx2 : 1; + uint32_t i2c_iso_en : 1; + uint32_t i2c_retention : 1; + uint32_t xpd_bb_i2c : 1; + uint32_t xpd_cpll_i2c : 1; + uint32_t xpd_bbpll_i2c : 1; + uint32_t xpd_apll_i2c : 1; + uint32_t xpd_mpll_i2c : 1; + uint32_t xpd_cpll : 1; + uint32_t xpd_bbpll : 1; + uint32_t xpd_apll : 1; + uint32_t xpd_mpll : 1; + uint32_t reserved1 : 1; }; uint32_t val; } pmu_hp_clk_power_reg_t; From f1f3f204fe8d4c17d5b377be978d364be53244b7 Mon Sep 17 00:00:00 2001 From: wuzhenghui Date: Wed, 6 May 2026 16:25:57 +0800 Subject: [PATCH 02/13] feat(esp_hw_support): support esp32s31 lightsleep/deepsleep example --- .../test_apps/uart/main/test_hp_uart_wakeup.c | 2 +- .../esp32s31/include/hal/clk_gate_ll.h | 38 +++++++++++++++++++ .../esp_hal_pmu/esp32s31/include/hal/pmu_ll.h | 1 + .../esp_hw_support/port/esp32s31/rtc_clk.c | 21 ++++------ components/esp_hw_support/sleep_modem.c | 2 +- components/esp_hw_support/sleep_modes.c | 15 +++++++- components/esp_hw_support/sleep_usb.c | 14 ++++--- .../test_apps/sleep_retention/README.md | 4 +- .../test_apps/wakeup_tests/README.md | 4 +- components/esp_pm/linker.lf | 2 +- components/esp_system/port/cpu_start.c | 11 +++--- .../esp32p4/include/soc/Kconfig.soc_caps.in | 4 ++ components/soc/esp32p4/include/soc/soc_caps.h | 1 + .../esp32s31/include/soc/Kconfig.soc_caps.in | 26 ++++++++----- .../soc/esp32s31/include/soc/soc_caps.h | 13 +++---- examples/system/deep_sleep/README.md | 4 +- .../system/deep_sleep/main/Kconfig.projbuild | 1 + .../system/deep_sleep/pytest_deep_sleep.py | 1 - examples/system/light_sleep/README.md | 4 +- .../system/light_sleep/main/gpio_wakeup.c | 2 + .../system/light_sleep/pytest_light_sleep.py | 1 - examples/system/ulp/lp_core/gpio/README.md | 4 +- .../lp_core/gpio_intr_pulse_counter/README.md | 4 +- .../system/ulp/lp_core/gpio_wakeup/README.md | 4 +- examples/system/ulp/lp_core/lp_spi/README.md | 4 +- .../system/ulp/lp_core/lp_touch/README.md | 4 +- .../lp_uart/lp_uart_char_seq_wakeup/README.md | 4 +- .../lp_core/lp_uart/lp_uart_echo/README.md | 4 +- .../lp_core/lp_uart/lp_uart_print/README.md | 4 +- 29 files changed, 132 insertions(+), 71 deletions(-) create mode 100644 components/esp_hal_clock/esp32s31/include/hal/clk_gate_ll.h diff --git a/components/esp_driver_uart/test_apps/uart/main/test_hp_uart_wakeup.c b/components/esp_driver_uart/test_apps/uart/main/test_hp_uart_wakeup.c index f85c7cba374..91e93568bc3 100644 --- a/components/esp_driver_uart/test_apps/uart/main/test_hp_uart_wakeup.c +++ b/components/esp_driver_uart/test_apps/uart/main/test_hp_uart_wakeup.c @@ -36,7 +36,7 @@ #elif CONFIG_IDF_TARGET_ESP32C61 #define DEFAULT_UART1_TX_IO_NUM GPIO_NUM_4 #define DEFAULT_UART1_RX_IO_NUM GPIO_NUM_5 -#elif CONFIG_IDF_TARGET_ESP32C5 +#elif CONFIG_IDF_TARGET_ESP32C5 || CONFIG_IDF_TARGET_ESP32S31 #define DEFAULT_UART1_TX_IO_NUM GPIO_NUM_2 #define DEFAULT_UART1_RX_IO_NUM GPIO_NUM_3 #elif CONFIG_IDF_TARGET_ESP32H21 diff --git a/components/esp_hal_clock/esp32s31/include/hal/clk_gate_ll.h b/components/esp_hal_clock/esp32s31/include/hal/clk_gate_ll.h new file mode 100644 index 00000000000..60942aaf86e --- /dev/null +++ b/components/esp_hal_clock/esp32s31/include/hal/clk_gate_ll.h @@ -0,0 +1,38 @@ +/* + * SPDX-FileCopyrightText: 2026 Espressif Systems (Shanghai) CO LTD + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#pragma once + +#include +#include +#include "esp_attr.h" +#include "soc/soc.h" +#include "soc/reset_reasons.h" +#include "soc/lp_clkrst_struct.h" +#include "soc/lp_clkrst_reg.h" + +#ifdef __cplusplus +extern "C" { +#endif + +/** + * Enable or disable the clock gate for rtc fast to lp periph + * @param enable Enable / disable + */ +FORCE_INLINE_ATTR void _clk_gate_ll_rtc_fast_to_lp_periph_en(bool enable) +{ + LP_CLKRST.root_clk_conf.fosc_clk_force_on = enable; +} +/// use a macro to wrap the function, force the caller to use it in a critical section +/// the critical section needs to declare the __DECLARE_RCC_ATOMIC_ENV variable in advance +#define clk_gate_ll_rtc_fast_to_lp_periph_en(...) do { \ + (void)__DECLARE_RCC_ATOMIC_ENV; \ + _clk_gate_ll_rtc_fast_to_lp_periph_en(__VA_ARGS__); \ + } while(0) + +#ifdef __cplusplus +} +#endif diff --git a/components/esp_hal_pmu/esp32s31/include/hal/pmu_ll.h b/components/esp_hal_pmu/esp32s31/include/hal/pmu_ll.h index 15192392543..b120ba0815c 100644 --- a/components/esp_hal_pmu/esp32s31/include/hal/pmu_ll.h +++ b/components/esp_hal_pmu/esp32s31/include/hal/pmu_ll.h @@ -795,6 +795,7 @@ FORCE_INLINE_ATTR uint32_t pmu_ll_ext1_get_wakeup_status(void) FORCE_INLINE_ATTR void pmu_ll_ext1_clear_wakeup_status(void) { REG_SET_BIT(PMU_EXT_WAKEUP_CNTL_REG, PMU_EXT_WAKEUP_STATUS_CLR); + REG_CLR_BIT(PMU_EXT_WAKEUP_CNTL_REG, PMU_EXT_WAKEUP_STATUS_CLR); } /** diff --git a/components/esp_hw_support/port/esp32s31/rtc_clk.c b/components/esp_hw_support/port/esp32s31/rtc_clk.c index d463ec6e358..ac4f2813c04 100644 --- a/components/esp_hw_support/port/esp32s31/rtc_clk.c +++ b/components/esp_hw_support/port/esp32s31/rtc_clk.c @@ -15,6 +15,7 @@ #include "esp_private/rtc_clk.h" #include "esp_hw_log.h" #include "esp_rom_sys.h" +#include "esp_sleep.h" #include "hal/clk_tree_ll.h" #include "esp_private/sleep_event.h" #include "esp_private/regi2c_ctrl.h" @@ -87,19 +88,13 @@ void rtc_clk_slow_src_set(soc_rtc_slow_clk_src_t clk_src) { clk_ll_rtc_slow_set_src(clk_src); esp_rom_delay_us(SOC_DELAY_RTC_SLOW_CLK_SWITCH); - // TODO: IDF-14645 -// #ifndef BOOTLOADER_BUILD -// if (clk_src == SOC_RTC_SLOW_CLK_SRC_XTAL32K) { -// esp_sleep_pd_config(ESP_PD_DOMAIN_XTAL32K, ESP_PD_OPTION_ON); -// } else { -// esp_sleep_pd_config(ESP_PD_DOMAIN_XTAL32K, ESP_PD_OPTION_AUTO); -// } -// if (clk_src == SOC_RTC_SLOW_CLK_SRC_RC32K) { -// esp_sleep_pd_config(ESP_PD_DOMAIN_RC32K, ESP_PD_OPTION_ON); -// } else { -// esp_sleep_pd_config(ESP_PD_DOMAIN_RC32K, ESP_PD_OPTION_AUTO); -// } -// #endif +#ifndef BOOTLOADER_BUILD + if (clk_src == SOC_RTC_SLOW_CLK_SRC_XTAL32K) { + esp_sleep_pd_config(ESP_PD_DOMAIN_XTAL32K, ESP_PD_OPTION_ON); + } else { + esp_sleep_pd_config(ESP_PD_DOMAIN_XTAL32K, ESP_PD_OPTION_AUTO); + } +#endif } soc_rtc_slow_clk_src_t rtc_clk_slow_src_get(void) diff --git a/components/esp_hw_support/sleep_modem.c b/components/esp_hw_support/sleep_modem.c index 1bffcf6129e..fb318afd660 100644 --- a/components/esp_hw_support/sleep_modem.c +++ b/components/esp_hw_support/sleep_modem.c @@ -225,7 +225,7 @@ uint32_t sleep_modem_reject_triggers(void) { uint32_t reject_triggers = 0; #if SOC_PM_SUPPORT_PMU_MODEM_STATE - reject_triggers = (s_sleep_modem.wifi.phy_link != NULL) ? BIT(16) : 0; + reject_triggers = (s_sleep_modem.wifi.phy_link != NULL) ? PMU_MODEM_WAKEUP_PROTECT : 0; #endif return reject_triggers; } diff --git a/components/esp_hw_support/sleep_modes.c b/components/esp_hw_support/sleep_modes.c index 3731b1e4097..6b2f04bead4 100644 --- a/components/esp_hw_support/sleep_modes.c +++ b/components/esp_hw_support/sleep_modes.c @@ -146,6 +146,10 @@ #include "esp32p4/rom/rtc.h" #include "hal/gpio_ll.h" #include "hal/clk_gate_ll.h" +#elif CONFIG_IDF_TARGET_ESP32S31 +#include "esp32s31/rom/rtc.h" +#include "hal/gpio_ll.h" +#include "hal/clk_gate_ll.h" #endif #if CONFIG_ESP_INT_WDT && CONFIG_ESP32_ECO3_CACHE_LOCK_FIX @@ -245,6 +249,9 @@ #define DEFAULT_SLEEP_OUT_OVERHEAD_US (324) #define DEFAULT_HARDWARE_OUT_OVERHEAD_US (240) #define LDO_POWER_TAKEOVER_PREPARATION_TIME_US (185) +#elif CONFIG_IDF_TARGET_ESP32S31 +#define DEFAULT_SLEEP_OUT_OVERHEAD_US (324) +#define DEFAULT_HARDWARE_OUT_OVERHEAD_US (240) #endif // Actually costs 80us, using the fastest slow clock 150K calculation takes about 16 ticks @@ -694,7 +701,7 @@ static SLEEP_FN_ATTR void misc_modules_sleep_prepare(uint32_t sleep_flags, bool // Only avoid USJ pad leakage here, USB OTG pad leakage is prevented through USB Host driver. sleep_console_usj_pad_backup_and_disable(); #endif -#if SOC_USB_OTG_SUPPORTED && SOC_PM_SUPPORT_CNNT_PD +#if SOC_USB_OTG_SUPPORTED && SOC_PM_SUPPORT_CNNT_PD && SOC_USB_OTG_NEED_SOFTWARE_SUSPEND_BEFORE_SLEEP if (!(sleep_flags & PMU_SLEEP_PD_CNNT)) { sleep_usb_otg_phy_backup_and_disable(); } @@ -715,7 +722,11 @@ static SLEEP_FN_ATTR void misc_modules_sleep_prepare(uint32_t sleep_flags, bool /* When using SPIRAM on the ESP32-C5, we need to use Cache_WriteBack_All to protect SPIRAM data because the cache powers down when we power down the CPU */ if(sleep_flags & PMU_SLEEP_PD_CPU) { + #if SOC_SHARED_IDCACHE_SUPPORTED || CONFIG_IDF_TARGET_ESP32H4 Cache_WriteBack_All(); +#else + Cache_WriteBack_All(CACHE_MAP_L1_DCACHE); +#endif } #endif #if ADC_LL_ANA_CALI_REG_PD_WORKAROUND @@ -762,7 +773,7 @@ static SLEEP_FN_ATTR void misc_modules_wake_prepare(uint32_t sleep_flags) #if SOC_USB_SERIAL_JTAG_SUPPORTED && !SOC_USB_SERIAL_JTAG_SUPPORT_LIGHT_SLEEP sleep_console_usj_pad_restore(); #endif -#if SOC_USB_OTG_SUPPORTED && SOC_PM_SUPPORT_CNNT_PD +#if SOC_USB_OTG_SUPPORTED && SOC_PM_SUPPORT_CNNT_PD && SOC_USB_OTG_NEED_SOFTWARE_SUSPEND_BEFORE_SLEEP if (!(sleep_flags & PMU_SLEEP_PD_CNNT)) { sleep_usb_otg_phy_restore(); } diff --git a/components/esp_hw_support/sleep_usb.c b/components/esp_hw_support/sleep_usb.c index d672fa2343a..9888a6b854f 100644 --- a/components/esp_hw_support/sleep_usb.c +++ b/components/esp_hw_support/sleep_usb.c @@ -20,18 +20,20 @@ void sleep_usb_otg_phy_backup_and_disable(void) if (!s_usb_utmi_bus_clock_state) { _usb_utmi_ll_enable_bus_clock(true); } + usb_dwc_dev_t *hw = USB_DWC_LL_GET_HW(0); // Forcing BVALID low to ignore the hardware-detected VBUS BVALID signal to suppress USB leakage. - s_usb_dwc_bvalid_override = usb_dwc_ll_get_bvalid_override(&USB_DWC_HS); - usb_dwc_ll_enable_bvalid_override(&USB_DWC_HS, true); - s_usb_utmi_stoppclk_state = usb_dwc_ll_get_stoppclk_st(&USB_DWC_HS); - usb_dwc_ll_set_stoppclk(&USB_DWC_HS, true); + s_usb_dwc_bvalid_override = usb_dwc_ll_get_bvalid_override(hw); + usb_dwc_ll_enable_bvalid_override(hw, true); + s_usb_utmi_stoppclk_state = usb_dwc_ll_get_stoppclk_st(hw); + usb_dwc_ll_set_stoppclk(hw, true); } void sleep_usb_otg_phy_restore(void) { _usb_utmi_ll_enable_bus_clock(true); - usb_dwc_ll_enable_bvalid_override(&USB_DWC_HS, s_usb_dwc_bvalid_override); - usb_dwc_ll_set_stoppclk(&USB_DWC_HS, s_usb_utmi_stoppclk_state); + usb_dwc_dev_t *hw = USB_DWC_LL_GET_HW(0); + usb_dwc_ll_enable_bvalid_override(hw, s_usb_dwc_bvalid_override); + usb_dwc_ll_set_stoppclk(hw, s_usb_utmi_stoppclk_state); if (!s_usb_utmi_bus_clock_state) { _usb_utmi_ll_enable_bus_clock(false); } diff --git a/components/esp_hw_support/test_apps/sleep_retention/README.md b/components/esp_hw_support/test_apps/sleep_retention/README.md index b366edfca53..fb2ccc2ca3e 100644 --- a/components/esp_hw_support/test_apps/sleep_retention/README.md +++ b/components/esp_hw_support/test_apps/sleep_retention/README.md @@ -1,2 +1,2 @@ -| Supported Targets | ESP32-C5 | ESP32-C6 | ESP32-C61 | ESP32-H2 | ESP32-H21 | ESP32-H4 | ESP32-P4 | -| ----------------- | -------- | -------- | --------- | -------- | --------- | -------- | -------- | +| Supported Targets | ESP32-C5 | ESP32-C6 | ESP32-C61 | ESP32-H2 | ESP32-H21 | ESP32-H4 | ESP32-P4 | ESP32-S31 | +| ----------------- | -------- | -------- | --------- | -------- | --------- | -------- | -------- | --------- | diff --git a/components/esp_hw_support/test_apps/wakeup_tests/README.md b/components/esp_hw_support/test_apps/wakeup_tests/README.md index 44f3780f1d6..d76348d52c2 100644 --- a/components/esp_hw_support/test_apps/wakeup_tests/README.md +++ b/components/esp_hw_support/test_apps/wakeup_tests/README.md @@ -1,2 +1,2 @@ -| Supported Targets | ESP32 | ESP32-C2 | ESP32-C3 | ESP32-C5 | ESP32-C6 | ESP32-C61 | ESP32-H2 | ESP32-H21 | ESP32-H4 | ESP32-P4 | ESP32-S2 | ESP32-S3 | -| ----------------- | ----- | -------- | -------- | -------- | -------- | --------- | -------- | --------- | -------- | -------- | -------- | -------- | +| Supported Targets | ESP32 | ESP32-C2 | ESP32-C3 | ESP32-C5 | ESP32-C6 | ESP32-C61 | ESP32-H2 | ESP32-H21 | ESP32-H4 | ESP32-P4 | ESP32-S2 | ESP32-S3 | ESP32-S31 | +| ----------------- | ----- | -------- | -------- | -------- | -------- | --------- | -------- | --------- | -------- | -------- | -------- | -------- | --------- | diff --git a/components/esp_pm/linker.lf b/components/esp_pm/linker.lf index 13432b5e366..ae69c7c2858 100644 --- a/components/esp_pm/linker.lf +++ b/components/esp_pm/linker.lf @@ -40,7 +40,7 @@ entries: esp_clk:esp_clk_private_unlock (noflash) if SOC_SYSTIMER_SUPPORTED = y: systimer (noflash) - if IDF_TARGET_ESP32H21 != y && IDF_TARGET_ESP32H4 != y: + if SOC_ADC_SUPPORTED = y: sar_periph_ctrl:sar_periph_ctrl_power_disable (noflash) sar_periph_ctrl:adc_reset_lock_acquire (noflash) sar_periph_ctrl:adc_reset_lock_release (noflash) diff --git a/components/esp_system/port/cpu_start.c b/components/esp_system/port/cpu_start.c index 05cf7e5c677..752e9eef304 100644 --- a/components/esp_system/port/cpu_start.c +++ b/components/esp_system/port/cpu_start.c @@ -569,14 +569,15 @@ FORCE_INLINE_ATTR IRAM_ATTR void ext_mem_init(void) MSPI_INIT_ATTR void sys_rtc_init(const soc_reset_reason_t *rst_reas) { #if SOC_RTC_WDT_SUPPORTED -#if CONFIG_IDF_TARGET_ESP32P4 +#if CONFIG_IDF_TARGET_ESP32P4 || CONFIG_IDF_TARGET_ESP32S31 #define RWDT_RESET RESET_REASON_CORE_RWDT -#define MWDT_RESET RESET_REASON_CORE_MWDT -#elif CONFIG_IDF_TARGET_ESP32S31 -#define RWDT_RESET RESET_REASON_CORE_RWDT -#define MWDT_RESET RESET_REASON_CORE_MWDT0 #else #define RWDT_RESET RESET_REASON_CORE_RTC_WDT +#endif + +#if CONFIG_IDF_TARGET_ESP32P4 +#define MWDT_RESET RESET_REASON_CORE_MWDT +#else #define MWDT_RESET RESET_REASON_CORE_MWDT0 #endif diff --git a/components/soc/esp32p4/include/soc/Kconfig.soc_caps.in b/components/soc/esp32p4/include/soc/Kconfig.soc_caps.in index 22f0913e4eb..984a7fa3f61 100644 --- a/components/soc/esp32p4/include/soc/Kconfig.soc_caps.in +++ b/components/soc/esp32p4/include/soc/Kconfig.soc_caps.in @@ -1171,6 +1171,10 @@ config SOC_USB_FSLS_PHY_NUM int default 1 +config SOC_USB_OTG_NEED_SOFTWARE_SUSPEND_BEFORE_SLEEP + bool + default y + config SOC_USB_UTMI_PHY_NUM int default 1 diff --git a/components/soc/esp32p4/include/soc/soc_caps.h b/components/soc/esp32p4/include/soc/soc_caps.h index 72976610f36..10193dc3fdb 100644 --- a/components/soc/esp32p4/include/soc/soc_caps.h +++ b/components/soc/esp32p4/include/soc/soc_caps.h @@ -432,6 +432,7 @@ // USB OTG Caps #define SOC_USB_OTG_PERIPH_NUM (2U) #define SOC_USB_FSLS_PHY_NUM (1U) +#define SOC_USB_OTG_NEED_SOFTWARE_SUSPEND_BEFORE_SLEEP (1) // USB PHY Caps #define SOC_USB_UTMI_PHY_NUM (1U) diff --git a/components/soc/esp32s31/include/soc/Kconfig.soc_caps.in b/components/soc/esp32s31/include/soc/Kconfig.soc_caps.in index de4d57c63dc..3394abd65c2 100644 --- a/components/soc/esp32s31/include/soc/Kconfig.soc_caps.in +++ b/components/soc/esp32s31/include/soc/Kconfig.soc_caps.in @@ -235,6 +235,14 @@ config SOC_PPA_SUPPORTED bool default y +config SOC_LIGHT_SLEEP_SUPPORTED + bool + default y + +config SOC_DEEP_SLEEP_SUPPORTED + bool + default y + config SOC_MODEM_CLOCK_SUPPORTED bool default y @@ -303,6 +311,10 @@ config SOC_XTAL_SUPPORT_40M bool default y +config SOC_XTAL_CLOCK_PATH_DEPENDS_ON_TOP_DOMAIN + bool + default y + config SOC_ADC_MAX_CHANNEL_NUM int default 10 @@ -443,6 +455,10 @@ config SOC_GPIO_SUPPORT_ETM bool default y +config SOC_GPIO_SUPPORT_HP_PERIPH_PD_SLEEP_WAKEUP + bool + default y + config SOC_LP_IO_HAS_INDEPENDENT_WAKEUP_SOURCE bool default y @@ -459,14 +475,10 @@ config SOC_GPIO_OUT_RANGE_MAX int default 61 -config SOC_GPIO_DEEP_SLEEP_WAKE_VALID_GPIO_MASK +config SOC_GPIO_HP_PERIPH_PD_SLEEP_WAKEABLE_MASK int default 0 -config SOC_GPIO_DEEP_SLEEP_WAKE_SUPPORTED_PIN_CNT - int - default 8 - config SOC_GPIO_SUPPORT_FORCE_HOLD bool default y @@ -715,10 +727,6 @@ config SOC_SPI_MEM_SUPPORT_TSUS_TRES_SEPERATE_CTR 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/esp32s31/include/soc/soc_caps.h b/components/soc/esp32s31/include/soc/soc_caps.h index f0f75fb525b..914c43ce69d 100644 --- a/components/soc/esp32s31/include/soc/soc_caps.h +++ b/components/soc/esp32s31/include/soc/soc_caps.h @@ -97,8 +97,8 @@ #define SOC_TOUCH_SENSOR_SUPPORTED 1 // #define SOC_RNG_SUPPORTED 1 // TODO: [ESP32S31] IDF-14632 #define SOC_PPA_SUPPORTED 1 -// #define SOC_LIGHT_SLEEP_SUPPORTED 1 // TODO: [ESP32S31] IDF-14645 -// #define SOC_DEEP_SLEEP_SUPPORTED 1 // TODO: [ESP32S31] IDF-14643 +#define SOC_LIGHT_SLEEP_SUPPORTED 1 +#define SOC_DEEP_SLEEP_SUPPORTED 1 #define SOC_MODEM_CLOCK_SUPPORTED 1 // #define SOC_PM_SUPPORTED 1 // TODO: [ESP32S31] IDF-14648 #define SOC_BITSCRAMBLER_SUPPORTED 1 @@ -122,7 +122,8 @@ #define SOC_BT_SUPPORTED 1 #define SOC_PHY_CALIBRATION_CLOCK_IS_INDEPENDENT 1 /*-------------------------- XTAL CAPS ---------------------------------------*/ -#define SOC_XTAL_SUPPORT_40M 1 +#define SOC_XTAL_SUPPORT_40M 1 +#define SOC_XTAL_CLOCK_PATH_DEPENDS_ON_TOP_DOMAIN 1 /*-------------------------- ADC CAPS ----------------------------------------*/ /*!< SAR ADC Module*/ @@ -185,7 +186,7 @@ #define SOC_GPIO_SUPPORT_ETM 1 // GPIO0~7 on ESP32S31 can support chip deep sleep wakeup -// #define SOC_GPIO_SUPPORT_HP_PERIPH_PD_SLEEP_WAKEUP (1) // TODO: [ESP32S31] IDF-14643 +#define SOC_GPIO_SUPPORT_HP_PERIPH_PD_SLEEP_WAKEUP (1) #define SOC_GPIO_SUPPORT_DEEPSLEEP_WAKEUP SOC_GPIO_SUPPORT_HP_PERIPH_PD_SLEEP_WAKEUP #define SOC_LP_IO_HAS_INDEPENDENT_WAKEUP_SOURCE (1) @@ -198,8 +199,7 @@ #define SOC_GPIO_IN_RANGE_MAX 61 #define SOC_GPIO_OUT_RANGE_MAX 61 -#define SOC_GPIO_DEEP_SLEEP_WAKE_VALID_GPIO_MASK (0ULL | 0xFF) -#define SOC_GPIO_DEEP_SLEEP_WAKE_SUPPORTED_PIN_CNT (8) +#define SOC_GPIO_HP_PERIPH_PD_SLEEP_WAKEABLE_MASK (0ULL | 0xFF) // digital I/O pad powered by VDD3V3_CPU or VDD_SPI(GPIO_NUM_8~GPIO_NUM_61, excluding GPIO29/41 which are not bonded) #define SOC_GPIO_VALID_DIGITAL_IO_PAD_MASK (SOC_GPIO_VALID_GPIO_MASK & ~((1ULL << SOC_RTCIO_PIN_COUNT) - 1)) @@ -298,7 +298,6 @@ #define SOC_SPI_MEM_SUPPORT_CACHE_32BIT_ADDR_MAP (1) #define SOC_SPI_MEM_SUPPORT_TSUS_TRES_SEPERATE_CTR (1) -#define SOC_MSPI_HAS_INDEPENT_IOMUX 1 #define SOC_MEMSPI_IS_INDEPENDENT 1 #define SOC_MEMSPI_ENCRYPTION_ALIGNMENT 16 /*!< 16-byte alignment restriction to mem addr and size if encryption is enabled */ diff --git a/examples/system/deep_sleep/README.md b/examples/system/deep_sleep/README.md index da637e2bba6..296cf07c926 100644 --- a/examples/system/deep_sleep/README.md +++ b/examples/system/deep_sleep/README.md @@ -1,5 +1,5 @@ -| Supported Targets | ESP32 | ESP32-C2 | ESP32-C3 | ESP32-C5 | ESP32-C6 | ESP32-C61 | ESP32-H2 | ESP32-H21 | ESP32-H4 | ESP32-P4 | ESP32-S2 | ESP32-S3 | -| ----------------- | ----- | -------- | -------- | -------- | -------- | --------- | -------- | --------- | -------- | -------- | -------- | -------- | +| Supported Targets | ESP32 | ESP32-C2 | ESP32-C3 | ESP32-C5 | ESP32-C6 | ESP32-C61 | ESP32-H2 | ESP32-H21 | ESP32-H4 | ESP32-P4 | ESP32-S2 | ESP32-S3 | ESP32-S31 | +| ----------------- | ----- | -------- | -------- | -------- | -------- | --------- | -------- | --------- | -------- | -------- | -------- | -------- | --------- | # Deep Sleep Example diff --git a/examples/system/deep_sleep/main/Kconfig.projbuild b/examples/system/deep_sleep/main/Kconfig.projbuild index 0fd23a8a7e8..2657efb2b88 100644 --- a/examples/system/deep_sleep/main/Kconfig.projbuild +++ b/examples/system/deep_sleep/main/Kconfig.projbuild @@ -268,6 +268,7 @@ menu "Example Configuration" range 0 7 if IDF_TARGET_ESP32C6 range 0 6 if IDF_TARGET_ESP32C61 || IDF_TARGET_ESP32C5 range 0 15 if IDF_TARGET_ESP32P4 + range 0 7 if IDF_TARGET_ESP32S31 range 0 5 help Only GPIOs which have RTC functionality (pads that powered by VDD3P3_RTC) can be used, diff --git a/examples/system/deep_sleep/pytest_deep_sleep.py b/examples/system/deep_sleep/pytest_deep_sleep.py index 9e6ef09c3e3..3cb8334c819 100644 --- a/examples/system/deep_sleep/pytest_deep_sleep.py +++ b/examples/system/deep_sleep/pytest_deep_sleep.py @@ -12,7 +12,6 @@ from pytest_embedded_idf.utils import idf_parametrize @idf_parametrize( 'config,target', [('esp32_singlecore', 'esp32'), ('basic', 'supported_targets')], indirect=['config', 'target'] ) -@pytest.mark.temp_skip_ci(targets=['esp32s31'], reason='s31 bringup on this module is not done') def test_deep_sleep(dut: Dut) -> None: dut.expect_exact('Enabling timer wakeup, 20s', timeout=10) dut.expect_exact('Not a deep sleep reset') diff --git a/examples/system/light_sleep/README.md b/examples/system/light_sleep/README.md index 6aeb8161ccf..89f3d632cba 100644 --- a/examples/system/light_sleep/README.md +++ b/examples/system/light_sleep/README.md @@ -1,5 +1,5 @@ -| Supported Targets | ESP32 | ESP32-C2 | ESP32-C3 | ESP32-C5 | ESP32-C6 | ESP32-C61 | ESP32-H2 | ESP32-H21 | ESP32-H4 | ESP32-P4 | ESP32-S2 | ESP32-S3 | -| ----------------- | ----- | -------- | -------- | -------- | -------- | --------- | -------- | --------- | -------- | -------- | -------- | -------- | +| Supported Targets | ESP32 | ESP32-C2 | ESP32-C3 | ESP32-C5 | ESP32-C6 | ESP32-C61 | ESP32-H2 | ESP32-H21 | ESP32-H4 | ESP32-P4 | ESP32-S2 | ESP32-S3 | ESP32-S31 | +| ----------------- | ----- | -------- | -------- | -------- | -------- | --------- | -------- | --------- | -------- | -------- | -------- | -------- | --------- | # Light Sleep Example diff --git a/examples/system/light_sleep/main/gpio_wakeup.c b/examples/system/light_sleep/main/gpio_wakeup.c index 75778640b83..aac5be4fd62 100644 --- a/examples/system/light_sleep/main/gpio_wakeup.c +++ b/examples/system/light_sleep/main/gpio_wakeup.c @@ -21,6 +21,8 @@ #define BOOT_BUTTON_NUM 35 #elif CONFIG_IDF_TARGET_ESP32H21 #define BOOT_BUTTON_NUM 14 +#elif CONFIG_IDF_TARGET_ESP32S31 +#define BOOT_BUTTON_NUM 61 #else #define BOOT_BUTTON_NUM 0 #endif diff --git a/examples/system/light_sleep/pytest_light_sleep.py b/examples/system/light_sleep/pytest_light_sleep.py index 14b2393521b..dc56b443256 100644 --- a/examples/system/light_sleep/pytest_light_sleep.py +++ b/examples/system/light_sleep/pytest_light_sleep.py @@ -10,7 +10,6 @@ from pytest_embedded_idf.utils import idf_parametrize @pytest.mark.generic @idf_parametrize('target', ['supported_targets'], indirect=['target']) -@pytest.mark.temp_skip_ci(targets=['esp32s31'], reason='s31 bringup on this module is not done') def test_light_sleep(dut: Dut) -> None: ENTERING_SLEEP_STR = 'Entering light sleep' EXIT_SLEEP_REGEX = r'Returned from light sleep, reason: (\w+), t=(\d+) ms, slept for (\d+) ms' diff --git a/examples/system/ulp/lp_core/gpio/README.md b/examples/system/ulp/lp_core/gpio/README.md index 363d0b6b6af..d082cf4748d 100644 --- a/examples/system/ulp/lp_core/gpio/README.md +++ b/examples/system/ulp/lp_core/gpio/README.md @@ -1,5 +1,5 @@ -| Supported Targets | ESP32-C5 | ESP32-C6 | ESP32-P4 | -| ----------------- | -------- | -------- | -------- | +| Supported Targets | ESP32-C5 | ESP32-C6 | ESP32-P4 | ESP32-S31 | +| ----------------- | -------- | -------- | -------- | --------- | # LP Core simple example with GPIO Polling: diff --git a/examples/system/ulp/lp_core/gpio_intr_pulse_counter/README.md b/examples/system/ulp/lp_core/gpio_intr_pulse_counter/README.md index c6e3dfd7ed4..3a74ba666bb 100644 --- a/examples/system/ulp/lp_core/gpio_intr_pulse_counter/README.md +++ b/examples/system/ulp/lp_core/gpio_intr_pulse_counter/README.md @@ -1,5 +1,5 @@ -| Supported Targets | ESP32-C5 | ESP32-C6 | ESP32-P4 | -| ----------------- | -------- | -------- | -------- | +| Supported Targets | ESP32-C5 | ESP32-C6 | ESP32-P4 | ESP32-S31 | +| ----------------- | -------- | -------- | -------- | --------- | # LP Core Pulse Counting Example diff --git a/examples/system/ulp/lp_core/gpio_wakeup/README.md b/examples/system/ulp/lp_core/gpio_wakeup/README.md index 66925750726..0f8a94bf4c0 100644 --- a/examples/system/ulp/lp_core/gpio_wakeup/README.md +++ b/examples/system/ulp/lp_core/gpio_wakeup/README.md @@ -1,5 +1,5 @@ -| Supported Targets | ESP32-C5 | ESP32-C6 | ESP32-P4 | -| ----------------- | -------- | -------- | -------- | +| Supported Targets | ESP32-C5 | ESP32-C6 | ESP32-P4 | ESP32-S31 | +| ----------------- | -------- | -------- | -------- | --------- | # ULP-LP-Core simple example with GPIO Interrupt: This example demonstrates how to program the LP-Core coprocessor to wake up from a RTC IO interrupt, instead of waking periodically from the ULP timer. diff --git a/examples/system/ulp/lp_core/lp_spi/README.md b/examples/system/ulp/lp_core/lp_spi/README.md index 0d5f94ccd49..a7a619da935 100644 --- a/examples/system/ulp/lp_core/lp_spi/README.md +++ b/examples/system/ulp/lp_core/lp_spi/README.md @@ -1,5 +1,5 @@ -| Supported Targets | ESP32-P4 | -| ----------------- | -------- | +| Supported Targets | ESP32-P4 | ESP32-S31 | +| ----------------- | -------- | --------- | # LP SPI Example diff --git a/examples/system/ulp/lp_core/lp_touch/README.md b/examples/system/ulp/lp_core/lp_touch/README.md index 868e774980d..b913ce143a8 100644 --- a/examples/system/ulp/lp_core/lp_touch/README.md +++ b/examples/system/ulp/lp_core/lp_touch/README.md @@ -1,5 +1,5 @@ -| Supported Targets | ESP32-P4 | -| ----------------- | -------- | +| Supported Targets | ESP32-P4 | ESP32-S31 | +| ----------------- | -------- | --------- | # LP Core Touch Sensor example: diff --git a/examples/system/ulp/lp_core/lp_uart/lp_uart_char_seq_wakeup/README.md b/examples/system/ulp/lp_core/lp_uart/lp_uart_char_seq_wakeup/README.md index 3dc2d4833a8..4b828286237 100644 --- a/examples/system/ulp/lp_core/lp_uart/lp_uart_char_seq_wakeup/README.md +++ b/examples/system/ulp/lp_core/lp_uart/lp_uart_char_seq_wakeup/README.md @@ -1,5 +1,5 @@ -| Supported Targets | ESP32-C5 | ESP32-C6 | ESP32-P4 | -| ----------------- | -------- | -------- | -------- | +| Supported Targets | ESP32-C5 | ESP32-C6 | ESP32-P4 | ESP32-S31 | +| ----------------- | -------- | -------- | -------- | --------- | # LP UART specific character sequence wakeup mode Example diff --git a/examples/system/ulp/lp_core/lp_uart/lp_uart_echo/README.md b/examples/system/ulp/lp_core/lp_uart/lp_uart_echo/README.md index daf06d51b0c..f33ac6b136f 100644 --- a/examples/system/ulp/lp_core/lp_uart/lp_uart_echo/README.md +++ b/examples/system/ulp/lp_core/lp_uart/lp_uart_echo/README.md @@ -1,5 +1,5 @@ -| Supported Targets | ESP32-C5 | ESP32-C6 | ESP32-P4 | -| ----------------- | -------- | -------- | -------- | +| Supported Targets | ESP32-C5 | ESP32-C6 | ESP32-P4 | ESP32-S31 | +| ----------------- | -------- | -------- | -------- | --------- | # LP UART Echo Example diff --git a/examples/system/ulp/lp_core/lp_uart/lp_uart_print/README.md b/examples/system/ulp/lp_core/lp_uart/lp_uart_print/README.md index ca96fdc0a36..0ab370e704d 100644 --- a/examples/system/ulp/lp_core/lp_uart/lp_uart_print/README.md +++ b/examples/system/ulp/lp_core/lp_uart/lp_uart_print/README.md @@ -1,5 +1,5 @@ -| Supported Targets | ESP32-C5 | ESP32-C6 | ESP32-P4 | -| ----------------- | -------- | -------- | -------- | +| Supported Targets | ESP32-C5 | ESP32-C6 | ESP32-P4 | ESP32-S31 | +| ----------------- | -------- | -------- | -------- | --------- | # LP UART Print Example From 68bd919fda1be643e19c055a4fcf2fc12ea88755 Mon Sep 17 00:00:00 2001 From: wuzhenghui Date: Wed, 6 May 2026 16:27:28 +0800 Subject: [PATCH 03/13] feat(esp_hw_support): support esp32s31 DFS --- components/esp_pm/pm_impl.c | 25 +------- components/soc/esp32c2/include/soc/soc.h | 1 - components/soc/esp32c3/include/soc/soc.h | 1 - components/soc/esp32c5/include/soc/soc.h | 1 - components/soc/esp32c6/include/soc/soc.h | 1 - components/soc/esp32c61/include/soc/soc.h | 1 - components/soc/esp32h2/include/soc/soc.h | 1 - components/soc/esp32h21/include/soc/soc.h | 1 - components/soc/esp32h4/include/soc/soc.h | 1 - components/soc/esp32p4/include/soc/soc.h | 1 - components/soc/esp32s3/include/soc/soc.h | 1 - .../esp32s31/include/soc/Kconfig.soc_caps.in | 4 ++ components/soc/esp32s31/include/soc/soc.h | 1 - .../soc/esp32s31/include/soc/soc_caps.h | 1 + .../main/Kconfig.esp32s31.supported_freq | 57 +++++++++++++++++++ 15 files changed, 65 insertions(+), 33 deletions(-) create mode 100644 examples/lowpower/power_management/main/Kconfig.esp32s31.supported_freq diff --git a/components/esp_pm/pm_impl.c b/components/esp_pm/pm_impl.c index a3ac7442285..5a2f9e6b291 100644 --- a/components/esp_pm/pm_impl.c +++ b/components/esp_pm/pm_impl.c @@ -82,27 +82,6 @@ #elif CONFIG_IDF_TARGET_ESP32S2 /* Minimal divider at which REF_CLK_FREQ can be obtained */ #define REF_CLK_DIV_MIN 2 -#elif CONFIG_IDF_TARGET_ESP32S3 -/* Minimal divider at which REF_CLK_FREQ can be obtained */ -#define REF_CLK_DIV_MIN 2 // TODO: IDF-5660 -#elif CONFIG_IDF_TARGET_ESP32C3 -#define REF_CLK_DIV_MIN 2 -#elif CONFIG_IDF_TARGET_ESP32C2 -#define REF_CLK_DIV_MIN 2 -#elif CONFIG_IDF_TARGET_ESP32C6 -#define REF_CLK_DIV_MIN 2 -#elif CONFIG_IDF_TARGET_ESP32C61 -#define REF_CLK_DIV_MIN 2 -#elif CONFIG_IDF_TARGET_ESP32C5 -#define REF_CLK_DIV_MIN 2 -#elif CONFIG_IDF_TARGET_ESP32H2 -#define REF_CLK_DIV_MIN 2 -#elif CONFIG_IDF_TARGET_ESP32H21 -#define REF_CLK_DIV_MIN 2 -#elif CONFIG_IDF_TARGET_ESP32P4 -#define REF_CLK_DIV_MIN 2 -#elif CONFIG_IDF_TARGET_ESP32H4 -#define REF_CLK_DIV_MIN 2 #endif #ifdef CONFIG_PM_PROFILING @@ -451,11 +430,13 @@ esp_err_t esp_pm_configure(const void* vconfig) return ESP_ERR_INVALID_ARG; } +#ifdef REF_CLK_DIV_MIN int xtal_freq_mhz = esp_clk_xtal_freq() / MHZ; if (min_freq_mhz < xtal_freq_mhz && min_freq_mhz * MHZ / REF_CLK_FREQ < REF_CLK_DIV_MIN) { ESP_LOGW(TAG, "min_freq_mhz should be >= %d", REF_CLK_FREQ * REF_CLK_DIV_MIN / MHZ); return ESP_ERR_INVALID_ARG; } +#endif if (!rtc_clk_cpu_freq_mhz_to_config(max_freq_mhz, &freq_config)) { ESP_LOGW(TAG, "invalid max_freq_mhz value (%d)", max_freq_mhz); @@ -479,7 +460,7 @@ esp_err_t esp_pm_configure(const void* vconfig) /* Maximum SOC APB clock frequency is 40 MHz, maximum Modem (WiFi, * Bluetooth, etc..) APB clock frequency is 80 MHz */ int apb_clk_freq = esp_clk_apb_freq() / MHZ; -#if (CONFIG_ESP_WIFI_ENABLED || CONFIG_BT_ENABLED || CONFIG_IEEE802154_ENABLED) && SOC_PHY_SUPPORTED +#if (CONFIG_ESP_WIFI_ENABLED || CONFIG_BT_ENABLED || CONFIG_IEEE802154_ENABLED) && SOC_PHY_SUPPORTED && !SOC_MODEM_APB_CLOCK_IS_INDEPENDENT apb_clk_freq = MAX(apb_clk_freq, MODEM_REQUIRED_MIN_APB_CLK_FREQ / MHZ); #endif int apb_max_freq = MIN(max_freq_mhz, apb_clk_freq); /* CPU frequency in APB_MAX mode */ diff --git a/components/soc/esp32c2/include/soc/soc.h b/components/soc/esp32c2/include/soc/soc.h index a81dcfe1c27..ba17f66b864 100644 --- a/components/soc/esp32c2/include/soc/soc.h +++ b/components/soc/esp32c2/include/soc/soc.h @@ -142,7 +142,6 @@ //Periheral Clock {{ #define APB_CLK_FREQ (SOC_XTAL_FREQ_MHZ * 1000000 ) #define MODEM_REQUIRED_MIN_APB_CLK_FREQ ( 80*1000000 ) -#define REF_CLK_FREQ ( 1000000 ) //}} /* Overall memory map */ diff --git a/components/soc/esp32c3/include/soc/soc.h b/components/soc/esp32c3/include/soc/soc.h index 19161ee2f1d..bbcd9680ee1 100644 --- a/components/soc/esp32c3/include/soc/soc.h +++ b/components/soc/esp32c3/include/soc/soc.h @@ -135,7 +135,6 @@ //Periheral Clock {{ #define APB_CLK_FREQ ( 80*1000000 ) #define MODEM_REQUIRED_MIN_APB_CLK_FREQ ( 80*1000000 ) -#define REF_CLK_FREQ ( 1000000 ) //}} /* Overall memory map */ diff --git a/components/soc/esp32c5/include/soc/soc.h b/components/soc/esp32c5/include/soc/soc.h index 5aa0babd506..6383311de6c 100644 --- a/components/soc/esp32c5/include/soc/soc.h +++ b/components/soc/esp32c5/include/soc/soc.h @@ -133,7 +133,6 @@ //Periheral Clock {{ #define APB_CLK_FREQ ( 40*1000000 ) #define MODEM_REQUIRED_MIN_APB_CLK_FREQ ( 80*1000000 ) -#define REF_CLK_FREQ ( 1000000 ) //}} /* Overall memory map */ diff --git a/components/soc/esp32c6/include/soc/soc.h b/components/soc/esp32c6/include/soc/soc.h index 2dc76dc8f5a..724a826034e 100644 --- a/components/soc/esp32c6/include/soc/soc.h +++ b/components/soc/esp32c6/include/soc/soc.h @@ -136,7 +136,6 @@ //Periheral Clock {{ #define APB_CLK_FREQ ( 40*1000000 ) #define MODEM_REQUIRED_MIN_APB_CLK_FREQ ( 80*1000000 ) -#define REF_CLK_FREQ ( 1000000 ) //}} /* Overall memory map */ diff --git a/components/soc/esp32c61/include/soc/soc.h b/components/soc/esp32c61/include/soc/soc.h index 3c7bd81d5be..3cc4ad23ea2 100644 --- a/components/soc/esp32c61/include/soc/soc.h +++ b/components/soc/esp32c61/include/soc/soc.h @@ -133,7 +133,6 @@ //Periheral Clock {{ #define APB_CLK_FREQ ( 40*1000000 ) #define MODEM_REQUIRED_MIN_APB_CLK_FREQ ( 80*1000000 ) -#define REF_CLK_FREQ ( 1000000 ) //}} /* Overall memory map */ diff --git a/components/soc/esp32h2/include/soc/soc.h b/components/soc/esp32h2/include/soc/soc.h index ab799e1b7d6..27f75641f99 100644 --- a/components/soc/esp32h2/include/soc/soc.h +++ b/components/soc/esp32h2/include/soc/soc.h @@ -135,7 +135,6 @@ //Periheral Clock {{ #define APB_CLK_FREQ ( 32*1000000 ) #define MODEM_REQUIRED_MIN_APB_CLK_FREQ ( 32*1000000 ) -#define REF_CLK_FREQ ( 1000000 ) //}} /* Overall memory map */ diff --git a/components/soc/esp32h21/include/soc/soc.h b/components/soc/esp32h21/include/soc/soc.h index 38a25c7b937..24668ac5c5b 100644 --- a/components/soc/esp32h21/include/soc/soc.h +++ b/components/soc/esp32h21/include/soc/soc.h @@ -137,7 +137,6 @@ //Periheral Clock {{ #define APB_CLK_FREQ ( 32*1000000 ) #define MODEM_REQUIRED_MIN_APB_CLK_FREQ ( 32*1000000 ) -#define REF_CLK_FREQ ( 1000000 ) //}} /* Overall memory map */ diff --git a/components/soc/esp32h4/include/soc/soc.h b/components/soc/esp32h4/include/soc/soc.h index 71275ced101..1daca7b6c6b 100644 --- a/components/soc/esp32h4/include/soc/soc.h +++ b/components/soc/esp32h4/include/soc/soc.h @@ -126,7 +126,6 @@ //Periheral Clock {{ #define APB_CLK_FREQ ( 32*1000000 ) #define MODEM_REQUIRED_MIN_APB_CLK_FREQ ( 32*1000000 ) -#define REF_CLK_FREQ ( 1000000 ) //}} /* Overall memory map */ diff --git a/components/soc/esp32p4/include/soc/soc.h b/components/soc/esp32p4/include/soc/soc.h index a9046f83c05..d0f5d1ccc05 100644 --- a/components/soc/esp32p4/include/soc/soc.h +++ b/components/soc/esp32p4/include/soc/soc.h @@ -133,7 +133,6 @@ //Periheral Clock {{ #define APB_CLK_FREQ ( 90*1000000 ) -#define REF_CLK_FREQ ( 1000000 ) //}} /* Overall memory map */ diff --git a/components/soc/esp32s3/include/soc/soc.h b/components/soc/esp32s3/include/soc/soc.h index d4c867a834f..8588770c3cf 100644 --- a/components/soc/esp32s3/include/soc/soc.h +++ b/components/soc/esp32s3/include/soc/soc.h @@ -150,7 +150,6 @@ //Periheral Clock {{ #define APB_CLK_FREQ (80*1000000) #define MODEM_REQUIRED_MIN_APB_CLK_FREQ (80*1000000) -#define REF_CLK_FREQ (1000000) //}} /* Overall memory map */ diff --git a/components/soc/esp32s31/include/soc/Kconfig.soc_caps.in b/components/soc/esp32s31/include/soc/Kconfig.soc_caps.in index 3394abd65c2..d876a82bcd8 100644 --- a/components/soc/esp32s31/include/soc/Kconfig.soc_caps.in +++ b/components/soc/esp32s31/include/soc/Kconfig.soc_caps.in @@ -975,6 +975,10 @@ config SOC_MODEM_CLOCK_IS_INDEPENDENT bool default y +config SOC_MODEM_APB_CLOCK_IS_INDEPENDENT + bool + default y + config SOC_MODEM_CLOCK_SOC_PLL_SOURCE_CG_SUPPORTED bool default y diff --git a/components/soc/esp32s31/include/soc/soc.h b/components/soc/esp32s31/include/soc/soc.h index 771d0ed6ec5..8c8da22ad16 100644 --- a/components/soc/esp32s31/include/soc/soc.h +++ b/components/soc/esp32s31/include/soc/soc.h @@ -128,7 +128,6 @@ //Periheral Clock {{ #define APB_CLK_FREQ ( 40*1000000 ) -#define REF_CLK_FREQ ( 1000000 ) //}} /* Overall memory map */ diff --git a/components/soc/esp32s31/include/soc/soc_caps.h b/components/soc/esp32s31/include/soc/soc_caps.h index 914c43ce69d..1b70b3d4327 100644 --- a/components/soc/esp32s31/include/soc/soc_caps.h +++ b/components/soc/esp32s31/include/soc/soc_caps.h @@ -402,6 +402,7 @@ // /*-------------------------- CLOCK SUBSYSTEM CAPS ----------------------------------------*/ #define SOC_MODEM_CLOCK_IS_INDEPENDENT (1) +#define SOC_MODEM_APB_CLOCK_IS_INDEPENDENT (1) #define SOC_MODEM_CLOCK_SOC_PLL_SOURCE_CG_SUPPORTED (1) #define SOC_MODEM_CLOCK_WIFI_BB_80X1_AS_APB (1) diff --git a/examples/lowpower/power_management/main/Kconfig.esp32s31.supported_freq b/examples/lowpower/power_management/main/Kconfig.esp32s31.supported_freq new file mode 100644 index 00000000000..770a65d671f --- /dev/null +++ b/examples/lowpower/power_management/main/Kconfig.esp32s31.supported_freq @@ -0,0 +1,57 @@ +choice EXAMPLE_PM_MAX_FREQ_MHZ + prompt "Max CPU frequency (MHz)" + default EXAMPLE_PM_MAX_FREQ_MHZ_320 + config EXAMPLE_PM_MAX_FREQ_MHZ_10 + bool "10" + config EXAMPLE_PM_MAX_FREQ_MHZ_20 + bool "20" + config EXAMPLE_PM_MAX_FREQ_MHZ_40 + bool "40" + config EXAMPLE_PM_MAX_FREQ_MHZ_80 + bool "80" + config EXAMPLE_PM_MAX_FREQ_MHZ_160 + bool "160" + config EXAMPLE_PM_MAX_FREQ_MHZ_240 + bool "240" + config EXAMPLE_PM_MAX_FREQ_MHZ_320 + bool "320" +endchoice + +config EXAMPLE_PM_MAX_FREQ + int + default 10 if EXAMPLE_PM_MAX_FREQ_MHZ_10 + default 20 if EXAMPLE_PM_MAX_FREQ_MHZ_20 + default 40 if EXAMPLE_PM_MAX_FREQ_MHZ_40 + default 80 if EXAMPLE_PM_MAX_FREQ_MHZ_80 + default 160 if EXAMPLE_PM_MAX_FREQ_MHZ_160 + default 240 if EXAMPLE_PM_MAX_FREQ_MHZ_240 + default 320 if EXAMPLE_PM_MAX_FREQ_MHZ_320 + +choice EXAMPLE_PM_MIN_FREQ_MHZ + prompt "Min CPU frequency (MHz)" + default EXAMPLE_PM_MIN_FREQ_MHZ_40 + config EXAMPLE_PM_MIN_FREQ_MHZ_10 + bool "10" + config EXAMPLE_PM_MIN_FREQ_MHZ_20 + bool "20" + config EXAMPLE_PM_MIN_FREQ_MHZ_40 + bool "40" + config EXAMPLE_PM_MIN_FREQ_MHZ_80 + bool "80" + config EXAMPLE_PM_MIN_FREQ_MHZ_160 + bool "160" + config EXAMPLE_PM_MIN_FREQ_MHZ_240 + bool "240" + config EXAMPLE_PM_MIN_FREQ_MHZ_320 + bool "320" +endchoice + +config EXAMPLE_PM_MIN_FREQ + int + default 10 if EXAMPLE_PM_MIN_FREQ_MHZ_10 + default 20 if EXAMPLE_PM_MIN_FREQ_MHZ_20 + default 40 if EXAMPLE_PM_MIN_FREQ_MHZ_40 + default 80 if EXAMPLE_PM_MIN_FREQ_MHZ_80 + default 160 if EXAMPLE_PM_MIN_FREQ_MHZ_160 + default 240 if EXAMPLE_PM_MIN_FREQ_MHZ_240 + default 320 if EXAMPLE_PM_MIN_FREQ_MHZ_320 From a07e2c8d851cc4be059fc8e8250eb06dc9a4d7d2 Mon Sep 17 00:00:00 2001 From: wuzhenghui Date: Tue, 14 Apr 2026 11:43:29 +0800 Subject: [PATCH 04/13] feat(esp_hw_support): support esp32s31 CPU/FPU retention --- .../lowpower/port/esp32s31/rvsleep-frames.h | 225 ++++++++ .../lowpower/port/esp32s31/sleep_cpu.c | 525 ++++++++++++++++++ .../lowpower/port/esp32s31/sleep_cpu_asm.S | 219 ++++++++ .../port/esp32s31/sleep_cpu_dynamic.c | 147 +++++ .../port/esp32s31/sleep_cpu_retention.h | 57 ++ .../lowpower/port/esp32s31/sleep_cpu_static.c | 146 +++++ .../lowpower/port/esp32s31/sleep_fpu_asm.S | 118 ++++ .../esp32s31/include/soc/Kconfig.soc_caps.in | 8 + .../soc/esp32s31/include/soc/soc_caps.h | 3 +- 9 files changed, 1447 insertions(+), 1 deletion(-) create mode 100644 components/esp_hw_support/lowpower/port/esp32s31/rvsleep-frames.h create mode 100644 components/esp_hw_support/lowpower/port/esp32s31/sleep_cpu.c create mode 100644 components/esp_hw_support/lowpower/port/esp32s31/sleep_cpu_asm.S create mode 100644 components/esp_hw_support/lowpower/port/esp32s31/sleep_cpu_dynamic.c create mode 100644 components/esp_hw_support/lowpower/port/esp32s31/sleep_cpu_retention.h create mode 100644 components/esp_hw_support/lowpower/port/esp32s31/sleep_cpu_static.c create mode 100644 components/esp_hw_support/lowpower/port/esp32s31/sleep_fpu_asm.S diff --git a/components/esp_hw_support/lowpower/port/esp32s31/rvsleep-frames.h b/components/esp_hw_support/lowpower/port/esp32s31/rvsleep-frames.h new file mode 100644 index 00000000000..cbf7257b02b --- /dev/null +++ b/components/esp_hw_support/lowpower/port/esp32s31/rvsleep-frames.h @@ -0,0 +1,225 @@ +/* + * SPDX-FileCopyrightText: 2026 Espressif Systems (Shanghai) CO LTD + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#ifndef __RVSLEEP_FRAMES_H__ +#define __RVSLEEP_FRAMES_H__ + +#include "sdkconfig.h" + +/* Align a value up to nearest n-byte boundary, where n is a power of 2. */ +#define ALIGNUP(n, val) (((val) + (n) - 1) & -(n)) + +#ifdef STRUCT_BEGIN +#undef STRUCT_BEGIN +#undef STRUCT_FIELD +#undef STRUCT_AFIELD +#undef STRUCT_END +#endif + +#if defined(_ASMLANGUAGE) || defined(__ASSEMBLER__) +#ifdef __clang__ +#define STRUCT_BEGIN .set RV_STRUCT_OFFSET, 0 +#define STRUCT_FIELD(ctype,size,asname,name) .set asname, RV_STRUCT_OFFSET; .set RV_STRUCT_OFFSET, asname + size +#define STRUCT_AFIELD(ctype,size,asname,name,n) .set asname, RV_STRUCT_OFFSET;\ + .set RV_STRUCT_OFFSET, asname + (size)*(n); +#define STRUCT_END(sname) .set sname##Size, RV_STRUCT_OFFSET; +#else // __clang__ +#define STRUCT_BEGIN .pushsection .text; .struct 0 +#define STRUCT_FIELD(ctype,size,asname,name) asname: .space size +#define STRUCT_AFIELD(ctype,size,asname,name,n) asname: .space (size)*(n) +#define STRUCT_END(sname) sname##Size:; .popsection +#endif // __clang__ +#else +#define STRUCT_BEGIN typedef struct { +#define STRUCT_FIELD(ctype,size,asname,name) ctype name; +#define STRUCT_AFIELD(ctype,size,asname,name,n) ctype name[n]; +#define STRUCT_END(sname) } sname; +#endif + +/* + * ------------------------------------------------------------------------------- + * RISC-V CORE CRITICAL REGISTER CONTEXT LAYOUT FOR SLEEP + * ------------------------------------------------------------------------------- + */ +STRUCT_BEGIN + STRUCT_FIELD (long, 4, RV_SLP_CTX_MEPC, mepc) /* Machine Exception Program Counter */ + STRUCT_FIELD (long, 4, RV_SLP_CTX_RA, ra) /* Return address */ + STRUCT_FIELD (long, 4, RV_SLP_CTX_SP, sp) /* Stack pointer */ + STRUCT_FIELD (long, 4, RV_SLP_CTX_GP, gp) /* Global pointer */ + STRUCT_FIELD (long, 4, RV_SLP_CTX_TP, tp) /* Thread pointer */ + STRUCT_FIELD (long, 4, RV_SLP_CTX_T0, t0) /* Temporary/alternate link register */ + STRUCT_FIELD (long, 4, RV_SLP_CTX_T1, t1) /* t1-2: Temporaries */ + STRUCT_FIELD (long, 4, RV_SLP_CTX_T2, t2) + STRUCT_FIELD (long, 4, RV_SLP_CTX_S0, s0) /* Saved register/frame pointer */ + STRUCT_FIELD (long, 4, RV_SLP_CTX_S1, s1) /* Saved register */ + STRUCT_FIELD (long, 4, RV_SLP_CTX_A0, a0) /* a0-1: Function arguments/return address */ + STRUCT_FIELD (long, 4, RV_SLP_CTX_A1, a1) + STRUCT_FIELD (long, 4, RV_SLP_CTX_A2, a2) /* a2-7: Function arguments */ + STRUCT_FIELD (long, 4, RV_SLP_CTX_A3, a3) + STRUCT_FIELD (long, 4, RV_SLP_CTX_A4, a4) + STRUCT_FIELD (long, 4, RV_SLP_CTX_A5, a5) + STRUCT_FIELD (long, 4, RV_SLP_CTX_A6, a6) + STRUCT_FIELD (long, 4, RV_SLP_CTX_A7, a7) + STRUCT_FIELD (long, 4, RV_SLP_CTX_S2, s2) /* s2-11: Saved registers */ + STRUCT_FIELD (long, 4, RV_SLP_CTX_S3, s3) + STRUCT_FIELD (long, 4, RV_SLP_CTX_S4, s4) + STRUCT_FIELD (long, 4, RV_SLP_CTX_S5, s5) + STRUCT_FIELD (long, 4, RV_SLP_CTX_S6, s6) + STRUCT_FIELD (long, 4, RV_SLP_CTX_S7, s7) + STRUCT_FIELD (long, 4, RV_SLP_CTX_S8, s8) + STRUCT_FIELD (long, 4, RV_SLP_CTX_S9, s9) + STRUCT_FIELD (long, 4, RV_SLP_CTX_S10, s10) + STRUCT_FIELD (long, 4, RV_SLP_CTX_S11, s11) + STRUCT_FIELD (long, 4, RV_SLP_CTX_T3, t3) /* t3-6: Temporaries */ + STRUCT_FIELD (long, 4, RV_SLP_CTX_T4, t4) + STRUCT_FIELD (long, 4, RV_SLP_CTX_T5, t5) + STRUCT_FIELD (long, 4, RV_SLP_CTX_T6, t6) + STRUCT_FIELD (long, 4, RV_SLP_CTX_MSTATUS, mstatus) /* Machine Status */ + STRUCT_FIELD (long, 4, RV_SLP_CTX_MTVEC, mtvec) /* Machine Trap-Vector Base Address */ + STRUCT_FIELD (long, 4, RV_SLP_CTX_MCAUSE, mcause) /* Machine Trap Cause */ + STRUCT_FIELD (long, 4, RV_SLP_CTX_MTVAL, mtval) /* Machine Trap Value */ + STRUCT_FIELD (long, 4, RV_SLP_CTX_MIE, mie) /* Machine intr enable */ + STRUCT_FIELD (long, 4, RV_SLP_CTX_MIP, mip) /* Machine intr pending */ + STRUCT_FIELD (long, 4, RV_SLP_CTX_MINTTHRESH, mintthresh) /* Machine intr threshold */ + + /* FPU context */ + STRUCT_FIELD (long, 4, RV_SLP_CTX_FPU_FT0, fpu_ft0) + STRUCT_FIELD (long, 4, RV_SLP_CTX_FPU_FT1, fpu_ft1) + STRUCT_FIELD (long, 4, RV_SLP_CTX_FPU_FT2, fpu_ft2) + STRUCT_FIELD (long, 4, RV_SLP_CTX_FPU_FT3, fpu_ft3) + STRUCT_FIELD (long, 4, RV_SLP_CTX_FPU_FT4, fpu_ft4) + STRUCT_FIELD (long, 4, RV_SLP_CTX_FPU_FT5, fpu_ft5) + STRUCT_FIELD (long, 4, RV_SLP_CTX_FPU_FT6, fpu_ft6) + STRUCT_FIELD (long, 4, RV_SLP_CTX_FPU_FT7, fpu_ft7) + STRUCT_FIELD (long, 4, RV_SLP_CTX_FPU_FS0, fpu_fs0) + STRUCT_FIELD (long, 4, RV_SLP_CTX_FPU_FS1, fpu_fs1) + STRUCT_FIELD (long, 4, RV_SLP_CTX_FPU_FA0, fpu_fa0) + STRUCT_FIELD (long, 4, RV_SLP_CTX_FPU_FA1, fpu_fa1) + STRUCT_FIELD (long, 4, RV_SLP_CTX_FPU_FA2, fpu_fa2) + STRUCT_FIELD (long, 4, RV_SLP_CTX_FPU_FA3, fpu_fa3) + STRUCT_FIELD (long, 4, RV_SLP_CTX_FPU_FA4, fpu_fa4) + STRUCT_FIELD (long, 4, RV_SLP_CTX_FPU_FA5, fpu_fa5) + STRUCT_FIELD (long, 4, RV_SLP_CTX_FPU_FA6, fpu_fa6) + STRUCT_FIELD (long, 4, RV_SLP_CTX_FPU_FA7, fpu_fa7) + STRUCT_FIELD (long, 4, RV_SLP_CTX_FPU_FS2, fpu_fs2) + STRUCT_FIELD (long, 4, RV_SLP_CTX_FPU_FS3, fpu_fs3) + STRUCT_FIELD (long, 4, RV_SLP_CTX_FPU_FS4, fpu_fs4) + STRUCT_FIELD (long, 4, RV_SLP_CTX_FPU_FS5, fpu_fs5) + STRUCT_FIELD (long, 4, RV_SLP_CTX_FPU_FS6, fpu_fs6) + STRUCT_FIELD (long, 4, RV_SLP_CTX_FPU_FS7, fpu_fs7) + STRUCT_FIELD (long, 4, RV_SLP_CTX_FPU_FS8, fpu_fs8) + STRUCT_FIELD (long, 4, RV_SLP_CTX_FPU_FS9, fpu_fs9) + STRUCT_FIELD (long, 4, RV_SLP_CTX_FPU_FS10, fpu_fs10) + STRUCT_FIELD (long, 4, RV_SLP_CTX_FPU_FS11, fpu_fs11) + STRUCT_FIELD (long, 4, RV_SLP_CTX_FPU_FT8, fpu_ft8) + STRUCT_FIELD (long, 4, RV_SLP_CTX_FPU_FT9, fpu_ft9) + STRUCT_FIELD (long, 4, RV_SLP_CTX_FPU_FT10, fpu_ft10) + STRUCT_FIELD (long, 4, RV_SLP_CTX_FPU_FT11, fpu_ft11) + STRUCT_FIELD (long, 4, RV_SLP_CTX_FPU_FCSR, fpu_fcsr) + STRUCT_FIELD (long, 4, RV_SLP_CTX_PMUFUNC, pmufunc) /* A field is used to identify whether it is going + * to sleep or has just been awakened. We use the + * lowest 2 bits as indication information, 3 means + * being awakened, 1 means going to sleep */ +#if CONFIG_PM_CHECK_SLEEP_RETENTION_FRAME + STRUCT_FIELD (long, 4, RV_SLP_CSF_CTX_CRC, frame_crc) /* Used to check RvCoreCriticalSleepFrame integrity */ +#endif +STRUCT_END(RvCoreCriticalSleepFrame) + +#if defined(_ASMLANGUAGE) || defined(__ASSEMBLER__) +#define RV_SLEEP_CTX_SZ1 RvCoreCriticalSleepFrameSize +#else +#define RV_SLEEP_CTX_SZ1 sizeof(RvCoreCriticalSleepFrame) +#endif + +/* + * Sleep stack frame size, after align up to 16 bytes boundary + */ +#define RV_SLEEP_CTX_FRMSZ (ALIGNUP(0x10, RV_SLEEP_CTX_SZ1)) + +/* + * ------------------------------------------------------------------------------- + * RISC-V CORE NON-CRITICAL REGISTER CONTEXT LAYOUT FOR SLEEP + * ------------------------------------------------------------------------------- + */ +STRUCT_BEGIN + STRUCT_FIELD (long, 4, RV_SLP_CTX_MSCRATCH, mscratch) + STRUCT_FIELD (long, 4, RV_SLP_CTX_MISA, misa) + STRUCT_FIELD (long, 4, RV_SLP_CTX_MTVT, mtvt) /* Machine mode vector interrupt base address register */ + STRUCT_FIELD (long, 4, RV_SLP_CTX_MSCRATCHCSW, mscratchcsw) /* Machine mode multi-mode data backup register */ + STRUCT_FIELD (long, 4, RV_SLP_CTX_MSCRATCHCSWL, mscratchcswl) /* Machine mode interrupt data backup register */ + STRUCT_FIELD (long, 4, RV_SLP_CTX_MXSTATUS, mxstatus) /* Extended status register */ + STRUCT_FIELD (long, 4, RV_SLP_CTX_MHCR, mhcr) /* Hardware control register */ + STRUCT_FIELD (long, 4, RV_SLP_CTX_MHINT, mhint) /* Implicit operation register */ + STRUCT_FIELD (long, 4, RV_SLP_CTX_MRADDR, mraddr) /* Processor reset start address indication register */ + STRUCT_FIELD (long, 4, RV_SLP_CTX_MEXSTATUS, mexstatus) /* Extended exception status register */ + STRUCT_FIELD (long, 4, RV_SLP_CTX_JVT, jvt) /* Table Jump base vector and control register */ + STRUCT_FIELD (long, 4, RV_SLP_CTX_MEDELEG, medeleg) /* Table Jump base vector and control register */ + STRUCT_FIELD (long, 4, RV_SLP_CTX_TSELECT, tselect) + STRUCT_FIELD (long, 4, RV_SLP_CTX_TDATA1, tdata1) + STRUCT_FIELD (long, 4, RV_SLP_CTX_TDATA2, tdata2) + STRUCT_FIELD (long, 4, RV_SLP_CTX_TCONTROL, tcontrol) + STRUCT_FIELD (long, 4, RV_SLP_CTX_PMPADDR0, pmpaddr0) + STRUCT_FIELD (long, 4, RV_SLP_CTX_PMPADDR1, pmpaddr1) + STRUCT_FIELD (long, 4, RV_SLP_CTX_PMPADDR2, pmpaddr2) + STRUCT_FIELD (long, 4, RV_SLP_CTX_PMPADDR3, pmpaddr3) + STRUCT_FIELD (long, 4, RV_SLP_CTX_PMPADDR4, pmpaddr4) + STRUCT_FIELD (long, 4, RV_SLP_CTX_PMPADDR5, pmpaddr5) + STRUCT_FIELD (long, 4, RV_SLP_CTX_PMPADDR6, pmpaddr6) + STRUCT_FIELD (long, 4, RV_SLP_CTX_PMPADDR7, pmpaddr7) + STRUCT_FIELD (long, 4, RV_SLP_CTX_PMPADDR8, pmpaddr8) + STRUCT_FIELD (long, 4, RV_SLP_CTX_PMPADDR9, pmpaddr9) + STRUCT_FIELD (long, 4, RV_SLP_CTX_PMPADDR10, pmpaddr10) + STRUCT_FIELD (long, 4, RV_SLP_CTX_PMPADDR11, pmpaddr11) + STRUCT_FIELD (long, 4, RV_SLP_CTX_PMPADDR12, pmpaddr12) + STRUCT_FIELD (long, 4, RV_SLP_CTX_PMPADDR13, pmpaddr13) + STRUCT_FIELD (long, 4, RV_SLP_CTX_PMPADDR14, pmpaddr14) + STRUCT_FIELD (long, 4, RV_SLP_CTX_PMPADDR15, pmpaddr15) + STRUCT_FIELD (long, 4, RV_SLP_CTX_PMPCFG0, pmpcfg0) + STRUCT_FIELD (long, 4, RV_SLP_CTX_PMPCFG1, pmpcfg1) + STRUCT_FIELD (long, 4, RV_SLP_CTX_PMPCFG2, pmpcfg2) + STRUCT_FIELD (long, 4, RV_SLP_CTX_PMPCFG3, pmpcfg3) + + STRUCT_FIELD (long, 4, RV_SLP_CTX_PMAADDR0, pmaaddr0) + STRUCT_FIELD (long, 4, RV_SLP_CTX_PMAADDR1, pmaaddr1) + STRUCT_FIELD (long, 4, RV_SLP_CTX_PMAADDR2, pmaaddr2) + STRUCT_FIELD (long, 4, RV_SLP_CTX_PMAADDR3, pmaaddr3) + STRUCT_FIELD (long, 4, RV_SLP_CTX_PMAADDR4, pmaaddr4) + STRUCT_FIELD (long, 4, RV_SLP_CTX_PMAADDR5, pmaaddr5) + STRUCT_FIELD (long, 4, RV_SLP_CTX_PMAADDR6, pmaaddr6) + STRUCT_FIELD (long, 4, RV_SLP_CTX_PMAADDR7, pmaaddr7) + STRUCT_FIELD (long, 4, RV_SLP_CTX_PMAADDR8, pmaaddr8) + STRUCT_FIELD (long, 4, RV_SLP_CTX_PMAADDR9, pmaaddr9) + STRUCT_FIELD (long, 4, RV_SLP_CTX_PMAADDR10, pmaaddr10) + STRUCT_FIELD (long, 4, RV_SLP_CTX_PMAADDR11, pmaaddr11) + STRUCT_FIELD (long, 4, RV_SLP_CTX_PMAADDR12, pmaaddr12) + STRUCT_FIELD (long, 4, RV_SLP_CTX_PMAADDR13, pmaaddr13) + STRUCT_FIELD (long, 4, RV_SLP_CTX_PMAADDR14, pmaaddr14) + STRUCT_FIELD (long, 4, RV_SLP_CTX_PMAADDR15, pmaaddr15) + STRUCT_FIELD (long, 4, RV_SLP_CTX_PMACFG0, pmacfg0) + STRUCT_FIELD (long, 4, RV_SLP_CTX_PMACFG1, pmacfg1) + STRUCT_FIELD (long, 4, RV_SLP_CTX_PMACFG2, pmacfg2) + STRUCT_FIELD (long, 4, RV_SLP_CTX_PMACFG3, pmacfg3) + STRUCT_FIELD (long, 4, RV_SLP_CTX_PMACFG4, pmacfg4) + STRUCT_FIELD (long, 4, RV_SLP_CTX_PMACFG5, pmacfg5) + STRUCT_FIELD (long, 4, RV_SLP_CTX_PMACFG6, pmacfg6) + STRUCT_FIELD (long, 4, RV_SLP_CTX_PMACFG7, pmacfg7) + STRUCT_FIELD (long, 4, RV_SLP_CTX_PMACFG8, pmacfg8) + STRUCT_FIELD (long, 4, RV_SLP_CTX_PMACFG9, pmacfg9) + STRUCT_FIELD (long, 4, RV_SLP_CTX_PMACFG10, pmacfg10) + STRUCT_FIELD (long, 4, RV_SLP_CTX_PMACFG11, pmacfg11) + STRUCT_FIELD (long, 4, RV_SLP_CTX_PMACFG12, pmacfg12) + STRUCT_FIELD (long, 4, RV_SLP_CTX_PMACFG13, pmacfg13) + STRUCT_FIELD (long, 4, RV_SLP_CTX_PMACFG14, pmacfg14) + STRUCT_FIELD (long, 4, RV_SLP_CTX_PMACFG15, pmacfg15) + + STRUCT_FIELD (long, 4, RV_SLP_CTX_MCYCLE, mcycle) + STRUCT_FIELD (long, 4, RV_SLP_CTX_MCYCLEH, mcycleh) +#if CONFIG_PM_CHECK_SLEEP_RETENTION_FRAME + STRUCT_FIELD (long, 4, RV_SLP_NCSF_CTX_CRC, frame_crc) /* Used to check RvCoreNonCriticalSleepFrame integrity */ +#endif +STRUCT_END(RvCoreNonCriticalSleepFrame) + +#endif /* #ifndef __RVSLEEP_FRAMES_H__ */ diff --git a/components/esp_hw_support/lowpower/port/esp32s31/sleep_cpu.c b/components/esp_hw_support/lowpower/port/esp32s31/sleep_cpu.c new file mode 100644 index 00000000000..495465573cf --- /dev/null +++ b/components/esp_hw_support/lowpower/port/esp32s31/sleep_cpu.c @@ -0,0 +1,525 @@ +/* + * SPDX-FileCopyrightText: 2026 Espressif Systems (Shanghai) CO LTD + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#include +#include +#include +#include +#include + +#include "esp_attr.h" +#include "esp_check.h" +#include "esp_ipc_isr.h" +#include "esp_sleep.h" +#include "esp_log.h" +#include "esp_rom_crc.h" +#include "freertos/FreeRTOS.h" +#include "freertos/task.h" +#include "freertos/portmacro.h" +#include "riscv/csr.h" +#include "soc/clic_reg.h" +#include "soc/rtc_periph.h" +#include "soc/soc_caps.h" +#include "soc/hp_sys_clkrst_reg.h" +#include "esp_private/sleep_cpu.h" +#include "esp_private/sleep_event.h" +#include "sdkconfig.h" +#include "esp_private/esp_pmu.h" + +#include "esp32s31/rom/ets_sys.h" +#include "esp32s31/rom/rtc.h" +#include "rvsleep-frames.h" +#include "sleep_cpu_retention.h" + +#if CONFIG_PM_CHECK_SLEEP_RETENTION_FRAME +#include "esp_private/system_internal.h" +#include "hal/uart_hal.h" +#endif + + +#if CONFIG_PM_ESP_SLEEP_POWER_DOWN_CPU && !CONFIG_FREERTOS_UNICORE +static DRAM_ATTR smp_retention_state_t s_smp_retention_state[portNUM_PROCESSORS]; +#endif + +static bool s_fpu_saved[portNUM_PROCESSORS]; + +ESP_LOG_ATTR_TAG(TAG, "sleep"); + +static DRAM_ATTR __attribute__((unused)) sleep_cpu_retention_t s_cpu_retention; + +extern RvCoreCriticalSleepFrame *rv_core_critical_regs_frame[portNUM_PROCESSORS]; + +FORCE_INLINE_ATTR uint32_t save_mstatus_and_disable_global_int(void) +{ + return RV_READ_MSTATUS_AND_DISABLE_INTR(); +} + +FORCE_INLINE_ATTR void restore_mstatus(uint32_t mstatus_val) +{ + RV_WRITE_CSR(mstatus, mstatus_val); +} + +#define CUSTOM_CSR_MTVT (0x307) +#define CUSTOM_CSR_MSCRATCHCSW (0x348) +#define CUSTOM_CSR_MSCRATCHCSWL (0x349) +#define CUSTOM_CSR_MXSTATUS (0x7c0) +#define CUSTOM_CSR_MHCR (0x7c1) +#define CUSTOM_CSR_MHINT (0x7c5) +#define CUSTOM_CSR_MRADDR (0x7e0) +#define CUSTOM_CSR_MEXSTATUS (0x7e1) +#define CUSTOM_CSR_JVT (0x017) +#define CUSTOM_CSR_UINTTHRESH (0x047) +#define CUSTOM_CSR_UINTSTATUS (0xCB1) + +static IRAM_ATTR RvCoreNonCriticalSleepFrame * rv_core_noncritical_regs_save(void) +{ + RvCoreNonCriticalSleepFrame *frame = s_cpu_retention.retent.non_critical_frame[esp_cpu_get_core_id()]; + + frame->mscratch = RV_READ_CSR(mscratch); + frame->misa = RV_READ_CSR(misa); + frame->mtvt = RV_READ_CSR(CUSTOM_CSR_MTVT); + frame->mscratchcsw = RV_READ_CSR(CUSTOM_CSR_MSCRATCHCSW); + frame->mscratchcswl = RV_READ_CSR(CUSTOM_CSR_MSCRATCHCSWL); + frame->mxstatus = RV_READ_CSR(CUSTOM_CSR_MXSTATUS); + frame->mhcr = RV_READ_CSR(CUSTOM_CSR_MHCR); + frame->mhint = RV_READ_CSR(CUSTOM_CSR_MHINT); + frame->mraddr = RV_READ_CSR(CUSTOM_CSR_MRADDR); + frame->mexstatus = RV_READ_CSR(CUSTOM_CSR_MEXSTATUS); + frame->jvt = RV_READ_CSR(CUSTOM_CSR_JVT); + frame->medeleg = RV_READ_CSR(medeleg); + frame->tselect = RV_READ_CSR(tselect); + frame->tdata1 = RV_READ_CSR(tdata1); + frame->tdata2 = RV_READ_CSR(tdata2); + frame->tcontrol = RV_READ_CSR(tcontrol); + + frame->pmpaddr0 = RV_READ_CSR(pmpaddr0); + frame->pmpaddr1 = RV_READ_CSR(pmpaddr1); + frame->pmpaddr2 = RV_READ_CSR(pmpaddr2); + frame->pmpaddr3 = RV_READ_CSR(pmpaddr3); + frame->pmpaddr4 = RV_READ_CSR(pmpaddr4); + frame->pmpaddr5 = RV_READ_CSR(pmpaddr5); + frame->pmpaddr6 = RV_READ_CSR(pmpaddr6); + frame->pmpaddr7 = RV_READ_CSR(pmpaddr7); + frame->pmpaddr8 = RV_READ_CSR(pmpaddr8); + frame->pmpaddr9 = RV_READ_CSR(pmpaddr9); + frame->pmpaddr10 = RV_READ_CSR(pmpaddr10); + frame->pmpaddr11 = RV_READ_CSR(pmpaddr11); + frame->pmpaddr12 = RV_READ_CSR(pmpaddr12); + frame->pmpaddr13 = RV_READ_CSR(pmpaddr13); + frame->pmpaddr14 = RV_READ_CSR(pmpaddr14); + frame->pmpaddr15 = RV_READ_CSR(pmpaddr15); + frame->pmpcfg0 = RV_READ_CSR(pmpcfg0); + frame->pmpcfg1 = RV_READ_CSR(pmpcfg1); + frame->pmpcfg2 = RV_READ_CSR(pmpcfg2); + frame->pmpcfg3 = RV_READ_CSR(pmpcfg3); + + frame->pmaaddr0 = RV_READ_CSR(CSR_PMAADDR(0)); + frame->pmaaddr1 = RV_READ_CSR(CSR_PMAADDR(1)); + frame->pmaaddr2 = RV_READ_CSR(CSR_PMAADDR(2)); + frame->pmaaddr3 = RV_READ_CSR(CSR_PMAADDR(3)); + frame->pmaaddr4 = RV_READ_CSR(CSR_PMAADDR(4)); + frame->pmaaddr5 = RV_READ_CSR(CSR_PMAADDR(5)); + frame->pmaaddr6 = RV_READ_CSR(CSR_PMAADDR(6)); + frame->pmaaddr7 = RV_READ_CSR(CSR_PMAADDR(7)); + frame->pmaaddr8 = RV_READ_CSR(CSR_PMAADDR(8)); + frame->pmaaddr9 = RV_READ_CSR(CSR_PMAADDR(9)); + frame->pmaaddr10 = RV_READ_CSR(CSR_PMAADDR(10)); + frame->pmaaddr11 = RV_READ_CSR(CSR_PMAADDR(11)); + frame->pmaaddr12 = RV_READ_CSR(CSR_PMAADDR(12)); + frame->pmaaddr13 = RV_READ_CSR(CSR_PMAADDR(13)); + frame->pmaaddr14 = RV_READ_CSR(CSR_PMAADDR(14)); + frame->pmaaddr15 = RV_READ_CSR(CSR_PMAADDR(15)); + frame->pmacfg0 = RV_READ_CSR(CSR_PMACFG(0)); + frame->pmacfg1 = RV_READ_CSR(CSR_PMACFG(1)); + frame->pmacfg2 = RV_READ_CSR(CSR_PMACFG(2)); + frame->pmacfg3 = RV_READ_CSR(CSR_PMACFG(3)); + frame->pmacfg4 = RV_READ_CSR(CSR_PMACFG(4)); + frame->pmacfg5 = RV_READ_CSR(CSR_PMACFG(5)); + frame->pmacfg6 = RV_READ_CSR(CSR_PMACFG(6)); + frame->pmacfg7 = RV_READ_CSR(CSR_PMACFG(7)); + frame->pmacfg8 = RV_READ_CSR(CSR_PMACFG(8)); + frame->pmacfg9 = RV_READ_CSR(CSR_PMACFG(9)); + frame->pmacfg10 = RV_READ_CSR(CSR_PMACFG(10)); + frame->pmacfg11 = RV_READ_CSR(CSR_PMACFG(11)); + frame->pmacfg12 = RV_READ_CSR(CSR_PMACFG(12)); + frame->pmacfg13 = RV_READ_CSR(CSR_PMACFG(13)); + frame->pmacfg14 = RV_READ_CSR(CSR_PMACFG(14)); + frame->pmacfg15 = RV_READ_CSR(CSR_PMACFG(15)); + + frame->mcycle = RV_READ_CSR(mcycle); + frame->mcycleh = RV_READ_CSR(mcycleh); + return frame; +} + +static IRAM_ATTR void rv_core_noncritical_regs_restore(void) +{ + RvCoreNonCriticalSleepFrame *frame = s_cpu_retention.retent.non_critical_frame[esp_cpu_get_core_id()]; + + RV_WRITE_CSR(CSR_PMAADDR(0), frame->pmaaddr0); + RV_WRITE_CSR(CSR_PMAADDR(1), frame->pmaaddr1); + RV_WRITE_CSR(CSR_PMAADDR(2), frame->pmaaddr2); + RV_WRITE_CSR(CSR_PMAADDR(3), frame->pmaaddr3); + RV_WRITE_CSR(CSR_PMAADDR(4), frame->pmaaddr4); + RV_WRITE_CSR(CSR_PMAADDR(5), frame->pmaaddr5); + RV_WRITE_CSR(CSR_PMAADDR(6), frame->pmaaddr6); + RV_WRITE_CSR(CSR_PMAADDR(7), frame->pmaaddr7); + RV_WRITE_CSR(CSR_PMAADDR(8), frame->pmaaddr8); + RV_WRITE_CSR(CSR_PMAADDR(9), frame->pmaaddr9); + RV_WRITE_CSR(CSR_PMAADDR(10),frame->pmaaddr10); + RV_WRITE_CSR(CSR_PMAADDR(11),frame->pmaaddr11); + RV_WRITE_CSR(CSR_PMAADDR(12),frame->pmaaddr12); + RV_WRITE_CSR(CSR_PMAADDR(13),frame->pmaaddr13); + RV_WRITE_CSR(CSR_PMAADDR(14),frame->pmaaddr14); + RV_WRITE_CSR(CSR_PMAADDR(15),frame->pmaaddr15); + RV_WRITE_CSR(CSR_PMACFG(0), frame->pmacfg0); + RV_WRITE_CSR(CSR_PMACFG(1), frame->pmacfg1); + RV_WRITE_CSR(CSR_PMACFG(2), frame->pmacfg2); + RV_WRITE_CSR(CSR_PMACFG(3), frame->pmacfg3); + RV_WRITE_CSR(CSR_PMACFG(4), frame->pmacfg4); + RV_WRITE_CSR(CSR_PMACFG(5), frame->pmacfg5); + RV_WRITE_CSR(CSR_PMACFG(6), frame->pmacfg6); + RV_WRITE_CSR(CSR_PMACFG(7), frame->pmacfg7); + RV_WRITE_CSR(CSR_PMACFG(8), frame->pmacfg8); + RV_WRITE_CSR(CSR_PMACFG(9), frame->pmacfg9); + RV_WRITE_CSR(CSR_PMACFG(10), frame->pmacfg10); + RV_WRITE_CSR(CSR_PMACFG(11), frame->pmacfg11); + RV_WRITE_CSR(CSR_PMACFG(12), frame->pmacfg12); + RV_WRITE_CSR(CSR_PMACFG(13), frame->pmacfg13); + RV_WRITE_CSR(CSR_PMACFG(14), frame->pmacfg14); + RV_WRITE_CSR(CSR_PMACFG(15), frame->pmacfg15); + + RV_WRITE_CSR(mscratch, frame->mscratch); + RV_WRITE_CSR(misa, frame->misa); + RV_WRITE_CSR(CUSTOM_CSR_MTVT, frame->mtvt); + RV_WRITE_CSR(CUSTOM_CSR_MSCRATCHCSW, frame->mscratchcsw); + RV_WRITE_CSR(CUSTOM_CSR_MSCRATCHCSWL, frame->mscratchcswl); + RV_WRITE_CSR(CUSTOM_CSR_MXSTATUS, frame->mxstatus); + RV_WRITE_CSR(CUSTOM_CSR_MHCR, frame->mhcr); + RV_WRITE_CSR(CUSTOM_CSR_MHINT, frame->mhint); + RV_WRITE_CSR(CUSTOM_CSR_MRADDR, frame->mraddr); + RV_WRITE_CSR(CUSTOM_CSR_MEXSTATUS, frame->mexstatus); + RV_WRITE_CSR(CUSTOM_CSR_JVT, frame->jvt); + RV_WRITE_CSR(medeleg, frame->medeleg); + RV_WRITE_CSR(tselect, frame->tselect); + RV_WRITE_CSR(tdata1, frame->tdata1); + RV_WRITE_CSR(tdata2, frame->tdata2); + RV_WRITE_CSR(tcontrol, frame->tcontrol); + + RV_WRITE_CSR(pmpaddr0, frame->pmpaddr0); + RV_WRITE_CSR(pmpaddr1, frame->pmpaddr1); + RV_WRITE_CSR(pmpaddr2, frame->pmpaddr2); + RV_WRITE_CSR(pmpaddr3, frame->pmpaddr3); + RV_WRITE_CSR(pmpaddr4, frame->pmpaddr4); + RV_WRITE_CSR(pmpaddr5, frame->pmpaddr5); + RV_WRITE_CSR(pmpaddr6, frame->pmpaddr6); + RV_WRITE_CSR(pmpaddr7, frame->pmpaddr7); + RV_WRITE_CSR(pmpaddr8, frame->pmpaddr8); + RV_WRITE_CSR(pmpaddr9, frame->pmpaddr9); + RV_WRITE_CSR(pmpaddr10, frame->pmpaddr10); + RV_WRITE_CSR(pmpaddr11, frame->pmpaddr11); + RV_WRITE_CSR(pmpaddr12, frame->pmpaddr12); + RV_WRITE_CSR(pmpaddr13, frame->pmpaddr13); + RV_WRITE_CSR(pmpaddr14, frame->pmpaddr14); + RV_WRITE_CSR(pmpaddr15, frame->pmpaddr15); + RV_WRITE_CSR(pmpcfg0, frame->pmpcfg0); + RV_WRITE_CSR(pmpcfg1, frame->pmpcfg1); + RV_WRITE_CSR(pmpcfg2, frame->pmpcfg2); + RV_WRITE_CSR(pmpcfg3, frame->pmpcfg3); + + RV_WRITE_CSR(mcycle, frame->mcycle); + RV_WRITE_CSR(mcycleh, frame->mcycleh); +} + +static IRAM_ATTR void cpu_domain_dev_regs_save(cpu_domain_dev_sleep_frame_t *frame) +{ + assert(frame); + cpu_domain_dev_regs_region_t *region = frame->region; + uint32_t *regs_frame = frame->regs_frame; + + int offset = 0; + for (int i = 0; i < frame->region_num; i++) { + for (uint32_t addr = region[i].start; addr < region[i].end; addr+=4) { + regs_frame[offset++] = *(uint32_t *)addr; + } + } +} + +static IRAM_ATTR void cpu_domain_dev_regs_restore(cpu_domain_dev_sleep_frame_t *frame) +{ + assert(frame); + cpu_domain_dev_regs_region_t *region = frame->region; + uint32_t *regs_frame = frame->regs_frame; + + int offset = 0; + for (int i = 0; i < frame->region_num; i++) { + for (uint32_t addr = region[i].start; addr < region[i].end; addr+=4) { + *(uint32_t *)addr = regs_frame[offset++]; + } + } +} + +#if CONFIG_PM_CHECK_SLEEP_RETENTION_FRAME +static IRAM_ATTR void update_retention_frame_crc(uint32_t *frame_ptr, uint32_t frame_check_size, uint32_t *frame_crc_ptr) +{ + *(frame_crc_ptr) = esp_rom_crc32_le(0, (void *)frame_ptr, frame_check_size); +} + +static IRAM_ATTR void validate_retention_frame_crc(uint32_t *frame_ptr, uint32_t frame_check_size, uint32_t *frame_crc_ptr) +{ + if(*(frame_crc_ptr) != esp_rom_crc32_le(0, (void *)(frame_ptr), frame_check_size)){ + // resume uarts + for (int i = 0; i < SOC_UART_NUM; ++i) { + if (!uart_ll_is_enabled(i)) { + continue; + } + uart_ll_force_xon(i); + } + + /* Since it is still in the critical now, use ESP_EARLY_LOG */ + ESP_EARLY_LOGE(TAG, "Sleep retention frame is corrupted"); + esp_restart_noos(); + } +} +#endif + +extern RvCoreCriticalSleepFrame * rv_core_critical_regs_save(void); +extern RvCoreCriticalSleepFrame * rv_core_critical_regs_restore(void); +extern void rv_core_fpu_save(RvCoreCriticalSleepFrame *frame); +extern void rv_core_fpu_restore(RvCoreCriticalSleepFrame *frame); +typedef uint32_t (* sleep_cpu_entry_cb_t)(uint32_t, uint32_t, uint32_t, bool); + +static IRAM_ATTR esp_err_t do_cpu_retention(sleep_cpu_entry_cb_t goto_sleep, + uint32_t wakeup_opt, uint32_t reject_opt, uint32_t lslp_mem_inf_fpu, bool dslp) +{ + uint8_t core_id = esp_cpu_get_core_id(); + bool reject = false; + RvCoreCriticalSleepFrame *frame = s_cpu_retention.retent.critical_frame[core_id]; + /* mstatus is core privated CSR, do it near the core critical regs restore */ + uint32_t mstatus = save_mstatus_and_disable_global_int(); + s_fpu_saved[core_id] = xPortFPUContextIsDirty(core_id); + if (s_fpu_saved[core_id]) { + rv_core_fpu_save(frame); + } + rv_core_critical_regs_save(); + if ((frame->pmufunc & 0x3) == 0x1) { + esp_sleep_execute_event_callbacks(SLEEP_EVENT_SW_CPU_TO_MEM_END, (void *)0); +#if CONFIG_PM_CHECK_SLEEP_RETENTION_FRAME + /* Minus 2 * sizeof(long) is for bypass `pmufunc` and `frame_crc` field */ + update_retention_frame_crc((uint32_t*)frame, RV_SLEEP_CTX_SZ1 - 2 * sizeof(long), (uint32_t *)(&frame->frame_crc)); +#endif + REG_WRITE(RTC_SLEEP_WAKE_STUB_ADDR_REG, (uint32_t)rv_core_critical_regs_restore); + +#if CONFIG_PM_ESP_SLEEP_POWER_DOWN_CPU && !CONFIG_FREERTOS_UNICORE + atomic_store(&s_smp_retention_state[core_id], SMP_BACKUP_DONE); + while (atomic_load(&s_smp_retention_state[!core_id]) != SMP_BACKUP_DONE) { + ; + } +#endif + + reject = (*goto_sleep)(wakeup_opt, reject_opt, lslp_mem_inf_fpu, dslp); + } +#if CONFIG_PM_CHECK_SLEEP_RETENTION_FRAME + else { + validate_retention_frame_crc((uint32_t*)frame, RV_SLEEP_CTX_SZ1 - 2 * sizeof(long), (uint32_t *)(&frame->frame_crc)); + } +#endif + if (s_fpu_saved[core_id]) { + rv_core_fpu_restore(frame); + } + restore_mstatus(mstatus); + return reject ? reject : pmu_sleep_finish(dslp); +} + +esp_err_t IRAM_ATTR esp_sleep_cpu_retention(uint32_t (*goto_sleep)(uint32_t, uint32_t, uint32_t, bool), + uint32_t wakeup_opt, uint32_t reject_opt, uint32_t lslp_mem_inf_fpu, bool dslp) +{ + esp_sleep_execute_event_callbacks(SLEEP_EVENT_SW_CPU_TO_MEM_START, (void *)0); + uint8_t core_id = esp_cpu_get_core_id(); +#if CONFIG_PM_ESP_SLEEP_POWER_DOWN_CPU && !CONFIG_FREERTOS_UNICORE + atomic_store(&s_smp_retention_state[core_id], SMP_BACKUP_START); +#endif + cpu_domain_dev_regs_save(s_cpu_retention.retent.clic_frame[core_id]); + rv_core_noncritical_regs_save(); + +#if CONFIG_PM_CHECK_SLEEP_RETENTION_FRAME + RvCoreNonCriticalSleepFrame *frame = s_cpu_retention.retent.non_critical_frame[core_id]; + /* Minus sizeof(long) is for bypass `frame_crc` field */ + update_retention_frame_crc((uint32_t*)frame, sizeof(RvCoreNonCriticalSleepFrame) - sizeof(long), (uint32_t *)(&frame->frame_crc)); +#endif + + esp_err_t err = do_cpu_retention(goto_sleep, wakeup_opt, reject_opt, lslp_mem_inf_fpu, dslp); + + if (err == ESP_OK) { +#if CONFIG_PM_CHECK_SLEEP_RETENTION_FRAME + validate_retention_frame_crc((uint32_t*)frame, sizeof(RvCoreNonCriticalSleepFrame) - sizeof(long), (uint32_t *)(&frame->frame_crc)); +#endif + } +#if CONFIG_PM_ESP_SLEEP_POWER_DOWN_CPU && !CONFIG_FREERTOS_UNICORE + // Start core1 + if (core_id == 0) { + REG_SET_BIT(HP_SYS_CLKRST_HPCORE1_CTRL0_REG, HP_SYS_CLKRST_REG_CORE1_CPU_CLK_EN); + REG_CLR_BIT(HP_SYS_CLKRST_HPCORE1_CTRL0_REG, HP_SYS_CLKRST_REG_CORE1_GLOBAL_RST_EN); + } + + atomic_store(&s_smp_retention_state[core_id], SMP_RESTORE_START); +#endif + if (err == ESP_OK) { + cpu_domain_dev_regs_restore(s_cpu_retention.retent.clic_frame[core_id]); + rv_core_noncritical_regs_restore(); + } + +#if CONFIG_PM_ESP_SLEEP_POWER_DOWN_CPU && !CONFIG_FREERTOS_UNICORE + atomic_store(&s_smp_retention_state[core_id], SMP_RESTORE_DONE); +#endif + return err; +} + +esp_err_t esp_sleep_cpu_retention_init(void) +{ +#if CONFIG_PM_ESP_SLEEP_POWER_DOWN_CPU && !CONFIG_FREERTOS_UNICORE + return esp_sleep_cpu_retention_init_impl(& s_cpu_retention, s_smp_retention_state); +#else + return esp_sleep_cpu_retention_init_impl(& s_cpu_retention); +#endif +} + +esp_err_t esp_sleep_cpu_retention_deinit(void) +{ + return esp_sleep_cpu_retention_deinit_impl(& s_cpu_retention); +} + +bool cpu_domain_pd_allowed(void) +{ + bool allowed = true; + for (uint8_t core_id = 0; core_id < portNUM_PROCESSORS; ++core_id) { + allowed &= (s_cpu_retention.retent.critical_frame[core_id] != NULL); + allowed &= (s_cpu_retention.retent.non_critical_frame[core_id] != NULL); + } + for (uint8_t core_id = 0; core_id < portNUM_PROCESSORS; ++core_id) { + allowed &= (s_cpu_retention.retent.clic_frame[core_id] != NULL); + } + return allowed; +} + +esp_err_t sleep_cpu_configure(bool light_sleep_enable) +{ +#if CONFIG_PM_ESP_SLEEP_POWER_DOWN_CPU + if (light_sleep_enable) { + ESP_RETURN_ON_ERROR(esp_sleep_cpu_retention_init(), TAG, "Failed to enable CPU power down during light sleep."); + } else { + ESP_RETURN_ON_ERROR(esp_sleep_cpu_retention_deinit(), TAG, "Failed to release CPU retention memory"); + } +#endif + return ESP_OK; +} + +#if !CONFIG_FREERTOS_UNICORE +#if CONFIG_PM_ESP_SLEEP_POWER_DOWN_CPU +static IRAM_ATTR void smp_core_do_retention(void) +{ + uint8_t core_id = esp_cpu_get_core_id(); + + if (core_id == 0) { + WRITE_PERI_REG(HP_SYSTEM_CPU_INT_FROM_CPU_2_REG, 0); + } else { + WRITE_PERI_REG(HP_SYSTEM_CPU_INT_FROM_CPU_3_REG, 0); + } + + // Wait another core start to do retention + bool smp_skip_retention = false; + smp_retention_state_t another_core_state; + ESP_COMPILER_DIAGNOSTIC_PUSH_IGNORE("-Wanalyzer-infinite-loop") + while (1) { + another_core_state = atomic_load(&s_smp_retention_state[!core_id]); + if (another_core_state == SMP_SKIP_RETENTION) { + // If another core skips the retention, the current core should also have to skip it. + smp_skip_retention = true; + break; + } else if (another_core_state == SMP_BACKUP_START) { + break; + } + } + ESP_COMPILER_DIAGNOSTIC_POP("-Wanalyzer-infinite-loop") + + if (!smp_skip_retention) { + atomic_store(&s_smp_retention_state[core_id], SMP_BACKUP_START); + rv_core_noncritical_regs_save(); + cpu_domain_dev_regs_save(s_cpu_retention.retent.clic_frame[core_id]); + RvCoreCriticalSleepFrame *frame_critical = s_cpu_retention.retent.critical_frame[core_id]; + uint32_t mstatus = save_mstatus_and_disable_global_int(); + s_fpu_saved[core_id] = xPortFPUContextIsDirty(core_id); + if (s_fpu_saved[core_id]) { + rv_core_fpu_save(frame_critical); + } + rv_core_critical_regs_save(); + if ((frame_critical->pmufunc & 0x3) == 0x1) { + atomic_store(&s_smp_retention_state[core_id], SMP_BACKUP_DONE); + // wait another core trigger sleep and wakeup + while (1) { + // If another core's sleep request is rejected by the hardware, jumps out of blocking. + another_core_state = atomic_load(&s_smp_retention_state[!core_id]); + if (another_core_state == SMP_SKIP_RETENTION) { + break; + } + } + } else { + // Start core1 + if (core_id == 0) { + REG_SET_BIT(HP_SYS_CLKRST_HPCORE1_CTRL0_REG, HP_SYS_CLKRST_REG_CORE1_CPU_CLK_EN); + REG_CLR_BIT(HP_SYS_CLKRST_HPCORE1_CTRL0_REG, HP_SYS_CLKRST_REG_CORE1_GLOBAL_RST_EN); + } + atomic_store(&s_smp_retention_state[core_id], SMP_RESTORE_START); + if (s_fpu_saved[core_id]) { + rv_core_fpu_restore(frame_critical); + } + restore_mstatus(mstatus); + cpu_domain_dev_regs_restore(s_cpu_retention.retent.clic_frame[core_id]); + rv_core_noncritical_regs_restore(); + atomic_store(&s_smp_retention_state[core_id], SMP_RESTORE_DONE); + } + } + // wait another core out sleep + while (atomic_load(&s_smp_retention_state[!core_id]) != SMP_IDLE) { + ; + } + atomic_store(&s_smp_retention_state[core_id], SMP_IDLE); +} + + +IRAM_ATTR void esp_sleep_cpu_skip_retention(void) { + atomic_store(&s_smp_retention_state[esp_cpu_get_core_id()], SMP_SKIP_RETENTION); +} +#endif + +void sleep_smp_cpu_sleep_prepare(void) +{ +#if CONFIG_PM_ESP_SLEEP_POWER_DOWN_CPU + while (atomic_load(&s_smp_retention_state[!esp_cpu_get_core_id()]) != SMP_IDLE) { + ; + } + esp_ipc_isr_call((esp_ipc_isr_func_t)smp_core_do_retention, NULL); +#else + esp_ipc_isr_stall_other_cpu(); +#endif +} + +void sleep_smp_cpu_wakeup_prepare(void) +{ +#if CONFIG_PM_ESP_SLEEP_POWER_DOWN_CPU + uint8_t core_id = esp_cpu_get_core_id(); + if (atomic_load(&s_smp_retention_state[core_id]) == SMP_RESTORE_DONE) { + ESP_COMPILER_DIAGNOSTIC_PUSH_IGNORE("-Wanalyzer-infinite-loop") + while (atomic_load(&s_smp_retention_state[!core_id]) != SMP_RESTORE_DONE) { + ; + } + ESP_COMPILER_DIAGNOSTIC_POP("-Wanalyzer-infinite-loop") + } + atomic_store(&s_smp_retention_state[core_id], SMP_IDLE); +#else + esp_ipc_isr_release_other_cpu(); +#endif +} +#endif //!CONFIG_FREERTOS_UNICORE diff --git a/components/esp_hw_support/lowpower/port/esp32s31/sleep_cpu_asm.S b/components/esp_hw_support/lowpower/port/esp32s31/sleep_cpu_asm.S new file mode 100644 index 00000000000..d40d61364de --- /dev/null +++ b/components/esp_hw_support/lowpower/port/esp32s31/sleep_cpu_asm.S @@ -0,0 +1,219 @@ +/* + * SPDX-FileCopyrightText: 2026 Espressif Systems (Shanghai) CO LTD + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#include "soc/soc.h" +#include "rvsleep-frames.h" +#include "freertos/FreeRTOSConfig.h" +#include "sdkconfig.h" +#include "riscv/csr_clic.h" + + .section .data1,"aw" + .global rv_core_critical_regs_frame + .type rv_core_critical_regs_frame,@object + .align 4 +rv_core_critical_regs_frame: + .rept (portNUM_PROCESSORS) + .word 0 + .endr + +/* +-------------------------------------------------------------------------------- + This assembly subroutine is used to save the critical registers of the CPU + core to the internal RAM before sleep, and modify the PMU control flag to + indicate that the system needs to sleep. When the subroutine returns, it + will return the memory pointer that saves the context information of the CPU + critical registers. +-------------------------------------------------------------------------------- +*/ + + .section .iram1,"ax" + .global rv_core_critical_regs_save + .type rv_core_critical_regs_save,@function + .align 4 + +rv_core_critical_regs_save: + + /* arrived here in critical section. we need: + save riscv core critical registers to RvCoreCriticalSleepFrame + */ + csrw mscratch, t0 /* use mscratch as temp storage */ + la a0, rv_core_critical_regs_frame + csrr t1, mhartid + slli t1, t1, 2 + add a0, a0, t1 + lw t0, 0(a0) /* t0 pointer to RvCoreCriticalSleepFrame object */ + + sw ra, RV_SLP_CTX_RA(t0) + sw sp, RV_SLP_CTX_SP(t0) + sw gp, RV_SLP_CTX_GP(t0) + sw tp, RV_SLP_CTX_TP(t0) + sw t1, RV_SLP_CTX_T1(t0) + sw t2, RV_SLP_CTX_T2(t0) + sw s0, RV_SLP_CTX_S0(t0) + sw s1, RV_SLP_CTX_S1(t0) + + /* a0 is caller saved, so it does not need to be saved, but it should be the + pointer value of RvCoreCriticalSleepFrame for return. + */ + mv a0, t0 + sw a0, RV_SLP_CTX_A0(t0) + sw a1, RV_SLP_CTX_A1(t0) + sw a2, RV_SLP_CTX_A2(t0) + sw a3, RV_SLP_CTX_A3(t0) + sw a4, RV_SLP_CTX_A4(t0) + sw a5, RV_SLP_CTX_A5(t0) + sw a6, RV_SLP_CTX_A6(t0) + sw a7, RV_SLP_CTX_A7(t0) + sw s2, RV_SLP_CTX_S2(t0) + sw s3, RV_SLP_CTX_S3(t0) + sw s4, RV_SLP_CTX_S4(t0) + sw s5, RV_SLP_CTX_S5(t0) + sw s6, RV_SLP_CTX_S6(t0) + sw s7, RV_SLP_CTX_S7(t0) + sw s8, RV_SLP_CTX_S8(t0) + sw s9, RV_SLP_CTX_S9(t0) + sw s10, RV_SLP_CTX_S10(t0) + sw s11, RV_SLP_CTX_S11(t0) + sw t3, RV_SLP_CTX_T3(t0) + sw t4, RV_SLP_CTX_T4(t0) + sw t5, RV_SLP_CTX_T5(t0) + sw t6, RV_SLP_CTX_T6(t0) + + csrr t1, mstatus + sw t1, RV_SLP_CTX_MSTATUS(t0) + csrr t2, mtvec + sw t2, RV_SLP_CTX_MTVEC(t0) + csrr t3, mcause + sw t3, RV_SLP_CTX_MCAUSE(t0) + csrr t1, mtval + sw t1, RV_SLP_CTX_MTVAL(t0) + csrr t2, mie + sw t2, RV_SLP_CTX_MIE(t0) + csrr t3, mip + sw t3, RV_SLP_CTX_MIP(t0) + csrr t1, mepc + sw t1, RV_SLP_CTX_MEPC(t0) + csrr t2, MINTTHRESH_CSR + sw t2, RV_SLP_CTX_MINTTHRESH(t0) + + /* + !!! Let idf knows it's going to sleep !!! + + RV_SLP_STK_PMUFUNC field is used to identify whether it is going to sleep or + has just been awakened. We use the lowest 2 bits as indication information, + 3 means being awakened, 1 means going to sleep. + */ + li t1, ~0x3 + lw t2, RV_SLP_CTX_PMUFUNC(t0) + and t2, t1, t2 + ori t2, t2, 0x1 + sw t2, RV_SLP_CTX_PMUFUNC(t0) + + mv t3, t0 + csrr t0, mscratch + sw t0, RV_SLP_CTX_T0(t3) + lw t1, RV_SLP_CTX_T1(t3) + lw t2, RV_SLP_CTX_T2(t3) + lw t3, RV_SLP_CTX_T3(t3) + + ret + + .size rv_core_critical_regs_save, . - rv_core_critical_regs_save + +/* +-------------------------------------------------------------------------------- + This assembly subroutine is used to restore the CPU core critical register + context before sleep after system wakes up, modify the PMU control + information, and return the critical register context memory object pointer. + After the subroutine returns, continue to restore other modules of the + system. +-------------------------------------------------------------------------------- +*/ + + .section .iram1,"ax" + .global rv_core_critical_regs_restore + .weak rv_core_critical_regs_restore + .type rv_core_critical_regs_restore,@function + .global _rv_core_critical_regs_restore + .type _rv_core_critical_regs_restore,@function + .align 4 + +_rv_core_critical_regs_restore: /* export a strong symbol to jump to here, used + * for a static callback */ + nop + +rv_core_critical_regs_restore: + la t0, rv_core_critical_regs_frame + csrr t1, mhartid + slli t1, t1, 2 + add t0, t0, t1 + lw t0, 0(t0) /* t0 pointer to RvCoreCriticalSleepFrame object */ + beqz t0, .skip_restore /* make sure we do not jump to zero address */ + + /* + !!! Let idf knows it's sleep awake. !!! + + RV_SLP_STK_PMUFUNC field is used to identify whether it is going to sleep or + has just been awakened. We use the lowest 2 bits as indication information, + 3 means being awakened, 1 means going to sleep. + */ + lw t1, RV_SLP_CTX_PMUFUNC(t0) + ori t1, t1, 0x3 + sw t1, RV_SLP_CTX_PMUFUNC(t0) + + lw t2, RV_SLP_CTX_MINTTHRESH(t0) + csrw MINTTHRESH_CSR, t2 + lw t2, RV_SLP_CTX_MEPC(t0) + csrw mepc, t2 + lw t3, RV_SLP_CTX_MIP(t0) + csrw mip, t3 + lw t1, RV_SLP_CTX_MIE(t0) + csrw mie, t1 + lw t2, RV_SLP_CTX_MSTATUS(t0) + csrw mstatus, t2 + lw t3, RV_SLP_CTX_MTVEC(t0) + csrw mtvec, t3 + lw t1, RV_SLP_CTX_MCAUSE(t0) + csrw mcause, t1 + lw t2, RV_SLP_CTX_MTVAL(t0) + csrw mtval, t2 + + lw t6, RV_SLP_CTX_T6(t0) + lw t5, RV_SLP_CTX_T5(t0) + lw t4, RV_SLP_CTX_T4(t0) + lw t3, RV_SLP_CTX_T3(t0) + lw s11, RV_SLP_CTX_S11(t0) + lw s10, RV_SLP_CTX_S10(t0) + lw s9, RV_SLP_CTX_S9(t0) + lw s8, RV_SLP_CTX_S8(t0) + lw s7, RV_SLP_CTX_S7(t0) + lw s6, RV_SLP_CTX_S6(t0) + lw s5, RV_SLP_CTX_S5(t0) + lw s4, RV_SLP_CTX_S4(t0) + lw s3, RV_SLP_CTX_S3(t0) + lw s2, RV_SLP_CTX_S2(t0) + lw a7, RV_SLP_CTX_A7(t0) + lw a6, RV_SLP_CTX_A6(t0) + lw a5, RV_SLP_CTX_A5(t0) + lw a4, RV_SLP_CTX_A4(t0) + lw a3, RV_SLP_CTX_A3(t0) + lw a2, RV_SLP_CTX_A2(t0) + lw a1, RV_SLP_CTX_A1(t0) + lw a0, RV_SLP_CTX_A0(t0) + lw s1, RV_SLP_CTX_S1(t0) + lw s0, RV_SLP_CTX_S0(t0) + lw t2, RV_SLP_CTX_T2(t0) + lw t1, RV_SLP_CTX_T1(t0) + lw tp, RV_SLP_CTX_TP(t0) + lw gp, RV_SLP_CTX_GP(t0) + lw sp, RV_SLP_CTX_SP(t0) + lw ra, RV_SLP_CTX_RA(t0) + lw t0, RV_SLP_CTX_T0(t0) + +.skip_restore: + ret + + .size rv_core_critical_regs_restore, . - rv_core_critical_regs_restore diff --git a/components/esp_hw_support/lowpower/port/esp32s31/sleep_cpu_dynamic.c b/components/esp_hw_support/lowpower/port/esp32s31/sleep_cpu_dynamic.c new file mode 100644 index 00000000000..87b4cfae52c --- /dev/null +++ b/components/esp_hw_support/lowpower/port/esp32s31/sleep_cpu_dynamic.c @@ -0,0 +1,147 @@ +/* + * SPDX-FileCopyrightText: 2026 Espressif Systems (Shanghai) CO LTD + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#include +#include "esp_check.h" +#include "esp_heap_caps.h" + +#include "soc/clic_reg.h" +#include "esp_sleep.h" + +#include "rvsleep-frames.h" +#include "sleep_cpu_retention.h" + + +extern RvCoreCriticalSleepFrame *rv_core_critical_regs_frame[portNUM_PROCESSORS]; + +static void * cpu_domain_dev_sleep_frame_alloc_and_init(const cpu_domain_dev_regs_region_t *regions, const int region_num) +{ + const int region_sz = sizeof(cpu_domain_dev_regs_region_t) * region_num; + int regs_frame_sz = 0; + for (int num = 0; num < region_num; num++) { + regs_frame_sz += regions[num].end - regions[num].start; + } + void *frame = heap_caps_malloc(sizeof(cpu_domain_dev_sleep_frame_t) + region_sz + regs_frame_sz, MALLOC_CAP_32BIT|MALLOC_CAP_INTERNAL); + if (frame) { + cpu_domain_dev_regs_region_t *region = (cpu_domain_dev_regs_region_t *)(frame + sizeof(cpu_domain_dev_sleep_frame_t)); + memcpy(region, regions, region_num * sizeof(cpu_domain_dev_regs_region_t)); + void *regs_frame = frame + sizeof(cpu_domain_dev_sleep_frame_t) + region_sz; + memset(regs_frame, 0, regs_frame_sz); + *(cpu_domain_dev_sleep_frame_t *)frame = (cpu_domain_dev_sleep_frame_t) { + .region = region, + .region_num = region_num, + .regs_frame = (uint32_t *)regs_frame + }; + } + return frame; +} + +static inline void * cpu_domain_clic_sleep_frame_alloc_and_init(uint8_t core_id) +{ + const static cpu_domain_dev_regs_region_t regions[portNUM_PROCESSORS][3] = { + [0 ... portNUM_PROCESSORS - 1] = { + { .start = CLIC_INT_CONFIG_REG, .end = CLIC_INT_CONFIG_REG + 4 }, + { .start = CLIC_INT_THRESH_REG, .end = CLIC_INT_THRESH_REG + 4 }, + { .start = CLIC_INT_CTRL_REG(0), .end = CLIC_INT_CTRL_REG(47) + 4 }, + } + }; + return cpu_domain_dev_sleep_frame_alloc_and_init(regions[core_id], sizeof(regions[core_id]) / sizeof(cpu_domain_dev_regs_region_t)); +} + +#if CONFIG_PM_ESP_SLEEP_POWER_DOWN_CPU && !CONFIG_FREERTOS_UNICORE +esp_err_t esp_sleep_cpu_retention_init_impl(sleep_cpu_retention_t *sleep_cpu_retention_ptr, smp_retention_state_t *s_smp_retention_state) +{ + for (uint8_t core_id = 0; core_id < portNUM_PROCESSORS; ++core_id) { + if (sleep_cpu_retention_ptr->retent.critical_frame[core_id] == NULL) { + void *frame = heap_caps_calloc(1, RV_SLEEP_CTX_FRMSZ, MALLOC_CAP_32BIT|MALLOC_CAP_INTERNAL); + if (frame == NULL) { + goto err; + } + sleep_cpu_retention_ptr->retent.critical_frame[core_id] = (RvCoreCriticalSleepFrame *)frame; + rv_core_critical_regs_frame[core_id] = (RvCoreCriticalSleepFrame *)frame; + } + if (sleep_cpu_retention_ptr->retent.non_critical_frame[core_id] == NULL) { + void *frame = heap_caps_calloc(1, sizeof(RvCoreNonCriticalSleepFrame), MALLOC_CAP_32BIT|MALLOC_CAP_INTERNAL); + if (frame == NULL) { + goto err; + } + sleep_cpu_retention_ptr->retent.non_critical_frame[core_id] = (RvCoreNonCriticalSleepFrame *)frame; + } + } + for (uint8_t core_id = 0; core_id < portNUM_PROCESSORS; ++core_id) { + if (sleep_cpu_retention_ptr->retent.clic_frame[core_id] == NULL) { + void *frame = cpu_domain_clic_sleep_frame_alloc_and_init(core_id); + if (frame == NULL) { + goto err; + } + sleep_cpu_retention_ptr->retent.clic_frame[core_id] = (cpu_domain_dev_sleep_frame_t *)frame; + } + } + for (uint8_t core_id = 0; core_id < portNUM_PROCESSORS; ++core_id) { + atomic_init(&s_smp_retention_state[core_id], SMP_IDLE); + } + return ESP_OK; +err: + esp_sleep_cpu_retention_deinit(); + return ESP_ERR_NO_MEM; +} +#else +esp_err_t esp_sleep_cpu_retention_init_impl(sleep_cpu_retention_t *sleep_cpu_retention_ptr) +{ + for (uint8_t core_id = 0; core_id < portNUM_PROCESSORS; ++core_id) { + if (sleep_cpu_retention_ptr->retent.critical_frame[core_id] == NULL) { + void *frame = heap_caps_calloc(1, RV_SLEEP_CTX_FRMSZ, MALLOC_CAP_32BIT|MALLOC_CAP_INTERNAL); + if (frame == NULL) { + goto err; + } + sleep_cpu_retention_ptr->retent.critical_frame[core_id] = (RvCoreCriticalSleepFrame *)frame; + rv_core_critical_regs_frame[core_id] = (RvCoreCriticalSleepFrame *)frame; + } + if (sleep_cpu_retention_ptr->retent.non_critical_frame[core_id] == NULL) { + void *frame = heap_caps_calloc(1, sizeof(RvCoreNonCriticalSleepFrame), MALLOC_CAP_32BIT|MALLOC_CAP_INTERNAL); + if (frame == NULL) { + goto err; + } + sleep_cpu_retention_ptr->retent.non_critical_frame[core_id] = (RvCoreNonCriticalSleepFrame *)frame; + } + } + for (uint8_t core_id = 0; core_id < portNUM_PROCESSORS; ++core_id) { + if (sleep_cpu_retention_ptr->retent.clic_frame[core_id] == NULL) { + void *frame = cpu_domain_clic_sleep_frame_alloc_and_init(core_id); + if (frame == NULL) { + goto err; + } + sleep_cpu_retention_ptr->retent.clic_frame[core_id] = (cpu_domain_dev_sleep_frame_t *)frame; + } + } + return ESP_OK; +err: + esp_sleep_cpu_retention_deinit(); + return ESP_ERR_NO_MEM; +} +#endif + +esp_err_t esp_sleep_cpu_retention_deinit_impl(sleep_cpu_retention_t *sleep_cpu_retention_ptr) +{ + for (uint8_t core_id = 0; core_id < portNUM_PROCESSORS; ++core_id) { + if (sleep_cpu_retention_ptr->retent.critical_frame[core_id]) { + heap_caps_free((void *)sleep_cpu_retention_ptr->retent.critical_frame[core_id]); + sleep_cpu_retention_ptr->retent.critical_frame[core_id] = NULL; + rv_core_critical_regs_frame[core_id] = NULL; + } + if (sleep_cpu_retention_ptr->retent.non_critical_frame[core_id]) { + heap_caps_free((void *)sleep_cpu_retention_ptr->retent.non_critical_frame[core_id]); + sleep_cpu_retention_ptr->retent.non_critical_frame[core_id] = NULL; + } + } + for (uint8_t core_id = 0; core_id < portNUM_PROCESSORS; ++core_id) { + if (sleep_cpu_retention_ptr->retent.clic_frame[core_id]) { + heap_caps_free((void *)sleep_cpu_retention_ptr->retent.clic_frame[core_id]); + sleep_cpu_retention_ptr->retent.clic_frame[core_id] = NULL; + } + } + return ESP_OK; +} diff --git a/components/esp_hw_support/lowpower/port/esp32s31/sleep_cpu_retention.h b/components/esp_hw_support/lowpower/port/esp32s31/sleep_cpu_retention.h new file mode 100644 index 00000000000..55f340c2f9e --- /dev/null +++ b/components/esp_hw_support/lowpower/port/esp32s31/sleep_cpu_retention.h @@ -0,0 +1,57 @@ +/* + * SPDX-FileCopyrightText: 2026 Espressif Systems (Shanghai) CO LTD + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#ifndef __SLEEP_CPU_RETENTION_H__ +#define __SLEEP_CPU_RETENTION_H__ + +#include "rvsleep-frames.h" +#include "freertos/FreeRTOS.h" +#include "esp_err.h" + +#if CONFIG_PM_ESP_SLEEP_POWER_DOWN_CPU && !CONFIG_FREERTOS_UNICORE +#include +#include "soc/hp_system_reg.h" +typedef enum { + SMP_IDLE, + SMP_BACKUP_START, + SMP_BACKUP_DONE, + SMP_RESTORE_START, + SMP_RESTORE_DONE, + SMP_SKIP_RETENTION, +} smp_retention_state_t; +#endif + +typedef struct { + uint32_t start; + uint32_t end; +} cpu_domain_dev_regs_region_t; + +typedef struct { + cpu_domain_dev_regs_region_t *region; + int region_num; + uint32_t *regs_frame; +} cpu_domain_dev_sleep_frame_t; + +/** + * Internal structure which holds all requested light sleep cpu retention parameters + */ +typedef struct { + struct { + RvCoreCriticalSleepFrame *critical_frame[portNUM_PROCESSORS]; + RvCoreNonCriticalSleepFrame *non_critical_frame[portNUM_PROCESSORS]; + cpu_domain_dev_sleep_frame_t *clic_frame[portNUM_PROCESSORS]; + } retent; +} sleep_cpu_retention_t; + +#if CONFIG_PM_ESP_SLEEP_POWER_DOWN_CPU && !CONFIG_FREERTOS_UNICORE + esp_err_t esp_sleep_cpu_retention_init_impl(sleep_cpu_retention_t *sleep_cpu_retention_ptr, smp_retention_state_t *s_smp_retention_state); +#else + esp_err_t esp_sleep_cpu_retention_init_impl(sleep_cpu_retention_t *sleep_cpu_retention_ptr); +#endif + +esp_err_t esp_sleep_cpu_retention_deinit_impl(sleep_cpu_retention_t *sleep_cpu_retention_ptr); + +#endif /* #ifndef __SLEEP_CPU_RETENTION_H__ */ diff --git a/components/esp_hw_support/lowpower/port/esp32s31/sleep_cpu_static.c b/components/esp_hw_support/lowpower/port/esp32s31/sleep_cpu_static.c new file mode 100644 index 00000000000..317362fe6f9 --- /dev/null +++ b/components/esp_hw_support/lowpower/port/esp32s31/sleep_cpu_static.c @@ -0,0 +1,146 @@ +/* + * SPDX-FileCopyrightText: 2026 Espressif Systems (Shanghai) CO LTD + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#include +#include "esp_check.h" +#include "soc/clic_reg.h" + +#include "rvsleep-frames.h" +#include "sleep_cpu_retention.h" + + +extern RvCoreCriticalSleepFrame *rv_core_critical_regs_frame[portNUM_PROCESSORS]; + +#define R_CONCAT(s1, s2) _R_CONCAT(s1, s2) +#define _R_CONCAT(s1, s2) s1 ## s2 + +#define CPU_DOMAIN_DEV_SZ0 (CPU_DOMAIN_DEV_END_ADDR0 - CPU_DOMAIN_DEV_START_ADDR0) +#define CPU_DOMAIN_DEV_SZ1 (CPU_DOMAIN_DEV_END_ADDR1 - CPU_DOMAIN_DEV_START_ADDR1) +#define CPU_DOMAIN_DEV_SZ2 (CPU_DOMAIN_DEV_END_ADDR2 - CPU_DOMAIN_DEV_START_ADDR2) +#define CPU_DOMAIN_DEV_SZ3 (CPU_DOMAIN_DEV_END_ADDR3 - CPU_DOMAIN_DEV_START_ADDR3) +#define CPU_DOMAIN_DEV_SZ4 (CPU_DOMAIN_DEV_END_ADDR4 - CPU_DOMAIN_DEV_START_ADDR4) +#define CPU_DOMAIN_DEV_SZ5 (CPU_DOMAIN_DEV_END_ADDR5 - CPU_DOMAIN_DEV_START_ADDR5) +#define CPU_DOMAIN_DEV_SZ6 (CPU_DOMAIN_DEV_END_ADDR6 - CPU_DOMAIN_DEV_START_ADDR6) +#define CPU_DOMAIN_DEV_SZ7 (CPU_DOMAIN_DEV_END_ADDR7 - CPU_DOMAIN_DEV_START_ADDR7) + +#define CPU_DOMAIN_DEV_TOTAL_SZ(n) (R_CONCAT(__TOTAL_SZ, n)) + +#define __TOTAL_SZ0 (sizeof(cpu_domain_dev_sleep_frame_t)) +#define __TOTAL_SZ1 ((__TOTAL_SZ0) + (sizeof(cpu_domain_dev_regs_region_t)) + CPU_DOMAIN_DEV_SZ0) +#define __TOTAL_SZ2 ((__TOTAL_SZ1) + (sizeof(cpu_domain_dev_regs_region_t)) + CPU_DOMAIN_DEV_SZ1) +#define __TOTAL_SZ3 ((__TOTAL_SZ2) + (sizeof(cpu_domain_dev_regs_region_t)) + CPU_DOMAIN_DEV_SZ2) +#define __TOTAL_SZ4 ((__TOTAL_SZ3) + (sizeof(cpu_domain_dev_regs_region_t)) + CPU_DOMAIN_DEV_SZ3) +#define __TOTAL_SZ5 ((__TOTAL_SZ4) + (sizeof(cpu_domain_dev_regs_region_t)) + CPU_DOMAIN_DEV_SZ4) +#define __TOTAL_SZ6 ((__TOTAL_SZ5) + (sizeof(cpu_domain_dev_regs_region_t)) + CPU_DOMAIN_DEV_SZ5) +#define __TOTAL_SZ7 ((__TOTAL_SZ6) + (sizeof(cpu_domain_dev_regs_region_t)) + CPU_DOMAIN_DEV_SZ6) +#define __TOTAL_SZ8 ((__TOTAL_SZ7) + (sizeof(cpu_domain_dev_regs_region_t)) + CPU_DOMAIN_DEV_SZ7) + + +static void * cpu_domain_dev_sleep_frame_alloc_and_init(const cpu_domain_dev_regs_region_t *regions, const int region_num, void * frame) +{ + const int region_sz = sizeof(cpu_domain_dev_regs_region_t) * region_num; + int regs_frame_sz = 0; + for (int num = 0; num < region_num; num++) { + regs_frame_sz += regions[num].end - regions[num].start; + } + if (frame) { + cpu_domain_dev_regs_region_t *region = (cpu_domain_dev_regs_region_t *)(frame + sizeof(cpu_domain_dev_sleep_frame_t)); + memcpy(region, regions, region_num * sizeof(cpu_domain_dev_regs_region_t)); + void *regs_frame = frame + sizeof(cpu_domain_dev_sleep_frame_t) + region_sz; + memset(regs_frame, 0, regs_frame_sz); + *(cpu_domain_dev_sleep_frame_t *)frame = (cpu_domain_dev_sleep_frame_t) { + .region = region, + .region_num = region_num, + .regs_frame = (uint32_t *)regs_frame + }; + } + return frame; +} + +static inline void * cpu_domain_clic_sleep_frame_alloc_and_init(uint8_t core_id) +{ + static DRAM_ATTR cpu_domain_dev_regs_region_t regions[portNUM_PROCESSORS][2] = { + [0 ... portNUM_PROCESSORS - 1] = { + { .start = CLIC_INT_CONFIG_REG, .end = CLIC_INT_CONFIG_REG + 4 }, + { .start = CLIC_INT_THRESH_REG, .end = CLIC_INT_THRESH_REG + 4 }, + { .start = CLIC_INT_CTRL_REG(0), .end = CLIC_INT_CTRL_REG(47) + 4 }, + } + }; + static DRAM_ATTR uint8_t sleep_frame[portNUM_PROCESSORS][CPU_DOMAIN_DEV_TOTAL_SZ(2)] __attribute__((aligned(4))); + return cpu_domain_dev_sleep_frame_alloc_and_init(regions[core_id], sizeof(regions[core_id]) / sizeof(regions[core_id][0]), sleep_frame[core_id]); +} + +#if CONFIG_PM_ESP_SLEEP_POWER_DOWN_CPU && !CONFIG_FREERTOS_UNICORE +esp_err_t esp_sleep_cpu_retention_init_impl(sleep_cpu_retention_t *sleep_cpu_retention_ptr, smp_retention_state_t *s_smp_retention_state) +{ + static DRAM_ATTR uint8_t rv_core_critical_regs[RV_SLEEP_CTX_FRMSZ * portNUM_PROCESSORS] __attribute__((aligned(4))); + static DRAM_ATTR uint8_t rv_core_non_critical_regs[sizeof(RvCoreNonCriticalSleepFrame)* portNUM_PROCESSORS] __attribute__((aligned(4))); + for (uint8_t core_id = 0; core_id < portNUM_PROCESSORS; ++core_id) { + if (sleep_cpu_retention_ptr->retent.critical_frame[core_id] == NULL) { + void * regs = rv_core_critical_regs + core_id * RV_SLEEP_CTX_FRMSZ; + sleep_cpu_retention_ptr->retent.critical_frame[core_id] = (RvCoreCriticalSleepFrame *) regs; + rv_core_critical_regs_frame[core_id] = (RvCoreCriticalSleepFrame *) regs; + } + if (sleep_cpu_retention_ptr->retent.non_critical_frame[core_id] == NULL) { + sleep_cpu_retention_ptr->retent.non_critical_frame[core_id] = (RvCoreNonCriticalSleepFrame *)(rv_core_non_critical_regs+core_id * sizeof(RvCoreNonCriticalSleepFrame)); + } + } + + for (uint8_t core_id = 0; core_id < portNUM_PROCESSORS; ++core_id) { + if (sleep_cpu_retention_ptr->retent.clic_frame[core_id] == NULL) { + void *frame = cpu_domain_clic_sleep_frame_alloc_and_init(core_id); + sleep_cpu_retention_ptr->retent.clic_frame[core_id] = (cpu_domain_dev_sleep_frame_t *)frame; + } + } + for (uint8_t core_id = 0; core_id < portNUM_PROCESSORS; ++core_id) { + atomic_init(&s_smp_retention_state[core_id], SMP_IDLE); + } + return ESP_OK; +} +#else +esp_err_t esp_sleep_cpu_retention_init_impl(sleep_cpu_retention_t *sleep_cpu_retention_ptr) +{ + static DRAM_ATTR uint8_t rv_core_critical_regs[RV_SLEEP_CTX_FRMSZ * portNUM_PROCESSORS] __attribute__((aligned(4))); + static DRAM_ATTR uint8_t rv_core_non_critical_regs[sizeof(RvCoreNonCriticalSleepFrame)* portNUM_PROCESSORS] __attribute__((aligned(4))); + for (uint8_t core_id = 0; core_id < portNUM_PROCESSORS; ++core_id) { + if (sleep_cpu_retention_ptr->retent.critical_frame[core_id] == NULL) { + void * regs = rv_core_critical_regs + core_id * RV_SLEEP_CTX_FRMSZ; + sleep_cpu_retention_ptr->retent.critical_frame[core_id] = (RvCoreCriticalSleepFrame *) regs; + rv_core_critical_regs_frame[core_id] = (RvCoreCriticalSleepFrame *) regs; + } + if (sleep_cpu_retention_ptr->retent.non_critical_frame[core_id] == NULL) { + sleep_cpu_retention_ptr->retent.non_critical_frame[core_id] = (RvCoreNonCriticalSleepFrame *)(rv_core_non_critical_regs+core_id * sizeof(RvCoreNonCriticalSleepFrame)); + } + } + + for (uint8_t core_id = 0; core_id < portNUM_PROCESSORS; ++core_id) { + if (sleep_cpu_retention_ptr->retent.clic_frame[core_id] == NULL) { + void *frame = cpu_domain_clic_sleep_frame_alloc_and_init(core_id); + sleep_cpu_retention_ptr->retent.clic_frame[core_id] = (cpu_domain_dev_sleep_frame_t *)frame; + } + } + return ESP_OK; +} +#endif + +esp_err_t esp_sleep_cpu_retention_deinit_impl(sleep_cpu_retention_t *sleep_cpu_retention_ptr) +{ + for (uint8_t core_id = 0; core_id < portNUM_PROCESSORS; ++core_id) { + if (sleep_cpu_retention_ptr->retent.critical_frame[core_id]) { + sleep_cpu_retention_ptr->retent.critical_frame[core_id] = NULL; + rv_core_critical_regs_frame[core_id] = NULL; + } + if (sleep_cpu_retention_ptr->retent.non_critical_frame[core_id]) { + sleep_cpu_retention_ptr->retent.non_critical_frame[core_id] = NULL; + } + } + for (uint8_t core_id = 0; core_id < portNUM_PROCESSORS; ++core_id) { + if (sleep_cpu_retention_ptr->retent.clic_frame[core_id]) { + sleep_cpu_retention_ptr->retent.clic_frame[core_id] = NULL; + } + } + return ESP_OK; +} diff --git a/components/esp_hw_support/lowpower/port/esp32s31/sleep_fpu_asm.S b/components/esp_hw_support/lowpower/port/esp32s31/sleep_fpu_asm.S new file mode 100644 index 00000000000..30b632672ee --- /dev/null +++ b/components/esp_hw_support/lowpower/port/esp32s31/sleep_fpu_asm.S @@ -0,0 +1,118 @@ +/* + * SPDX-FileCopyrightText: 2026 Espressif Systems (Shanghai) CO LTD + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#include "rvsleep-frames.h" +#include "freertos/FreeRTOSConfig.h" +#include "sdkconfig.h" + + .section .iram1,"ax" + .global rv_core_fpu_save + .type rv_core_fpu_save,@function + .align 4 + +/* Bit to set in mstatus to enable the FPU */ +#define CSR_MSTATUS_FPU_ENABLE (1 << 13) +/* Bit to clear in mstatus to disable the FPU */ +#define CSR_MSTATUS_FPU_DISABLE (3 << 13) + +.macro fpu_enable reg + li \reg, CSR_MSTATUS_FPU_ENABLE + csrs mstatus, \reg +.endm + +rv_core_fpu_save: + fpu_enable t0 + mv t0, a0 /* t0 = frame (arg0) */ + fsw ft0, RV_SLP_CTX_FPU_FT0(t0) + fsw ft1, RV_SLP_CTX_FPU_FT1(t0) + fsw ft2, RV_SLP_CTX_FPU_FT2(t0) + fsw ft3, RV_SLP_CTX_FPU_FT3(t0) + fsw ft4, RV_SLP_CTX_FPU_FT4(t0) + fsw ft5, RV_SLP_CTX_FPU_FT5(t0) + fsw ft6, RV_SLP_CTX_FPU_FT6(t0) + fsw ft7, RV_SLP_CTX_FPU_FT7(t0) + fsw fs0, RV_SLP_CTX_FPU_FS0(t0) + fsw fs1, RV_SLP_CTX_FPU_FS1(t0) + fsw fa0, RV_SLP_CTX_FPU_FA0(t0) + fsw fa1, RV_SLP_CTX_FPU_FA1(t0) + fsw fa2, RV_SLP_CTX_FPU_FA2(t0) + fsw fa3, RV_SLP_CTX_FPU_FA3(t0) + fsw fa4, RV_SLP_CTX_FPU_FA4(t0) + fsw fa5, RV_SLP_CTX_FPU_FA5(t0) + fsw fa6, RV_SLP_CTX_FPU_FA6(t0) + fsw fa7, RV_SLP_CTX_FPU_FA7(t0) + fsw fs2, RV_SLP_CTX_FPU_FS2(t0) + fsw fs3, RV_SLP_CTX_FPU_FS3(t0) + fsw fs4, RV_SLP_CTX_FPU_FS4(t0) + fsw fs5, RV_SLP_CTX_FPU_FS5(t0) + fsw fs6, RV_SLP_CTX_FPU_FS6(t0) + fsw fs7, RV_SLP_CTX_FPU_FS7(t0) + fsw fs8, RV_SLP_CTX_FPU_FS8(t0) + fsw fs9, RV_SLP_CTX_FPU_FS9(t0) + fsw fs10, RV_SLP_CTX_FPU_FS10(t0) + fsw fs11, RV_SLP_CTX_FPU_FS11(t0) + fsw ft8, RV_SLP_CTX_FPU_FT8(t0) + fsw ft9, RV_SLP_CTX_FPU_FT9(t0) + fsw ft10, RV_SLP_CTX_FPU_FT10(t0) + fsw ft11, RV_SLP_CTX_FPU_FT11(t0) + csrr t1, fcsr + sw t1, RV_SLP_CTX_FPU_FCSR(t0) + /* Chip will go to sleep and FPU will be powered down, not necessary to disable FPU here. */ + ret + + .size rv_core_fpu_save, . - rv_core_fpu_save + +/* +-------------------------------------------------------------------------------- + FPU restore: a0 = RvCoreCriticalSleepFrame *. Restore all FP registers from the frame. +-------------------------------------------------------------------------------- +*/ + .section .iram1,"ax" + .global rv_core_fpu_restore + .type rv_core_fpu_restore,@function + .align 4 + +rv_core_fpu_restore: + fpu_enable t0 + mv t0, a0 /* t0 = frame (arg0) */ + flw ft0, RV_SLP_CTX_FPU_FT0(t0) + flw ft1, RV_SLP_CTX_FPU_FT1(t0) + flw ft2, RV_SLP_CTX_FPU_FT2(t0) + flw ft3, RV_SLP_CTX_FPU_FT3(t0) + flw ft4, RV_SLP_CTX_FPU_FT4(t0) + flw ft5, RV_SLP_CTX_FPU_FT5(t0) + flw ft6, RV_SLP_CTX_FPU_FT6(t0) + flw ft7, RV_SLP_CTX_FPU_FT7(t0) + flw fs0, RV_SLP_CTX_FPU_FS0(t0) + flw fs1, RV_SLP_CTX_FPU_FS1(t0) + flw fa0, RV_SLP_CTX_FPU_FA0(t0) + flw fa1, RV_SLP_CTX_FPU_FA1(t0) + flw fa2, RV_SLP_CTX_FPU_FA2(t0) + flw fa3, RV_SLP_CTX_FPU_FA3(t0) + flw fa4, RV_SLP_CTX_FPU_FA4(t0) + flw fa5, RV_SLP_CTX_FPU_FA5(t0) + flw fa6, RV_SLP_CTX_FPU_FA6(t0) + flw fa7, RV_SLP_CTX_FPU_FA7(t0) + flw fs2, RV_SLP_CTX_FPU_FS2(t0) + flw fs3, RV_SLP_CTX_FPU_FS3(t0) + flw fs4, RV_SLP_CTX_FPU_FS4(t0) + flw fs5, RV_SLP_CTX_FPU_FS5(t0) + flw fs6, RV_SLP_CTX_FPU_FS6(t0) + flw fs7, RV_SLP_CTX_FPU_FS7(t0) + flw fs8, RV_SLP_CTX_FPU_FS8(t0) + flw fs9, RV_SLP_CTX_FPU_FS9(t0) + flw fs10, RV_SLP_CTX_FPU_FS10(t0) + flw fs11, RV_SLP_CTX_FPU_FS11(t0) + flw ft8, RV_SLP_CTX_FPU_FT8(t0) + flw ft9, RV_SLP_CTX_FPU_FT9(t0) + flw ft10, RV_SLP_CTX_FPU_FT10(t0) + flw ft11, RV_SLP_CTX_FPU_FT11(t0) + lw t1, RV_SLP_CTX_FPU_FCSR(t0) + csrw fcsr, t1 + /* Caller restores mstatus (and thus FPU state); not necessary to disable FPU here. */ + ret + + .size rv_core_fpu_restore, . - rv_core_fpu_restore diff --git a/components/soc/esp32s31/include/soc/Kconfig.soc_caps.in b/components/soc/esp32s31/include/soc/Kconfig.soc_caps.in index d876a82bcd8..539f4691de8 100644 --- a/components/soc/esp32s31/include/soc/Kconfig.soc_caps.in +++ b/components/soc/esp32s31/include/soc/Kconfig.soc_caps.in @@ -1083,6 +1083,10 @@ config SOC_PM_SUPPORT_TOUCH_SENSOR_WAKEUP bool default y +config SOC_PM_SUPPORT_CPU_PD + bool + default y + config SOC_PM_SUPPORT_XTAL32K_PD bool default y @@ -1131,6 +1135,10 @@ config SOC_PM_CPU_RETENTION_BY_SW bool default y +config SOC_PM_FPU_RETENTION_BY_SW + bool + default y + config SOC_PM_CACHE_RETENTION_BY_PAU bool default y diff --git a/components/soc/esp32s31/include/soc/soc_caps.h b/components/soc/esp32s31/include/soc/soc_caps.h index 1b70b3d4327..6ea35c1ab58 100644 --- a/components/soc/esp32s31/include/soc/soc_caps.h +++ b/components/soc/esp32s31/include/soc/soc_caps.h @@ -441,7 +441,7 @@ #define SOC_PM_SUPPORT_WIFI_WAKEUP (1) #define SOC_PM_SUPPORT_BEACON_WAKEUP (1) #define SOC_PM_SUPPORT_TOUCH_SENSOR_WAKEUP (1) /*! Date: Wed, 6 May 2026 16:29:59 +0800 Subject: [PATCH 05/13] feat(esp_hw_support): support esp32s31 system & peripherals retention --- .../port/esp32s31/peripheral_domain_pd.c | 10 +++--- .../esp_hw_support/sleep_system_peripheral.c | 13 +++---- .../esp32p4/include/soc/Kconfig.soc_caps.in | 4 +++ components/soc/esp32p4/include/soc/soc_caps.h | 1 + .../esp32s31/include/soc/Kconfig.soc_caps.in | 12 +++++++ .../soc/esp32s31/include/soc/soc_caps.h | 5 +-- .../include/soc/system_periph_retention.h | 10 ++++++ .../soc/esp32s31/system_retention_periph.c | 35 ++++++++++++++++--- examples/lowpower/power_management/README.md | 4 +-- 9 files changed, 75 insertions(+), 19 deletions(-) diff --git a/components/esp_hw_support/port/esp32s31/peripheral_domain_pd.c b/components/esp_hw_support/port/esp32s31/peripheral_domain_pd.c index 5b7ce3919ab..142a71bc37c 100644 --- a/components/esp_hw_support/port/esp32s31/peripheral_domain_pd.c +++ b/components/esp_hw_support/port/esp32s31/peripheral_domain_pd.c @@ -82,13 +82,15 @@ bool peripheral_domain_pd_allowed(void) RETENTION_MODULE_BITMAP_SET(&mask, SLEEP_RETENTION_MODULE_MCPWM2); RETENTION_MODULE_BITMAP_SET(&mask, SLEEP_RETENTION_MODULE_MCPWM3); - // ESP32S31 supports PCNT sleep retention - RETENTION_MODULE_BITMAP_SET(&mask, SLEEP_RETENTION_MODULE_PCNT0); - RETENTION_MODULE_BITMAP_SET(&mask, SLEEP_RETENTION_MODULE_PCNT1); - // ESP32S31 supports SDM sleep retention RETENTION_MODULE_BITMAP_SET(&mask, SLEEP_RETENTION_MODULE_SDM0); + // ESP32S31 supports LCDCAM sleep retention + RETENTION_MODULE_BITMAP_SET(&mask, SLEEP_RETENTION_MODULE_LCDCAM); + + // ESP32S31 supports JPEG sleep retention + RETENTION_MODULE_BITMAP_SET(&mask, SLEEP_RETENTION_MODULE_JPEG); + const sleep_retention_module_bitmap_t peripheral_domain_inited_modules = sleep_retention_module_bitmap_and(inited_modules, mask); const sleep_retention_module_bitmap_t peripheral_domain_created_modules = sleep_retention_module_bitmap_and(created_modules, mask); return sleep_retention_module_bitmap_eq(peripheral_domain_inited_modules, peripheral_domain_created_modules); diff --git a/components/esp_hw_support/sleep_system_peripheral.c b/components/esp_hw_support/sleep_system_peripheral.c index 73babdba3b7..81d6569e414 100644 --- a/components/esp_hw_support/sleep_system_peripheral.c +++ b/components/esp_hw_support/sleep_system_peripheral.c @@ -34,7 +34,8 @@ static __attribute__((unused)) esp_err_t sleep_sys_periph_hp_system_retention_in static __attribute__((unused)) esp_err_t sleep_sys_periph_tee_apm_retention_init(void *arg) { /* TBD for ESP32P4 IDF-10020. */ -#ifndef CONFIG_IDF_TARGET_ESP32P4 +/* TBD for ESP32S31 IDF-14620. */ +#if !defined(CONFIG_IDF_TARGET_ESP32P4) && !defined(CONFIG_IDF_TARGET_ESP32S31) esp_err_t err = sleep_retention_entries_create(tee_apm_regs_retention, ARRAY_SIZE(tee_apm_regs_retention), REGDMA_LINK_PRI_NON_CRITICAL_TEE_APM, SLEEP_RETENTION_MODULE_SYS_PERIPH); if (err == ESP_OK) { err = sleep_retention_entries_create(tee_apm_highpri_regs_retention, ARRAY_SIZE(tee_apm_highpri_regs_retention), REGDMA_LINK_PRI_CRITICAL_TEE_APM, SLEEP_RETENTION_MODULE_SYS_PERIPH); @@ -74,7 +75,7 @@ static __attribute__((unused)) esp_err_t sleep_sys_periph_flash_spimem_retention return ESP_OK; } -#if CONFIG_SPIRAM && CONFIG_IDF_TARGET_ESP32P4 +#if CONFIG_SPIRAM && SOC_PSRAM_MEMSPI_IS_INDEPENDENT /* TODO: PM-205, In the ESP32C5, Flash and PSRAM use the same set of SPIMEM hardware, while in P4, Flash and PSRAM each have their own SPIMEM hardware. * It’s necessary to confirm whether the ESP32C5 can independently manage SPIMEM retention for Flash and PSRAM in software. */ static __attribute__((unused)) esp_err_t sleep_sys_periph_psram_spimem_retention_init(void *arg) @@ -99,7 +100,7 @@ esp_err_t sleep_sys_periph_cache_retention_init(void) { esp_err_t err = sleep_retention_entries_create(cache_regs_retention, ARRAY_SIZE(cache_regs_retention), REGDMA_LINK_PRI_SYS_PERIPH_HIGH, SLEEP_RETENTION_MODULE_SYS_PERIPH); ESP_RETURN_ON_ERROR(err, TAG, "failed to allocate memory for digital peripherals (Cache) retention"); - ESP_LOGI(TAG, "L2 Cache sleep retention initialization"); + ESP_LOGD(TAG, "L2 Cache sleep retention initialization"); return ESP_OK; } #endif @@ -109,7 +110,7 @@ esp_err_t sleep_pau_retention_init(void) { esp_err_t err = sleep_retention_entries_create(pau_regs_retention, ARRAY_SIZE(pau_regs_retention), REGDMA_LINK_PRI_SYS_PERIPH_LOW, SLEEP_RETENTION_MODULE_SYS_PERIPH); ESP_RETURN_ON_ERROR(err, TAG, "failed to allocate memory for system (PAU) retention"); - ESP_LOGI(TAG, "PAU sleep retention initialization"); + ESP_LOGD(TAG, "PAU sleep retention initialization"); return ESP_OK; } #endif @@ -119,7 +120,7 @@ esp_err_t sleep_pvt_retention_init(void) { esp_err_t err = sleep_retention_entries_create(pvt_regs_retention, ARRAY_SIZE(pvt_regs_retention), REGDMA_LINK_PRI_SYS_PERIPH_LOW, SLEEP_RETENTION_MODULE_SYS_PERIPH); ESP_RETURN_ON_ERROR(err, TAG, "failed to allocate memory for system (PVT) retention"); - ESP_LOGI(TAG, "PVT sleep retention initialization"); + ESP_LOGD(TAG, "PVT sleep retention initialization"); return ESP_OK; } #endif @@ -147,7 +148,7 @@ static __attribute__((unused)) esp_err_t sleep_sys_periph_retention_init(void *a if(err) goto error; err = sleep_sys_periph_flash_spimem_retention_init(arg); if(err) goto error; -#if CONFIG_SPIRAM && CONFIG_IDF_TARGET_ESP32P4 +#if CONFIG_SPIRAM && SOC_PSRAM_MEMSPI_IS_INDEPENDENT err = sleep_sys_periph_psram_spimem_retention_init(arg); if(err) goto error; #endif diff --git a/components/soc/esp32p4/include/soc/Kconfig.soc_caps.in b/components/soc/esp32p4/include/soc/Kconfig.soc_caps.in index 984a7fa3f61..7001db6a976 100644 --- a/components/soc/esp32p4/include/soc/Kconfig.soc_caps.in +++ b/components/soc/esp32p4/include/soc/Kconfig.soc_caps.in @@ -1411,6 +1411,10 @@ config SOC_MEMSPI_SUPPORT_CONTROL_DUMMY_OUT bool default y +config SOC_PSRAM_MEMSPI_IS_INDEPENDENT + bool + default y + config SOC_SPI_MEM_FLASH_SUPPORT_HPM bool default y diff --git a/components/soc/esp32p4/include/soc/soc_caps.h b/components/soc/esp32p4/include/soc/soc_caps.h index 10193dc3fdb..d9d1c20681f 100644 --- a/components/soc/esp32p4/include/soc/soc_caps.h +++ b/components/soc/esp32p4/include/soc/soc_caps.h @@ -534,6 +534,7 @@ #define SOC_MSPI_HAS_INDEPENT_IOMUX 1 #define SOC_MEMSPI_IS_INDEPENDENT 1 #define SOC_MEMSPI_SUPPORT_CONTROL_DUMMY_OUT 1 +#define SOC_PSRAM_MEMSPI_IS_INDEPENDENT 1 #define SOC_SPI_MEM_FLASH_SUPPORT_HPM (1) /*!< Support High Performance Mode */ diff --git a/components/soc/esp32s31/include/soc/Kconfig.soc_caps.in b/components/soc/esp32s31/include/soc/Kconfig.soc_caps.in index 539f4691de8..d9b36b66cd9 100644 --- a/components/soc/esp32s31/include/soc/Kconfig.soc_caps.in +++ b/components/soc/esp32s31/include/soc/Kconfig.soc_caps.in @@ -247,6 +247,10 @@ config SOC_MODEM_CLOCK_SUPPORTED bool default y +config SOC_PM_SUPPORTED + bool + default y + config SOC_BITSCRAMBLER_SUPPORTED bool default y @@ -731,6 +735,10 @@ config SOC_MEMSPI_IS_INDEPENDENT bool default y +config SOC_PSRAM_MEMSPI_IS_INDEPENDENT + bool + default y + config SOC_MEMSPI_ENCRYPTION_ALIGNMENT int default 16 @@ -1103,6 +1111,10 @@ config SOC_PM_SUPPORT_VDDSDIO_PD bool default y +config SOC_PM_SUPPORT_TOP_PD + bool + default y + config SOC_PM_SUPPORT_HP_AON_PD bool default y diff --git a/components/soc/esp32s31/include/soc/soc_caps.h b/components/soc/esp32s31/include/soc/soc_caps.h index 6ea35c1ab58..decdf740282 100644 --- a/components/soc/esp32s31/include/soc/soc_caps.h +++ b/components/soc/esp32s31/include/soc/soc_caps.h @@ -100,7 +100,7 @@ #define SOC_LIGHT_SLEEP_SUPPORTED 1 #define SOC_DEEP_SLEEP_SUPPORTED 1 #define SOC_MODEM_CLOCK_SUPPORTED 1 -// #define SOC_PM_SUPPORTED 1 // TODO: [ESP32S31] IDF-14648 +#define SOC_PM_SUPPORTED 1 #define SOC_BITSCRAMBLER_SUPPORTED 1 #define SOC_SIMD_INSTRUCTION_SUPPORTED 1 #define SOC_CORDIC_SUPPORTED 1 @@ -299,6 +299,7 @@ #define SOC_SPI_MEM_SUPPORT_TSUS_TRES_SEPERATE_CTR (1) #define SOC_MEMSPI_IS_INDEPENDENT 1 +#define SOC_PSRAM_MEMSPI_IS_INDEPENDENT 1 #define SOC_MEMSPI_ENCRYPTION_ALIGNMENT 16 /*!< 16-byte alignment restriction to mem addr and size if encryption is enabled */ @@ -446,7 +447,7 @@ #define SOC_PM_SUPPORT_RC32K_PD (1) #define SOC_PM_SUPPORT_RC_FAST_PD (1) #define SOC_PM_SUPPORT_VDDSDIO_PD (1) -// #define SOC_PM_SUPPORT_TOP_PD (1) // TODO: [ESP32S31] IDF-14647 +#define SOC_PM_SUPPORT_TOP_PD (1) #define SOC_PM_SUPPORT_HP_AON_PD (1) #define SOC_PM_SUPPORT_CNNT_PD (1) #define SOC_PM_SUPPORT_RTC_PERIPH_PD (1) diff --git a/components/soc/esp32s31/include/soc/system_periph_retention.h b/components/soc/esp32s31/include/soc/system_periph_retention.h index 7fa9390cd7e..345955df495 100644 --- a/components/soc/esp32s31/include/soc/system_periph_retention.h +++ b/components/soc/esp32s31/include/soc/system_periph_retention.h @@ -25,6 +25,16 @@ extern "C" #define INT_MTX_RETENTION_LINK_LEN 2 extern const regdma_entries_config_t intr_matrix_regs_retention[INT_MTX_RETENTION_LINK_LEN]; +/** + * @brief Provide access to cache configuration registers retention + * context definition. + * + * This is an internal function of the sleep retention driver, and is not + * useful for external use. + */ +#define CACHE_RETENTION_LINK_LEN 4 +extern const regdma_entries_config_t cache_regs_retention[CACHE_RETENTION_LINK_LEN]; + /** * @brief Provide access to tee apm configuration registers retention * context definition. diff --git a/components/soc/esp32s31/system_retention_periph.c b/components/soc/esp32s31/system_retention_periph.c index 56b461576a8..2627754605b 100644 --- a/components/soc/esp32s31/system_retention_periph.c +++ b/components/soc/esp32s31/system_retention_periph.c @@ -38,6 +38,20 @@ const regdma_entries_config_t intr_matrix_regs_retention[] = { }; _Static_assert(ARRAY_SIZE(intr_matrix_regs_retention) == INT_MTX_RETENTION_LINK_LEN, "Inconsistent INT_MTX retention link length definitions"); +#define L1_CACHE_ACS_FAIL_INR_CLR (CACHE_L1_ICACHE0_FAIL_INT_CLR | CACHE_L1_ICACHE1_FAIL_INT_CLR | CACHE_L1_ICACHE2_FAIL_INT_CLR | CACHE_L1_ICACHE3_FAIL_INT_CLR |CACHE_L1_DCACHE_FAIL_INT_CLR) +#define L1_CACHE_ACS_FAIL_INR_CLR_M (CACHE_L1_ICACHE0_FAIL_INT_CLR_M | CACHE_L1_ICACHE1_FAIL_INT_CLR_M | CACHE_L1_ICACHE2_FAIL_INT_CLR_M | CACHE_L1_ICACHE3_FAIL_INT_CLR_M |CACHE_L1_DCACHE_FAIL_INT_CLR_M) + +#define N_REGS_CACHE_0() (((CACHE_L1_CACHE_ATOMIC_CONF_REG - CACHE_L1_ICACHE_CTRL_REG) / 4) + 1) +#define N_REGS_CACHE_1() (((CACHE_L1_CACHE_ACS_FAIL_INT_ENA_REG - CACHE_L1_CACHE_ACS_FAIL_CTRL_REG) / 4) + 1) + +const regdma_entries_config_t cache_regs_retention[] = { + [0] = { .config = REGDMA_LINK_CONTINUOUS_INIT(REGDMA_CACHE_LINK(0x1), CACHE_L1_ICACHE_CTRL_REG, CACHE_L1_ICACHE_CTRL_REG, N_REGS_CACHE_0(), 0, 1), .owner = ENTRY(0) | ENTRY(2)}, + [1] = { .config = REGDMA_LINK_CONTINUOUS_INIT(REGDMA_CACHE_LINK(0x2), CACHE_L1_CACHE_ACS_FAIL_CTRL_REG, CACHE_L1_CACHE_ACS_FAIL_CTRL_REG, N_REGS_CACHE_1(), 0, 1), .owner = ENTRY(0) | ENTRY(2)}, + [2] = { .config = REGDMA_LINK_CONTINUOUS_INIT(REGDMA_CACHE_LINK(0x3), CACHE_TRACE_ENA_REG, CACHE_TRACE_ENA_REG, 1, 0, 1), .owner = ENTRY(0) | ENTRY(2)}, + [3] = { .config = REGDMA_LINK_WRITE_INIT(REGDMA_CACHE_LINK(0x0), CACHE_L1_CACHE_ACS_FAIL_INT_CLR_REG, L1_CACHE_ACS_FAIL_INR_CLR, L1_CACHE_ACS_FAIL_INR_CLR_M, 1, 1), .owner = ENTRY(0) | ENTRY(2) }, +}; +_Static_assert(ARRAY_SIZE(cache_regs_retention) == CACHE_RETENTION_LINK_LEN, "Inconsistent L2 CACHE retention link length definitions"); + #if SOC_APM_SUPPORTED /* TEE APM Registers Context */ #define N_REGS_TEE_0() (((TEE_M87_MODE_CTRL_REG - TEE_M0_MODE_CTRL_REG) / 4) + 1) @@ -132,11 +146,22 @@ _Static_assert(ARRAY_SIZE(flash_spimem_regs_retention) == SPIMEM_FLASH_RETENTION #define N_REGS_SPI1_S_MEM_2() (((SPI1_MEM_S_TIMING_CALI_REG - SPI1_MEM_S_TIMING_CALI_REG) / 4) + 1) const regdma_entries_config_t psram_spimem_regs_retention[SPIMEM_PSRAM_RETENTION_LINK_LEN] = { - [0] = { .config = REGDMA_LINK_CONTINUOUS_INIT(REGDMA_SPIMEM_LINK(0x00), SPI_MEM_S_CTRL_REG, SPI_MEM_S_CTRL_REG, N_REGS_SPI0_S_MEM_0(), 0, 0), .owner = ENTRY(0) | ENTRY(2) }, /* spi0_mem */ - [1] = { .config = REGDMA_LINK_CONTINUOUS_INIT(REGDMA_SPIMEM_LINK(0x01), SPI_MEM_S_DDR_REG, SPI_MEM_S_DDR_REG, N_REGS_SPI0_S_MEM_1(), 0, 0), .owner = ENTRY(0) | ENTRY(2) }, - [2] = { .config = REGDMA_LINK_CONTINUOUS_INIT(REGDMA_SPIMEM_LINK(0x02), SPI1_MEM_S_CMD_REG, SPI1_MEM_S_CMD_REG, N_REGS_SPI1_S_MEM_0(), 0, 0), .owner = ENTRY(0) | ENTRY(2) }, - [3] = { .config = REGDMA_LINK_CONTINUOUS_INIT(REGDMA_SPIMEM_LINK(0x03), SPI1_MEM_S_DDR_REG, SPI1_MEM_S_DDR_REG, N_REGS_SPI1_S_MEM_1(), 0, 0), .owner = ENTRY(0) | ENTRY(2) }, - [4] = { .config = REGDMA_LINK_CONTINUOUS_INIT(REGDMA_SPIMEM_LINK(0x04), SPI1_MEM_S_TIMING_CALI_REG, SPI1_MEM_S_TIMING_CALI_REG, N_REGS_SPI1_S_MEM_2(), 0, 0), .owner = ENTRY(0) | ENTRY(2) }, + [0] = { .config = REGDMA_LINK_WRITE_INIT(REGDMA_SPIMEM_LINK(0x07), HP_SYS_CLKRST_PSRAM_CTRL0_REG, HP_SYS_CLKRST_REG_PSRAM_CORE_CLK_EN, HP_SYS_CLKRST_REG_PSRAM_CORE_CLK_EN_M, 1, 0), .owner = ENTRY(0) | ENTRY(2) }, // Enable PSRAM mspi core clock on restore + [1] = { .config = REGDMA_LINK_WRITE_INIT(REGDMA_SPIMEM_LINK(0x08), HP_SYS_CLKRST_PSRAM_CTRL0_REG, HP_SYS_CLKRST_REG_PSRAM_SYS_CLK_EN, HP_SYS_CLKRST_REG_PSRAM_SYS_CLK_EN_M, 1, 0), .owner = ENTRY(0) | ENTRY(2) }, // Enable PSRAM mspi core clock on restore + [2] = { .config = REGDMA_LINK_CONTINUOUS_INIT(REGDMA_SPIMEM_LINK(0x09), SPI_MEM_S_CTRL_REG, SPI_MEM_S_CTRL_REG, N_REGS_SPI0_S_MEM_0(), 0, 0), .owner = ENTRY(0) | ENTRY(2) }, /* spi0_mem */ + [3] = { .config = REGDMA_LINK_CONTINUOUS_INIT(REGDMA_SPIMEM_LINK(0x0a), SPI_MEM_S_MISC_REG, SPI_MEM_S_MISC_REG, N_REGS_SPI0_S_MEM_1(), 0, 0), .owner = ENTRY(0) | ENTRY(2) }, + [4] = { .config = REGDMA_LINK_CONTINUOUS_INIT(REGDMA_SPIMEM_LINK(0x0b), SPI_MEM_S_INT_ENA_REG, SPI_MEM_S_INT_ENA_REG, N_REGS_SPI0_S_MEM_2(), 0, 0), .owner = ENTRY(0) | ENTRY(2) }, + [5] = { .config = REGDMA_LINK_CONTINUOUS_INIT(REGDMA_SPIMEM_LINK(0x0c), SPI_MEM_S_ECC_CTRL_REG, SPI_MEM_S_ECC_CTRL_REG, N_REGS_SPI0_S_MEM_3(), 0, 0), .owner = ENTRY(0) | ENTRY(2) }, + [6] = { .config = REGDMA_LINK_CONTINUOUS_INIT(REGDMA_SPIMEM_LINK(0x0d), SPI_MEM_S_MMU_POWER_CTRL_REG, SPI_MEM_S_MMU_POWER_CTRL_REG, N_REGS_SPI0_S_MEM_4(), 0, 0), .owner = ENTRY(0) | ENTRY(2) }, + [7] = { .config = REGDMA_LINK_CONTINUOUS_INIT(REGDMA_SPIMEM_LINK(0x0e), SPI1_MEM_S_CMD_REG, SPI1_MEM_S_CMD_REG, N_REGS_SPI1_S_MEM_0(), 0, 0), .owner = ENTRY(0) | ENTRY(2) }, /* spi1_mem */ + [8] = { .config = REGDMA_LINK_CONTINUOUS_INIT(REGDMA_SPIMEM_LINK(0x0f), SPI1_MEM_S_FLASH_WAITI_CTRL_REG, SPI1_MEM_S_FLASH_WAITI_CTRL_REG, N_REGS_SPI1_S_MEM_1(), 0, 0), .owner = ENTRY(0) | ENTRY(2) }, + [9] = { .config = REGDMA_LINK_CONTINUOUS_INIT(REGDMA_SPIMEM_LINK(0x10), SPI1_MEM_S_INT_ENA_REG, SPI1_MEM_S_INT_ENA_REG, N_REGS_SPI1_S_MEM_2(), 0, 0), .owner = ENTRY(0) | ENTRY(2) }, + [10] = { .config = REGDMA_LINK_CONTINUOUS_INIT(REGDMA_SPIMEM_LINK(0x11), SPI1_MEM_S_TIMING_CALI_REG, SPI1_MEM_S_TIMING_CALI_REG, N_REGS_SPI1_S_MEM_3(), 0, 0), .owner = ENTRY(0) | ENTRY(2) }, + [11] = { .config = REGDMA_LINK_CONTINUOUS_INIT(REGDMA_SPIMEM_LINK(0x12), SPI1_MEM_S_CLOCK_GATE_REG, SPI1_MEM_S_CLOCK_GATE_REG, N_REGS_SPI1_S_MEM_4(), 0, 0), .owner = ENTRY(0) | ENTRY(2) }, + [12] = { .config = REGDMA_LINK_WRITE_INIT(REGDMA_SPIMEM_LINK(0x13), HP_SYS_CLKRST_PSRAM_CTRL0_REG, 0, HP_SYS_CLKRST_REG_PSRAM_CORE_CLK_EN_M, 0, 1), .owner = ENTRY(0) | ENTRY(2) }, // Disable PSRAM mspi core clock on backup + [13] = { .config = REGDMA_LINK_WRITE_INIT(REGDMA_SPIMEM_LINK(0x14), HP_SYS_CLKRST_PSRAM_CTRL0_REG, 0, HP_SYS_CLKRST_REG_PSRAM_SYS_CLK_EN_M, 0, 1), .owner = ENTRY(0) | ENTRY(2) }, // Disable PSRAM mspi core clock on backup + /* PSRAM MSPI IOMUX */ + [14] = { .config = REGDMA_LINK_CONTINUOUS_INIT(REGDMA_SPIMEM_LINK(0x15), IOMUX_MSPI_PIN_PSRAM_D_PIN0_REG, IOMUX_MSPI_PIN_PSRAM_D_PIN0_REG, N_REGS_MSPI1_IOMUX(), 0, 0), .owner = ENTRY(0) | ENTRY(2) }, }; _Static_assert(ARRAY_SIZE(psram_spimem_regs_retention) == SPIMEM_PSRAM_RETENTION_LINK_LEN, "Inconsistent PSRAM SPI Mem retention link length definitions"); diff --git a/examples/lowpower/power_management/README.md b/examples/lowpower/power_management/README.md index 313c27f354a..14e521c079f 100644 --- a/examples/lowpower/power_management/README.md +++ b/examples/lowpower/power_management/README.md @@ -1,5 +1,5 @@ -| Supported Targets | ESP32 | ESP32-C2 | ESP32-C3 | ESP32-C5 | ESP32-C6 | ESP32-C61 | ESP32-H2 | ESP32-H21 | ESP32-H4 | ESP32-P4 | ESP32-S2 | ESP32-S3 | -| ----------------- | ----- | -------- | -------- | -------- | -------- | --------- | -------- | --------- | -------- | -------- | -------- | -------- | +| Supported Targets | ESP32 | ESP32-C2 | ESP32-C3 | ESP32-C5 | ESP32-C6 | ESP32-C61 | ESP32-H2 | ESP32-H21 | ESP32-H4 | ESP32-P4 | ESP32-S2 | ESP32-S3 | ESP32-S31 | +| ----------------- | ----- | -------- | -------- | -------- | -------- | --------- | -------- | --------- | -------- | -------- | -------- | -------- | --------- | # Power Management Example From 7c10126309426055af4f0df889282c320720fc8d Mon Sep 17 00:00:00 2001 From: wuzhenghui Date: Tue, 7 Apr 2026 15:31:30 +0800 Subject: [PATCH 06/13] feat(esp_hw_support): isolate all digital io to suppress pad leakage on TOP pd sleep --- .../src/esp32s31/bootloader_esp32s31.c | 9 + components/esp_driver_gpio/src/gpio.c | 4 +- .../esp32s31/include/hal/gpio_ll.h | 91 +++++++++ .../esp_hal_gpio/include/hal/gpio_types.h | 22 +-- .../esp32s31/include/soc/spi_pins.h | 12 +- .../include/esp_private/esp_sleep_internal.h | 32 ++- components/esp_hw_support/linker.lf | 2 + components/esp_hw_support/sleep_gpio.c | 183 ++++++++++++++---- components/esp_hw_support/sleep_modes.c | 31 ++- .../esp32s31/include/soc/Kconfig.soc_caps.in | 4 + .../soc/esp32s31/include/soc/soc_caps.h | 2 +- components/spi_flash/linker.lf | 4 + 12 files changed, 321 insertions(+), 75 deletions(-) diff --git a/components/bootloader_support/src/esp32s31/bootloader_esp32s31.c b/components/bootloader_support/src/esp32s31/bootloader_esp32s31.c index c197eea497e..c155b74e6cf 100644 --- a/components/bootloader_support/src/esp32s31/bootloader_esp32s31.c +++ b/components/bootloader_support/src/esp32s31/bootloader_esp32s31.c @@ -24,6 +24,7 @@ #include "soc/rtc_wdt_reg.h" #include "hal/rwdt_ll.h" #endif +#include "hal/gpio_ll.h" #include "soc/pmu_reg.h" #include "hal/regi2c_ctrl_ll.h" #include "hal/modem_lpcon_ll.h" @@ -35,6 +36,14 @@ ESP_LOG_ATTR_TAG(TAG, "boot.esp32s31"); static inline void bootloader_hardware_init(void) { + /* GPIO 41 is not bonded out to the package, Isolate it to suppress + * floating leakage.*/ + gpio_ll_input_disable(&GPIO, 41); + gpio_ll_output_disable(&GPIO, 41); + gpio_ll_pullup_dis(&GPIO, 41); + gpio_ll_pulldown_dis(&GPIO, 41); + gpio_ll_func_sel(&GPIO, 41, PIN_FUNC_GPIO); + modem_lpcon_ll_enable_bus_clock(true); #if !CONFIG_IDF_ENV_FPGA diff --git a/components/esp_driver_gpio/src/gpio.c b/components/esp_driver_gpio/src/gpio.c index 51db4a00799..0c0a6723ab1 100644 --- a/components/esp_driver_gpio/src/gpio.c +++ b/components/esp_driver_gpio/src/gpio.c @@ -1143,9 +1143,9 @@ esp_err_t gpio_dump_io_configuration(FILE *out_stream, uint64_t io_bit_mask) fprintf(out_stream, "IO[%"PRIu32"]%s -\n", gpio_num, esp_gpio_is_reserved(BIT64(gpio_num)) ? " **RESERVED**" : ""); fprintf(out_stream, " Pullup: %d, Pulldown: %d, DriveCap: %"PRIu32"\n", io_config.pu, io_config.pd, (uint32_t)io_config.drv); fprintf(out_stream, " InputEn: %d, OutputEn: %s%s, OpenDrain: %d\n", io_config.ie, oe_str, ((io_config.fun_sel == PIN_FUNC_GPIO) && (io_config.oe_inv)) ? " (inversed)" : "", io_config.od); - fprintf(out_stream, " FuncSel: %"PRIu32" (%s)\n", io_config.fun_sel, (io_config.fun_sel == PIN_FUNC_GPIO) ? "GPIO" : "IOMUX"); + fprintf(out_stream, " FuncSel: %"PRIu32" (%s)\n", (uint32_t)io_config.fun_sel, (io_config.fun_sel == PIN_FUNC_GPIO) ? "GPIO" : "IOMUX"); if (io_config.fun_sel == PIN_FUNC_GPIO) { - fprintf(out_stream, " GPIO Matrix SigOut ID: %"PRIu32"%s\n", io_config.sig_out, (io_config.sig_out == SIG_GPIO_OUT_IDX) ? " (simple GPIO output)" : ""); + fprintf(out_stream, " GPIO Matrix SigOut ID: %"PRIu32"%s\n", (uint32_t)io_config.sig_out, (io_config.sig_out == SIG_GPIO_OUT_IDX) ? " (simple GPIO output)" : ""); } if (io_config.ie && io_config.fun_sel == PIN_FUNC_GPIO) { uint32_t cnt = 0; diff --git a/components/esp_hal_gpio/esp32s31/include/hal/gpio_ll.h b/components/esp_hal_gpio/esp32s31/include/hal/gpio_ll.h index 0237313723f..79c97180ac0 100644 --- a/components/esp_hal_gpio/esp32s31/include/hal/gpio_ll.h +++ b/components/esp_hal_gpio/esp32s31/include/hal/gpio_ll.h @@ -57,6 +57,7 @@ extern "C" { * @param gpio_num GPIO number * @param[out] io_config Pointer to the structure that saves the specific IO configuration */ +__attribute__((always_inline)) static inline void gpio_ll_get_io_config(gpio_dev_t *hw, uint32_t gpio_num, gpio_io_config_t *io_config) { uint32_t bit_shift = (gpio_num < 32) ? gpio_num : (gpio_num - 32); @@ -74,6 +75,79 @@ static inline void gpio_ll_get_io_config(gpio_dev_t *hw, uint32_t gpio_num, gpio io_config->slp_sel = IO_MUX.gpio[gpio_num].slp_sel; } +/** + * @brief Get a 64-bit mask of all digital GPIOs that are currently held. + * Reads the two hold control registers once, avoiding per-pin register access. + * + * @param hw Peripheral GPIO hardware instance address (unused, hold regs are in LP_SYS). + * @return Bitmask where bit N is set if GPIO N is held. Only digital IO bits are meaningful. + */ +__attribute__((always_inline)) +static inline uint64_t gpio_ll_get_digital_gpio_hold_mask(gpio_dev_t *hw) +{ + (void)hw; + uint32_t hold0 = LP_SYS.hp_gpio_o_hold_ctrl0.hp_gpio_0_hold_ctrl0; + uint32_t hold1 = LP_SYS.hp_gpio_o_hold_ctrl1.hp_gpio_0_hold_ctrl1; + return ((uint64_t)hold0 << SOC_RTCIO_PIN_COUNT) | + ((uint64_t)hold1 << (32 + SOC_RTCIO_PIN_COUNT)); +} + +/** + * @brief Backup IOMUX pad configuration for sleep isolate. + * Only fills pu, pd, ie, fun_sel in io_config from a single IOMUX register read. + * + * @param gpio_num GPIO number + * @param[out] io_config Pointer to the IO configuration structure + */ +__attribute__((always_inline)) +static inline void gpio_ll_backup_pad_config_for_sleep_isolate(uint32_t gpio_num, gpio_io_config_t *io_config) +{ + uint32_t iomux_reg_val = IO_MUX.gpio[gpio_num].val; + io_config->pu = (iomux_reg_val & FUN_PU_M) >> FUN_PU_S; + io_config->pd = (iomux_reg_val & FUN_PD_M) >> FUN_PD_S; + io_config->ie = (iomux_reg_val & FUN_IE_M) >> FUN_IE_S; + io_config->fun_sel = (iomux_reg_val & MCU_SEL_M) >> MCU_SEL_S; +} + +/** + * @brief Set pad configuration for sleep isolate with minimal register writes. + * Reads pu, pd, ie, oe, fun_sel from io_config and writes them to hardware registers + * in a single IOMUX read-modify-write plus one GPIO enable write. + * + * @param gpio_num GPIO number + * @param io_config Pointer to the IO configuration structure + */ +__attribute__((always_inline)) +static inline void gpio_ll_set_pad_config_for_sleep_isolate(uint32_t gpio_num, gpio_io_config_t *io_config) +{ + uint32_t iomux_reg_val = IO_MUX.gpio[gpio_num].val; + iomux_reg_val &= ~(FUN_PU_M | FUN_PD_M | FUN_IE_M | MCU_SEL_M); + if (io_config->pu) { + iomux_reg_val |= FUN_PU_M; + } + if (io_config->pd) { + iomux_reg_val |= FUN_PD_M; + } + if (io_config->ie) { + iomux_reg_val |= FUN_IE_M; + } + iomux_reg_val |= (io_config->fun_sel << MCU_SEL_S) & MCU_SEL_M; + IO_MUX.gpio[gpio_num].val = iomux_reg_val; + if (io_config->oe) { + if (gpio_num < 32) { + GPIO.enable_w1ts.enable_w1ts = (0x1 << gpio_num); + } else { + GPIO.enable1_w1ts.enable1_w1ts = (0x1 << (gpio_num - 32)); + } + } else { + if (gpio_num < 32) { + GPIO.enable_w1tc.enable_w1tc = (0x1 << gpio_num); + } else { + GPIO.enable1_w1tc.enable1_w1tc = (0x1 << (gpio_num - 32)); + } + } +} + /** * @brief Enable pull-up on GPIO. * @@ -354,6 +428,23 @@ static inline void gpio_ll_output_enable(gpio_dev_t *hw, uint32_t gpio_num) } } +/** + * @brief Check if GPIO output is enabled. + * + * @param hw Peripheral GPIO hardware instance address. + * @param gpio_num GPIO number + * @return true if output is enabled, false otherwise + */ +__attribute__((always_inline)) +static inline bool gpio_ll_output_is_enabled(gpio_dev_t *hw, uint32_t gpio_num) +{ + if (gpio_num < 32) { + return (hw->enable.val >> gpio_num) & 0x1; + } else { + return (hw->enable1.val >> (gpio_num - 32)) & 0x1; + } +} + /** * @brief Disable open-drain mode on GPIO. * diff --git a/components/esp_hal_gpio/include/hal/gpio_types.h b/components/esp_hal_gpio/include/hal/gpio_types.h index f651ba3d796..0b9495e98b5 100644 --- a/components/esp_hal_gpio/include/hal/gpio_types.h +++ b/components/esp_hal_gpio/include/hal/gpio_types.h @@ -157,17 +157,17 @@ typedef enum { * @brief Structure that contains the configuration of an IO */ typedef struct { - uint32_t fun_sel; /*!< Value of IOMUX function selection */ - uint32_t sig_out; /*!< Index of the outputting peripheral signal */ - gpio_drive_cap_t drv; /*!< Value of drive strength */ - bool pu; /*!< Status of pull-up enabled or not */ - bool pd; /*!< Status of pull-down enabled or not */ - bool ie; /*!< Status of input enabled or not */ - bool oe; /*!< Status of output enabled or not */ - bool oe_ctrl_by_periph; /*!< True if use output enable signal from peripheral, otherwise False */ - bool oe_inv; /*!< Whether the output enable signal is inversed or not */ - bool od; /*!< Status of open-drain enabled or not */ - bool slp_sel; /*!< Status of pin sleep mode enabled or not */ + gpio_drive_cap_t drv; /*!< Value of drive strength */ + uint32_t fun_sel : 8; /*!< Value of IOMUX function selection */ + uint32_t sig_out : 16;/*!< Index of the outputting peripheral signal */ + uint32_t pu : 1; /*!< Status of pull-up enabled or not */ + uint32_t pd : 1; /*!< Status of pull-down enabled or not */ + uint32_t ie : 1; /*!< Status of input enabled or not */ + uint32_t oe : 1; /*!< Status of output enabled or not */ + uint32_t oe_ctrl_by_periph : 1; /*!< True if use output enable signal from peripheral, otherwise False */ + uint32_t oe_inv : 1; /*!< Whether the output enable signal is inversed or not */ + uint32_t od : 1; /*!< Status of open-drain enabled or not */ + uint32_t slp_sel : 1; /*!< Status of pin sleep mode enabled or not */ } gpio_io_config_t; #ifdef __cplusplus diff --git a/components/esp_hal_gpspi/esp32s31/include/soc/spi_pins.h b/components/esp_hal_gpspi/esp32s31/include/soc/spi_pins.h index a5cc2c152e8..5b53ab0749b 100644 --- a/components/esp_hal_gpspi/esp32s31/include/soc/spi_pins.h +++ b/components/esp_hal_gpspi/esp32s31/include/soc/spi_pins.h @@ -10,12 +10,12 @@ // On S31, SPI pins defined here are all wrong. these pins are individual pins, don't use normal GPIO pins anymore. #define GPIO_NUM_INVALID -1 #define MSPI_IOMUX_PIN_NUM_CS1 GPIO_NUM_INVALID -#define MSPI_IOMUX_PIN_NUM_HD GPIO_NUM_INVALID -#define MSPI_IOMUX_PIN_NUM_WP GPIO_NUM_INVALID -#define MSPI_IOMUX_PIN_NUM_CS0 GPIO_NUM_INVALID -#define MSPI_IOMUX_PIN_NUM_CLK GPIO_NUM_INVALID -#define MSPI_IOMUX_PIN_NUM_MISO GPIO_NUM_INVALID -#define MSPI_IOMUX_PIN_NUM_MOSI GPIO_NUM_INVALID +#define MSPI_IOMUX_PIN_NUM_HD 30 +#define MSPI_IOMUX_PIN_NUM_WP 28 +#define MSPI_IOMUX_PIN_NUM_CS0 26 +#define MSPI_IOMUX_PIN_NUM_CLK 31 +#define MSPI_IOMUX_PIN_NUM_MISO 27 +#define MSPI_IOMUX_PIN_NUM_MOSI 32 #define MSPI_IOMUX_PIN_NUM_D4 GPIO_NUM_INVALID #define MSPI_IOMUX_PIN_NUM_D5 GPIO_NUM_INVALID #define MSPI_IOMUX_PIN_NUM_D6 GPIO_NUM_INVALID 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 c2257f4c566..93a6733860a 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 @@ -97,14 +97,36 @@ esp_err_t esp_sleep_acquire_lp_use_xtal(void); esp_err_t esp_sleep_release_lp_use_xtal(void); #endif -#if !SOC_GPIO_SUPPORT_HOLD_SINGLE_IO_IN_DSLP +#if !SOC_GPIO_SUPPORT_HOLD_SINGLE_IO_IN_DSLP || SOC_GPIO_NEED_SOFT_ISOLATE_DURING_PD /** - * @brief Isolate all digital IOs except those that are held during deep sleep + * @brief Soft-isolate valid digital IO pads (SOC_GPIO_VALID_DIGITAL_IO_PAD_MASK) for leakage control * - * Reduce digital IOs current leakage during deep sleep. + * Skips pads that are digitally held and pads reserved by the driver. + * MSPI signal pads are not in this pass; use esp_sleep_isolate_mspi_gpio() after cache/MSPI idle. + * + * @param do_backup If true, back up each pad's pu/pd/ie/oe/fun_sel before isolating so that + * esp_sleep_restore_isolated_digital_gpio() can restore them later. + * Pass false when restore is not needed (e.g. deep sleep). */ -void esp_sleep_isolate_digital_gpio(void); -#endif +void esp_sleep_isolate_digital_gpio(bool do_backup); + +/** + * @brief Backup and isolate (or pull up) the five base MSPI lines (CLK/Q/D/HD/WP) + * + * If CONFIG_ESP_SLEEP_MSPI_NEED_ALL_IO_PU && !SOC_MSPI_HAS_INDEPENT_IOMUX, enables pull-up only; + * otherwise full pad isolate like esp_sleep_isolate_digital_gpio. Intended after SPI bus is idle + * (e.g. light sleep with TOP power-down). + */ +void esp_sleep_isolate_mspi_gpio(void); + +/** + * @brief Restore pu/pd/ie/oe and IOMUX fun_sel for every GPIO in the soft-isolate backup bitmap + * + * Reverses esp_sleep_isolate_digital_gpio / esp_sleep_isolate_mspi_gpio. Call after wake before + * normal Flash access. + */ +void esp_sleep_restore_isolated_digital_gpio(void); +#endif // !SOC_GPIO_SUPPORT_HOLD_SINGLE_IO_IN_DSLP || SOC_GPIO_NEED_SOFT_ISOLATE_DURING_PD #if SOC_PM_SUPPORT_PMU_CLK_ICG /** diff --git a/components/esp_hw_support/linker.lf b/components/esp_hw_support/linker.lf index 6c091ac5f5e..b4aaaadc6e0 100644 --- a/components/esp_hw_support/linker.lf +++ b/components/esp_hw_support/linker.lf @@ -85,6 +85,8 @@ entries: sleep_modes: esp_sleep_enable_vbat_under_volt_wakeup (noflash) sleep_modes: esp_sleep_sub_mode_config (noflash) sleep_modes: esp_sleep_sub_mode_force_disable (noflash) + if (SOC_GPIO_SUPPORT_HOLD_SINGLE_IO_IN_DSLP = n || SOC_GPIO_NEED_SOFT_ISOLATE_DURING_PD = y): + esp_gpio_reserve (noflash) [mapping:hal_gpio_pm] archive: libesp_hal_gpio.a diff --git a/components/esp_hw_support/sleep_gpio.c b/components/esp_hw_support/sleep_gpio.c index b609eae148d..ecf5aa936c8 100644 --- a/components/esp_hw_support/sleep_gpio.c +++ b/components/esp_hw_support/sleep_gpio.c @@ -5,6 +5,7 @@ */ #include +#include #include #include #include @@ -27,6 +28,7 @@ #include "hal/rtc_hal.h" +#include "esp_private/esp_gpio_reserve.h" #include "esp_private/gpio.h" #include "esp_private/sleep_gpio.h" #include "esp_private/spi_flash_os.h" @@ -173,17 +175,99 @@ void esp_sleep_enable_gpio_switch(bool enable) } #endif -#if !SOC_GPIO_SUPPORT_HOLD_SINGLE_IO_IN_DSLP -IRAM_ATTR void esp_sleep_isolate_digital_gpio(void) +#if !SOC_GPIO_SUPPORT_HOLD_SINGLE_IO_IN_DSLP || SOC_GPIO_NEED_SOFT_ISOLATE_DURING_PD +IRAM_ATTR static void sleep_gpio_isolate_one_pin(gpio_num_t gpio_num) { - gpio_hal_context_t gpio_hal = { - .dev = GPIO_HAL_GET_HW(GPIO_PORT_0) - }; +#if SOC_GPIO_NEED_SOFT_ISOLATE_DURING_PD + gpio_io_config_t isolate_config = { .pu = 0, .pd = 0, .ie = 0, .oe = 0, .fun_sel = PIN_FUNC_GPIO }; + gpio_ll_set_pad_config_for_sleep_isolate(gpio_num, &isolate_config); +#else + gpio_hal_context_t gpio_hal = { .dev = GPIO_HAL_GET_HW(GPIO_PORT_0) }; + gpio_hal_input_disable(&gpio_hal, gpio_num); + gpio_hal_output_disable(&gpio_hal, gpio_num); + gpio_hal_pullup_dis(&gpio_hal, gpio_num); + gpio_hal_pulldown_dis(&gpio_hal, gpio_num); + gpio_hal_func_sel(&gpio_hal, gpio_num, PIN_FUNC_GPIO); +#endif +} +#if SOC_GPIO_NEED_SOFT_ISOLATE_DURING_PD +typedef struct { + uint64_t backuped; + uint64_t pu; + uint64_t pd; + uint64_t ie; + uint64_t oe; + uint8_t fun_sel[GPIO_NUM_MAX]; +} gpio_isolate_backup_t; + +static DRAM_ATTR gpio_isolate_backup_t s_gpio_isolate_backup; + +IRAM_ATTR static void sleep_gpio_backup_one_pin(gpio_num_t gpio_num) +{ + gpio_hal_context_t gpio_hal = { .dev = GPIO_HAL_GET_HW(GPIO_PORT_0) }; + gpio_io_config_t io_config; + gpio_ll_backup_pad_config_for_sleep_isolate(gpio_num, &io_config); + if (io_config.pu) { + s_gpio_isolate_backup.pu |= (1ULL << gpio_num); + } + if (io_config.pd) { + s_gpio_isolate_backup.pd |= (1ULL << gpio_num); + } + if (io_config.ie) { + s_gpio_isolate_backup.ie |= (1ULL << gpio_num); + } + if (gpio_ll_output_is_enabled(gpio_hal.dev, gpio_num)) { + s_gpio_isolate_backup.oe |= (1ULL << gpio_num); + } + s_gpio_isolate_backup.fun_sel[gpio_num] = (uint8_t)io_config.fun_sel; + s_gpio_isolate_backup.backuped |= (1ULL << gpio_num); +} + +IRAM_ATTR void esp_sleep_isolate_mspi_gpio(void) +{ + DRAM_ATTR static const esp_mspi_io_t s_mspi_pins[] = { + ESP_MSPI_IO_CLK, ESP_MSPI_IO_Q, ESP_MSPI_IO_D, ESP_MSPI_IO_HD, ESP_MSPI_IO_WP + }; + for (size_t i = 0; i < sizeof(s_mspi_pins) / sizeof(s_mspi_pins[0]); i++) { + gpio_num_t gpio_num = (gpio_num_t)esp_mspi_get_io(s_mspi_pins[i]); + sleep_gpio_backup_one_pin(gpio_num); +#if CONFIG_ESP_SLEEP_MSPI_NEED_ALL_IO_PU && !SOC_MSPI_HAS_INDEPENT_IOMUX + gpio_hal_context_t gpio_hal = { .dev = GPIO_HAL_GET_HW(GPIO_PORT_0) }; + gpio_hal_pullup_en(&gpio_hal, gpio_num); +#else + sleep_gpio_isolate_one_pin(gpio_num); +#endif + } +} + +IRAM_ATTR void esp_sleep_restore_isolated_digital_gpio(void) +{ + uint64_t backuped = s_gpio_isolate_backup.backuped; + while (backuped) { + gpio_num_t gpio_num = (gpio_num_t)__builtin_ctzll(backuped); + gpio_io_config_t io_config = { + .pu = (s_gpio_isolate_backup.pu >> gpio_num) & 0x1, + .pd = (s_gpio_isolate_backup.pd >> gpio_num) & 0x1, + .ie = (s_gpio_isolate_backup.ie >> gpio_num) & 0x1, + .oe = (s_gpio_isolate_backup.oe >> gpio_num) & 0x1, + .fun_sel = (uint32_t)s_gpio_isolate_backup.fun_sel[gpio_num], + }; + gpio_ll_set_pad_config_for_sleep_isolate(gpio_num, &io_config); + backuped &= backuped - 1; + } +} +#endif // SOC_GPIO_NEED_SOFT_ISOLATE_DURING_PD + +IRAM_ATTR void esp_sleep_isolate_digital_gpio(bool do_backup) +{ + gpio_hal_context_t gpio_hal = { .dev = GPIO_HAL_GET_HW(GPIO_PORT_0) }; +#if !SOC_GPIO_SUPPORT_HOLD_SINGLE_IO_IN_DSLP /* no need to do isolate if digital IOs are not being held in deep sleep */ if (!gpio_hal_deep_sleep_hold_is_en(&gpio_hal)) { return; } +#endif /** * there is a situation where we cannot isolate digital IO before deep sleep: @@ -193,42 +277,61 @@ IRAM_ATTR void esp_sleep_isolate_digital_gpio(void) * the bottom current of deep sleep will be higher than light sleep, and there is no * reason to use deep sleep at this time. */ - assert(esp_ptr_internal(&gpio_hal) && "If hold digital IO, the stack of the task calling esp_deep_sleep_start must be in internal ram!"); + assert(esp_ptr_internal(esp_cpu_get_sp()) && "If hold digital IO, the stack of the task calling esp_deep_sleep_start must be in internal ram!"); - /* isolate digital IO that is not held(keep the configuration of digital IOs held by users) */ - for (gpio_num_t gpio_num = GPIO_NUM_0; gpio_num < GPIO_NUM_MAX; gpio_num++) { - if (GPIO_IS_VALID_DIGITAL_IO_PAD(gpio_num) && !gpio_hal_is_digital_io_hold(&gpio_hal, gpio_num)) { - - bool is_mspi_io_pad = false; - esp_mspi_io_t mspi_ios[] = { ESP_MSPI_IO_CS0, ESP_MSPI_IO_CLK, ESP_MSPI_IO_Q, ESP_MSPI_IO_D, ESP_MSPI_IO_HD, ESP_MSPI_IO_WP }; - for (int i = 0; i < sizeof(mspi_ios) / sizeof(mspi_ios[0]); i++) { - if (esp_mspi_get_io(mspi_ios[i]) == gpio_num) { - is_mspi_io_pad = true; - break; - } - } - // Ignore MSPI and default Console UART io pads, When the CPU executes - // the following instructions to configure the MSPI IO PAD, access on - // the MSPI signal lines (as CPU instruction execution and MSPI access - // operations are asynchronous) may cause the SoC to hang. - if (is_mspi_io_pad || gpio_num == U0RXD_GPIO_NUM || gpio_num == U0TXD_GPIO_NUM) { - continue; - } - - /* disable I/O */ - gpio_hal_input_disable(&gpio_hal, gpio_num); - gpio_hal_output_disable(&gpio_hal, gpio_num); - - /* disable pull up/down */ - gpio_hal_pullup_dis(&gpio_hal, gpio_num); - gpio_hal_pulldown_dis(&gpio_hal, gpio_num); - - /* make pad work as gpio(otherwise, deep sleep bottom current will rise) */ - gpio_hal_func_sel(&gpio_hal, gpio_num, PIN_FUNC_GPIO); - } + DRAM_ATTR static volatile uint64_t s_pad_mask = SOC_GPIO_VALID_DIGITAL_IO_PAD_MASK; + uint64_t pad_mask = s_pad_mask; +#if SOC_GPIO_NEED_SOFT_ISOLATE_DURING_PD + if (do_backup) { + s_gpio_isolate_backup.backuped = 0; + s_gpio_isolate_backup.pu = 0; + s_gpio_isolate_backup.pd = 0; + s_gpio_isolate_backup.ie = 0; + s_gpio_isolate_backup.oe = 0; } + + uint64_t hold_mask = gpio_ll_get_digital_gpio_hold_mask(gpio_hal.dev); + gpio_io_config_t isolate_config = { .pu = 0, .pd = 0, .ie = 0, .oe = 0, .fun_sel = PIN_FUNC_GPIO }; + + while (pad_mask) { + gpio_num_t gpio_num = (gpio_num_t)__builtin_ctzll(pad_mask); + if (!(hold_mask & (1ULL << gpio_num)) && + !esp_gpio_is_reserved(BIT64(gpio_num))) { + if (do_backup) { + gpio_io_config_t io_config; + gpio_ll_backup_pad_config_for_sleep_isolate(gpio_num, &io_config); + if (io_config.pu) { + s_gpio_isolate_backup.pu |= (1ULL << gpio_num); + } + if (io_config.pd) { + s_gpio_isolate_backup.pd |= (1ULL << gpio_num); + } + if (io_config.ie) { + s_gpio_isolate_backup.ie |= (1ULL << gpio_num); + } + if (gpio_ll_output_is_enabled(gpio_hal.dev, gpio_num)) { + s_gpio_isolate_backup.oe |= (1ULL << gpio_num); + } + s_gpio_isolate_backup.fun_sel[gpio_num] = (uint8_t)io_config.fun_sel; + s_gpio_isolate_backup.backuped |= (1ULL << gpio_num); + } + gpio_ll_set_pad_config_for_sleep_isolate(gpio_num, &isolate_config); + } + pad_mask &= pad_mask - 1; + } +#else + (void)do_backup; + while (pad_mask) { + gpio_num_t gpio_num = (gpio_num_t)__builtin_ctzll(pad_mask); + if (!gpio_hal_is_digital_io_hold(&gpio_hal, gpio_num) && + !esp_gpio_is_reserved(BIT64(gpio_num))) { + sleep_gpio_isolate_one_pin(gpio_num); + } + pad_mask &= pad_mask - 1; + } +#endif } -#endif //!SOC_GPIO_SUPPORT_HOLD_SINGLE_IO_IN_DSLP +#endif //!SOC_GPIO_SUPPORT_HOLD_SINGLE_IO_IN_DSLP || SOC_GPIO_NEED_SOFT_ISOLATE_DURING_PD #if SOC_DEEP_SLEEP_SUPPORTED static void esp_deep_sleep_wakeup_io_reset(void) @@ -250,10 +353,8 @@ static void esp_deep_sleep_wakeup_io_reset(void) #endif #if SOC_GPIO_SUPPORT_HP_PERIPH_PD_SLEEP_WAKEUP + gpio_hal_context_t gpio_hal = { .dev = GPIO_HAL_GET_HW(GPIO_PORT_0) }; uint64_t dslp_io_mask = SOC_GPIO_HP_PERIPH_PD_SLEEP_WAKEABLE_MASK; - gpio_hal_context_t gpio_hal = { - .dev = GPIO_HAL_GET_HW(GPIO_PORT_0) - }; while (dslp_io_mask) { int gpio_num = __builtin_ctzll(dslp_io_mask); bool wakeup_io_enabled = gpio_hal_wakeup_is_enabled_on_hp_periph_powerdown_sleep(&gpio_hal, gpio_num); diff --git a/components/esp_hw_support/sleep_modes.c b/components/esp_hw_support/sleep_modes.c index 6b2f04bead4..18e618ed59c 100644 --- a/components/esp_hw_support/sleep_modes.c +++ b/components/esp_hw_support/sleep_modes.c @@ -251,7 +251,7 @@ #define LDO_POWER_TAKEOVER_PREPARATION_TIME_US (185) #elif CONFIG_IDF_TARGET_ESP32S31 #define DEFAULT_SLEEP_OUT_OVERHEAD_US (324) -#define DEFAULT_HARDWARE_OUT_OVERHEAD_US (240) +#define DEFAULT_HARDWARE_OUT_OVERHEAD_US (780) #endif // Actually costs 80us, using the fastest slow clock 150K calculation takes about 16 ticks @@ -894,16 +894,16 @@ static esp_err_t FORCE_IRAM_ATTR esp_sleep_start_safe(uint32_t sleep_flags, uint } #endif if (deep_sleep) { -#if !SOC_GPIO_SUPPORT_HOLD_SINGLE_IO_IN_DSLP - esp_sleep_isolate_digital_gpio(); +#if !SOC_GPIO_SUPPORT_HOLD_SINGLE_IO_IN_DSLP || SOC_GPIO_NEED_SOFT_ISOLATE_DURING_PD + esp_sleep_isolate_digital_gpio(false); #endif #if CONFIG_IDF_TARGET_ESP32P4 && CONFIG_ESP_SLEEP_SET_FLASH_DPD - if ((sleep_flags & RTC_SLEEP_FLASH_DPD) && (!ESP_CHIP_REV_ABOVE(efuse_hal_chip_revision(), 300))) { - /* Switch Flash from standby mode to deep powerdown mode */ - /* During bootloader phase following wakeup from deepsleep, flash will exit dpd mode */ - spi_flash_enable_deep_power_down_mode(true); - } + if ((sleep_flags & RTC_SLEEP_FLASH_DPD) && (!ESP_CHIP_REV_ABOVE(efuse_hal_chip_revision(), 300))) { + /* Switch Flash from standby mode to deep powerdown mode */ + /* During bootloader phase following wakeup from deepsleep, flash will exit dpd mode */ + spi_flash_enable_deep_power_down_mode(true); + } #endif #if ESP_ROM_SUPPORT_DEEP_SLEEP_WAKEUP_STUB && SOC_DEEP_SLEEP_SUPPORTED @@ -924,7 +924,7 @@ static esp_err_t FORCE_IRAM_ATTR esp_sleep_start_safe(uint32_t sleep_flags, uint #endif // Enter Deep Sleep -#if!ESP_ROM_SUPPORT_DEEP_SLEEP_WAKEUP_STUB || SOC_PM_SUPPORT_DEEPSLEEP_CHECK_STUB_ONLY || !CONFIG_ESP_SYSTEM_ALLOW_RTC_FAST_MEM_AS_HEAP +#if !ESP_ROM_SUPPORT_DEEP_SLEEP_WAKEUP_STUB || SOC_PM_SUPPORT_DEEPSLEEP_CHECK_STUB_ONLY || !CONFIG_ESP_SYSTEM_ALLOW_RTC_FAST_MEM_AS_HEAP #if SOC_PMU_SUPPORTED result = call_rtc_sleep_start(reject_triggers, config->power.hp_sys.dig_power.mem_dslp, deep_sleep); #else @@ -935,6 +935,11 @@ static esp_err_t FORCE_IRAM_ATTR esp_sleep_start_safe(uint32_t sleep_flags, uint result = rtc_deep_sleep_start(s_config.wakeup_triggers, reject_triggers); #endif } else { +#if SOC_GPIO_NEED_SOFT_ISOLATE_DURING_PD + if (sleep_flags & RTC_SLEEP_PD_DIG) { + esp_sleep_isolate_digital_gpio(true); + } +#endif /* Cache Suspend 1: will wait cache idle in cache suspend */ suspend_cache(); if (!(sleep_flags & RTC_SLEEP_PD_VDDSDIO)) { @@ -959,6 +964,9 @@ static esp_err_t FORCE_IRAM_ATTR esp_sleep_start_safe(uint32_t sleep_flags, uint /* Cache suspend also means SPI bus IDLE, then we can hold SPI CS pin safely */ gpio_ll_hold_en(&GPIO, MSPI_IOMUX_PIN_NUM_CS1); #endif +#if SOC_GPIO_NEED_SOFT_ISOLATE_DURING_PD + esp_sleep_isolate_mspi_gpio(); +#endif #endif // !SOC_MSPI_HAS_INDEPENT_IOMUX } #endif @@ -1018,6 +1026,11 @@ static esp_err_t FORCE_IRAM_ATTR esp_sleep_start_safe(uint32_t sleep_flags, uint #endif // !SOC_MSPI_HAS_INDEPENT_IOMUX } #endif +#if SOC_GPIO_NEED_SOFT_ISOLATE_DURING_PD + if (sleep_flags & RTC_SLEEP_PD_DIG) { + esp_sleep_restore_isolated_digital_gpio(); + } +#endif #if CONFIG_ESP_SLEEP_SET_FLASH_DPD if (sleep_flags & RTC_SLEEP_FLASH_DPD) { //Release Flash out from deep powerdown mode diff --git a/components/soc/esp32s31/include/soc/Kconfig.soc_caps.in b/components/soc/esp32s31/include/soc/Kconfig.soc_caps.in index d9b36b66cd9..e25b0b4cb0a 100644 --- a/components/soc/esp32s31/include/soc/Kconfig.soc_caps.in +++ b/components/soc/esp32s31/include/soc/Kconfig.soc_caps.in @@ -463,6 +463,10 @@ config SOC_GPIO_SUPPORT_HP_PERIPH_PD_SLEEP_WAKEUP bool default y +config SOC_GPIO_NEED_SOFT_ISOLATE_DURING_PD + bool + default y + config SOC_LP_IO_HAS_INDEPENDENT_WAKEUP_SOURCE bool default y diff --git a/components/soc/esp32s31/include/soc/soc_caps.h b/components/soc/esp32s31/include/soc/soc_caps.h index decdf740282..58c762bdb20 100644 --- a/components/soc/esp32s31/include/soc/soc_caps.h +++ b/components/soc/esp32s31/include/soc/soc_caps.h @@ -187,7 +187,7 @@ // GPIO0~7 on ESP32S31 can support chip deep sleep wakeup #define SOC_GPIO_SUPPORT_HP_PERIPH_PD_SLEEP_WAKEUP (1) -#define SOC_GPIO_SUPPORT_DEEPSLEEP_WAKEUP SOC_GPIO_SUPPORT_HP_PERIPH_PD_SLEEP_WAKEUP +#define SOC_GPIO_NEED_SOFT_ISOLATE_DURING_PD (1) #define SOC_LP_IO_HAS_INDEPENDENT_WAKEUP_SOURCE (1) // LP IO peripherals have independent clock gating to manage diff --git a/components/spi_flash/linker.lf b/components/spi_flash/linker.lf index d4c7308acb7..f78ffbbedb0 100644 --- a/components/spi_flash/linker.lf +++ b/components/spi_flash/linker.lf @@ -82,6 +82,10 @@ entries: if ESP_SLEEP_SET_FLASH_DPD = y: spi_flash_dpd_enable (noflash) + if SOC_GPIO_NEED_SOFT_ISOLATE_DURING_PD = y: + flash_ops: esp_mspi_get_io (noflash) + flash_ops: s_mspi_io_num_default (noflash) + [mapping:spi_flash_hal] archive: libesp_hal_mspi.a entries: From 4df0db5201da44f71c0e79449bf576f1979cbf80 Mon Sep 17 00:00:00 2001 From: wuzhenghui Date: Mon, 13 Apr 2026 17:40:04 +0800 Subject: [PATCH 07/13] change(esp_hw_support): GPIO wakeup source is not avaliable if HP periph is off on s31 --- components/soc/esp32s31/include/soc/Kconfig.soc_caps.in | 8 -------- components/soc/esp32s31/include/soc/soc_caps.h | 4 ---- 2 files changed, 12 deletions(-) diff --git a/components/soc/esp32s31/include/soc/Kconfig.soc_caps.in b/components/soc/esp32s31/include/soc/Kconfig.soc_caps.in index e25b0b4cb0a..2399a115845 100644 --- a/components/soc/esp32s31/include/soc/Kconfig.soc_caps.in +++ b/components/soc/esp32s31/include/soc/Kconfig.soc_caps.in @@ -459,10 +459,6 @@ config SOC_GPIO_SUPPORT_ETM bool default y -config SOC_GPIO_SUPPORT_HP_PERIPH_PD_SLEEP_WAKEUP - bool - default y - config SOC_GPIO_NEED_SOFT_ISOLATE_DURING_PD bool default y @@ -483,10 +479,6 @@ config SOC_GPIO_OUT_RANGE_MAX int default 61 -config SOC_GPIO_HP_PERIPH_PD_SLEEP_WAKEABLE_MASK - int - default 0 - config SOC_GPIO_SUPPORT_FORCE_HOLD bool default y diff --git a/components/soc/esp32s31/include/soc/soc_caps.h b/components/soc/esp32s31/include/soc/soc_caps.h index 58c762bdb20..ee6401d6a26 100644 --- a/components/soc/esp32s31/include/soc/soc_caps.h +++ b/components/soc/esp32s31/include/soc/soc_caps.h @@ -185,8 +185,6 @@ // GPIO peripheral has the ETM extension #define SOC_GPIO_SUPPORT_ETM 1 -// GPIO0~7 on ESP32S31 can support chip deep sleep wakeup -#define SOC_GPIO_SUPPORT_HP_PERIPH_PD_SLEEP_WAKEUP (1) #define SOC_GPIO_NEED_SOFT_ISOLATE_DURING_PD (1) #define SOC_LP_IO_HAS_INDEPENDENT_WAKEUP_SOURCE (1) @@ -199,8 +197,6 @@ #define SOC_GPIO_IN_RANGE_MAX 61 #define SOC_GPIO_OUT_RANGE_MAX 61 -#define SOC_GPIO_HP_PERIPH_PD_SLEEP_WAKEABLE_MASK (0ULL | 0xFF) - // digital I/O pad powered by VDD3V3_CPU or VDD_SPI(GPIO_NUM_8~GPIO_NUM_61, excluding GPIO29/41 which are not bonded) #define SOC_GPIO_VALID_DIGITAL_IO_PAD_MASK (SOC_GPIO_VALID_GPIO_MASK & ~((1ULL << SOC_RTCIO_PIN_COUNT) - 1)) From 3f6957450f9ad64a58f071a5ea17f5d9ef2932e3 Mon Sep 17 00:00:00 2001 From: wuzhenghui Date: Mon, 27 Apr 2026 20:11:59 +0800 Subject: [PATCH 08/13] feat(esp_hw_support): support psram PD_TOP retention --- .../esp32s31/include/hal/clk_tree_ll.h | 2 + .../esp32p4/include/hal/mspi_ll.h | 4 +- .../esp32s31/include/hal/mspi_ll.h | 40 ++++++++++++++++++ components/esp_hw_support/Kconfig | 2 +- .../esp_hw_support/port/esp32p4/pmu_sleep.c | 4 +- .../esp_hw_support/port/esp32s31/pmu_param.c | 4 +- .../esp_hw_support/port/esp32s31/pmu_sleep.c | 42 +++++++++++++++++++ .../port/esp32s31/private_include/pmu_param.h | 4 +- .../esp_pm/test_apps/.build-test-rules.yml | 4 +- components/esp_pm/test_apps/esp_pm/README.md | 4 +- .../esp_pm/test_apps/esp_pm/pytest_esp_pm.py | 1 - .../esp_system_unity_tests/main/test_sleep.c | 2 +- .../esp32s31/include/soc/Kconfig.soc_caps.in | 4 ++ .../soc/esp32s31/include/soc/soc_caps.h | 1 + .../include/soc/system_periph_retention.h | 4 +- .../soc/esp32s31/system_retention_periph.c | 28 ++++++------- 16 files changed, 119 insertions(+), 31 deletions(-) diff --git a/components/esp_hal_clock/esp32s31/include/hal/clk_tree_ll.h b/components/esp_hal_clock/esp32s31/include/hal/clk_tree_ll.h index e01cb2b81bf..eaa36ca3a0f 100644 --- a/components/esp_hal_clock/esp32s31/include/hal/clk_tree_ll.h +++ b/components/esp_hal_clock/esp32s31/include/hal/clk_tree_ll.h @@ -153,6 +153,7 @@ static inline __attribute__((always_inline)) void clk_ll_mpll_enable(void) SET_PERI_REG_MASK(PMU_IMM_HP_CK_POWER_1_REG, PMU_TIE_HIGH_GLOBAL_MPLL_ICG) ; SET_PERI_REG_MASK(PMU_IMM_HP_CK_POWER_1_REG, PMU_TIE_HIGH_XPD_MPLL | PMU_TIE_HIGH_XPD_MPLL_I2C); SET_PERI_REG_MASK(HP_ALIVE_SYS_HP_CLK_CTRL_REG, HP_ALIVE_SYS_HP_MPLL_500M_CLK_EN); + SET_PERI_REG_MASK(PMU_HP_ACTIVE_HP_CK_POWER_REG, PMU_HP_ACTIVE_XPD_MPLL_I2C | PMU_HP_ACTIVE_XPD_MPLL); } /** @@ -163,6 +164,7 @@ static inline __attribute__((always_inline)) void clk_ll_mpll_disable(void) SET_PERI_REG_MASK(PMU_IMM_HP_CK_POWER_1_REG, PMU_TIE_LOW_GLOBAL_MPLL_ICG) ; SET_PERI_REG_MASK(PMU_IMM_HP_CK_POWER_1_REG, PMU_TIE_LOW_XPD_MPLL | PMU_TIE_LOW_XPD_MPLL_I2C); CLEAR_PERI_REG_MASK(HP_ALIVE_SYS_HP_CLK_CTRL_REG, HP_ALIVE_SYS_HP_MPLL_500M_CLK_EN); + CLEAR_PERI_REG_MASK(PMU_HP_ACTIVE_HP_CK_POWER_REG, PMU_HP_ACTIVE_XPD_MPLL_I2C | PMU_HP_ACTIVE_XPD_MPLL); } /** diff --git a/components/esp_hal_mspi/esp32p4/include/hal/mspi_ll.h b/components/esp_hal_mspi/esp32p4/include/hal/mspi_ll.h index 7e87504bf2d..03e37cfe96d 100644 --- a/components/esp_hal_mspi/esp32p4/include/hal/mspi_ll.h +++ b/components/esp_hal_mspi/esp32p4/include/hal/mspi_ll.h @@ -802,7 +802,7 @@ static inline void mspi_ll_unhold_all_flash_pins(void) * Sets all PSRAM pins (pin_group0, dqs0, pin_group1, dqs1) to hold status */ __attribute__((always_inline)) -static inline void mspi_ll_hold_all_psram_pins(void) +static inline void mspi_ll_psram_hold_all_pins(void) { #if HAL_CONFIG(CHIP_SUPPORT_MIN_REV) >= 300 // Only rev3+ chips support mspi pad holding. LP_SYS.ded_pad_rtc_hold_ctrl.ded_pad_rtc_hold_ctrl |= 0x3FFFFC0; @@ -814,7 +814,7 @@ static inline void mspi_ll_hold_all_psram_pins(void) * Releases hold status for all PSRAM pins (pin_group0, dqs0, pin_group1, dqs1) */ __attribute__((always_inline)) -static inline void mspi_ll_unhold_all_psram_pins(void) +static inline void mspi_ll_psram_unhold_all_pins(void) { #if HAL_CONFIG(CHIP_SUPPORT_MIN_REV) >= 300 // Only rev3+ chips support mspi pad holding. LP_SYS.ded_pad_rtc_hold_ctrl.ded_pad_rtc_hold_ctrl &= ~0x3FFFFC0; diff --git a/components/esp_hal_mspi/esp32s31/include/hal/mspi_ll.h b/components/esp_hal_mspi/esp32s31/include/hal/mspi_ll.h index 0cf056203fb..d58f5f8fad5 100644 --- a/components/esp_hal_mspi/esp32s31/include/hal/mspi_ll.h +++ b/components/esp_hal_mspi/esp32s31/include/hal/mspi_ll.h @@ -304,6 +304,46 @@ static inline void mspi_ll_psram_enable_axi_access(uint8_t spi_num, bool enable) SPIMEM2.mem_cache_fctrl.close_axi_inf_en = !enable; } +/** + * @brief Hold all PSRAM pins + * Sets all PSRAM pins (pin_group0, dqs0, pin_group1, dqs1) to hold status + */ +__attribute__((always_inline)) +static inline void mspi_ll_psram_hold_all_pins(void) +{ + MSPI_IOMUX.psram_pin_group.pin_group0[0].reg_psram_pin_hold = 1; + MSPI_IOMUX.psram_pin_group.pin_group0[1].reg_psram_pin_hold = 1; + MSPI_IOMUX.psram_pin_group.pin_group0[2].reg_psram_pin_hold = 1; + MSPI_IOMUX.psram_pin_group.pin_group0[3].reg_psram_pin_hold = 1; + MSPI_IOMUX.psram_pin_group.pin_group0[4].reg_psram_pin_hold = 1; + MSPI_IOMUX.psram_pin_group.pin_group0[5].reg_psram_pin_hold = 1; + MSPI_IOMUX.psram_pin_group.pin_group0[6].reg_psram_pin_hold = 1; + MSPI_IOMUX.psram_pin_group.pin_group0[7].reg_psram_pin_hold = 1; + MSPI_IOMUX.psram_pin_group.dqs0.reg_psram_dqs_hold = 1; + MSPI_IOMUX.psram_pin_group.pin_clk.reg_psram_pin_hold = 1; + MSPI_IOMUX.psram_pin_group.pin_cs.reg_psram_pin_hold = 1; +} + +/** + * @brief Unhold all PSRAM pins + * Releases hold status for all PSRAM pins (pin_group0, dqs0, pin_group1, dqs1) + */ +__attribute__((always_inline)) +static inline void mspi_ll_psram_unhold_all_pins(void) +{ + MSPI_IOMUX.psram_pin_group.pin_group0[0].reg_psram_pin_hold = 0; + MSPI_IOMUX.psram_pin_group.pin_group0[1].reg_psram_pin_hold = 0; + MSPI_IOMUX.psram_pin_group.pin_group0[2].reg_psram_pin_hold = 0; + MSPI_IOMUX.psram_pin_group.pin_group0[3].reg_psram_pin_hold = 0; + MSPI_IOMUX.psram_pin_group.pin_group0[4].reg_psram_pin_hold = 0; + MSPI_IOMUX.psram_pin_group.pin_group0[5].reg_psram_pin_hold = 0; + MSPI_IOMUX.psram_pin_group.pin_group0[6].reg_psram_pin_hold = 0; + MSPI_IOMUX.psram_pin_group.pin_group0[7].reg_psram_pin_hold = 0; + MSPI_IOMUX.psram_pin_group.dqs0.reg_psram_dqs_hold = 0; + MSPI_IOMUX.psram_pin_group.pin_clk.reg_psram_pin_hold = 0; + MSPI_IOMUX.psram_pin_group.pin_cs.reg_psram_pin_hold = 0; +} + #ifdef __cplusplus } #endif diff --git a/components/esp_hw_support/Kconfig b/components/esp_hw_support/Kconfig index f6769fd8d09..7cbca55944e 100644 --- a/components/esp_hw_support/Kconfig +++ b/components/esp_hw_support/Kconfig @@ -82,7 +82,7 @@ menu "Hardware Settings" select PM_SLP_IRAM_OPT select ESP_PERIPH_CTRL_FUNC_IN_IRAM select ESP_REGI2C_CTRL_FUNC_IN_IRAM - depends on !SPIRAM || ESP_LDO_RESERVE_PSRAM + depends on !SPIRAM || ESP_LDO_RESERVE_PSRAM || SOC_PSRAM_HAS_DEDICATED_LDO depends on !(IDF_TARGET_ESP32P4 && (ESP32P4_REV_MIN_FULL < 100)) default n help diff --git a/components/esp_hw_support/port/esp32p4/pmu_sleep.c b/components/esp_hw_support/port/esp32p4/pmu_sleep.c index 1d7bca046f5..e8a108d14db 100644 --- a/components/esp_hw_support/port/esp32p4/pmu_sleep.c +++ b/components/esp_hw_support/port/esp32p4/pmu_sleep.c @@ -422,7 +422,7 @@ SPM_IRAM_ATTR uint32_t pmu_sleep_start(uint32_t wakeup_opt, uint32_t reject_opt, #if CONFIG_PM_SLP_SPIRAM_HALFSLEEP_ENABLED esp_psram_impl_enter_halfsleep_mode(); #endif - mspi_ll_hold_all_psram_pins(); + mspi_ll_psram_hold_all_pins(); #endif s_mpll_freq_mhz_before_sleep = rtc_clk_mpll_get_freq(); if (s_mpll_freq_mhz_before_sleep) { @@ -519,7 +519,7 @@ SPM_IRAM_ATTR bool pmu_sleep_finish(bool dslp) #endif } #if CONFIG_SPIRAM - mspi_ll_unhold_all_psram_pins(); + mspi_ll_psram_unhold_all_pins(); #if CONFIG_PM_SLP_SPIRAM_HALFSLEEP_ENABLED esp_psram_impl_exit_halfsleep_mode(); #endif diff --git a/components/esp_hw_support/port/esp32s31/pmu_param.c b/components/esp_hw_support/port/esp32s31/pmu_param.c index 2fc17e5e225..eccb737e869 100644 --- a/components/esp_hw_support/port/esp32s31/pmu_param.c +++ b/components/esp_hw_support/port/esp32s31/pmu_param.c @@ -42,11 +42,11 @@ ESP_HW_LOG_ATTR_TAG(TAG, "pmu_param"); .xpd_cpll_i2c = 1, \ .xpd_bbpll_i2c = 1, \ .xpd_apll_i2c = 0, \ - .xpd_mpll_i2c = 0, \ + .xpd_mpll_i2c = 1, \ .xpd_cpll = 1, \ .xpd_bbpll = 1, \ .xpd_apll = 0, \ - .xpd_mpll = 0, \ + .xpd_mpll = 1, \ }, \ .xtal = { \ .xpd_xtal = 1 \ diff --git a/components/esp_hw_support/port/esp32s31/pmu_sleep.c b/components/esp_hw_support/port/esp32s31/pmu_sleep.c index 29e61e36958..a13585a6bd7 100644 --- a/components/esp_hw_support/port/esp32s31/pmu_sleep.c +++ b/components/esp_hw_support/port/esp32s31/pmu_sleep.c @@ -16,10 +16,21 @@ #include "soc/pmu_struct.h" #include "esp_private/esp_pmu.h" #include "pmu_param.h" +#include "hal/clk_tree_hal.h" #include "hal/lp_aon_hal.h" #include "hal/efuse_ll.h" #include "hal/efuse_hal.h" +#include "hal/mspi_ll.h" #include "esp_hw_log.h" +#if CONFIG_SPIRAM +#include "esp_private/esp_psram_impl.h" +#include "hal/psram_ctrlr_ll.h" +#endif +#if !SOC_APM_SUPPORTED +#include "hal/apm_hal.h" +#endif + +#include "esp_private/rtc_clk.h" ESP_HW_LOG_ATTR_TAG(TAG, "pmu_sleep"); @@ -28,6 +39,7 @@ ESP_HW_LOG_ATTR_TAG(TAG, "pmu_sleep"); static bool s_pmu_sleep_regdma_backup_enabled; +static uint32_t s_mpll_freq_mhz_before_sleep = 0; void pmu_sleep_enable_regdma_backup(void) { @@ -320,6 +332,16 @@ void pmu_sleep_init(const pmu_sleep_config_t *config, bool dslp) IRAM_ATTR uint32_t pmu_sleep_start(uint32_t wakeup_opt, uint32_t reject_opt, uint32_t lslp_mem_inf_fpu, bool dslp) { + if (!dslp) { +#if CONFIG_SPIRAM + psram_ctrlr_ll_wait_all_transaction_done(); +#if CONFIG_PM_SLP_SPIRAM_HALFSLEEP_ENABLED + esp_psram_impl_enter_halfsleep_mode(); +#endif + mspi_ll_psram_hold_all_pins(); +#endif + s_mpll_freq_mhz_before_sleep = rtc_clk_mpll_get_freq(); + } lp_aon_hal_inform_wakeup_type(dslp); pmu_ll_hp_set_wakeup_enable(&PMU, wakeup_opt); @@ -354,6 +376,26 @@ IRAM_ATTR bool pmu_sleep_finish(bool dslp) // Wait eFuse memory update done. while(efuse_ll_get_controller_state() != EFUSE_CONTROLLER_STATE_IDLE); #endif + + if (!dslp) { + if (s_mpll_freq_mhz_before_sleep) { + rtc_clk_mpll_enable(); + rtc_clk_mpll_configure(clk_hal_xtal_get_freq_mhz(), s_mpll_freq_mhz_before_sleep, false); + } +#if CONFIG_SPIRAM + mspi_ll_psram_unhold_all_pins(); +#if CONFIG_PM_SLP_SPIRAM_HALFSLEEP_ENABLED + esp_psram_impl_exit_halfsleep_mode(); +#endif +#endif + } + +#if !SOC_APM_SUPPORTED + apm_hal_enable_ctrl_filter_all(false); +#else + ESP_STATIC_ASSERT(0, "TEE/APM retention need to be supported!"); +#endif + return pmu_ll_hp_is_sleep_reject(&PMU); } diff --git a/components/esp_hw_support/port/esp32s31/private_include/pmu_param.h b/components/esp_hw_support/port/esp32s31/private_include/pmu_param.h index 48b4555cd97..cb8f5c1f0e1 100644 --- a/components/esp_hw_support/port/esp32s31/private_include/pmu_param.h +++ b/components/esp_hw_support/port/esp32s31/private_include/pmu_param.h @@ -360,7 +360,7 @@ typedef struct { .hp_sys = { \ .analog = { \ .drv_b = PMU_HP_DRVB_LIGHTSLEEP, \ - .pd_cur = PMU_PD_CUR_SLEEP_DEFAULT, \ + .pd_cur = PMU_PD_CUR_SLEEP_ON, \ .bias_sleep = PMU_BIASSLP_SLEEP_DEFAULT, \ .xpd = PMU_HP_XPD_LIGHTSLEEP, \ .dbg_atten = PMU_DBG_ATTEN_LIGHTSLEEP_DEFAULT, \ @@ -370,7 +370,7 @@ typedef struct { .lp_sys[PMU_MODE_LP_SLEEP] = { \ .analog = { \ .drv_b = PMU_LP_DRVB_LIGHTSLEEP, \ - .pd_cur = PMU_PD_CUR_SLEEP_DEFAULT, \ + .pd_cur = PMU_PD_CUR_SLEEP_ON, \ .bias_sleep = PMU_BIASSLP_SLEEP_DEFAULT, \ .slp_xpd = PMU_LP_SLP_XPD_SLEEP_DEFAULT, \ .slp_dbias = PMU_LP_SLP_DBIAS_SLEEP_DEFAULT, \ diff --git a/components/esp_pm/test_apps/.build-test-rules.yml b/components/esp_pm/test_apps/.build-test-rules.yml index bfdf55d31ac..bed18a701bc 100644 --- a/components/esp_pm/test_apps/.build-test-rules.yml +++ b/components/esp_pm/test_apps/.build-test-rules.yml @@ -5,9 +5,9 @@ components/esp_pm/test_apps: - if: INCLUDE_DEFAULT == 1 disable: - if: CONFIG_NAME == "pm_pd_top_sleep" and IDF_TARGET not in ["esp32c5", "esp32c6", "esp32h2", "esp32p4"] - - if: IDF_TARGET in ["esp32h21", "esp32h4", "esp32s31"] + - if: IDF_TARGET in ["esp32h21", "esp32h4"] temporary: true - reason: not support yet # TODO: [ESP32H21] IDF-11522, [ESP32H4] IDF-12286 [ESP32S31] IDF-14645 + reason: not support yet # TODO: [ESP32H21] IDF-11522, [ESP32H4] IDF-12286 depends_components: - *common_components - esp_pm diff --git a/components/esp_pm/test_apps/esp_pm/README.md b/components/esp_pm/test_apps/esp_pm/README.md index 7b96141437e..be7ab2904d9 100644 --- a/components/esp_pm/test_apps/esp_pm/README.md +++ b/components/esp_pm/test_apps/esp_pm/README.md @@ -1,2 +1,2 @@ -| Supported Targets | ESP32 | ESP32-C2 | ESP32-C3 | ESP32-C5 | ESP32-C6 | ESP32-C61 | ESP32-H2 | ESP32-P4 | ESP32-S2 | ESP32-S3 | -| ----------------- | ----- | -------- | -------- | -------- | -------- | --------- | -------- | -------- | -------- | -------- | +| Supported Targets | ESP32 | ESP32-C2 | ESP32-C3 | ESP32-C5 | ESP32-C6 | ESP32-C61 | ESP32-H2 | ESP32-P4 | ESP32-S2 | ESP32-S3 | ESP32-S31 | +| ----------------- | ----- | -------- | -------- | -------- | -------- | --------- | -------- | -------- | -------- | -------- | --------- | diff --git a/components/esp_pm/test_apps/esp_pm/pytest_esp_pm.py b/components/esp_pm/test_apps/esp_pm/pytest_esp_pm.py index d7a65e69174..51a0b28ef1e 100644 --- a/components/esp_pm/test_apps/esp_pm/pytest_esp_pm.py +++ b/components/esp_pm/test_apps/esp_pm/pytest_esp_pm.py @@ -17,7 +17,6 @@ from pytest_embedded_idf.utils import idf_parametrize indirect=True, ) @idf_parametrize('target', ['supported_targets'], indirect=['target']) -@pytest.mark.temp_skip_ci(targets=['esp32s31'], reason='s31 bringup on this module is not done') def test_esp_pm(dut: Dut) -> None: dut.run_all_single_board_cases() diff --git a/components/esp_system/test_apps/esp_system_unity_tests/main/test_sleep.c b/components/esp_system/test_apps/esp_system_unity_tests/main/test_sleep.c index 8bc4e4973e5..2f6f74e1cef 100644 --- a/components/esp_system/test_apps/esp_system_unity_tests/main/test_sleep.c +++ b/components/esp_system/test_apps/esp_system_unity_tests/main/test_sleep.c @@ -1,5 +1,5 @@ /* - * SPDX-FileCopyrightText: 2019-2024 Espressif Systems (Shanghai) CO LTD + * SPDX-FileCopyrightText: 2019-2026 Espressif Systems (Shanghai) CO LTD * * SPDX-License-Identifier: Apache-2.0 */ diff --git a/components/soc/esp32s31/include/soc/Kconfig.soc_caps.in b/components/soc/esp32s31/include/soc/Kconfig.soc_caps.in index 2399a115845..ca45cf59db6 100644 --- a/components/soc/esp32s31/include/soc/Kconfig.soc_caps.in +++ b/components/soc/esp32s31/include/soc/Kconfig.soc_caps.in @@ -727,6 +727,10 @@ config SOC_SPI_MEM_SUPPORT_TSUS_TRES_SEPERATE_CTR bool default y +config SOC_PSRAM_HAS_DEDICATED_LDO + bool + default y + config SOC_MEMSPI_IS_INDEPENDENT bool default y diff --git a/components/soc/esp32s31/include/soc/soc_caps.h b/components/soc/esp32s31/include/soc/soc_caps.h index ee6401d6a26..988dfe2a0da 100644 --- a/components/soc/esp32s31/include/soc/soc_caps.h +++ b/components/soc/esp32s31/include/soc/soc_caps.h @@ -294,6 +294,7 @@ #define SOC_SPI_MEM_SUPPORT_CACHE_32BIT_ADDR_MAP (1) #define SOC_SPI_MEM_SUPPORT_TSUS_TRES_SEPERATE_CTR (1) +#define SOC_PSRAM_HAS_DEDICATED_LDO 1 #define SOC_MEMSPI_IS_INDEPENDENT 1 #define SOC_PSRAM_MEMSPI_IS_INDEPENDENT 1 diff --git a/components/soc/esp32s31/include/soc/system_periph_retention.h b/components/soc/esp32s31/include/soc/system_periph_retention.h index 345955df495..29b30a18efd 100644 --- a/components/soc/esp32s31/include/soc/system_periph_retention.h +++ b/components/soc/esp32s31/include/soc/system_periph_retention.h @@ -62,7 +62,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 5 +#define IOMUX_RETENTION_LINK_LEN 4 extern const regdma_entries_config_t iomux_regs_retention[IOMUX_RETENTION_LINK_LEN]; /** @@ -82,7 +82,7 @@ extern const regdma_entries_config_t flash_spimem_regs_retention[SPIMEM_FLASH_RE * This is an internal function of the sleep retention driver, and is not * useful for external use. */ -#define SPIMEM_PSRAM_RETENTION_LINK_LEN 5 +#define SPIMEM_PSRAM_RETENTION_LINK_LEN 15 extern const regdma_entries_config_t psram_spimem_regs_retention[SPIMEM_PSRAM_RETENTION_LINK_LEN]; /** diff --git a/components/soc/esp32s31/system_retention_periph.c b/components/soc/esp32s31/system_retention_periph.c index 2627754605b..3876a816785 100644 --- a/components/soc/esp32s31/system_retention_periph.c +++ b/components/soc/esp32s31/system_retention_periph.c @@ -19,7 +19,6 @@ #include "soc/spi1_mem_s_reg.h" #include "soc/systimer_reg.h" #include "soc/timer_group_reg.h" -// #include "soc/timer_periph.h" #include "soc/uart_reg.h" #include "esp32s31/rom/cache.h" #include "soc/hp_apm_reg.h" @@ -52,7 +51,6 @@ const regdma_entries_config_t cache_regs_retention[] = { }; _Static_assert(ARRAY_SIZE(cache_regs_retention) == CACHE_RETENTION_LINK_LEN, "Inconsistent L2 CACHE retention link length definitions"); -#if SOC_APM_SUPPORTED /* TEE APM Registers Context */ #define N_REGS_TEE_0() (((TEE_M87_MODE_CTRL_REG - TEE_M0_MODE_CTRL_REG) / 4) + 1) #define N_REGS_TEE_1() (((TEE_SUPERVISOR_PRIV_SEL_REG - TEE_SUPERVISOR_PRIV_SEL_REG) / 4) + 1) @@ -87,7 +85,6 @@ const regdma_entries_config_t tee_apm_regs_retention[] = { [13] = { .config = REGDMA_LINK_CONTINUOUS_INIT(REGDMA_TEEAPM_LINK(0x0d), HP_PERI1_PMS_BUS_ERR_CONF_REG, HP_PERI1_PMS_BUS_ERR_CONF_REG, N_REGS_PMS1_2(), 0, 0), .owner = ENTRY(0) | ENTRY(2) }, }; _Static_assert(ARRAY_SIZE(tee_apm_regs_retention) == TEE_APM_RETENTION_LINK_LEN, "Inconsistent HP_SYSTEM retention link length definitions"); -#endif /* HP System Registers Context */ #define N_REGS_HP_SYSTEM() (((HP_SYSTEM_GMAC1_PAD_BIST_INT_ENA_REG - DR_REG_HP_SYS_BASE) / 4) + 1) @@ -98,7 +95,6 @@ _Static_assert(ARRAY_SIZE(hp_system_regs_retention) == HP_SYSTEM_RETENTION_LINK_ /* IO MUX Registers Context */ #define N_REGS_IOMUX() (((IO_MUX_GPIO62_REG - REG_IO_MUX_BASE) / 4) + 1) -#define N_REGS_MSPI_IOMUX() (((IOMUX_MSPI_PIN_PSRAM_CS_PIN0_REG - IOMUX_MSPI_PIN_CLK_EN0_REG) / 4) + 1) #define N_REGS_GPIO_PINx() (((GPIO_PIN62_REG - GPIO_PIN0_REG) / 4) + 1) #define N_REGS_GPIO_FUNC_IN() (((GPIO_FUNC255_IN_SEL_CFG_REG - GPIO_FUNC0_IN_SEL_CFG_REG) / 4) + 1) #define N_REGS_GPIO_FUNC_OUT() (((GPIO_FUNC62_OUT_SEL_CFG_REG - GPIO_FUNC0_OUT_SEL_CFG_REG) / 4) + 1) @@ -106,13 +102,11 @@ _Static_assert(ARRAY_SIZE(hp_system_regs_retention) == HP_SYSTEM_RETENTION_LINK_ const regdma_entries_config_t iomux_regs_retention[] = { /* 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) | ENTRY(2) }, - /* MSPI IOMUX */ - [1] = { .config = REGDMA_LINK_CONTINUOUS_INIT(REGDMA_IOMUX_LINK(0x01), IOMUX_MSPI_PIN_PSRAM_CS_PIN0_REG, IOMUX_MSPI_PIN_PSRAM_CS_PIN0_REG, N_REGS_MSPI_IOMUX(), 0, 0), .owner = ENTRY(0) | ENTRY(2) }, /* GPIO_PIN0_REG ~ GPIO_PIN62_REG*/ - [2] = { .config = REGDMA_LINK_CONTINUOUS_INIT(REGDMA_IOMUX_LINK(0x02), GPIO_PIN0_REG, GPIO_PIN0_REG, N_REGS_GPIO_PINx(), 0, 0), .owner = ENTRY(0) | ENTRY(2) }, + [1] = { .config = REGDMA_LINK_CONTINUOUS_INIT(REGDMA_IOMUX_LINK(0x01), GPIO_PIN0_REG, GPIO_PIN0_REG, N_REGS_GPIO_PINx(), 0, 0), .owner = ENTRY(0) | ENTRY(2) }, /* GPIO_FUNC0_IN_SEL_CFG_REG ~ GPIO_FUNC255_IN_SEL_CFG_REG & GPIO_FUNC0_OUT_SEL_CFG_REG ~ GPIO_FUNC0_IN_SEL_CFG_REG */ - [3] = { .config = REGDMA_LINK_CONTINUOUS_INIT(REGDMA_IOMUX_LINK(0x03), GPIO_FUNC0_IN_SEL_CFG_REG, GPIO_FUNC0_IN_SEL_CFG_REG, N_REGS_GPIO_FUNC_IN(), 0, 0), .owner = ENTRY(0) | ENTRY(2) }, - [4] = { .config = REGDMA_LINK_CONTINUOUS_INIT(REGDMA_IOMUX_LINK(0x04), GPIO_FUNC0_OUT_SEL_CFG_REG, GPIO_FUNC0_OUT_SEL_CFG_REG, N_REGS_GPIO_FUNC_OUT(), 0, 0), .owner = ENTRY(0) | ENTRY(2) }, + [2] = { .config = REGDMA_LINK_CONTINUOUS_INIT(REGDMA_IOMUX_LINK(0x02), GPIO_FUNC0_IN_SEL_CFG_REG, GPIO_FUNC0_IN_SEL_CFG_REG, N_REGS_GPIO_FUNC_IN(), 0, 0), .owner = ENTRY(0) | ENTRY(2) }, + [3] = { .config = REGDMA_LINK_CONTINUOUS_INIT(REGDMA_IOMUX_LINK(0x03), GPIO_FUNC0_OUT_SEL_CFG_REG, GPIO_FUNC0_OUT_SEL_CFG_REG, N_REGS_GPIO_FUNC_OUT(), 0, 0), .owner = ENTRY(0) | ENTRY(2) }, }; _Static_assert(ARRAY_SIZE(iomux_regs_retention) == IOMUX_RETENTION_LINK_LEN, "Inconsistent IOMUX retention link length definitions"); @@ -139,11 +133,17 @@ const regdma_entries_config_t flash_spimem_regs_retention[] = { _Static_assert(ARRAY_SIZE(flash_spimem_regs_retention) == SPIMEM_FLASH_RETENTION_LINK_LEN, "Inconsistent Flash SPI Mem retention link length definitions"); #if CONFIG_SPIRAM -#define N_REGS_SPI0_S_MEM_0() (((SPI_MEM_S_INT_ENA_REG - SPI_MEM_S_CTRL_REG) / 4) + 1) -#define N_REGS_SPI0_S_MEM_1() (((SPI_MEM_S_CLOCK_GATE_REG - SPI_MEM_S_DDR_REG) / 4) + 1) -#define N_REGS_SPI1_S_MEM_0() (((SPI1_MEM_S_INT_ENA_REG - SPI1_MEM_S_CMD_REG) / 4) + 1) -#define N_REGS_SPI1_S_MEM_1() (((SPI1_MEM_S_DDR_REG - SPI1_MEM_S_DDR_REG) / 4) + 1) -#define N_REGS_SPI1_S_MEM_2() (((SPI1_MEM_S_TIMING_CALI_REG - SPI1_MEM_S_TIMING_CALI_REG) / 4) + 1) +#define N_REGS_SPI0_S_MEM_0() (((SPI_MEM_S_USER2_REG - SPI_MEM_S_CTRL_REG) / 4) + 1) +#define N_REGS_SPI0_S_MEM_1() (((SPI_MEM_S_FSM_REG - SPI_MEM_S_MISC_REG) / 4) + 1) +#define N_REGS_SPI0_S_MEM_2() (((SPI_SMEM_S_DDR_REG - SPI_MEM_S_INT_ENA_REG) / 4) + 1) +#define N_REGS_SPI0_S_MEM_3() (((SPI_SMEM_S_DOUT_HEX_MODE_REG - SPI_MEM_S_ECC_CTRL_REG) / 4) + 1) +#define N_REGS_SPI0_S_MEM_4() (((SPI_MEM_S_DPA_CTRL_REG - SPI_MEM_S_MMU_POWER_CTRL_REG) / 4) + 1) +#define N_REGS_SPI1_S_MEM_0() ((SPI1_MEM_S_CACHE_FCTRL_REG - SPI1_MEM_S_CMD_REG) / 4 + 1) +#define N_REGS_SPI1_S_MEM_1() (((SPI1_MEM_S_SUS_STATUS_REG - SPI1_MEM_S_FLASH_WAITI_CTRL_REG) / 4) + 1) +#define N_REGS_SPI1_S_MEM_2() (((SPI1_MEM_S_DDR_REG - SPI1_MEM_S_INT_ENA_REG) / 4) + 1) +#define N_REGS_SPI1_S_MEM_3() (1) +#define N_REGS_SPI1_S_MEM_4() (1) +#define N_REGS_MSPI1_IOMUX() (((IOMUX_MSPI_PIN_PSRAM_CS_PIN0_REG - IOMUX_MSPI_PIN_PSRAM_D_PIN0_REG) / 4) + 1) const regdma_entries_config_t psram_spimem_regs_retention[SPIMEM_PSRAM_RETENTION_LINK_LEN] = { [0] = { .config = REGDMA_LINK_WRITE_INIT(REGDMA_SPIMEM_LINK(0x07), HP_SYS_CLKRST_PSRAM_CTRL0_REG, HP_SYS_CLKRST_REG_PSRAM_CORE_CLK_EN, HP_SYS_CLKRST_REG_PSRAM_CORE_CLK_EN_M, 1, 0), .owner = ENTRY(0) | ENTRY(2) }, // Enable PSRAM mspi core clock on restore From d3c5129050867df340aa4881546ae7e2fab74a0b Mon Sep 17 00:00:00 2001 From: wuzhenghui Date: Wed, 15 Apr 2026 18:20:55 +0800 Subject: [PATCH 09/13] fix(driver): fix retention unsupported mouldes TOP pd test --- components/esp_driver_dma/src/gdma.c | 25 ++++++++++++++++--- components/esp_driver_gptimer/src/gptimer.c | 5 ++++ components/esp_driver_i2c/i2c_master.c | 4 +++ .../test_apps/i2s/main/test_i2s_sleep.c | 4 +-- .../main/test_jpeg_sleep_retention.c | 4 ++- components/esp_driver_ledc/src/ledc.c | 4 +++ components/esp_driver_mcpwm/src/mcpwm_cap.c | 4 +++ components/esp_driver_mcpwm/src/mcpwm_timer.c | 4 +++ components/esp_driver_parlio/src/parlio_rx.c | 4 +++ components/esp_driver_parlio/src/parlio_tx.c | 4 +++ .../pulse_cnt/main/test_pulse_cnt_sleep.c | 2 +- components/esp_driver_rmt/src/rmt_rx.c | 4 +++ components/esp_driver_rmt/src/rmt_tx.c | 4 +++ components/esp_driver_sdm/src/sdm.c | 4 +++ .../slave_hd/main/test_spi_slave_hd.c | 4 +-- .../esp_driver_tsens/src/temperature_sensor.c | 3 +++ components/esp_driver_twai/esp_twai_onchip.c | 4 +++ components/esp_driver_uart/src/uart.c | 4 +++ .../test_apps/uart/main/test_uart_retention.c | 3 ++- components/esp_hw_support/etm/esp_etm.c | 4 +++ components/esp_lcd/i80/esp_lcd_panel_io_i80.c | 4 +++ examples/system/ulp/.build-test-rules.yml | 2 +- 22 files changed, 92 insertions(+), 12 deletions(-) diff --git a/components/esp_driver_dma/src/gdma.c b/components/esp_driver_dma/src/gdma.c index b248ba2bad0..b4d5be044ca 100644 --- a/components/esp_driver_dma/src/gdma.c +++ b/components/esp_driver_dma/src/gdma.c @@ -28,6 +28,7 @@ #include "gdma_priv.h" #include "esp_memory_utils.h" +#include "esp_sleep.h" #define GDMA_INVALID_PERIPH_TRIG (0x3F) #define SEARCH_REQUEST_RX_CHANNEL (1 << 0) @@ -87,11 +88,15 @@ static esp_err_t do_allocate_gdma_channel(const gdma_channel_search_info_t *sear TAG, "invalid interrupt priority:%d", config->intr_priority); } -#if CONFIG_PM_POWER_DOWN_PERIPHERAL_IN_LIGHT_SLEEP && SOC_GDMA_SUPPORT_SLEEP_RETENTION +#if CONFIG_PM_POWER_DOWN_PERIPHERAL_IN_LIGHT_SLEEP +#if SOC_GDMA_SUPPORT_SLEEP_RETENTION // retention module is per GDMA pair, before we allocate the pair object, some common registers are already configured in "hal_init" // if a light sleep happens and powers off the gdma module, those registers will get lost // to work around it, we can acquire a power lock first, before any register configuration sleep_retention_power_lock_acquire(); +#else + esp_sleep_pd_config(ESP_PD_DOMAIN_TOP, ESP_PD_OPTION_ON); //IDF-15640 +#endif #endif // Determine search code based on which channels are requested @@ -237,8 +242,12 @@ err: free(alloc_rx_channel); } -#if CONFIG_PM_POWER_DOWN_PERIPHERAL_IN_LIGHT_SLEEP && SOC_GDMA_SUPPORT_SLEEP_RETENTION +#if CONFIG_PM_POWER_DOWN_PERIPHERAL_IN_LIGHT_SLEEP +#if SOC_GDMA_SUPPORT_SLEEP_RETENTION sleep_retention_power_lock_release(); +#else + esp_sleep_pd_config(ESP_PD_DOMAIN_TOP, ESP_PD_OPTION_AUTO); //IDF-15640 +#endif #endif return ret; } @@ -874,9 +883,13 @@ static esp_err_t gdma_del_tx_channel(gdma_channel_t *dma_channel) free(tx_chan); ESP_LOGV(TAG, "del tx channel (%d,%d)", group_id, pair_id); -#if CONFIG_PM_POWER_DOWN_PERIPHERAL_IN_LIGHT_SLEEP && SOC_GDMA_SUPPORT_SLEEP_RETENTION +#if CONFIG_PM_POWER_DOWN_PERIPHERAL_IN_LIGHT_SLEEP +#if SOC_GDMA_SUPPORT_SLEEP_RETENTION // release sleep retention lock gdma_release_sleep_retention(pair); +#else + esp_sleep_pd_config(ESP_PD_DOMAIN_TOP, ESP_PD_OPTION_AUTO); //IDF-15640 +#endif #endif // release pair handle (will free pair if both channels are deleted) gdma_try_free_pair_handle(pair); @@ -909,9 +922,13 @@ static esp_err_t gdma_del_rx_channel(gdma_channel_t *dma_channel) free(rx_chan); ESP_LOGV(TAG, "del rx channel (%d,%d)", group_id, pair_id); -#if CONFIG_PM_POWER_DOWN_PERIPHERAL_IN_LIGHT_SLEEP && SOC_GDMA_SUPPORT_SLEEP_RETENTION +#if CONFIG_PM_POWER_DOWN_PERIPHERAL_IN_LIGHT_SLEEP +#if SOC_GDMA_SUPPORT_SLEEP_RETENTION // release sleep retention lock gdma_release_sleep_retention(pair); +#else + esp_sleep_pd_config(ESP_PD_DOMAIN_TOP, ESP_PD_OPTION_AUTO); //IDF-15640 +#endif #endif // release pair handle (will free pair if both channels are deleted) gdma_try_free_pair_handle(pair); diff --git a/components/esp_driver_gptimer/src/gptimer.c b/components/esp_driver_gptimer/src/gptimer.c index cabbecf78de..c1695f181b1 100644 --- a/components/esp_driver_gptimer/src/gptimer.c +++ b/components/esp_driver_gptimer/src/gptimer.c @@ -9,6 +9,7 @@ #include "driver/gptimer.h" #include "gptimer_priv.h" #include "esp_memory_utils.h" +#include "esp_sleep.h" static void gptimer_default_isr(void *args); @@ -155,6 +156,10 @@ esp_err_t gptimer_new_timer(const gptimer_config_t *config, gptimer_handle_t *re #if GPTIMER_USE_RETENTION_LINK gptimer_create_retention_module(timer); #endif // GPTIMER_USE_RETENTION_LINK + } else { +#if !SOC_TIMER_SUPPORT_SLEEP_RETENTION && CONFIG_PM_POWER_DOWN_PERIPHERAL_IN_LIGHT_SLEEP + esp_sleep_pd_config(ESP_PD_DOMAIN_TOP, ESP_PD_OPTION_ON); //IDF-15641 +#endif } // initialize HAL layer diff --git a/components/esp_driver_i2c/i2c_master.c b/components/esp_driver_i2c/i2c_master.c index 972f9ba5c02..8998ecb463a 100644 --- a/components/esp_driver_i2c/i2c_master.c +++ b/components/esp_driver_i2c/i2c_master.c @@ -28,6 +28,7 @@ #include "hal/i2c_ll.h" #include "hal/i2c_periph.h" #include "driver/i2c_master.h" +#include "esp_sleep.h" #include "i2c_private.h" static const char *TAG = "i2c.master"; @@ -1045,6 +1046,9 @@ esp_err_t i2c_new_master_bus(const i2c_master_bus_config_t *bus_config, i2c_mast #if !SOC_I2C_SUPPORT_SLEEP_RETENTION ESP_RETURN_ON_FALSE(bus_config->flags.allow_pd == 0, ESP_ERR_NOT_SUPPORTED, TAG, "not able to power down in light sleep"); +#if SOC_PM_SUPPORT_TOP_PD + esp_sleep_pd_config(ESP_PD_DOMAIN_TOP, ESP_PD_OPTION_ON); //IDF-15642 +#endif #endif // SOC_I2C_SUPPORT_SLEEP_RETENTION i2c_master = heap_caps_calloc(1, sizeof(i2c_master_bus_t) + 20 * sizeof(i2c_transaction_t), I2C_MEM_ALLOC_CAPS); diff --git a/components/esp_driver_i2s/test_apps/i2s/main/test_i2s_sleep.c b/components/esp_driver_i2s/test_apps/i2s/main/test_i2s_sleep.c index 2d33535f773..f04fc11c6f5 100644 --- a/components/esp_driver_i2s/test_apps/i2s/main/test_i2s_sleep.c +++ b/components/esp_driver_i2s/test_apps/i2s/main/test_i2s_sleep.c @@ -43,8 +43,8 @@ static void s_test_i2s_enter_light_sleep(int sec, bool allow_power_down) printf("Woke up from light sleep\n"); TEST_ASSERT_EQUAL(0, sleep_ctx.sleep_request_result); -#if SOC_HAS(PAU) - // check if the power domain also is powered down +#if SOC_HAS(PAU) && SOC_GDMA_SUPPORT_SLEEP_RETENTION + // check if the power domain also is powered down (if GDMA retention is not supported, TOP pd will be rejected) TEST_ASSERT_EQUAL(allow_power_down ? PMU_SLEEP_PD_TOP : 0, (sleep_ctx.sleep_flags) & PMU_SLEEP_PD_TOP); #endif esp_sleep_set_sleep_context(NULL); diff --git a/components/esp_driver_jpeg/test_apps/jpeg_test_apps/main/test_jpeg_sleep_retention.c b/components/esp_driver_jpeg/test_apps/jpeg_test_apps/main/test_jpeg_sleep_retention.c index 80ab5db79bd..286f8be0450 100644 --- a/components/esp_driver_jpeg/test_apps/jpeg_test_apps/main/test_jpeg_sleep_retention.c +++ b/components/esp_driver_jpeg/test_apps/jpeg_test_apps/main/test_jpeg_sleep_retention.c @@ -26,6 +26,8 @@ extern const uint8_t image_esp1080_jpg_start[] asm("_binary_esp1080_jpg_start"); extern const uint8_t image_esp1080_jpg_end[] asm("_binary_esp1080_jpg_end"); +#define JPEG_SLEEP_RETENTION_TIMEOUT_MS 50 + #if SOC_LIGHT_SLEEP_SUPPORTED && CONFIG_PM_ENABLE static void test_jpeg_sleep_retention(bool allow_pd) { @@ -33,7 +35,7 @@ static void test_jpeg_sleep_retention(bool allow_pd) jpeg_decode_engine_cfg_t decode_eng_cfg = { .intr_priority = 0, - .timeout_ms = 40, + .timeout_ms = JPEG_SLEEP_RETENTION_TIMEOUT_MS, .flags = { .allow_pd = allow_pd, }, diff --git a/components/esp_driver_ledc/src/ledc.c b/components/esp_driver_ledc/src/ledc.c index ddd62a5a69b..4430a18384f 100644 --- a/components/esp_driver_ledc/src/ledc.c +++ b/components/esp_driver_ledc/src/ledc.c @@ -18,6 +18,7 @@ #include "hal/ledc_hal.h" #include "driver/ledc.h" #include "esp_private/esp_sleep_internal.h" +#include "esp_sleep.h" #include "esp_private/periph_ctrl.h" #include "driver/gpio.h" #include "esp_private/gpio.h" @@ -877,6 +878,9 @@ esp_err_t ledc_channel_config(const ledc_channel_config_t *ledc_conf) LEDC_ARG_CHECK(ledc_conf->sleep_mode < LEDC_SLEEP_MODE_INVALID, "sleep_mode"); #if !SOC_LEDC_SUPPORT_SLEEP_RETENTION ESP_RETURN_ON_FALSE(ledc_conf->sleep_mode != LEDC_SLEEP_MODE_NO_ALIVE_ALLOW_PD, ESP_ERR_NOT_SUPPORTED, LEDC_TAG, "register back up is not supported"); +#if SOC_PM_SUPPORT_TOP_PD + esp_sleep_pd_config(ESP_PD_DOMAIN_TOP, ESP_PD_OPTION_ON); //IDF-15643 +#endif #endif esp_err_t ret = ESP_OK; diff --git a/components/esp_driver_mcpwm/src/mcpwm_cap.c b/components/esp_driver_mcpwm/src/mcpwm_cap.c index e85ec9b4119..413ef0477cd 100644 --- a/components/esp_driver_mcpwm/src/mcpwm_cap.c +++ b/components/esp_driver_mcpwm/src/mcpwm_cap.c @@ -10,6 +10,7 @@ #include "esp_memory_utils.h" #include "driver/mcpwm_cap.h" #include "driver/gpio.h" +#include "esp_sleep.h" #include "esp_private/gpio.h" static void mcpwm_capture_default_isr(void *args); @@ -73,6 +74,9 @@ esp_err_t mcpwm_new_capture_timer(const mcpwm_capture_timer_config_t *config, mc #if !SOC_MCPWM_SUPPORT_SLEEP_RETENTION ESP_RETURN_ON_FALSE(config->flags.allow_pd == 0, ESP_ERR_NOT_SUPPORTED, TAG, "register back up is not supported"); +#if SOC_PM_SUPPORT_TOP_PD + esp_sleep_pd_config(ESP_PD_DOMAIN_TOP, ESP_PD_OPTION_ON); //IDF-15644 +#endif #endif // SOC_MCPWM_SUPPORT_SLEEP_RETENTION cap_timer = heap_caps_calloc(1, sizeof(mcpwm_cap_timer_t), MCPWM_MEM_ALLOC_CAPS); diff --git a/components/esp_driver_mcpwm/src/mcpwm_timer.c b/components/esp_driver_mcpwm/src/mcpwm_timer.c index 4ca9c68ebd6..56b8905753b 100644 --- a/components/esp_driver_mcpwm/src/mcpwm_timer.c +++ b/components/esp_driver_mcpwm/src/mcpwm_timer.c @@ -8,6 +8,7 @@ #include "esp_memory_utils.h" #include "hal/mcpwm_ll.h" #include "driver/mcpwm_timer.h" +#include "esp_sleep.h" #include "esp_private/mcpwm.h" static void mcpwm_timer_default_isr(void *args); @@ -83,6 +84,9 @@ esp_err_t mcpwm_new_timer(const mcpwm_timer_config_t *config, mcpwm_timer_handle #if !SOC_MCPWM_SUPPORT_SLEEP_RETENTION ESP_RETURN_ON_FALSE(config->flags.allow_pd == 0, ESP_ERR_NOT_SUPPORTED, TAG, "register back up is not supported"); +#if SOC_PM_SUPPORT_TOP_PD + esp_sleep_pd_config(ESP_PD_DOMAIN_TOP, ESP_PD_OPTION_ON); //IDF-15644 +#endif #endif // SOC_MCPWM_SUPPORT_SLEEP_RETENTION timer = heap_caps_calloc(1, sizeof(mcpwm_timer_t), MCPWM_MEM_ALLOC_CAPS); diff --git a/components/esp_driver_parlio/src/parlio_rx.c b/components/esp_driver_parlio/src/parlio_rx.c index 4bc92783a37..30723a7456e 100644 --- a/components/esp_driver_parlio/src/parlio_rx.c +++ b/components/esp_driver_parlio/src/parlio_rx.c @@ -10,6 +10,7 @@ #include "driver/gpio.h" #include "driver/parlio_rx.h" #include "parlio_priv.h" +#include "esp_sleep.h" #define ALIGN_UP(num, align) (((num) + ((align) - 1)) & ~((align) - 1)) @@ -614,6 +615,9 @@ esp_err_t parlio_new_rx_unit(const parlio_rx_unit_config_t *config, parlio_rx_un #if !SOC_PARLIO_SUPPORT_SLEEP_RETENTION ESP_RETURN_ON_FALSE(config->flags.allow_pd == 0, ESP_ERR_NOT_SUPPORTED, TAG, "register back up is not supported"); +#if SOC_PM_SUPPORT_TOP_PD + esp_sleep_pd_config(ESP_PD_DOMAIN_TOP, ESP_PD_OPTION_ON); //IDF-15645 +#endif #endif // SOC_PARLIO_SUPPORT_SLEEP_RETENTION /* Allocate unit memory */ diff --git a/components/esp_driver_parlio/src/parlio_tx.c b/components/esp_driver_parlio/src/parlio_tx.c index f8f37672b3a..f7cdb133695 100644 --- a/components/esp_driver_parlio/src/parlio_tx.c +++ b/components/esp_driver_parlio/src/parlio_tx.c @@ -9,6 +9,7 @@ #include "driver/gpio.h" #include "driver/parlio_tx.h" #include "parlio_priv.h" +#include "esp_sleep.h" #if SOC_PARLIO_TX_SUPPORT_LOOP_TRANSMISSION static bool parlio_tx_gdma_eof_callback(gdma_channel_handle_t dma_chan, gdma_event_data_t *event_data, void *user_data); @@ -273,6 +274,9 @@ esp_err_t parlio_new_tx_unit(const parlio_tx_unit_config_t *config, parlio_tx_un #if !SOC_PARLIO_SUPPORT_SLEEP_RETENTION ESP_RETURN_ON_FALSE(config->flags.allow_pd == 0, ESP_ERR_NOT_SUPPORTED, TAG, "register back up is not supported"); +#if SOC_PM_SUPPORT_TOP_PD + esp_sleep_pd_config(ESP_PD_DOMAIN_TOP, ESP_PD_OPTION_ON); //IDF-15645 +#endif #endif // SOC_PARLIO_SUPPORT_SLEEP_RETENTION // allocate unit from internal memory because it contains atomic member diff --git a/components/esp_driver_pcnt/test_apps/pulse_cnt/main/test_pulse_cnt_sleep.c b/components/esp_driver_pcnt/test_apps/pulse_cnt/main/test_pulse_cnt_sleep.c index fb89c70f795..423e49b68d0 100644 --- a/components/esp_driver_pcnt/test_apps/pulse_cnt/main/test_pulse_cnt_sleep.c +++ b/components/esp_driver_pcnt/test_apps/pulse_cnt/main/test_pulse_cnt_sleep.c @@ -125,7 +125,7 @@ static void test_pcnt_sleep_retention(void) TEST_ESP_OK(pcnt_unit_get_count(units[i], &count_value)); TEST_ASSERT_EQUAL(20, count_value); // check the register value - reg_value = pcnt_ll_get_count(&PCNT, i); + reg_value = pcnt_ll_get_count(PCNT_LL_GET_HW(unit_config.group_id), i); TEST_ASSERT_EQUAL(20, reg_value); } diff --git a/components/esp_driver_rmt/src/rmt_rx.c b/components/esp_driver_rmt/src/rmt_rx.c index 51af2291c21..8efa545d167 100644 --- a/components/esp_driver_rmt/src/rmt_rx.c +++ b/components/esp_driver_rmt/src/rmt_rx.c @@ -10,6 +10,7 @@ #include "driver/gpio.h" #include "driver/rmt_rx.h" #include "rmt_private.h" +#include "esp_sleep.h" static esp_err_t rmt_del_rx_channel(rmt_channel_handle_t channel); static esp_err_t rmt_rx_demodulate_carrier(rmt_channel_handle_t channel, const rmt_carrier_config_t *config); @@ -206,6 +207,9 @@ esp_err_t rmt_new_rx_channel(const rmt_rx_channel_config_t *config, rmt_channel_ #if !SOC_RMT_SUPPORT_SLEEP_RETENTION ESP_RETURN_ON_FALSE(config->flags.allow_pd == 0, ESP_ERR_NOT_SUPPORTED, TAG, "not able to power down in light sleep"); +#if SOC_PM_SUPPORT_TOP_PD + esp_sleep_pd_config(ESP_PD_DOMAIN_TOP, ESP_PD_OPTION_ON); //IDF-15646 +#endif #endif // SOC_RMT_SUPPORT_SLEEP_RETENTION // allocate channel memory from internal memory because it contains atomic variable diff --git a/components/esp_driver_rmt/src/rmt_tx.c b/components/esp_driver_rmt/src/rmt_tx.c index b1a70e5a580..67054a0613d 100644 --- a/components/esp_driver_rmt/src/rmt_tx.c +++ b/components/esp_driver_rmt/src/rmt_tx.c @@ -11,6 +11,7 @@ #include "driver/gpio.h" #include "driver/rmt_tx.h" #include "rmt_private.h" +#include "esp_sleep.h" struct rmt_sync_manager_t { rmt_group_t *group; // which group the synchro belongs to @@ -273,6 +274,9 @@ esp_err_t rmt_new_tx_channel(const rmt_tx_channel_config_t *config, rmt_channel_ #if !SOC_RMT_SUPPORT_SLEEP_RETENTION ESP_RETURN_ON_FALSE(config->flags.allow_pd == 0, ESP_ERR_NOT_SUPPORTED, TAG, "not able to power down in light sleep"); +#if SOC_PM_SUPPORT_TOP_PD + esp_sleep_pd_config(ESP_PD_DOMAIN_TOP, ESP_PD_OPTION_ON); //IDF-15646 +#endif #endif // SOC_RMT_SUPPORT_SLEEP_RETENTION // allocate channel memory from internal memory because it contains atomic variable diff --git a/components/esp_driver_sdm/src/sdm.c b/components/esp_driver_sdm/src/sdm.c index 57d352301e3..430c984cc65 100644 --- a/components/esp_driver_sdm/src/sdm.c +++ b/components/esp_driver_sdm/src/sdm.c @@ -18,6 +18,7 @@ #include "esp_err.h" #include "esp_heap_caps.h" #include "esp_log.h" +#include "esp_sleep.h" #include "esp_check.h" #include "esp_pm.h" #include "esp_clk_tree.h" @@ -276,6 +277,9 @@ esp_err_t sdm_new_channel(const sdm_config_t *config, sdm_channel_handle_t *ret_ [[maybe_unused]] bool allow_pd = config->flags.allow_pd == 1; #if !SOC_SDM_SUPPORT_SLEEP_RETENTION ESP_RETURN_ON_FALSE(allow_pd == false, ESP_ERR_NOT_SUPPORTED, TAG, "not able to power down in light sleep"); +#if SOC_PM_SUPPORT_TOP_PD + esp_sleep_pd_config(ESP_PD_DOMAIN_TOP, ESP_PD_OPTION_ON); //IDF-15647 +#endif #endif // SOC_SDM_SUPPORT_SLEEP_RETENTION // allocate channel memory from internal memory because it contains atomic variable diff --git a/components/esp_driver_spi/test_apps/slave_hd/main/test_spi_slave_hd.c b/components/esp_driver_spi/test_apps/slave_hd/main/test_spi_slave_hd.c index 9bf1b7061ed..97e04ed1638 100644 --- a/components/esp_driver_spi/test_apps/slave_hd/main/test_spi_slave_hd.c +++ b/components/esp_driver_spi/test_apps/slave_hd/main/test_spi_slave_hd.c @@ -966,7 +966,7 @@ TEST_CASE("test_spi_slave_hd_sleep_retention", "[spi]") printf("Waked up!\n"); // check if the sleep happened as expected TEST_ASSERT_EQUAL(0, sleep_ctx.sleep_request_result); -#if SOC_SPI_SUPPORT_SLEEP_RETENTION && CONFIG_PM_POWER_DOWN_PERIPHERAL_IN_LIGHT_SLEEP +#if SOC_SPI_SUPPORT_SLEEP_RETENTION && SOC_GDMA_SUPPORT_SLEEP_RETENTION && CONFIG_PM_POWER_DOWN_PERIPHERAL_IN_LIGHT_SLEEP // check if the power domain also is powered down TEST_ASSERT_EQUAL((bus_cfg.flags & SPICOMMON_BUSFLAG_SLP_ALLOW_PD) ? PMU_SLEEP_PD_TOP : 0, (sleep_ctx.sleep_flags) & PMU_SLEEP_PD_TOP); #endif @@ -1044,7 +1044,7 @@ TEST_CASE("test_spi_slave_hd_append_sleep_retention", "[spi]") printf("Waked up!\n"); // check if the sleep happened as expected TEST_ASSERT_EQUAL(0, sleep_ctx.sleep_request_result); -#if SOC_SPI_SUPPORT_SLEEP_RETENTION && CONFIG_PM_POWER_DOWN_PERIPHERAL_IN_LIGHT_SLEEP +#if SOC_SPI_SUPPORT_SLEEP_RETENTION && SOC_GDMA_SUPPORT_SLEEP_RETENTION && CONFIG_PM_POWER_DOWN_PERIPHERAL_IN_LIGHT_SLEEP // check if the power domain also is powered down TEST_ASSERT_EQUAL((bus_cfg.flags & SPICOMMON_BUSFLAG_SLP_ALLOW_PD) ? PMU_SLEEP_PD_TOP : 0, (sleep_ctx.sleep_flags) & PMU_SLEEP_PD_TOP); #endif diff --git a/components/esp_driver_tsens/src/temperature_sensor.c b/components/esp_driver_tsens/src/temperature_sensor.c index cdbbb22f46d..7a11d7d06a9 100644 --- a/components/esp_driver_tsens/src/temperature_sensor.c +++ b/components/esp_driver_tsens/src/temperature_sensor.c @@ -145,6 +145,9 @@ esp_err_t temperature_sensor_install(const temperature_sensor_config_t *tsens_co #if !SOC_TEMPERATURE_SENSOR_SUPPORT_SLEEP_RETENTION ESP_RETURN_ON_FALSE(tsens_config->flags.allow_pd == 0, ESP_ERR_NOT_SUPPORTED, TAG, "not able to power down in light sleep"); +#if SOC_PM_SUPPORT_TOP_PD + esp_sleep_pd_config(ESP_PD_DOMAIN_TOP, ESP_PD_OPTION_ON); //IDF-15648 +#endif #endif // SOC_TEMPERATURE_SENSOR_SUPPORT_SLEEP_RETENTION #if SOC_TEMPERATURE_SENSOR_SUPPORT_SLEEP_RETENTION && !SOC_TEMPERATURE_SENSOR_UNDER_PD_TOP_DOMAIN diff --git a/components/esp_driver_twai/esp_twai_onchip.c b/components/esp_driver_twai/esp_twai_onchip.c index 0495fc66565..4b939934a8c 100644 --- a/components/esp_driver_twai/esp_twai_onchip.c +++ b/components/esp_driver_twai/esp_twai_onchip.c @@ -12,6 +12,7 @@ #include "twai_private.h" #include "hal/twai_periph.h" #include "hal/twai_hal.h" +#include "esp_sleep.h" #if SOC_HAS(TWAI_FD) #include "hal/twaifd_ll.h" #endif @@ -664,6 +665,9 @@ esp_err_t twai_new_node_onchip(const twai_onchip_node_config_t *node_config, twa ESP_RETURN_ON_FALSE(!node_config->intr_priority || (BIT(node_config->intr_priority) & ESP_INTR_FLAG_LOWMED), ESP_ERR_INVALID_ARG, TAG, "Invalid intr_priority level"); #if !SOC_TWAI_SUPPORT_SLEEP_RETENTION ESP_RETURN_ON_FALSE(!node_config->flags.sleep_allow_pd, ESP_ERR_NOT_SUPPORTED, TAG, "sleep retention is not supported on this target"); +#if SOC_PM_SUPPORT_TOP_PD + esp_sleep_pd_config(ESP_PD_DOMAIN_TOP, ESP_PD_OPTION_ON); //IDF-15649 +#endif #endif // Allocate TWAI node from internal memory because it contains atomic variable twai_onchip_ctx_t *node = heap_caps_calloc(1, sizeof(twai_onchip_ctx_t) + twai_hal_get_mem_requirment(), MALLOC_CAP_INTERNAL | MALLOC_CAP_8BIT); diff --git a/components/esp_driver_uart/src/uart.c b/components/esp_driver_uart/src/uart.c index 3b7a097e5f5..c7eb9b2721b 100644 --- a/components/esp_driver_uart/src/uart.c +++ b/components/esp_driver_uart/src/uart.c @@ -28,6 +28,7 @@ #include "esp_private/gpio.h" #include "esp_private/esp_gpio_reserve.h" #include "esp_private/periph_ctrl.h" +#include "esp_sleep.h" #include "esp_private/sleep_retention.h" #include "esp_clk_tree.h" #include "esp_rom_gpio.h" @@ -1067,6 +1068,9 @@ esp_err_t uart_param_config(uart_port_t uart_num, const uart_config_t *uart_conf bool allow_pd __attribute__((unused)) = (uart_config->flags.allow_pd || uart_config->flags.backup_before_sleep); #if !SOC_UART_SUPPORT_SLEEP_RETENTION ESP_RETURN_ON_FALSE(allow_pd == 0, ESP_ERR_NOT_SUPPORTED, UART_TAG, "not able to power down in light sleep"); +#if SOC_PM_SUPPORT_TOP_PD + esp_sleep_pd_config(ESP_PD_DOMAIN_TOP, ESP_PD_OPTION_ON); //IDF-15650 +#endif #endif uart_module_enable(uart_num); diff --git a/components/esp_driver_uart/test_apps/uart/main/test_uart_retention.c b/components/esp_driver_uart/test_apps/uart/main/test_uart_retention.c index d230a84987a..0da28358b04 100644 --- a/components/esp_driver_uart/test_apps/uart/main/test_uart_retention.c +++ b/components/esp_driver_uart/test_apps/uart/main/test_uart_retention.c @@ -55,11 +55,11 @@ TEST_CASE("uart restored correctly after auto light sleep", "[uart][hp-uart-only // Ensure UART is fully idle before starting loopback RX/TX test TEST_ESP_OK(uart_wait_tx_done(uart_num, portMAX_DELAY)); vTaskDelay(pdMS_TO_TICKS(20)); // make sure last byte has flushed from TX FIFO - TEST_ESP_OK(uart_flush_input(uart_num)); for (int i = 0; i < 5; i++) { char tx_data[20] = {0}; char rx_data[20] = {0}; + TEST_ESP_OK(uart_flush_input(uart_num)); int len = sprintf(tx_data, "Hello World %d!\n", i); uart_write_bytes(uart_num, tx_data, len); int size = 0; @@ -97,6 +97,7 @@ TEST_CASE("uart restored correctly after manually enter light sleep", "[uart][hp for (int i = 0; i < 5; i++) { char tx_data[20] = {0}; char rx_data[20] = {0}; + TEST_ESP_OK(uart_flush_input(uart_num)); int len = sprintf(tx_data, "Hello World %d!\n", i); uart_write_bytes(uart_num, tx_data, len); int size = uart_read_bytes(uart_num, rx_data, len, pdMS_TO_TICKS(20)); diff --git a/components/esp_hw_support/etm/esp_etm.c b/components/esp_hw_support/etm/esp_etm.c index dc2a8c7c577..7060ccaea1f 100644 --- a/components/esp_hw_support/etm/esp_etm.c +++ b/components/esp_hw_support/etm/esp_etm.c @@ -18,6 +18,7 @@ #include "freertos/task.h" #include "hal/etm_periph.h" #include "esp_log.h" +#include "esp_sleep.h" #include "esp_check.h" #include "esp_heap_caps.h" #include "esp_etm.h" @@ -246,6 +247,9 @@ esp_err_t esp_etm_new_channel(const esp_etm_channel_config_t *config, esp_etm_ch [[maybe_unused]] bool allow_pd = config->flags.allow_pd == 1; #if !SOC_ETM_SUPPORT_SLEEP_RETENTION ESP_RETURN_ON_FALSE(allow_pd == 0, ESP_ERR_NOT_SUPPORTED, TAG, "not able to power down in light sleep"); +#if SOC_PM_SUPPORT_TOP_PD + esp_sleep_pd_config(ESP_PD_DOMAIN_TOP, ESP_PD_OPTION_ON); //IDF-15651 +#endif #endif // SOC_ETM_SUPPORT_SLEEP_RETENTION // allocate channel memory from internal memory because it contains atomic variable diff --git a/components/esp_lcd/i80/esp_lcd_panel_io_i80.c b/components/esp_lcd/i80/esp_lcd_panel_io_i80.c index 8de7b915976..d6d82583f73 100644 --- a/components/esp_lcd/i80/esp_lcd_panel_io_i80.c +++ b/components/esp_lcd/i80/esp_lcd_panel_io_i80.c @@ -11,6 +11,7 @@ #include "hal/cache_ll.h" #include "hal/cache_hal.h" #include "esp_private/sleep_retention.h" +#include "esp_sleep.h" // Use retention link only when the target supports sleep retention is enabled #define I80_USE_RETENTION_LINK (SOC_LCDCAM_LCD_SUPPORT_SLEEP_RETENTION && CONFIG_PM_POWER_DOWN_PERIPHERAL_IN_LIGHT_SLEEP) @@ -128,6 +129,9 @@ esp_err_t esp_lcd_new_i80_bus(const esp_lcd_i80_bus_config_t *bus_config, esp_lc TAG, "invalid bus width:%d", bus_config->bus_width); #if !SOC_LCDCAM_LCD_SUPPORT_SLEEP_RETENTION ESP_RETURN_ON_FALSE(bus_config->flags.allow_pd == 0, ESP_ERR_NOT_SUPPORTED, TAG, "register back up is not supported"); +#if SOC_PM_SUPPORT_TOP_PD + esp_sleep_pd_config(ESP_PD_DOMAIN_TOP, ESP_PD_OPTION_ON); //IDF-15652 +#endif #endif // SOC_LCDCAM_LCD_SUPPORT_SLEEP_RETENTION // allocate i80 bus memory diff --git a/examples/system/ulp/.build-test-rules.yml b/examples/system/ulp/.build-test-rules.yml index e5f68895551..9adcf69329b 100644 --- a/examples/system/ulp/.build-test-rules.yml +++ b/examples/system/ulp/.build-test-rules.yml @@ -61,7 +61,7 @@ examples/system/ulp/lp_core/lp_adc: examples/system/ulp/lp_core/lp_i2c: enable: - - if: SOC_LP_CORE_SUPPORT_I2C == 1 and SOC_DEEP_SLEEP_SUPPORTED == 1 + - if: SOC_LP_CORE_SUPPORT_I2C == 1 and (SOC_DEEP_SLEEP_SUPPORTED == 1 and SOC_LP_CORE_SUPPORTED == 1) <<: *ulp_default_depends depends_components+: - esp_hal_i2c From cbba7795becbdf30f15ec7e577f35c2dd7df1e16 Mon Sep 17 00:00:00 2001 From: wuzhenghui Date: Mon, 27 Apr 2026 20:09:55 +0800 Subject: [PATCH 10/13] fix(soc): fix gpio output status retention for all targets --- .../soc/esp32c5/include/soc/system_periph_retention.h | 2 +- components/soc/esp32c5/system_retention_periph.c | 4 +++- .../soc/esp32c6/include/soc/system_periph_retention.h | 2 +- components/soc/esp32c6/system_retention_periph.c | 4 +++- .../soc/esp32c61/include/soc/system_periph_retention.h | 2 +- components/soc/esp32c61/system_retention_periph.c | 10 +++++----- .../soc/esp32h2/include/soc/system_periph_retention.h | 2 +- components/soc/esp32h2/system_retention_periph.c | 3 ++- .../soc/esp32h21/include/soc/system_periph_retention.h | 2 +- components/soc/esp32h21/system_retention_periph.c | 1 + .../soc/esp32h4/include/soc/system_periph_retention.h | 2 +- components/soc/esp32h4/system_retention_periph.c | 4 +++- .../soc/esp32p4/include/soc/system_periph_retention.h | 2 +- components/soc/esp32p4/system_retention_periph.c | 3 ++- .../soc/esp32s31/include/soc/system_periph_retention.h | 2 +- components/soc/esp32s31/system_retention_periph.c | 2 ++ 16 files changed, 29 insertions(+), 18 deletions(-) diff --git a/components/soc/esp32c5/include/soc/system_periph_retention.h b/components/soc/esp32c5/include/soc/system_periph_retention.h index ed57f13ca58..b310fb6858e 100644 --- a/components/soc/esp32c5/include/soc/system_periph_retention.h +++ b/components/soc/esp32c5/include/soc/system_periph_retention.h @@ -54,7 +54,7 @@ extern const regdma_entries_config_t tee_apm_highpri_regs_retention[TEE_APM_HIGH * This is an internal function of the sleep retention driver, and is not * useful for external use. */ -#define IOMUX_RETENTION_LINK_LEN 5 +#define IOMUX_RETENTION_LINK_LEN 7 extern const regdma_entries_config_t iomux_regs_retention[IOMUX_RETENTION_LINK_LEN]; /** diff --git a/components/soc/esp32c5/system_retention_periph.c b/components/soc/esp32c5/system_retention_periph.c index dc587ffc299..b040a755b63 100644 --- a/components/soc/esp32c5/system_retention_periph.c +++ b/components/soc/esp32c5/system_retention_periph.c @@ -65,7 +65,9 @@ const regdma_entries_config_t iomux_regs_retention[] = { [4] = { .config = REGDMA_LINK_ADDR_MAP_INIT(REGDMA_IOMUX_LINK(0x04), GPIO_RETENTION_MAP_BASE, GPIO_RETENTION_MAP_BASE, GPIO_RETENTION_REGS_CNT, 0, 0, gpio_regs_map[0], gpio_regs_map[1], gpio_regs_map[2], gpio_regs_map[3]), .owner = ENTRY(0) | ENTRY(2) - }, \ + }, + [5] = { .config = REGDMA_LINK_CONTINUOUS_INIT(REGDMA_IOMUX_LINK(0x05), GPIO_ENABLE_REG, GPIO_ENABLE_W1TS_REG, 1, 0, 0), .owner = ENTRY(0) | ENTRY(2) }, + [6] = { .config = REGDMA_LINK_CONTINUOUS_INIT(REGDMA_IOMUX_LINK(0x06), GPIO_ENABLE1_REG, GPIO_ENABLE1_W1TS_REG, 1, 0, 0), .owner = ENTRY(0) | ENTRY(2) }, }; _Static_assert(ARRAY_SIZE(iomux_regs_retention) == IOMUX_RETENTION_LINK_LEN, "Inconsistent IOMUX retention link length definitions"); diff --git a/components/soc/esp32c6/include/soc/system_periph_retention.h b/components/soc/esp32c6/include/soc/system_periph_retention.h index 9328372d444..eb59862c9b2 100644 --- a/components/soc/esp32c6/include/soc/system_periph_retention.h +++ b/components/soc/esp32c6/include/soc/system_periph_retention.h @@ -54,7 +54,7 @@ extern const regdma_entries_config_t tee_apm_highpri_regs_retention[TEE_APM_HIGH * This is an internal function of the sleep retention driver, and is not * useful for external use. */ -#define IOMUX_RETENTION_LINK_LEN 4 +#define IOMUX_RETENTION_LINK_LEN 6 extern const regdma_entries_config_t iomux_regs_retention[IOMUX_RETENTION_LINK_LEN]; /** diff --git a/components/soc/esp32c6/system_retention_periph.c b/components/soc/esp32c6/system_retention_periph.c index bc54dc5f438..2ed5ed13425 100644 --- a/components/soc/esp32c6/system_retention_periph.c +++ b/components/soc/esp32c6/system_retention_periph.c @@ -54,7 +54,9 @@ 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) | ENTRY(2) }, /* io_mux */ [1] = { .config = REGDMA_LINK_CONTINUOUS_INIT(REGDMA_IOMUX_LINK(0x01), GPIO_FUNC0_OUT_SEL_CFG_REG, GPIO_FUNC0_OUT_SEL_CFG_REG, N_REGS_IOMUX_1(), 0, 0), .owner = ENTRY(0) | ENTRY(2) }, [2] = { .config = REGDMA_LINK_CONTINUOUS_INIT(REGDMA_IOMUX_LINK(0x02), GPIO_STATUS_NEXT_REG, GPIO_STATUS_NEXT_REG, N_REGS_IOMUX_2(), 0, 0), .owner = ENTRY(0) | ENTRY(2) }, - [3] = { .config = REGDMA_LINK_CONTINUOUS_INIT(REGDMA_IOMUX_LINK(0x03), DR_REG_GPIO_BASE, DR_REG_GPIO_BASE, N_REGS_IOMUX_3(), 0, 0), .owner = ENTRY(0) | ENTRY(2) } + [3] = { .config = REGDMA_LINK_CONTINUOUS_INIT(REGDMA_IOMUX_LINK(0x03), DR_REG_GPIO_BASE, DR_REG_GPIO_BASE, N_REGS_IOMUX_3(), 0, 0), .owner = ENTRY(0) | ENTRY(2) }, + [4] = { .config = REGDMA_LINK_CONTINUOUS_INIT(REGDMA_IOMUX_LINK(0x04), GPIO_ENABLE_REG, GPIO_ENABLE_W1TS_REG, 1, 0, 0), .owner = ENTRY(0) | ENTRY(2) }, + [5] = { .config = REGDMA_LINK_CONTINUOUS_INIT(REGDMA_IOMUX_LINK(0x05), GPIO_ENABLE1_REG, GPIO_ENABLE1_W1TS_REG, 1, 0, 0), .owner = ENTRY(0) | ENTRY(2) }, }; _Static_assert(ARRAY_SIZE(iomux_regs_retention) == IOMUX_RETENTION_LINK_LEN, "Inconsistent IOMUX retention link length definitions"); diff --git a/components/soc/esp32c61/include/soc/system_periph_retention.h b/components/soc/esp32c61/include/soc/system_periph_retention.h index 8b742c3a779..bd57e42bd52 100644 --- a/components/soc/esp32c61/include/soc/system_periph_retention.h +++ b/components/soc/esp32c61/include/soc/system_periph_retention.h @@ -54,7 +54,7 @@ extern const regdma_entries_config_t tee_apm_highpri_regs_retention[TEE_APM_HIGH * This is an internal function of the sleep retention driver, and is not * useful for external use. */ -#define IOMUX_RETENTION_LINK_LEN 7 +#define IOMUX_RETENTION_LINK_LEN 9 extern const regdma_entries_config_t iomux_regs_retention[IOMUX_RETENTION_LINK_LEN]; /** diff --git a/components/soc/esp32c61/system_retention_periph.c b/components/soc/esp32c61/system_retention_periph.c index 951d409c732..7a392804cd9 100644 --- a/components/soc/esp32c61/system_retention_periph.c +++ b/components/soc/esp32c61/system_retention_periph.c @@ -1,5 +1,5 @@ /* - * SPDX-FileCopyrightText: 2024-2025 Espressif Systems (Shanghai) CO LTD + * SPDX-FileCopyrightText: 2024-2026 Espressif Systems (Shanghai) CO LTD * * SPDX-License-Identifier: Apache-2.0 OR MIT */ @@ -49,8 +49,6 @@ _Static_assert((ARRAY_SIZE(tee_apm_regs_retention) == TEE_APM_RETENTION_LINK_LEN #define N_REGS_IOMUX_2() (((GPIO_FUNC121_IN_SEL_CFG_REG - GPIO_FUNC0_IN_SEL_CFG_REG) / 4) + 1) #define N_REGS_IOMUX_3() (SOC_GPIO_PIN_COUNT) #define N_REGS_IOMUX_4() (1) -#define N_REGS_IOMUX_5() (1) -#define N_REGS_IOMUX_6() (1) const regdma_entries_config_t iomux_regs_retention[] = { [0] = { .config = REGDMA_LINK_CONTINUOUS_INIT(REGDMA_IOMUX_LINK(0x00), IO_MUX_GPIO0_REG, IO_MUX_GPIO0_REG, N_REGS_IOMUX_0(), 0, 0), .owner = ENTRY(0) | ENTRY(2) }, /* io_mux */ @@ -58,8 +56,10 @@ const regdma_entries_config_t iomux_regs_retention[] = { [2] = { .config = REGDMA_LINK_CONTINUOUS_INIT(REGDMA_IOMUX_LINK(0x02), GPIO_FUNC0_IN_SEL_CFG_REG, GPIO_FUNC0_IN_SEL_CFG_REG, N_REGS_IOMUX_2(), 0, 0), .owner = ENTRY(0) | ENTRY(2) }, [3] = { .config = REGDMA_LINK_CONTINUOUS_INIT(REGDMA_IOMUX_LINK(0x03), GPIO_PIN0_REG, GPIO_PIN0_REG, N_REGS_IOMUX_3(), 0, 0), .owner = ENTRY(0) | ENTRY(2) }, [4] = { .config = REGDMA_LINK_CONTINUOUS_INIT(REGDMA_IOMUX_LINK(0x04), GPIO_STATUS_REG, GPIO_STATUS_REG, N_REGS_IOMUX_4(), 0, 0), .owner = ENTRY(0) | ENTRY(2) }, - [5] = { .config = REGDMA_LINK_CONTINUOUS_INIT(REGDMA_IOMUX_LINK(0x05), GPIO_ENABLE_REG, GPIO_ENABLE_REG, N_REGS_IOMUX_5(), 0, 0), .owner = ENTRY(0) | ENTRY(2) }, - [6] = { .config = REGDMA_LINK_CONTINUOUS_INIT(REGDMA_IOMUX_LINK(0x06), GPIO_OUT_REG, GPIO_OUT_REG, N_REGS_IOMUX_6(), 0, 0), .owner = ENTRY(0) | ENTRY(2) } + [5] = { .config = REGDMA_LINK_CONTINUOUS_INIT(REGDMA_IOMUX_LINK(0x05), GPIO_ENABLE_REG, GPIO_ENABLE_W1TS_REG, 1, 0, 0), .owner = ENTRY(0) | ENTRY(2) }, + [6] = { .config = REGDMA_LINK_CONTINUOUS_INIT(REGDMA_IOMUX_LINK(0x06), GPIO_ENABLE1_REG, GPIO_ENABLE1_W1TS_REG, 1, 0, 0), .owner = ENTRY(0) | ENTRY(2) }, + [7] = { .config = REGDMA_LINK_CONTINUOUS_INIT(REGDMA_IOMUX_LINK(0x07), GPIO_OUT_REG, GPIO_OUT_W1TS_REG, 1, 0, 0), .owner = ENTRY(0) | ENTRY(2) }, + [8] = { .config = REGDMA_LINK_CONTINUOUS_INIT(REGDMA_IOMUX_LINK(0x08), GPIO_OUT1_REG, GPIO_OUT1_W1TS_REG, 1, 0, 0), .owner = ENTRY(0) | ENTRY(2) }, }; _Static_assert(ARRAY_SIZE(iomux_regs_retention) == IOMUX_RETENTION_LINK_LEN, "Inconsistent IOMUX retention link length definitions"); diff --git a/components/soc/esp32h2/include/soc/system_periph_retention.h b/components/soc/esp32h2/include/soc/system_periph_retention.h index 2b2ac0793b6..b37b239bc57 100644 --- a/components/soc/esp32h2/include/soc/system_periph_retention.h +++ b/components/soc/esp32h2/include/soc/system_periph_retention.h @@ -54,7 +54,7 @@ extern const regdma_entries_config_t tee_apm_highpri_regs_retention[TEE_APM_HIGH * This is an internal function of the sleep retention driver, and is not * useful for external use. */ -#define IOMUX_RETENTION_LINK_LEN 4 +#define IOMUX_RETENTION_LINK_LEN 5 extern const regdma_entries_config_t iomux_regs_retention[IOMUX_RETENTION_LINK_LEN]; /** diff --git a/components/soc/esp32h2/system_retention_periph.c b/components/soc/esp32h2/system_retention_periph.c index 0354a855609..908c16e7fe4 100644 --- a/components/soc/esp32h2/system_retention_periph.c +++ b/components/soc/esp32h2/system_retention_periph.c @@ -52,7 +52,8 @@ 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) | ENTRY(2) }, /* io_mux */ [1] = { .config = REGDMA_LINK_CONTINUOUS_INIT(REGDMA_IOMUX_LINK(0x01), GPIO_FUNC0_OUT_SEL_CFG_REG, GPIO_FUNC0_OUT_SEL_CFG_REG, N_REGS_IOMUX_1(), 0, 0), .owner = ENTRY(0) | ENTRY(2) }, [2] = { .config = REGDMA_LINK_CONTINUOUS_INIT(REGDMA_IOMUX_LINK(0x02), GPIO_STATUS_NEXT_REG, GPIO_STATUS_NEXT_REG, N_REGS_IOMUX_2(), 0, 0), .owner = ENTRY(0) | ENTRY(2) }, - [3] = { .config = REGDMA_LINK_CONTINUOUS_INIT(REGDMA_IOMUX_LINK(0x03), DR_REG_GPIO_BASE, DR_REG_GPIO_BASE, N_REGS_IOMUX_3(), 0, 0), .owner = ENTRY(0) | ENTRY(2) } + [3] = { .config = REGDMA_LINK_CONTINUOUS_INIT(REGDMA_IOMUX_LINK(0x03), DR_REG_GPIO_BASE, DR_REG_GPIO_BASE, N_REGS_IOMUX_3(), 0, 0), .owner = ENTRY(0) | ENTRY(2) }, + [4] = { .config = REGDMA_LINK_CONTINUOUS_INIT(REGDMA_IOMUX_LINK(0x04), GPIO_ENABLE_REG, GPIO_ENABLE_W1TS_REG, 1, 0, 0), .owner = ENTRY(0) | ENTRY(2) }, }; _Static_assert(ARRAY_SIZE(iomux_regs_retention) == IOMUX_RETENTION_LINK_LEN, "Inconsistent IOMUX retention link length definitions"); diff --git a/components/soc/esp32h21/include/soc/system_periph_retention.h b/components/soc/esp32h21/include/soc/system_periph_retention.h index a26f496ef01..3ce2111add6 100644 --- a/components/soc/esp32h21/include/soc/system_periph_retention.h +++ b/components/soc/esp32h21/include/soc/system_periph_retention.h @@ -54,7 +54,7 @@ extern const regdma_entries_config_t tee_apm_highpri_regs_retention[TEE_APM_HIGH * This is an internal function of the sleep retention driver, and is not * useful for external use. */ -#define IOMUX_RETENTION_LINK_LEN 4 +#define IOMUX_RETENTION_LINK_LEN 5 extern const regdma_entries_config_t iomux_regs_retention[IOMUX_RETENTION_LINK_LEN]; /** diff --git a/components/soc/esp32h21/system_retention_periph.c b/components/soc/esp32h21/system_retention_periph.c index 1eb2e9dc801..fef63f9a68e 100644 --- a/components/soc/esp32h21/system_retention_periph.c +++ b/components/soc/esp32h21/system_retention_periph.c @@ -65,6 +65,7 @@ const regdma_entries_config_t iomux_regs_retention[] = { [1] = { .config = REGDMA_LINK_CONTINUOUS_INIT(REGDMA_IOMUX_LINK(0x01), DR_REG_GPIO_BASE, DR_REG_GPIO_BASE, N_REGS_IOMUX_1(), 0, 0), .owner = ENTRY(0) | ENTRY(2) }, [2] = { .config = REGDMA_LINK_CONTINUOUS_INIT(REGDMA_IOMUX_LINK(0x02), GPIO_FUNC0_IN_SEL_CFG_REG, GPIO_FUNC0_IN_SEL_CFG_REG, N_REGS_IOMUX_2(), 0, 0), .owner = ENTRY(0) | ENTRY(2) }, [3] = { .config = REGDMA_LINK_CONTINUOUS_INIT(REGDMA_IOMUX_LINK(0x03), GPIO_FUNC0_OUT_SEL_CFG_REG, GPIO_FUNC0_OUT_SEL_CFG_REG, N_REGS_IOMUX_3(), 0, 0), .owner = ENTRY(0) | ENTRY(2) }, + [4] = { .config = REGDMA_LINK_CONTINUOUS_INIT(REGDMA_IOMUX_LINK(0x04), GPIO_ENABLE_REG, GPIO_ENABLE_W1TS_REG, 1, 0, 0), .owner = ENTRY(0) | ENTRY(2) }, }; _Static_assert(ARRAY_SIZE(iomux_regs_retention) == IOMUX_RETENTION_LINK_LEN, "Inconsistent IOMUX retention link length definitions"); diff --git a/components/soc/esp32h4/include/soc/system_periph_retention.h b/components/soc/esp32h4/include/soc/system_periph_retention.h index c547bc2c59d..8b6444b8f37 100644 --- a/components/soc/esp32h4/include/soc/system_periph_retention.h +++ b/components/soc/esp32h4/include/soc/system_periph_retention.h @@ -54,7 +54,7 @@ extern const regdma_entries_config_t tee_apm_highpri_regs_retention[TEE_APM_HIGH * This is an internal function of the sleep retention driver, and is not * useful for external use. */ -#define IOMUX_RETENTION_LINK_LEN 5 +#define IOMUX_RETENTION_LINK_LEN 7 extern const regdma_entries_config_t iomux_regs_retention[IOMUX_RETENTION_LINK_LEN]; /** diff --git a/components/soc/esp32h4/system_retention_periph.c b/components/soc/esp32h4/system_retention_periph.c index 0b6a7ec2f2e..fb2934c48ee 100644 --- a/components/soc/esp32h4/system_retention_periph.c +++ b/components/soc/esp32h4/system_retention_periph.c @@ -61,7 +61,9 @@ const regdma_entries_config_t iomux_regs_retention[] = { [1] = { .config = REGDMA_LINK_CONTINUOUS_INIT(REGDMA_IOMUX_LINK(0x01), GPIO_FUNC0_OUT_SEL_CFG_REG, GPIO_FUNC0_OUT_SEL_CFG_REG, N_REGS_IOMUX_1(), 0, 0), .owner = ENTRY(0) | ENTRY(2) }, [2] = { .config = REGDMA_LINK_CONTINUOUS_INIT(REGDMA_IOMUX_LINK(0x02), GPIO_FUNC0_IN_SEL_CFG_REG, GPIO_FUNC0_IN_SEL_CFG_REG, N_REGS_IOMUX_2(), 0, 0), .owner = ENTRY(0) | ENTRY(2) }, [3] = { .config = REGDMA_LINK_CONTINUOUS_INIT(REGDMA_IOMUX_LINK(0x03), GPIO_PIN0_REG, GPIO_PIN0_REG, N_REGS_IOMUX_3(), 0, 0), .owner = ENTRY(0) | ENTRY(2) }, - [4] = { .config = REGDMA_LINK_CONTINUOUS_INIT(REGDMA_IOMUX_LINK(0x04), GPIO_OUT_REG, GPIO_OUT_REG, N_REGS_IOMUX_4(), 0, 0), .owner = ENTRY(0) | ENTRY(2) } + [4] = { .config = REGDMA_LINK_CONTINUOUS_INIT(REGDMA_IOMUX_LINK(0x04), GPIO_OUT_REG, GPIO_OUT_W1TS_REG, N_REGS_IOMUX_4(), 0, 0), .owner = ENTRY(0) | ENTRY(2) }, + [5] = { .config = REGDMA_LINK_CONTINUOUS_INIT(REGDMA_IOMUX_LINK(0x05), GPIO_ENABLE_REG, GPIO_ENABLE_W1TS_REG, 1, 0, 0), .owner = ENTRY(0) | ENTRY(2) }, + [6] = { .config = REGDMA_LINK_CONTINUOUS_INIT(REGDMA_IOMUX_LINK(0x06), GPIO_ENABLE1_REG, GPIO_ENABLE1_W1TS_REG, 1, 0, 0), .owner = ENTRY(0) | ENTRY(2) }, }; _Static_assert(ARRAY_SIZE(iomux_regs_retention) == IOMUX_RETENTION_LINK_LEN, "Inconsistent IOMUX retention link length definitions"); diff --git a/components/soc/esp32p4/include/soc/system_periph_retention.h b/components/soc/esp32p4/include/soc/system_periph_retention.h index e4294b84759..3e95cb002fd 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 6 +#define IOMUX_RETENTION_LINK_LEN 8 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 66fba2354f6..cfca9276f6f 100644 --- a/components/soc/esp32p4/system_retention_periph.c +++ b/components/soc/esp32p4/system_retention_periph.c @@ -116,11 +116,12 @@ const regdma_entries_config_t iomux_regs_retention[] = { [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) }, + [6] = { .config = REGDMA_LINK_CONTINUOUS_INIT(REGDMA_IOMUX_LINK(0x06), GPIO_ENABLE_REG, GPIO_ENABLE_W1TS_REG, 1, 0, 0), .owner = ENTRY(0) }, + [7] = { .config = REGDMA_LINK_CONTINUOUS_INIT(REGDMA_IOMUX_LINK(0x07), GPIO_ENABLE1_REG, GPIO_ENABLE1_W1TS_REG, 1, 0, 0), .owner = ENTRY(0) }, }; _Static_assert(ARRAY_SIZE(iomux_regs_retention) == IOMUX_RETENTION_LINK_LEN, "Inconsistent IOMUX retention link length definitions"); diff --git a/components/soc/esp32s31/include/soc/system_periph_retention.h b/components/soc/esp32s31/include/soc/system_periph_retention.h index 29b30a18efd..50d3028e3d2 100644 --- a/components/soc/esp32s31/include/soc/system_periph_retention.h +++ b/components/soc/esp32s31/include/soc/system_periph_retention.h @@ -62,7 +62,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 4 +#define IOMUX_RETENTION_LINK_LEN 6 extern const regdma_entries_config_t iomux_regs_retention[IOMUX_RETENTION_LINK_LEN]; /** diff --git a/components/soc/esp32s31/system_retention_periph.c b/components/soc/esp32s31/system_retention_periph.c index 3876a816785..57ac5b638de 100644 --- a/components/soc/esp32s31/system_retention_periph.c +++ b/components/soc/esp32s31/system_retention_periph.c @@ -107,6 +107,8 @@ const regdma_entries_config_t iomux_regs_retention[] = { /* GPIO_FUNC0_IN_SEL_CFG_REG ~ GPIO_FUNC255_IN_SEL_CFG_REG & GPIO_FUNC0_OUT_SEL_CFG_REG ~ GPIO_FUNC0_IN_SEL_CFG_REG */ [2] = { .config = REGDMA_LINK_CONTINUOUS_INIT(REGDMA_IOMUX_LINK(0x02), GPIO_FUNC0_IN_SEL_CFG_REG, GPIO_FUNC0_IN_SEL_CFG_REG, N_REGS_GPIO_FUNC_IN(), 0, 0), .owner = ENTRY(0) | ENTRY(2) }, [3] = { .config = REGDMA_LINK_CONTINUOUS_INIT(REGDMA_IOMUX_LINK(0x03), GPIO_FUNC0_OUT_SEL_CFG_REG, GPIO_FUNC0_OUT_SEL_CFG_REG, N_REGS_GPIO_FUNC_OUT(), 0, 0), .owner = ENTRY(0) | ENTRY(2) }, + [4] = { .config = REGDMA_LINK_CONTINUOUS_INIT(REGDMA_IOMUX_LINK(0x04), GPIO_ENABLE_REG, GPIO_ENABLE_W1TS_REG, 1, 0, 0), .owner = ENTRY(0) | ENTRY(2) }, + [5] = { .config = REGDMA_LINK_CONTINUOUS_INIT(REGDMA_IOMUX_LINK(0x05), GPIO_ENABLE1_REG, GPIO_ENABLE1_W1TS_REG, 1, 0, 0), .owner = ENTRY(0) | ENTRY(2) }, }; _Static_assert(ARRAY_SIZE(iomux_regs_retention) == IOMUX_RETENTION_LINK_LEN, "Inconsistent IOMUX retention link length definitions"); From a37e430ad10d76371495336b1d546338a9fa0191 Mon Sep 17 00:00:00 2001 From: wuzhenghui Date: Tue, 14 Apr 2026 11:23:18 +0800 Subject: [PATCH 11/13] change(ci): disable esp32s31 multi_device test since lack of runner --- .../templates/known_generate_test_child_pipeline_warnings.yml | 1 + 1 file changed, 1 insertion(+) diff --git a/tools/ci/dynamic_pipelines/templates/known_generate_test_child_pipeline_warnings.yml b/tools/ci/dynamic_pipelines/templates/known_generate_test_child_pipeline_warnings.yml index f2ad651ce26..d60e668ef53 100644 --- a/tools/ci/dynamic_pipelines/templates/known_generate_test_child_pipeline_warnings.yml +++ b/tools/ci/dynamic_pipelines/templates/known_generate_test_child_pipeline_warnings.yml @@ -28,3 +28,4 @@ no_runner_tags: - esp32p4,jtag - esp32s2,usb_host_flash_disk - esp32s31,usb_host_flash_disk + - esp32s31_2,generic_multi_device,rev_default From 820475f0e7705ce0ee4860aaa6ac2bd8bb7479e6 Mon Sep 17 00:00:00 2001 From: wuzhenghui Date: Fri, 24 Apr 2026 20:35:55 +0800 Subject: [PATCH 12/13] fix(esp_hw_support): 1v8 flash/psram dedicate ldo depends on pd_cur on --- .../esp_hw_support/include/esp_private/esp_pmu.h | 3 +++ .../esp_hw_support/port/esp32s31/pmu_init.c | 14 +++++++++++++- .../esp_hw_support/port/esp32s31/pmu_sleep.c | 8 ++++++++ .../port/esp32s31/private_include/pmu_param.h | 4 ++-- components/hal/esp32s31/include/hal/efuse_ll.h | 16 ++++++++++++++++ .../soc/esp32s31/include/soc/Kconfig.soc_caps.in | 4 ++++ components/soc/esp32s31/include/soc/soc_caps.h | 1 + 7 files changed, 47 insertions(+), 3 deletions(-) diff --git a/components/esp_hw_support/include/esp_private/esp_pmu.h b/components/esp_hw_support/include/esp_private/esp_pmu.h index af7cc9042cb..3383455878a 100644 --- a/components/esp_hw_support/include/esp_private/esp_pmu.h +++ b/components/esp_hw_support/include/esp_private/esp_pmu.h @@ -199,6 +199,9 @@ typedef struct { #if SOC_PM_SLEEP_CLK_ICG_USE_REGDMA void *priv; #endif +#if SOC_SPI_FLASH_HAS_DEDICATED_LDO + bool flash_ldo_volt_1v8; +#endif } pmu_context_t; pmu_context_t * PMU_instance(void); diff --git a/components/esp_hw_support/port/esp32s31/pmu_init.c b/components/esp_hw_support/port/esp32s31/pmu_init.c index 508c01e666f..5cbaf821e74 100644 --- a/components/esp_hw_support/port/esp32s31/pmu_init.c +++ b/components/esp_hw_support/port/esp32s31/pmu_init.c @@ -5,6 +5,7 @@ */ #include +#include #include #include #include "sdkconfig.h" @@ -13,6 +14,9 @@ #include "soc/soc.h" #include "soc/pmu_struct.h" #include "hal/efuse_hal.h" +#include "hal/efuse_ll.h" +#include "hal/gpio_ll.h" +#include "hal/gpio_types.h" #include "hal/pmu_hal.h" #include "pmu_param.h" #include "esp_private/esp_pmu.h" @@ -38,9 +42,17 @@ pmu_context_t * __attribute__((weak)) IRAM_ATTR PMU_instance(void) { /* It should be explicitly defined in the internal RAM, because this * instance will be used in pmu_sleep.c */ - static DRAM_ATTR pmu_hal_context_t pmu_hal = { .dev = &PMU }; + static DRAM_ATTR pmu_hal_context_t pmu_hal = { .dev = NULL }; static DRAM_ATTR pmu_sleep_machine_constant_t pmu_mc = PMU_SLEEP_MC_DEFAULT(); static DRAM_ATTR pmu_context_t pmu_context = { .hal = &pmu_hal, .mc = (void *)&pmu_mc }; + + if (pmu_hal.dev == NULL) { + pmu_hal.dev = &PMU; + const uint32_t sel = efuse_ll_get_flash_power_sel(); + const uint32_t en = efuse_ll_get_flash_power_sel_en(); + const int g36 = gpio_ll_get_level(GPIO_LL_GET_HW(0), (uint32_t)GPIO_NUM_36); + pmu_context.flash_ldo_volt_1v8 = (sel == 0 && g36 == 0) || (sel == 1 && en == 0); + } return &pmu_context; } diff --git a/components/esp_hw_support/port/esp32s31/pmu_sleep.c b/components/esp_hw_support/port/esp32s31/pmu_sleep.c index a13585a6bd7..3abb86e68c7 100644 --- a/components/esp_hw_support/port/esp32s31/pmu_sleep.c +++ b/components/esp_hw_support/port/esp32s31/pmu_sleep.c @@ -212,6 +212,14 @@ const pmu_sleep_config_t* pmu_sleep_config_default( // Get light sleep analog default pmu_sleep_analog_config_t analog_default = PMU_SLEEP_ANALOG_LSLP_CONFIG_DEFAULT(sleep_flags); +#if !CONFIG_SPIRAM + if (PMU_instance()->flash_ldo_volt_1v8) +#endif + { + analog_default.hp_sys.analog.pd_cur = PMU_PD_CUR_SLEEP_ON; + analog_default.lp_sys[LP(SLEEP)].analog.pd_cur = PMU_PD_CUR_SLEEP_ON; + } + if (!(sleep_flags & PMU_SLEEP_PD_XTAL)) { // Analog parameters in HP_SLEEP diff --git a/components/esp_hw_support/port/esp32s31/private_include/pmu_param.h b/components/esp_hw_support/port/esp32s31/private_include/pmu_param.h index cb8f5c1f0e1..48b4555cd97 100644 --- a/components/esp_hw_support/port/esp32s31/private_include/pmu_param.h +++ b/components/esp_hw_support/port/esp32s31/private_include/pmu_param.h @@ -360,7 +360,7 @@ typedef struct { .hp_sys = { \ .analog = { \ .drv_b = PMU_HP_DRVB_LIGHTSLEEP, \ - .pd_cur = PMU_PD_CUR_SLEEP_ON, \ + .pd_cur = PMU_PD_CUR_SLEEP_DEFAULT, \ .bias_sleep = PMU_BIASSLP_SLEEP_DEFAULT, \ .xpd = PMU_HP_XPD_LIGHTSLEEP, \ .dbg_atten = PMU_DBG_ATTEN_LIGHTSLEEP_DEFAULT, \ @@ -370,7 +370,7 @@ typedef struct { .lp_sys[PMU_MODE_LP_SLEEP] = { \ .analog = { \ .drv_b = PMU_LP_DRVB_LIGHTSLEEP, \ - .pd_cur = PMU_PD_CUR_SLEEP_ON, \ + .pd_cur = PMU_PD_CUR_SLEEP_DEFAULT, \ .bias_sleep = PMU_BIASSLP_SLEEP_DEFAULT, \ .slp_xpd = PMU_LP_SLP_XPD_SLEEP_DEFAULT, \ .slp_dbias = PMU_LP_SLP_DBIAS_SLEEP_DEFAULT, \ diff --git a/components/hal/esp32s31/include/hal/efuse_ll.h b/components/hal/esp32s31/include/hal/efuse_ll.h index 4f3b0f7bf1f..c1e90442d8b 100644 --- a/components/hal/esp32s31/include/hal/efuse_ll.h +++ b/components/hal/esp32s31/include/hal/efuse_ll.h @@ -142,6 +142,22 @@ __attribute__((always_inline)) static inline uint32_t efuse_ll_get_controller_st return EFUSE.status.state; } +/** + * @brief Flash power select eFuse bit (see VDD_SPI voltage table; not the final rail by itself) + */ +__attribute__((always_inline)) static inline uint32_t efuse_ll_get_flash_power_sel(void) +{ + return EFUSE.rd_repeat_data6.pmu_flash_power_sel; +} + +/** + * @brief Flash power select valid eFuse bit (1: `flash_power_sel` is valid, 0: not) + */ +__attribute__((always_inline)) static inline uint32_t efuse_ll_get_flash_power_sel_en(void) +{ + return EFUSE.rd_repeat_data6.pmu_flash_power_sel_en; +} + /******************* eFuse control functions *************************/ #ifdef __cplusplus diff --git a/components/soc/esp32s31/include/soc/Kconfig.soc_caps.in b/components/soc/esp32s31/include/soc/Kconfig.soc_caps.in index ca45cf59db6..3ea2ff0aa3c 100644 --- a/components/soc/esp32s31/include/soc/Kconfig.soc_caps.in +++ b/components/soc/esp32s31/include/soc/Kconfig.soc_caps.in @@ -227,6 +227,10 @@ config SOC_SPI_FLASH_SUPPORTED bool default y +config SOC_SPI_FLASH_HAS_DEDICATED_LDO + bool + default y + config SOC_TOUCH_SENSOR_SUPPORTED bool default y diff --git a/components/soc/esp32s31/include/soc/soc_caps.h b/components/soc/esp32s31/include/soc/soc_caps.h index 988dfe2a0da..1abda0792cc 100644 --- a/components/soc/esp32s31/include/soc/soc_caps.h +++ b/components/soc/esp32s31/include/soc/soc_caps.h @@ -94,6 +94,7 @@ #define SOC_WDT_SUPPORTED 1 #define SOC_RTC_WDT_SUPPORTED 1 #define SOC_SPI_FLASH_SUPPORTED 1 // TODO: [ESP32S31] IDF-14777 +#define SOC_SPI_FLASH_HAS_DEDICATED_LDO 1 #define SOC_TOUCH_SENSOR_SUPPORTED 1 // #define SOC_RNG_SUPPORTED 1 // TODO: [ESP32S31] IDF-14632 #define SOC_PPA_SUPPORTED 1 From 3a40436e634c1c9755278460911a91a6081c459d Mon Sep 17 00:00:00 2001 From: wuzhenghui Date: Mon, 27 Apr 2026 14:30:45 +0800 Subject: [PATCH 13/13] refactor(esp_hw_support): refactor cache ops to sleep_cache.c --- components/esp_hw_support/CMakeLists.txt | 1 + .../include/esp_private/sleep_cache.h | 36 +++++++++ components/esp_hw_support/linker.lf | 1 + components/esp_hw_support/sleep_cache.c | 81 +++++++++++++++++++ components/esp_hw_support/sleep_modes.c | 59 +++----------- 5 files changed, 129 insertions(+), 49 deletions(-) create mode 100644 components/esp_hw_support/include/esp_private/sleep_cache.h create mode 100644 components/esp_hw_support/sleep_cache.c diff --git a/components/esp_hw_support/CMakeLists.txt b/components/esp_hw_support/CMakeLists.txt index 7c059e65c45..1802f717e84 100644 --- a/components/esp_hw_support/CMakeLists.txt +++ b/components/esp_hw_support/CMakeLists.txt @@ -66,6 +66,7 @@ if(NOT non_os_build) endif() if(CONFIG_SOC_LIGHT_SLEEP_SUPPORTED) list(APPEND srcs "sleep_modem.c" + "sleep_cache.c" "sleep_modes.c" "sleep_uart.c" "sleep_console.c" diff --git a/components/esp_hw_support/include/esp_private/sleep_cache.h b/components/esp_hw_support/include/esp_private/sleep_cache.h new file mode 100644 index 00000000000..3079b2ad42a --- /dev/null +++ b/components/esp_hw_support/include/esp_private/sleep_cache.h @@ -0,0 +1,36 @@ +/* + * SPDX-FileCopyrightText: 2026 Espressif Systems (Shanghai) CO LTD + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#pragma once + +#include +#include "sdkconfig.h" + +#ifdef __cplusplus +extern "C" { +#endif + +/** + * @brief Nestable suspend: disable external-mem cache on the first call (refcounted). Critical section; pair with @ref sleep_cache_resume. + */ +void sleep_cache_suspend(void); + +/** + * @brief Nestable resume: restore cache when the refcount returns to zero. Must match @ref sleep_cache_suspend. + */ +void sleep_cache_resume(void); + +#if !CONFIG_SOC_CACHE_INTERNAL_MEM_VIA_L1CACHE && CONFIG_SPIRAM +/** + * @brief Cache writeback before sleep when CPU/cache may power down (SPIRAM / PAU paths; Kconfig-driven). + * @param sleep_flags Same flags as light sleep, e.g. @c PMU_SLEEP_PD_CPU when relevant. + */ +void sleep_cache_safe_writeback(uint32_t sleep_flags); +#endif + +#ifdef __cplusplus +} +#endif diff --git a/components/esp_hw_support/linker.lf b/components/esp_hw_support/linker.lf index b4aaaadc6e0..1d227091472 100644 --- a/components/esp_hw_support/linker.lf +++ b/components/esp_hw_support/linker.lf @@ -37,6 +37,7 @@ entries: rtc_sleep:rtc_sleep_pd (noflash) if IDF_TARGET_ESP32S3 = y || IDF_TARGET_ESP32C2 = y || IDF_TARGET_ESP32C3 = y: rtc_sleep:rtc_sleep_pu (noflash) + sleep_cache (noflash) if SOC_PMU_SUPPORTED = y: if SPIRAM_FLASH_LOAD_TO_PSRAM = y: pmu_init (noflash) diff --git a/components/esp_hw_support/sleep_cache.c b/components/esp_hw_support/sleep_cache.c new file mode 100644 index 00000000000..7d695cd2cd0 --- /dev/null +++ b/components/esp_hw_support/sleep_cache.c @@ -0,0 +1,81 @@ +/* + * SPDX-FileCopyrightText: 2026 Espressif Systems (Shanghai) CO LTD + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#include +#include +#include "sdkconfig.h" +#include "soc/soc_caps.h" +#include "esp_attr.h" +#include "esp_cpu.h" +#include "esp_private/sleep_cache.h" +#include "esp_private/cache_utils.h" +#include "hal/cache_ll.h" + +#if SOC_PMU_SUPPORTED +#include "esp_private/esp_pmu.h" +#endif + +static int s_cache_suspend_cnt = 0; +static uint32_t s_cache_state = 0; + +// Must be called from critical sections. +void sleep_cache_suspend(void) +{ + s_cache_suspend_cnt++; + if (s_cache_suspend_cnt == 1) { +#if CONFIG_ESP_SLEEP_CACHE_SAFE_ASSERTION && CONFIG_IDF_TARGET_ESP32P4 + // The implementation of P4 L2 cache suspend is to shut down MSPI AXI instead of shutting down Cache BUS. + // If the access to external memory hits in the cache, it will not trigger a cache error. So in order to + // fully check the access to external memory, writeback & invalidate is needed here. + Cache_WriteBack_Invalidate_All(CACHE_MAP_MASK); +#endif + spi_flash_disable_cache(esp_cpu_get_core_id(), &s_cache_state); + } +} + +// Must be called from critical sections. +void sleep_cache_resume(void) +{ + s_cache_suspend_cnt--; + assert(s_cache_suspend_cnt >= 0 && DRAM_STR("cache resume doesn't match suspend ops")); + if (s_cache_suspend_cnt == 0) { + spi_flash_restore_cache(esp_cpu_get_core_id(), s_cache_state); + } +} + +#if !CONFIG_SOC_CACHE_INTERNAL_MEM_VIA_L1CACHE && CONFIG_SPIRAM +void sleep_cache_safe_writeback(uint32_t sleep_flags) +{ +#if CONFIG_PM_POWER_DOWN_CPU_IN_LIGHT_SLEEP && !SOC_PM_CACHE_RETENTION_BY_PAU && SOC_EXT_MEM_CACHE_TAG_IN_CPU_DOMAIN +#if SOC_PMU_SUPPORTED + /* When SPIRAM is using, we need to use Cache_WriteBack_All to protect SPIRAM data + because the cache powers down when we power down the CPU */ + if (sleep_flags & PMU_SLEEP_PD_CPU) { + if (s_cache_suspend_cnt) { + spi_flash_restore_cache(esp_cpu_get_core_id(), s_cache_state); + } + Cache_WriteBack_All(); + if (s_cache_suspend_cnt) { + spi_flash_disable_cache(esp_cpu_get_core_id(), &s_cache_state); + } + } +#else + (void)sleep_flags; +#endif +#elif CONFIG_PM_POWER_DOWN_PERIPHERAL_IN_LIGHT_SLEEP && SOC_PM_CACHE_RETENTION_BY_PAU + (void)sleep_flags; + if (s_cache_suspend_cnt) { + spi_flash_restore_cache(esp_cpu_get_core_id(), s_cache_state); + } + Cache_WriteBack_All(CACHE_MAP_L1_DCACHE); + if (s_cache_suspend_cnt) { + spi_flash_disable_cache(esp_cpu_get_core_id(), &s_cache_state); + } +#else + (void)sleep_flags; +#endif +} +#endif diff --git a/components/esp_hw_support/sleep_modes.c b/components/esp_hw_support/sleep_modes.c index 18e618ed59c..086e4b23bea 100644 --- a/components/esp_hw_support/sleep_modes.c +++ b/components/esp_hw_support/sleep_modes.c @@ -66,7 +66,6 @@ #include "soc/rtc.h" -#include "hal/cache_ll.h" #include "hal/clk_tree_ll.h" #if SOC_WDT_SUPPORTED || SOC_RTC_WDT_SUPPORTED || SOC_SLEEP_TGWDT_STOP_WORKAROUND #include "hal/wdt_hal.h" @@ -88,11 +87,11 @@ #include "sdkconfig.h" #include "esp_rom_serial_output.h" #include "esp_rom_sys.h" -#include "esp_private/cache_utils.h" #include "esp_private/brownout.h" #include "esp_private/sleep_console.h" #include "esp_private/sleep_uart.h" #include "esp_private/sleep_cpu.h" +#include "esp_private/sleep_cache.h" #include "esp_private/sleep_modem.h" #include "esp_private/sleep_flash.h" #include "esp_private/sleep_usb.h" @@ -105,7 +104,6 @@ #endif #ifdef CONFIG_IDF_TARGET_ESP32 -#include "esp32/rom/cache.h" #include "esp32/rom/rtc.h" #include "esp_private/gpio.h" #elif CONFIG_IDF_TARGET_ESP32S2 @@ -131,16 +129,13 @@ #include "hal/gpio_ll.h" #elif CONFIG_IDF_TARGET_ESP32H2 #include "esp32h2/rom/rtc.h" -#include "esp32h2/rom/cache.h" #include "soc/extmem_reg.h" #include "hal/gpio_ll.h" #elif CONFIG_IDF_TARGET_ESP32H21 #include "esp32h21/rom/rtc.h" -#include "esp32h21/rom/cache.h" #include "hal/gpio_ll.h" #elif CONFIG_IDF_TARGET_ESP32H4 #include "esp32h4/rom/rtc.h" -#include "esp32h4/rom/cache.h" #include "hal/gpio_ll.h" #elif CONFIG_IDF_TARGET_ESP32P4 #include "esp32p4/rom/rtc.h" @@ -586,32 +581,6 @@ static void s_do_deep_sleep_phy_callback(void) } #endif -static int s_cache_suspend_cnt = 0; -static uint32_t s_cache_state = 0; - -// Must be called from critical sections. -static void FORCE_IRAM_ATTR suspend_cache(void) { - s_cache_suspend_cnt++; - if (s_cache_suspend_cnt == 1) { -#if CONFIG_ESP_SLEEP_CACHE_SAFE_ASSERTION && CONFIG_IDF_TARGET_ESP32P4 - // The implementation of P4 L2 cache suspend is to shut down MSPI AXI instead of shutting down Cache BUS. - // If the access to external memory hits in the cache, it will not trigger a cache error. So in order to - // fully check the access to external memory, writeback & invalidate is needed here. - Cache_WriteBack_Invalidate_All(CACHE_MAP_MASK); -#endif - spi_flash_disable_cache(esp_cpu_get_core_id(), &s_cache_state); - } -} - -// Must be called from critical sections. -static void FORCE_IRAM_ATTR resume_cache(void) { - s_cache_suspend_cnt--; - assert(s_cache_suspend_cnt >= 0 && DRAM_STR("cache resume doesn't match suspend ops")); - if (s_cache_suspend_cnt == 0) { - spi_flash_restore_cache(esp_cpu_get_core_id(), s_cache_state); - } -} - #if SOC_SLEEP_TGWDT_STOP_WORKAROUND static uint32_t s_stopped_tgwdt_bmap = 0; #endif @@ -718,16 +687,8 @@ static SLEEP_FN_ATTR void misc_modules_sleep_prepare(uint32_t sleep_flags, bool #if CONFIG_PM_POWER_DOWN_CPU_IN_LIGHT_SLEEP && SOC_PM_CPU_RETENTION_BY_RTCCNTL sleep_enable_cpu_retention(); #endif -#if CONFIG_PM_POWER_DOWN_CPU_IN_LIGHT_SLEEP && SOC_PM_CPU_RETENTION_BY_SW && SOC_EXT_MEM_CACHE_TAG_IN_CPU_DOMAIN && CONFIG_SPIRAM - /* When using SPIRAM on the ESP32-C5, we need to use Cache_WriteBack_All to protect SPIRAM data - because the cache powers down when we power down the CPU */ - if(sleep_flags & PMU_SLEEP_PD_CPU) { - #if SOC_SHARED_IDCACHE_SUPPORTED || CONFIG_IDF_TARGET_ESP32H4 - Cache_WriteBack_All(); -#else - Cache_WriteBack_All(CACHE_MAP_L1_DCACHE); -#endif - } +#if !SOC_CACHE_INTERNAL_MEM_VIA_L1CACHE && CONFIG_SPIRAM + sleep_cache_safe_writeback(sleep_flags); #endif #if ADC_LL_ANA_CALI_REG_PD_WORKAROUND adc_hal_i2c_saradc_reg_backup(); @@ -941,7 +902,7 @@ static esp_err_t FORCE_IRAM_ATTR esp_sleep_start_safe(uint32_t sleep_flags, uint } #endif /* Cache Suspend 1: will wait cache idle in cache suspend */ - suspend_cache(); + sleep_cache_suspend(); if (!(sleep_flags & RTC_SLEEP_PD_VDDSDIO)) { #if CONFIG_ESP_SLEEP_SET_FLASH_DPD if (sleep_flags & RTC_SLEEP_FLASH_DPD) { @@ -1038,7 +999,7 @@ static esp_err_t FORCE_IRAM_ATTR esp_sleep_start_safe(uint32_t sleep_flags, uint } #endif /* Cache Resume 1: Resume cache for continue running*/ - resume_cache(); + sleep_cache_resume(); #if CONFIG_PM_SLP_SPIRAM_HALFSLEEP_ENABLED && CONFIG_SPIRAM_XIP_FROM_PSRAM // Code outside of esp_sleep_start_safe may be linked to FLASH, and if CONFIG_SPIRAM_XIP_FROM_PSRAM // is enabled, code in Flash will be copied to PSRAM for execution. We need to wait here until @@ -1223,7 +1184,7 @@ static esp_err_t SLEEP_FN_ATTR esp_sleep_start(uint32_t sleep_flags, uint32_t cl if (sleep_flags & RTC_SLEEP_PD_VDDSDIO) { /* Cache Suspend 2: If previous sleep powerdowned the flash, suspend cache here so that the access to flash before flash ready can be explicitly exposed. */ - suspend_cache(); + sleep_cache_suspend(); } #endif // Restore CPU frequency @@ -1372,7 +1333,7 @@ static esp_err_t FORCE_IRAM_ATTR deep_sleep_start(bool allow_sleep_rejection) err = ESP_ERR_SLEEP_REJECT; #if CONFIG_ESP_SLEEP_CACHE_SAFE_ASSERTION /* Cache Resume 2: if CONFIG_ESP_SLEEP_CACHE_SAFE_ASSERTION is enabled, cache has been suspended in esp_sleep_start */ - resume_cache(); + sleep_cache_resume(); #endif ESP_EARLY_LOGE(TAG, "Deep sleep request is rejected"); } else { @@ -1458,7 +1419,7 @@ static SLEEP_FN_ATTR esp_err_t esp_light_sleep_inner(uint32_t sleep_flags, uint3 #if CONFIG_ESP_SLEEP_CACHE_SAFE_ASSERTION if (sleep_flags & RTC_SLEEP_PD_VDDSDIO) { /* Cache Resume 2: flash is ready now, we can resume the cache and access flash safely after */ - resume_cache(); + sleep_cache_resume(); } #endif @@ -1557,7 +1518,7 @@ esp_err_t esp_light_sleep_start(void) ignore_check_wakeup_triggers |= RTC_EXT1_TRIG_EN; #endif if (!(s_config.wakeup_triggers & ignore_check_wakeup_triggers)) { - suspend_cache(); + sleep_cache_suspend(); } #endif @@ -1742,7 +1703,7 @@ esp_err_t esp_light_sleep_start(void) #if CONFIG_ESP_SLEEP_CACHE_SAFE_ASSERTION && CONFIG_PM_SLP_IRAM_OPT /* Cache Resume 0: sleep process done, resume cache for continue running */ if (!(s_config.wakeup_triggers & ignore_check_wakeup_triggers)) { - resume_cache(); + sleep_cache_resume(); } #endif