Merge branch 'bugfix/esp32s31_sdmmc_io_current_leakage' into 'master'

fix(sdmmc): corrected the control of dedicated sdmmc pads on S31

Closes IDF-16084

See merge request espressif/esp-idf!52403
This commit is contained in:
Song Ruo Jing
2026-09-18 14:38:30 +08:00
23 changed files with 197 additions and 66 deletions

View File

@@ -108,9 +108,6 @@ esp_err_t sd_host_create_sdmmc_controller(const sd_host_sdmmc_cfg_t *config, sd_
#endif //CONFIG_PM_ENABLE
sdmmc_hal_init(&ctlr->hal);
PERIPH_RCC_ATOMIC() {
sdmmc_ll_pad_set_pin_dedicated_ctrl(ctlr->hal.dev, true);
}
ESP_GOTO_ON_ERROR(esp_clk_tree_enable_src(SDMMC_CLK_SRC_DEFAULT, true), err, TAG, "failed to acquire clk");
uint32_t src_freq_hz = 0;
esp_clk_tree_src_get_freq_hz(SDMMC_CLK_SRC_DEFAULT, ESP_CLK_TREE_SRC_FREQ_PRECISION_CACHED, &src_freq_hz);
@@ -398,6 +395,11 @@ static esp_err_t sd_host_controller_remove_sdmmc_slot(sd_host_slot_handle_t slot
gpio_output_disable(slot_ctx->io_config.d7_io);
}
// release the pads so that they can be used as normal GPIOs
PERIPH_RCC_ATOMIC() {
sdmmc_ll_pad_set_pin_dedicated_ctrl(ctlr->hal.dev, slot_ctx->slot_id, false);
}
xSemaphoreGive(ctlr->mutex);
free(slot);
@@ -1394,6 +1396,10 @@ static esp_err_t sdmmc_slot_io_config(sd_host_sdmmc_slot_t *slot, const sd_host_
GPIO_NUM_CHECK(slot_gpio->d7_io);
}
PERIPH_RCC_ATOMIC() {
sdmmc_ll_pad_set_pin_dedicated_ctrl(slot->ctlr->hal.dev, slot_id, true);
}
configure_pin(slot_gpio->clk_io, sdmmc_slot_gpio_sig[slot_id].clk, GPIO_MODE_OUTPUT, "clk", use_gpio_matrix);
configure_pin(slot_gpio->cmd_io, sdmmc_slot_gpio_sig[slot_id].cmd, GPIO_MODE_INPUT_OUTPUT, "cmd", use_gpio_matrix);
configure_pin(slot_gpio->d0_io, sdmmc_slot_gpio_sig[slot_id].d0, GPIO_MODE_INPUT_OUTPUT, "d0", use_gpio_matrix);

View File

@@ -0,0 +1,19 @@
/*
* SPDX-FileCopyrightText: 2026 Espressif Systems (Shanghai) CO LTD
*
* SPDX-License-Identifier: Apache-2.0
*/
#pragma once
#include <stdint.h>
#include <stdbool.h>
#include "esp_attr.h"
#ifdef __cplusplus
extern "C" {
#endif
#ifdef __cplusplus
}
#endif

View File

@@ -762,6 +762,15 @@ static inline void gpio_ll_set_output_signal_matrix_source(gpio_dev_t *hw, uint3
hw->func_out_sel_cfg[gpio_num].inv_sel = out_inv;
}
/**
* @brief Clear all GPIO dedicated control signals
*/
__attribute__((always_inline))
static inline void gpio_ll_clear_dedicated_ctrl(void)
{
// no dedicated ctrl pad
}
#ifdef __cplusplus
}
#endif

View File

@@ -769,6 +769,15 @@ static inline bool gpio_ll_hp_periph_powerdown_sleep_wakeup_is_enabled(gpio_dev_
return GET_PERI_REG_MASK(RTC_CNTL_GPIO_WAKEUP_REG, 1 << (RTC_CNTL_GPIO_PIN0_WAKEUP_ENABLE_S - gpio_num));
}
/**
* @brief Clear all GPIO dedicated control signals
*/
__attribute__((always_inline))
static inline void gpio_ll_clear_dedicated_ctrl(void)
{
// no dedicated ctrl pad
}
#ifdef __cplusplus
}
#endif

View File

