Skip to content

Commit

Permalink
Merge branch 'bugfix/wakeup_io_hold_unhold_in_sleep_v5.0' into 'relea…
Browse files Browse the repository at this point in the history
…se/v5.0'

gpio: Fix deep sleep wakeup IOs unable to unhold after wakeup (v5.0)

See merge request espressif/esp-idf!22665
  • Loading branch information
ginkgm committed Mar 20, 2023
2 parents 58f24af + 27f9ee6 commit 55e040b
Show file tree
Hide file tree
Showing 31 changed files with 412 additions and 261 deletions.
12 changes: 6 additions & 6 deletions components/driver/gpio/gpio.c
Original file line number Diff line number Diff line change
Expand Up @@ -707,25 +707,25 @@ void gpio_deep_sleep_hold_dis(void)

#if SOC_GPIO_SUPPORT_FORCE_HOLD

esp_err_t gpio_force_hold_all()
esp_err_t IRAM_ATTR gpio_force_hold_all()
{
#if SOC_RTCIO_HOLD_SUPPORTED
rtc_gpio_force_hold_all();
rtc_gpio_force_hold_en_all();
#endif
portENTER_CRITICAL(&gpio_context.gpio_spinlock);
gpio_hal_force_hold_all();
portEXIT_CRITICAL(&gpio_context.gpio_spinlock);
return ESP_OK;
}

