Skip to content

Commit 9516e80

Browse files
committed
Merge branch 'bugfix/wakeup_io_hold_unhold_in_sleep_v4.4' into 'release/v4.4'
gpio: Fix deep sleep wakeup IOs unable to unhold after wakeup (v4.4) See merge request espressif/esp-idf!22670
2 parents 2951d6e + 81b5907 commit 9516e80

File tree

27 files changed

+360
-204
lines changed

27 files changed

+360
-204
lines changed

components/driver/gpio.c

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -690,25 +690,25 @@ void gpio_deep_sleep_hold_dis(void)
690690

691691
#if SOC_GPIO_SUPPORT_FORCE_HOLD
692692

693-
esp_err_t gpio_force_hold_all()
693+
esp_err_t IRAM_ATTR gpio_force_hold_all()
694694
{
695695
#if SOC_RTCIO_HOLD_SUPPORTED
696-
rtc_gpio_force_hold_all();
696+
rtc_gpio_force_hold_en_all();
697697
#endif
698698
portENTER_CRITICAL(&gpio_context.gpio_spinlock);
699699
gpio_hal_force_hold_all(gpio_context.gpio_hal);
700700
portEXIT_CRITICAL(&gpio_context.gpio_spinlock);
701701
return ESP_OK;
702702
}
703703

704-
esp_err_t gpio_force_unhold_all()
704+
esp_err_t IRAM_ATTR gpio_force_unhold_all()
705705
{
706-
#if SOC_RTCIO_HOLD_SUPPORTED
707-
rtc_gpio_force_hold_dis_all();
708-
#endif
709706
portENTER_CRITICAL(&gpio_context.gpio_spinlock);
710707
gpio_hal_force_unhold_all();
711708
portEXIT_CRITICAL(&gpio_context.gpio_spinlock);
709+
#if SOC_RTCIO_HOLD_SUPPORTED
710+
rtc_gpio_force_hold_dis_all();
711+
#endif
712712
return ESP_OK;
713713
}
714714
#endif

components/driver/include/driver/gpio.h

