From 7c10126309426055af4f0df889282c320720fc8d Mon Sep 17 00:00:00 2001 From: wuzhenghui Date: Tue, 7 Apr 2026 15:31:30 +0800 Subject: [PATCH] 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: