Skip to content

Commit

Permalink
gpio: Fix IO hold function related problems
Browse files Browse the repository at this point in the history
1. Fix deep sleep wakeup IOs can not be unhold issue
2. Correct hold related APIs' description
3. Fix gpio_force_hold_all API

docs: Add GPIO wakeup source to sleep_modes doc for ESP32C3 and C2
  • Loading branch information
songruo committed Nov 11, 2022
1 parent 8c40017 commit d0a7dc3
Show file tree
Hide file tree
Showing 29 changed files with 286 additions and 196 deletions.
12 changes: 6 additions & 6 deletions components/driver/gpio/gpio.c
Original file line number Diff line number Diff line change
Expand Up @@ -717,25 +717,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 @@ -360,16 +360,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 @@ -399,19 +404,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 @@ -433,14 +440,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
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 SOC_GPIO_SUPPORT_SLP_SWITCH && CONFIG_GPIO_ESP32_SUPPORT_SWITCH_SLP_PULL
Expand All @@ -41,6 +41,11 @@ void gpio_sleep_mode_config_unapply(void);

#endif // SOC_GPIO_SUPPORT_SLP_SWITCH && 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 @@ -245,25 +245,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 @@ -144,3 +146,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 @@ -215,7 +215,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_PM_SUPPORT_DEEPSLEEP_CHECK_STUB_ONLY
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 @@ -1096,7 +1096,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 @@ -1106,7 +1106,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 @@ -1131,10 +1131,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 @@ -1147,9 +1147,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 @@ -1165,7 +1166,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 @@ -1178,7 +1179,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
14 changes: 5 additions & 9 deletions components/esp_system/port/cpu_start.c
Original file line number Diff line number Diff line change
Expand Up @@ -75,8 +75,7 @@
#include "esp_private/crosscore_int.h"
#include "esp_flash_encrypt.h"

#include "hal/rtc_io_hal.h"
#include "hal/gpio_hal.h"
#include "esp_private/sleep_gpio.h"
#include "hal/wdt_hal.h"
#include "soc/rtc.h"
#include "hal/efuse_ll.h"
Expand Down Expand Up @@ -527,13 +526,10 @@ void IRAM_ATTR call_start_cpu0(void)
#endif
#endif

#if SOC_RTCIO_HOLD_SUPPORTED
rtcio_hal_unhold_all();
#elif CONFIG_IDF_TARGET_ESP32C6 // TODO: IDF-6027
CLEAR_PERI_REG_MASK(LP_AON_GPIO_HOLD0_REG, 0xFF);
#else
gpio_hal_force_unhold_all();
#endif
// Need to unhold the IOs that were hold right before entering deep sleep, which are used as wakeup pins
if (rst_reas[0] == RESET_REASON_CORE_DEEP_SLEEP) {
esp_deep_sleep_wakeup_io_reset();
}

esp_cache_err_int_init();

Expand Down
14 changes: 12 additions & 2 deletions components/hal/esp32/include/hal/rtc_cntl_ll.h
Original file line number Diff line number Diff line change
Expand Up @@ -20,12 +20,12 @@ static inline void rtc_cntl_ll_set_wakeup_timer(uint64_t t)
WRITE_PERI_REG(RTC_CNTL_SLP_TIMER1_REG, t >> 32);
}

static inline void rtc_cntl_ll_ext1_clear_wakeup_pins(void)
static inline void rtc_cntl_ll_ext1_clear_wakeup_status(void)
{
REG_SET_BIT(RTC_CNTL_EXT_WAKEUP1_REG, RTC_CNTL_EXT_WAKEUP1_STATUS_CLR);
}

static inline uint32_t rtc_cntl_ll_ext1_get_wakeup_pins(void)
static inline uint32_t rtc_cntl_ll_ext1_get_wakeup_status(void)
{
return REG_GET_FIELD(RTC_CNTL_EXT_WAKEUP1_STATUS_REG, RTC_CNTL_EXT_WAKEUP1_STATUS);
}
Expand All @@ -37,6 +37,16 @@ static inline void rtc_cntl_ll_ext1_set_wakeup_pins(uint32_t mask, int mode)
mode, RTC_CNTL_EXT_WAKEUP1_LV_S);
}

static inline void rtc_cntl_ll_ext1_clear_wakeup_pins(void)
{
CLEAR_PERI_REG_MASK(RTC_CNTL_EXT_WAKEUP1_REG, RTC_CNTL_EXT_WAKEUP1_SEL_M);
}

static inline uint32_t rtc_cntl_ll_ext1_get_wakeup_pins(void)
{
return REG_GET_FIELD(RTC_CNTL_EXT_WAKEUP1_REG, RTC_CNTL_EXT_WAKEUP1_SEL);
}

static inline void rtc_cntl_ll_ulp_wakeup_enable(void)
{
SET_PERI_REG_MASK(RTC_CNTL_STATE0_REG, RTC_CNTL_ULP_CP_WAKEUP_FORCE_EN);
Expand Down
8 changes: 4 additions & 4 deletions components/hal/esp32/include/hal/rtc_io_ll.h
Original file line number Diff line number Diff line change
Expand Up @@ -207,7 +207,7 @@ static inline void rtcio_ll_pulldown_disable(int rtcio_num)
}

/**
* Enable force hold function for RTC IO pad.
* Enable force hold function on an RTC IO pad.
*
* Enabling HOLD function will cause the pad to lock current status, such as,
* input/output enable, input/output value, function, drive strength values.
Expand All @@ -223,7 +223,7 @@ static inline void rtcio_ll_force_hold_enable(int rtcio_num)
}

/**
* Disable hold function on an RTC IO pad
* Disable hold function on an RTC IO pad.
*
* @note If disable the pad hold, the status of pad maybe changed in sleep mode.
* @param rtcio_num The index of rtcio. 0 ~ MAX(rtcio).
Expand All @@ -235,7 +235,7 @@ static inline void rtcio_ll_force_hold_disable(int rtcio_num)
}

/**
* Enable force hold function for RTC IO pad.
* Enable force hold function on all RTC IO pads.
*
* Enabling HOLD function will cause the pad to lock current status, such as,
* input/output enable, input/output value, function, drive strength values.
Expand All @@ -250,7 +250,7 @@ static inline void rtcio_ll_force_hold_all(void)
}

/**
* Disable hold function on an RTC IO pad
* Disable hold function on all RTC IO pads.
*
* @note If disable the pad hold, the status of pad maybe changed in sleep mode.
* @param rtcio_num The index of rtcio. 0 ~ MAX(rtcio).
Expand Down
Loading

0 comments on commit d0a7dc3

Please sign in to comment.