Files
esp-idf/components/esp_hw_support/sleep_gpio.c
Wu Zheng Hui fe3da7167e Merge branch 'fix/fix_esp32p4_retention_cost' into 'master'
fix(esp_hw_support): optimize retention cost and update sleep time compensation

See merge request espressif/esp-idf!37920
2025-04-23 13:20:49 +08:00

239 lines
9.1 KiB
C

/*
* SPDX-FileCopyrightText: 2015-2025 Espressif Systems (Shanghai) CO LTD
*
* SPDX-License-Identifier: Apache-2.0
*/
#include <stddef.h>
#include <string.h>
#include <sys/lock.h>
#include <sys/param.h>
#include "esp_attr.h"
#include "esp_sleep.h"
#include "esp_log.h"
#include "esp_memory_utils.h"
#include "soc/soc_caps.h"
#include "soc/uart_pins.h"
#include "sdkconfig.h"
#include "driver/gpio.h"
#include "hal/gpio_hal.h"
#include "hal/rtc_io_hal.h"
#include "soc/rtc_io_periph.h"
#include "soc/uart_channel.h"
#include "hal/rtc_hal.h"
#include "esp_private/gpio.h"
#include "esp_private/sleep_gpio.h"
#include "esp_private/spi_flash_os.h"
#include "esp_private/startup_internal.h"
#include "bootloader_flash.h"
static const char *TAG = "sleep_gpio";
#if CONFIG_GPIO_ESP32_SUPPORT_SWITCH_SLP_PULL
void gpio_sleep_mode_config_apply(void)
{
for (gpio_num_t gpio_num = GPIO_NUM_0; gpio_num < GPIO_NUM_MAX; gpio_num++) {
if (GPIO_IS_VALID_GPIO(gpio_num)) {
gpio_sleep_pupd_config_apply(gpio_num);
}
}
}
IRAM_ATTR void gpio_sleep_mode_config_unapply(void)
{
for (gpio_num_t gpio_num = GPIO_NUM_0; gpio_num < GPIO_NUM_MAX; gpio_num++) {
if (GPIO_IS_VALID_GPIO(gpio_num)) {
gpio_sleep_pupd_config_unapply(gpio_num);
}
}
}
#endif
void esp_sleep_config_gpio_isolate(void)
{
ESP_EARLY_LOGI(TAG, "Configure to isolate all GPIO pins in sleep state");
for (gpio_num_t gpio_num = GPIO_NUM_0; gpio_num < GPIO_NUM_MAX; gpio_num++) {
if (GPIO_IS_VALID_GPIO(gpio_num)) {
gpio_sleep_set_direction(gpio_num, GPIO_MODE_DISABLE);
gpio_sleep_set_pull_mode(gpio_num, GPIO_FLOATING);
}
}
#if CONFIG_ESP_SLEEP_MSPI_NEED_ALL_IO_PU && !SOC_MSPI_HAS_INDEPENT_IOMUX
gpio_sleep_set_pull_mode(esp_mspi_get_io(ESP_MSPI_IO_CLK), GPIO_PULLUP_ONLY);
gpio_sleep_set_pull_mode(esp_mspi_get_io(ESP_MSPI_IO_Q), GPIO_PULLUP_ONLY);
gpio_sleep_set_pull_mode(esp_mspi_get_io(ESP_MSPI_IO_D), GPIO_PULLUP_ONLY);
gpio_sleep_set_pull_mode(esp_mspi_get_io(ESP_MSPI_IO_HD), GPIO_PULLUP_ONLY);
gpio_sleep_set_pull_mode(esp_mspi_get_io(ESP_MSPI_IO_WP), GPIO_PULLUP_ONLY);
#if SOC_SPI_MEM_SUPPORT_FLASH_OPI_MODE
bool octal_mspi_required = bootloader_flash_is_octal_mode_enabled();
#if CONFIG_SPIRAM_MODE_OCT
octal_mspi_required |= true;
#endif // CONFIG_SPIRAM_MODE_OCT
if (octal_mspi_required) {
gpio_sleep_set_pull_mode(esp_mspi_get_io(ESP_MSPI_IO_DQS), GPIO_PULLUP_ONLY);
gpio_sleep_set_pull_mode(esp_mspi_get_io(ESP_MSPI_IO_D4), GPIO_PULLUP_ONLY);
gpio_sleep_set_pull_mode(esp_mspi_get_io(ESP_MSPI_IO_D5), GPIO_PULLUP_ONLY);
gpio_sleep_set_pull_mode(esp_mspi_get_io(ESP_MSPI_IO_D6), GPIO_PULLUP_ONLY);
gpio_sleep_set_pull_mode(esp_mspi_get_io(ESP_MSPI_IO_D7), GPIO_PULLUP_ONLY);
}
#endif // SOC_SPI_MEM_SUPPORT_FLASH_OPI_MODE
#endif // CONFIG_ESP_SLEEP_MSPI_NEED_ALL_IO_PU
}
void esp_sleep_enable_gpio_switch(bool enable)
{
ESP_EARLY_LOGI(TAG, "%s automatic switching of GPIO sleep configuration", enable ? "Enable" : "Disable");
for (gpio_num_t gpio_num = GPIO_NUM_0; gpio_num < GPIO_NUM_MAX; gpio_num++) {
if (GPIO_IS_VALID_GPIO(gpio_num)) {
#if CONFIG_ESP_CONSOLE_UART
#if CONFIG_ESP_CONSOLE_UART_CUSTOM
const int uart_tx_gpio = (CONFIG_ESP_CONSOLE_UART_TX_GPIO >= 0) ? CONFIG_ESP_CONSOLE_UART_TX_GPIO : UART_NUM_0_TXD_DIRECT_GPIO_NUM;
const int uart_rx_gpio = (CONFIG_ESP_CONSOLE_UART_RX_GPIO >= 0) ? CONFIG_ESP_CONSOLE_UART_RX_GPIO : UART_NUM_0_RXD_DIRECT_GPIO_NUM;
if ((gpio_num == uart_tx_gpio) || (gpio_num == uart_rx_gpio)) {
#else
if ((gpio_num == UART_NUM_0_TXD_DIRECT_GPIO_NUM) || (gpio_num == UART_NUM_0_RXD_DIRECT_GPIO_NUM)) {
#endif
gpio_sleep_sel_dis(gpio_num);
continue;
}
#endif
/* If the PSRAM is disable in ESP32xx chips equipped with PSRAM, there will be a large current leakage. */
#if CONFIG_ESP_SLEEP_PSRAM_LEAKAGE_WORKAROUND && CONFIG_SPIRAM & !SOC_MSPI_HAS_INDEPENT_IOMUX
if (gpio_num == esp_mspi_get_io(ESP_MSPI_IO_CS1)) {
gpio_sleep_sel_dis(gpio_num);
continue;
}
#endif // CONFIG_ESP_SLEEP_PSRAM_LEAKAGE_WORKAROUND && CONFIG_SPIRAM
#if CONFIG_ESP_SLEEP_FLASH_LEAKAGE_WORKAROUND & !SOC_MSPI_HAS_INDEPENT_IOMUX
if (gpio_num == esp_mspi_get_io(ESP_MSPI_IO_CS0)) {
gpio_sleep_sel_dis(gpio_num);
continue;
}
#endif // CONFIG_ESP_SLEEP_FLASH_LEAKAGE_WORKAROUND
if (enable) {
gpio_sleep_sel_en(gpio_num);
} else {
gpio_sleep_sel_dis(gpio_num);
}
}
}
}
#if SOC_GPIO_SUPPORT_HOLD_IO_IN_DSLP && !SOC_GPIO_SUPPORT_HOLD_SINGLE_IO_IN_DSLP
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)) {
bool is_mspi_io_pad = false;
esp_mspi_io_t mspi_ios[] = { ESP_MSPI_IO_CS0, ESP_MSPI_IO_CLK, ESP_MSPI_IO_Q, ESP_MSPI_IO_D, ESP_MSPI_IO_HD, ESP_MSPI_IO_WP };
for (int i = 0; i < sizeof(mspi_ios) / sizeof(mspi_ios[0]); i++) {
if (esp_mspi_get_io(mspi_ios[i]) == gpio_num) {
is_mspi_io_pad = true;
break;
}
}
// Ignore MSPI and default Console UART io pads, When the CPU executes
// the following instructions to configure the MSPI IO PAD, access on
// the MSPI signal lines (as CPU instruction execution and MSPI access
// operations are asynchronous) may cause the SoC to hang.
if (is_mspi_io_pad || gpio_num == U0RXD_GPIO_NUM || gpio_num == U0TXD_GPIO_NUM) {
continue;
}
/* 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 //SOC_GPIO_SUPPORT_HOLD_IO_IN_DSLP && !SOC_GPIO_SUPPORT_HOLD_SINGLE_IO_IN_DSLP
#if SOC_DEEP_SLEEP_SUPPORTED
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();
// 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
}
#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)
{
// 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);
return ESP_OK;
}
void esp_sleep_gpio_include(void)
{
// Linker hook function, exists to make the linker examine this file
}
#endif