@@ -790,6 +790,15 @@ static inline void gpio_ll_get_io_config(gpio_dev_t *hw, uint32_t gpio_num, gpio
io_config->slp_sel = (iomux_reg_val & SLP_SEL_M) >> SLP_SEL_S;
}
/**
* @brief Clear all GPIO dedicated control signals
*/
__attribute__((always_inline))
static inline void gpio_ll_clear_dedicated_ctrl(void)
{
// no dedicated ctrl pad
}
#ifdef __cplusplus
}
#endif

View File

@@ -735,6 +735,15 @@ static inline void gpio_ll_sleep_output_enable(gpio_dev_t *hw, uint32_t gpio_num
IO_MUX.gpio[gpio_num].mcu_oe = 1;
}
/**
* @brief Clear all GPIO dedicated control signals
*/
__attribute__((always_inline))
static inline void gpio_ll_clear_dedicated_ctrl(void)
{
// no dedicated ctrl pad
}
#ifdef __cplusplus
}
#endif

View File

@@ -696,6 +696,15 @@ static inline void gpio_ll_sleep_output_enable(gpio_dev_t *hw, uint32_t gpio_num
PIN_SLP_OUTPUT_ENABLE(IO_MUX_GPIO0_REG + (gpio_num * 4));
}
/**
* @brief Clear all GPIO dedicated control signals
*/
__attribute__((always_inline))
static inline void gpio_ll_clear_dedicated_ctrl(void)
{
// no dedicated ctrl pad
}
#ifdef __cplusplus
}
#endif

View File

@@ -735,6 +735,15 @@ static inline void gpio_ll_sleep_output_enable(gpio_dev_t *hw, uint32_t gpio_num
IO_MUX.gpion[gpio_num].gpion_mcu_oe = 1;
}
/**
* @brief Clear all GPIO dedicated control signals
*/
__attribute__((always_inline))
static inline void gpio_ll_clear_dedicated_ctrl(void)
{
// no dedicated ctrl pad
}
#ifdef __cplusplus
}
#endif

View File

@@ -804,6 +804,15 @@ static inline bool gpio_ll_hp_periph_powerdown_sleep_wakeup_is_enabled(gpio_dev_
return wakeup_sel_mask & BIT(gpio_num - 7);
}
/**
* @brief Clear all GPIO dedicated control signals
*/
__attribute__((always_inline))
static inline void gpio_ll_clear_dedicated_ctrl(void)
{
// no dedicated ctrl pad
}
#ifdef __cplusplus
}
#endif

View File

@@ -788,6 +788,15 @@ static inline bool gpio_ll_hp_periph_powerdown_sleep_wakeup_is_enabled(gpio_dev_
return wakeup_sel_mask & BIT(gpio_num - 5);
}
/**
* @brief Clear all GPIO dedicated control signals
*/
__attribute__((always_inline))
static inline void gpio_ll_clear_dedicated_ctrl(void)
{
// no dedicated ctrl pad
}
#ifdef __cplusplus
}
#endif

View File

@@ -778,6 +778,15 @@ static inline void gpio_ll_sleep_output_enable(gpio_dev_t *hw, uint32_t gpio_num
IO_MUX.gpio[gpio_num].mcu_oe = 1;
}
/**
* @brief Clear all GPIO dedicated control signals
*/
__attribute__((always_inline))
static inline void gpio_ll_clear_dedicated_ctrl(void)
{
// no dedicated ctrl pad
}
#ifdef __cplusplus
}
#endif

View File

@@ -872,6 +872,15 @@ static inline void gpio_ll_sleep_output_enable(gpio_dev_t *hw, uint32_t gpio_num
IO_MUX.gpio[gpio_num].mcu_oe = 1;
}
/**
* @brief Clear all GPIO dedicated control signals
*/
__attribute__((always_inline))
static inline void gpio_ll_clear_dedicated_ctrl(void)
{
// no dedicated ctrl pad
}
#ifdef __cplusplus
}
#endif

View File

@@ -734,6 +734,15 @@ static inline void gpio_ll_sleep_output_enable(gpio_dev_t *hw, uint32_t gpio_num
PIN_SLP_OUTPUT_ENABLE(GPIO_PIN_MUX_REG[gpio_num]);
}
/**
* @brief Clear all GPIO dedicated control signals
*/
__attribute__((always_inline))
static inline void gpio_ll_clear_dedicated_ctrl(void)
{
// no dedicated ctrl pad
}
#ifdef __cplusplus
}
#endif

