Merge branch 'feat/support_gpio_source_for_more_chips' into 'master'

feat(esp_hw_support): support GPIO wakeup deepsleep on esp32/esp32s2/esp32s3

Closes PM-666

See merge request espressif/esp-idf!45773
This commit is contained in:
Wu Zheng Hui
2026-03-26 11:28:45 +08:00
38 changed files with 299 additions and 132 deletions
@@ -1,5 +1,5 @@
/*
* SPDX-FileCopyrightText: 2015-2025 Espressif Systems (Shanghai) CO LTD
* SPDX-FileCopyrightText: 2015-2026 Espressif Systems (Shanghai) CO LTD
*
* SPDX-License-Identifier: Apache-2.0
*/
@@ -39,12 +39,6 @@ void esp_sleep_gpio_pupd_config_workaround_apply(void);
void esp_sleep_gpio_pupd_config_workaround_unapply(void);
#endif // CONFIG_IDF_TARGET_ESP32
/**
* @brief Call once in startup to disable the wakeup IO pins and release their holding state after waking up from Deep-sleep
*/
void esp_deep_sleep_wakeup_io_reset(void);
#ifdef __cplusplus
}
#endif
+14 -2
View File
@@ -594,15 +594,27 @@ uint64_t esp_sleep_get_ext1_wakeup_status(void);
#if SOC_GPIO_SUPPORT_HP_PERIPH_PD_SLEEP_WAKEUP
/**
* @brief Get the bit mask of GPIOs which caused wakeup (gpio)
* @brief Get the bit mask of RTC IO which caused wakeup (GPIO wakeup source).
*
* If wakeup was caused by another source, this function will return 0.
* Each bit corresponds to an RTC IO.
* Use esp_sleep_wakeup_io_bit2num() to convert a bit index to GPIO number.
*
* @return bit mask, if GPIOn caused wakeup, BIT(n) will be set
* @return bit mask of RTC IO that caused wakeup
*/
uint64_t esp_sleep_get_gpio_wakeup_status(void);
#endif //SOC_GPIO_SUPPORT_HP_PERIPH_PD_SLEEP_WAKEUP
/**
* @brief Convert RTC IO status bit index to GPIO number.
*
* Used to interpret the bit mask returned by esp_sleep_get_gpio_wakeup_status().
*
* @param bit RTC IO bit index (0 to SOC_RTCIO_PIN_COUNT-1).
* @return GPIO number, or GPIO_NUM_NC if bit is invalid or has no corresponding GPIO.
*/
gpio_num_t esp_sleep_wakeup_io_bit2num(uint32_t bit);
/**
* @brief Configure power domain options for sleep mode
*
+41 -8
View File
@@ -1,5 +1,5 @@
/*
* SPDX-FileCopyrightText: 2015-2025 Espressif Systems (Shanghai) CO LTD
* SPDX-FileCopyrightText: 2015-2026 Espressif Systems (Shanghai) CO LTD
*
* SPDX-License-Identifier: Apache-2.0
*/
@@ -10,6 +10,7 @@
#include <sys/param.h>
#include "esp_attr.h"
#include "esp_system.h"
#include "esp_sleep.h"
#include "esp_log.h"
#include "esp_memory_utils.h"
@@ -97,6 +98,7 @@ void esp_sleep_gpio_pupd_config_workaround_unapply(void)
}
#endif
#if CONFIG_ESP_SLEEP_GPIO_RESET_WORKAROUND || CONFIG_PM_SLP_DISABLE_GPIO
void esp_sleep_config_gpio_isolate(void)
{
ESP_EARLY_LOGI(TAG, "Configure to isolate all GPIO pins in sleep state");
@@ -169,6 +171,7 @@ 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)
@@ -228,7 +231,7 @@ IRAM_ATTR void esp_sleep_isolate_digital_gpio(void)
#endif //!SOC_GPIO_SUPPORT_HOLD_SINGLE_IO_IN_DSLP
#if SOC_DEEP_SLEEP_SUPPORTED
void esp_deep_sleep_wakeup_io_reset(void)
static void esp_deep_sleep_wakeup_io_reset(void)
{
#if SOC_PM_SUPPORT_EXT1_WAKEUP
uint32_t rtc_io_mask = rtc_hal_ext1_get_wakeup_pins();
@@ -247,37 +250,67 @@ void esp_deep_sleep_wakeup_io_reset(void)
#endif
#if SOC_GPIO_SUPPORT_HP_PERIPH_PD_SLEEP_WAKEUP
uint32_t dl_io_mask = SOC_GPIO_HP_PERIPH_PD_SLEEP_WAKEABLE_MASK;
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 (dl_io_mask) {
int gpio_num = __builtin_ffs(dl_io_mask) - 1;
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);
if (wakeup_io_enabled) {
// Disable the wakeup before releasing hold, such that wakeup status can reflect the correct wakeup pin
gpio_hal_wakeup_disable_on_hp_periph_powerdown_sleep(&gpio_hal, gpio_num);
gpio_hal_hold_dis(&gpio_hal, gpio_num);
}
dl_io_mask &= ~BIT(gpio_num);
dslp_io_mask &= dslp_io_mask - 1;
}
#endif
}
#endif
#if CONFIG_ESP_SLEEP_GPIO_RESET_WORKAROUND || CONFIG_PM_SLP_DISABLE_GPIO
ESP_SYSTEM_INIT_FN(esp_sleep_startup_init, SECONDARY, BIT(0), 105)
{
#if SOC_DEEP_SLEEP_SUPPORTED
// Need to unhold the IOs that were hold right before entering deep sleep, which are used as wakeup pins
if (esp_reset_reason() == ESP_RST_DEEPSLEEP) {
esp_deep_sleep_wakeup_io_reset();
}
#endif //#if SOC_DEEP_SLEEP_SUPPORTED
#if CONFIG_ESP_SLEEP_GPIO_RESET_WORKAROUND || CONFIG_PM_SLP_DISABLE_GPIO
// Configure to isolate (disable the Input/Output/Pullup/Pulldown
// function of the pin) all GPIO pins in sleep state
esp_sleep_config_gpio_isolate();
// Enable automatic switching of GPIO configuration
esp_sleep_enable_gpio_switch(true);
#endif
return ESP_OK;
}
/**
* @brief Convert RTC IO status bit number to GPIO number (for sleep wakeup status translation).
*
* @param bit RTC IO intr status bit index (0 to SOC_RTCIO_PIN_COUNT-1).
* @return GPIO number, or GPIO_NUM_NC if bit is invalid or has no corresponding GPIO.
*/
gpio_num_t esp_sleep_wakeup_io_bit2num(uint32_t bit)
{
#if SOC_RTCIO_PIN_COUNT > 0
if (bit >= SOC_RTCIO_PIN_COUNT) {
return GPIO_NUM_NC;
}
for (int gpio = 0; gpio < SOC_GPIO_PIN_COUNT; gpio++) {
if (rtc_io_num_map[gpio] == (int8_t)bit) {
return (gpio_num_t)gpio;
}
}
#elif (SOC_RTCIO_PIN_COUNT == 0)
return (gpio_num_t)bit;
#endif
return GPIO_NUM_NC;
}
void esp_sleep_gpio_include(void)
{
// Linker hook function, exists to make the linker examine this file
}
#endif
+44 -42
View File
@@ -97,11 +97,14 @@
#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/cache.h"
#include "esp32/rom/rtc.h"
#include "esp_private/gpio.h"
#include "esp_private/sleep_gpio.h"
#elif CONFIG_IDF_TARGET_ESP32S2
#include "esp32s2/rom/rtc.h"
#include "soc/extmem_reg.h"
@@ -292,8 +295,8 @@ typedef struct {
uint32_t ext0_rtc_gpio_num : 5;
#endif
#if SOC_GPIO_SUPPORT_HP_PERIPH_PD_SLEEP_WAKEUP
uint32_t gpio_wakeup_mask : SOC_GPIO_HP_PERIPH_PD_SLEEP_WAKEABLE_PIN_CNT;
uint32_t gpio_trigger_mode : SOC_GPIO_HP_PERIPH_PD_SLEEP_WAKEABLE_PIN_CNT;
uint64_t gpio_wakeup_mask;
uint64_t gpio_trigger_mode;
#endif
uint32_t sleep_time_adjustment;
uint32_t ccount_ticks_record;
@@ -1029,9 +1032,6 @@ static esp_err_t SLEEP_FN_ATTR esp_sleep_start(uint32_t sleep_flags, uint32_t cl
int64_t sleep_duration = (int64_t) s_config.sleep_duration - (int64_t) s_config.sleep_time_adjustment;
// Sleep UART prepare
sleep_uart_prepare(sleep_flags, deep_sleep);
#if CONFIG_ESP_PHY_ENABLED && SOC_DEEP_SLEEP_SUPPORTED
// Do deep-sleep PHY related callback, which need to be executed when the PLL clock is exists.
// For light-sleep, PHY state is managed by the upper layer of the wifi/bt protocol stack.
@@ -1040,22 +1040,12 @@ static esp_err_t SLEEP_FN_ATTR esp_sleep_start(uint32_t sleep_flags, uint32_t cl
}
#endif
#if !CONFIG_APP_BUILD_TYPE_PURE_RAM_APP
uint32_t xtal_freq = rtc_clk_xtal_freq_get();
esp_clk_utils_mspi_speed_mode_sync_before_cpu_freq_switching(xtal_freq, xtal_freq);
#endif
#if SOC_PM_RETENTION_SW_TRIGGER_REGDMA
if (!deep_sleep && (sleep_flags & PMU_SLEEP_PD_TOP)) {
sleep_retention_do_system_retention(true);
}
#endif
// Save current frequency and switch to XTAL
rtc_cpu_freq_config_t cpu_freq_config;
rtc_clk_cpu_freq_get_config(&cpu_freq_config);
rtc_clk_cpu_freq_set_xtal_for_sleep();
#if SOC_PM_SUPPORT_EXT0_WAKEUP
// Configure pins for external wakeup
if (s_config.wakeup_triggers & RTC_EXT0_TRIG_EN) {
@@ -1076,6 +1066,19 @@ static esp_err_t SLEEP_FN_ATTR esp_sleep_start(uint32_t sleep_flags, uint32_t cl
}
#endif
// Sleep UART prepare
sleep_uart_prepare(sleep_flags, deep_sleep);
#if !CONFIG_APP_BUILD_TYPE_PURE_RAM_APP
uint32_t xtal_freq = rtc_clk_xtal_freq_get();
esp_clk_utils_mspi_speed_mode_sync_before_cpu_freq_switching(xtal_freq, xtal_freq);
#endif
// Save current frequency and switch to XTAL
rtc_cpu_freq_config_t cpu_freq_config;
rtc_clk_cpu_freq_get_config(&cpu_freq_config);
rtc_clk_cpu_freq_set_xtal_for_sleep();
#if CONFIG_ULP_COPROC_ENABLED
// Enable ULP wakeup
#if CONFIG_ULP_COPROC_TYPE_FSM
@@ -2239,15 +2242,13 @@ uint64_t esp_sleep_get_ext1_wakeup_status(void)
uint32_t status = rtc_hal_ext1_get_wakeup_status();
// Translate bit map of RTC IO numbers into the bit map of GPIO numbers
uint64_t gpio_mask = 0;
for (int gpio = 0; gpio < SOC_GPIO_PIN_COUNT; ++gpio) {
if (!esp_sleep_is_valid_wakeup_gpio(gpio)) {
continue;
while (status) {
int rtc_pin = __builtin_ctz(status);
gpio_num_t gpio = esp_sleep_wakeup_io_bit2num(rtc_pin);
if (gpio != GPIO_NUM_NC) {
gpio_mask |= 1ULL << gpio;
}
int rtc_pin = rtc_io_number_get(gpio);
if ((status & BIT(rtc_pin)) == 0) {
continue;
}
gpio_mask |= 1ULL << gpio;
status &= ~BIT(rtc_pin);
}
return gpio_mask;
}
@@ -2265,26 +2266,29 @@ uint64_t esp_sleep_get_gpio_wakeup_status(void)
static void esp_sleep_gpio_wakeup_prepare_on_hp_periph_powerdown(void)
{
uint32_t valid_wake_io_mask = SOC_GPIO_HP_PERIPH_PD_SLEEP_WAKEABLE_MASK;
for (gpio_num_t gpio_idx = __builtin_ctz(valid_wake_io_mask); valid_wake_io_mask >> gpio_idx; gpio_idx++) {
if ((s_config.gpio_wakeup_mask & BIT64(gpio_idx)) == 0) {
continue;
}
uint64_t valid_wake_io_mask = s_config.gpio_wakeup_mask & SOC_GPIO_HP_PERIPH_PD_SLEEP_WAKEABLE_MASK;
while (valid_wake_io_mask) {
int gpio_idx = __builtin_ctzll(valid_wake_io_mask);
#if SOC_LP_IO_CLOCK_IS_INDEPENDENT
// To suppress build errors about spinlock's __DECLARE_RCC_ATOMIC_ENV
int __DECLARE_RCC_ATOMIC_ENV __attribute__ ((unused));
rtcio_ll_enable_io_clock(true);
#endif
#if CONFIG_ESP_SLEEP_GPIO_ENABLE_INTERNAL_RESISTORS
if (s_config.gpio_trigger_mode & BIT(gpio_idx)) {
ESP_ERROR_CHECK(gpio_pullup_dis(gpio_idx));
ESP_ERROR_CHECK(gpio_pulldown_en(gpio_idx));
if (GPIO_IS_VALID_OUTPUT_GPIO(gpio_idx)) {
if (s_config.gpio_trigger_mode & BIT(gpio_idx)) {
gpio_pullup_dis(gpio_idx);
gpio_pulldown_en(gpio_idx);
} else {
gpio_pullup_en(gpio_idx);
gpio_pulldown_dis(gpio_idx);
}
} else {
ESP_ERROR_CHECK(gpio_pullup_en(gpio_idx));
ESP_ERROR_CHECK(gpio_pulldown_dis(gpio_idx));
ESP_EARLY_LOGE(TAG, "GPIO%d not support internal PU/PD", gpio_idx);
}
#endif
ESP_ERROR_CHECK(gpio_hold_en(gpio_idx));
valid_wake_io_mask &= valid_wake_io_mask - 1;
}
// Clear state from previous wakeup
rtc_hal_gpio_clear_wakeup_status();
@@ -2311,19 +2315,17 @@ esp_err_t esp_sleep_enable_gpio_wakeup_on_hp_periph_powerdown(uint64_t gpio_pin_
}
}
}
for (gpio_num_t gpio_idx = __builtin_ctzll(gpio_pin_mask); gpio_pin_mask >> gpio_idx; gpio_idx++) {
if ((gpio_pin_mask & BIT64(gpio_idx)) == 0) {
continue;
}
while (gpio_pin_mask) {
int gpio_idx = __builtin_ctzll(gpio_pin_mask);
err = gpio_wakeup_enable_on_hp_periph_powerdown_sleep(gpio_idx, intr_type);
if (err != ESP_OK) return err;
s_config.gpio_wakeup_mask |= BIT(gpio_idx);
s_config.gpio_wakeup_mask |= BIT64(gpio_idx);
if (mode == ESP_GPIO_WAKEUP_GPIO_HIGH) {
s_config.gpio_trigger_mode |= BIT(gpio_idx);
s_config.gpio_trigger_mode |= BIT64(gpio_idx);
} else {
s_config.gpio_trigger_mode &= ~BIT(gpio_idx);
s_config.gpio_trigger_mode &= ~BIT64(gpio_idx);
}
gpio_pin_mask &= gpio_pin_mask - 1;
}
s_config.wakeup_triggers |= RTC_GPIO_TRIG_EN;
return err;
@@ -55,6 +55,6 @@ components/esp_hw_support/test_apps/wakeup_tests:
enable:
- if: SOC_DEEP_SLEEP_SUPPORTED == 1 and SOC_LIGHT_SLEEP_SUPPORTED == 1
disable_test:
- if: IDF_TARGET in ["esp32c61", "esp32h21", "esp32h4"]
- if: IDF_TARGET in ["esp32h21", "esp32h4"]
temporary: true
reason: lack of runners
@@ -1,5 +1,5 @@
/*
* SPDX-FileCopyrightText: 2023-2025 Espressif Systems (Shanghai) CO LTD
* SPDX-FileCopyrightText: 2023-2026 Espressif Systems (Shanghai) CO LTD
*
* SPDX-License-Identifier: Unlicense OR CC0-1.0
*/
@@ -8,7 +8,6 @@
#include "unity_test_utils.h"
#include "test_utils.h"
#include "esp_sleep.h"
#include "driver/rtc_io.h"
#include "driver/gpio.h"
#include "hal/gpio_ll.h"
#include "esp_console.h"
@@ -384,9 +383,9 @@ static int process_get_wakeup_cause(int argc, char **argv)
if (causes & BIT(ESP_SLEEP_WAKEUP_GPIO)) {
if (esp_reset_reason() == ESP_RST_DEEPSLEEP) {
#if SOC_GPIO_SUPPORT_HP_PERIPH_PD_SLEEP_WAKEUP
uint64_t wakeup_pin_mask = esp_sleep_get_gpio_wakeup_status();
if (wakeup_pin_mask != 0) {
int pin = __builtin_ffsll(wakeup_pin_mask) - 1;
uint64_t wakeup_channel_mask = esp_sleep_get_gpio_wakeup_status();
if (wakeup_channel_mask != 0) {
int pin = esp_sleep_wakeup_io_bit2num((uint32_t)__builtin_ctzll(wakeup_channel_mask));
printf("Wake up from GPIO at IO%d\n", pin);
} else {
printf("Wake up from GPIO triggered, but unknown wake-up IO\n");
@@ -1,10 +1,11 @@
# SPDX-FileCopyrightText: 2022-2025 Espressif Systems (Shanghai) CO LTD
# SPDX-FileCopyrightText: 2022-2026 Espressif Systems (Shanghai) CO LTD
# SPDX-License-Identifier: CC0-1.0
from time import sleep
import pytest
from pytest_embedded_idf.dut import IdfDut
from pytest_embedded_idf.utils import idf_parametrize
from pytest_embedded_idf.utils import soc_filtered_targets
TEST_CONFIGS = [
pytest.param('default'),
@@ -26,7 +27,7 @@ available_gpio_nums = {
}
available_rtcio_nums = {
'esp32': [36, 37, 38, 39, 34, 35, 4, 0, 2, 15, 13, 12, 14, 27],
'esp32': [36, 37, 38, 39, 34, 35, 25, 26, 33, 32, 4, 0, 2, 15, 13, 12, 14, 27],
'esp32s2': [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21],
'esp32s3': [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21],
'esp32c2': [0, 1, 2, 3, 4, 5],
@@ -42,11 +43,7 @@ available_rtcio_nums = {
@pytest.mark.generic_multi_device
@pytest.mark.parametrize('count', [2], indirect=True)
@pytest.mark.parametrize('config', TEST_CONFIGS, indirect=True)
@idf_parametrize(
'target',
['esp32', 'esp32s2', 'esp32s3', 'esp32c6', 'esp32h2', 'esp32p4', 'esp32c5'],
indirect=['target'],
)
@idf_parametrize('target', soc_filtered_targets('SOC_PM_SUPPORT_EXT1_WAKEUP == 1'), indirect=['target'])
def test_ext1_deepsleep(dut: tuple[IdfDut, IdfDut]) -> None:
wakee = dut[0]
waker = dut[1]
@@ -97,7 +94,7 @@ def test_ext1_deepsleep(dut: tuple[IdfDut, IdfDut]) -> None:
@pytest.mark.generic_multi_device
@pytest.mark.parametrize('count', [2], indirect=True)
@pytest.mark.parametrize('config', TEST_CONFIGS, indirect=True)
@idf_parametrize('target', ['esp32c2', 'esp32c3', 'esp32c6', 'esp32p4', 'esp32c5'], indirect=['target'])
@idf_parametrize('target', soc_filtered_targets('SOC_GPIO_SUPPORT_HP_PERIPH_PD_SLEEP_WAKEUP == 1'), indirect=['target'])
def test_rtcio_deepsleep(dut: tuple[IdfDut, IdfDut]) -> None:
wakee = dut[0]
waker = dut[1]
@@ -143,7 +140,6 @@ def test_rtcio_deepsleep(dut: tuple[IdfDut, IdfDut]) -> None:
@pytest.mark.parametrize('count', [2], indirect=True)
@pytest.mark.parametrize('config', TEST_CONFIGS, indirect=True)
@idf_parametrize('target', ['supported_targets'], indirect=['target'])
@pytest.mark.temp_skip_ci(targets=['esp32c61'], reason='p4 rev3 migration')
def test_gpio_wakeup_enable_lightsleep(dut: tuple[IdfDut, IdfDut]) -> None:
wakee = dut[0]
waker = dut[1]