/* * SPDX-FileCopyrightText: 2015-2026 Espressif Systems (Shanghai) CO LTD * * SPDX-License-Identifier: Apache-2.0 */ #include #include #include #include #include #include "esp_attr.h" #include "esp_rom_caps.h" #include "esp_macros.h" #include "esp_memory_utils.h" #include "esp_sleep.h" #include "esp_private/esp_clk_tree_common.h" #include "esp_private/esp_clk_utils.h" #include "esp_private/esp_sleep_internal.h" #include "esp_private/esp_timer_private.h" #include "esp_private/rtc_clk.h" #include "soc/rtc.h" #include "esp_private/sleep_event.h" #include "esp_private/system_internal.h" #include "esp_private/sleep_retention.h" #include "esp_private/io_mux.h" #include "esp_private/critical_section.h" #include "esp_private/spi_flash_os.h" #include "esp_log.h" #include "esp_newlib.h" #include "esp_timer.h" #include "esp_ipc_isr.h" #include "freertos/FreeRTOS.h" #include "freertos/task.h" #include "soc/soc_caps.h" #include "soc/spi_pins.h" #include "soc/chip_revision.h" #include "driver/rtc_io.h" #include "driver/gpio.h" #include "hal/efuse_hal.h" #include "hal/rtc_io_hal.h" #include "hal/clk_tree_hal.h" #include "rom/rtc.h" #if RNG_LL_NEEDS_RESET_WHEN_WAKEUP #include "hal/rng_ll.h" #endif #if SOC_SLEEP_SYSTIMER_STALL_WORKAROUND #include "hal/systimer_ll.h" #endif #if SOC_SLEEP_TGWDT_STOP_WORKAROUND #include "hal/mwdt_ll.h" #include "hal/timer_ll.h" #endif #if !SOC_PMU_SUPPORTED #include "hal/rtc_cntl_ll.h" #endif #include "hal/rtc_hal.h" #include "soc/rtc.h" #include "hal/clk_gate_ll.h" #include "hal/clk_tree_ll.h" #if SOC_WDT_SUPPORTED || SOC_RTC_WDT_SUPPORTED || SOC_SLEEP_TGWDT_STOP_WORKAROUND #include "hal/wdt_hal.h" #endif #include "hal/uart_hal.h" #if SOC_TOUCH_SENSOR_SUPPORTED #include "hal/touch_sens_hal.h" #endif #if SOC_ADC_SUPPORTED #include "hal/adc_ll.h" // For `ADC_LL_ANA_CALI_REG_PD_WORKAROUND`, temp #include "hal/adc_hal_common.h" #endif #include "hal/temperature_sensor_hal.h" #include "hal/mspi_ll.h" #include "hal/gpio_ll.h" #if SOC_LP_CORE_HW_AUTO_CLRWAKEUPCAUSE #include "hal/lp_aon_hal.h" #endif #include "sdkconfig.h" #include "esp_rom_serial_output.h" #include "esp_rom_sys.h" #include "esp_private/brownout.h" #include "esp_private/sleep_console.h" #include "esp_private/sleep_uart.h" #include "esp_private/sleep_cpu.h" #include "esp_private/sleep_cache.h" #include "esp_private/sleep_modem.h" #include "esp_private/sleep_flash.h" #include "esp_private/sleep_usb.h" #include "esp_private/esp_clk.h" #include "esp_private/esp_task_wdt.h" #include "esp_private/sar_periph_ctrl.h" #include "esp_private/sleep_gpio.h" #ifdef CONFIG_IDF_TARGET_ESP32 #include "esp_private/gpio.h" #elif CONFIG_IDF_TARGET_ESP32S2 #include "soc/extmem_reg.h" #include "esp_private/gpio.h" #endif #if CONFIG_ESP_INT_WDT && CONFIG_ESP32_ECO3_CACHE_LOCK_FIX #include "esp_private/eco3_livelock_workaround.h" #endif #include "hal/rtc_timer_hal.h" #if SOC_VBAT_SUPPORTED #include "esp_vbat.h" #include "hal/vbat_ll.h" #endif #if SOC_PMU_SUPPORTED #include "esp_private/esp_pmu.h" #include "esp_private/sleep_sys_periph.h" #include "esp_private/sleep_clock.h" #endif #if SOC_PM_SUPPORT_PMU_CLK_ICG #include "esp_private/sleep_clock_icg.h" #endif #if SOC_PM_RETENTION_SW_TRIGGER_REGDMA #include "esp_private/sleep_retention.h" #endif #if CONFIG_PM_SLP_SPIRAM_HALFSLEEP_ENABLED #include "esp_private/esp_psram_impl.h" #endif // If light sleep time is less than that, don't power down flash #define FLASH_PD_MIN_SLEEP_TIME_US 2000 // Default waiting time for the software to wait for Flash ready after waking up from sleep #define ESP_SLEEP_WAIT_FLASH_READY_DEFAULT_DELAY_US 700 // Cycles for RTC Timer clock source (internal oscillator) calibrate #define RTC_CLK_SRC_CAL_CYCLES (10) #define FAST_CLK_SRC_CAL_CYCLES (2048) /* ~ 127.4 us */ #ifdef CONFIG_IDF_TARGET_ESP32 #if !CONFIG_PM_SLP_IRAM_OPT #define DEFAULT_SLEEP_OUT_OVERHEAD_US (476) #else #define DEFAULT_SLEEP_OUT_OVERHEAD_US (212) #endif #define DEFAULT_HARDWARE_OUT_OVERHEAD_US (60) #elif CONFIG_IDF_TARGET_ESP32S2 #define DEFAULT_SLEEP_OUT_OVERHEAD_US (147) #define DEFAULT_HARDWARE_OUT_OVERHEAD_US (28) #elif CONFIG_IDF_TARGET_ESP32S3 #define DEFAULT_SLEEP_OUT_OVERHEAD_US (382) #define DEFAULT_HARDWARE_OUT_OVERHEAD_US (133) # if !CONFIG_PM_SLP_IRAM_OPT #undef DEFAULT_SLEEP_OUT_OVERHEAD_US #define DEFAULT_SLEEP_OUT_OVERHEAD_US (8628) # endif #elif CONFIG_IDF_TARGET_ESP32C3 #define DEFAULT_SLEEP_OUT_OVERHEAD_US (105) #define DEFAULT_HARDWARE_OUT_OVERHEAD_US (37) #elif CONFIG_IDF_TARGET_ESP32C2 #define DEFAULT_SLEEP_OUT_OVERHEAD_US (118) #define DEFAULT_HARDWARE_OUT_OVERHEAD_US (9) # if !CONFIG_PM_SLP_IRAM_OPT #undef DEFAULT_SLEEP_OUT_OVERHEAD_US #define DEFAULT_SLEEP_OUT_OVERHEAD_US (2779) #undef DEFAULT_HARDWARE_OUT_OVERHEAD_US #define DEFAULT_HARDWARE_OUT_OVERHEAD_US (157) # endif #elif CONFIG_IDF_TARGET_ESP32C6 #define DEFAULT_SLEEP_OUT_OVERHEAD_US (318) #define DEFAULT_HARDWARE_OUT_OVERHEAD_US (56) #define PVT_REINIT_COST_US (60) #elif CONFIG_IDF_TARGET_ESP32C5 #define DEFAULT_SLEEP_OUT_OVERHEAD_US (318) #define DEFAULT_HARDWARE_OUT_OVERHEAD_US (56) #define PVT_REINIT_COST_US (75) #elif CONFIG_IDF_TARGET_ESP32C61 #define DEFAULT_SLEEP_OUT_OVERHEAD_US (65) #define DEFAULT_HARDWARE_OUT_OVERHEAD_US (70) #define PVT_REINIT_COST_US (90) #elif CONFIG_IDF_TARGET_ESP32H2 #define DEFAULT_SLEEP_OUT_OVERHEAD_US (118) #define DEFAULT_HARDWARE_OUT_OVERHEAD_US (9) #elif CONFIG_IDF_TARGET_ESP32H21 #define DEFAULT_SLEEP_OUT_OVERHEAD_US (118) #define DEFAULT_HARDWARE_OUT_OVERHEAD_US (9) #elif CONFIG_IDF_TARGET_ESP32H4 #define DEFAULT_SLEEP_OUT_OVERHEAD_US (130) #define DEFAULT_HARDWARE_OUT_OVERHEAD_US (9) #elif CONFIG_IDF_TARGET_ESP32P4 #define DEFAULT_SLEEP_OUT_OVERHEAD_US (324) #define DEFAULT_HARDWARE_OUT_OVERHEAD_US (240) #define LDO_POWER_TAKEOVER_PREPARATION_TIME_US (185) #elif CONFIG_IDF_TARGET_ESP32S31 #define DEFAULT_SLEEP_OUT_OVERHEAD_US (324) #define DEFAULT_HARDWARE_OUT_OVERHEAD_US (780) #define PVT_REINIT_COST_US (95) #endif // Actually costs 80us, using the fastest slow clock 150K calculation takes about 16 ticks #define SLEEP_TIMER_ALARM_TO_SLEEP_TICKS (16) #define LIGHT_SLEEP_TIME_OVERHEAD_US DEFAULT_HARDWARE_OUT_OVERHEAD_US #ifdef CONFIG_ESP_SYSTEM_RTC_EXT_XTAL #define DEEP_SLEEP_TIME_OVERHEAD_US (650 + 100 * 240 / CONFIG_ESP_DEFAULT_CPU_FREQ_MHZ) #else #define DEEP_SLEEP_TIME_OVERHEAD_US (250 + 100 * 240 / CONFIG_ESP_DEFAULT_CPU_FREQ_MHZ) #endif #define RTC_MODULE_SLEEP_PREPARE_CYCLES (6) #define CHECK_SOURCE(source, value, mask) ((s_config.wakeup_triggers & mask) && \ (source == value)) #if CONFIG_PM_SLP_IRAM_OPT #define SLEEP_FN_ATTR FORCE_IRAM_ATTR #else #define SLEEP_FN_ATTR #endif #define MAX_DSLP_HOOKS 3 static esp_deep_sleep_cb_t s_dslp_cb[MAX_DSLP_HOOKS] = {0}; #if CONFIG_ESP_PHY_ENABLED && SOC_DEEP_SLEEP_SUPPORTED static esp_deep_sleep_cb_t s_dslp_phy_cb[MAX_DSLP_HOOKS] = {0}; #endif /** * Internal structure which holds all requested sleep parameters */ typedef struct { struct { esp_sleep_pd_option_t pd_option; int16_t refs; uint16_t reserved; /* reserved for 4 bytes aligned */ } domain[ESP_PD_DOMAIN_MAX]; portMUX_TYPE lock; uint64_t sleep_duration; uint32_t wakeup_triggers; #if SOC_PM_SUPPORT_EXT1_WAKEUP uint32_t ext1_trigger_mode : 22; // 22 is the maximum RTCIO number in all chips uint32_t ext1_rtc_gpio_mask : 22; #endif #if SOC_PM_SUPPORT_EXT0_WAKEUP uint32_t ext0_trigger_level : 1; uint32_t ext0_rtc_gpio_num : 5; #endif #if SOC_GPIO_SUPPORT_HP_PERIPH_PD_SLEEP_WAKEUP uint64_t gpio_wakeup_mask; uint8_t gpio_trigger_mode[SOC_GPIO_PIN_COUNT]; #endif uint32_t sleep_time_adjustment; uint32_t ccount_ticks_record; uint32_t sleep_time_overhead_out; uint32_t rtc_clk_cal_period; uint32_t fast_clk_cal_period; uint64_t rtc_ticks_at_sleep_start; #if SOC_DCDC_SUPPORTED uint64_t rtc_ticks_at_ldo_prepare; #endif bool overhead_out_need_remeasure; } sleep_config_t; #if CONFIG_ESP_SLEEP_DEBUG static esp_sleep_context_t *s_sleep_ctx = NULL; void esp_sleep_set_sleep_context(esp_sleep_context_t *sleep_ctx) { s_sleep_ctx = sleep_ctx; } #endif static uint32_t s_lightsleep_cnt = 0; #if SOC_RTCIO_PIN_COUNT > 0 _Static_assert(22 >= SOC_RTCIO_PIN_COUNT, "Chip has more RTCIOs than 22, should increase ext1_rtc_gpio_mask field size"); #endif static sleep_config_t s_config = { .domain = { [0 ... ESP_PD_DOMAIN_MAX - 1] = { .pd_option = ESP_PD_OPTION_AUTO, .refs = 0 } }, .lock = portMUX_INITIALIZER_UNLOCKED, .ccount_ticks_record = 0, .sleep_time_overhead_out = DEFAULT_SLEEP_OUT_OVERHEAD_US, .wakeup_triggers = 0, .overhead_out_need_remeasure = true }; /* Internal variable used to track if light sleep wakeup sources are to be expected when determining wakeup cause. */ static bool s_light_sleep_wakeup = false; ESP_LOG_ATTR_TAG(TAG, "sleep"); /* APP core of esp32 can't access to RTC FAST MEMORY, do not define it with RTC_IRAM_ATTR, RTC_SLOW_ATTR is for ESP_SLEEP_USE_ADC_TSEN_MONITOR_MODE only, For other submodules, there is no logical error if the ref_cnt is stored in DRAM. and ESP_SLEEP_USE_ADC_TSEN_MONITOR_MODE will only be used on chips that support RTC_MEM */ #if SOC_RTC_FAST_MEM_SUPPORTED || SOC_RTC_SLOW_MEM_SUPPORTED RTC_SLOW_ATTR #endif static int32_t s_sleep_sub_mode_ref_cnt[ESP_SLEEP_MODE_MAX] = { 0 }; void esp_sleep_enter_critical(void) { esp_os_enter_critical(&s_config.lock); } void esp_sleep_exit_critical(void) { esp_os_exit_critical(&s_config.lock); } void esp_sleep_enter_critical_safe(void) { esp_os_enter_critical_safe(&s_config.lock); } void esp_sleep_exit_critical_safe(void) { esp_os_exit_critical_safe(&s_config.lock); } void esp_sleep_overhead_out_time_refresh(void) { esp_sleep_enter_critical(); s_config.overhead_out_need_remeasure = true; esp_sleep_exit_critical(); } static uint32_t get_power_down_flags(void); static uint32_t get_sleep_flags(uint32_t pd_flags, bool deepsleep); #if CONFIG_ESP_SLEEP_ENABLE_RTC_WDT_IN_SLEEP && SOC_RTC_WDT_SUPPORTED static uint32_t get_sleep_rtc_wdt_timeout(uint64_t sleep_duration); static uint32_t calc_sleep_slow_clk_required_cycles(uint32_t timeout, uint32_t rtc_slow_clk_cal_period); #endif #if SOC_PM_SUPPORT_EXT0_WAKEUP static void ext0_wakeup_prepare(void); #endif #if SOC_PM_SUPPORT_EXT1_WAKEUP static void ext1_wakeup_prepare(void); #endif static esp_err_t timer_wakeup_prepare(int64_t sleep_duration); #if SOC_TOUCH_SENSOR_SUPPORTED static void touch_wakeup_prepare(void); #endif #if SOC_VBAT_SUPPORTED static void vbat_under_volt_wakeup_prepare(void); #endif #if SOC_GPIO_SUPPORT_HP_PERIPH_PD_SLEEP_WAKEUP static void esp_sleep_gpio_wakeup_prepare_on_hp_periph_powerdown(void); #endif #if ESP_ROM_SUPPORT_DEEP_SLEEP_WAKEUP_STUB && SOC_DEEP_SLEEP_SUPPORTED #if SOC_PM_SUPPORT_DEEPSLEEP_CHECK_STUB_ONLY static RTC_FAST_ATTR esp_deep_sleep_wake_stub_fn_t wake_stub_fn_handler = NULL; static void RTC_IRAM_ATTR __attribute__((used, noinline)) esp_wake_stub_start(void) { if (wake_stub_fn_handler) { (*wake_stub_fn_handler)(); } } /* We must have a default deep sleep wake stub entry function, which must be * located at the start address of the RTC fast memory, and its implementation * must be simple enough to ensure that there is no literal data before the * wake stub entry, otherwise, the literal data before the wake stub entry * will not be CRC checked. */ static void __attribute__((section(".rtc.entry.text"))) esp_wake_stub_entry(void) { #define _SYM2STR(s) # s #define SYM2STR(s) _SYM2STR(s) #ifdef __riscv __asm__ __volatile__ ( "addi sp, sp, -16 \n" "sw ra, 0(sp) \n" "jal ra, " SYM2STR(esp_wake_stub_start) "\n" "lw ra, 0(sp) \n" "addi sp, sp, 16 \n" ); #else // call4 has a larger effective addressing range (-524284 to 524288 bytes), // which is sufficient for instruction addressing in RTC fast memory. __asm__ __volatile__ ("call4 " SYM2STR(esp_wake_stub_start) "\n"); #endif } void RTC_IRAM_ATTR esp_set_deep_sleep_wake_stub_default_entry(void) { extern char _rtc_text_start[]; #if CONFIG_ESP32S3_RTCDATA_IN_FAST_MEM extern char _rtc_noinit_end[]; size_t rtc_fast_length = (size_t)_rtc_noinit_end - (size_t)_rtc_text_start; #else extern char _rtc_force_fast_end[]; size_t rtc_fast_length = (size_t)_rtc_force_fast_end - (size_t)_rtc_text_start; #endif esp_rom_set_rtc_wake_addr((esp_rom_wake_func_t)esp_wake_stub_entry, rtc_fast_length); } #endif // SOC_PM_SUPPORT_DEEPSLEEP_CHECK_STUB_ONLY /* Wake from deep sleep stub See esp_deepsleep.h esp_wake_deep_sleep() comments for details. */ esp_deep_sleep_wake_stub_fn_t esp_get_deep_sleep_wake_stub(void) { #if SOC_PM_SUPPORT_DEEPSLEEP_CHECK_STUB_ONLY esp_deep_sleep_wake_stub_fn_t stub_ptr = wake_stub_fn_handler; #else esp_deep_sleep_wake_stub_fn_t stub_ptr = (esp_deep_sleep_wake_stub_fn_t) REG_READ(RTC_ENTRY_ADDR_REG); #endif if (!esp_ptr_executable(stub_ptr)) { return NULL; } return stub_ptr; } #if CONFIG_IDF_TARGET_ESP32 /* APP core of esp32 can't access to RTC FAST MEMORY, link to RTC SLOW MEMORY instead*/ RTC_SLOW_ATTR #elif (SOC_RTC_FAST_MEM_SUPPORTED || SOC_RTC_SLOW_MEM_SUPPORTED) RTC_IRAM_ATTR #endif void esp_set_deep_sleep_wake_stub(esp_deep_sleep_wake_stub_fn_t new_stub) { #if SOC_PM_SUPPORT_DEEPSLEEP_CHECK_STUB_ONLY wake_stub_fn_handler = new_stub; #else REG_WRITE(RTC_ENTRY_ADDR_REG, (uint32_t)new_stub); #endif } #if CONFIG_IDF_TARGET_ESP32 /* APP core of esp32 can't access to RTC FAST MEMORY, link to RTC SLOW MEMORY instead*/ RTC_SLOW_ATTR #elif (SOC_RTC_FAST_MEM_SUPPORTED || SOC_RTC_SLOW_MEM_SUPPORTED) RTC_IRAM_ATTR #endif void esp_default_wake_deep_sleep(void) { /* Clear MMU for CPU 0 */ #if CONFIG_IDF_TARGET_ESP32 _DPORT_REG_WRITE(DPORT_PRO_CACHE_CTRL1_REG, _DPORT_REG_READ(DPORT_PRO_CACHE_CTRL1_REG) | DPORT_PRO_CACHE_MMU_IA_CLR); _DPORT_REG_WRITE(DPORT_PRO_CACHE_CTRL1_REG, _DPORT_REG_READ(DPORT_PRO_CACHE_CTRL1_REG) & (~DPORT_PRO_CACHE_MMU_IA_CLR)); #if CONFIG_ESP_SLEEP_WAIT_FLASH_READY_EXTRA_DELAY > 0 // ROM code has not started yet, so we need to set delay factor // used by esp_rom_delay_us first. ets_update_cpu_frequency_rom(ets_get_detected_xtal_freq() / 1000000); // Time from VDD_SDIO power up to first flash read in ROM code is 700 us, // for some flash chips is not sufficient, this delay is configured in menuconfig, // it can be used to give the flash chip some extra time to become ready. // For later chips, we have EFUSE_FLASH_TPUW field to configure it and do // this delay in the ROM. esp_rom_delay_us(CONFIG_ESP_SLEEP_WAIT_FLASH_READY_EXTRA_DELAY); #endif #elif CONFIG_IDF_TARGET_ESP32S2 REG_SET_BIT(EXTMEM_CACHE_DBG_INT_ENA_REG, EXTMEM_CACHE_DBG_EN); #endif } void __attribute__((weak, alias("esp_default_wake_deep_sleep"))) esp_wake_deep_sleep(void); #endif // SOC_RTC_FAST_MEM_SUPPORTED void esp_deep_sleep(uint64_t time_in_us) { esp_sleep_enable_timer_wakeup(time_in_us); esp_deep_sleep_start(); } esp_err_t esp_deep_sleep_try(uint64_t time_in_us) { esp_sleep_enable_timer_wakeup(time_in_us); return esp_deep_sleep_try_to_start(); } static esp_err_t s_sleep_hook_register(esp_deep_sleep_cb_t new_cb, esp_deep_sleep_cb_t s_cb_array[MAX_DSLP_HOOKS]) { esp_sleep_enter_critical(); for (int n = 0; n < MAX_DSLP_HOOKS; n++) { if (s_cb_array[n]==NULL || s_cb_array[n]==new_cb) { s_cb_array[n]=new_cb; esp_sleep_exit_critical(); return ESP_OK; } } esp_sleep_exit_critical(); ESP_LOGE(TAG, "Registered deepsleep callbacks exceeds MAX_DSLP_HOOKS"); return ESP_ERR_NO_MEM; } static void s_sleep_hook_deregister(esp_deep_sleep_cb_t old_cb, esp_deep_sleep_cb_t s_cb_array[MAX_DSLP_HOOKS]) { esp_sleep_enter_critical(); for (int n = 0; n < MAX_DSLP_HOOKS; n++) { if(s_cb_array[n] == old_cb) { s_cb_array[n] = NULL; } } esp_sleep_exit_critical(); } esp_err_t esp_deep_sleep_register_hook(esp_deep_sleep_cb_t new_dslp_cb) { return s_sleep_hook_register(new_dslp_cb, s_dslp_cb); } void esp_deep_sleep_deregister_hook(esp_deep_sleep_cb_t old_dslp_cb) { s_sleep_hook_deregister(old_dslp_cb, s_dslp_cb); } #if CONFIG_ESP_PHY_ENABLED && SOC_DEEP_SLEEP_SUPPORTED esp_err_t esp_deep_sleep_register_phy_hook(esp_deep_sleep_cb_t new_dslp_cb) { return s_sleep_hook_register(new_dslp_cb, s_dslp_phy_cb); } void esp_deep_sleep_deregister_phy_hook(esp_deep_sleep_cb_t old_dslp_cb) { s_sleep_hook_deregister(old_dslp_cb, s_dslp_phy_cb); } static void s_do_deep_sleep_phy_callback(void) { for (int n = 0; n < MAX_DSLP_HOOKS; n++) { if (s_dslp_phy_cb[n] != NULL) { s_dslp_phy_cb[n](); } } } #endif #if SOC_SLEEP_TGWDT_STOP_WORKAROUND static uint32_t s_stopped_tgwdt_bmap = 0; #endif // Must be called from critical sections. static SLEEP_FN_ATTR void suspend_timers(uint32_t sleep_flags) { if (!(sleep_flags & RTC_SLEEP_PD_XTAL)) { #if SOC_SLEEP_TGWDT_STOP_WORKAROUND /* If timegroup implemented task watchdog or interrupt watchdog is running, we have to stop it. */ for (uint32_t tg_num = 0; tg_num < TIMG_LL_GET(INST_NUM); ++tg_num) { if (mwdt_ll_check_if_enabled(TIMER_LL_GET_HW(tg_num))) { mwdt_ll_write_protect_disable(TIMER_LL_GET_HW(tg_num)); mwdt_ll_disable(TIMER_LL_GET_HW(tg_num)); mwdt_ll_write_protect_enable(TIMER_LL_GET_HW(tg_num)); s_stopped_tgwdt_bmap |= BIT(tg_num); } } #endif #if SOC_SLEEP_SYSTIMER_STALL_WORKAROUND for (uint32_t counter_id = 0; counter_id < SYSTIMER_LL_COUNTER_NUM; ++counter_id) { systimer_ll_enable_counter(&SYSTIMER, counter_id, false); } #endif } } // Must be called from critical sections. static SLEEP_FN_ATTR void resume_timers(uint32_t sleep_flags) { if (!(sleep_flags & RTC_SLEEP_PD_XTAL)) { #if SOC_SLEEP_SYSTIMER_STALL_WORKAROUND for (uint32_t counter_id = 0; counter_id < SYSTIMER_LL_COUNTER_NUM; ++counter_id) { systimer_ll_enable_counter(&SYSTIMER, counter_id, true); } #endif #if SOC_SLEEP_TGWDT_STOP_WORKAROUND for (uint32_t tg_num = 0; tg_num < TIMG_LL_GET(INST_NUM); ++tg_num) { if (s_stopped_tgwdt_bmap & BIT(tg_num)) { mwdt_ll_write_protect_disable(TIMER_LL_GET_HW(tg_num)); mwdt_ll_enable(TIMER_LL_GET_HW(tg_num)); mwdt_ll_write_protect_enable(TIMER_LL_GET_HW(tg_num)); } } #endif } } /** * LP peripherals prepare XTAL, FOSC or other clocks as the clock source for sleep. */ static SLEEP_FN_ATTR void lp_periph_use_clk_sleep_prepare(uint32_t sleep_flags, bool deep_sleep) { #if SOC_PMU_SUPPORTED #if CONFIG_IDF_TARGET_ESP32P4 if (sleep_flags & RTC_SLEEP_LP_PERIPH_USE_XTAL) { /* Force the xtal clk pass lp clock gate */ _clk_gate_ll_xtal_to_lp_periph_en(true); } else { /* Set xtal lp clock gate controlled by hardware fsm */ _clk_gate_ll_xtal_to_lp_periph_en(false); } #endif #if SOC_LP_PERIPHERALS_SUPPORTED if (sleep_flags & RTC_SLEEP_LP_PERIPH_USE_RC_FAST) { /* Force the rtc_fast clk pass lp clock gate */ _clk_gate_ll_rtc_fast_to_lp_periph_en(true); } else { /* Lp clock gate of rtc_fast clk is decided by FSM(PMU state)*/ _clk_gate_ll_rtc_fast_to_lp_periph_en(false); } #endif #endif } /** * These save-restore workaround should be moved to lower layer */ static SLEEP_FN_ATTR void misc_modules_sleep_prepare(uint32_t sleep_flags, bool deep_sleep) { if (deep_sleep){ for (int n = 0; n < MAX_DSLP_HOOKS; n++) { if (s_dslp_cb[n] != NULL) { s_dslp_cb[n](); } } } else { #if SOC_USB_SERIAL_JTAG_SUPPORTED && !SOC_USB_SERIAL_JTAG_SUPPORT_LIGHT_SLEEP // Only avoid USJ pad leakage here, USB OTG pad leakage is prevented through USB Host driver. sleep_console_usj_pad_backup_and_disable(); #endif #if SOC_USB_OTG_SUPPORTED && SOC_PM_SUPPORT_CNNT_PD && SOC_USB_OTG_NEED_SOFTWARE_SUSPEND_BEFORE_SLEEP if (!(sleep_flags & PMU_SLEEP_PD_CNNT)) { sleep_usb_otg_phy_backup_and_disable(); } #endif #if CONFIG_MAC_BB_PD mac_bb_power_down_cb_execute(); #endif #if CONFIG_IDF_TARGET_ESP32 esp_sleep_gpio_pupd_config_workaround_apply(); #endif #if CONFIG_PM_POWER_DOWN_CPU_IN_LIGHT_SLEEP || CONFIG_PM_POWER_DOWN_PERIPHERAL_IN_LIGHT_SLEEP #if SOC_PM_CPU_RETENTION_BY_RTCCNTL // Inside sleep_enable_cpu_retention, it will writeback the cache if the tag memory is power down with the CPU. sleep_enable_cpu_retention(); #elif SOC_PM_CPU_RETENTION_BY_SW && CONFIG_SPIRAM #if SOC_CACHE_INTERNAL_MEM_VIA_L1CACHE // For chips with SOC_CACHE_INTERNAL_MEM_VIA_L1CACHE, if writeback is performed here, // L1 dcache will still be dirty later, since the current function stack is in L2 MEM, // so the writeback will be postponed to pmu_sleep. #else // When SPIRAM is using, we need to writeback all dirty cache data to protect SPIRAM data conherence // since the cache tag memory will be powered down with CPU. sleep_cache_safe_writeback(sleep_flags); #endif // SOC_CACHE_INTERNAL_MEM_VIA_L1CACHE #endif // SOC_PM_CPU_RETENTION_BY_SW && CONFIG_SPIRAM #endif // CONFIG_PM_POWER_DOWN_CPU_IN_LIGHT_SLEEP || CONFIG_PM_POWER_DOWN_PERIPHERAL_IN_LIGHT_SLEEP #if ADC_LL_ANA_CALI_REG_PD_WORKAROUND adc_hal_i2c_saradc_reg_backup(); #endif #if SOC_TEMPERATURE_SENSOR_SUPPORT_SLEEP_RETENTION temperature_sensor_hal_i2c_saradc_reg_backup(); #endif } #if CONFIG_ESP_ENABLE_PVT && SOC_PVT_EN_WITH_SLEEP pvt_func_enable(false); #endif #if SOC_ADC_SUPPORTED if (s_sleep_sub_mode_ref_cnt[ESP_SLEEP_USE_ADC_TSEN_MONITOR_MODE] == 0) { // TODO: IDF-7370 sar_periph_ctrl_power_disable(); } #endif suspend_timers(sleep_flags); } /** * These save-restore workaround should be moved to lower layer */ static SLEEP_FN_ATTR void misc_modules_wake_prepare(uint32_t sleep_flags) { resume_timers(sleep_flags); #if CONFIG_ESP_ENABLE_PVT && SOC_PVT_EN_WITH_SLEEP if ((sleep_flags & PMU_SLEEP_PD_CPU) && !(sleep_flags & PMU_SLEEP_PD_TOP)) { /* If TOP is powered down, configuration in pvt_auto_dbias_init was already restored in wakeup retention*/ pvt_auto_dbias_init(); } pvt_func_enable(true); #endif #if CONFIG_PM_POWER_DOWN_PERIPHERAL_IN_LIGHT_SLEEP && SOC_WDT_SUPPORTED if (sleep_flags & PMU_SLEEP_PD_TOP) { // There is no driver to manage the flashboot watchdog, and it is definitely be in off state when // the system is running, after waking up from pd_top sleep, shut it down by software here. wdt_hal_context_t mwdt_ctx = {.inst = WDT_MWDT0, .mwdt_dev = &TIMERG0}; wdt_hal_write_protect_disable(&mwdt_ctx); wdt_hal_set_flashboot_en(&mwdt_ctx, false); wdt_hal_write_protect_enable(&mwdt_ctx); } #endif #if SOC_USB_SERIAL_JTAG_SUPPORTED && !SOC_USB_SERIAL_JTAG_SUPPORT_LIGHT_SLEEP sleep_console_usj_pad_restore(); #endif #if SOC_USB_OTG_SUPPORTED && SOC_PM_SUPPORT_CNNT_PD && SOC_USB_OTG_NEED_SOFTWARE_SUSPEND_BEFORE_SLEEP if (!(sleep_flags & PMU_SLEEP_PD_CNNT)) { sleep_usb_otg_phy_restore(); } #endif #if SOC_ADC_SUPPORTED if (s_sleep_sub_mode_ref_cnt[ESP_SLEEP_USE_ADC_TSEN_MONITOR_MODE] == 0) { sar_periph_ctrl_power_enable(); } #endif #if CONFIG_PM_POWER_DOWN_CPU_IN_LIGHT_SLEEP && SOC_PM_CPU_RETENTION_BY_RTCCNTL sleep_disable_cpu_retention(); #endif #if CONFIG_IDF_TARGET_ESP32 esp_sleep_gpio_pupd_config_workaround_unapply(); #endif #if CONFIG_MAC_BB_PD mac_bb_power_up_cb_execute(); #endif #if ADC_LL_ANA_CALI_REG_PD_WORKAROUND adc_hal_i2c_saradc_reg_restore(); #endif #if SOC_TEMPERATURE_SENSOR_SUPPORT_SLEEP_RETENTION temperature_sensor_hal_i2c_saradc_reg_restore(); #endif #if RNG_LL_NEEDS_RESET_WHEN_WAKEUP if (sleep_flags & PMU_SLEEP_PD_LP_PERIPH) { // Re-enable the RNG module. rng_ll_reset(); rng_ll_enable(); } #endif } /** * RTC WDT prepare for using during sleep */ static SLEEP_FN_ATTR void sleep_rtc_wdt_prepare(bool enable) { #if SOC_RTC_WDT_SUPPORTED wdt_hal_context_t rtc_wdt_ctx = RWDT_HAL_CONTEXT_DEFAULT(); if (enable) { wdt_hal_init(&rtc_wdt_ctx, WDT_RWDT, 0, false); // Use default timeout for sleep monitoring uint32_t rtc_wdt_timeout_required_cycles = (uint32_t)(1000ULL * rtc_clk_slow_freq_get_hz() / 1000ULL); #if CONFIG_ESP_SLEEP_ENABLE_RTC_WDT_IN_SLEEP if (s_config.wakeup_triggers & RTC_TIMER_TRIG_EN) { uint32_t rtc_wdt_timeout = get_sleep_rtc_wdt_timeout(s_config.sleep_duration); rtc_wdt_timeout_required_cycles = calc_sleep_slow_clk_required_cycles(rtc_wdt_timeout, s_config.rtc_clk_cal_period); } #endif wdt_hal_write_protect_disable(&rtc_wdt_ctx); wdt_hal_config_stage(&rtc_wdt_ctx, WDT_STAGE0, rtc_wdt_timeout_required_cycles, WDT_STAGE_ACTION_RESET_RTC); wdt_hal_enable(&rtc_wdt_ctx); wdt_hal_write_protect_enable(&rtc_wdt_ctx); } else { wdt_hal_write_protect_disable(&rtc_wdt_ctx); wdt_hal_disable(&rtc_wdt_ctx); wdt_hal_write_protect_enable(&rtc_wdt_ctx); } #else (void)enable; #endif /* SOC_RTC_WDT_SUPPORTED */ } static SLEEP_FN_ATTR void sleep_low_power_clock_calibration(bool is_dslp) { // Calibrate rtc slow clock #ifdef CONFIG_ESP_SYSTEM_RTC_EXT_XTAL if (rtc_clk_slow_src_get() == SOC_RTC_SLOW_CLK_SRC_XTAL32K) { uint64_t time_per_us = 1000000ULL; s_config.rtc_clk_cal_period = (time_per_us << RTC_CLK_CAL_FRACT) / rtc_clk_slow_freq_get_hz(); } else { // If the external 32 kHz XTAL does not exist, use the internal 150 kHz RC oscillator // as the RTC slow clock source. s_config.rtc_clk_cal_period = rtc_clk_cal(CLK_CAL_RTC_SLOW, RTC_CLK_SRC_CAL_CYCLES); esp_clk_slowclk_cal_set(s_config.rtc_clk_cal_period); } #elif CONFIG_RTC_CLK_SRC_INT_RC && CONFIG_IDF_TARGET_ESP32S2 s_config.rtc_clk_cal_period = rtc_clk_cal_cycling(CLK_CAL_RTC_SLOW, RTC_CLK_SRC_CAL_CYCLES); esp_clk_slowclk_cal_set(s_config.rtc_clk_cal_period); #else #if CONFIG_PM_ENABLE if ((s_lightsleep_cnt % CONFIG_PM_LIGHTSLEEP_RTC_OSC_CAL_INTERVAL == 0) || is_dslp) #endif { s_config.rtc_clk_cal_period = rtc_clk_cal(CLK_CAL_RTC_SLOW, RTC_CLK_SRC_CAL_CYCLES); esp_clk_slowclk_cal_set(s_config.rtc_clk_cal_period); } #endif // Calibrate rtc fast clock, only PMU supported chips sleep process is needed. #if SOC_PMU_SUPPORTED #if CONFIG_PM_ENABLE if ((s_lightsleep_cnt % CONFIG_PM_LIGHTSLEEP_RTC_OSC_CAL_INTERVAL == 0) || is_dslp) #endif { s_config.fast_clk_cal_period = rtc_clk_cal(CLK_CAL_RC_FAST, FAST_CLK_SRC_CAL_CYCLES); } #endif } static uint32_t call_rtc_sleep_start(uint32_t reject_triggers, uint32_t lslp_mem_inf_fpu, bool dslp); #if SOC_PMU_SUPPORTED static esp_err_t FORCE_IRAM_ATTR esp_sleep_start_safe(uint32_t sleep_flags, uint32_t reject_triggers, bool deep_sleep, pmu_sleep_config_t *config) #else static esp_err_t FORCE_IRAM_ATTR esp_sleep_start_safe(uint32_t sleep_flags, uint32_t reject_triggers, bool deep_sleep, rtc_sleep_config_t *config) #endif { esp_err_t result = ESP_OK; #if CONFIG_ESP_SLEEP_DEBUG if (s_sleep_ctx != NULL) { s_sleep_ctx->wakeup_triggers = s_config.wakeup_triggers; } #endif if (deep_sleep) { esp_sleep_gpio_clear_dedicated_ctrl(); #if !SOC_GPIO_SUPPORT_HOLD_SINGLE_IO_IN_DSLP || SOC_GPIO_NEED_SOFT_ISOLATE_DURING_PD esp_sleep_isolate_digital_gpio(true); #endif #if CONFIG_IDF_TARGET_ESP32P4 && CONFIG_ESP_SLEEP_SET_FLASH_DPD if ((sleep_flags & RTC_SLEEP_FLASH_DPD) && (!ESP_CHIP_REV_ABOVE(efuse_hal_chip_revision(), 300))) { /* Switch Flash from standby mode to deep powerdown mode */ /* During bootloader phase following wakeup from deepsleep, flash will exit dpd mode */ spi_flash_enable_deep_power_down_mode(true, false); } #endif #if ESP_ROM_SUPPORT_DEEP_SLEEP_WAKEUP_STUB && SOC_DEEP_SLEEP_SUPPORTED #if SOC_PM_SUPPORT_DEEPSLEEP_CHECK_STUB_ONLY esp_set_deep_sleep_wake_stub_default_entry(); #elif !CONFIG_ESP_SYSTEM_ALLOW_RTC_FAST_MEM_AS_HEAP && SOC_RTC_FAST_MEM_SUPPORTED /* If not possible stack is in RTC FAST memory, use the ROM function to calculate the CRC and save ~140 bytes IRAM */ set_rtc_memory_crc(); #endif // SOC_PM_SUPPORT_DEEPSLEEP_CHECK_STUB_ONLY #endif #if SOC_DCDC_SUPPORTED uint64_t ldo_increased_us = rtc_time_slowclk_to_us(rtc_time_get() - s_config.rtc_ticks_at_ldo_prepare, s_config.rtc_clk_cal_period); if (ldo_increased_us < LDO_POWER_TAKEOVER_PREPARATION_TIME_US) { esp_rom_delay_us(LDO_POWER_TAKEOVER_PREPARATION_TIME_US - ldo_increased_us); } pmu_sleep_shutdown_dcdc(); #endif // Enter Deep Sleep #if !ESP_ROM_SUPPORT_DEEP_SLEEP_WAKEUP_STUB || SOC_PM_SUPPORT_DEEPSLEEP_CHECK_STUB_ONLY || !CONFIG_ESP_SYSTEM_ALLOW_RTC_FAST_MEM_AS_HEAP #if SOC_PMU_SUPPORTED result = call_rtc_sleep_start(reject_triggers, config->power.hp_sys.dig_power.mem_dslp, deep_sleep); #else result = call_rtc_sleep_start(reject_triggers, config->lslp_mem_inf_fpu, deep_sleep); #endif #else /* Otherwise, need to call the dedicated soc function for this */ result = rtc_deep_sleep_start(s_config.wakeup_triggers, reject_triggers); #endif } else { #if SOC_GPIO_NEED_SOFT_ISOLATE_DURING_PD if (sleep_flags & RTC_SLEEP_PD_DIG) { esp_sleep_isolate_digital_gpio(false); } #endif /* Cache Suspend 1: will wait cache idle in cache suspend */ sleep_cache_suspend(); if (!(sleep_flags & RTC_SLEEP_PD_VDDSDIO)) { #if CONFIG_ESP_SLEEP_SET_FLASH_DPD if (sleep_flags & RTC_SLEEP_FLASH_DPD) { /* Switch Flash from standby mode to deep powerdown mode */ spi_flash_enable_deep_power_down_mode(true, false); } #endif #if CONFIG_PM_POWER_DOWN_PERIPHERAL_IN_LIGHT_SLEEP /* On esp32c6, only the lp_aon pad hold function can only hold the GPIO state in the active mode. In order to avoid the leakage of the SPI cs pin, hold it here */ if(sleep_flags & PMU_SLEEP_PD_TOP) { #if SOC_MSPI_HAS_INDEPENT_IOMUX mspi_ll_hold_all_flash_pins(); #else // !SOC_MSPI_HAS_INDEPENT_IOMUX #if CONFIG_ESP_SLEEP_FLASH_LEAKAGE_WORKAROUND /* Cache suspend also means SPI bus IDLE, then we can hold SPI CS pin safely */ gpio_ll_hold_en(&GPIO, MSPI_IOMUX_PIN_NUM_CS0); #endif #if CONFIG_ESP_SLEEP_PSRAM_LEAKAGE_WORKAROUND && CONFIG_SPIRAM /* Cache suspend also means SPI bus IDLE, then we can hold SPI CS pin safely */ gpio_ll_hold_en(&GPIO, MSPI_IOMUX_PIN_NUM_CS1); #endif #if SOC_GPIO_NEED_SOFT_ISOLATE_DURING_PD esp_sleep_isolate_mspi_gpio(); #endif #endif // !SOC_MSPI_HAS_INDEPENT_IOMUX } #endif } #if CONFIG_PM_POWER_DOWN_PERIPHERAL_IN_LIGHT_SLEEP if (sleep_flags & PMU_SLEEP_PD_TOP) { #if CONFIG_IDF_TARGET_ESP32P4 && (CONFIG_ESP_REV_MIN_FULL == 300) sleep_retention_do_extra_retention(true); #endif } #endif #if SOC_PMU_SUPPORTED #if SOC_PM_CPU_RETENTION_BY_SW && CONFIG_PM_ESP_SLEEP_POWER_DOWN_CPU esp_sleep_execute_event_callbacks(SLEEP_EVENT_HW_GOTO_SLEEP, (void *)0); if (sleep_flags & (PMU_SLEEP_PD_CPU | PMU_SLEEP_PD_TOP)) { result = esp_sleep_cpu_retention(pmu_sleep_start, s_config.wakeup_triggers, reject_triggers, config->power.hp_sys.dig_power.mem_dslp, deep_sleep); } else #endif { #if !CONFIG_FREERTOS_UNICORE && CONFIG_PM_ESP_SLEEP_POWER_DOWN_CPU && SOC_PM_CPU_RETENTION_BY_SW // Skip smp retention if CPU power domain power-down is not allowed esp_sleep_cpu_skip_retention(); #endif result = call_rtc_sleep_start(reject_triggers, config->power.hp_sys.dig_power.mem_dslp, deep_sleep); } esp_sleep_execute_event_callbacks(SLEEP_EVENT_HW_EXIT_SLEEP, (void *)0); #else result = call_rtc_sleep_start(reject_triggers, config->lslp_mem_inf_fpu, deep_sleep); #endif #if CONFIG_PM_POWER_DOWN_PERIPHERAL_IN_LIGHT_SLEEP if (sleep_flags & PMU_SLEEP_PD_TOP) { #if SOC_PM_RETENTION_SW_TRIGGER_REGDMA sleep_retention_do_system_retention(false); #endif #if CONFIG_P4_REV3_MSPI_CRASH_AFTER_POWER_UP_WORKAROUND sleep_flash_p4_rev3_workaround(); sleep_retention_do_extra_retention(false); #endif } #endif #if CONFIG_PM_POWER_DOWN_PERIPHERAL_IN_LIGHT_SLEEP /* Unhold the SPI CS pin */ if(!(sleep_flags & RTC_SLEEP_PD_VDDSDIO) && (sleep_flags & PMU_SLEEP_PD_TOP)) { #if SOC_MSPI_HAS_INDEPENT_IOMUX mspi_ll_unhold_all_flash_pins(); #else // !SOC_MSPI_HAS_INDEPENT_IOMUX #if CONFIG_ESP_SLEEP_FLASH_LEAKAGE_WORKAROUND gpio_ll_hold_dis(&GPIO, MSPI_IOMUX_PIN_NUM_CS0); #endif #if CONFIG_ESP_SLEEP_PSRAM_LEAKAGE_WORKAROUND && CONFIG_SPIRAM gpio_ll_hold_dis(&GPIO, MSPI_IOMUX_PIN_NUM_CS1); #endif #endif // !SOC_MSPI_HAS_INDEPENT_IOMUX } #endif #if SOC_GPIO_NEED_SOFT_ISOLATE_DURING_PD if (sleep_flags & RTC_SLEEP_PD_DIG) { esp_sleep_restore_isolated_digital_gpio(); } #endif #if CONFIG_ESP_SLEEP_SET_FLASH_DPD if (sleep_flags & RTC_SLEEP_FLASH_DPD) { //Release Flash out from deep powerdown mode spi_flash_enable_deep_power_down_mode(false, true); } #endif #if CONFIG_PM_SLP_SPIRAM_HALFSLEEP_ENABLED && (CONFIG_SPIRAM_XIP_FROM_PSRAM || !CONFIG_PM_SLP_IRAM_OPT) // Code outside of esp_sleep_start_safe may be linked to FLASH if CONFIG_PM_SLP_IRAM_OPT is false, // Code that accesses flash memory may cause cached PSRAM data to be replaced back into PSRAM before // it has fully resumed. And if CONFIG_SPIRAM_XIP_FROM_PSRAM is enabled, code in Flash will be copied // to PSRAM for execution. We need to wait here until PSRAM exits half-sleep before returning. esp_psram_impl_resume_from_halfsleep_mode(s_config.rtc_clk_cal_period); #endif /* Cache Resume 1: Resume cache for continue running*/ sleep_cache_resume(); } return result; } static esp_err_t SLEEP_FN_ATTR esp_sleep_start(uint32_t sleep_flags, esp_sleep_mode_t mode, bool allow_sleep_rejection, esp_sleep_extra_args_t *args) { // Stop UART output so that output is not lost due to APB frequency change. // For light sleep, suspend UART output — it will resume after wakeup. // For deep sleep, wait for the contents of UART FIFO to be sent. bool deep_sleep = (mode == ESP_SLEEP_MODE_DEEP_SLEEP); bool should_skip_sleep = false; int64_t sleep_duration = (int64_t) s_config.sleep_duration - (int64_t) s_config.sleep_time_adjustment; #if CONFIG_ESP_PHY_ENABLED && SOC_DEEP_SLEEP_SUPPORTED // Do deep-sleep PHY related callback, which need to be executed when the PLL clock is exists. // For light-sleep, PHY state is managed by the upper layer of the wifi/bt protocol stack. if (deep_sleep) { s_do_deep_sleep_phy_callback(); } #endif #if SOC_PM_RETENTION_SW_TRIGGER_REGDMA if (!deep_sleep && (sleep_flags & PMU_SLEEP_PD_TOP)) { sleep_retention_do_system_retention(true); } #endif #if SOC_PM_SUPPORT_EXT0_WAKEUP // Configure pins for external wakeup if (s_config.wakeup_triggers & RTC_EXT0_TRIG_EN) { ext0_wakeup_prepare(); } // for !(s_config.wakeup_triggers & RTC_EXT0_TRIG_EN), ext0 wakeup will be turned off in hardware in the real call to sleep #endif #if SOC_PM_SUPPORT_EXT1_WAKEUP if (s_config.wakeup_triggers & RTC_EXT1_TRIG_EN) { ext1_wakeup_prepare(); } // for !(s_config.wakeup_triggers & RTC_EXT1_TRIG_EN), ext1 wakeup will be turned off in hardware in the real call to sleep #endif #if SOC_GPIO_SUPPORT_HP_PERIPH_PD_SLEEP_WAKEUP if ((sleep_flags & RTC_SLEEP_PD_DIG) && (s_config.wakeup_triggers & RTC_GPIO_TRIG_EN)) { esp_sleep_gpio_wakeup_prepare_on_hp_periph_powerdown(); } #endif // Sleep UART prepare sleep_uart_prepare(sleep_flags, deep_sleep); #if !CONFIG_APP_BUILD_TYPE_PURE_RAM_APP uint32_t xtal_freq = rtc_clk_xtal_freq_get(); esp_clk_utils_mspi_speed_mode_sync_before_cpu_freq_switching(xtal_freq, xtal_freq); #endif // Save current frequency and switch to XTAL rtc_cpu_freq_config_t cpu_freq_config; rtc_clk_cpu_freq_get_config(&cpu_freq_config); rtc_clk_cpu_freq_set_xtal_for_sleep(); #if CONFIG_ULP_COPROC_ENABLED // Enable ULP wakeup #if CONFIG_ULP_COPROC_TYPE_FSM if (s_config.wakeup_triggers & RTC_ULP_TRIG_EN) { #ifdef CONFIG_IDF_TARGET_ESP32 rtc_hal_ulp_wakeup_enable(); #else rtc_hal_ulp_int_clear(); #endif } #endif #if CONFIG_ULP_COPROC_TYPE_RISCV if (s_config.wakeup_triggers & (RTC_COCPU_TRIG_EN | RTC_COCPU_TRAP_TRIG_EN)) { rtc_hal_ulp_int_clear(); } #endif #if CONFIG_ULP_COPROC_TYPE_LP_CORE if (s_config.wakeup_triggers & (RTC_LP_CORE_TRIG_EN | RTC_LP_CORE_TRAP_TRIG_EN)) { pmu_ll_hp_clear_sw_intr_status(&PMU); pmu_ll_hp_clear_lp_cpu_exc_intr_status(&PMU); } #endif #endif // CONFIG_ULP_COPROC_ENABLED misc_modules_sleep_prepare(sleep_flags, deep_sleep); #if SOC_TOUCH_SENSOR_SUPPORTED bool keep_rtc_power_on = false; if (deep_sleep) { if (s_config.wakeup_triggers & RTC_TOUCH_TRIG_EN) { touch_wakeup_prepare(); #if CONFIG_IDF_TARGET_ESP32S2 /* Workaround: In deep sleep, for ESP32S2, Power down the RTC_PERIPH will change the slope configuration of Touch sensor sleep pad. * The configuration change will change the reading of the sleep pad, which will cause the touch wake-up sensor to trigger falsely. */ keep_rtc_power_on = true; #endif } } else { /* In light sleep, the RTC_PERIPH power domain should be in the power-on state (Power on the touch circuit in light sleep), * otherwise the touch sensor FSM will be cleared, causing touch sensor false triggering. */ keep_rtc_power_on |= touch_ll_is_fsm_repeated_timer_enabled(); } /* Whether need to keep RTC_PERIPH power on eventually */ if (keep_rtc_power_on) { sleep_flags &= ~RTC_SLEEP_PD_RTC_PERIPH; } #endif #if SOC_VBAT_SUPPORTED if (deep_sleep && (s_config.wakeup_triggers & RTC_VBAT_UNDER_VOLT_TRIG_EN)) { vbat_under_volt_wakeup_prepare(); } #endif uint32_t reject_triggers = allow_sleep_rejection ? (s_config.wakeup_triggers & RTC_SLEEP_REJECT_MASK) : 0; if (!deep_sleep) { /* Enable sleep reject for faster return from this function, * in case the wakeup is already triggered. */ reject_triggers |= sleep_modem_reject_triggers(); } #if CONFIG_ESP_SLEEP_DEBUG if (s_sleep_ctx != NULL) { s_sleep_ctx->sleep_flags = sleep_flags; } #endif /* Prepare for LP peripherals to select a clock source. */ lp_periph_use_clk_sleep_prepare(sleep_flags, deep_sleep); // Enter sleep esp_err_t result; #if SOC_PMU_SUPPORTED #if SOC_DCDC_SUPPORTED if (deep_sleep) { s_config.rtc_ticks_at_ldo_prepare = rtc_time_get(); pmu_sleep_increase_ldo_volt(); } #endif pmu_sleep_extra_args_t sleep_extra_args = { .sleep_flags = sleep_flags, .clk_flags = { args->clk_flags[0], args->clk_flags[1] }, .adjustment = s_config.sleep_time_adjustment, .slowclk_src = rtc_clk_slow_src_get(), .slowclk_period = s_config.rtc_clk_cal_period, .fastclk_period = s_config.fast_clk_cal_period }; pmu_sleep_config_t config; pmu_sleep_init(pmu_sleep_config_default(&config, &sleep_extra_args, deep_sleep), deep_sleep); #else (void) args; rtc_sleep_config_t config; rtc_sleep_get_default_config(sleep_flags, &config); rtc_sleep_init(config); // Set state machine time for light sleep rtc_sleep_low_init(s_config.rtc_clk_cal_period, deep_sleep); #endif // Configure timer wakeup bool timer_wakeup_armed = false; if (!should_skip_sleep && (s_config.wakeup_triggers & RTC_TIMER_TRIG_EN)) { if (timer_wakeup_prepare(sleep_duration) != ESP_OK) { should_skip_sleep = allow_sleep_rejection ? true : false; } else { timer_wakeup_armed = true; } } if (should_skip_sleep) { result = ESP_ERR_SLEEP_REJECT; #if CONFIG_PM_ESP_SLEEP_POWER_DOWN_CPU && !CONFIG_FREERTOS_UNICORE && SOC_PM_CPU_RETENTION_BY_SW esp_sleep_cpu_skip_retention(); #endif } else { result = esp_sleep_start_safe(sleep_flags, reject_triggers, deep_sleep, &config); } if (timer_wakeup_armed) { /* Disarm leftover comparator after non-timer wakeups so it cannot fire into other LP/RTC timer users. */ rtc_timer_hal_clear_wakeup_time(0); } #if CONFIG_ESP_SLEEP_CACHE_SAFE_ASSERTION if (sleep_flags & RTC_SLEEP_PD_VDDSDIO) { /* Cache Suspend 2: If previous sleep powerdowned the flash, suspend cache here so that the access to flash before flash ready can be explicitly exposed. */ sleep_cache_suspend(); } #endif // Restore CPU frequency (Will fallback to rtc_clk_cpu_freq_set_config if PLL source is not configured.) rtc_clk_cpu_freq_set_config_fast(&cpu_freq_config); #if SOC_PM_SUPPORT_PMU_MODEM_STATE && !SOC_PM_BBPLL_PD_IN_MODEM_STATE && !SOC_PM_MODEM_STATE_USE_XTAL if (pmu_sleep_pll_already_enabled()) { rtc_clk_modem_pll_lock_release(); } #endif esp_sleep_execute_event_callbacks(SLEEP_EVENT_SW_CLK_READY, (void *)0); if (!deep_sleep) { if (result == ESP_OK) { s_config.ccount_ticks_record = esp_cpu_get_cycle_count(); } misc_modules_wake_prepare(sleep_flags); } #if !CONFIG_APP_BUILD_TYPE_PURE_RAM_APP esp_clk_utils_mspi_speed_mode_sync_after_cpu_freq_switching(cpu_freq_config.source_freq_mhz, cpu_freq_config.freq_mhz); #endif // re-enable UART output sleep_uart_resume(); return result ? ESP_ERR_SLEEP_REJECT : ESP_OK; } static uint32_t FORCE_IRAM_ATTR call_rtc_sleep_start(uint32_t reject_triggers, uint32_t lslp_mem_inf_fpu, bool dslp) { #ifdef CONFIG_IDF_TARGET_ESP32 return rtc_sleep_start(s_config.wakeup_triggers, reject_triggers); #elif SOC_PMU_SUPPORTED return pmu_sleep_start(s_config.wakeup_triggers, reject_triggers, lslp_mem_inf_fpu, dslp); #else return rtc_sleep_start(s_config.wakeup_triggers, reject_triggers, lslp_mem_inf_fpu); #endif } static esp_err_t FORCE_IRAM_ATTR deep_sleep_start(bool allow_sleep_rejection) { #if SOC_VBAT_SUPPORTED if (s_sleep_sub_mode_ref_cnt[ESP_SLEEP_VBAT_POWER_DEEPSLEEP_MODE]) { esp_vbat_state_t battery_state = esp_vbat_get_battery_state(); if (battery_state == ESP_VBAT_STATE_CHARGING) { ESP_LOGW(TAG, "Battery is charging, should wait until charging is complete before entering deep sleep!"); } else if (battery_state == ESP_VBAT_STATE_LOWBATTERY) { ESP_LOGE(TAG, "Battery is in low battery state, chip may lose power during deepsleep!"); } if ((battery_state != ESP_VBAT_STATE_NORMAL) && allow_sleep_rejection) { return ESP_ERR_INVALID_STATE; } } #endif #if CONFIG_SPIRAM && CONFIG_FREERTOS_TASK_CREATE_ALLOW_EXT_MEM if (!esp_task_stack_is_sane_cache_disabled()) { ESP_LOGE(TAG, "Deep sleep requests are not allowed from tasks with stacks in PSRAM"); if (allow_sleep_rejection) { return ESP_ERR_NOT_ALLOWED; } } #endif #if CONFIG_IDF_TARGET_ESP32S2 && CONFIG_ESP_BROWNOUT_DET /* Due to hardware limitations, on S2 the brownout detector sometimes trigger during deep sleep to circumvent this we disable the brownout detector before sleeping */ esp_brownout_disable(); #endif //CONFIG_IDF_TARGET_ESP32S2 && CONFIG_ESP_BROWNOUT_DET esp_sync_timekeeping_timers(); #if CONFIG_ESP_INT_WDT && CONFIG_ESP32_ECO3_CACHE_LOCK_FIX // The other core will be stalled by high-priority interrupt and spins on variables in internal RAM, // which naturally avoids cache livelock, so the 20ms livelock workaround timeout is not needed. // Must do it before stalling another core, since s_iwdt_configure_lock spinlock is acquired in // esp_int_wdt_livelock_workaround, which may cause deadlock. esp_int_wdt_livelock_workaround(false); #endif /* Disable interrupts and stall another core in case another task writes * to RTC memory while we calculate RTC memory CRC. */ esp_sleep_enter_critical(); #if CONFIG_FREERTOS_PORT_THREAD_SAFE_CLAIM esp_ipc_isr_stall_other_cpu(); #else /* Retry with the lock held on success. Drop the lock on failure so the other * CPU can leave its critical section (and to avoid deadlock on s_config.lock). */ while (esp_ipc_isr_stall_other_cpu_safe() != ESP_OK) { esp_os_exit_critical(&s_config.lock); esp_rom_delay_us(portTICK_PERIOD_MS * 1000 / 10); esp_os_enter_critical(&s_config.lock); } #endif esp_ipc_isr_stall_pause(); /* Another core is stalled and interrupts are disabled, so we can safely claim the thread-safe critical section to avoid deadlocks and fastup the sleep process. */ #if CONFIG_FREERTOS_PORT_THREAD_SAFE_CLAIM xPortThreadSafeClaim(); #endif // record current RTC time s_config.rtc_ticks_at_sleep_start = rtc_time_get(); #if ESP_ROM_SUPPORT_DEEP_SLEEP_WAKEUP_STUB // Configure wake stub if (esp_get_deep_sleep_wake_stub() == NULL) { esp_set_deep_sleep_wake_stub(esp_wake_deep_sleep); } #endif // ESP_ROM_SUPPORT_DEEP_SLEEP_WAKEUP_STUB esp_sleep_extra_args_t extra_args = { 0 }; // Decide which power domains can be powered down uint32_t pd_flags = get_power_down_flags(); // Re-calibrate the RTC clock sleep_low_power_clock_calibration(true); // Correct the sleep time s_config.sleep_time_adjustment = DEEP_SLEEP_TIME_OVERHEAD_US; // Safety net: enable WDT in case exit from deep sleep fails #if SOC_RTC_WDT_SUPPORTED wdt_hal_context_t rtc_wdt_ctx = RWDT_HAL_CONTEXT_DEFAULT(); bool rtc_wdt_was_enabled = wdt_hal_is_enabled(&rtc_wdt_ctx); // If WDT was enabled in the user code, then do not change it here. if (!rtc_wdt_was_enabled) { sleep_rtc_wdt_prepare(true); } else { ESP_EARLY_LOGW(TAG, "RTC WDT is enabled and will not be reconfigured again!"); } #endif /* SOC_RTC_WDT_SUPPORTED */ #if SOC_PMU_SUPPORTED uint32_t force_pd_flags = PMU_SLEEP_PD_TOP | PMU_SLEEP_PD_VDDSDIO | PMU_SLEEP_PD_MODEM | PMU_SLEEP_PD_HP_PERIPH \ | PMU_SLEEP_PD_CPU | PMU_SLEEP_PD_MEM | PMU_SLEEP_PD_XTAL; #if SOC_PM_SUPPORT_HP_AON_PD force_pd_flags |= PMU_SLEEP_PD_HP_AON; #endif #if SOC_PM_SUPPORT_CNNT_PD force_pd_flags |= PMU_SLEEP_PD_CNNT; #endif #else uint32_t force_pd_flags = RTC_SLEEP_PD_DIG | RTC_SLEEP_PD_VDDSDIO | RTC_SLEEP_PD_INT_8M | RTC_SLEEP_PD_XTAL; #endif /** * If all wireless modules share one power domain, we name this power domain "modem". * If wireless modules have their own power domain, we give these power domains separate * names. */ #if SOC_PM_SUPPORT_MODEM_PD force_pd_flags |= RTC_SLEEP_PD_MODEM; #endif #if SOC_PM_SUPPORT_WIFI_PD force_pd_flags |= RTC_SLEEP_PD_WIFI; #endif #if SOC_PM_SUPPORT_BT_PD force_pd_flags |= RTC_SLEEP_PD_BT; #endif // Append flags to indicate the sleep sub-mode and modify the pd_flags according to sub-mode attributes. uint32_t sleep_flags = get_sleep_flags(force_pd_flags | pd_flags, true); // Enter sleep esp_err_t err = ESP_OK; if (esp_sleep_start(sleep_flags, ESP_SLEEP_MODE_DEEP_SLEEP, allow_sleep_rejection, &extra_args) == ESP_ERR_SLEEP_REJECT) { err = ESP_ERR_SLEEP_REJECT; #if CONFIG_ESP_SLEEP_CACHE_SAFE_ASSERTION /* Cache Resume 2: if CONFIG_ESP_SLEEP_CACHE_SAFE_ASSERTION is enabled, cache has been suspended in esp_sleep_start */ sleep_cache_resume(); #endif ESP_EARLY_LOGE(TAG, "Deep sleep request is rejected"); } else { // Because RTC is in a slower clock domain than the CPU, it // can take several CPU cycles for the sleep mode to start. ESP_INFINITE_LOOP(); } // Never returns here, except that the sleep is rejected. #if SOC_RTC_WDT_SUPPORTED if (!rtc_wdt_was_enabled) { sleep_rtc_wdt_prepare(false); } #endif /* SOC_RTC_WDT_SUPPORTED */ /* Restore port critical before unstalling other CPU */ #if CONFIG_FREERTOS_PORT_THREAD_SAFE_CLAIM xPortThreadSafeDisclaim(); #endif esp_ipc_isr_stall_resume(); esp_ipc_isr_release_other_cpu(); #if CONFIG_ESP_INT_WDT && CONFIG_ESP32_ECO3_CACHE_LOCK_FIX // Configure WDT to use livelock workaround timeout after releasing other CPU esp_int_wdt_livelock_workaround(true); #endif esp_sleep_exit_critical(); #if CONFIG_IDF_TARGET_ESP32S2 && CONFIG_ESP_BROWNOUT_DET /* Brownout was disabled before attempting deep sleep; restore it after rejection. */ esp_brownout_init(); #endif //CONFIG_IDF_TARGET_ESP32S2 && CONFIG_ESP_BROWNOUT_DET return err; } void FORCE_IRAM_ATTR esp_deep_sleep_start(void) { bool allow_sleep_rejection = true; deep_sleep_start(!allow_sleep_rejection); // Never returns here abort(); } esp_err_t FORCE_IRAM_ATTR esp_deep_sleep_try_to_start(void) { bool allow_sleep_rejection = true; return deep_sleep_start(allow_sleep_rejection); } /** * Helper function which handles entry to and exit from light sleep * Placed into IRAM as flash may need some time to be powered on. */ static esp_err_t esp_light_sleep_inner(uint32_t sleep_flags, uint32_t flash_enable_time_us, esp_sleep_extra_args_t *args) __attribute__((noinline)); static SLEEP_FN_ATTR esp_err_t esp_light_sleep_inner(uint32_t sleep_flags, uint32_t flash_enable_time_us, esp_sleep_extra_args_t *args) { #if SOC_CONFIGURABLE_VDDSDIO_SUPPORTED rtc_vddsdio_config_t vddsdio_config = rtc_vddsdio_get_config(); #endif // Enter sleep esp_err_t reject = esp_sleep_start(sleep_flags, ESP_SLEEP_MODE_LIGHT_SLEEP, true, args); #if SOC_CONFIGURABLE_VDDSDIO_SUPPORTED // If VDDSDIO regulator was controlled by RTC registers before sleep, // restore the configuration. if (vddsdio_config.force) { rtc_vddsdio_set_config(vddsdio_config); } #endif // If SPI flash was powered down, wait for it to become ready if (!reject && (sleep_flags & RTC_SLEEP_PD_VDDSDIO)) { #if SOC_PM_SUPPORT_TOP_PD if (sleep_flags & PMU_SLEEP_PD_TOP) { uint32_t flash_ready_hw_waited_time_us = pmu_sleep_get_wakup_retention_cost(); uint32_t flash_ready_sw_waited_time_us = (esp_cpu_get_cycle_count() - s_config.ccount_ticks_record) / (esp_clk_cpu_freq() / MHZ); uint32_t flash_ready_waited_time_us = flash_ready_hw_waited_time_us + flash_ready_sw_waited_time_us; if (flash_enable_time_us > flash_ready_waited_time_us){ flash_enable_time_us -= flash_ready_waited_time_us; } else { flash_enable_time_us = 0; } } #endif // Wait for the flash chip to start up esp_rom_delay_us(flash_enable_time_us); } else { } #if CONFIG_ESP_SLEEP_CACHE_SAFE_ASSERTION if (sleep_flags & RTC_SLEEP_PD_VDDSDIO) { /* Cache Resume 2: flash is ready now, we can resume the cache and access flash safely after */ sleep_cache_resume(); } #endif return reject; } #if !CONFIG_FREERTOS_UNICORE static SLEEP_FN_ATTR esp_err_t sleep_smp_cpu_sleep_prepare(void) { #if CONFIG_PM_ESP_SLEEP_POWER_DOWN_CPU && SOC_PM_CPU_RETENTION_BY_SW sleep_cpu_retention_start(); #endif #if CONFIG_ESP_INT_WDT && CONFIG_ESP32_ECO3_CACHE_LOCK_FIX // The other core will be stalled by high-priority interrupt and spins on variables in internal RAM, // which naturally avoids cache livelock, so the 20ms livelock workaround timeout is not needed. esp_int_wdt_livelock_workaround(false); #endif #if CONFIG_FREERTOS_PORT_THREAD_SAFE_CLAIM esp_err_t ipc_isr_err = ESP_OK; esp_ipc_isr_stall_other_cpu(); #else /* Dual-core PM_ENABLE selects THREAD_SAFE_CLAIM, so auto light sleep always takes * the blocking path above. This safe-stall fallback is for non-PM callers of * esp_light_sleep_start(): reject and let the upper layer decide whether to retry. */ esp_err_t ipc_isr_err = esp_ipc_isr_stall_other_cpu_safe(); #endif if (ipc_isr_err == ESP_OK) { #if CONFIG_PM_ESP_SLEEP_POWER_DOWN_CPU && SOC_PM_CPU_RETENTION_BY_SW // Run CPU retention in the context of the other safely stalled CPU. esp_ipc_isr_call(sleep_cpu_retention_execute, NULL); #endif esp_ipc_isr_stall_pause(); } else { #if CONFIG_PM_ESP_SLEEP_POWER_DOWN_CPU && SOC_PM_CPU_RETENTION_BY_SW sleep_cpu_retention_finish(); #endif #if CONFIG_ESP_INT_WDT && CONFIG_ESP32_ECO3_CACHE_LOCK_FIX // Configure WDT to use livelock workaround timeout after releasing other CPU esp_int_wdt_livelock_workaround(true); #endif return ESP_ERR_SLEEP_REJECT; } return ESP_OK; } static SLEEP_FN_ATTR void sleep_smp_cpu_wakeup_prepare(void) { esp_ipc_isr_stall_resume(); #if CONFIG_PM_ESP_SLEEP_POWER_DOWN_CPU && SOC_PM_CPU_RETENTION_BY_SW sleep_cpu_retention_finish(); #endif esp_ipc_isr_release_other_cpu(); #if CONFIG_ESP_INT_WDT && CONFIG_ESP32_ECO3_CACHE_LOCK_FIX // Configure WDT to use livelock workaround timeout after releasing other CPU esp_int_wdt_livelock_workaround(true); #endif } #endif // !CONFIG_FREERTOS_UNICORE /** * vddsdio is used for power supply of spi flash * * pd flash via menuconfig | pd flash via `esp_sleep_pd_config` | result * --------------------------------------------------------------------------------------------------- * 0 | 0 | no pd flash * x | 1 | pd flash with relaxed conditions(force_pd) * 1 | 0 | pd flash with strict conditions(safe_pd) */ FORCE_INLINE_ATTR bool can_power_down_vddsdio(uint32_t sleep_flags, const uint32_t vddsdio_pd_sleep_duration) { bool force_pd = !(s_config.wakeup_triggers & RTC_TIMER_TRIG_EN) || (s_config.sleep_duration > vddsdio_pd_sleep_duration); bool safe_pd = (s_config.wakeup_triggers == RTC_TIMER_TRIG_EN) && (s_config.sleep_duration > vddsdio_pd_sleep_duration); return (sleep_flags & RTC_SLEEP_PD_VDDSDIO) ? force_pd : safe_pd; } esp_err_t esp_light_sleep_start(void) { s_config.ccount_ticks_record = esp_cpu_get_cycle_count(); esp_sleep_execute_event_callbacks(SLEEP_EVENT_SW_GOTO_SLEEP, (void *)0); esp_sleep_enter_critical(); s_config.rtc_ticks_at_sleep_start = rtc_time_get(); uint32_t ccount_at_sleep_start = esp_cpu_get_cycle_count(); esp_sleep_execute_event_callbacks(SLEEP_EVENT_HW_TIME_START, (void *)0); uint64_t high_res_time_at_start = esp_timer_get_time(); uint32_t sleep_time_overhead_in = (ccount_at_sleep_start - s_config.ccount_ticks_record) / (esp_clk_cpu_freq() / 1000000ULL); #if CONFIG_ESP_SLEEP_DEBUG if (s_sleep_ctx != NULL) { s_sleep_ctx->sleep_in_rtc_time_stamp = s_config.rtc_ticks_at_sleep_start; } #endif #if !CONFIG_FREERTOS_UNICORE if (sleep_smp_cpu_sleep_prepare() != ESP_OK) { esp_sleep_exit_critical(); return ESP_ERR_SLEEP_REJECT; } #endif /* Another core is stalled and interrupts are disabled, so we can safely claim the thread-safe critical section to avoid deadlocks and fastup the sleep process. */ #if CONFIG_FREERTOS_PORT_THREAD_SAFE_CLAIM xPortThreadSafeClaim(); #endif #if CONFIG_ESP_TASK_WDT_USE_ESP_TIMER /* If a task watchdog timer is running, we have to stop it. */ esp_err_t timerret = esp_task_wdt_stop(); #endif // CONFIG_ESP_TASK_WDT_USE_ESP_TIMER #if CONFIG_ESP_SLEEP_CACHE_SAFE_ASSERTION && CONFIG_PM_SLP_IRAM_OPT /* Cache Suspend 0: if CONFIG_PM_SLP_IRAM_OPT is enabled, suspend cache here so that the access to flash * during the sleep process can be explicitly exposed. * * If we use EXTx wakeup, we must put related codes in IRAM, but The `rtc_io_desc` table * consumes a significant amount of memory. For example, on the ESP32, its size is 1008 * bytes. Therefore, when EXTx wakeup is enabled, we do not perform cache access checks here */ uint32_t ignore_check_wakeup_triggers = 0; #if SOC_PM_SUPPORT_EXT0_WAKEUP ignore_check_wakeup_triggers |= RTC_EXT0_TRIG_EN; #endif #if SOC_PM_SUPPORT_EXT1_WAKEUP ignore_check_wakeup_triggers |= RTC_EXT1_TRIG_EN; #endif if (!(s_config.wakeup_triggers & ignore_check_wakeup_triggers)) { sleep_cache_suspend(); } #endif // Decide which power domains can be powered down uint32_t pd_flags = get_power_down_flags(); // Append flags to indicate the sleep sub-mode and modify the pd_flags according to sub-mode attributes. uint32_t sleep_flags = get_sleep_flags(pd_flags, false); esp_sleep_extra_args_t extra_args = { 0 }; #if SOC_PM_SUPPORT_PMU_CLK_ICG sleep_clock_icg_get_icg_flags(&extra_args.clk_flags[0], &extra_args.clk_flags[1]); #endif // Re-calibrate the RTC clock sleep_low_power_clock_calibration(false); uint32_t cur_cpu_freq = esp_clk_cpu_freq() / MHZ; uint32_t xtal_freq = rtc_clk_xtal_freq_get(); if (s_config.overhead_out_need_remeasure) { if (cur_cpu_freq < xtal_freq) { s_config.sleep_time_overhead_out = DEFAULT_SLEEP_OUT_OVERHEAD_US * xtal_freq / cur_cpu_freq; } else { s_config.sleep_time_overhead_out = DEFAULT_SLEEP_OUT_OVERHEAD_US; } } /* * Adjustment time consists of parts below: * 1. Hardware time waiting for internal 8M oscillate clock and XTAL; * 2. Hardware state switching time of the rtc main state machine; * 3. Code execution time when clock is not stable; * 4. Code execution time which can be measured; */ #if SOC_PMU_SUPPORTED int sleep_time_sw_adjustment = LIGHT_SLEEP_TIME_OVERHEAD_US + sleep_time_overhead_in + s_config.sleep_time_overhead_out; #if CONFIG_ESP_ENABLE_PVT && !SOC_PVT_EN_WITH_SLEEP /* PVT will only be enabled during the wake-up process if the CPU's clock source is PLL when the CPU goes to sleep. */ if ((cur_cpu_freq > xtal_freq) #if CONFIG_IDF_TARGET_ESP32C5 /* On esp32c5, CPU 40m is divided from PLL160/240M */ || (xtal_freq == SOC_XTAL_FREQ_48M && cur_cpu_freq == 40) #endif ) { sleep_time_sw_adjustment += PVT_REINIT_COST_US; } #endif int sleep_time_hw_adjustment = pmu_sleep_calculate_hw_wait_time(sleep_flags, rtc_clk_slow_src_get(), s_config.rtc_clk_cal_period, s_config.fast_clk_cal_period); s_config.sleep_time_adjustment = sleep_time_sw_adjustment + sleep_time_hw_adjustment; const int64_t min_sleep_duration_us = ((pmu_sleep_machine_constant_t *)PMU_instance()->mc)->lp.min_slp_time_us; #else // RTC_CNTL supported uint32_t rtc_cntl_xtl_buf_wait_slp_cycles = rtc_time_us_to_slowclk(RTC_CNTL_XTL_BUF_WAIT_SLP_US, s_config.rtc_clk_cal_period); s_config.sleep_time_adjustment = LIGHT_SLEEP_TIME_OVERHEAD_US + sleep_time_overhead_in + s_config.sleep_time_overhead_out + rtc_time_slowclk_to_us(rtc_cntl_xtl_buf_wait_slp_cycles + RTC_CNTL_CK8M_WAIT_SLP_CYCLES + RTC_CNTL_WAKEUP_DELAY_CYCLES, s_config.rtc_clk_cal_period); const int64_t min_sleep_duration_us = rtc_time_slowclk_to_us(RTC_CNTL_MIN_SLP_VAL_MIN, s_config.rtc_clk_cal_period); #endif // Decide if VDD_SDIO needs to be powered down; // If it needs to be powered down, adjust sleep time. const uint32_t flash_enable_time_us = ESP_SLEEP_WAIT_FLASH_READY_DEFAULT_DELAY_US + CONFIG_ESP_SLEEP_WAIT_FLASH_READY_EXTRA_DELAY; /** * If VDD_SDIO power domain is requested to be turned off, bit `RTC_SLEEP_PD_VDDSDIO` * will be set in `sleep_flags`. */ if (sleep_flags & RTC_SLEEP_PD_VDDSDIO) { #if !SOC_PM_FLASH_KEEP_POWER_IN_LSLP /* * When VDD_SDIO power domain has to be turned off, the minimum sleep time of the * system needs to meet the sum below: * 1. Wait time for the flash power-on after waking up; * 2. The execution time of codes between RTC Timer get start time * with hardware starts to switch state to sleep; * 3. The hardware state switching time of the rtc state machine during * sleep and wake-up. This process requires 6 cycles to complete. * The specific hardware state switching process and the cycles * consumed are rtc_cpu_run_stall(1), cut_pll_rtl(2), cut_8m(1), * min_protect(2); * 4. All the adjustment time which is s_config.sleep_time_adjustment below. */ #if SOC_PMU_SUPPORTED uint32_t rtc_slowclk_us = rtc_time_slowclk_to_us(RTC_MODULE_SLEEP_PREPARE_CYCLES, s_config.rtc_clk_cal_period) + min_sleep_duration_us; #else uint32_t rtc_slowclk_us = rtc_time_slowclk_to_us(RTC_MODULE_SLEEP_PREPARE_CYCLES + RTC_CNTL_MIN_SLP_VAL_MIN, s_config.rtc_clk_cal_period); #endif const uint32_t vddsdio_pd_sleep_duration = MAX(FLASH_PD_MIN_SLEEP_TIME_US, flash_enable_time_us + s_config.sleep_time_adjustment + rtc_slowclk_us); if (can_power_down_vddsdio(sleep_flags, vddsdio_pd_sleep_duration)) { if (s_config.sleep_time_overhead_out < flash_enable_time_us) { s_config.sleep_time_adjustment += flash_enable_time_us; } } else { /** * Minimum sleep time is not enough, then keep the VDD_SDIO power * domain on. */ sleep_flags &= ~RTC_SLEEP_PD_VDDSDIO; if (s_config.sleep_time_overhead_out > flash_enable_time_us) { s_config.sleep_time_adjustment -= flash_enable_time_us; } } #else sleep_flags &= ~RTC_SLEEP_PD_VDDSDIO; #endif } #if CONFIG_ESP_SLEEP_SET_FLASH_DPD if (!(sleep_flags & RTC_SLEEP_PD_VDDSDIO) && (sleep_flags & RTC_SLEEP_FLASH_DPD)) { const uint32_t flash_enable_dpd_us = spi_flash_dpd_get_enter_duration() + spi_flash_dpd_get_exit_duration(); if (s_config.sleep_duration > flash_enable_dpd_us) { if (s_config.sleep_time_overhead_out < flash_enable_dpd_us) { s_config.sleep_time_adjustment += flash_enable_dpd_us; } } else { /** * Minimum sleep time is not enough, then reject flash enter deep power-down mode */ sleep_flags &= ~RTC_SLEEP_FLASH_DPD; if (s_config.sleep_time_overhead_out > flash_enable_dpd_us) { s_config.sleep_time_adjustment -= flash_enable_dpd_us; } } } #endif periph_inform_out_light_sleep_overhead(s_config.sleep_time_adjustment - sleep_time_overhead_in); // Safety net: enable WDT in case exit from light sleep fails #if SOC_RTC_WDT_SUPPORTED wdt_hal_context_t rtc_wdt_ctx = RWDT_HAL_CONTEXT_DEFAULT(); bool rtc_wdt_was_enabled = wdt_hal_is_enabled(&rtc_wdt_ctx); // If WDT was enabled in the user code, then do not change it here. if (!rtc_wdt_was_enabled) { sleep_rtc_wdt_prepare(true); } else { ESP_EARLY_LOGW(TAG, "RTC WDT is enabled and will not be reconfigured again!"); } #endif /* SOC_RTC_WDT_SUPPORTED */ esp_err_t err = ESP_OK; int64_t final_sleep_duration_us = (int64_t)s_config.sleep_duration - (int64_t)s_config.sleep_time_adjustment; // reset light sleep wakeup flag before a new light sleep s_light_sleep_wakeup = false; s_lightsleep_cnt++; #if CONFIG_ESP_SLEEP_DEBUG if (s_sleep_ctx != NULL) { s_sleep_ctx->lightsleep_cnt = s_lightsleep_cnt; } #endif // if rtc timer wakeup source is enabled, need to compare final sleep duration and min sleep duration to avoid late wakeup if ((s_config.wakeup_triggers & RTC_TIMER_TRIG_EN) && (final_sleep_duration_us <= min_sleep_duration_us)) { err = ESP_ERR_SLEEP_TOO_SHORT_SLEEP_DURATION; } #if CONFIG_SPIRAM && CONFIG_FREERTOS_TASK_CREATE_ALLOW_EXT_MEM else if (!esp_task_stack_is_sane_cache_disabled()) { err = ESP_ERR_NOT_ALLOWED; } #endif else { // Enter sleep, then wait for flash to be ready on wakeup err = esp_light_sleep_inner(sleep_flags, flash_enable_time_us, &extra_args); } // light sleep wakeup flag only makes sense after a successful light sleep s_light_sleep_wakeup = (err == ESP_OK); // System timer has been stopped for the duration of the sleep, correct for that. uint64_t rtc_ticks_at_end = rtc_time_get(); if (s_light_sleep_wakeup) { uint64_t rtc_time_diff = rtc_time_slowclk_to_us(rtc_ticks_at_end - s_config.rtc_ticks_at_sleep_start, s_config.rtc_clk_cal_period); /** * If sleep duration is too small(less than 1 rtc_slow_clk cycle), rtc_time_diff will be zero. * In this case, just ignore the time compensation and keep esp_timer monotonic. */ if (rtc_time_diff > 0) { esp_timer_private_set(high_res_time_at_start + rtc_time_diff); } esp_set_time_from_rtc(); } else { #if !CONFIG_FREERTOS_UNICORE && CONFIG_PM_ESP_SLEEP_POWER_DOWN_CPU && SOC_PM_CPU_RETENTION_BY_SW esp_sleep_cpu_skip_retention(); #endif } #if CONFIG_PM_SLP_SPIRAM_HALFSLEEP_ENABLED && !CONFIG_SPIRAM_XIP_FROM_PSRAM && CONFIG_PM_SLP_IRAM_OPT // If CONFIG_SPIRAM_XIP_FROM_PSRAM is not enabled and CONFIG_PM_SLP_IRAM_OPT is enable, // the sleep-wake process prior to this point does not access the PSRAM, so we can postpone waiting // for the PSRAM to resume until here, in order to reuse the time overhead // of the wake-up process as much as possible. esp_psram_impl_resume_from_halfsleep_mode(s_config.rtc_clk_cal_period); #endif #if CONFIG_ESP_SLEEP_CACHE_SAFE_ASSERTION && CONFIG_PM_SLP_IRAM_OPT /* Cache Resume 0: sleep process done, resume cache for continue running */ if (!(s_config.wakeup_triggers & ignore_check_wakeup_triggers)) { sleep_cache_resume(); } #endif /* Restore port critical before unstalling other CPU */ #if CONFIG_FREERTOS_PORT_THREAD_SAFE_CLAIM xPortThreadSafeDisclaim(); #endif #if !CONFIG_FREERTOS_UNICORE sleep_smp_cpu_wakeup_prepare(); #endif #if SOC_RTC_WDT_SUPPORTED if (!rtc_wdt_was_enabled) { sleep_rtc_wdt_prepare(false); } #endif /* SOC_RTC_WDT_SUPPORTED */ #if CONFIG_ESP_TASK_WDT_USE_ESP_TIMER /* Restart the Task Watchdog timer as it was stopped before sleeping. */ if (timerret == ESP_OK) { esp_task_wdt_restart(); } #endif // CONFIG_ESP_TASK_WDT_USE_ESP_TIMER esp_sleep_execute_event_callbacks(SLEEP_EVENT_SW_EXIT_SLEEP, (void *)0); #if CONFIG_ESP_SLEEP_DEBUG if (s_sleep_ctx != NULL) { s_sleep_ctx->sleep_out_rtc_time_stamp = rtc_ticks_at_end; s_sleep_ctx->sleep_request_result = err; } #endif if (s_light_sleep_wakeup) { s_config.sleep_time_overhead_out = (esp_cpu_get_cycle_count() - s_config.ccount_ticks_record) / (esp_clk_cpu_freq() / 1000000ULL); s_config.overhead_out_need_remeasure = false; } esp_sleep_exit_critical(); return err; } esp_err_t esp_sleep_disable_wakeup_source(esp_sleep_source_t source) { // For most of sources it is enough to set trigger mask in local // configuration structure. The actual RTC wake up options // will be updated by esp_sleep_start(). if (source == ESP_SLEEP_WAKEUP_ALL) { s_config.wakeup_triggers = 0; } else if (CHECK_SOURCE(source, ESP_SLEEP_WAKEUP_TIMER, RTC_TIMER_TRIG_EN)) { s_config.wakeup_triggers &= ~RTC_TIMER_TRIG_EN; s_config.sleep_duration = 0; #if SOC_PM_SUPPORT_EXT0_WAKEUP } else if (CHECK_SOURCE(source, ESP_SLEEP_WAKEUP_EXT0, RTC_EXT0_TRIG_EN)) { s_config.ext0_rtc_gpio_num = 0; s_config.ext0_trigger_level = 0; s_config.wakeup_triggers &= ~RTC_EXT0_TRIG_EN; #endif #if SOC_PM_SUPPORT_EXT1_WAKEUP } else if (CHECK_SOURCE(source, ESP_SLEEP_WAKEUP_EXT1, RTC_EXT1_TRIG_EN)) { s_config.ext1_rtc_gpio_mask = 0; s_config.ext1_trigger_mode = 0; s_config.wakeup_triggers &= ~RTC_EXT1_TRIG_EN; #endif #if SOC_PM_SUPPORT_TOUCH_SENSOR_WAKEUP } else if (CHECK_SOURCE(source, ESP_SLEEP_WAKEUP_TOUCHPAD, RTC_TOUCH_TRIG_EN)) { s_config.wakeup_triggers &= ~RTC_TOUCH_TRIG_EN; #endif } else if (CHECK_SOURCE(source, ESP_SLEEP_WAKEUP_GPIO, RTC_GPIO_TRIG_EN)) { #if SOC_GPIO_SUPPORT_DEEPSLEEP_WAKEUP s_config.gpio_wakeup_mask = 0; memset(s_config.gpio_trigger_mode, 0, sizeof(s_config.gpio_trigger_mode)); #endif s_config.wakeup_triggers &= ~RTC_GPIO_TRIG_EN; } else if (CHECK_SOURCE(source, ESP_SLEEP_WAKEUP_UART0, RTC_UART0_TRIG_EN)) { s_config.wakeup_triggers &= ~RTC_UART0_TRIG_EN; } else if (CHECK_SOURCE(source, ESP_SLEEP_WAKEUP_UART1, RTC_UART1_TRIG_EN)) { s_config.wakeup_triggers &= ~RTC_UART1_TRIG_EN; #if (SOC_UART_HP_NUM > 2) && !SOC_PM_RTC_NOT_SUPPORT_UART2_WAKEUP } else if (CHECK_SOURCE(source, ESP_SLEEP_WAKEUP_UART2, RTC_UART2_TRIG_EN)) { s_config.wakeup_triggers &= ~RTC_UART2_TRIG_EN; #endif #if (SOC_UART_HP_NUM > 3) } else if (CHECK_SOURCE(source, ESP_SLEEP_WAKEUP_UART3, RTC_UART3_TRIG_EN)) { s_config.wakeup_triggers &= ~RTC_UART3_TRIG_EN; #endif #if (SOC_UART_HP_NUM > 4) } else if (CHECK_SOURCE(source, ESP_SLEEP_WAKEUP_UART4, RTC_UART4_TRIG_EN)) { s_config.wakeup_triggers &= ~RTC_UART4_TRIG_EN; #endif #if CONFIG_ULP_COPROC_TYPE_FSM } else if (CHECK_SOURCE(source, ESP_SLEEP_WAKEUP_ULP, RTC_ULP_TRIG_EN)) { s_config.wakeup_triggers &= ~RTC_ULP_TRIG_EN; #endif #if SOC_LP_VAD_SUPPORTED } else if (CHECK_SOURCE(source, ESP_SLEEP_WAKEUP_VAD, RTC_LP_VAD_TRIG_EN)) { s_config.wakeup_triggers &= ~RTC_LP_VAD_TRIG_EN; #endif #if SOC_VBAT_SUPPORTED } else if (CHECK_SOURCE(source, ESP_SLEEP_WAKEUP_VBAT_UNDER_VOLT, RTC_VBAT_UNDER_VOLT_TRIG_EN)) { s_config.wakeup_triggers &= ~RTC_VBAT_UNDER_VOLT_TRIG_EN; #endif #if SOC_PM_SUPPORT_USB_WAKEUP } else if (CHECK_SOURCE(source, ESP_SLEEP_WAKEUP_USB, RTC_USB_TRIG_EN)) { s_config.wakeup_triggers &= ~RTC_USB_TRIG_EN; #endif } else { ESP_EARLY_LOGE(TAG, "Incorrect wakeup source (%d) to disable.", (int) source); return ESP_ERR_INVALID_STATE; } return ESP_OK; } esp_err_t esp_sleep_enable_ulp_wakeup(void) { #ifndef CONFIG_ULP_COPROC_ENABLED return ESP_ERR_INVALID_STATE; #endif // CONFIG_ULP_COPROC_ENABLED #if CONFIG_IDF_TARGET_ESP32 #if ((defined CONFIG_RTC_EXT_CRYST_ADDIT_CURRENT) || (defined CONFIG_RTC_EXT_CRYST_ADDIT_CURRENT_V2)) ESP_LOGE(TAG, "Failed to enable wakeup when provide current to external 32kHz crystal"); return ESP_ERR_NOT_SUPPORTED; #endif if (s_config.wakeup_triggers & RTC_EXT0_TRIG_EN) { ESP_LOGE(TAG, "Conflicting wake-up trigger: ext0"); return ESP_ERR_INVALID_STATE; } #endif //CONFIG_IDF_TARGET_ESP32 #if CONFIG_ULP_COPROC_TYPE_FSM && CONFIG_ULP_COPROC_TYPE_RISCV s_config.wakeup_triggers |= (RTC_ULP_TRIG_EN | RTC_COCPU_TRIG_EN | RTC_COCPU_TRAP_TRIG_EN); return ESP_OK; #elif CONFIG_ULP_COPROC_TYPE_FSM s_config.wakeup_triggers |= RTC_ULP_TRIG_EN; return ESP_OK; #elif CONFIG_ULP_COPROC_TYPE_RISCV s_config.wakeup_triggers |= (RTC_COCPU_TRIG_EN | RTC_COCPU_TRAP_TRIG_EN); return ESP_OK; #elif CONFIG_ULP_COPROC_TYPE_LP_CORE #if CONFIG_ULP_TRAP_WAKEUP s_config.wakeup_triggers |= RTC_LP_CORE_TRIG_EN | RTC_LP_CORE_TRAP_TRIG_EN; #else s_config.wakeup_triggers |= RTC_LP_CORE_TRIG_EN; #endif return ESP_OK; #else return ESP_ERR_NOT_SUPPORTED; #endif //CONFIG_ULP_COPROC_TYPE_FSM } esp_err_t esp_sleep_enable_timer_wakeup(uint64_t time_in_us) { #if CONFIG_SOC_CLK_TREE_SUPPORTED uint32_t lp_slow_freq_hz = esp_clk_tree_lp_slow_get_freq_hz(ESP_CLK_TREE_SRC_FREQ_PRECISION_APPROX); assert(lp_slow_freq_hz); if (time_in_us > ((BIT64(SOC_LP_TIMER_BIT_WIDTH_LO + SOC_LP_TIMER_BIT_WIDTH_HI) - 1) / lp_slow_freq_hz) * MHZ ) { return ESP_ERR_INVALID_ARG; } #endif esp_sleep_enter_critical(); s_config.wakeup_triggers |= RTC_TIMER_TRIG_EN; s_config.sleep_duration = time_in_us; esp_sleep_exit_critical(); return ESP_OK; } #if SOC_PM_SUPPORT_RTC_PERIPH_PD esp_err_t esp_sleep_acquire_lp_use_xtal(void) { esp_err_t ret = ESP_FAIL; ret = esp_sleep_pd_config(ESP_PD_DOMAIN_XTAL, ESP_PD_OPTION_ON); if (ret != ESP_OK) { ESP_LOGE(TAG, "fail to keep xtal power on"); return ret; } ret = esp_sleep_sub_mode_config(ESP_SLEEP_LP_USE_XTAL_MODE, true); if (ret != ESP_OK) { ESP_LOGE(TAG, "fail to set to ESP_SLEEP_LP_USE_XTAL_MODE mode"); return ret; } return ESP_OK; } esp_err_t esp_sleep_release_lp_use_xtal(void) { esp_err_t ret = ESP_FAIL; ret = esp_sleep_pd_config(ESP_PD_DOMAIN_XTAL, ESP_PD_OPTION_OFF); if (ret != ESP_OK) { ESP_LOGE(TAG, "fail to keep xtal power off"); return ret; } ret = esp_sleep_sub_mode_config(ESP_SLEEP_LP_USE_XTAL_MODE, false); if (ret != ESP_OK) { ESP_LOGE(TAG, "fail to disable ESP_SLEEP_LP_USE_XTAL_MODE mode"); return ret; } return ESP_OK; } #endif #if SOC_LP_VAD_SUPPORTED esp_err_t esp_sleep_enable_vad_wakeup(void) { esp_err_t ret = ESP_FAIL; ret = esp_sleep_acquire_lp_use_xtal(); ret = esp_sleep_pd_config(ESP_PD_DOMAIN_RTC_PERIPH, ESP_PD_OPTION_ON); if (ret != ESP_OK) { ESP_LOGE(TAG, "fail to keep rtc periph power on"); return ret; } s_config.wakeup_triggers |= RTC_LP_VAD_TRIG_EN; return ret; } #endif #if SOC_VBAT_SUPPORTED esp_err_t esp_sleep_enable_vbat_under_volt_wakeup(void) { s_config.wakeup_triggers |= RTC_VBAT_UNDER_VOLT_TRIG_EN; return ESP_OK; } #endif static SLEEP_FN_ATTR esp_err_t timer_wakeup_prepare(int64_t sleep_duration) { if (sleep_duration < 0) { sleep_duration = 0; } int64_t ticks = rtc_time_us_to_slowclk(sleep_duration, s_config.rtc_clk_cal_period); int64_t target_wakeup_tick = s_config.rtc_ticks_at_sleep_start + ticks; #if CONFIG_PM_POWER_DOWN_PERIPHERAL_IN_LIGHT_SLEEP int64_t backup_cost_ticks = rtc_time_us_to_slowclk(((pmu_sleep_machine_constant_t *)PMU_instance()->mc)->hp.regdma_a2s_work_time_us, s_config.rtc_clk_cal_period); // Last timer wake-up validity check if ((sleep_duration == 0) || \ (target_wakeup_tick < rtc_time_get() + SLEEP_TIMER_ALARM_TO_SLEEP_TICKS + backup_cost_ticks)) { // Treat too short sleep duration setting as timer reject return ESP_ERR_SLEEP_REJECT; } #endif rtc_timer_hal_set_wakeup_time(0, target_wakeup_tick); return ESP_OK; } #if SOC_VBAT_SUPPORTED static void vbat_under_volt_wakeup_prepare(void) { vbat_ll_clear_intr_mask(VBAT_LL_CHARGER_UNDERVOLTAGE_INTR); } #endif #if SOC_TOUCH_SENSOR_SUPPORTED static void touch_wakeup_prepare(void) { touch_hal_prepare_deep_sleep(); } esp_err_t esp_sleep_enable_touchpad_wakeup(void) { #if CONFIG_IDF_TARGET_ESP32 #if ((defined CONFIG_RTC_EXT_CRYST_ADDIT_CURRENT) || (defined CONFIG_RTC_EXT_CRYST_ADDIT_CURRENT_V2)) ESP_LOGE(TAG, "Failed to enable wakeup when provide current to external 32kHz crystal"); return ESP_ERR_NOT_SUPPORTED; #endif if (s_config.wakeup_triggers & (RTC_EXT0_TRIG_EN)) { ESP_LOGE(TAG, "Conflicting wake-up trigger: ext0"); return ESP_ERR_INVALID_STATE; } #endif //CONFIG_IDF_TARGET_ESP32 s_config.wakeup_triggers |= RTC_TOUCH_TRIG_EN; return ESP_OK; } int esp_sleep_get_touchpad_wakeup_status(void) { if (!(esp_sleep_get_wakeup_causes() & BIT(ESP_SLEEP_WAKEUP_TOUCHPAD))) { return -1; } uint32_t chan_num; #if SOC_TOUCH_SENSOR_VERSION == 1 touch_ll_get_active_channel_mask(&chan_num); #else touch_ll_sleep_get_channel_num(&chan_num); #endif return (int)chan_num; } #endif // SOC_TOUCH_SENSOR_SUPPORTED bool esp_sleep_is_valid_wakeup_gpio(gpio_num_t gpio_num) { #if SOC_RTCIO_PIN_COUNT > 0 return RTC_GPIO_IS_VALID_GPIO(gpio_num); #elif SOC_GPIO_SUPPORT_HP_PERIPH_PD_SLEEP_WAKEUP return GPIO_IS_HP_PERIPH_PD_WAKEUP_VALID_IO(gpio_num); #else return false; #endif } #if SOC_PM_SUPPORT_EXT0_WAKEUP esp_err_t esp_sleep_enable_ext0_wakeup(gpio_num_t gpio_num, int level) { if (level < 0 || level > 1) { return ESP_ERR_INVALID_ARG; } if (!esp_sleep_is_valid_wakeup_gpio(gpio_num)) { return ESP_ERR_INVALID_ARG; } #if CONFIG_IDF_TARGET_ESP32 if (s_config.wakeup_triggers & (RTC_TOUCH_TRIG_EN | RTC_ULP_TRIG_EN)) { ESP_LOGE(TAG, "Conflicting wake-up triggers: touch / ULP"); return ESP_ERR_INVALID_STATE; } #endif //CONFIG_IDF_TARGET_ESP32 s_config.ext0_rtc_gpio_num = rtc_io_number_get(gpio_num); s_config.ext0_trigger_level = level; s_config.wakeup_triggers |= RTC_EXT0_TRIG_EN; return ESP_OK; } static void ext0_wakeup_prepare(void) { int rtc_gpio_num = s_config.ext0_rtc_gpio_num; #if SOC_LP_IO_CLOCK_IS_INDEPENDENT // To suppress build errors about spinlock's __DECLARE_RCC_ATOMIC_ENV int __DECLARE_RCC_ATOMIC_ENV __attribute__ ((unused)); rtcio_ll_enable_io_clock(true); #endif rtcio_hal_ext0_set_wakeup_pin(rtc_gpio_num, s_config.ext0_trigger_level); rtcio_hal_function_select(rtc_gpio_num, RTCIO_LL_FUNC_RTC); rtcio_hal_iomux_func_sel(rtc_gpio_num, RTCIO_LL_PIN_FUNC); rtcio_hal_input_enable(rtc_gpio_num); } #endif // SOC_PM_SUPPORT_EXT0_WAKEUP #if SOC_PM_SUPPORT_EXT1_WAKEUP && SOC_RTCIO_PIN_COUNT > 0 esp_err_t esp_sleep_enable_ext1_wakeup(uint64_t io_mask, esp_sleep_ext1_wakeup_mode_t level_mode) { if (io_mask == 0 && level_mode > ESP_EXT1_WAKEUP_ANY_HIGH) { return ESP_ERR_INVALID_ARG; } // Reset all EXT1 configs esp_sleep_disable_ext1_wakeup_io(0); return esp_sleep_enable_ext1_wakeup_io(io_mask, level_mode); } esp_err_t esp_sleep_enable_ext1_wakeup_io(uint64_t io_mask, esp_sleep_ext1_wakeup_mode_t level_mode) { if (io_mask == 0 && level_mode > ESP_EXT1_WAKEUP_ANY_HIGH) { return ESP_ERR_INVALID_ARG; } // Translate bit map of GPIO numbers into the bit map of RTC IO numbers uint32_t rtc_gpio_mask = 0; for (int gpio = 0; io_mask; ++gpio, io_mask >>= 1) { if ((io_mask & 1) == 0) { continue; } if (!esp_sleep_is_valid_wakeup_gpio(gpio)) { ESP_LOGE(TAG, "Not an RTC IO: GPIO%d", gpio); return ESP_ERR_INVALID_ARG; } rtc_gpio_mask |= BIT(rtc_io_number_get(gpio)); } #if !SOC_PM_SUPPORT_EXT1_WAKEUP_MODE_PER_PIN uint32_t ext1_rtc_gpio_mask = 0; uint32_t ext1_trigger_mode = 0; ext1_rtc_gpio_mask = s_config.ext1_rtc_gpio_mask | rtc_gpio_mask; if (level_mode) { ext1_trigger_mode = s_config.ext1_trigger_mode | rtc_gpio_mask; } else { ext1_trigger_mode = s_config.ext1_trigger_mode & (~rtc_gpio_mask); } if (((ext1_rtc_gpio_mask & ext1_trigger_mode) != ext1_rtc_gpio_mask) && ((ext1_rtc_gpio_mask & ext1_trigger_mode) != 0)) { return ESP_ERR_NOT_ALLOWED; } #endif s_config.ext1_rtc_gpio_mask |= rtc_gpio_mask; if (level_mode) { s_config.ext1_trigger_mode |= rtc_gpio_mask; } else { s_config.ext1_trigger_mode &= (~rtc_gpio_mask); } s_config.wakeup_triggers |= RTC_EXT1_TRIG_EN; return ESP_OK; } esp_err_t esp_sleep_disable_ext1_wakeup_io(uint64_t io_mask) { if (io_mask == 0) { s_config.ext1_rtc_gpio_mask = 0; s_config.ext1_trigger_mode = 0; } else { // Translate bit map of GPIO numbers into the bit map of RTC IO numbers uint32_t rtc_gpio_mask = 0; for (int gpio = 0; io_mask; ++gpio, io_mask >>= 1) { if ((io_mask & 1) == 0) { continue; } if (!esp_sleep_is_valid_wakeup_gpio(gpio)) { ESP_LOGE(TAG, "Not an RTC IO Considering io_mask: GPIO%d", gpio); return ESP_ERR_INVALID_ARG; } rtc_gpio_mask |= BIT(rtc_io_number_get(gpio)); } s_config.ext1_rtc_gpio_mask &= (~rtc_gpio_mask); s_config.ext1_trigger_mode &= (~rtc_gpio_mask); } if (s_config.ext1_rtc_gpio_mask == 0) { s_config.wakeup_triggers &= (~RTC_EXT1_TRIG_EN); } return ESP_OK; } #if SOC_PM_SUPPORT_EXT1_WAKEUP_MODE_PER_PIN esp_err_t esp_sleep_enable_ext1_wakeup_with_level_mask(uint64_t io_mask, uint64_t level_mask) { if ((level_mask & io_mask) != level_mask) { return ESP_ERR_INVALID_ARG; } // Translate bit map of GPIO numbers into the bit map of RTC IO numbers // Translate bit map of GPIO wakeup mode into the bit map of RTC IO wakeup mode uint32_t rtc_gpio_mask = 0, rtc_gpio_wakeup_mode_mask = 0; for (int gpio = 0; io_mask; ++gpio, io_mask >>= 1, level_mask >>= 1) { if ((io_mask & 1) == 0) { continue; } if (!esp_sleep_is_valid_wakeup_gpio(gpio)) { ESP_LOGE(TAG, "Not an RTC IO Considering io_mask: GPIO%d", gpio); return ESP_ERR_INVALID_ARG; } rtc_gpio_mask |= BIT(rtc_io_number_get(gpio)); if ((level_mask & 1) == 1) { rtc_gpio_wakeup_mode_mask |= BIT(rtc_io_number_get(gpio)); } } s_config.ext1_rtc_gpio_mask = rtc_gpio_mask; s_config.ext1_trigger_mode = rtc_gpio_wakeup_mode_mask; s_config.wakeup_triggers |= RTC_EXT1_TRIG_EN; return ESP_OK; } #endif static void ext1_wakeup_prepare(void) { // Configure all RTC IOs selected as ext1 wakeup inputs uint32_t rtc_gpio_mask = s_config.ext1_rtc_gpio_mask; for (int gpio = 0; gpio < SOC_GPIO_PIN_COUNT && rtc_gpio_mask != 0; ++gpio) { int rtc_pin = rtc_io_number_get(gpio); if ((rtc_gpio_mask & BIT(rtc_pin)) == 0) { continue; } #if SOC_LP_IO_CLOCK_IS_INDEPENDENT // To suppress build errors about spinlock's __DECLARE_RCC_ATOMIC_ENV int __DECLARE_RCC_ATOMIC_ENV __attribute__ ((unused)); rtcio_ll_enable_io_clock(true); #endif #if SOC_RTCIO_INPUT_OUTPUT_SUPPORTED // Route pad to RTC rtcio_hal_function_select(rtc_pin, RTCIO_LL_FUNC_RTC); // Select LP GPIO function rtcio_hal_iomux_func_sel(rtc_pin, RTCIO_LL_PIN_FUNC); // set input enable in sleep mode rtcio_hal_input_enable(rtc_pin); #if SOC_PM_SUPPORT_RTC_PERIPH_PD // Pad configuration depends on RTC_PERIPH state in sleep mode if (s_config.domain[ESP_PD_DOMAIN_RTC_PERIPH].pd_option != ESP_PD_OPTION_ON) { rtcio_hal_hold_enable(rtc_pin); } #endif #else /* ESP32H2 use hp iomux to config rtcio, and there is no complete * rtcio functionality. In the case of EXT1 wakeup, rtcio only provides * a pathway to EXT1. */ // Route pad to DIGITAL rtcio_hal_function_select(rtc_pin, RTCIO_LL_FUNC_DIGITAL); // set input enable gpio_ll_input_enable(&GPIO, gpio); #if SOC_RTCIO_HOLD_SUPPORTED // hold rtc_pin to use it during sleep state rtcio_hal_hold_enable(rtc_pin); #endif #endif // Keep track of pins which are processed to bail out early rtc_gpio_mask &= ~BIT(rtc_pin); } // Clear state from previous wakeup rtc_hal_ext1_clear_wakeup_status(); // Set RTC IO pins and mode to be used for wakeup rtc_hal_ext1_set_wakeup_pins(s_config.ext1_rtc_gpio_mask, s_config.ext1_trigger_mode); } uint64_t esp_sleep_get_ext1_wakeup_status(void) { if (!(esp_sleep_get_wakeup_causes() & BIT(ESP_SLEEP_WAKEUP_EXT1))) { return 0; } 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; while (status) { int rtc_pin = __builtin_ctz(status); gpio_num_t gpio = esp_sleep_wakeup_io_bit2num(rtc_pin); if (gpio != GPIO_NUM_NC) { gpio_mask |= 1ULL << gpio; } status &= ~BIT(rtc_pin); } return gpio_mask; } #endif // SOC_PM_SUPPORT_EXT1_WAKEUP && SOC_RTCIO_PIN_COUNT > 0 #if SOC_GPIO_SUPPORT_HP_PERIPH_PD_SLEEP_WAKEUP uint64_t esp_sleep_get_gpio_wakeup_status(void) { if (!(esp_sleep_get_wakeup_causes() & BIT(ESP_SLEEP_WAKEUP_GPIO))) { return 0; } return rtc_hal_gpio_get_wakeup_status(); } static void esp_sleep_gpio_wakeup_prepare_on_hp_periph_powerdown(void) { uint64_t valid_wake_io_mask = s_config.gpio_wakeup_mask & SOC_GPIO_HP_PERIPH_PD_SLEEP_WAKEABLE_MASK; while (valid_wake_io_mask) { int gpio_idx = __builtin_ctzll(valid_wake_io_mask); #if SOC_LP_IO_CLOCK_IS_INDEPENDENT // To suppress build errors about spinlock's __DECLARE_RCC_ATOMIC_ENV int __DECLARE_RCC_ATOMIC_ENV __attribute__ ((unused)); rtcio_ll_enable_io_clock(true); #endif __attribute__ ((unused)) gpio_int_type_t intr_type = (gpio_int_type_t)s_config.gpio_trigger_mode[gpio_idx]; #if CONFIG_ESP_SLEEP_GPIO_ENABLE_INTERNAL_RESISTORS if (GPIO_IS_VALID_OUTPUT_GPIO(gpio_idx)) { if (intr_type == GPIO_INTR_LOW_LEVEL || intr_type == GPIO_INTR_NEGEDGE) { gpio_pullup_en(gpio_idx); gpio_pulldown_dis(gpio_idx); } else if (intr_type == GPIO_INTR_HIGH_LEVEL || intr_type == GPIO_INTR_POSEDGE) { gpio_pullup_dis(gpio_idx); gpio_pulldown_en(gpio_idx); } else { gpio_pullup_dis(gpio_idx); gpio_pulldown_dis(gpio_idx); } } else { ESP_EARLY_LOGE(TAG, "GPIO%d not support internal PU/PD", gpio_idx); } #endif #if SOC_RTC_GPIO_EDGE_WAKEUP_SUPPORTED /* Clear any pending edge-wakeup latch so a stale event does not immediately re-wake the chip. */ if (intr_type == GPIO_INTR_POSEDGE || intr_type == GPIO_INTR_NEGEDGE || intr_type == GPIO_INTR_ANYEDGE) { gpio_hal_clear_hp_periph_pd_sleep_edge_wakeup_latch(NULL, gpio_idx); } #endif ESP_ERROR_CHECK(gpio_hold_en(gpio_idx)); valid_wake_io_mask &= valid_wake_io_mask - 1; } // Clear state from previous wakeup rtc_hal_gpio_clear_wakeup_status(); } esp_err_t esp_sleep_enable_gpio_wakeup_on_hp_periph_powerdown(uint64_t gpio_pin_mask, esp_sleep_gpio_wake_up_mode_t mode) { if (gpio_pin_mask == 0) { return ESP_ERR_INVALID_ARG; } const gpio_int_type_t intr_type = WAKEUP_MODE_2_INT_TYPE(mode); if (intr_type == GPIO_INTR_DISABLE || intr_type > GPIO_INTR_HIGH_LEVEL) { ESP_LOGE(TAG, "invalid mode"); return ESP_ERR_INVALID_ARG; } #if !SOC_RTC_GPIO_EDGE_WAKEUP_SUPPORTED if (intr_type != GPIO_INTR_LOW_LEVEL && intr_type != GPIO_INTR_HIGH_LEVEL) { ESP_LOGE(TAG, "invalid mode"); return ESP_ERR_INVALID_ARG; } #endif esp_err_t err = ESP_OK; uint64_t invalid_io_mask = gpio_pin_mask & ~SOC_GPIO_HP_PERIPH_PD_SLEEP_WAKEABLE_MASK; if (invalid_io_mask != 0) { for (gpio_num_t gpio_idx = __builtin_ctzll(invalid_io_mask); invalid_io_mask >> gpio_idx; gpio_idx++) { if (invalid_io_mask & BIT64(gpio_idx)) { ESP_LOGE(TAG, "GPIO %d does not support wakeup on peripheral powerdown sleep", gpio_idx); return ESP_ERR_INVALID_ARG; } } } while (gpio_pin_mask) { int gpio_idx = __builtin_ctzll(gpio_pin_mask); err = gpio_wakeup_enable_on_hp_periph_powerdown_sleep(gpio_idx, intr_type); if (err != ESP_OK) return err; s_config.gpio_wakeup_mask |= BIT64(gpio_idx); s_config.gpio_trigger_mode[gpio_idx] = (uint8_t)intr_type; gpio_pin_mask &= gpio_pin_mask - 1; } s_config.wakeup_triggers |= RTC_GPIO_TRIG_EN; return err; } #endif //SOC_GPIO_SUPPORT_HP_PERIPH_PD_SLEEP_WAKEUP esp_err_t esp_sleep_enable_gpio_wakeup(void) { #if CONFIG_PM_POWER_DOWN_PERIPHERAL_IN_LIGHT_SLEEP ESP_LOGW(TAG, "%s wakeup source is not available if the peripheral power domain is powered down in sleep", "GPIO"); #endif #if CONFIG_IDF_TARGET_ESP32 if (s_config.wakeup_triggers & (RTC_TOUCH_TRIG_EN | RTC_ULP_TRIG_EN)) { ESP_LOGE(TAG, "Conflicting wake-up triggers: touch / ULP"); return ESP_ERR_INVALID_STATE; } #endif s_config.wakeup_triggers |= RTC_GPIO_TRIG_EN; return ESP_OK; } esp_err_t esp_sleep_enable_uart_wakeup(int uart_num) { #if CONFIG_PM_POWER_DOWN_PERIPHERAL_IN_LIGHT_SLEEP ESP_LOGW(TAG, "%s wakeup source is not available if the peripheral power domain is powered down in sleep", "UART"); #endif if (uart_num == UART_NUM_0) { s_config.wakeup_triggers |= RTC_UART0_TRIG_EN; } else if (uart_num == UART_NUM_1) { s_config.wakeup_triggers |= RTC_UART1_TRIG_EN; #if (SOC_UART_HP_NUM > 2) && !SOC_PM_RTC_NOT_SUPPORT_UART2_WAKEUP } else if (uart_num == UART_NUM_2) { s_config.wakeup_triggers |= RTC_UART2_TRIG_EN; #endif #if (SOC_UART_HP_NUM > 3) } else if (uart_num == UART_NUM_3) { s_config.wakeup_triggers |= RTC_UART3_TRIG_EN; #endif #if (SOC_UART_HP_NUM > 4) } else if (uart_num == UART_NUM_4) { s_config.wakeup_triggers |= RTC_UART4_TRIG_EN; #endif } else #if SOC_PM_SUPPORT_LP_UART_WAKEUP if (uart_num == LP_UART_NUM_0) { s_config.wakeup_triggers |= PMU_LP_UART_WAKEUP_EN; } else #endif { return ESP_ERR_INVALID_ARG; } return ESP_OK; } esp_err_t esp_sleep_enable_wifi_wakeup(void) { #if SOC_PM_SUPPORT_WIFI_WAKEUP s_config.wakeup_triggers |= RTC_WIFI_TRIG_EN; return ESP_OK; #else return ESP_ERR_NOT_SUPPORTED; #endif } esp_err_t esp_sleep_disable_wifi_wakeup(void) { #if SOC_PM_SUPPORT_WIFI_WAKEUP s_config.wakeup_triggers &= (~RTC_WIFI_TRIG_EN); return ESP_OK; #else return ESP_ERR_NOT_SUPPORTED; #endif } esp_err_t esp_sleep_enable_wifi_beacon_wakeup(void) { #if SOC_PM_SUPPORT_BEACON_WAKEUP s_config.wakeup_triggers |= PMU_WIFI_BEACON_WAKEUP_EN; return ESP_OK; #else return ESP_ERR_NOT_SUPPORTED; #endif } esp_err_t esp_sleep_disable_wifi_beacon_wakeup(void) { #if SOC_PM_SUPPORT_BEACON_WAKEUP s_config.wakeup_triggers &= (~PMU_WIFI_BEACON_WAKEUP_EN); return ESP_OK; #else return ESP_ERR_NOT_SUPPORTED; #endif } esp_err_t esp_sleep_enable_bt_wakeup(void) { #if SOC_PM_SUPPORT_BT_WAKEUP s_config.wakeup_triggers |= RTC_BT_TRIG_EN; return ESP_OK; #else return ESP_ERR_NOT_SUPPORTED; #endif } esp_err_t esp_sleep_disable_bt_wakeup(void) { #if SOC_PM_SUPPORT_BT_WAKEUP s_config.wakeup_triggers &= (~RTC_BT_TRIG_EN); return ESP_OK; #else return ESP_ERR_NOT_SUPPORTED; #endif } esp_err_t esp_sleep_enable_usb_wakeup(void) { #if SOC_PM_SUPPORT_USB_WAKEUP s_config.wakeup_triggers |= RTC_USB_TRIG_EN; return ESP_OK; #else return ESP_ERR_NOT_SUPPORTED; #endif } esp_err_t esp_sleep_disable_usb_wakeup(void) { #if SOC_PM_SUPPORT_USB_WAKEUP s_config.wakeup_triggers &= (~RTC_USB_TRIG_EN); return ESP_OK; #else return ESP_ERR_NOT_SUPPORTED; #endif } esp_sleep_wakeup_cause_t esp_sleep_get_wakeup_cause(void) { if (esp_rom_get_reset_reason(0) != RESET_REASON_CORE_DEEP_SLEEP && !s_light_sleep_wakeup) { return ESP_SLEEP_WAKEUP_UNDEFINED; } #if SOC_PMU_SUPPORTED uint32_t wakeup_cause = pmu_ll_hp_get_wakeup_cause(&PMU); #else uint32_t wakeup_cause = rtc_cntl_ll_get_wakeup_cause(); #endif uint32_t uart_wakeup_mask = RTC_UART0_TRIG_EN | RTC_UART1_TRIG_EN; #if (SOC_UART_HP_NUM > 2) && !SOC_PM_RTC_NOT_SUPPORT_UART2_WAKEUP uart_wakeup_mask |= RTC_UART2_TRIG_EN; #endif #if (SOC_UART_HP_NUM > 3) uart_wakeup_mask |= RTC_UART3_TRIG_EN; #endif #if (SOC_UART_HP_NUM > 4) uart_wakeup_mask |= RTC_UART4_TRIG_EN; #endif if (wakeup_cause & RTC_TIMER_TRIG_EN) { return ESP_SLEEP_WAKEUP_TIMER; } else if (wakeup_cause & RTC_GPIO_TRIG_EN) { return ESP_SLEEP_WAKEUP_GPIO; } else if (wakeup_cause & uart_wakeup_mask) { return ESP_SLEEP_WAKEUP_UART; #if SOC_PM_SUPPORT_EXT0_WAKEUP } else if (wakeup_cause & RTC_EXT0_TRIG_EN) { return ESP_SLEEP_WAKEUP_EXT0; #endif #if SOC_PM_SUPPORT_EXT1_WAKEUP } else if (wakeup_cause & RTC_EXT1_TRIG_EN) { return ESP_SLEEP_WAKEUP_EXT1; #endif #if SOC_PM_SUPPORT_TOUCH_SENSOR_WAKEUP } else if (wakeup_cause & RTC_TOUCH_TRIG_EN) { return ESP_SLEEP_WAKEUP_TOUCHPAD; #endif #if SOC_ULP_FSM_SUPPORTED } else if (wakeup_cause & RTC_ULP_TRIG_EN) { return ESP_SLEEP_WAKEUP_ULP; #endif #if SOC_PM_SUPPORT_WIFI_WAKEUP } else if (wakeup_cause & RTC_WIFI_TRIG_EN) { return ESP_SLEEP_WAKEUP_WIFI; #endif #if SOC_PM_SUPPORT_BT_WAKEUP } else if (wakeup_cause & RTC_BT_TRIG_EN) { return ESP_SLEEP_WAKEUP_BT; #endif #if SOC_RISCV_COPROC_SUPPORTED } else if (wakeup_cause & RTC_COCPU_TRIG_EN) { return ESP_SLEEP_WAKEUP_ULP; } else if (wakeup_cause & RTC_COCPU_TRAP_TRIG_EN) { return ESP_SLEEP_WAKEUP_COCPU_TRAP_TRIG; #endif #if SOC_LP_CORE_SUPPORTED } else if (wakeup_cause & RTC_LP_CORE_TRIG_EN) { return ESP_SLEEP_WAKEUP_ULP; } else if (wakeup_cause & RTC_LP_CORE_TRAP_TRIG_EN) { return ESP_SLEEP_WAKEUP_COCPU_TRAP_TRIG; #endif #if SOC_LP_VAD_SUPPORTED } else if (wakeup_cause & RTC_LP_VAD_TRIG_EN) { return ESP_SLEEP_WAKEUP_VAD; #endif #if SOC_VBAT_SUPPORTED } else if (wakeup_cause & RTC_VBAT_UNDER_VOLT_TRIG_EN) { return ESP_SLEEP_WAKEUP_VBAT_UNDER_VOLT; #endif } else { return ESP_SLEEP_WAKEUP_UNDEFINED; } } uint32_t esp_sleep_get_wakeup_causes(void) { uint32_t wakeup_cause = 0; if (esp_rom_get_reset_reason(0) != RESET_REASON_CORE_DEEP_SLEEP && !s_light_sleep_wakeup) { wakeup_cause |= BIT(ESP_SLEEP_WAKEUP_UNDEFINED); return wakeup_cause; } #if SOC_PMU_SUPPORTED uint32_t wakeup_cause_raw = pmu_ll_hp_get_wakeup_cause(&PMU); #else uint32_t wakeup_cause_raw = rtc_cntl_ll_get_wakeup_cause(); #endif #if SOC_LP_CORE_HW_AUTO_CLRWAKEUPCAUSE /* LP store register to read wakeup cause saved by LP core. * Must match the register used in lp_core_utils.c */ uint32_t lp_core_wakeup_cause_status0 = lp_aon_hal_load_wakeup_cause(); if ((wakeup_cause_raw == 0) && (lp_core_wakeup_cause_status0 != 0)) { wakeup_cause_raw = lp_core_wakeup_cause_status0; } #endif if (wakeup_cause_raw & RTC_TIMER_TRIG_EN) { wakeup_cause |= BIT(ESP_SLEEP_WAKEUP_TIMER); } if (wakeup_cause_raw & RTC_GPIO_TRIG_EN) { wakeup_cause |= BIT(ESP_SLEEP_WAKEUP_GPIO); } if (wakeup_cause_raw & RTC_UART0_TRIG_EN) { wakeup_cause |= BIT(ESP_SLEEP_WAKEUP_UART0); } if (wakeup_cause_raw & RTC_UART1_TRIG_EN) { wakeup_cause |= BIT(ESP_SLEEP_WAKEUP_UART1); } #if (SOC_UART_HP_NUM > 2) && !SOC_PM_RTC_NOT_SUPPORT_UART2_WAKEUP if (wakeup_cause_raw & RTC_UART2_TRIG_EN) { wakeup_cause |= BIT(ESP_SLEEP_WAKEUP_UART2); } #endif #if (SOC_UART_HP_NUM > 3) if (wakeup_cause_raw & RTC_UART3_TRIG_EN) { wakeup_cause |= BIT(ESP_SLEEP_WAKEUP_UART3); } #endif #if (SOC_UART_HP_NUM > 4) if (wakeup_cause_raw & RTC_UART4_TRIG_EN) { wakeup_cause |= BIT(ESP_SLEEP_WAKEUP_UART4); } #endif #if SOC_PM_SUPPORT_EXT0_WAKEUP if (wakeup_cause_raw & RTC_EXT0_TRIG_EN) { wakeup_cause |= BIT(ESP_SLEEP_WAKEUP_EXT0); } #endif #if SOC_PM_SUPPORT_EXT1_WAKEUP if (wakeup_cause_raw & RTC_EXT1_TRIG_EN) { wakeup_cause |= BIT(ESP_SLEEP_WAKEUP_EXT1); } #endif #if SOC_PM_SUPPORT_TOUCH_SENSOR_WAKEUP if (wakeup_cause_raw & RTC_TOUCH_TRIG_EN) { wakeup_cause |= BIT(ESP_SLEEP_WAKEUP_TOUCHPAD); } #endif #if SOC_ULP_FSM_SUPPORTED if (wakeup_cause_raw & RTC_ULP_TRIG_EN) { wakeup_cause |= BIT(ESP_SLEEP_WAKEUP_ULP); } #endif #if SOC_PM_SUPPORT_WIFI_WAKEUP if (wakeup_cause_raw & RTC_WIFI_TRIG_EN) { wakeup_cause |= BIT(ESP_SLEEP_WAKEUP_WIFI); } #endif #if SOC_PM_SUPPORT_BT_WAKEUP if (wakeup_cause_raw & RTC_BT_TRIG_EN) { wakeup_cause |= BIT(ESP_SLEEP_WAKEUP_BT); } #endif #if SOC_RISCV_COPROC_SUPPORTED if (wakeup_cause_raw & RTC_COCPU_TRIG_EN) { wakeup_cause |= BIT(ESP_SLEEP_WAKEUP_ULP); } if (wakeup_cause_raw & RTC_COCPU_TRAP_TRIG_EN) { wakeup_cause |= BIT(ESP_SLEEP_WAKEUP_COCPU_TRAP_TRIG); } #endif #if SOC_LP_CORE_SUPPORTED if (wakeup_cause_raw & RTC_LP_CORE_TRIG_EN) { wakeup_cause |= BIT(ESP_SLEEP_WAKEUP_ULP); } if (wakeup_cause_raw & RTC_LP_CORE_TRAP_TRIG_EN) { wakeup_cause |= BIT(ESP_SLEEP_WAKEUP_COCPU_TRAP_TRIG); } #endif #if SOC_LP_VAD_SUPPORTED if (wakeup_cause_raw & RTC_LP_VAD_TRIG_EN) { wakeup_cause |= BIT(ESP_SLEEP_WAKEUP_VAD); } #endif #if SOC_VBAT_SUPPORTED if (wakeup_cause_raw & RTC_VBAT_UNDER_VOLT_TRIG_EN) { wakeup_cause |= BIT(ESP_SLEEP_WAKEUP_VBAT_UNDER_VOLT); } #endif #if SOC_PM_SUPPORT_USB_WAKEUP if (wakeup_cause_raw & RTC_USB_TRIG_EN) { wakeup_cause |= BIT(ESP_SLEEP_WAKEUP_USB); } #endif if (wakeup_cause == 0) { wakeup_cause |= BIT(ESP_SLEEP_WAKEUP_UNDEFINED); } return wakeup_cause; } esp_err_t esp_sleep_pd_config(esp_sleep_pd_domain_t domain, esp_sleep_pd_option_t option) { if (domain >= ESP_PD_DOMAIN_MAX || option > ESP_PD_OPTION_AUTO) { return ESP_ERR_INVALID_ARG; } esp_err_t err = ESP_OK; esp_sleep_enter_critical_safe(); int refs = 0; if (s_config.domain[domain].pd_option == ESP_PD_OPTION_AUTO) { // If domain is currently in auto mode, transition to the new mode directly // - If option is ESP_PD_OPTION_ON: set refs to 1 // - If option is ESP_PD_OPTION_OFF: set refs to 0 // - If option is ESP_PD_OPTION_AUTO: no change in refs s_config.domain[domain].refs = (option == ESP_PD_OPTION_ON) ? 1 : 0; s_config.domain[domain].pd_option = option; } else { if (option == ESP_PD_OPTION_AUTO) { // If switching from manual to auto mode, reset references and return to auto management s_config.domain[domain].refs = 0; s_config.domain[domain].pd_option = option; } else { // Manual mode operations (ESP_PD_OPTION_ON/ESP_PD_OPTION_OFF) // The reference counting implements the following state machine: // - ON operations increment references, only update pd_option when refs transitions from 0 to 1 // - OFF operations decrement references, only update pd_option when refs transitions from 1 to 0 // - This provides symmetric reference counting for resource management if (option == ESP_PD_OPTION_ON) { // Get refs value after incrementing: this ensures pd_option is updated when transitioning // from 0->1 (first request) but not on subsequent increments refs = s_config.domain[domain].refs++; } else if (option == ESP_PD_OPTION_OFF) { // Get refs value after decrementing: this ensures pd_option is updated when transitioning // from 1->0 (last release) but not on intermediate decrements refs = --s_config.domain[domain].refs; } if (refs == 0) { // Only update pd_option when reference count reaches 0, indicating all users have released s_config.domain[domain].pd_option = option; } else if (refs < 0) { // Error case: reference count went negative, which indicates unbalanced ON/OFF calls s_config.domain[domain].refs = 0; err = ESP_ERR_INVALID_STATE; } } } esp_sleep_exit_critical_safe(); if (err == ESP_ERR_INVALID_STATE) { ESP_LOGE(TAG, "Domain is already in ESP_PD_OPTION_OFF state, please check whether the domain pd_option is managed symmetrically."); } return err; } static const char* s_submode2str[] = { [ESP_SLEEP_RTC_USE_RC_FAST_MODE] = "ESP_SLEEP_RTC_USE_RC_FAST_MODE", [ESP_SLEEP_DIG_USE_RC_FAST_MODE] = "ESP_SLEEP_DIG_USE_RC_FAST_MODE", [ESP_SLEEP_USE_ADC_TSEN_MONITOR_MODE] = "ESP_SLEEP_USE_ADC_TSEN_MONITOR_MODE", [ESP_SLEEP_ULTRA_LOW_MODE] = "ESP_SLEEP_ULTRA_LOW_MODE", [ESP_SLEEP_RTC_FAST_USE_XTAL_MODE] = "ESP_SLEEP_RTC_FAST_USE_XTAL_MODE", [ESP_SLEEP_DIG_USE_XTAL_MODE] = "ESP_SLEEP_DIG_USE_XTAL_MODE", [ESP_SLEEP_LP_USE_XTAL_MODE] = "ESP_SLEEP_LP_USE_XTAL_MODE", [ESP_SLEEP_LP_USE_RC_FAST_MODE] = "ESP_SLEEP_LP_USE_RC_FAST_MODE", [ESP_SLEEP_VBAT_POWER_DEEPSLEEP_MODE] = "ESP_SLEEP_VBAT_POWER_DEEPSLEEP_MODE", #if CONFIG_IDF_TARGET_ESP32 [ESP_SLEEP_ANALOG_LOW_POWER_MODE] = "ESP_SLEEP_ANALOG_LOW_POWER_MODE", #endif }; esp_err_t esp_sleep_sub_mode_config(esp_sleep_sub_mode_t mode, bool activate) { if (mode >= ESP_SLEEP_MODE_MAX) { return ESP_ERR_INVALID_ARG; } esp_sleep_enter_critical_safe(); if (activate) { s_sleep_sub_mode_ref_cnt[mode]++; } else { s_sleep_sub_mode_ref_cnt[mode]--; } if (s_sleep_sub_mode_ref_cnt[mode] < 0) { ESP_EARLY_LOGW(TAG, "%s disabled multiple times!! (If this log appears only once after OTA upgrade, it can be ignored.)", s_submode2str[mode]); s_sleep_sub_mode_ref_cnt[mode] = 0; } esp_sleep_exit_critical_safe(); return ESP_OK; } esp_err_t esp_sleep_sub_mode_force_disable(esp_sleep_sub_mode_t mode) { if (mode >= ESP_SLEEP_MODE_MAX) { return ESP_ERR_INVALID_ARG; } esp_sleep_enter_critical_safe(); s_sleep_sub_mode_ref_cnt[mode] = 0; esp_sleep_exit_critical_safe(); return ESP_OK; } int32_t* esp_sleep_sub_mode_dump_config(FILE *stream) { if (stream) { for (uint32_t mode = 0; mode < ESP_SLEEP_MODE_MAX; mode++) { fprintf(stream, LOG_COLOR_I "%s : %s (cnt = %" PRId32 ")\n" LOG_RESET_COLOR, s_submode2str[mode], s_sleep_sub_mode_ref_cnt[mode] ? "ENABLED" : "DISABLED", s_sleep_sub_mode_ref_cnt[mode]); } } return s_sleep_sub_mode_ref_cnt; } /** * The modules in the CPU and modem power domains still depend on the top power domain. * To be safe, the CPU and Modem power domains must also be powered off and saved when * the TOP is powered off. If not power down XTAL, power down TOP is meaningless, and * the XTAL clock control of some chips(esp32c6/esp32h2) depends on the top domain. */ #if SOC_PM_SUPPORT_TOP_PD && SOC_PAU_SUPPORTED FORCE_INLINE_ATTR bool top_domain_pd_allowed(void) { bool top_pd_allowed = true; #if CONFIG_PM_ESP_SLEEP_POWER_DOWN_CPU top_pd_allowed &= cpu_domain_pd_allowed(); #else top_pd_allowed = false; #endif top_pd_allowed &= clock_domain_pd_allowed(); top_pd_allowed &= peripheral_domain_pd_allowed(); #if SOC_PM_SUPPORT_MODEM_PD top_pd_allowed &= modem_domain_pd_allowed(); #endif #if SOC_XTAL_CLOCK_PATH_DEPENDS_ON_TOP_DOMAIN top_pd_allowed &= (s_config.domain[ESP_PD_DOMAIN_XTAL].pd_option != ESP_PD_OPTION_ON); #endif #if CONFIG_IDF_TARGET_ESP32C5 if (!ESP_CHIP_REV_ABOVE(efuse_hal_chip_revision(), 102)) { // ESP32C5 chips lower than v1.2 are not supported to power down the TOP domain top_pd_allowed = false; } #endif return top_pd_allowed; } #endif static SLEEP_FN_ATTR uint32_t get_power_down_flags(void) { // Where needed, convert AUTO options to ON. Later interpret AUTO as OFF. // RTC_SLOW_MEM is needed for the ULP, so keep RTC_SLOW_MEM powered up if ULP // is used and RTC_SLOW_MEM is Auto. // If there is any data placed into .rtc.data or .rtc.bss segments, and // RTC_SLOW_MEM is Auto, keep it powered up as well. #if SOC_PM_SUPPORT_RTC_SLOW_MEM_PD && SOC_ULP_SUPPORTED // Labels are defined in the linker script extern int _rtc_slow_length, _rtc_reserved_length; /** * Compiler considers "(size_t) &_rtc_slow_length > 0" to always be true. * So use a volatile variable to prevent compiler from doing this optimization. */ volatile size_t rtc_slow_mem_used = (size_t)&_rtc_slow_length + (size_t)&_rtc_reserved_length; if ((s_config.domain[ESP_PD_DOMAIN_RTC_SLOW_MEM].pd_option == ESP_PD_OPTION_AUTO) && (rtc_slow_mem_used > 0 || (s_config.wakeup_triggers & RTC_ULP_TRIG_EN))) { s_config.domain[ESP_PD_DOMAIN_RTC_SLOW_MEM].pd_option = ESP_PD_OPTION_ON; } #endif #if SOC_PM_SUPPORT_RTC_FAST_MEM_PD #if !CONFIG_ESP_SYSTEM_ALLOW_RTC_FAST_MEM_AS_HEAP /* RTC_FAST_MEM is needed for deep sleep stub. If RTC_FAST_MEM is Auto, keep it powered on, so that deep sleep stub can run. In the new chip revision, deep sleep stub will be optional, and this can be changed. */ if (s_config.domain[ESP_PD_DOMAIN_RTC_FAST_MEM].pd_option == ESP_PD_OPTION_AUTO) { s_config.domain[ESP_PD_DOMAIN_RTC_FAST_MEM].pd_option = ESP_PD_OPTION_ON; } #else /* If RTC_FAST_MEM is used for heap, force RTC_FAST_MEM to be powered on. */ s_config.domain[ESP_PD_DOMAIN_RTC_FAST_MEM].pd_option = ESP_PD_OPTION_ON; #endif #endif #if SOC_PM_SUPPORT_RTC_PERIPH_PD // RTC_PERIPH is needed for EXT0 wakeup and GPIO wakeup. // If RTC_PERIPH is left auto (EXT0/GPIO aren't enabled), RTC_PERIPH will be powered off by default. if (s_config.domain[ESP_PD_DOMAIN_RTC_PERIPH].pd_option == ESP_PD_OPTION_AUTO) { if (s_config.wakeup_triggers & (RTC_EXT0_TRIG_EN | RTC_GPIO_TRIG_EN)) { s_config.domain[ESP_PD_DOMAIN_RTC_PERIPH].pd_option = ESP_PD_OPTION_ON; } #if CONFIG_IDF_TARGET_ESP32 else if (s_config.wakeup_triggers & (RTC_TOUCH_TRIG_EN | RTC_ULP_TRIG_EN)) { // On ESP32, forcing power up of RTC_PERIPH // prevents ULP timer and touch FSMs from working correctly. s_config.domain[ESP_PD_DOMAIN_RTC_PERIPH].pd_option = ESP_PD_OPTION_OFF; } #endif //CONFIG_IDF_TARGET_ESP32 #if SOC_LP_CORE_SUPPORTED else if (s_config.wakeup_triggers & RTC_LP_CORE_TRIG_EN) { // Need to keep RTC_PERIPH on to allow lp core to wakeup during sleep (e.g. from lp timer) s_config.domain[ESP_PD_DOMAIN_RTC_PERIPH].pd_option = ESP_PD_OPTION_ON; } #endif //CONFIG_IDF_TARGET_ESP32 } #endif // SOC_PM_SUPPORT_RTC_PERIPH_PD /** * VDD_SDIO power domain shall be kept on during the light sleep * when CONFIG_ESP_SLEEP_POWER_DOWN_FLASH is not set and off when it is set. * The application can still force the power domain to remain on by calling * `esp_sleep_pd_config` before getting into light sleep mode. * * In deep sleep mode, the power domain will be turned off, regardless the * value of this field. */ #if SOC_PM_SUPPORT_VDDSDIO_PD if (s_config.domain[ESP_PD_DOMAIN_VDDSDIO].pd_option == ESP_PD_OPTION_AUTO) { #ifndef CONFIG_ESP_SLEEP_POWER_DOWN_FLASH s_config.domain[ESP_PD_DOMAIN_VDDSDIO].pd_option = ESP_PD_OPTION_ON; #endif } #endif #ifdef CONFIG_IDF_TARGET_ESP32 s_config.domain[ESP_PD_DOMAIN_XTAL].pd_option = ESP_PD_OPTION_OFF; #endif // Prepare flags based on the selected options uint32_t pd_flags = 0; #if SOC_PM_SUPPORT_RTC_FAST_MEM_PD if (s_config.domain[ESP_PD_DOMAIN_RTC_FAST_MEM].pd_option != ESP_PD_OPTION_ON) { pd_flags |= RTC_SLEEP_PD_RTC_FAST_MEM; } #endif #if SOC_PM_SUPPORT_RTC_SLOW_MEM_PD if (s_config.domain[ESP_PD_DOMAIN_RTC_SLOW_MEM].pd_option != ESP_PD_OPTION_ON) { pd_flags |= RTC_SLEEP_PD_RTC_SLOW_MEM; } #endif #if SOC_PM_SUPPORT_RTC_PERIPH_PD if (s_config.domain[ESP_PD_DOMAIN_RTC_PERIPH].pd_option != ESP_PD_OPTION_ON) { pd_flags |= RTC_SLEEP_PD_RTC_PERIPH; } #endif #if SOC_PM_SUPPORT_CPU_PD && CONFIG_PM_ESP_SLEEP_POWER_DOWN_CPU && !CONFIG_ESP32P4_SELECTS_REV_LESS_V3 if ((s_config.domain[ESP_PD_DOMAIN_CPU].pd_option != ESP_PD_OPTION_ON) && cpu_domain_pd_allowed()) { pd_flags |= RTC_SLEEP_PD_CPU; } #endif #if SOC_PM_SUPPORT_XTAL32K_PD if (s_config.domain[ESP_PD_DOMAIN_XTAL32K].pd_option != ESP_PD_OPTION_ON) { pd_flags |= PMU_SLEEP_PD_XTAL32K; } #endif #if SOC_PM_SUPPORT_RC32K_PD #if !CONFIG_ESP_CLK_RC32K_NOT_TO_USE if (s_config.domain[ESP_PD_DOMAIN_RC32K].pd_option != ESP_PD_OPTION_ON) { pd_flags |= PMU_SLEEP_PD_RC32K; } #else pd_flags |= PMU_SLEEP_PD_RC32K; #endif #endif #if SOC_PM_SUPPORT_RC_FAST_PD if (s_config.domain[ESP_PD_DOMAIN_RC_FAST].pd_option != ESP_PD_OPTION_ON) { pd_flags |= RTC_SLEEP_PD_INT_8M; } #endif if (s_config.domain[ESP_PD_DOMAIN_XTAL].pd_option != ESP_PD_OPTION_ON) { pd_flags |= RTC_SLEEP_PD_XTAL; } #if SOC_PM_SUPPORT_TOP_PD && SOC_PAU_SUPPORTED if ((s_config.domain[ESP_PD_DOMAIN_TOP].pd_option != ESP_PD_OPTION_ON) && top_domain_pd_allowed()) { pd_flags |= PMU_SLEEP_PD_TOP; } #endif #if SOC_PM_SUPPORT_MODEM_PD && SOC_PAU_SUPPORTED if ((s_config.domain[ESP_PD_DOMAIN_MODEM].pd_option != ESP_PD_OPTION_ON) && modem_domain_pd_allowed() #if SOC_PM_MODEM_RETENTION_BY_REGDMA && clock_domain_pd_allowed() #endif ) { pd_flags |= RTC_SLEEP_PD_MODEM; } #endif #if SOC_PM_SUPPORT_CNNT_PD // The TOP domain depends on the CNNT domain, only after the TOP power domain has been powered off is the CNNT power domain allowed to power down. if (s_config.domain[ESP_PD_DOMAIN_CNNT].pd_option != ESP_PD_OPTION_ON && (pd_flags & PMU_SLEEP_PD_TOP)) { pd_flags |= PMU_SLEEP_PD_CNNT; } #endif #if SOC_PM_SUPPORT_VDDSDIO_PD if (s_config.domain[ESP_PD_DOMAIN_VDDSDIO].pd_option != ESP_PD_OPTION_ON) { pd_flags |= RTC_SLEEP_PD_VDDSDIO; } #endif #if ((defined CONFIG_RTC_CLK_SRC_EXT_CRYS) && (defined CONFIG_RTC_EXT_CRYST_ADDIT_CURRENT) && (SOC_PM_SUPPORT_RTC_PERIPH_PD)) if ((s_config.wakeup_triggers & (RTC_TOUCH_TRIG_EN | RTC_ULP_TRIG_EN)) == 0) { // If enabled EXT1 only and enable the additional current by touch, should be keep RTC_PERIPH power on. pd_flags &= ~RTC_SLEEP_PD_RTC_PERIPH; } #endif #if CONFIG_ESP_SLEEP_SET_FLASH_DPD { uint32_t pd_flags_for_dpd = pd_flags; #if SOC_PM_FLASH_KEEP_POWER_IN_LSLP /* Light sleep never powers down the flash supply on these targets; an app may still set VDDSDIO * domain to OFF manually, and we later strip RTC_SLEEP_PD_VDDSDIO. * Mask the bit when deriving DPD so flash deep power-down is not suppressed. */ pd_flags_for_dpd &= ~RTC_SLEEP_PD_VDDSDIO; #endif if (!(pd_flags_for_dpd & RTC_SLEEP_PD_VDDSDIO)) { // Flash power domain will disable DPD mode. pd_flags |= RTC_SLEEP_FLASH_DPD; } else { ESP_LOGW(TAG, "Flash DPD mode cannot be enabled when VDDSDIO is configured to power down."); } } #endif #if CONFIG_IDF_TARGET_ESP32P4 if (!ESP_CHIP_REV_ABOVE(efuse_hal_chip_revision(), 300)) { if (pd_flags & RTC_SLEEP_PD_VDDSDIO) { ESP_LOGE(TAG, "ESP32P4 chips lower than v1.0 are not allowed to power down the Flash"); } } #endif #if SOC_PM_TOP_DEPENDS_ON_RTC_PERIPH if (!(pd_flags & PMU_SLEEP_PD_TOP)) { // TOP power domain depends on the RTC_PERIPH power domain on ESP32C6 and ESP32H4, RTC_PERIPH should only be disabled when the TOP domain is down. pd_flags &= ~RTC_SLEEP_PD_RTC_PERIPH; } #endif return pd_flags; } static SLEEP_FN_ATTR uint32_t get_sleep_flags(uint32_t sleep_flags, bool deepsleep) { // Override user-configured FOSC power modes. if (s_sleep_sub_mode_ref_cnt[ESP_SLEEP_RTC_USE_RC_FAST_MODE]) { sleep_flags &= ~RTC_SLEEP_PD_INT_8M; } // Override user-configured XTAL power modes. if (s_sleep_sub_mode_ref_cnt[ESP_SLEEP_DIG_USE_XTAL_MODE] && !deepsleep) { sleep_flags &= ~RTC_SLEEP_PD_XTAL; } if (s_sleep_sub_mode_ref_cnt[ESP_SLEEP_DIG_USE_RC_FAST_MODE] && !deepsleep) { sleep_flags &= ~RTC_SLEEP_PD_INT_8M; sleep_flags |= RTC_SLEEP_DIG_USE_8M; } if (s_sleep_sub_mode_ref_cnt[ESP_SLEEP_USE_ADC_TSEN_MONITOR_MODE]) { sleep_flags |= RTC_SLEEP_USE_ADC_TESEN_MONITOR; } if (s_sleep_sub_mode_ref_cnt[ESP_SLEEP_ULTRA_LOW_MODE] == 0) { sleep_flags |= RTC_SLEEP_NO_ULTRA_LOW; } if (s_sleep_sub_mode_ref_cnt[ESP_SLEEP_RTC_FAST_USE_XTAL_MODE]) { sleep_flags |= RTC_SLEEP_XTAL_AS_RTC_FAST; } #if SOC_PMU_SUPPORTED #if CONFIG_IDF_TARGET_ESP32P4 if (s_sleep_sub_mode_ref_cnt[ESP_SLEEP_LP_USE_XTAL_MODE] && !deepsleep) { sleep_flags |= RTC_SLEEP_LP_PERIPH_USE_XTAL; } #endif #if SOC_LP_PERIPHERALS_SUPPORTED if (s_sleep_sub_mode_ref_cnt[ESP_SLEEP_LP_USE_RC_FAST_MODE]) { sleep_flags &= ~RTC_SLEEP_PD_INT_8M; sleep_flags |= RTC_SLEEP_LP_PERIPH_USE_RC_FAST; } #endif #endif #if SOC_VBAT_SUPPORTED if (s_sleep_sub_mode_ref_cnt[ESP_SLEEP_VBAT_POWER_DEEPSLEEP_MODE] && deepsleep) { sleep_flags |= RTC_SLEEP_POWER_BY_VBAT; } #endif #if CONFIG_IDF_TARGET_ESP32 if (s_sleep_sub_mode_ref_cnt[ESP_SLEEP_ANALOG_LOW_POWER_MODE]) { sleep_flags |= RTC_SLEEP_WITH_LOWPOWER_ANALOG; } #endif #ifdef CONFIG_ESP_SLEEP_RTC_BUS_ISO_WORKAROUND if (!deepsleep) { sleep_flags &= ~RTC_SLEEP_PD_RTC_PERIPH; } #endif #if CONFIG_ESP_SLEEP_ENABLE_RTC_WDT_IN_SLEEP && SOC_RTC_WDT_SUPPORTED if (s_config.wakeup_triggers & RTC_TIMER_TRIG_EN) { sleep_flags |= RTC_SLEEP_USE_RTC_WDT; } else { sleep_flags &= ~RTC_SLEEP_USE_RTC_WDT; } #endif #if CONFIG_IDF_TARGET_ESP32P4 /* Due to esp32p4 eco0 hardware bug, if LP peripheral power domain is powerdowned in sleep, there will be a possibility of triggering the EFUSE_CRC reset, so disable the power-down of this power domain on lightsleep for ECO0 version. */ if (!ESP_CHIP_REV_ABOVE(efuse_hal_chip_revision(), 1)) { sleep_flags &= ~RTC_SLEEP_PD_RTC_PERIPH; } /* The LDO VO1 channel that powers the Flash on esp32p4(