View File

@@ -761,6 +761,15 @@ static inline void gpio_ll_get_io_config(gpio_dev_t *hw, uint32_t gpio_num, gpio
io_config->slp_sel = (iomux_reg_val & SLP_SEL_M) >> SLP_SEL_S;
}
/**
* @brief Clear all GPIO dedicated control signals
*/
__attribute__((always_inline))
static inline void gpio_ll_clear_dedicated_ctrl(void)
{
// no dedicated ctrl pad
}
#ifdef __cplusplus
}
#endif

View File

@@ -28,6 +28,7 @@
#include "soc/lp_system_reg.h"
#include "soc/pmu_struct.h"
#include "soc/usb_serial_jtag_struct.h"
#include "soc/cnnt_io_mux_struct.h"
#include "soc/clk_tree_defs.h"
#include "soc/interrupts.h"
#include "hal/gpio_types.h"
@@ -881,6 +882,17 @@ static inline void gpio_ll_sleep_output_enable(gpio_dev_t *hw, uint32_t gpio_num
IO_MUX.gpio[gpio_num].mcu_oe = 1;
}
/**
* @brief Clear all GPIO dedicated control signals (e.g. sdmmc, emac)
*
* Set is done in each peripheral's own LL layer.
*/
__attribute__((always_inline))
static inline void gpio_ll_clear_dedicated_ctrl(void)
{
CNNT_PAD_CTRL.ctrl.val = 0;
}
#ifdef __cplusplus
}
#endif

View File