esp_err_t gpio_force_unhold_all()
esp_err_t IRAM_ATTR gpio_force_unhold_all()
{
#if SOC_RTCIO_HOLD_SUPPORTED
rtc_gpio_force_hold_dis_all();
#endif
portENTER_CRITICAL(&gpio_context.gpio_spinlock);
gpio_hal_force_unhold_all();
portEXIT_CRITICAL(&gpio_context.gpio_spinlock);
#if SOC_RTCIO_HOLD_SUPPORTED
rtc_gpio_force_hold_dis_all();
#endif
return ESP_OK;
}
#endif
Expand Down
42 changes: 28 additions & 14 deletions components/driver/include/driver/gpio.h
Original file line number Diff line number Diff line change
Expand Up @@ -361,16 +361,21 @@ esp_err_t gpio_get_drive_capability(gpio_num_t gpio_num, gpio_drive_cap_t *stren
/**
* @brief Enable gpio pad hold function.
*
* When the pin is set to hold, the state is latched at that moment and will not change no matter how the internal
* signals change or how the IO MUX/GPIO configuration is modified (including input enable, output enable,
* output value, function, and drive strength values). It can be used to retain the pin state through a
* core reset and system reset triggered by watchdog time-out or Deep-sleep events.
*
* The gpio pad hold function works in both input and output modes, but must be output-capable gpios.
* If pad hold enabled:
* in output mode: the output level of the pad will be force locked and can not be changed.
* in input mode: the input value read will not change, regardless the changes of input signal.
* in input mode: input read value can still reflect the changes of the input signal.
*
* The state of digital gpio cannot be held during Deep-sleep, and it will resume the hold function
* The state of the digital gpio cannot be held during Deep-sleep, and it will resume to hold at its default pin state
* when the chip wakes up from Deep-sleep. If the digital gpio also needs to be held during Deep-sleep,
* `gpio_deep_sleep_hold_en` should also be called.
*
* Power down or call gpio_hold_dis will disable this function.
* Power down or call `gpio_hold_dis` will disable this function.
*
* @param gpio_num GPIO number, only support output-capable GPIOs
*
Expand Down Expand Up @@ -400,19 +405,21 @@ esp_err_t gpio_hold_en(gpio_num_t gpio_num);
esp_err_t gpio_hold_dis(gpio_num_t gpio_num);

/**
* @brief Enable all digital gpio pad hold function during Deep-sleep.
* @brief Enable all digital gpio pads hold function during Deep-sleep.
*
* Enabling this feature makes all digital gpio pads be at the holding state during Deep-sleep. The state of each pad
* holds is its active configuration (not pad's sleep configuration!).
*
* When the chip is in Deep-sleep mode, all digital gpio will hold the state before sleep, and when the chip is woken up,
* the status of digital gpio will not be held. Note that the pad hold feature only works when the chip is in Deep-sleep mode,
* when not in sleep mode, the digital gpio state can be changed even you have called this function.
* Note that this pad hold feature only works when the chip is in Deep-sleep mode. When the chip is in active mode,
* the digital gpio state can be changed freely even you have called this function.
*
* Power down or call gpio_hold_dis will disable this function, otherwise, the digital gpio hold feature works as long as the chip enter Deep-sleep.
* After this API is being called, the digital gpio Deep-sleep hold feature will work during every sleep process. You
* should call `gpio_deep_sleep_hold_dis` to disable this feature.
*/
void gpio_deep_sleep_hold_en(void);

/**
* @brief Disable all digital gpio pad hold function during Deep-sleep.
*
* @brief Disable all digital gpio pads hold function during Deep-sleep.
*/
void gpio_deep_sleep_hold_dis(void);

Expand All @@ -434,14 +441,21 @@ void gpio_iomux_out(uint8_t gpio_num, int func, bool oen_inv);

#if SOC_GPIO_SUPPORT_FORCE_HOLD
/**
* @brief Force hold digital and rtc gpio pad.
* @note GPIO force hold, whether the chip in sleep mode or wakeup mode.
* @brief Force hold all digital and rtc gpio pads.
*
* GPIO force hold, no matter the chip in active mode or sleep modes.
*
* This function will immediately cause all pads to latch the current values of input enable, output enable,
* output value, function, and drive strength values.
*
* @warning This function will hold flash and UART pins as well. Therefore, this function, and all code run afterwards
* (till calling `gpio_force_unhold_all` to disable this feature), MUST be placed in internal RAM as holding the flash
* pins will halt SPI flash operation, and holding the UART pins will halt any UART logging.
* */
esp_err_t gpio_force_hold_all(void);

/**
* @brief Force unhold digital and rtc gpio pad.
* @note GPIO force unhold, whether the chip in sleep mode or wakeup mode.
* @brief Force unhold all digital and rtc gpio pads.
* */
esp_err_t gpio_force_unhold_all(void);
#endif
Expand Down
2 changes: 1 addition & 1 deletion components/driver/include/driver/rtc_io.h
Original file line number Diff line number Diff line change
Expand Up @@ -258,7 +258,7 @@ esp_err_t rtc_gpio_isolate(gpio_num_t gpio_num);
* Force hold signal is enabled before going into deep sleep for pins which
* are used for EXT1 wakeup.
*/
esp_err_t rtc_gpio_force_hold_all(void);
esp_err_t rtc_gpio_force_hold_en_all(void);

/**
* @brief Disable force hold signal for all RTC IOs
Expand Down
78 changes: 76 additions & 2 deletions components/driver/test/test_rtcio.c
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@
#include "esp_log.h"
#include "soc/rtc_io_periph.h"

#if !TEMPORARY_DISABLED_FOR_TARGETS(ESP32S3, ESP32C3, ESP32C2)
#if SOC_RTCIO_PIN_COUNT > 0

#define RTCIO_CHECK(condition) TEST_ASSERT_MESSAGE((condition == ESP_OK), "ret is not ESP_OK")
#define RTCIO_VERIFY(condition, msg) TEST_ASSERT_MESSAGE((condition), msg)
Expand Down Expand Up @@ -77,6 +77,32 @@ const int s_test_map[TEST_GPIO_PIN_COUNT] = {
GPIO_NUM_20, //GPIO20
GPIO_NUM_21, //GPIO21
};
#elif defined CONFIG_IDF_TARGET_ESP32S3
#define TEST_GPIO_PIN_COUNT 21
const int s_test_map[TEST_GPIO_PIN_COUNT] = {
// GPIO_NUM_0, //GPIO0 // Workaround: GPIO0 is strap pin, can not be used pullup/pulldown test.
GPIO_NUM_1, //GPIO1
GPIO_NUM_2, //GPIO2
GPIO_NUM_3, //GPIO3
GPIO_NUM_4, //GPIO4
GPIO_NUM_5, //GPIO5
GPIO_NUM_6, //GPIO6
GPIO_NUM_7, //GPIO7
GPIO_NUM_8, //GPIO8
GPIO_NUM_9, //GPIO9
GPIO_NUM_10, //GPIO10
GPIO_NUM_11, //GPIO11
GPIO_NUM_12, //GPIO12
GPIO_NUM_13, //GPIO13
GPIO_NUM_14, //GPIO14
GPIO_NUM_15, //GPIO15
GPIO_NUM_16, //GPIO16
GPIO_NUM_17, //GPIO17
GPIO_NUM_18, //GPIO18
GPIO_NUM_19, //GPIO19
GPIO_NUM_20, //GPIO20
GPIO_NUM_21, //GPIO21
};
#endif

/*
Expand Down Expand Up @@ -276,4 +302,52 @@ TEST_CASE("RTCIO output hold test", "[rtcio]")
ESP_LOGI(TAG, "RTCIO hold test over");
}

#endif
// It is not necessary to test every rtcio pin, it will take too much ci testing time for deep sleep
// Only tests on s_test_map[TEST_RTCIO_DEEP_SLEEP_PIN_INDEX] pin
// (ESP32: IO25, ESP32S2, S3: IO6) these pads' default configuration is low level
#define TEST_RTCIO_DEEP_SLEEP_PIN_INDEX 5

static void rtcio_deep_sleep_hold_test_first_stage(void)
{
printf("configure rtcio pin to hold during deep sleep");
int io_num = s_test_map[TEST_RTCIO_DEEP_SLEEP_PIN_INDEX];

TEST_ESP_OK(esp_sleep_enable_timer_wakeup(2000000));

gpio_config_t io_conf = {
.intr_type = GPIO_INTR_DISABLE,
.mode = GPIO_MODE_INPUT_OUTPUT,
.pin_bit_mask = (1ULL << io_num),
.pull_down_en = 0,
.pull_up_en = 0,
};
gpio_config(&io_conf);

gpio_set_level(io_num, 1);
// Enable global persistence
TEST_ESP_OK(gpio_hold_en(io_num));

esp_deep_sleep_start();
}

static void rtcio_deep_sleep_hold_test_second_stage(void)
{
int io_num = s_test_map[TEST_RTCIO_DEEP_SLEEP_PIN_INDEX];
// Check reset reason is waking up from deepsleep
TEST_ASSERT_EQUAL(ESP_RST_DEEPSLEEP, esp_reset_reason());
// Pin should stay at high level after the deep sleep
TEST_ASSERT_EQUAL_INT(1, gpio_get_level(io_num));

gpio_hold_dis(io_num);
}

/*
* Test rtcio hold function during deep sleep.
* This test case can only check the hold state after waking up from deep sleep
* If you want to check that the rtcio hold function works properly during deep sleep,
* please use logic analyzer or oscillscope
*/
TEST_CASE_MULTIPLE_STAGES("RTCIO_deep_sleep_output_hold_test", "[rtcio]",
rtcio_deep_sleep_hold_test_first_stage,
rtcio_deep_sleep_hold_test_second_stage)
#endif // SOC_RTCIO_PIN_COUNT > 0
7 changes: 6 additions & 1 deletion components/esp_hw_support/include/esp_private/sleep_gpio.h
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ extern "C" {
/**
* @file sleep_gpio.h
*
* This file contains declarations of GPIO related functions in light sleep mode.
* This file contains declarations of GPIO related functions in sleep modes.
*/

#if CONFIG_GPIO_ESP32_SUPPORT_SWITCH_SLP_PULL
Expand All @@ -41,6 +41,11 @@ void gpio_sleep_mode_config_unapply(void);

#endif // CONFIG_GPIO_ESP32_SUPPORT_SWITCH_SLP_PULL

/**
* @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 changes: 7 additions & 7 deletions components/esp_hw_support/include/esp_sleep.h
Original file line number Diff line number Diff line change
Expand Up @@ -239,25 +239,25 @@ esp_err_t esp_sleep_enable_ext1_wakeup(uint64_t mask, esp_sleep_ext1_wakeup_mode
* This function enables an IO pin to wake up the chip from deep sleep.
*
* @note This function does not modify pin configuration. The pins are
* configured in esp_deep_sleep_start/esp_light_sleep_start,
* immediately before entering sleep mode.
* configured inside esp_deep_sleep_start, immediately before entering sleep mode.
*
* @note You don't need to care to pull-up or pull-down before using this
* function, because this will be done in esp_deep_sleep_start/esp_light_sleep_start
* based on param mask you give. BTW, when you use low level to wake up the
* chip, we strongly recommand you to add external registors (pull-up).
* function, because this will be set internally in esp_deep_sleep_start
* based on the wakeup mode. BTW, when you use low level to wake up the
* chip, we strongly recommend you to add external resistors (pull-up).
*
* @param gpio_pin_mask Bit mask of GPIO numbers which will cause wakeup. Only GPIOs
* which are have RTC functionality can be used in this bit map.
* which have RTC functionality (pads that powered by VDD3P3_RTC) can be used in this bit map.
* @param mode Select logic function used to determine wakeup condition:
* - ESP_GPIO_WAKEUP_GPIO_LOW: wake up when the gpio turn to low.
* - ESP_GPIO_WAKEUP_GPIO_HIGH: wake up when the gpio turn to high.
* @return
* - ESP_OK on success
* - ESP_ERR_INVALID_ARG if gpio num is more than 5 or mode is invalid,
* - ESP_ERR_INVALID_ARG if the mask contains any invalid deep sleep wakeup pin or wakeup mode is invalid
*/
esp_err_t esp_deep_sleep_enable_gpio_wakeup(uint64_t gpio_pin_mask, esp_deepsleep_gpio_wake_up_mode_t mode);
#endif

/**
* @brief Enable wakeup from light sleep using GPIOs
*
Expand Down
36 changes: 36 additions & 0 deletions components/esp_hw_support/sleep_gpio.c
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,8 @@

#include "driver/gpio.h"
#include "hal/gpio_hal.h"
#include "hal/rtc_io_hal.h"
#include "hal/rtc_hal.h"
#include "esp_private/gpio.h"
#include "esp_private/sleep_gpio.h"
#include "esp_private/spi_flash_os.h"
Expand Down Expand Up @@ -133,3 +135,37 @@ IRAM_ATTR void esp_sleep_isolate_digital_gpio(void)
}
}
#endif

void esp_deep_sleep_wakeup_io_reset(void)
{
#if SOC_PM_SUPPORT_EXT_WAKEUP
uint32_t rtc_io_mask = rtc_hal_ext1_get_wakeup_pins();
// Disable ext1 wakeup before releasing hold, such that wakeup status can reflect the correct wakeup pin
rtc_hal_ext1_clear_wakeup_pins();
for (int gpio_num = 0; gpio_num < SOC_GPIO_PIN_COUNT && rtc_io_mask != 0; ++gpio_num) {
int rtcio_num = rtc_io_num_map[gpio_num];
if ((rtc_io_mask & BIT(rtcio_num)) == 0) {
continue;
}
rtcio_hal_hold_disable(rtcio_num);
rtc_io_mask &= ~BIT(rtcio_num);
}
#endif

#if SOC_GPIO_SUPPORT_DEEPSLEEP_WAKEUP
uint32_t dl_io_mask = SOC_GPIO_DEEP_SLEEP_WAKE_VALID_GPIO_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;
bool wakeup_io_enabled = gpio_hal_deepsleep_wakeup_is_enabled(&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_deepsleep_wakeup_disable(&gpio_hal, gpio_num);
gpio_hal_hold_dis(&gpio_hal, gpio_num);
}
dl_io_mask &= ~BIT(gpio_num);
}
#endif
}
20 changes: 10 additions & 10 deletions components/esp_hw_support/sleep_modes.c
Original file line number Diff line number Diff line change
Expand Up @@ -207,7 +207,7 @@ static void timer_wakeup_prepare(void);
static void touch_wakeup_prepare(void);
#endif
#if SOC_GPIO_SUPPORT_DEEPSLEEP_WAKEUP
static void esp_deep_sleep_wakeup_prepare(void);
static void gpio_deep_sleep_wakeup_prepare(void);
#endif

#if SOC_RTC_FAST_MEM_SUPPORTED
Expand Down Expand Up @@ -431,8 +431,8 @@ static uint32_t IRAM_ATTR esp_sleep_start(uint32_t pd_flags)
#endif

#if SOC_GPIO_SUPPORT_DEEPSLEEP_WAKEUP
if (s_config.wakeup_triggers & RTC_GPIO_TRIG_EN) {
esp_deep_sleep_wakeup_prepare();
if (deep_sleep && (s_config.wakeup_triggers & RTC_GPIO_TRIG_EN)) {
gpio_deep_sleep_wakeup_prepare();
}
#endif

Expand Down Expand Up @@ -1101,7 +1101,7 @@ static void ext1_wakeup_prepare(void)
}

// Clear state from previous wakeup
rtc_hal_ext1_clear_wakeup_pins();
rtc_hal_ext1_clear_wakeup_status();
// Set RTC IO pins and mode (any high, all low) to be used for wakeup
rtc_hal_ext1_set_wakeup_pins(s_config.ext1_rtc_gpio_mask, s_config.ext1_trigger_mode);
}
Expand All @@ -1111,7 +1111,7 @@ uint64_t esp_sleep_get_ext1_wakeup_status(void)
if (esp_sleep_get_wakeup_cause() != ESP_SLEEP_WAKEUP_EXT1) {
return 0;
}
uint32_t status = rtc_hal_ext1_get_wakeup_pins();
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 < GPIO_PIN_COUNT; ++gpio) {
Expand All @@ -1136,10 +1136,10 @@ uint64_t esp_sleep_get_gpio_wakeup_status(void)
return 0;
}

return rtc_hal_gpio_get_wakeup_pins();
return rtc_hal_gpio_get_wakeup_status();
}

static void esp_deep_sleep_wakeup_prepare(void)
static void gpio_deep_sleep_wakeup_prepare(void)
{
for (gpio_num_t gpio_idx = GPIO_NUM_0; gpio_idx < GPIO_NUM_MAX; gpio_idx++) {
if (((1ULL << gpio_idx) & s_config.gpio_wakeup_mask) == 0) {
Expand All @@ -1152,9 +1152,10 @@ static void esp_deep_sleep_wakeup_prepare(void)
ESP_ERROR_CHECK(gpio_pullup_en(gpio_idx));
ESP_ERROR_CHECK(gpio_pulldown_dis(gpio_idx));
}
rtc_hal_gpio_set_wakeup_pins();
ESP_ERROR_CHECK(gpio_hold_en(gpio_idx));
}
// Clear state from previous wakeup
rtc_hal_gpio_clear_wakeup_status();
}

esp_err_t esp_deep_sleep_enable_gpio_wakeup(uint64_t gpio_pin_mask, esp_deepsleep_gpio_wake_up_mode_t mode)
Expand All @@ -1170,7 +1171,7 @@ esp_err_t esp_deep_sleep_enable_gpio_wakeup(uint64_t gpio_pin_mask, esp_deepslee
continue;
}
if (!esp_sleep_is_valid_wakeup_gpio(gpio_idx)) {
ESP_LOGE(TAG, "invalid mask, please ensure gpio number is no more than 5");
ESP_LOGE(TAG, "gpio %d is an invalid deep sleep wakeup IO", gpio_idx);
return ESP_ERR_INVALID_ARG;
}
err = gpio_deep_sleep_wakeup_enable(gpio_idx, intr_type);
Expand All @@ -1183,7 +1184,6 @@ esp_err_t esp_deep_sleep_enable_gpio_wakeup(uint64_t gpio_pin_mask, esp_deepslee
}
}
s_config.wakeup_triggers |= RTC_GPIO_TRIG_EN;
rtc_hal_gpio_clear_wakeup_pins();
return err;
}

Expand Down
Loading

0 comments on commit 55e040b

Please sign in to comment.