Skip to content

Commit

Permalink
esp_hw_support/sleep: fix current leakage when hold digital io during…
Browse files Browse the repository at this point in the history
… deep sleep
  • Loading branch information
jingli authored and espressif-bot committed Dec 2, 2022
1 parent df80bc8 commit 21c9ec5
Show file tree
Hide file tree
Showing 23 changed files with 477 additions and 24 deletions.
4 changes: 3 additions & 1 deletion components/driver/include/driver/gpio.h
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,9 @@ extern "C" {
/// Check whether it can be a valid GPIO number of output mode
#define GPIO_IS_VALID_OUTPUT_GPIO(gpio_num) ((gpio_num >= 0) && \
(((1ULL << (gpio_num)) & SOC_GPIO_VALID_OUTPUT_GPIO_MASK) != 0))

/// Check whether it can be a valid digital I/O pad
#define GPIO_IS_VALID_DIGITAL_IO_PAD(gpio_num) ((gpio_num >= 0) && \
(((1ULL << (gpio_num)) & SOC_GPIO_VALID_DIGITAL_IO_PAD_MASK) != 0))

typedef intr_handle_t gpio_isr_handle_t;

Expand Down
10 changes: 10 additions & 0 deletions components/esp_hw_support/include/esp_private/esp_sleep_internal.h
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@

#pragma once
#include <stdbool.h>
#include "sdkconfig.h"

#ifdef __cplusplus
extern "C" {
Expand All @@ -20,6 +21,15 @@ extern "C" {
*/
void esp_sleep_enable_adc_tsens_monitor(bool enable);

// IDF does not officially support esp32h2 in v5.0
#if !CONFIG_IDF_TARGET_ESP32H2
/**
* @brief Isolate all digital IOs except those that are held during deep sleep
*
* Reduce digital IOs current leakage during deep sleep.
*/
void esp_sleep_isolate_digital_gpio(void);
#endif

#ifdef __cplusplus
}
Expand Down
43 changes: 43 additions & 0 deletions components/esp_hw_support/sleep_gpio.c
Original file line number Diff line number Diff line change
Expand Up @@ -12,11 +12,13 @@
#include "esp_attr.h"
#include "esp_sleep.h"
#include "esp_log.h"
#include "esp_memory_utils.h"
#include "soc/soc_caps.h"

#include "sdkconfig.h"

#include "driver/gpio.h"
#include "hal/gpio_hal.h"
#include "esp_private/gpio.h"
#include "esp_private/sleep_gpio.h"
#include "esp_private/spi_flash_os.h"
Expand Down Expand Up @@ -94,3 +96,44 @@ void esp_sleep_enable_gpio_switch(bool enable)
}

#endif // SOC_GPIO_SUPPORT_SLP_SWITCH

// IDF does not officially support esp32h2 in v5.0
#if !CONFIG_IDF_TARGET_ESP32H2
IRAM_ATTR void esp_sleep_isolate_digital_gpio(void)
{
gpio_hal_context_t gpio_hal = {
.dev = GPIO_HAL_GET_HW(GPIO_PORT_0)
};

/* no need to do isolate if digital IOs are not being held in deep sleep */
if (!gpio_hal_deep_sleep_hold_is_en(&gpio_hal)) {
return;
}

/**
* there is a situation where we cannot isolate digital IO before deep sleep:
* - task stack is located in external ram(mspi ram), since we will isolate mspi io
*
* assert here instead of returning directly, because if digital IO is not isolated,
* the bottom current of deep sleep will be higher than light sleep, and there is no
* reason to use deep sleep at this time.
*/
assert(esp_ptr_internal(&gpio_hal) && "If hold digital IO, the stack of the task calling esp_deep_sleep_start must be in internal ram!");

/* isolate digital IO that is not held(keep the configuration of digital IOs held by users) */
for (gpio_num_t gpio_num = GPIO_NUM_0; gpio_num < GPIO_NUM_MAX; gpio_num++) {
if (GPIO_IS_VALID_DIGITAL_IO_PAD(gpio_num) && !gpio_hal_is_digital_io_hold(&gpio_hal, gpio_num)) {
/* disable I/O */
gpio_hal_input_disable(&gpio_hal, gpio_num);
gpio_hal_output_disable(&gpio_hal, gpio_num);

/* disable pull up/down */
gpio_hal_pullup_dis(&gpio_hal, gpio_num);
gpio_hal_pulldown_dis(&gpio_hal, gpio_num);

/* make pad work as gpio(otherwise, deep sleep bottom current will rise) */
gpio_hal_func_sel(&gpio_hal, gpio_num, PIN_FUNC_GPIO);
}
}
}
#endif
5 changes: 5 additions & 0 deletions components/esp_hw_support/sleep_modes.c
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@
#include "esp_attr.h"
#include "esp_memory_utils.h"
#include "esp_sleep.h"
#include "esp_private/esp_sleep_internal.h"
#include "esp_private/esp_timer_private.h"
#include "esp_private/system_internal.h"
#include "esp_log.h"
Expand Down Expand Up @@ -507,6 +508,10 @@ static uint32_t IRAM_ATTR esp_sleep_start(uint32_t pd_flags)
*/
portENTER_CRITICAL(&spinlock_rtc_deep_sleep);