Lines changed: 28 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -364,16 +364,21 @@ esp_err_t gpio_get_drive_capability(gpio_num_t gpio_num, gpio_drive_cap_t *stren
364364
/**
365365
* @brief Enable gpio pad hold function.
366366
*
367+
* When the pin is set to hold, the state is latched at that moment and will not change no matter how the internal
368+
* signals change or how the IO MUX/GPIO configuration is modified (including input enable, output enable,
369+
* output value, function, and drive strength values). It can be used to retain the pin state through a
370+
* core reset and system reset triggered by watchdog time-out or Deep-sleep events.
371+
*
367372
* The gpio pad hold function works in both input and output modes, but must be output-capable gpios.
368373
* If pad hold enabled:
369374
* in output mode: the output level of the pad will be force locked and can not be changed.
370-
* in input mode: the input value read will not change, regardless the changes of input signal.
375+
* in input mode: input read value can still reflect the changes of the input signal.
371376
*
372-
* The state of digital gpio cannot be held during Deep-sleep, and it will resume the hold function
377+
* The state of the digital gpio cannot be held during Deep-sleep, and it will resume to hold at its default pin state
373378
* when the chip wakes up from Deep-sleep. If the digital gpio also needs to be held during Deep-sleep,
374379
* `gpio_deep_sleep_hold_en` should also be called.
375380
*
376-
* Power down or call gpio_hold_dis will disable this function.
381+
* Power down or call `gpio_hold_dis` will disable this function.
377382
*
378383
* @param gpio_num GPIO number, only support output-capable GPIOs
379384
*
@@ -403,19 +408,21 @@ esp_err_t gpio_hold_en(gpio_num_t gpio_num);
403408
esp_err_t gpio_hold_dis(gpio_num_t gpio_num);
404409

405410
/**
406-
* @brief Enable all digital gpio pad hold function during Deep-sleep.
411+
* @brief Enable all digital gpio pads hold function during Deep-sleep.
412+
*
413+
* Enabling this feature makes all digital gpio pads be at the holding state during Deep-sleep. The state of each pad
414+
* holds is its active configuration (not pad's sleep configuration!).
407415
*
408-
* When the chip is in Deep-sleep mode, all digital gpio will hold the state before sleep, and when the chip is woken up,
409-
* 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,
410-
* when not in sleep mode, the digital gpio state can be changed even you have called this function.
416+
* Note that this pad hold feature only works when the chip is in Deep-sleep mode. When the chip is in active mode,
417+
* the digital gpio state can be changed freely even you have called this function.
411418
*
412-
* 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.
419+
* After this API is being called, the digital gpio Deep-sleep hold feature will work during every sleep process. You
420+
* should call `gpio_deep_sleep_hold_dis` to disable this feature.
413421
*/
414422
void gpio_deep_sleep_hold_en(void);
415423

416424
/**
417-
* @brief Disable all digital gpio pad hold function during Deep-sleep.
418-
*
425+
* @brief Disable all digital gpio pads hold function during Deep-sleep.
419426
*/
420427
void gpio_deep_sleep_hold_dis(void);
421428

@@ -437,14 +444,21 @@ void gpio_iomux_out(uint8_t gpio_num, int func, bool oen_inv);
437444

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

445460
/**
446-
* @brief Force unhold digital and rtc gpio pad.
447-
* @note GPIO force unhold, whether the chip in sleep mode or wakeup mode.
461+
* @brief Force unhold all digital and rtc gpio pads.
448462
* */
449463
esp_err_t gpio_force_unhold_all(void);
450464
#endif

components/driver/include/driver/rtc_io.h

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -269,7 +269,7 @@ esp_err_t rtc_gpio_isolate(gpio_num_t gpio_num);
269269
* Force hold signal is enabled before going into deep sleep for pins which
270270
* are used for EXT1 wakeup.
271271
*/
272-
esp_err_t rtc_gpio_force_hold_all(void);
272+
esp_err_t rtc_gpio_force_hold_en_all(void);
273273

274274
/**
275275
* @brief Disable force hold signal for all RTC IOs

components/driver/test/test_rtcio.c

Lines changed: 76 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,7 @@
2121
#include "esp_log.h"
2222
#include "soc/rtc_io_periph.h"
2323

24-
#if !TEMPORARY_DISABLED_FOR_TARGETS(ESP32S3, ESP32C3)
24+
#if SOC_RTCIO_PIN_COUNT > 0
2525

2626
#define RTCIO_CHECK(condition) TEST_ASSERT_MESSAGE((condition == ESP_OK), "ret is not ESP_OK")
2727
#define RTCIO_VERIFY(condition, msg) TEST_ASSERT_MESSAGE((condition), msg)
@@ -77,6 +77,32 @@ const int s_test_map[TEST_GPIO_PIN_COUNT] = {
7777
GPIO_NUM_20, //GPIO20
7878
GPIO_NUM_21, //GPIO21
7979
};
80+
#elif defined CONFIG_IDF_TARGET_ESP32S3
81+
#define TEST_GPIO_PIN_COUNT 21
82+
const int s_test_map[TEST_GPIO_PIN_COUNT] = {
83+
// GPIO_NUM_0, //GPIO0 // Workaround: GPIO0 is strap pin, can not be used pullup/pulldown test.
84+
GPIO_NUM_1, //GPIO1
85+
GPIO_NUM_2, //GPIO2
86+
GPIO_NUM_3, //GPIO3
87+
GPIO_NUM_4, //GPIO4
88+
GPIO_NUM_5, //GPIO5
89+
GPIO_NUM_6, //GPIO6
90+
GPIO_NUM_7, //GPIO7
91+
GPIO_NUM_8, //GPIO8
92+
GPIO_NUM_9, //GPIO9
93+
GPIO_NUM_10, //GPIO10
94+
GPIO_NUM_11, //GPIO11
95+
GPIO_NUM_12, //GPIO12
96+
GPIO_NUM_13, //GPIO13
97+
GPIO_NUM_14, //GPIO14
98+
GPIO_NUM_15, //GPIO15
99+
GPIO_NUM_16, //GPIO16
100+
GPIO_NUM_17, //GPIO17
101+
GPIO_NUM_18, //GPIO18
102+
GPIO_NUM_19, //GPIO19
103+
GPIO_NUM_20, //GPIO20
104+
GPIO_NUM_21, //GPIO21
105+
};
80106
#endif
81107

82108
/*
@@ -276,4 +302,52 @@ TEST_CASE("RTCIO output hold test", "[rtcio]")
276302
ESP_LOGI(TAG, "RTCIO hold test over");
277303
}
278304

279-
#endif
305+
// It is not necessary to test every rtcio pin, it will take too much ci testing time for deep sleep
306+
// Only tests on s_test_map[TEST_RTCIO_DEEP_SLEEP_PIN_INDEX] pin
307+
// (ESP32: IO25, ESP32S2, S3: IO6) these pads' default configuration is low level
308+
#define TEST_RTCIO_DEEP_SLEEP_PIN_INDEX 5
309+
310+
static void rtcio_deep_sleep_hold_test_first_stage(void)
311+
{
312+
printf("configure rtcio pin to hold during deep sleep");
313+
int io_num = s_test_map[TEST_RTCIO_DEEP_SLEEP_PIN_INDEX];
314+
315+
TEST_ESP_OK(esp_sleep_enable_timer_wakeup(2000000));
316+
317+
gpio_config_t io_conf = {
318+
.intr_type = GPIO_INTR_DISABLE,
319+
.mode = GPIO_MODE_INPUT_OUTPUT,
320+
.pin_bit_mask = (1ULL << io_num),
321+
.pull_down_en = 0,
322+
.pull_up_en = 0,
323+
};
324+
gpio_config(&io_conf);
325+
326+
gpio_set_level(io_num, 1);
327+
// Enable global persistence
328+
TEST_ESP_OK(gpio_hold_en(io_num));
329+
330+
esp_deep_sleep_start();
331+
}
332+
333+
static void rtcio_deep_sleep_hold_test_second_stage(void)
334+
{
335+
int io_num = s_test_map[TEST_RTCIO_DEEP_SLEEP_PIN_INDEX];
336+
// Check reset reason is waking up from deepsleep
337+
TEST_ASSERT_EQUAL(ESP_RST_DEEPSLEEP, esp_reset_reason());
338+
// Pin should stay at high level after the deep sleep
339+
TEST_ASSERT_EQUAL_INT(1, gpio_get_level(io_num));
340+
341+
gpio_hold_dis(io_num);
342+
}
343+
344+
/*
345+
* Test rtcio hold function during deep sleep.
346+
* This test case can only check the hold state after waking up from deep sleep
347+
* If you want to check that the rtcio hold function works properly during deep sleep,
348+
* please use logic analyzer or oscillscope
349+
*/
350+
TEST_CASE_MULTIPLE_STAGES("RTCIO_deep_sleep_output_hold_test", "[rtcio]",
351+
rtcio_deep_sleep_hold_test_first_stage,
352+
rtcio_deep_sleep_hold_test_second_stage)
353+
#endif // SOC_RTCIO_PIN_COUNT > 0

components/esp_hw_support/include/esp_private/sleep_gpio.h

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,7 @@ extern "C" {
1515
/**
1616
* @file sleep_gpio.h
1717
*
18-
* This file contains declarations of GPIO related functions in light sleep mode.
18+
* This file contains declarations of GPIO related functions in sleep modes.
1919
*/
2020

2121
#if CONFIG_GPIO_ESP32_SUPPORT_SWITCH_SLP_PULL
@@ -41,6 +41,11 @@ void gpio_sleep_mode_config_unapply(void);
4141

4242
#endif // CONFIG_GPIO_ESP32_SUPPORT_SWITCH_SLP_PULL
4343

44+
/**
45+
* @brief Call once in startup to disable the wakeup IO pins and release their holding state after waking up from Deep-sleep
46+
*/
47+
void esp_deep_sleep_wakeup_io_reset(void);
48+
4449
#ifdef __cplusplus
4550
}
4651
#endif

components/esp_hw_support/include/esp_sleep.h

Lines changed: 7 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -234,25 +234,25 @@ esp_err_t esp_sleep_enable_ext1_wakeup(uint64_t mask, esp_sleep_ext1_wakeup_mode
234234
* This function enables an IO pin to wake the chip from deep sleep
235235
*
236236
* @note This function does not modify pin configuration. The pins are
237-
* configured in esp_sleep_start, immediately before
238-
* entering sleep mode.
237+
* configured inside esp_deep_sleep_start, immediately before entering sleep mode.
239238
*
240239
* @note You don't need to care to pull-up or pull-down before using this
241-
* function, because this will be done in esp_sleep_start based on
242-
* param mask you give. BTW, when you use low level to wake up the
243-
* chip, we strongly recommand you to add external registors(pull-up).
240+
* function, because this will be set internally in esp_deep_sleep_start
241+
* based on the wakeup mode. BTW, when you use low level to wake up the
242+
* chip, we strongly recommend you to add external resistors (pull-up).
244243
*
245244
* @param gpio_pin_mask Bit mask of GPIO numbers which will cause wakeup. Only GPIOs
246-
* which are have RTC functionality can be used in this bit map.
245+
* which have RTC functionality (pads that powered by VDD3P3_RTC) can be used in this bit map.
247246
* @param mode Select logic function used to determine wakeup condition:
248247
* - ESP_GPIO_WAKEUP_GPIO_LOW: wake up when the gpio turn to low.
249248
* - ESP_GPIO_WAKEUP_GPIO_HIGH: wake up when the gpio turn to high.
250249
* @return
251250
* - ESP_OK on success
252-
* - ESP_ERR_INVALID_ARG if gpio num is more than 5 or mode is invalid,
251+
* - ESP_ERR_INVALID_ARG if the mask contains any invalid deep sleep wakeup pin or wakeup mode is invalid
253252
*/
254253
esp_err_t esp_deep_sleep_enable_gpio_wakeup(uint64_t gpio_pin_mask, esp_deepsleep_gpio_wake_up_mode_t mode);
255254
#endif
255+
256256
/**
257257
* @brief Enable wakeup from light sleep using GPIOs
258258
*

components/esp_hw_support/sleep_gpio.c

Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,8 @@
1919

2020
#include "driver/gpio.h"
2121
#include "hal/gpio_hal.h"
22+
#include "hal/rtc_io_hal.h"
23+
#include "hal/rtc_hal.h"
2224
#include "esp_private/gpio.h"
2325
#include "esp_private/sleep_gpio.h"
2426
#include "esp_private/spi_flash_os.h"
@@ -133,3 +135,37 @@ IRAM_ATTR void esp_sleep_isolate_digital_gpio(void)
133135
}
134136
}
135137
#endif
138+
139+
void esp_deep_sleep_wakeup_io_reset(void)
140+
{
141+
#if SOC_PM_SUPPORT_EXT_WAKEUP
142+
uint32_t rtc_io_mask = rtc_hal_ext1_get_wakeup_pins();
143+
// Disable ext1 wakeup before releasing hold, such that wakeup status can reflect the correct wakeup pin
144+
rtc_hal_ext1_clear_wakeup_pins();
145+
for (int gpio_num = 0; gpio_num < SOC_GPIO_PIN_COUNT && rtc_io_mask != 0; ++gpio_num) {
146+
int rtcio_num = rtc_io_num_map[gpio_num];
147+
if ((rtc_io_mask & BIT(rtcio_num)) == 0) {
148+
continue;
149+
}
150+
rtcio_hal_hold_disable(rtcio_num);
151+
rtc_io_mask &= ~BIT(rtcio_num);
152+
}
153+
#endif
154+
155+
#if SOC_GPIO_SUPPORT_DEEPSLEEP_WAKEUP
156+
uint32_t dl_io_mask = SOC_GPIO_DEEP_SLEEP_WAKEUP_VALID_GPIO_MASK;
157+
gpio_hal_context_t gpio_hal = {
158+
.dev = GPIO_HAL_GET_HW(GPIO_PORT_0)
159+
};
160+
while (dl_io_mask) {
161+
int gpio_num = __builtin_ffs(dl_io_mask) - 1;
162+
bool wakeup_io_enabled = gpio_hal_deepsleep_wakeup_is_enabled(&gpio_hal, gpio_num);
163+
if (wakeup_io_enabled) {
164+
// Disable the wakeup before releasing hold, such that wakeup status can reflect the correct wakeup pin
165+
gpio_hal_deepsleep_wakeup_disable(&gpio_hal, gpio_num);
166+
gpio_hal_hold_dis(&gpio_hal, gpio_num);
167+
}
168+
dl_io_mask &= ~BIT(gpio_num);
169+
}
170+
#endif
171+
}

components/esp_hw_support/sleep_modes.c

Lines changed: 10 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -201,7 +201,7 @@ static void timer_wakeup_prepare(void);
201201
static void touch_wakeup_prepare(void);
202202
#endif
203203
#if SOC_GPIO_SUPPORT_DEEPSLEEP_WAKEUP
204-
static void esp_deep_sleep_wakeup_prepare(void);
204+
static void gpio_deep_sleep_wakeup_prepare(void);
205205
#endif
206206

207207
#if SOC_PM_SUPPORT_DEEPSLEEP_VERIFY_STUB_ONLY
@@ -417,8 +417,8 @@ static uint32_t IRAM_ATTR esp_sleep_start(uint32_t pd_flags)
417417
#endif
418418

419419
#if SOC_GPIO_SUPPORT_DEEPSLEEP_WAKEUP
420-
if (s_config.wakeup_triggers & RTC_GPIO_TRIG_EN) {
421-
esp_deep_sleep_wakeup_prepare();
420+
if (deep_sleep && (s_config.wakeup_triggers & RTC_GPIO_TRIG_EN)) {
421+
gpio_deep_sleep_wakeup_prepare();
422422
}
423423
#endif
424424

@@ -1100,7 +1100,7 @@ static void ext1_wakeup_prepare(void)
11001100
}
11011101

11021102
// Clear state from previous wakeup
1103-
rtc_hal_ext1_clear_wakeup_pins();
1103+
rtc_hal_ext1_clear_wakeup_status();
11041104
// Set RTC IO pins and mode (any high, all low) to be used for wakeup
11051105
rtc_hal_ext1_set_wakeup_pins(s_config.ext1_rtc_gpio_mask, s_config.ext1_trigger_mode);
11061106
}
@@ -1110,7 +1110,7 @@ uint64_t esp_sleep_get_ext1_wakeup_status(void)
11101110
if (esp_sleep_get_wakeup_cause() != ESP_SLEEP_WAKEUP_EXT1) {
11111111
return 0;
11121112
}
1113-
uint32_t status = rtc_hal_ext1_get_wakeup_pins();
1113+
uint32_t status = rtc_hal_ext1_get_wakeup_status();
11141114
// Translate bit map of RTC IO numbers into the bit map of GPIO numbers
11151115
uint64_t gpio_mask = 0;
11161116
for (int gpio = 0; gpio < GPIO_PIN_COUNT; ++gpio) {
@@ -1135,10 +1135,10 @@ uint64_t esp_sleep_get_gpio_wakeup_status(void)
11351135
return 0;
11361136
}
11371137

1138-
return rtc_hal_gpio_get_wakeup_pins();
1138+
return rtc_hal_gpio_get_wakeup_status();
11391139
}
11401140

1141-
static void esp_deep_sleep_wakeup_prepare(void)
1141+
static void gpio_deep_sleep_wakeup_prepare(void)
11421142
{
11431143
for (gpio_num_t gpio_idx = GPIO_NUM_0; gpio_idx < GPIO_NUM_MAX; gpio_idx++) {
11441144
if (((1ULL << gpio_idx) & s_config.gpio_wakeup_mask) == 0) {
@@ -1151,9 +1151,10 @@ static void esp_deep_sleep_wakeup_prepare(void)
11511151
ESP_ERROR_CHECK(gpio_pullup_en(gpio_idx));
11521152
ESP_ERROR_CHECK(gpio_pulldown_dis(gpio_idx));
11531153
}
1154-
rtc_hal_gpio_set_wakeup_pins();
11551154
ESP_ERROR_CHECK(gpio_hold_en(gpio_idx));
11561155
}
1156+
// Clear state from previous wakeup
1157+
rtc_hal_gpio_clear_wakeup_status();
11571158
}
11581159

11591160
esp_err_t esp_deep_sleep_enable_gpio_wakeup(uint64_t gpio_pin_mask, esp_deepsleep_gpio_wake_up_mode_t mode)
@@ -1169,7 +1170,7 @@ esp_err_t esp_deep_sleep_enable_gpio_wakeup(uint64_t gpio_pin_mask, esp_deepslee
11691170
continue;
11701171
}
11711172
if (!esp_sleep_is_valid_wakeup_gpio(gpio_idx)) {
1172-
ESP_LOGE(TAG, "invalid mask, please ensure gpio number is no more than 5");
1173+
ESP_LOGE(TAG, "gpio %d is an invalid deep sleep wakeup IO", gpio_idx);
11731174
return ESP_ERR_INVALID_ARG;
11741175
}
11751176
err = gpio_deep_sleep_wakeup_enable(gpio_idx, intr_type);
@@ -1182,7 +1183,6 @@ esp_err_t esp_deep_sleep_enable_gpio_wakeup(uint64_t gpio_pin_mask, esp_deepslee
11821183
}
11831184
}
11841185
s_config.wakeup_triggers |= RTC_GPIO_TRIG_EN;
1185-
rtc_hal_gpio_clear_wakeup_pins();
11861186
return err;
11871187
}
11881188

0 commit comments

Comments
 (0)