@@ -201,11 +201,13 @@ static inline void sdmmc_ll_mem_set_low_power_mode(sdmmc_dev_t *dev, sdmmc_ll_me
* @brief Set SDMMC pad pin dedicated ctrl
*
* @param dev Peripheral instance address
* @param slot Slot index
* @param enable True to enable, False to disable
*/
static inline void sdmmc_ll_pad_set_pin_dedicated_ctrl(sdmmc_dev_t *dev, bool enable)
static inline void sdmmc_ll_pad_set_pin_dedicated_ctrl(sdmmc_dev_t *dev, uint32_t slot, bool enable)
{
(void)dev;
(void)slot;
(void)enable;
}

View File

@@ -234,11 +234,13 @@ static inline void sdmmc_ll_mem_set_low_power_mode(sdmmc_dev_t *dev, sdmmc_ll_me
* @brief Set SDMMC pad pin dedicated ctrl
*
* @param dev Peripheral instance address
* @param slot Slot index
* @param enable True to enable, False to disable
*/
static inline void sdmmc_ll_pad_set_pin_dedicated_ctrl(sdmmc_dev_t *dev, bool enable)
static inline void sdmmc_ll_pad_set_pin_dedicated_ctrl(sdmmc_dev_t *dev, uint32_t slot, bool enable)
{
(void)dev;
(void)slot;
(void)enable;
}

View File

@@ -210,11 +210,13 @@ static inline void sdmmc_ll_mem_set_low_power_mode(sdmmc_dev_t *dev, sdmmc_ll_me
* @brief Set SDMMC pad pin dedicated ctrl
*
* @param dev Peripheral instance address
* @param slot Slot index
* @param enable True to enable, False to disable
*/
static inline void sdmmc_ll_pad_set_pin_dedicated_ctrl(sdmmc_dev_t *dev, bool enable)
static inline void sdmmc_ll_pad_set_pin_dedicated_ctrl(sdmmc_dev_t *dev, uint32_t slot, bool enable)
{
(void)dev;
(void)slot;
(void)enable;
}

View File

@@ -233,14 +233,20 @@ static inline void sdmmc_ll_mem_set_low_power_mode(sdmmc_dev_t *dev, sdmmc_ll_me
/**
* @brief Set SDMMC pad pin dedicated ctrl
*
* Enable it when the slot is in use, disable it when the slot is released, so
* that the pads can be used as normal GPIOs again.
*
* @param dev Peripheral instance address
* @param slot Slot index
* @param enable True to enable, False to disable
*/
static inline void sdmmc_ll_pad_set_pin_dedicated_ctrl(sdmmc_dev_t *dev, bool enable)
static inline void sdmmc_ll_pad_set_pin_dedicated_ctrl(sdmmc_dev_t *dev, uint32_t slot, bool enable)
{
CNNT_PAD_CTRL.ctrl.sdio_pad_pin_ctrl_ded_sel = enable;
// only slot 0 is routed to the dedicated SDIO pads, slot 1 doesn't rely on this bit
if (slot == 0) {
CNNT_PAD_CTRL.ctrl.sdio_pad_pin_ctrl_ded_sel = 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 sdmmc_ll_pad_set_pin_dedicated_ctrl(...) do { \

View File

@@ -109,14 +109,14 @@ esp_err_t esp_sleep_release_lp_use_xtal(void);
/**
* @brief Soft-isolate valid digital IO pads (SOC_GPIO_VALID_DIGITAL_IO_PAD_MASK) for leakage control
*
* Skips pads that are digitally held and pads reserved by the driver.
* Skips pads that are digitally held and pads reserved by the driver (only for light sleep).
* 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).
* @param dslp If false, 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 after wakeup.
* Pass true indicating that backup/restore is not needed, just isolate the pads (i.e. deep sleep).
*/
void esp_sleep_isolate_digital_gpio(bool do_backup);
void esp_sleep_isolate_digital_gpio(bool dslp);
/**
* @brief Backup and isolate (or pull up) the five base MSPI lines (CLK/Q/D/HD/WP)

View File

@@ -7,6 +7,8 @@
#pragma once
#include <stdint.h>
#include "sdkconfig.h"
#include "esp_attr.h"
#include "hal/gpio_ll.h"
#ifdef __cplusplus
extern "C" {
@@ -39,6 +41,15 @@ void esp_sleep_gpio_pupd_config_workaround_apply(void);
void esp_sleep_gpio_pupd_config_workaround_unapply(void);
#endif // CONFIG_IDF_TARGET_ESP32
/**
* @brief Clear all GPIO dedicated control signals
*/
FORCE_INLINE_ATTR void esp_sleep_gpio_clear_dedicated_ctrl(void)
{
gpio_ll_clear_dedicated_ctrl();
}
#ifdef __cplusplus
}
#endif

View File

@@ -116,7 +116,7 @@ void esp_sleep_gpio_pupd_config_workaround_unapply(void)
}
}
}
#endif
#endif // CONFIG_IDF_TARGET_ESP32
#if CONFIG_ESP_SLEEP_GPIO_RESET_WORKAROUND || CONFIG_PM_SLP_DISABLE_GPIO
void esp_sleep_config_gpio_isolate(void)
@@ -283,7 +283,7 @@ IRAM_ATTR void esp_sleep_restore_isolated_digital_gpio(void)
}
#endif // SOC_GPIO_NEED_SOFT_ISOLATE_DURING_PD
IRAM_ATTR void esp_sleep_isolate_digital_gpio(bool do_backup)
IRAM_ATTR void esp_sleep_isolate_digital_gpio(bool dslp)
{
gpio_hal_context_t gpio_hal = { .dev = GPIO_HAL_GET_HW(GPIO_PORT_0) };
#if !SOC_GPIO_SUPPORT_HOLD_SINGLE_IO_IN_DSLP
@@ -306,7 +306,7 @@ IRAM_ATTR void esp_sleep_isolate_digital_gpio(bool do_backup)
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) {
if (!dslp) {
s_gpio_isolate_backup.backuped = 0;
s_gpio_isolate_backup.pu = 0;
s_gpio_isolate_backup.pd = 0;
@@ -320,8 +320,10 @@ IRAM_ATTR void esp_sleep_isolate_digital_gpio(bool do_backup)
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) {
// for light sleep, we will skip the reserved GPIOs
// for deep sleep, since it does not return and reset the entire digital domain, it should be fine to isolate all digital IOs to minimize the leakage
(dslp || !esp_gpio_is_reserved(BIT64(gpio_num)))) {
if (!dslp) {
gpio_io_config_t io_config;
gpio_ll_backup_pad_config_for_sleep_isolate(gpio_num, &io_config);
if (io_config.pu) {
@@ -343,8 +345,8 @@ IRAM_ATTR void esp_sleep_isolate_digital_gpio(bool do_backup)
}
pad_mask &= pad_mask - 1;
}
#else
(void)do_backup;
#else // !SOC_GPIO_NEED_SOFT_ISOLATE_DURING_PD
(void)dslp;
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) &&

View File

@@ -41,6 +41,7 @@
#include "hal/efuse_hal.h"
#include "hal/rtc_io_hal.h"
#include "hal/clk_tree_hal.h"
#include "rom/rtc.h"
#if RNG_LL_NEEDS_RESET_WHEN_WAKEUP
#include "hal/rng_ll.h"
@@ -66,6 +67,7 @@
#include "soc/rtc.h"
#include "hal/clk_gate_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"
@@ -80,6 +82,7 @@
#endif
#include "hal/temperature_sensor_hal.h"
#include "hal/mspi_ll.h"
#include "hal/gpio_ll.h"
#if SOC_LP_CORE_HW_AUTO_CLRWAKEUPCAUSE
#include "hal/lp_aon_hal.h"
#endif
@@ -99,62 +102,19 @@
#include "esp_private/esp_task_wdt.h"
#include "esp_private/sar_periph_ctrl.h"
#if SOC_PM_SUPPORT_EXT1_WAKEUP && SOC_RTCIO_PIN_COUNT > 0
#include "esp_private/sleep_gpio.h"
#endif
#ifdef CONFIG_IDF_TARGET_ESP32
#include "esp32/rom/rtc.h"
#include "esp_private/gpio.h"
#elif CONFIG_IDF_TARGET_ESP32S2
#include "esp32s2/rom/rtc.h"
#include "soc/extmem_reg.h"
#include "esp_private/gpio.h"
#elif CONFIG_IDF_TARGET_ESP32S3
#include "esp32s3/rom/rtc.h"
#elif CONFIG_IDF_TARGET_ESP32C3
#include "esp32c3/rom/rtc.h"
#elif CONFIG_IDF_TARGET_ESP32C2
#include "esp32c2/rom/rtc.h"
#elif CONFIG_IDF_TARGET_ESP32C6
#include "esp32c6/rom/rtc.h"
#include "hal/gpio_ll.h"
#include "hal/clk_gate_ll.h"
#elif CONFIG_IDF_TARGET_ESP32C5
#include "esp32c5/rom/rtc.h"
#include "hal/gpio_ll.h"
#include "hal/clk_gate_ll.h"
#elif CONFIG_IDF_TARGET_ESP32C61
#include "esp32c61/rom/rtc.h"
#include "hal/gpio_ll.h"
#elif CONFIG_IDF_TARGET_ESP32H2
#include "esp32h2/rom/rtc.h"
#include "soc/extmem_reg.h"
#include "hal/gpio_ll.h"
#elif CONFIG_IDF_TARGET_ESP32H21
#include "esp32h21/rom/rtc.h"
#include "hal/gpio_ll.h"
#elif CONFIG_IDF_TARGET_ESP32H4
#include "esp32h4/rom/rtc.h"
#include "hal/gpio_ll.h"
#elif CONFIG_IDF_TARGET_ESP32P4
#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
#include "esp_private/eco3_livelock_workaround.h"
#endif
#if SOC_MSPI_HAS_INDEPENT_IOMUX
#include "hal/mspi_ll.h"
#endif
#include "hal/rtc_timer_hal.h"
#if SOC_VBAT_SUPPORTED
@@ -872,8 +832,9 @@ static esp_err_t FORCE_IRAM_ATTR esp_sleep_start_safe(uint32_t sleep_flags, uint
}
#endif
if (deep_sleep) {
esp_sleep_gpio_clear_dedicated_ctrl();
#if !SOC_GPIO_SUPPORT_HOLD_SINGLE_IO_IN_DSLP || SOC_GPIO_NEED_SOFT_ISOLATE_DURING_PD
esp_sleep_isolate_digital_gpio(false);
esp_sleep_isolate_digital_gpio(true);
#endif
#if CONFIG_IDF_TARGET_ESP32P4 && CONFIG_ESP_SLEEP_SET_FLASH_DPD
@@ -915,7 +876,7 @@ static esp_err_t FORCE_IRAM_ATTR esp_sleep_start_safe(uint32_t sleep_flags, uint
} else {
#if SOC_GPIO_NEED_SOFT_ISOLATE_DURING_PD
if (sleep_flags & RTC_SLEEP_PD_DIG) {
esp_sleep_isolate_digital_gpio(true);
esp_sleep_isolate_digital_gpio(false);
}
#endif
/* Cache Suspend 1: will wait cache idle in cache suspend */