#if !CONFIG_IDF_TARGET_ESP32H2 // IDF does not officially support esp32h2 in v5.0
esp_sleep_isolate_digital_gpio();
#endif

#if SOC_PM_SUPPORT_DEEPSLEEP_CHECK_STUB_ONLY
extern char _rtc_text_start[];
#if CONFIG_ESP32S3_RTCDATA_IN_FAST_MEM
Expand Down
70 changes: 67 additions & 3 deletions components/hal/esp32/include/hal/gpio_ll.h
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,9 @@
extern "C" {
#endif

// the address of esp32's IO_MUX_GPIOx_REGs are not incremented as the gpio number increments(address are out of order)
extern const uint8_t GPIO_PIN_MUX_REG_OFFSET[SOC_GPIO_PIN_COUNT];

// Get GPIO hardware instance with giving gpio num
#define GPIO_LL_GET_HW(num) (((num) == 0) ? (&GPIO) : NULL)

Expand All @@ -53,9 +56,10 @@ static inline void gpio_ll_pullup_en(gpio_dev_t *hw, uint32_t gpio_num)
* @param hw Peripheral GPIO hardware instance address.
* @param gpio_num GPIO number
*/
__attribute__((always_inline))
static inline void gpio_ll_pullup_dis(gpio_dev_t *hw, uint32_t gpio_num)
{
REG_CLR_BIT(GPIO_PIN_MUX_REG[gpio_num], FUN_PU);
REG_CLR_BIT(DR_REG_IO_MUX_BASE + GPIO_PIN_MUX_REG_OFFSET[gpio_num], FUN_PU);
}

/**
Expand Down Expand Up @@ -87,9 +91,10 @@ static inline void gpio_ll_pulldown_en(gpio_dev_t *hw, uint32_t gpio_num)
* @param hw Peripheral GPIO hardware instance address.
* @param gpio_num GPIO number
*/
__attribute__((always_inline))
static inline void gpio_ll_pulldown_dis(gpio_dev_t *hw, uint32_t gpio_num)
{
REG_CLR_BIT(GPIO_PIN_MUX_REG[gpio_num], FUN_PD);
REG_CLR_BIT(DR_REG_IO_MUX_BASE + GPIO_PIN_MUX_REG_OFFSET[gpio_num], FUN_PD);
}

/**
Expand Down Expand Up @@ -303,9 +308,10 @@ static inline void gpio_ll_intr_disable(gpio_dev_t *hw, uint32_t gpio_num)
* @param hw Peripheral GPIO hardware instance address.
* @param gpio_num GPIO number
*/
__attribute__((always_inline))
static inline void gpio_ll_input_disable(gpio_dev_t *hw, uint32_t gpio_num)
{
PIN_INPUT_DISABLE(GPIO_PIN_MUX_REG[gpio_num]);
PIN_INPUT_DISABLE(DR_REG_IO_MUX_BASE + GPIO_PIN_MUX_REG_OFFSET[gpio_num]);
}

/**
Expand All @@ -325,6 +331,7 @@ static inline void gpio_ll_input_enable(gpio_dev_t *hw, uint32_t gpio_num)
* @param hw Peripheral GPIO hardware instance address.
* @param gpio_num GPIO number
*/
__attribute__((always_inline))
static inline void gpio_ll_output_disable(gpio_dev_t *hw, uint32_t gpio_num)
{
if (gpio_num < 32) {
Expand Down Expand Up @@ -419,6 +426,18 @@ static inline void gpio_ll_od_enable(gpio_dev_t *hw, uint32_t gpio_num)
hw->pin[gpio_num].pad_driver = 1;
}

/**
* @brief Select a function for the pin in the IOMUX
*
* @param hw Peripheral GPIO hardware instance address.
* @param gpio_num GPIO number
* @param func Function to assign to the pin
*/
static inline __attribute__((always_inline)) void gpio_ll_func_sel(gpio_dev_t *hw, uint8_t gpio_num, uint32_t func)
{
PIN_FUNC_SELECT(DR_REG_IO_MUX_BASE + GPIO_PIN_MUX_REG_OFFSET[gpio_num], func);
}

/**
* @brief GPIO set output level
*
Expand Down Expand Up @@ -531,6 +550,21 @@ static inline void gpio_ll_deep_sleep_hold_dis(gpio_dev_t *hw)
CLEAR_PERI_REG_MASK(RTC_CNTL_DIG_ISO_REG, RTC_CNTL_DG_PAD_AUTOHOLD_EN_M);
}

/**
* @brief Get deep sleep hold status
*
* @param hw Peripheral GPIO hardware instance address.
*
* @return
* - true deep sleep hold is enabled
* - false deep sleep hold is disabled
*/
__attribute__((always_inline))
static inline bool gpio_ll_deep_sleep_hold_is_en(gpio_dev_t *hw)
{
return !GET_PERI_REG_MASK(RTC_CNTL_DIG_ISO_REG, RTC_CNTL_DG_PAD_FORCE_UNHOLD) && GET_PERI_REG_MASK(RTC_CNTL_DIG_ISO_REG, RTC_CNTL_DG_PAD_AUTOHOLD_EN_M);
}

/**
* @brief Enable gpio pad hold function.
*
Expand All @@ -553,6 +587,36 @@ static inline void gpio_ll_hold_dis(gpio_dev_t *hw, uint32_t gpio_num)
CLEAR_PERI_REG_MASK(RTC_IO_DIG_PAD_HOLD_REG, GPIO_HOLD_MASK[gpio_num]);
}

/**
* @brief Get digital gpio pad hold status.
*
* @param hw Peripheral GPIO hardware instance address.
* @param gpio_num GPIO number, only support output GPIOs
*
* @note caller must ensure that gpio_num is a digital io pad
*
* @return
* - true digital gpio pad is held
* - false digital gpio pad is unheld
*/
__attribute__((always_inline))
static inline bool gpio_ll_is_digital_io_hold(gpio_dev_t *hw, uint32_t gpio_num)
{
uint32_t mask = 0;

switch (gpio_num) {
case 1: mask = BIT(1); break;
case 3: mask = BIT(0); break;
case 5: mask = BIT(8); break;
case 6 ... 11 : mask = BIT(gpio_num - 4); break;
case 16 ... 19:
case 21 ... 23: mask = BIT(gpio_num - 7); break;
default: break;
}

return GET_PERI_REG_MASK(RTC_IO_DIG_PAD_HOLD_REG, mask);
}

/**
* @brief Set pad input to a peripheral signal through the IOMUX.
*
Expand Down
58 changes: 54 additions & 4 deletions components/hal/esp32c2/include/hal/gpio_ll.h
Original file line number Diff line number Diff line change
Expand Up @@ -14,12 +14,13 @@

#pragma once

#include <stdlib.h>
#include <stdbool.h>
#include "soc/soc.h"
#include "soc/gpio_periph.h"
#include "soc/rtc_cntl_reg.h"
#include "hal/gpio_types.h"
#include "hal/assert.h"
#include "stdlib.h"

#ifdef __cplusplus
extern "C" {
Expand Down Expand Up @@ -47,9 +48,10 @@ static inline void gpio_ll_pullup_en(gpio_dev_t *hw, uint32_t gpio_num)
* @param hw Peripheral GPIO hardware instance address.
* @param gpio_num GPIO number
*/
__attribute__((always_inline))
static inline void gpio_ll_pullup_dis(gpio_dev_t *hw, uint32_t gpio_num)
{
REG_CLR_BIT(GPIO_PIN_MUX_REG[gpio_num], FUN_PU);
REG_CLR_BIT(IO_MUX_GPIO0_REG + (gpio_num * 4), FUN_PU);
}

/**
Expand All @@ -69,9 +71,10 @@ static inline void gpio_ll_pulldown_en(gpio_dev_t *hw, uint32_t gpio_num)
* @param hw Peripheral GPIO hardware instance address.
* @param gpio_num GPIO number
*/
__attribute__((always_inline))
static inline void gpio_ll_pulldown_dis(gpio_dev_t *hw, uint32_t gpio_num)
{
REG_CLR_BIT(GPIO_PIN_MUX_REG[gpio_num], FUN_PD);
REG_CLR_BIT(IO_MUX_GPIO0_REG + (gpio_num * 4), FUN_PD);
}

/**
Expand Down Expand Up @@ -168,9 +171,10 @@ static inline void gpio_ll_intr_disable(gpio_dev_t *hw, uint32_t gpio_num)
* @param hw Peripheral GPIO hardware instance address.
* @param gpio_num GPIO number
*/
__attribute__((always_inline))
static inline void gpio_ll_input_disable(gpio_dev_t *hw, uint32_t gpio_num)
{
PIN_INPUT_DISABLE(GPIO_PIN_MUX_REG[gpio_num]);
PIN_INPUT_DISABLE(IO_MUX_GPIO0_REG + (gpio_num * 4));
}

/**
Expand All @@ -190,6 +194,7 @@ static inline void gpio_ll_input_enable(gpio_dev_t *hw, uint32_t gpio_num)
* @param hw Peripheral GPIO hardware instance address.
* @param gpio_num GPIO number
*/
__attribute__((always_inline))
static inline void gpio_ll_output_disable(gpio_dev_t *hw, uint32_t gpio_num)
{
hw->enable_w1tc.enable_w1tc = (0x1 << gpio_num);
Expand Down Expand Up @@ -231,6 +236,18 @@ static inline void gpio_ll_od_enable(gpio_dev_t *hw, uint32_t gpio_num)
hw->pin[gpio_num].pad_driver = 1;
}

/**
* @brief Select a function for the pin in the IOMUX
*
* @param hw Peripheral GPIO hardware instance address.
* @param gpio_num GPIO number
* @param func Function to assign to the pin
*/
static inline __attribute__((always_inline)) void gpio_ll_func_sel(gpio_dev_t *hw, uint8_t gpio_num, uint32_t func)
{
PIN_FUNC_SELECT(IO_MUX_GPIO0_REG + (gpio_num * 4), func);
}

/**
* @brief GPIO set output level
*
Expand Down Expand Up @@ -332,6 +349,21 @@ static inline void gpio_ll_deep_sleep_hold_dis(gpio_dev_t *hw)
SET_PERI_REG_MASK(RTC_CNTL_DIG_ISO_REG, RTC_CNTL_CLR_DG_PAD_AUTOHOLD);
}

/**
* @brief Get deep sleep hold status
*
* @param hw Peripheral GPIO hardware instance address.
*
* @return
* - true deep sleep hold is enabled
* - false deep sleep hold is disabled
*/
__attribute__((always_inline))
static inline bool gpio_ll_deep_sleep_hold_is_en(gpio_dev_t *hw)
{
return !GET_PERI_REG_MASK(RTC_CNTL_DIG_ISO_REG, RTC_CNTL_DG_PAD_FORCE_UNHOLD) && GET_PERI_REG_MASK(RTC_CNTL_DIG_ISO_REG, RTC_CNTL_DG_PAD_AUTOHOLD_EN_M);
}

/**
* @brief Enable gpio pad hold function.
*
Expand Down Expand Up @@ -362,6 +394,24 @@ static inline void gpio_ll_hold_dis(gpio_dev_t *hw, uint32_t gpio_num)
}
}

/**
* @brief Get digital gpio pad hold status.
*
* @param hw Peripheral GPIO hardware instance address.
* @param gpio_num GPIO number, only support output GPIOs
*
* @note caller must ensure that gpio_num is a digital io pad
*
* @return
* - true digital gpio pad is held
* - false digital gpio pad is unheld
*/
__attribute__((always_inline))
static inline bool gpio_ll_is_digital_io_hold(gpio_dev_t *hw, uint32_t gpio_num)
{
return GET_PERI_REG_MASK(RTC_CNTL_DIG_PAD_HOLD_REG, BIT(gpio_num));
}

/**
* @brief Set pad input to a peripheral signal through the IOMUX.
*
Expand Down
Loading

0 comments on commit 21c9ec5

Please sign in to comment.