Merge branch 'feat/support_s31_sleep_features' into 'master'

feat(esp_hw_support): support esp32s31 lowpower features

Closes IDF-5660, IDF-14643, IDF-14645, IDF-14647, IDF-14648, IDF-14784, PM-708, and PM-714

See merge request espressif/esp-idf!47216
This commit is contained in:
Jiang Jiang Jian
2026-05-07 19:22:58 +08:00
125 changed files with 2678 additions and 431 deletions

View File

@@ -24,6 +24,7 @@
#include "soc/rtc_wdt_reg.h"
#include "hal/rwdt_ll.h"
#endif
#include "hal/gpio_ll.h"
#include "soc/pmu_reg.h"
#include "hal/regi2c_ctrl_ll.h"
#include "hal/modem_lpcon_ll.h"
@@ -35,8 +36,13 @@ ESP_LOG_ATTR_TAG(TAG, "boot.esp32s31");
static inline void bootloader_hardware_init(void)
{
/* Disable RF pll by default */
REG_SET_FIELD(PMU_RF_PWC_REG, PMU_XPD_RF_CIRCUIT, 0xFFFF);
/* GPIO 41 is not bonded out to the package, Isolate it to suppress
* floating leakage.*/
gpio_ll_input_disable(&GPIO, 41);
gpio_ll_output_disable(&GPIO, 41);
gpio_ll_pullup_dis(&GPIO, 41);
gpio_ll_pulldown_dis(&GPIO, 41);
gpio_ll_func_sel(&GPIO, 41, PIN_FUNC_GPIO);
modem_lpcon_ll_enable_bus_clock(true);

View File

@@ -28,6 +28,7 @@
#include "gdma_priv.h"
#include "esp_memory_utils.h"
#include "esp_sleep.h"
#define GDMA_INVALID_PERIPH_TRIG (0x3F)
#define SEARCH_REQUEST_RX_CHANNEL (1 << 0)
@@ -87,11 +88,15 @@ static esp_err_t do_allocate_gdma_channel(const gdma_channel_search_info_t *sear
TAG, "invalid interrupt priority:%d", config->intr_priority);
}
#if CONFIG_PM_POWER_DOWN_PERIPHERAL_IN_LIGHT_SLEEP && SOC_GDMA_SUPPORT_SLEEP_RETENTION
#if CONFIG_PM_POWER_DOWN_PERIPHERAL_IN_LIGHT_SLEEP
#if SOC_GDMA_SUPPORT_SLEEP_RETENTION
// retention module is per GDMA pair, before we allocate the pair object, some common registers are already configured in "hal_init"
// if a light sleep happens and powers off the gdma module, those registers will get lost
// to work around it, we can acquire a power lock first, before any register configuration
sleep_retention_power_lock_acquire();
#else
esp_sleep_pd_config(ESP_PD_DOMAIN_TOP, ESP_PD_OPTION_ON); //IDF-15640
#endif
#endif
// Determine search code based on which channels are requested
@@ -237,8 +242,12 @@ err:
free(alloc_rx_channel);
}
#if CONFIG_PM_POWER_DOWN_PERIPHERAL_IN_LIGHT_SLEEP && SOC_GDMA_SUPPORT_SLEEP_RETENTION
#if CONFIG_PM_POWER_DOWN_PERIPHERAL_IN_LIGHT_SLEEP
#if SOC_GDMA_SUPPORT_SLEEP_RETENTION
sleep_retention_power_lock_release();
#else
esp_sleep_pd_config(ESP_PD_DOMAIN_TOP, ESP_PD_OPTION_AUTO); //IDF-15640
#endif
#endif
return ret;
}
@@ -874,9 +883,13 @@ static esp_err_t gdma_del_tx_channel(gdma_channel_t *dma_channel)
free(tx_chan);
ESP_LOGV(TAG, "del tx channel (%d,%d)", group_id, pair_id);
#if CONFIG_PM_POWER_DOWN_PERIPHERAL_IN_LIGHT_SLEEP && SOC_GDMA_SUPPORT_SLEEP_RETENTION
#if CONFIG_PM_POWER_DOWN_PERIPHERAL_IN_LIGHT_SLEEP
#if SOC_GDMA_SUPPORT_SLEEP_RETENTION
// release sleep retention lock
gdma_release_sleep_retention(pair);
#else
esp_sleep_pd_config(ESP_PD_DOMAIN_TOP, ESP_PD_OPTION_AUTO); //IDF-15640
#endif
#endif
// release pair handle (will free pair if both channels are deleted)
gdma_try_free_pair_handle(pair);
@@ -909,9 +922,13 @@ static esp_err_t gdma_del_rx_channel(gdma_channel_t *dma_channel)
free(rx_chan);
ESP_LOGV(TAG, "del rx channel (%d,%d)", group_id, pair_id);
#if CONFIG_PM_POWER_DOWN_PERIPHERAL_IN_LIGHT_SLEEP && SOC_GDMA_SUPPORT_SLEEP_RETENTION
#if CONFIG_PM_POWER_DOWN_PERIPHERAL_IN_LIGHT_SLEEP
#if SOC_GDMA_SUPPORT_SLEEP_RETENTION
// release sleep retention lock
gdma_release_sleep_retention(pair);
#else
esp_sleep_pd_config(ESP_PD_DOMAIN_TOP, ESP_PD_OPTION_AUTO); //IDF-15640
#endif
#endif
// release pair handle (will free pair if both channels are deleted)
gdma_try_free_pair_handle(pair);

View File

@@ -1143,9 +1143,9 @@ esp_err_t gpio_dump_io_configuration(FILE *out_stream, uint64_t io_bit_mask)
fprintf(out_stream, "IO[%"PRIu32"]%s -\n", gpio_num, esp_gpio_is_reserved(BIT64(gpio_num)) ? " **RESERVED**" : "");
fprintf(out_stream, " Pullup: %d, Pulldown: %d, DriveCap: %"PRIu32"\n", io_config.pu, io_config.pd, (uint32_t)io_config.drv);
fprintf(out_stream, " InputEn: %d, OutputEn: %s%s, OpenDrain: %d\n", io_config.ie, oe_str, ((io_config.fun_sel == PIN_FUNC_GPIO) && (io_config.oe_inv)) ? " (inversed)" : "", io_config.od);
fprintf(out_stream, " FuncSel: %"PRIu32" (%s)\n", io_config.fun_sel, (io_config.fun_sel == PIN_FUNC_GPIO) ? "GPIO" : "IOMUX");
fprintf(out_stream, " FuncSel: %"PRIu32" (%s)\n", (uint32_t)io_config.fun_sel, (io_config.fun_sel == PIN_FUNC_GPIO) ? "GPIO" : "IOMUX");
if (io_config.fun_sel == PIN_FUNC_GPIO) {
fprintf(out_stream, " GPIO Matrix SigOut ID: %"PRIu32"%s\n", io_config.sig_out, (io_config.sig_out == SIG_GPIO_OUT_IDX) ? " (simple GPIO output)" : "");
fprintf(out_stream, " GPIO Matrix SigOut ID: %"PRIu32"%s\n", (uint32_t)io_config.sig_out, (io_config.sig_out == SIG_GPIO_OUT_IDX) ? " (simple GPIO output)" : "");
}
if (io_config.ie && io_config.fun_sel == PIN_FUNC_GPIO) {
uint32_t cnt = 0;

View File

@@ -9,6 +9,7 @@
#include "driver/gptimer.h"
#include "gptimer_priv.h"
#include "esp_memory_utils.h"
#include "esp_sleep.h"
static void gptimer_default_isr(void *args);
@@ -155,6 +156,10 @@ esp_err_t gptimer_new_timer(const gptimer_config_t *config, gptimer_handle_t *re
#if GPTIMER_USE_RETENTION_LINK
gptimer_create_retention_module(timer);
#endif // GPTIMER_USE_RETENTION_LINK
} else {
#if !SOC_TIMER_SUPPORT_SLEEP_RETENTION && CONFIG_PM_POWER_DOWN_PERIPHERAL_IN_LIGHT_SLEEP
esp_sleep_pd_config(ESP_PD_DOMAIN_TOP, ESP_PD_OPTION_ON); //IDF-15641
#endif
}
// initialize HAL layer

View File

@@ -28,6 +28,7 @@
#include "hal/i2c_ll.h"
#include "hal/i2c_periph.h"
#include "driver/i2c_master.h"
#include "esp_sleep.h"
#include "i2c_private.h"
static const char *TAG = "i2c.master";
@@ -1045,6 +1046,9 @@ esp_err_t i2c_new_master_bus(const i2c_master_bus_config_t *bus_config, i2c_mast
#if !SOC_I2C_SUPPORT_SLEEP_RETENTION
ESP_RETURN_ON_FALSE(bus_config->flags.allow_pd == 0, ESP_ERR_NOT_SUPPORTED, TAG, "not able to power down in light sleep");
#if SOC_PM_SUPPORT_TOP_PD
esp_sleep_pd_config(ESP_PD_DOMAIN_TOP, ESP_PD_OPTION_ON); //IDF-15642
#endif
#endif // SOC_I2C_SUPPORT_SLEEP_RETENTION
i2c_master = heap_caps_calloc(1, sizeof(i2c_master_bus_t) + 20 * sizeof(i2c_transaction_t), I2C_MEM_ALLOC_CAPS);

View File

@@ -43,8 +43,8 @@ static void s_test_i2s_enter_light_sleep(int sec, bool allow_power_down)
printf("Woke up from light sleep\n");
TEST_ASSERT_EQUAL(0, sleep_ctx.sleep_request_result);
#if SOC_HAS(PAU)
// check if the power domain also is powered down
#if SOC_HAS(PAU) && SOC_GDMA_SUPPORT_SLEEP_RETENTION
// check if the power domain also is powered down (if GDMA retention is not supported, TOP pd will be rejected)
TEST_ASSERT_EQUAL(allow_power_down ? PMU_SLEEP_PD_TOP : 0, (sleep_ctx.sleep_flags) & PMU_SLEEP_PD_TOP);
#endif
esp_sleep_set_sleep_context(NULL);

View File

@@ -26,6 +26,8 @@
extern const uint8_t image_esp1080_jpg_start[] asm("_binary_esp1080_jpg_start");
extern const uint8_t image_esp1080_jpg_end[] asm("_binary_esp1080_jpg_end");
#define JPEG_SLEEP_RETENTION_TIMEOUT_MS 50
#if SOC_LIGHT_SLEEP_SUPPORTED && CONFIG_PM_ENABLE
static void test_jpeg_sleep_retention(bool allow_pd)
{
@@ -33,7 +35,7 @@ static void test_jpeg_sleep_retention(bool allow_pd)
jpeg_decode_engine_cfg_t decode_eng_cfg = {
.intr_priority = 0,
.timeout_ms = 40,
.timeout_ms = JPEG_SLEEP_RETENTION_TIMEOUT_MS,
.flags = {
.allow_pd = allow_pd,
},

View File

@@ -18,6 +18,7 @@
#include "hal/ledc_hal.h"
#include "driver/ledc.h"
#include "esp_private/esp_sleep_internal.h"
#include "esp_sleep.h"
#include "esp_private/periph_ctrl.h"
#include "driver/gpio.h"
#include "esp_private/gpio.h"
@@ -877,6 +878,9 @@ esp_err_t ledc_channel_config(const ledc_channel_config_t *ledc_conf)
LEDC_ARG_CHECK(ledc_conf->sleep_mode < LEDC_SLEEP_MODE_INVALID, "sleep_mode");
#if !SOC_LEDC_SUPPORT_SLEEP_RETENTION
ESP_RETURN_ON_FALSE(ledc_conf->sleep_mode != LEDC_SLEEP_MODE_NO_ALIVE_ALLOW_PD, ESP_ERR_NOT_SUPPORTED, LEDC_TAG, "register back up is not supported");
#if SOC_PM_SUPPORT_TOP_PD
esp_sleep_pd_config(ESP_PD_DOMAIN_TOP, ESP_PD_OPTION_ON); //IDF-15643
#endif
#endif
esp_err_t ret = ESP_OK;

View File

@@ -10,6 +10,7 @@
#include "esp_memory_utils.h"
#include "driver/mcpwm_cap.h"
#include "driver/gpio.h"
#include "esp_sleep.h"
#include "esp_private/gpio.h"
static void mcpwm_capture_default_isr(void *args);
@@ -73,6 +74,9 @@ esp_err_t mcpwm_new_capture_timer(const mcpwm_capture_timer_config_t *config, mc
#if !SOC_MCPWM_SUPPORT_SLEEP_RETENTION
ESP_RETURN_ON_FALSE(config->flags.allow_pd == 0, ESP_ERR_NOT_SUPPORTED, TAG, "register back up is not supported");
#if SOC_PM_SUPPORT_TOP_PD
esp_sleep_pd_config(ESP_PD_DOMAIN_TOP, ESP_PD_OPTION_ON); //IDF-15644
#endif
#endif // SOC_MCPWM_SUPPORT_SLEEP_RETENTION
cap_timer = heap_caps_calloc(1, sizeof(mcpwm_cap_timer_t), MCPWM_MEM_ALLOC_CAPS);

View File

@@ -8,6 +8,7 @@
#include "esp_memory_utils.h"
#include "hal/mcpwm_ll.h"
#include "driver/mcpwm_timer.h"
#include "esp_sleep.h"
#include "esp_private/mcpwm.h"
static void mcpwm_timer_default_isr(void *args);
@@ -83,6 +84,9 @@ esp_err_t mcpwm_new_timer(const mcpwm_timer_config_t *config, mcpwm_timer_handle
#if !SOC_MCPWM_SUPPORT_SLEEP_RETENTION
ESP_RETURN_ON_FALSE(config->flags.allow_pd == 0, ESP_ERR_NOT_SUPPORTED, TAG, "register back up is not supported");
#if SOC_PM_SUPPORT_TOP_PD
esp_sleep_pd_config(ESP_PD_DOMAIN_TOP, ESP_PD_OPTION_ON); //IDF-15644
#endif
#endif // SOC_MCPWM_SUPPORT_SLEEP_RETENTION
timer = heap_caps_calloc(1, sizeof(mcpwm_timer_t), MCPWM_MEM_ALLOC_CAPS);

View File

@@ -10,6 +10,7 @@
#include "driver/gpio.h"
#include "driver/parlio_rx.h"
#include "parlio_priv.h"
#include "esp_sleep.h"
#define ALIGN_UP(num, align) (((num) + ((align) - 1)) & ~((align) - 1))
@@ -614,6 +615,9 @@ esp_err_t parlio_new_rx_unit(const parlio_rx_unit_config_t *config, parlio_rx_un
#if !SOC_PARLIO_SUPPORT_SLEEP_RETENTION
ESP_RETURN_ON_FALSE(config->flags.allow_pd == 0, ESP_ERR_NOT_SUPPORTED, TAG, "register back up is not supported");
#if SOC_PM_SUPPORT_TOP_PD
esp_sleep_pd_config(ESP_PD_DOMAIN_TOP, ESP_PD_OPTION_ON); //IDF-15645
#endif
#endif // SOC_PARLIO_SUPPORT_SLEEP_RETENTION
/* Allocate unit memory */

View File

@@ -9,6 +9,7 @@
#include "driver/gpio.h"
#include "driver/parlio_tx.h"
#include "parlio_priv.h"
#include "esp_sleep.h"
#if SOC_PARLIO_TX_SUPPORT_LOOP_TRANSMISSION
static bool parlio_tx_gdma_eof_callback(gdma_channel_handle_t dma_chan, gdma_event_data_t *event_data, void *user_data);
@@ -273,6 +274,9 @@ esp_err_t parlio_new_tx_unit(const parlio_tx_unit_config_t *config, parlio_tx_un
#if !SOC_PARLIO_SUPPORT_SLEEP_RETENTION
ESP_RETURN_ON_FALSE(config->flags.allow_pd == 0, ESP_ERR_NOT_SUPPORTED, TAG, "register back up is not supported");
#if SOC_PM_SUPPORT_TOP_PD
esp_sleep_pd_config(ESP_PD_DOMAIN_TOP, ESP_PD_OPTION_ON); //IDF-15645
#endif
#endif // SOC_PARLIO_SUPPORT_SLEEP_RETENTION
// allocate unit from internal memory because it contains atomic member

View File

@@ -125,7 +125,7 @@ static void test_pcnt_sleep_retention(void)
TEST_ESP_OK(pcnt_unit_get_count(units[i], &count_value));
TEST_ASSERT_EQUAL(20, count_value);
// check the register value
reg_value = pcnt_ll_get_count(&PCNT, i);
reg_value = pcnt_ll_get_count(PCNT_LL_GET_HW(unit_config.group_id), i);
TEST_ASSERT_EQUAL(20, reg_value);
}

View File

@@ -10,6 +10,7 @@
#include "driver/gpio.h"
#include "driver/rmt_rx.h"
#include "rmt_private.h"
#include "esp_sleep.h"
static esp_err_t rmt_del_rx_channel(rmt_channel_handle_t channel);
static esp_err_t rmt_rx_demodulate_carrier(rmt_channel_handle_t channel, const rmt_carrier_config_t *config);
@@ -206,6 +207,9 @@ esp_err_t rmt_new_rx_channel(const rmt_rx_channel_config_t *config, rmt_channel_
#if !SOC_RMT_SUPPORT_SLEEP_RETENTION
ESP_RETURN_ON_FALSE(config->flags.allow_pd == 0, ESP_ERR_NOT_SUPPORTED, TAG, "not able to power down in light sleep");
#if SOC_PM_SUPPORT_TOP_PD
esp_sleep_pd_config(ESP_PD_DOMAIN_TOP, ESP_PD_OPTION_ON); //IDF-15646
#endif
#endif // SOC_RMT_SUPPORT_SLEEP_RETENTION
// allocate channel memory from internal memory because it contains atomic variable

View File

@@ -11,6 +11,7 @@
#include "driver/gpio.h"
#include "driver/rmt_tx.h"
#include "rmt_private.h"
#include "esp_sleep.h"
struct rmt_sync_manager_t {
rmt_group_t *group; // which group the synchro belongs to
@@ -273,6 +274,9 @@ esp_err_t rmt_new_tx_channel(const rmt_tx_channel_config_t *config, rmt_channel_
#if !SOC_RMT_SUPPORT_SLEEP_RETENTION
ESP_RETURN_ON_FALSE(config->flags.allow_pd == 0, ESP_ERR_NOT_SUPPORTED, TAG, "not able to power down in light sleep");
#if SOC_PM_SUPPORT_TOP_PD
esp_sleep_pd_config(ESP_PD_DOMAIN_TOP, ESP_PD_OPTION_ON); //IDF-15646
#endif
#endif // SOC_RMT_SUPPORT_SLEEP_RETENTION
// allocate channel memory from internal memory because it contains atomic variable

View File

@@ -18,6 +18,7 @@
#include "esp_err.h"
#include "esp_heap_caps.h"
#include "esp_log.h"
#include "esp_sleep.h"
#include "esp_check.h"
#include "esp_pm.h"
#include "esp_clk_tree.h"
@@ -276,6 +277,9 @@ esp_err_t sdm_new_channel(const sdm_config_t *config, sdm_channel_handle_t *ret_
[[maybe_unused]] bool allow_pd = config->flags.allow_pd == 1;
#if !SOC_SDM_SUPPORT_SLEEP_RETENTION
ESP_RETURN_ON_FALSE(allow_pd == false, ESP_ERR_NOT_SUPPORTED, TAG, "not able to power down in light sleep");
#if SOC_PM_SUPPORT_TOP_PD
esp_sleep_pd_config(ESP_PD_DOMAIN_TOP, ESP_PD_OPTION_ON); //IDF-15647
#endif
#endif // SOC_SDM_SUPPORT_SLEEP_RETENTION
// allocate channel memory from internal memory because it contains atomic variable

View File

@@ -966,7 +966,7 @@ TEST_CASE("test_spi_slave_hd_sleep_retention", "[spi]")
printf("Waked up!\n");
// check if the sleep happened as expected
TEST_ASSERT_EQUAL(0, sleep_ctx.sleep_request_result);
#if SOC_SPI_SUPPORT_SLEEP_RETENTION && CONFIG_PM_POWER_DOWN_PERIPHERAL_IN_LIGHT_SLEEP
#if SOC_SPI_SUPPORT_SLEEP_RETENTION && SOC_GDMA_SUPPORT_SLEEP_RETENTION && CONFIG_PM_POWER_DOWN_PERIPHERAL_IN_LIGHT_SLEEP
// check if the power domain also is powered down
TEST_ASSERT_EQUAL((bus_cfg.flags & SPICOMMON_BUSFLAG_SLP_ALLOW_PD) ? PMU_SLEEP_PD_TOP : 0, (sleep_ctx.sleep_flags) & PMU_SLEEP_PD_TOP);
#endif
@@ -1044,7 +1044,7 @@ TEST_CASE("test_spi_slave_hd_append_sleep_retention", "[spi]")
printf("Waked up!\n");
// check if the sleep happened as expected
TEST_ASSERT_EQUAL(0, sleep_ctx.sleep_request_result);
#if SOC_SPI_SUPPORT_SLEEP_RETENTION && CONFIG_PM_POWER_DOWN_PERIPHERAL_IN_LIGHT_SLEEP
#if SOC_SPI_SUPPORT_SLEEP_RETENTION && SOC_GDMA_SUPPORT_SLEEP_RETENTION && CONFIG_PM_POWER_DOWN_PERIPHERAL_IN_LIGHT_SLEEP
// check if the power domain also is powered down
TEST_ASSERT_EQUAL((bus_cfg.flags & SPICOMMON_BUSFLAG_SLP_ALLOW_PD) ? PMU_SLEEP_PD_TOP : 0, (sleep_ctx.sleep_flags) & PMU_SLEEP_PD_TOP);
#endif

View File

@@ -145,6 +145,9 @@ esp_err_t temperature_sensor_install(const temperature_sensor_config_t *tsens_co
#if !SOC_TEMPERATURE_SENSOR_SUPPORT_SLEEP_RETENTION
ESP_RETURN_ON_FALSE(tsens_config->flags.allow_pd == 0, ESP_ERR_NOT_SUPPORTED, TAG, "not able to power down in light sleep");
#if SOC_PM_SUPPORT_TOP_PD
esp_sleep_pd_config(ESP_PD_DOMAIN_TOP, ESP_PD_OPTION_ON); //IDF-15648
#endif
#endif // SOC_TEMPERATURE_SENSOR_SUPPORT_SLEEP_RETENTION
#if SOC_TEMPERATURE_SENSOR_SUPPORT_SLEEP_RETENTION && !SOC_TEMPERATURE_SENSOR_UNDER_PD_TOP_DOMAIN

View File

@@ -12,6 +12,7 @@
#include "twai_private.h"
#include "hal/twai_periph.h"
#include "hal/twai_hal.h"
#include "esp_sleep.h"
#if SOC_HAS(TWAI_FD)
#include "hal/twaifd_ll.h"
#endif
@@ -664,6 +665,9 @@ esp_err_t twai_new_node_onchip(const twai_onchip_node_config_t *node_config, twa
ESP_RETURN_ON_FALSE(!node_config->intr_priority || (BIT(node_config->intr_priority) & ESP_INTR_FLAG_LOWMED), ESP_ERR_INVALID_ARG, TAG, "Invalid intr_priority level");
#if !SOC_TWAI_SUPPORT_SLEEP_RETENTION
ESP_RETURN_ON_FALSE(!node_config->flags.sleep_allow_pd, ESP_ERR_NOT_SUPPORTED, TAG, "sleep retention is not supported on this target");
#if SOC_PM_SUPPORT_TOP_PD
esp_sleep_pd_config(ESP_PD_DOMAIN_TOP, ESP_PD_OPTION_ON); //IDF-15649
#endif
#endif
// Allocate TWAI node from internal memory because it contains atomic variable
twai_onchip_ctx_t *node = heap_caps_calloc(1, sizeof(twai_onchip_ctx_t) + twai_hal_get_mem_requirment(), MALLOC_CAP_INTERNAL | MALLOC_CAP_8BIT);

View File

@@ -28,6 +28,7 @@
#include "esp_private/gpio.h"
#include "esp_private/esp_gpio_reserve.h"
#include "esp_private/periph_ctrl.h"
#include "esp_sleep.h"
#include "esp_private/sleep_retention.h"
#include "esp_clk_tree.h"
#include "esp_rom_gpio.h"
@@ -1067,6 +1068,9 @@ esp_err_t uart_param_config(uart_port_t uart_num, const uart_config_t *uart_conf
bool allow_pd __attribute__((unused)) = (uart_config->flags.allow_pd || uart_config->flags.backup_before_sleep);
#if !SOC_UART_SUPPORT_SLEEP_RETENTION
ESP_RETURN_ON_FALSE(allow_pd == 0, ESP_ERR_NOT_SUPPORTED, UART_TAG, "not able to power down in light sleep");
#if SOC_PM_SUPPORT_TOP_PD
esp_sleep_pd_config(ESP_PD_DOMAIN_TOP, ESP_PD_OPTION_ON); //IDF-15650
#endif
#endif
uart_module_enable(uart_num);

View File

@@ -36,7 +36,7 @@
#elif CONFIG_IDF_TARGET_ESP32C61
#define DEFAULT_UART1_TX_IO_NUM GPIO_NUM_4
#define DEFAULT_UART1_RX_IO_NUM GPIO_NUM_5
#elif CONFIG_IDF_TARGET_ESP32C5
#elif CONFIG_IDF_TARGET_ESP32C5 || CONFIG_IDF_TARGET_ESP32S31
#define DEFAULT_UART1_TX_IO_NUM GPIO_NUM_2
#define DEFAULT_UART1_RX_IO_NUM GPIO_NUM_3
#elif CONFIG_IDF_TARGET_ESP32H21

View File

@@ -55,11 +55,11 @@ TEST_CASE("uart restored correctly after auto light sleep", "[uart][hp-uart-only
// Ensure UART is fully idle before starting loopback RX/TX test
TEST_ESP_OK(uart_wait_tx_done(uart_num, portMAX_DELAY));
vTaskDelay(pdMS_TO_TICKS(20)); // make sure last byte has flushed from TX FIFO
TEST_ESP_OK(uart_flush_input(uart_num));
for (int i = 0; i < 5; i++) {
char tx_data[20] = {0};
char rx_data[20] = {0};
TEST_ESP_OK(uart_flush_input(uart_num));
int len = sprintf(tx_data, "Hello World %d!\n", i);
uart_write_bytes(uart_num, tx_data, len);
int size = 0;
@@ -97,6 +97,7 @@ TEST_CASE("uart restored correctly after manually enter light sleep", "[uart][hp
for (int i = 0; i < 5; i++) {
char tx_data[20] = {0};
char rx_data[20] = {0};
TEST_ESP_OK(uart_flush_input(uart_num));
int len = sprintf(tx_data, "Hello World %d!\n", i);
uart_write_bytes(uart_num, tx_data, len);
int size = uart_read_bytes(uart_num, rx_data, len, pdMS_TO_TICKS(20));

View File

@@ -0,0 +1,38 @@
/*
* SPDX-FileCopyrightText: 2026 Espressif Systems (Shanghai) CO LTD
*
* SPDX-License-Identifier: Apache-2.0
*/
#pragma once
#include <stdint.h>
#include <stdbool.h>
#include "esp_attr.h"
#include "soc/soc.h"
#include "soc/reset_reasons.h"
#include "soc/lp_clkrst_struct.h"
#include "soc/lp_clkrst_reg.h"
#ifdef __cplusplus
extern "C" {
#endif
/**
* Enable or disable the clock gate for rtc fast to lp periph
* @param enable Enable / disable
*/
FORCE_INLINE_ATTR void _clk_gate_ll_rtc_fast_to_lp_periph_en(bool enable)
{
LP_CLKRST.root_clk_conf.fosc_clk_force_on = enable;
}
/// use a macro to wrap the function, force the caller to use it in a critical section
/// the critical section needs to declare the __DECLARE_RCC_ATOMIC_ENV variable in advance
#define clk_gate_ll_rtc_fast_to_lp_periph_en(...) do { \
(void)__DECLARE_RCC_ATOMIC_ENV; \
_clk_gate_ll_rtc_fast_to_lp_periph_en(__VA_ARGS__); \
} while(0)
#ifdef __cplusplus
}
#endif

View File

@@ -153,6 +153,7 @@ static inline __attribute__((always_inline)) void clk_ll_mpll_enable(void)
SET_PERI_REG_MASK(PMU_IMM_HP_CK_POWER_1_REG, PMU_TIE_HIGH_GLOBAL_MPLL_ICG) ;
SET_PERI_REG_MASK(PMU_IMM_HP_CK_POWER_1_REG, PMU_TIE_HIGH_XPD_MPLL | PMU_TIE_HIGH_XPD_MPLL_I2C);
SET_PERI_REG_MASK(HP_ALIVE_SYS_HP_CLK_CTRL_REG, HP_ALIVE_SYS_HP_MPLL_500M_CLK_EN);
SET_PERI_REG_MASK(PMU_HP_ACTIVE_HP_CK_POWER_REG, PMU_HP_ACTIVE_XPD_MPLL_I2C | PMU_HP_ACTIVE_XPD_MPLL);
}
/**
@@ -163,6 +164,7 @@ static inline __attribute__((always_inline)) void clk_ll_mpll_disable(void)
SET_PERI_REG_MASK(PMU_IMM_HP_CK_POWER_1_REG, PMU_TIE_LOW_GLOBAL_MPLL_ICG) ;
SET_PERI_REG_MASK(PMU_IMM_HP_CK_POWER_1_REG, PMU_TIE_LOW_XPD_MPLL | PMU_TIE_LOW_XPD_MPLL_I2C);
CLEAR_PERI_REG_MASK(HP_ALIVE_SYS_HP_CLK_CTRL_REG, HP_ALIVE_SYS_HP_MPLL_500M_CLK_EN);
CLEAR_PERI_REG_MASK(PMU_HP_ACTIVE_HP_CK_POWER_REG, PMU_HP_ACTIVE_XPD_MPLL_I2C | PMU_HP_ACTIVE_XPD_MPLL);
}
/**

View File

@@ -57,6 +57,7 @@ extern "C" {
* @param gpio_num GPIO number
* @param[out] io_config Pointer to the structure that saves the specific IO configuration
*/
__attribute__((always_inline))
static inline void gpio_ll_get_io_config(gpio_dev_t *hw, uint32_t gpio_num, gpio_io_config_t *io_config)
{
uint32_t bit_shift = (gpio_num < 32) ? gpio_num : (gpio_num - 32);
@@ -74,6 +75,79 @@ static inline void gpio_ll_get_io_config(gpio_dev_t *hw, uint32_t gpio_num, gpio
io_config->slp_sel = IO_MUX.gpio[gpio_num].slp_sel;
}
/**
* @brief Get a 64-bit mask of all digital GPIOs that are currently held.
* Reads the two hold control registers once, avoiding per-pin register access.
*
* @param hw Peripheral GPIO hardware instance address (unused, hold regs are in LP_SYS).
* @return Bitmask where bit N is set if GPIO N is held. Only digital IO bits are meaningful.
*/
__attribute__((always_inline))
static inline uint64_t gpio_ll_get_digital_gpio_hold_mask(gpio_dev_t *hw)
{
(void)hw;
uint32_t hold0 = LP_SYS.hp_gpio_o_hold_ctrl0.hp_gpio_0_hold_ctrl0;
uint32_t hold1 = LP_SYS.hp_gpio_o_hold_ctrl1.hp_gpio_0_hold_ctrl1;
return ((uint64_t)hold0 << SOC_RTCIO_PIN_COUNT) |
((uint64_t)hold1 << (32 + SOC_RTCIO_PIN_COUNT));
}
/**
* @brief Backup IOMUX pad configuration for sleep isolate.
* Only fills pu, pd, ie, fun_sel in io_config from a single IOMUX register read.
*
* @param gpio_num GPIO number
* @param[out] io_config Pointer to the IO configuration structure
*/
__attribute__((always_inline))
static inline void gpio_ll_backup_pad_config_for_sleep_isolate(uint32_t gpio_num, gpio_io_config_t *io_config)
{
uint32_t iomux_reg_val = IO_MUX.gpio[gpio_num].val;
io_config->pu = (iomux_reg_val & FUN_PU_M) >> FUN_PU_S;
io_config->pd = (iomux_reg_val & FUN_PD_M) >> FUN_PD_S;
io_config->ie = (iomux_reg_val & FUN_IE_M) >> FUN_IE_S;
io_config->fun_sel = (iomux_reg_val & MCU_SEL_M) >> MCU_SEL_S;
}
/**
* @brief Set pad configuration for sleep isolate with minimal register writes.
* Reads pu, pd, ie, oe, fun_sel from io_config and writes them to hardware registers
* in a single IOMUX read-modify-write plus one GPIO enable write.
*
* @param gpio_num GPIO number
* @param io_config Pointer to the IO configuration structure
*/
__attribute__((always_inline))
static inline void gpio_ll_set_pad_config_for_sleep_isolate(uint32_t gpio_num, gpio_io_config_t *io_config)
{
uint32_t iomux_reg_val = IO_MUX.gpio[gpio_num].val;
iomux_reg_val &= ~(FUN_PU_M | FUN_PD_M | FUN_IE_M | MCU_SEL_M);
if (io_config->pu) {
iomux_reg_val |= FUN_PU_M;
}
if (io_config->pd) {
iomux_reg_val |= FUN_PD_M;
}
if (io_config->ie) {
iomux_reg_val |= FUN_IE_M;
}
iomux_reg_val |= (io_config->fun_sel << MCU_SEL_S) & MCU_SEL_M;
IO_MUX.gpio[gpio_num].val = iomux_reg_val;
if (io_config->oe) {
if (gpio_num < 32) {
GPIO.enable_w1ts.enable_w1ts = (0x1 << gpio_num);
} else {
GPIO.enable1_w1ts.enable1_w1ts = (0x1 << (gpio_num - 32));
}
} else {
if (gpio_num < 32) {
GPIO.enable_w1tc.enable_w1tc = (0x1 << gpio_num);
} else {
GPIO.enable1_w1tc.enable1_w1tc = (0x1 << (gpio_num - 32));
}
}
}
/**
* @brief Enable pull-up on GPIO.
*
@@ -354,6 +428,23 @@ static inline void gpio_ll_output_enable(gpio_dev_t *hw, uint32_t gpio_num)
}
}
/**
* @brief Check if GPIO output is enabled.
*
* @param hw Peripheral GPIO hardware instance address.
* @param gpio_num GPIO number
* @return true if output is enabled, false otherwise
*/
__attribute__((always_inline))
static inline bool gpio_ll_output_is_enabled(gpio_dev_t *hw, uint32_t gpio_num)
{
if (gpio_num < 32) {
return (hw->enable.val >> gpio_num) & 0x1;
} else {
return (hw->enable1.val >> (gpio_num - 32)) & 0x1;
}
}
/**
* @brief Disable open-drain mode on GPIO.
*

View File

@@ -157,17 +157,17 @@ typedef enum {
* @brief Structure that contains the configuration of an IO
*/
typedef struct {
uint32_t fun_sel; /*!< Value of IOMUX function selection */
uint32_t sig_out; /*!< Index of the outputting peripheral signal */
gpio_drive_cap_t drv; /*!< Value of drive strength */
bool pu; /*!< Status of pull-up enabled or not */
bool pd; /*!< Status of pull-down enabled or not */
bool ie; /*!< Status of input enabled or not */
bool oe; /*!< Status of output enabled or not */
bool oe_ctrl_by_periph; /*!< True if use output enable signal from peripheral, otherwise False */
bool oe_inv; /*!< Whether the output enable signal is inversed or not */
bool od; /*!< Status of open-drain enabled or not */
bool slp_sel; /*!< Status of pin sleep mode enabled or not */
gpio_drive_cap_t drv; /*!< Value of drive strength */
uint32_t fun_sel : 8; /*!< Value of IOMUX function selection */
uint32_t sig_out : 16;/*!< Index of the outputting peripheral signal */
uint32_t pu : 1; /*!< Status of pull-up enabled or not */
uint32_t pd : 1; /*!< Status of pull-down enabled or not */
uint32_t ie : 1; /*!< Status of input enabled or not */
uint32_t oe : 1; /*!< Status of output enabled or not */
uint32_t oe_ctrl_by_periph : 1; /*!< True if use output enable signal from peripheral, otherwise False */
uint32_t oe_inv : 1; /*!< Whether the output enable signal is inversed or not */
uint32_t od : 1; /*!< Status of open-drain enabled or not */
uint32_t slp_sel : 1; /*!< Status of pin sleep mode enabled or not */
} gpio_io_config_t;
#ifdef __cplusplus

View File

@@ -10,12 +10,12 @@
// On S31, SPI pins defined here are all wrong. these pins are individual pins, don't use normal GPIO pins anymore.
#define GPIO_NUM_INVALID -1
#define MSPI_IOMUX_PIN_NUM_CS1 GPIO_NUM_INVALID
#define MSPI_IOMUX_PIN_NUM_HD GPIO_NUM_INVALID
#define MSPI_IOMUX_PIN_NUM_WP GPIO_NUM_INVALID
#define MSPI_IOMUX_PIN_NUM_CS0 GPIO_NUM_INVALID
#define MSPI_IOMUX_PIN_NUM_CLK GPIO_NUM_INVALID
#define MSPI_IOMUX_PIN_NUM_MISO GPIO_NUM_INVALID
#define MSPI_IOMUX_PIN_NUM_MOSI GPIO_NUM_INVALID
#define MSPI_IOMUX_PIN_NUM_HD 30
#define MSPI_IOMUX_PIN_NUM_WP 28
#define MSPI_IOMUX_PIN_NUM_CS0 26
#define MSPI_IOMUX_PIN_NUM_CLK 31
#define MSPI_IOMUX_PIN_NUM_MISO 27
#define MSPI_IOMUX_PIN_NUM_MOSI 32
#define MSPI_IOMUX_PIN_NUM_D4 GPIO_NUM_INVALID
#define MSPI_IOMUX_PIN_NUM_D5 GPIO_NUM_INVALID
#define MSPI_IOMUX_PIN_NUM_D6 GPIO_NUM_INVALID

View File

@@ -802,7 +802,7 @@ static inline void mspi_ll_unhold_all_flash_pins(void)
* Sets all PSRAM pins (pin_group0, dqs0, pin_group1, dqs1) to hold status
*/
__attribute__((always_inline))
static inline void mspi_ll_hold_all_psram_pins(void)
static inline void mspi_ll_psram_hold_all_pins(void)
{
#if HAL_CONFIG(CHIP_SUPPORT_MIN_REV) >= 300 // Only rev3+ chips support mspi pad holding.
LP_SYS.ded_pad_rtc_hold_ctrl.ded_pad_rtc_hold_ctrl |= 0x3FFFFC0;
@@ -814,7 +814,7 @@ static inline void mspi_ll_hold_all_psram_pins(void)
* Releases hold status for all PSRAM pins (pin_group0, dqs0, pin_group1, dqs1)
*/
__attribute__((always_inline))
static inline void mspi_ll_unhold_all_psram_pins(void)
static inline void mspi_ll_psram_unhold_all_pins(void)
{
#if HAL_CONFIG(CHIP_SUPPORT_MIN_REV) >= 300 // Only rev3+ chips support mspi pad holding.
LP_SYS.ded_pad_rtc_hold_ctrl.ded_pad_rtc_hold_ctrl &= ~0x3FFFFC0;

View File

@@ -304,6 +304,46 @@ static inline void mspi_ll_psram_enable_axi_access(uint8_t spi_num, bool enable)
SPIMEM2.mem_cache_fctrl.close_axi_inf_en = !enable;
}
/**
* @brief Hold all PSRAM pins
* Sets all PSRAM pins (pin_group0, dqs0, pin_group1, dqs1) to hold status
*/
__attribute__((always_inline))
static inline void mspi_ll_psram_hold_all_pins(void)
{
MSPI_IOMUX.psram_pin_group.pin_group0[0].reg_psram_pin_hold = 1;
MSPI_IOMUX.psram_pin_group.pin_group0[1].reg_psram_pin_hold = 1;
MSPI_IOMUX.psram_pin_group.pin_group0[2].reg_psram_pin_hold = 1;
MSPI_IOMUX.psram_pin_group.pin_group0[3].reg_psram_pin_hold = 1;
MSPI_IOMUX.psram_pin_group.pin_group0[4].reg_psram_pin_hold = 1;
MSPI_IOMUX.psram_pin_group.pin_group0[5].reg_psram_pin_hold = 1;
MSPI_IOMUX.psram_pin_group.pin_group0[6].reg_psram_pin_hold = 1;
MSPI_IOMUX.psram_pin_group.pin_group0[7].reg_psram_pin_hold = 1;
MSPI_IOMUX.psram_pin_group.dqs0.reg_psram_dqs_hold = 1;
MSPI_IOMUX.psram_pin_group.pin_clk.reg_psram_pin_hold = 1;
MSPI_IOMUX.psram_pin_group.pin_cs.reg_psram_pin_hold = 1;
}
/**
* @brief Unhold all PSRAM pins
* Releases hold status for all PSRAM pins (pin_group0, dqs0, pin_group1, dqs1)
*/
__attribute__((always_inline))
static inline void mspi_ll_psram_unhold_all_pins(void)
{
MSPI_IOMUX.psram_pin_group.pin_group0[0].reg_psram_pin_hold = 0;
MSPI_IOMUX.psram_pin_group.pin_group0[1].reg_psram_pin_hold = 0;
MSPI_IOMUX.psram_pin_group.pin_group0[2].reg_psram_pin_hold = 0;
MSPI_IOMUX.psram_pin_group.pin_group0[3].reg_psram_pin_hold = 0;
MSPI_IOMUX.psram_pin_group.pin_group0[4].reg_psram_pin_hold = 0;
MSPI_IOMUX.psram_pin_group.pin_group0[5].reg_psram_pin_hold = 0;
MSPI_IOMUX.psram_pin_group.pin_group0[6].reg_psram_pin_hold = 0;
MSPI_IOMUX.psram_pin_group.pin_group0[7].reg_psram_pin_hold = 0;
MSPI_IOMUX.psram_pin_group.dqs0.reg_psram_dqs_hold = 0;
MSPI_IOMUX.psram_pin_group.pin_clk.reg_psram_pin_hold = 0;
MSPI_IOMUX.psram_pin_group.pin_cs.reg_psram_pin_hold = 0;
}
#ifdef __cplusplus
}
#endif

View File

@@ -514,6 +514,25 @@ FORCE_INLINE_ATTR void pmu_ll_hp_set_memory_power_up(pmu_dev_t *hw, uint32_t fpu
hw->power.mem_cntl.force_hp_mem_pu = fpu;
}
FORCE_INLINE_ATTR void pmu_ll_hp_set_memory_power_on_mask(pmu_dev_t *hw, uint32_t mem_mask)
{
hw->power.mem_mask.mem0_mask = (mem_mask & BIT(0)) ? 0x1F : 0;
hw->power.mem_mask.mem1_mask = (mem_mask & BIT(1)) ? 0x1F : 0;
hw->power.mem_mask.mem2_mask = (mem_mask & BIT(2)) ? 0x1F : 0;
if (mem_mask & BIT(3)) {
hw->power.mem_cntl.force_hp_mem_pu |= (mem_mask & BIT(3));
} else {
hw->power.mem_cntl.force_hp_mem_pu &= ~(mem_mask & BIT(3));
}
}
FORCE_INLINE_ATTR void pmu_ll_hp_set_memory_power_off_mask(pmu_dev_t *hw, uint32_t mem0_pd_mask, uint32_t mem1_pd_mask, uint32_t mem2_pd_mask)
{
hw->power.mem_mask.mem0_pd_mask = mem0_pd_mask;
hw->power.mem_mask.mem1_pd_mask = mem1_pd_mask;
hw->power.mem_mask.mem2_pd_mask = mem2_pd_mask;
}
FORCE_INLINE_ATTR void pmu_ll_hp_set_sleep_enable(pmu_dev_t *hw)
{
hw->wakeup.cntl0.sleep_req = 1;
@@ -755,12 +774,17 @@ FORCE_INLINE_ATTR uint32_t pmu_ll_get_sysclk_sleep_select_state(pmu_dev_t *hw)
return hw->clk_state0.sysclk_slp_sel;
}
FORCE_INLINE_ATTR void pmu_ll_set_rf_power_control(pmu_dev_t *hw, uint16_t val)
{
hw->hp_ext.rf_pwc.xpd_rf_circuit = val;
}
/**
* @brief Get ext1 wakeup source status
* @return The lower 8 bits of the returned value are the bitmap of
* the wakeup source status, bit 0~7 corresponds to LP_IO 0~7
*/
static inline uint32_t pmu_ll_ext1_get_wakeup_status(void)
FORCE_INLINE_ATTR uint32_t pmu_ll_ext1_get_wakeup_status(void)
{
return REG_GET_FIELD(PMU_EXT_WAKEUP_ST_REG, PMU_EXT_WAKEUP_STATUS);
}
@@ -768,9 +792,10 @@ static inline uint32_t pmu_ll_ext1_get_wakeup_status(void)
/**
* @brief Clear the ext1 wakeup source status
*/
static inline void pmu_ll_ext1_clear_wakeup_status(void)
FORCE_INLINE_ATTR void pmu_ll_ext1_clear_wakeup_status(void)
{
REG_SET_BIT(PMU_EXT_WAKEUP_CNTL_REG, PMU_EXT_WAKEUP_STATUS_CLR);
REG_CLR_BIT(PMU_EXT_WAKEUP_CNTL_REG, PMU_EXT_WAKEUP_STATUS_CLR);
}
/**
@@ -779,7 +804,7 @@ static inline void pmu_ll_ext1_clear_wakeup_status(void)
* @param level_mask 0: Wake the chip when all selected GPIOs go low
* 1: Wake the chip when any of the selected GPIOs go high
*/
static inline void pmu_ll_ext1_set_wakeup_pins(uint32_t io_mask, int level_mask)
FORCE_INLINE_ATTR void pmu_ll_ext1_set_wakeup_pins(uint32_t io_mask, int level_mask)
{
REG_SET_FIELD(PMU_EXT_WAKEUP_SEL_REG, PMU_EXT_WAKEUP_SEL, io_mask);
REG_SET_FIELD(PMU_EXT_WAKEUP_LV_REG, PMU_EXT_WAKEUP_LV, level_mask);
@@ -788,7 +813,7 @@ static inline void pmu_ll_ext1_set_wakeup_pins(uint32_t io_mask, int level_mask
/**
* @brief Clear all ext1 wakup-source setting
*/
static inline void pmu_ll_ext1_clear_wakeup_pins(void)
FORCE_INLINE_ATTR void pmu_ll_ext1_clear_wakeup_pins(void)
{
REG_SET_FIELD(PMU_EXT_WAKEUP_SEL_REG, PMU_EXT_WAKEUP_SEL, 0);
}
@@ -798,7 +823,7 @@ static inline void pmu_ll_ext1_clear_wakeup_pins(void)
* @return The lower 8 bits of the returned value are the bitmap of
* the wakeup source status, bit 0~7 corresponds to LP_IO 0~7
*/
static inline uint32_t pmu_ll_ext1_get_wakeup_pins(void)
FORCE_INLINE_ATTR uint32_t pmu_ll_ext1_get_wakeup_pins(void)
{
return REG_GET_FIELD(PMU_EXT_WAKEUP_SEL_REG, PMU_EXT_WAKEUP_SEL);
}

View File

@@ -66,6 +66,7 @@ if(NOT non_os_build)
endif()
if(CONFIG_SOC_LIGHT_SLEEP_SUPPORTED)
list(APPEND srcs "sleep_modem.c"
"sleep_cache.c"
"sleep_modes.c"
"sleep_uart.c"
"sleep_console.c"

View File

@@ -82,7 +82,7 @@ menu "Hardware Settings"
select PM_SLP_IRAM_OPT
select ESP_PERIPH_CTRL_FUNC_IN_IRAM
select ESP_REGI2C_CTRL_FUNC_IN_IRAM
depends on !SPIRAM || ESP_LDO_RESERVE_PSRAM
depends on !SPIRAM || ESP_LDO_RESERVE_PSRAM || SOC_PSRAM_HAS_DEDICATED_LDO
depends on !(IDF_TARGET_ESP32P4 && (ESP32P4_REV_MIN_FULL < 100))
default n
help

View File

@@ -18,6 +18,7 @@
#include "freertos/task.h"
#include "hal/etm_periph.h"
#include "esp_log.h"
#include "esp_sleep.h"
#include "esp_check.h"
#include "esp_heap_caps.h"
#include "esp_etm.h"
@@ -246,6 +247,9 @@ esp_err_t esp_etm_new_channel(const esp_etm_channel_config_t *config, esp_etm_ch
[[maybe_unused]] bool allow_pd = config->flags.allow_pd == 1;
#if !SOC_ETM_SUPPORT_SLEEP_RETENTION
ESP_RETURN_ON_FALSE(allow_pd == 0, ESP_ERR_NOT_SUPPORTED, TAG, "not able to power down in light sleep");
#if SOC_PM_SUPPORT_TOP_PD
esp_sleep_pd_config(ESP_PD_DOMAIN_TOP, ESP_PD_OPTION_ON); //IDF-15651
#endif
#endif // SOC_ETM_SUPPORT_SLEEP_RETENTION
// allocate channel memory from internal memory because it contains atomic variable

View File

@@ -123,8 +123,8 @@ typedef enum {
#define RTC_USB_TRIG_EN PMU_USB_WAKEUP_EN
#if SOC_LP_CORE_SUPPORTED
#define RTC_LP_CORE_TRIG_EN PMU_LP_CORE_WAKEUP_EN //!< LP core wakeup
#define RTC_LP_CORE_TRAP_TRIG_EN PMU_LP_CORE_TRAP_WAKEUP_EN //!< LP core trap (exception) wakeup
#define RTC_LP_CORE_TRIG_EN PMU_LP_CORE_WAKEUP_HP_EN //!< LP core wakeup
#define RTC_LP_CORE_TRAP_TRIG_EN PMU_LP_CORE_TRAP_WAKEUP_EN //!< LP core trap (exception) wakeup
#else
#define RTC_LP_CORE_TRIG_EN 0
#define RTC_LP_CORE_TRAP_TRIG_EN 0
@@ -199,6 +199,9 @@ typedef struct {
#if SOC_PM_SLEEP_CLK_ICG_USE_REGDMA
void *priv;
#endif
#if SOC_SPI_FLASH_HAS_DEDICATED_LDO
bool flash_ldo_volt_1v8;
#endif
} pmu_context_t;
pmu_context_t * PMU_instance(void);

View File

@@ -97,14 +97,36 @@ esp_err_t esp_sleep_acquire_lp_use_xtal(void);
esp_err_t esp_sleep_release_lp_use_xtal(void);
#endif
#if !SOC_GPIO_SUPPORT_HOLD_SINGLE_IO_IN_DSLP
#if !SOC_GPIO_SUPPORT_HOLD_SINGLE_IO_IN_DSLP || SOC_GPIO_NEED_SOFT_ISOLATE_DURING_PD
/**
* @brief Isolate all digital IOs except those that are held during deep sleep
* @brief Soft-isolate valid digital IO pads (SOC_GPIO_VALID_DIGITAL_IO_PAD_MASK) for leakage control
*
* Reduce digital IOs current leakage during deep sleep.
* Skips pads that are digitally held and pads reserved by the driver.
* MSPI signal pads are not in this pass; use esp_sleep_isolate_mspi_gpio() after cache/MSPI idle.
*
* @param do_backup If true, back up each pad's pu/pd/ie/oe/fun_sel before isolating so that
* esp_sleep_restore_isolated_digital_gpio() can restore them later.
* Pass false when restore is not needed (e.g. deep sleep).
*/
void esp_sleep_isolate_digital_gpio(void);
#endif
void esp_sleep_isolate_digital_gpio(bool do_backup);
/**
* @brief Backup and isolate (or pull up) the five base MSPI lines (CLK/Q/D/HD/WP)
*
* If CONFIG_ESP_SLEEP_MSPI_NEED_ALL_IO_PU && !SOC_MSPI_HAS_INDEPENT_IOMUX, enables pull-up only;
* otherwise full pad isolate like esp_sleep_isolate_digital_gpio. Intended after SPI bus is idle
* (e.g. light sleep with TOP power-down).
*/
void esp_sleep_isolate_mspi_gpio(void);
/**
* @brief Restore pu/pd/ie/oe and IOMUX fun_sel for every GPIO in the soft-isolate backup bitmap
*
* Reverses esp_sleep_isolate_digital_gpio / esp_sleep_isolate_mspi_gpio. Call after wake before
* normal Flash access.
*/
void esp_sleep_restore_isolated_digital_gpio(void);
#endif // !SOC_GPIO_SUPPORT_HOLD_SINGLE_IO_IN_DSLP || SOC_GPIO_NEED_SOFT_ISOLATE_DURING_PD
#if SOC_PM_SUPPORT_PMU_CLK_ICG
/**

View File

@@ -0,0 +1,36 @@
/*
* SPDX-FileCopyrightText: 2026 Espressif Systems (Shanghai) CO LTD
*
* SPDX-License-Identifier: Apache-2.0
*/
#pragma once
#include <stdint.h>
#include "sdkconfig.h"
#ifdef __cplusplus
extern "C" {
#endif
/**
* @brief Nestable suspend: disable external-mem cache on the first call (refcounted). Critical section; pair with @ref sleep_cache_resume.
*/
void sleep_cache_suspend(void);
/**
* @brief Nestable resume: restore cache when the refcount returns to zero. Must match @ref sleep_cache_suspend.
*/
void sleep_cache_resume(void);
#if !CONFIG_SOC_CACHE_INTERNAL_MEM_VIA_L1CACHE && CONFIG_SPIRAM
/**
* @brief Cache writeback before sleep when CPU/cache may power down (SPIRAM / PAU paths; Kconfig-driven).
* @param sleep_flags Same flags as light sleep, e.g. @c PMU_SLEEP_PD_CPU when relevant.
*/
void sleep_cache_safe_writeback(uint32_t sleep_flags);
#endif
#ifdef __cplusplus
}
#endif

View File

@@ -37,6 +37,7 @@ entries:
rtc_sleep:rtc_sleep_pd (noflash)
if IDF_TARGET_ESP32S3 = y || IDF_TARGET_ESP32C2 = y || IDF_TARGET_ESP32C3 = y:
rtc_sleep:rtc_sleep_pu (noflash)
sleep_cache (noflash)
if SOC_PMU_SUPPORTED = y:
if SPIRAM_FLASH_LOAD_TO_PSRAM = y:
pmu_init (noflash)
@@ -46,7 +47,7 @@ entries:
if SOC_PM_SLEEP_CLK_ICG_USE_REGDMA = y && PM_POWER_DOWN_PERIPHERAL_IN_LIGHT_SLEEP != y:
pmu_sleep_clock_icg:pmu_sleep_clock_icg_config (noflash)
sleep_mspi (noflash)
if PM_SLP_IRAM_OPT = y && IDF_TARGET_ESP32P4 != y:
if PM_SLP_IRAM_OPT = y && IDF_TARGET_ESP32S31 != y:
pmu_param:get_act_lp_dbias (noflash)
if IDF_TARGET_ESP32H4 || IDF_TARGET_ESP32H21 = y:
pmu_param:get_act_hp_drvb (noflash)
@@ -85,6 +86,8 @@ entries:
sleep_modes: esp_sleep_enable_vbat_under_volt_wakeup (noflash)
sleep_modes: esp_sleep_sub_mode_config (noflash)
sleep_modes: esp_sleep_sub_mode_force_disable (noflash)
if (SOC_GPIO_SUPPORT_HOLD_SINGLE_IO_IN_DSLP = n || SOC_GPIO_NEED_SOFT_ISOLATE_DURING_PD = y):
esp_gpio_reserve (noflash)
[mapping:hal_gpio_pm]
archive: libesp_hal_gpio.a

View File

@@ -0,0 +1,225 @@
/*
* SPDX-FileCopyrightText: 2026 Espressif Systems (Shanghai) CO LTD
*
* SPDX-License-Identifier: Apache-2.0
*/
#ifndef __RVSLEEP_FRAMES_H__
#define __RVSLEEP_FRAMES_H__
#include "sdkconfig.h"
/* Align a value up to nearest n-byte boundary, where n is a power of 2. */
#define ALIGNUP(n, val) (((val) + (n) - 1) & -(n))
#ifdef STRUCT_BEGIN
#undef STRUCT_BEGIN
#undef STRUCT_FIELD
#undef STRUCT_AFIELD
#undef STRUCT_END
#endif
#if defined(_ASMLANGUAGE) || defined(__ASSEMBLER__)
#ifdef __clang__
#define STRUCT_BEGIN .set RV_STRUCT_OFFSET, 0
#define STRUCT_FIELD(ctype,size,asname,name) .set asname, RV_STRUCT_OFFSET; .set RV_STRUCT_OFFSET, asname + size
#define STRUCT_AFIELD(ctype,size,asname,name,n) .set asname, RV_STRUCT_OFFSET;\
.set RV_STRUCT_OFFSET, asname + (size)*(n);
#define STRUCT_END(sname) .set sname##Size, RV_STRUCT_OFFSET;
#else // __clang__
#define STRUCT_BEGIN .pushsection .text; .struct 0
#define STRUCT_FIELD(ctype,size,asname,name) asname: .space size
#define STRUCT_AFIELD(ctype,size,asname,name,n) asname: .space (size)*(n)
#define STRUCT_END(sname) sname##Size:; .popsection
#endif // __clang__
#else
#define STRUCT_BEGIN typedef struct {
#define STRUCT_FIELD(ctype,size,asname,name) ctype name;
#define STRUCT_AFIELD(ctype,size,asname,name,n) ctype name[n];
#define STRUCT_END(sname) } sname;
#endif
/*
* -------------------------------------------------------------------------------
* RISC-V CORE CRITICAL REGISTER CONTEXT LAYOUT FOR SLEEP
* -------------------------------------------------------------------------------
*/
STRUCT_BEGIN
STRUCT_FIELD (long, 4, RV_SLP_CTX_MEPC, mepc) /* Machine Exception Program Counter */
STRUCT_FIELD (long, 4, RV_SLP_CTX_RA, ra) /* Return address */
STRUCT_FIELD (long, 4, RV_SLP_CTX_SP, sp) /* Stack pointer */
STRUCT_FIELD (long, 4, RV_SLP_CTX_GP, gp) /* Global pointer */
STRUCT_FIELD (long, 4, RV_SLP_CTX_TP, tp) /* Thread pointer */
STRUCT_FIELD (long, 4, RV_SLP_CTX_T0, t0) /* Temporary/alternate link register */
STRUCT_FIELD (long, 4, RV_SLP_CTX_T1, t1) /* t1-2: Temporaries */
STRUCT_FIELD (long, 4, RV_SLP_CTX_T2, t2)
STRUCT_FIELD (long, 4, RV_SLP_CTX_S0, s0) /* Saved register/frame pointer */
STRUCT_FIELD (long, 4, RV_SLP_CTX_S1, s1) /* Saved register */
STRUCT_FIELD (long, 4, RV_SLP_CTX_A0, a0) /* a0-1: Function arguments/return address */
STRUCT_FIELD (long, 4, RV_SLP_CTX_A1, a1)
STRUCT_FIELD (long, 4, RV_SLP_CTX_A2, a2) /* a2-7: Function arguments */
STRUCT_FIELD (long, 4, RV_SLP_CTX_A3, a3)
STRUCT_FIELD (long, 4, RV_SLP_CTX_A4, a4)
STRUCT_FIELD (long, 4, RV_SLP_CTX_A5, a5)
STRUCT_FIELD (long, 4, RV_SLP_CTX_A6, a6)
STRUCT_FIELD (long, 4, RV_SLP_CTX_A7, a7)
STRUCT_FIELD (long, 4, RV_SLP_CTX_S2, s2) /* s2-11: Saved registers */
STRUCT_FIELD (long, 4, RV_SLP_CTX_S3, s3)
STRUCT_FIELD (long, 4, RV_SLP_CTX_S4, s4)
STRUCT_FIELD (long, 4, RV_SLP_CTX_S5, s5)
STRUCT_FIELD (long, 4, RV_SLP_CTX_S6, s6)
STRUCT_FIELD (long, 4, RV_SLP_CTX_S7, s7)
STRUCT_FIELD (long, 4, RV_SLP_CTX_S8, s8)
STRUCT_FIELD (long, 4, RV_SLP_CTX_S9, s9)
STRUCT_FIELD (long, 4, RV_SLP_CTX_S10, s10)
STRUCT_FIELD (long, 4, RV_SLP_CTX_S11, s11)
STRUCT_FIELD (long, 4, RV_SLP_CTX_T3, t3) /* t3-6: Temporaries */
STRUCT_FIELD (long, 4, RV_SLP_CTX_T4, t4)
STRUCT_FIELD (long, 4, RV_SLP_CTX_T5, t5)
STRUCT_FIELD (long, 4, RV_SLP_CTX_T6, t6)
STRUCT_FIELD (long, 4, RV_SLP_CTX_MSTATUS, mstatus) /* Machine Status */
STRUCT_FIELD (long, 4, RV_SLP_CTX_MTVEC, mtvec) /* Machine Trap-Vector Base Address */
STRUCT_FIELD (long, 4, RV_SLP_CTX_MCAUSE, mcause) /* Machine Trap Cause */
STRUCT_FIELD (long, 4, RV_SLP_CTX_MTVAL, mtval) /* Machine Trap Value */
STRUCT_FIELD (long, 4, RV_SLP_CTX_MIE, mie) /* Machine intr enable */
STRUCT_FIELD (long, 4, RV_SLP_CTX_MIP, mip) /* Machine intr pending */
STRUCT_FIELD (long, 4, RV_SLP_CTX_MINTTHRESH, mintthresh) /* Machine intr threshold */
/* FPU context */
STRUCT_FIELD (long, 4, RV_SLP_CTX_FPU_FT0, fpu_ft0)
STRUCT_FIELD (long, 4, RV_SLP_CTX_FPU_FT1, fpu_ft1)
STRUCT_FIELD (long, 4, RV_SLP_CTX_FPU_FT2, fpu_ft2)
STRUCT_FIELD (long, 4, RV_SLP_CTX_FPU_FT3, fpu_ft3)
STRUCT_FIELD (long, 4, RV_SLP_CTX_FPU_FT4, fpu_ft4)
STRUCT_FIELD (long, 4, RV_SLP_CTX_FPU_FT5, fpu_ft5)
STRUCT_FIELD (long, 4, RV_SLP_CTX_FPU_FT6, fpu_ft6)
STRUCT_FIELD (long, 4, RV_SLP_CTX_FPU_FT7, fpu_ft7)
STRUCT_FIELD (long, 4, RV_SLP_CTX_FPU_FS0, fpu_fs0)
STRUCT_FIELD (long, 4, RV_SLP_CTX_FPU_FS1, fpu_fs1)
STRUCT_FIELD (long, 4, RV_SLP_CTX_FPU_FA0, fpu_fa0)
STRUCT_FIELD (long, 4, RV_SLP_CTX_FPU_FA1, fpu_fa1)
STRUCT_FIELD (long, 4, RV_SLP_CTX_FPU_FA2, fpu_fa2)
STRUCT_FIELD (long, 4, RV_SLP_CTX_FPU_FA3, fpu_fa3)
STRUCT_FIELD (long, 4, RV_SLP_CTX_FPU_FA4, fpu_fa4)
STRUCT_FIELD (long, 4, RV_SLP_CTX_FPU_FA5, fpu_fa5)
STRUCT_FIELD (long, 4, RV_SLP_CTX_FPU_FA6, fpu_fa6)
STRUCT_FIELD (long, 4, RV_SLP_CTX_FPU_FA7, fpu_fa7)
STRUCT_FIELD (long, 4, RV_SLP_CTX_FPU_FS2, fpu_fs2)
STRUCT_FIELD (long, 4, RV_SLP_CTX_FPU_FS3, fpu_fs3)
STRUCT_FIELD (long, 4, RV_SLP_CTX_FPU_FS4, fpu_fs4)
STRUCT_FIELD (long, 4, RV_SLP_CTX_FPU_FS5, fpu_fs5)
STRUCT_FIELD (long, 4, RV_SLP_CTX_FPU_FS6, fpu_fs6)
STRUCT_FIELD (long, 4, RV_SLP_CTX_FPU_FS7, fpu_fs7)
STRUCT_FIELD (long, 4, RV_SLP_CTX_FPU_FS8, fpu_fs8)
STRUCT_FIELD (long, 4, RV_SLP_CTX_FPU_FS9, fpu_fs9)
STRUCT_FIELD (long, 4, RV_SLP_CTX_FPU_FS10, fpu_fs10)
STRUCT_FIELD (long, 4, RV_SLP_CTX_FPU_FS11, fpu_fs11)
STRUCT_FIELD (long, 4, RV_SLP_CTX_FPU_FT8, fpu_ft8)
STRUCT_FIELD (long, 4, RV_SLP_CTX_FPU_FT9, fpu_ft9)
STRUCT_FIELD (long, 4, RV_SLP_CTX_FPU_FT10, fpu_ft10)
STRUCT_FIELD (long, 4, RV_SLP_CTX_FPU_FT11, fpu_ft11)
STRUCT_FIELD (long, 4, RV_SLP_CTX_FPU_FCSR, fpu_fcsr)
STRUCT_FIELD (long, 4, RV_SLP_CTX_PMUFUNC, pmufunc) /* A field is used to identify whether it is going
* to sleep or has just been awakened. We use the
* lowest 2 bits as indication information, 3 means
* being awakened, 1 means going to sleep */
#if CONFIG_PM_CHECK_SLEEP_RETENTION_FRAME
STRUCT_FIELD (long, 4, RV_SLP_CSF_CTX_CRC, frame_crc) /* Used to check RvCoreCriticalSleepFrame integrity */
#endif
STRUCT_END(RvCoreCriticalSleepFrame)
#if defined(_ASMLANGUAGE) || defined(__ASSEMBLER__)
#define RV_SLEEP_CTX_SZ1 RvCoreCriticalSleepFrameSize
#else
#define RV_SLEEP_CTX_SZ1 sizeof(RvCoreCriticalSleepFrame)
#endif
/*
* Sleep stack frame size, after align up to 16 bytes boundary
*/
#define RV_SLEEP_CTX_FRMSZ (ALIGNUP(0x10, RV_SLEEP_CTX_SZ1))
/*
* -------------------------------------------------------------------------------
* RISC-V CORE NON-CRITICAL REGISTER CONTEXT LAYOUT FOR SLEEP
* -------------------------------------------------------------------------------
*/
STRUCT_BEGIN
STRUCT_FIELD (long, 4, RV_SLP_CTX_MSCRATCH, mscratch)
STRUCT_FIELD (long, 4, RV_SLP_CTX_MISA, misa)
STRUCT_FIELD (long, 4, RV_SLP_CTX_MTVT, mtvt) /* Machine mode vector interrupt base address register */
STRUCT_FIELD (long, 4, RV_SLP_CTX_MSCRATCHCSW, mscratchcsw) /* Machine mode multi-mode data backup register */
STRUCT_FIELD (long, 4, RV_SLP_CTX_MSCRATCHCSWL, mscratchcswl) /* Machine mode interrupt data backup register */
STRUCT_FIELD (long, 4, RV_SLP_CTX_MXSTATUS, mxstatus) /* Extended status register */
STRUCT_FIELD (long, 4, RV_SLP_CTX_MHCR, mhcr) /* Hardware control register */
STRUCT_FIELD (long, 4, RV_SLP_CTX_MHINT, mhint) /* Implicit operation register */
STRUCT_FIELD (long, 4, RV_SLP_CTX_MRADDR, mraddr) /* Processor reset start address indication register */
STRUCT_FIELD (long, 4, RV_SLP_CTX_MEXSTATUS, mexstatus) /* Extended exception status register */
STRUCT_FIELD (long, 4, RV_SLP_CTX_JVT, jvt) /* Table Jump base vector and control register */
STRUCT_FIELD (long, 4, RV_SLP_CTX_MEDELEG, medeleg) /* Table Jump base vector and control register */
STRUCT_FIELD (long, 4, RV_SLP_CTX_TSELECT, tselect)
STRUCT_FIELD (long, 4, RV_SLP_CTX_TDATA1, tdata1)
STRUCT_FIELD (long, 4, RV_SLP_CTX_TDATA2, tdata2)
STRUCT_FIELD (long, 4, RV_SLP_CTX_TCONTROL, tcontrol)
STRUCT_FIELD (long, 4, RV_SLP_CTX_PMPADDR0, pmpaddr0)
STRUCT_FIELD (long, 4, RV_SLP_CTX_PMPADDR1, pmpaddr1)
STRUCT_FIELD (long, 4, RV_SLP_CTX_PMPADDR2, pmpaddr2)
STRUCT_FIELD (long, 4, RV_SLP_CTX_PMPADDR3, pmpaddr3)
STRUCT_FIELD (long, 4, RV_SLP_CTX_PMPADDR4, pmpaddr4)
STRUCT_FIELD (long, 4, RV_SLP_CTX_PMPADDR5, pmpaddr5)
STRUCT_FIELD (long, 4, RV_SLP_CTX_PMPADDR6, pmpaddr6)
STRUCT_FIELD (long, 4, RV_SLP_CTX_PMPADDR7, pmpaddr7)
STRUCT_FIELD (long, 4, RV_SLP_CTX_PMPADDR8, pmpaddr8)
STRUCT_FIELD (long, 4, RV_SLP_CTX_PMPADDR9, pmpaddr9)
STRUCT_FIELD (long, 4, RV_SLP_CTX_PMPADDR10, pmpaddr10)
STRUCT_FIELD (long, 4, RV_SLP_CTX_PMPADDR11, pmpaddr11)
STRUCT_FIELD (long, 4, RV_SLP_CTX_PMPADDR12, pmpaddr12)
STRUCT_FIELD (long, 4, RV_SLP_CTX_PMPADDR13, pmpaddr13)
STRUCT_FIELD (long, 4, RV_SLP_CTX_PMPADDR14, pmpaddr14)
STRUCT_FIELD (long, 4, RV_SLP_CTX_PMPADDR15, pmpaddr15)
STRUCT_FIELD (long, 4, RV_SLP_CTX_PMPCFG0, pmpcfg0)
STRUCT_FIELD (long, 4, RV_SLP_CTX_PMPCFG1, pmpcfg1)
STRUCT_FIELD (long, 4, RV_SLP_CTX_PMPCFG2, pmpcfg2)
STRUCT_FIELD (long, 4, RV_SLP_CTX_PMPCFG3, pmpcfg3)
STRUCT_FIELD (long, 4, RV_SLP_CTX_PMAADDR0, pmaaddr0)
STRUCT_FIELD (long, 4, RV_SLP_CTX_PMAADDR1, pmaaddr1)
STRUCT_FIELD (long, 4, RV_SLP_CTX_PMAADDR2, pmaaddr2)
STRUCT_FIELD (long, 4, RV_SLP_CTX_PMAADDR3, pmaaddr3)
STRUCT_FIELD (long, 4, RV_SLP_CTX_PMAADDR4, pmaaddr4)
STRUCT_FIELD (long, 4, RV_SLP_CTX_PMAADDR5, pmaaddr5)
STRUCT_FIELD (long, 4, RV_SLP_CTX_PMAADDR6, pmaaddr6)
STRUCT_FIELD (long, 4, RV_SLP_CTX_PMAADDR7, pmaaddr7)
STRUCT_FIELD (long, 4, RV_SLP_CTX_PMAADDR8, pmaaddr8)
STRUCT_FIELD (long, 4, RV_SLP_CTX_PMAADDR9, pmaaddr9)
STRUCT_FIELD (long, 4, RV_SLP_CTX_PMAADDR10, pmaaddr10)
STRUCT_FIELD (long, 4, RV_SLP_CTX_PMAADDR11, pmaaddr11)
STRUCT_FIELD (long, 4, RV_SLP_CTX_PMAADDR12, pmaaddr12)
STRUCT_FIELD (long, 4, RV_SLP_CTX_PMAADDR13, pmaaddr13)
STRUCT_FIELD (long, 4, RV_SLP_CTX_PMAADDR14, pmaaddr14)
STRUCT_FIELD (long, 4, RV_SLP_CTX_PMAADDR15, pmaaddr15)
STRUCT_FIELD (long, 4, RV_SLP_CTX_PMACFG0, pmacfg0)
STRUCT_FIELD (long, 4, RV_SLP_CTX_PMACFG1, pmacfg1)
STRUCT_FIELD (long, 4, RV_SLP_CTX_PMACFG2, pmacfg2)
STRUCT_FIELD (long, 4, RV_SLP_CTX_PMACFG3, pmacfg3)
STRUCT_FIELD (long, 4, RV_SLP_CTX_PMACFG4, pmacfg4)
STRUCT_FIELD (long, 4, RV_SLP_CTX_PMACFG5, pmacfg5)
STRUCT_FIELD (long, 4, RV_SLP_CTX_PMACFG6, pmacfg6)
STRUCT_FIELD (long, 4, RV_SLP_CTX_PMACFG7, pmacfg7)
STRUCT_FIELD (long, 4, RV_SLP_CTX_PMACFG8, pmacfg8)
STRUCT_FIELD (long, 4, RV_SLP_CTX_PMACFG9, pmacfg9)
STRUCT_FIELD (long, 4, RV_SLP_CTX_PMACFG10, pmacfg10)
STRUCT_FIELD (long, 4, RV_SLP_CTX_PMACFG11, pmacfg11)
STRUCT_FIELD (long, 4, RV_SLP_CTX_PMACFG12, pmacfg12)
STRUCT_FIELD (long, 4, RV_SLP_CTX_PMACFG13, pmacfg13)
STRUCT_FIELD (long, 4, RV_SLP_CTX_PMACFG14, pmacfg14)
STRUCT_FIELD (long, 4, RV_SLP_CTX_PMACFG15, pmacfg15)
STRUCT_FIELD (long, 4, RV_SLP_CTX_MCYCLE, mcycle)
STRUCT_FIELD (long, 4, RV_SLP_CTX_MCYCLEH, mcycleh)
#if CONFIG_PM_CHECK_SLEEP_RETENTION_FRAME
STRUCT_FIELD (long, 4, RV_SLP_NCSF_CTX_CRC, frame_crc) /* Used to check RvCoreNonCriticalSleepFrame integrity */
#endif
STRUCT_END(RvCoreNonCriticalSleepFrame)
#endif /* #ifndef __RVSLEEP_FRAMES_H__ */

View File

@@ -0,0 +1,525 @@
/*
* SPDX-FileCopyrightText: 2026 Espressif Systems (Shanghai) CO LTD
*
* SPDX-License-Identifier: Apache-2.0
*/
#include <stddef.h>
#include <string.h>
#include <inttypes.h>
#include <sys/lock.h>
#include <sys/param.h>
#include "esp_attr.h"
#include "esp_check.h"
#include "esp_ipc_isr.h"
#include "esp_sleep.h"
#include "esp_log.h"
#include "esp_rom_crc.h"
#include "freertos/FreeRTOS.h"
#include "freertos/task.h"
#include "freertos/portmacro.h"
#include "riscv/csr.h"
#include "soc/clic_reg.h"
#include "soc/rtc_periph.h"
#include "soc/soc_caps.h"
#include "soc/hp_sys_clkrst_reg.h"
#include "esp_private/sleep_cpu.h"
#include "esp_private/sleep_event.h"
#include "sdkconfig.h"
#include "esp_private/esp_pmu.h"
#include "esp32s31/rom/ets_sys.h"
#include "esp32s31/rom/rtc.h"
#include "rvsleep-frames.h"
#include "sleep_cpu_retention.h"
#if CONFIG_PM_CHECK_SLEEP_RETENTION_FRAME
#include "esp_private/system_internal.h"
#include "hal/uart_hal.h"
#endif
#if CONFIG_PM_ESP_SLEEP_POWER_DOWN_CPU && !CONFIG_FREERTOS_UNICORE
static DRAM_ATTR smp_retention_state_t s_smp_retention_state[portNUM_PROCESSORS];
#endif
static bool s_fpu_saved[portNUM_PROCESSORS];
ESP_LOG_ATTR_TAG(TAG, "sleep");
static DRAM_ATTR __attribute__((unused)) sleep_cpu_retention_t s_cpu_retention;
extern RvCoreCriticalSleepFrame *rv_core_critical_regs_frame[portNUM_PROCESSORS];
FORCE_INLINE_ATTR uint32_t save_mstatus_and_disable_global_int(void)
{
return RV_READ_MSTATUS_AND_DISABLE_INTR();
}
FORCE_INLINE_ATTR void restore_mstatus(uint32_t mstatus_val)
{
RV_WRITE_CSR(mstatus, mstatus_val);
}
#define CUSTOM_CSR_MTVT (0x307)
#define CUSTOM_CSR_MSCRATCHCSW (0x348)
#define CUSTOM_CSR_MSCRATCHCSWL (0x349)
#define CUSTOM_CSR_MXSTATUS (0x7c0)
#define CUSTOM_CSR_MHCR (0x7c1)
#define CUSTOM_CSR_MHINT (0x7c5)
#define CUSTOM_CSR_MRADDR (0x7e0)
#define CUSTOM_CSR_MEXSTATUS (0x7e1)
#define CUSTOM_CSR_JVT (0x017)
#define CUSTOM_CSR_UINTTHRESH (0x047)
#define CUSTOM_CSR_UINTSTATUS (0xCB1)
static IRAM_ATTR RvCoreNonCriticalSleepFrame * rv_core_noncritical_regs_save(void)
{
RvCoreNonCriticalSleepFrame *frame = s_cpu_retention.retent.non_critical_frame[esp_cpu_get_core_id()];
frame->mscratch = RV_READ_CSR(mscratch);
frame->misa = RV_READ_CSR(misa);
frame->mtvt = RV_READ_CSR(CUSTOM_CSR_MTVT);
frame->mscratchcsw = RV_READ_CSR(CUSTOM_CSR_MSCRATCHCSW);
frame->mscratchcswl = RV_READ_CSR(CUSTOM_CSR_MSCRATCHCSWL);
frame->mxstatus = RV_READ_CSR(CUSTOM_CSR_MXSTATUS);
frame->mhcr = RV_READ_CSR(CUSTOM_CSR_MHCR);
frame->mhint = RV_READ_CSR(CUSTOM_CSR_MHINT);
frame->mraddr = RV_READ_CSR(CUSTOM_CSR_MRADDR);
frame->mexstatus = RV_READ_CSR(CUSTOM_CSR_MEXSTATUS);
frame->jvt = RV_READ_CSR(CUSTOM_CSR_JVT);
frame->medeleg = RV_READ_CSR(medeleg);
frame->tselect = RV_READ_CSR(tselect);
frame->tdata1 = RV_READ_CSR(tdata1);
frame->tdata2 = RV_READ_CSR(tdata2);
frame->tcontrol = RV_READ_CSR(tcontrol);
frame->pmpaddr0 = RV_READ_CSR(pmpaddr0);
frame->pmpaddr1 = RV_READ_CSR(pmpaddr1);
frame->pmpaddr2 = RV_READ_CSR(pmpaddr2);
frame->pmpaddr3 = RV_READ_CSR(pmpaddr3);
frame->pmpaddr4 = RV_READ_CSR(pmpaddr4);
frame->pmpaddr5 = RV_READ_CSR(pmpaddr5);
frame->pmpaddr6 = RV_READ_CSR(pmpaddr6);
frame->pmpaddr7 = RV_READ_CSR(pmpaddr7);
frame->pmpaddr8 = RV_READ_CSR(pmpaddr8);
frame->pmpaddr9 = RV_READ_CSR(pmpaddr9);
frame->pmpaddr10 = RV_READ_CSR(pmpaddr10);
frame->pmpaddr11 = RV_READ_CSR(pmpaddr11);
frame->pmpaddr12 = RV_READ_CSR(pmpaddr12);
frame->pmpaddr13 = RV_READ_CSR(pmpaddr13);
frame->pmpaddr14 = RV_READ_CSR(pmpaddr14);
frame->pmpaddr15 = RV_READ_CSR(pmpaddr15);
frame->pmpcfg0 = RV_READ_CSR(pmpcfg0);
frame->pmpcfg1 = RV_READ_CSR(pmpcfg1);
frame->pmpcfg2 = RV_READ_CSR(pmpcfg2);
frame->pmpcfg3 = RV_READ_CSR(pmpcfg3);
frame->pmaaddr0 = RV_READ_CSR(CSR_PMAADDR(0));
frame->pmaaddr1 = RV_READ_CSR(CSR_PMAADDR(1));
frame->pmaaddr2 = RV_READ_CSR(CSR_PMAADDR(2));
frame->pmaaddr3 = RV_READ_CSR(CSR_PMAADDR(3));
frame->pmaaddr4 = RV_READ_CSR(CSR_PMAADDR(4));
frame->pmaaddr5 = RV_READ_CSR(CSR_PMAADDR(5));
frame->pmaaddr6 = RV_READ_CSR(CSR_PMAADDR(6));
frame->pmaaddr7 = RV_READ_CSR(CSR_PMAADDR(7));
frame->pmaaddr8 = RV_READ_CSR(CSR_PMAADDR(8));
frame->pmaaddr9 = RV_READ_CSR(CSR_PMAADDR(9));
frame->pmaaddr10 = RV_READ_CSR(CSR_PMAADDR(10));
frame->pmaaddr11 = RV_READ_CSR(CSR_PMAADDR(11));
frame->pmaaddr12 = RV_READ_CSR(CSR_PMAADDR(12));
frame->pmaaddr13 = RV_READ_CSR(CSR_PMAADDR(13));
frame->pmaaddr14 = RV_READ_CSR(CSR_PMAADDR(14));
frame->pmaaddr15 = RV_READ_CSR(CSR_PMAADDR(15));
frame->pmacfg0 = RV_READ_CSR(CSR_PMACFG(0));
frame->pmacfg1 = RV_READ_CSR(CSR_PMACFG(1));
frame->pmacfg2 = RV_READ_CSR(CSR_PMACFG(2));
frame->pmacfg3 = RV_READ_CSR(CSR_PMACFG(3));
frame->pmacfg4 = RV_READ_CSR(CSR_PMACFG(4));
frame->pmacfg5 = RV_READ_CSR(CSR_PMACFG(5));
frame->pmacfg6 = RV_READ_CSR(CSR_PMACFG(6));
frame->pmacfg7 = RV_READ_CSR(CSR_PMACFG(7));
frame->pmacfg8 = RV_READ_CSR(CSR_PMACFG(8));
frame->pmacfg9 = RV_READ_CSR(CSR_PMACFG(9));
frame->pmacfg10 = RV_READ_CSR(CSR_PMACFG(10));
frame->pmacfg11 = RV_READ_CSR(CSR_PMACFG(11));
frame->pmacfg12 = RV_READ_CSR(CSR_PMACFG(12));
frame->pmacfg13 = RV_READ_CSR(CSR_PMACFG(13));
frame->pmacfg14 = RV_READ_CSR(CSR_PMACFG(14));
frame->pmacfg15 = RV_READ_CSR(CSR_PMACFG(15));
frame->mcycle = RV_READ_CSR(mcycle);
frame->mcycleh = RV_READ_CSR(mcycleh);
return frame;
}
static IRAM_ATTR void rv_core_noncritical_regs_restore(void)
{
RvCoreNonCriticalSleepFrame *frame = s_cpu_retention.retent.non_critical_frame[esp_cpu_get_core_id()];
RV_WRITE_CSR(CSR_PMAADDR(0), frame->pmaaddr0);
RV_WRITE_CSR(CSR_PMAADDR(1), frame->pmaaddr1);
RV_WRITE_CSR(CSR_PMAADDR(2), frame->pmaaddr2);
RV_WRITE_CSR(CSR_PMAADDR(3), frame->pmaaddr3);
RV_WRITE_CSR(CSR_PMAADDR(4), frame->pmaaddr4);
RV_WRITE_CSR(CSR_PMAADDR(5), frame->pmaaddr5);
RV_WRITE_CSR(CSR_PMAADDR(6), frame->pmaaddr6);
RV_WRITE_CSR(CSR_PMAADDR(7), frame->pmaaddr7);
RV_WRITE_CSR(CSR_PMAADDR(8), frame->pmaaddr8);
RV_WRITE_CSR(CSR_PMAADDR(9), frame->pmaaddr9);
RV_WRITE_CSR(CSR_PMAADDR(10),frame->pmaaddr10);
RV_WRITE_CSR(CSR_PMAADDR(11),frame->pmaaddr11);
RV_WRITE_CSR(CSR_PMAADDR(12),frame->pmaaddr12);
RV_WRITE_CSR(CSR_PMAADDR(13),frame->pmaaddr13);
RV_WRITE_CSR(CSR_PMAADDR(14),frame->pmaaddr14);
RV_WRITE_CSR(CSR_PMAADDR(15),frame->pmaaddr15);
RV_WRITE_CSR(CSR_PMACFG(0), frame->pmacfg0);
RV_WRITE_CSR(CSR_PMACFG(1), frame->pmacfg1);
RV_WRITE_CSR(CSR_PMACFG(2), frame->pmacfg2);
RV_WRITE_CSR(CSR_PMACFG(3), frame->pmacfg3);
RV_WRITE_CSR(CSR_PMACFG(4), frame->pmacfg4);
RV_WRITE_CSR(CSR_PMACFG(5), frame->pmacfg5);
RV_WRITE_CSR(CSR_PMACFG(6), frame->pmacfg6);
RV_WRITE_CSR(CSR_PMACFG(7), frame->pmacfg7);
RV_WRITE_CSR(CSR_PMACFG(8), frame->pmacfg8);
RV_WRITE_CSR(CSR_PMACFG(9), frame->pmacfg9);
RV_WRITE_CSR(CSR_PMACFG(10), frame->pmacfg10);
RV_WRITE_CSR(CSR_PMACFG(11), frame->pmacfg11);
RV_WRITE_CSR(CSR_PMACFG(12), frame->pmacfg12);
RV_WRITE_CSR(CSR_PMACFG(13), frame->pmacfg13);
RV_WRITE_CSR(CSR_PMACFG(14), frame->pmacfg14);
RV_WRITE_CSR(CSR_PMACFG(15), frame->pmacfg15);
RV_WRITE_CSR(mscratch, frame->mscratch);
RV_WRITE_CSR(misa, frame->misa);
RV_WRITE_CSR(CUSTOM_CSR_MTVT, frame->mtvt);
RV_WRITE_CSR(CUSTOM_CSR_MSCRATCHCSW, frame->mscratchcsw);
RV_WRITE_CSR(CUSTOM_CSR_MSCRATCHCSWL, frame->mscratchcswl);
RV_WRITE_CSR(CUSTOM_CSR_MXSTATUS, frame->mxstatus);
RV_WRITE_CSR(CUSTOM_CSR_MHCR, frame->mhcr);
RV_WRITE_CSR(CUSTOM_CSR_MHINT, frame->mhint);
RV_WRITE_CSR(CUSTOM_CSR_MRADDR, frame->mraddr);
RV_WRITE_CSR(CUSTOM_CSR_MEXSTATUS, frame->mexstatus);
RV_WRITE_CSR(CUSTOM_CSR_JVT, frame->jvt);
RV_WRITE_CSR(medeleg, frame->medeleg);
RV_WRITE_CSR(tselect, frame->tselect);
RV_WRITE_CSR(tdata1, frame->tdata1);
RV_WRITE_CSR(tdata2, frame->tdata2);
RV_WRITE_CSR(tcontrol, frame->tcontrol);
RV_WRITE_CSR(pmpaddr0, frame->pmpaddr0);
RV_WRITE_CSR(pmpaddr1, frame->pmpaddr1);
RV_WRITE_CSR(pmpaddr2, frame->pmpaddr2);
RV_WRITE_CSR(pmpaddr3, frame->pmpaddr3);
RV_WRITE_CSR(pmpaddr4, frame->pmpaddr4);
RV_WRITE_CSR(pmpaddr5, frame->pmpaddr5);
RV_WRITE_CSR(pmpaddr6, frame->pmpaddr6);
RV_WRITE_CSR(pmpaddr7, frame->pmpaddr7);
RV_WRITE_CSR(pmpaddr8, frame->pmpaddr8);
RV_WRITE_CSR(pmpaddr9, frame->pmpaddr9);
RV_WRITE_CSR(pmpaddr10, frame->pmpaddr10);
RV_WRITE_CSR(pmpaddr11, frame->pmpaddr11);
RV_WRITE_CSR(pmpaddr12, frame->pmpaddr12);
RV_WRITE_CSR(pmpaddr13, frame->pmpaddr13);
RV_WRITE_CSR(pmpaddr14, frame->pmpaddr14);
RV_WRITE_CSR(pmpaddr15, frame->pmpaddr15);
RV_WRITE_CSR(pmpcfg0, frame->pmpcfg0);
RV_WRITE_CSR(pmpcfg1, frame->pmpcfg1);
RV_WRITE_CSR(pmpcfg2, frame->pmpcfg2);
RV_WRITE_CSR(pmpcfg3, frame->pmpcfg3);
RV_WRITE_CSR(mcycle, frame->mcycle);
RV_WRITE_CSR(mcycleh, frame->mcycleh);
}
static IRAM_ATTR void cpu_domain_dev_regs_save(cpu_domain_dev_sleep_frame_t *frame)
{
assert(frame);
cpu_domain_dev_regs_region_t *region = frame->region;
uint32_t *regs_frame = frame->regs_frame;
int offset = 0;
for (int i = 0; i < frame->region_num; i++) {
for (uint32_t addr = region[i].start; addr < region[i].end; addr+=4) {
regs_frame[offset++] = *(uint32_t *)addr;
}
}
}
static IRAM_ATTR void cpu_domain_dev_regs_restore(cpu_domain_dev_sleep_frame_t *frame)
{
assert(frame);
cpu_domain_dev_regs_region_t *region = frame->region;
uint32_t *regs_frame = frame->regs_frame;
int offset = 0;
for (int i = 0; i < frame->region_num; i++) {
for (uint32_t addr = region[i].start; addr < region[i].end; addr+=4) {
*(uint32_t *)addr = regs_frame[offset++];
}
}
}
#if CONFIG_PM_CHECK_SLEEP_RETENTION_FRAME
static IRAM_ATTR void update_retention_frame_crc(uint32_t *frame_ptr, uint32_t frame_check_size, uint32_t *frame_crc_ptr)
{
*(frame_crc_ptr) = esp_rom_crc32_le(0, (void *)frame_ptr, frame_check_size);
}
static IRAM_ATTR void validate_retention_frame_crc(uint32_t *frame_ptr, uint32_t frame_check_size, uint32_t *frame_crc_ptr)
{
if(*(frame_crc_ptr) != esp_rom_crc32_le(0, (void *)(frame_ptr), frame_check_size)){
// resume uarts
for (int i = 0; i < SOC_UART_NUM; ++i) {
if (!uart_ll_is_enabled(i)) {
continue;
}
uart_ll_force_xon(i);
}
/* Since it is still in the critical now, use ESP_EARLY_LOG */
ESP_EARLY_LOGE(TAG, "Sleep retention frame is corrupted");
esp_restart_noos();
}
}
#endif
extern RvCoreCriticalSleepFrame * rv_core_critical_regs_save(void);
extern RvCoreCriticalSleepFrame * rv_core_critical_regs_restore(void);
extern void rv_core_fpu_save(RvCoreCriticalSleepFrame *frame);
extern void rv_core_fpu_restore(RvCoreCriticalSleepFrame *frame);
typedef uint32_t (* sleep_cpu_entry_cb_t)(uint32_t, uint32_t, uint32_t, bool);
static IRAM_ATTR esp_err_t do_cpu_retention(sleep_cpu_entry_cb_t goto_sleep,
uint32_t wakeup_opt, uint32_t reject_opt, uint32_t lslp_mem_inf_fpu, bool dslp)
{
uint8_t core_id = esp_cpu_get_core_id();
bool reject = false;
RvCoreCriticalSleepFrame *frame = s_cpu_retention.retent.critical_frame[core_id];
/* mstatus is core privated CSR, do it near the core critical regs restore */
uint32_t mstatus = save_mstatus_and_disable_global_int();
s_fpu_saved[core_id] = xPortFPUContextIsDirty(core_id);
if (s_fpu_saved[core_id]) {
rv_core_fpu_save(frame);
}
rv_core_critical_regs_save();
if ((frame->pmufunc & 0x3) == 0x1) {
esp_sleep_execute_event_callbacks(SLEEP_EVENT_SW_CPU_TO_MEM_END, (void *)0);
#if CONFIG_PM_CHECK_SLEEP_RETENTION_FRAME
/* Minus 2 * sizeof(long) is for bypass `pmufunc` and `frame_crc` field */
update_retention_frame_crc((uint32_t*)frame, RV_SLEEP_CTX_SZ1 - 2 * sizeof(long), (uint32_t *)(&frame->frame_crc));
#endif
REG_WRITE(RTC_SLEEP_WAKE_STUB_ADDR_REG, (uint32_t)rv_core_critical_regs_restore);
#if CONFIG_PM_ESP_SLEEP_POWER_DOWN_CPU && !CONFIG_FREERTOS_UNICORE
atomic_store(&s_smp_retention_state[core_id], SMP_BACKUP_DONE);
while (atomic_load(&s_smp_retention_state[!core_id]) != SMP_BACKUP_DONE) {
;
}
#endif
reject = (*goto_sleep)(wakeup_opt, reject_opt, lslp_mem_inf_fpu, dslp);
}
#if CONFIG_PM_CHECK_SLEEP_RETENTION_FRAME
else {
validate_retention_frame_crc((uint32_t*)frame, RV_SLEEP_CTX_SZ1 - 2 * sizeof(long), (uint32_t *)(&frame->frame_crc));
}
#endif
if (s_fpu_saved[core_id]) {
rv_core_fpu_restore(frame);
}
restore_mstatus(mstatus);
return reject ? reject : pmu_sleep_finish(dslp);
}
esp_err_t IRAM_ATTR esp_sleep_cpu_retention(uint32_t (*goto_sleep)(uint32_t, uint32_t, uint32_t, bool),
uint32_t wakeup_opt, uint32_t reject_opt, uint32_t lslp_mem_inf_fpu, bool dslp)
{
esp_sleep_execute_event_callbacks(SLEEP_EVENT_SW_CPU_TO_MEM_START, (void *)0);
uint8_t core_id = esp_cpu_get_core_id();
#if CONFIG_PM_ESP_SLEEP_POWER_DOWN_CPU && !CONFIG_FREERTOS_UNICORE
atomic_store(&s_smp_retention_state[core_id], SMP_BACKUP_START);
#endif
cpu_domain_dev_regs_save(s_cpu_retention.retent.clic_frame[core_id]);
rv_core_noncritical_regs_save();
#if CONFIG_PM_CHECK_SLEEP_RETENTION_FRAME
RvCoreNonCriticalSleepFrame *frame = s_cpu_retention.retent.non_critical_frame[core_id];
/* Minus sizeof(long) is for bypass `frame_crc` field */
update_retention_frame_crc((uint32_t*)frame, sizeof(RvCoreNonCriticalSleepFrame) - sizeof(long), (uint32_t *)(&frame->frame_crc));
#endif
esp_err_t err = do_cpu_retention(goto_sleep, wakeup_opt, reject_opt, lslp_mem_inf_fpu, dslp);
if (err == ESP_OK) {
#if CONFIG_PM_CHECK_SLEEP_RETENTION_FRAME
validate_retention_frame_crc((uint32_t*)frame, sizeof(RvCoreNonCriticalSleepFrame) - sizeof(long), (uint32_t *)(&frame->frame_crc));
#endif
}
#if CONFIG_PM_ESP_SLEEP_POWER_DOWN_CPU && !CONFIG_FREERTOS_UNICORE
// Start core1
if (core_id == 0) {
REG_SET_BIT(HP_SYS_CLKRST_HPCORE1_CTRL0_REG, HP_SYS_CLKRST_REG_CORE1_CPU_CLK_EN);
REG_CLR_BIT(HP_SYS_CLKRST_HPCORE1_CTRL0_REG, HP_SYS_CLKRST_REG_CORE1_GLOBAL_RST_EN);
}
atomic_store(&s_smp_retention_state[core_id], SMP_RESTORE_START);
#endif
if (err == ESP_OK) {
cpu_domain_dev_regs_restore(s_cpu_retention.retent.clic_frame[core_id]);
rv_core_noncritical_regs_restore();
}
#if CONFIG_PM_ESP_SLEEP_POWER_DOWN_CPU && !CONFIG_FREERTOS_UNICORE
atomic_store(&s_smp_retention_state[core_id], SMP_RESTORE_DONE);
#endif
return err;
}
esp_err_t esp_sleep_cpu_retention_init(void)
{
#if CONFIG_PM_ESP_SLEEP_POWER_DOWN_CPU && !CONFIG_FREERTOS_UNICORE
return esp_sleep_cpu_retention_init_impl(& s_cpu_retention, s_smp_retention_state);
#else
return esp_sleep_cpu_retention_init_impl(& s_cpu_retention);
#endif
}
esp_err_t esp_sleep_cpu_retention_deinit(void)
{
return esp_sleep_cpu_retention_deinit_impl(& s_cpu_retention);
}
bool cpu_domain_pd_allowed(void)
{
bool allowed = true;
for (uint8_t core_id = 0; core_id < portNUM_PROCESSORS; ++core_id) {
allowed &= (s_cpu_retention.retent.critical_frame[core_id] != NULL);
allowed &= (s_cpu_retention.retent.non_critical_frame[core_id] != NULL);
}
for (uint8_t core_id = 0; core_id < portNUM_PROCESSORS; ++core_id) {
allowed &= (s_cpu_retention.retent.clic_frame[core_id] != NULL);
}
return allowed;
}
esp_err_t sleep_cpu_configure(bool light_sleep_enable)
{
#if CONFIG_PM_ESP_SLEEP_POWER_DOWN_CPU
if (light_sleep_enable) {
ESP_RETURN_ON_ERROR(esp_sleep_cpu_retention_init(), TAG, "Failed to enable CPU power down during light sleep.");
} else {
ESP_RETURN_ON_ERROR(esp_sleep_cpu_retention_deinit(), TAG, "Failed to release CPU retention memory");
}
#endif
return ESP_OK;
}
#if !CONFIG_FREERTOS_UNICORE
#if CONFIG_PM_ESP_SLEEP_POWER_DOWN_CPU
static IRAM_ATTR void smp_core_do_retention(void)
{
uint8_t core_id = esp_cpu_get_core_id();
if (core_id == 0) {
WRITE_PERI_REG(HP_SYSTEM_CPU_INT_FROM_CPU_2_REG, 0);
} else {
WRITE_PERI_REG(HP_SYSTEM_CPU_INT_FROM_CPU_3_REG, 0);
}
// Wait another core start to do retention
bool smp_skip_retention = false;
smp_retention_state_t another_core_state;
ESP_COMPILER_DIAGNOSTIC_PUSH_IGNORE("-Wanalyzer-infinite-loop")
while (1) {
another_core_state = atomic_load(&s_smp_retention_state[!core_id]);
if (another_core_state == SMP_SKIP_RETENTION) {
// If another core skips the retention, the current core should also have to skip it.
smp_skip_retention = true;
break;
} else if (another_core_state == SMP_BACKUP_START) {
break;
}
}
ESP_COMPILER_DIAGNOSTIC_POP("-Wanalyzer-infinite-loop")
if (!smp_skip_retention) {
atomic_store(&s_smp_retention_state[core_id], SMP_BACKUP_START);
rv_core_noncritical_regs_save();
cpu_domain_dev_regs_save(s_cpu_retention.retent.clic_frame[core_id]);
RvCoreCriticalSleepFrame *frame_critical = s_cpu_retention.retent.critical_frame[core_id];
uint32_t mstatus = save_mstatus_and_disable_global_int();
s_fpu_saved[core_id] = xPortFPUContextIsDirty(core_id);
if (s_fpu_saved[core_id]) {
rv_core_fpu_save(frame_critical);
}
rv_core_critical_regs_save();
if ((frame_critical->pmufunc & 0x3) == 0x1) {
atomic_store(&s_smp_retention_state[core_id], SMP_BACKUP_DONE);
// wait another core trigger sleep and wakeup
while (1) {
// If another core's sleep request is rejected by the hardware, jumps out of blocking.
another_core_state = atomic_load(&s_smp_retention_state[!core_id]);
if (another_core_state == SMP_SKIP_RETENTION) {
break;
}
}
} else {
// Start core1
if (core_id == 0) {
REG_SET_BIT(HP_SYS_CLKRST_HPCORE1_CTRL0_REG, HP_SYS_CLKRST_REG_CORE1_CPU_CLK_EN);
REG_CLR_BIT(HP_SYS_CLKRST_HPCORE1_CTRL0_REG, HP_SYS_CLKRST_REG_CORE1_GLOBAL_RST_EN);
}
atomic_store(&s_smp_retention_state[core_id], SMP_RESTORE_START);
if (s_fpu_saved[core_id]) {
rv_core_fpu_restore(frame_critical);
}
restore_mstatus(mstatus);
cpu_domain_dev_regs_restore(s_cpu_retention.retent.clic_frame[core_id]);
rv_core_noncritical_regs_restore();
atomic_store(&s_smp_retention_state[core_id], SMP_RESTORE_DONE);
}
}
// wait another core out sleep
while (atomic_load(&s_smp_retention_state[!core_id]) != SMP_IDLE) {
;
}
atomic_store(&s_smp_retention_state[core_id], SMP_IDLE);
}
IRAM_ATTR void esp_sleep_cpu_skip_retention(void) {
atomic_store(&s_smp_retention_state[esp_cpu_get_core_id()], SMP_SKIP_RETENTION);
}
#endif
void sleep_smp_cpu_sleep_prepare(void)
{
#if CONFIG_PM_ESP_SLEEP_POWER_DOWN_CPU
while (atomic_load(&s_smp_retention_state[!esp_cpu_get_core_id()]) != SMP_IDLE) {
;
}
esp_ipc_isr_call((esp_ipc_isr_func_t)smp_core_do_retention, NULL);
#else
esp_ipc_isr_stall_other_cpu();
#endif
}
void sleep_smp_cpu_wakeup_prepare(void)
{
#if CONFIG_PM_ESP_SLEEP_POWER_DOWN_CPU
uint8_t core_id = esp_cpu_get_core_id();
if (atomic_load(&s_smp_retention_state[core_id]) == SMP_RESTORE_DONE) {
ESP_COMPILER_DIAGNOSTIC_PUSH_IGNORE("-Wanalyzer-infinite-loop")
while (atomic_load(&s_smp_retention_state[!core_id]) != SMP_RESTORE_DONE) {
;
}
ESP_COMPILER_DIAGNOSTIC_POP("-Wanalyzer-infinite-loop")
}
atomic_store(&s_smp_retention_state[core_id], SMP_IDLE);
#else
esp_ipc_isr_release_other_cpu();
#endif
}
#endif //!CONFIG_FREERTOS_UNICORE

View File

@@ -0,0 +1,219 @@
/*
* SPDX-FileCopyrightText: 2026 Espressif Systems (Shanghai) CO LTD
*
* SPDX-License-Identifier: Apache-2.0
*/
#include "soc/soc.h"
#include "rvsleep-frames.h"
#include "freertos/FreeRTOSConfig.h"
#include "sdkconfig.h"
#include "riscv/csr_clic.h"
.section .data1,"aw"
.global rv_core_critical_regs_frame
.type rv_core_critical_regs_frame,@object
.align 4
rv_core_critical_regs_frame:
.rept (portNUM_PROCESSORS)
.word 0
.endr
/*
--------------------------------------------------------------------------------
This assembly subroutine is used to save the critical registers of the CPU
core to the internal RAM before sleep, and modify the PMU control flag to
indicate that the system needs to sleep. When the subroutine returns, it
will return the memory pointer that saves the context information of the CPU
critical registers.
--------------------------------------------------------------------------------
*/
.section .iram1,"ax"
.global rv_core_critical_regs_save
.type rv_core_critical_regs_save,@function
.align 4
rv_core_critical_regs_save:
/* arrived here in critical section. we need:
save riscv core critical registers to RvCoreCriticalSleepFrame
*/
csrw mscratch, t0 /* use mscratch as temp storage */
la a0, rv_core_critical_regs_frame
csrr t1, mhartid
slli t1, t1, 2
add a0, a0, t1
lw t0, 0(a0) /* t0 pointer to RvCoreCriticalSleepFrame object */
sw ra, RV_SLP_CTX_RA(t0)
sw sp, RV_SLP_CTX_SP(t0)
sw gp, RV_SLP_CTX_GP(t0)
sw tp, RV_SLP_CTX_TP(t0)
sw t1, RV_SLP_CTX_T1(t0)
sw t2, RV_SLP_CTX_T2(t0)
sw s0, RV_SLP_CTX_S0(t0)
sw s1, RV_SLP_CTX_S1(t0)
/* a0 is caller saved, so it does not need to be saved, but it should be the
pointer value of RvCoreCriticalSleepFrame for return.
*/
mv a0, t0
sw a0, RV_SLP_CTX_A0(t0)
sw a1, RV_SLP_CTX_A1(t0)
sw a2, RV_SLP_CTX_A2(t0)
sw a3, RV_SLP_CTX_A3(t0)
sw a4, RV_SLP_CTX_A4(t0)
sw a5, RV_SLP_CTX_A5(t0)
sw a6, RV_SLP_CTX_A6(t0)
sw a7, RV_SLP_CTX_A7(t0)
sw s2, RV_SLP_CTX_S2(t0)
sw s3, RV_SLP_CTX_S3(t0)
sw s4, RV_SLP_CTX_S4(t0)
sw s5, RV_SLP_CTX_S5(t0)
sw s6, RV_SLP_CTX_S6(t0)
sw s7, RV_SLP_CTX_S7(t0)
sw s8, RV_SLP_CTX_S8(t0)
sw s9, RV_SLP_CTX_S9(t0)
sw s10, RV_SLP_CTX_S10(t0)
sw s11, RV_SLP_CTX_S11(t0)
sw t3, RV_SLP_CTX_T3(t0)
sw t4, RV_SLP_CTX_T4(t0)
sw t5, RV_SLP_CTX_T5(t0)
sw t6, RV_SLP_CTX_T6(t0)
csrr t1, mstatus
sw t1, RV_SLP_CTX_MSTATUS(t0)
csrr t2, mtvec
sw t2, RV_SLP_CTX_MTVEC(t0)
csrr t3, mcause
sw t3, RV_SLP_CTX_MCAUSE(t0)
csrr t1, mtval
sw t1, RV_SLP_CTX_MTVAL(t0)
csrr t2, mie
sw t2, RV_SLP_CTX_MIE(t0)
csrr t3, mip
sw t3, RV_SLP_CTX_MIP(t0)
csrr t1, mepc
sw t1, RV_SLP_CTX_MEPC(t0)
csrr t2, MINTTHRESH_CSR
sw t2, RV_SLP_CTX_MINTTHRESH(t0)
/*
!!! Let idf knows it's going to sleep !!!
RV_SLP_STK_PMUFUNC field is used to identify whether it is going to sleep or
has just been awakened. We use the lowest 2 bits as indication information,
3 means being awakened, 1 means going to sleep.
*/
li t1, ~0x3
lw t2, RV_SLP_CTX_PMUFUNC(t0)
and t2, t1, t2
ori t2, t2, 0x1
sw t2, RV_SLP_CTX_PMUFUNC(t0)
mv t3, t0
csrr t0, mscratch
sw t0, RV_SLP_CTX_T0(t3)
lw t1, RV_SLP_CTX_T1(t3)
lw t2, RV_SLP_CTX_T2(t3)
lw t3, RV_SLP_CTX_T3(t3)
ret
.size rv_core_critical_regs_save, . - rv_core_critical_regs_save
/*
--------------------------------------------------------------------------------
This assembly subroutine is used to restore the CPU core critical register
context before sleep after system wakes up, modify the PMU control
information, and return the critical register context memory object pointer.
After the subroutine returns, continue to restore other modules of the
system.
--------------------------------------------------------------------------------
*/
.section .iram1,"ax"
.global rv_core_critical_regs_restore
.weak rv_core_critical_regs_restore
.type rv_core_critical_regs_restore,@function
.global _rv_core_critical_regs_restore
.type _rv_core_critical_regs_restore,@function
.align 4
_rv_core_critical_regs_restore: /* export a strong symbol to jump to here, used
* for a static callback */
nop
rv_core_critical_regs_restore:
la t0, rv_core_critical_regs_frame
csrr t1, mhartid
slli t1, t1, 2
add t0, t0, t1
lw t0, 0(t0) /* t0 pointer to RvCoreCriticalSleepFrame object */
beqz t0, .skip_restore /* make sure we do not jump to zero address */
/*
!!! Let idf knows it's sleep awake. !!!
RV_SLP_STK_PMUFUNC field is used to identify whether it is going to sleep or
has just been awakened. We use the lowest 2 bits as indication information,
3 means being awakened, 1 means going to sleep.
*/
lw t1, RV_SLP_CTX_PMUFUNC(t0)
ori t1, t1, 0x3
sw t1, RV_SLP_CTX_PMUFUNC(t0)
lw t2, RV_SLP_CTX_MINTTHRESH(t0)
csrw MINTTHRESH_CSR, t2
lw t2, RV_SLP_CTX_MEPC(t0)
csrw mepc, t2
lw t3, RV_SLP_CTX_MIP(t0)
csrw mip, t3
lw t1, RV_SLP_CTX_MIE(t0)
csrw mie, t1
lw t2, RV_SLP_CTX_MSTATUS(t0)
csrw mstatus, t2
lw t3, RV_SLP_CTX_MTVEC(t0)
csrw mtvec, t3
lw t1, RV_SLP_CTX_MCAUSE(t0)
csrw mcause, t1
lw t2, RV_SLP_CTX_MTVAL(t0)
csrw mtval, t2
lw t6, RV_SLP_CTX_T6(t0)
lw t5, RV_SLP_CTX_T5(t0)
lw t4, RV_SLP_CTX_T4(t0)
lw t3, RV_SLP_CTX_T3(t0)
lw s11, RV_SLP_CTX_S11(t0)
lw s10, RV_SLP_CTX_S10(t0)
lw s9, RV_SLP_CTX_S9(t0)
lw s8, RV_SLP_CTX_S8(t0)
lw s7, RV_SLP_CTX_S7(t0)
lw s6, RV_SLP_CTX_S6(t0)
lw s5, RV_SLP_CTX_S5(t0)
lw s4, RV_SLP_CTX_S4(t0)
lw s3, RV_SLP_CTX_S3(t0)
lw s2, RV_SLP_CTX_S2(t0)
lw a7, RV_SLP_CTX_A7(t0)
lw a6, RV_SLP_CTX_A6(t0)
lw a5, RV_SLP_CTX_A5(t0)
lw a4, RV_SLP_CTX_A4(t0)
lw a3, RV_SLP_CTX_A3(t0)
lw a2, RV_SLP_CTX_A2(t0)
lw a1, RV_SLP_CTX_A1(t0)
lw a0, RV_SLP_CTX_A0(t0)
lw s1, RV_SLP_CTX_S1(t0)
lw s0, RV_SLP_CTX_S0(t0)
lw t2, RV_SLP_CTX_T2(t0)
lw t1, RV_SLP_CTX_T1(t0)
lw tp, RV_SLP_CTX_TP(t0)
lw gp, RV_SLP_CTX_GP(t0)
lw sp, RV_SLP_CTX_SP(t0)
lw ra, RV_SLP_CTX_RA(t0)
lw t0, RV_SLP_CTX_T0(t0)
.skip_restore:
ret
.size rv_core_critical_regs_restore, . - rv_core_critical_regs_restore

View File

@@ -0,0 +1,147 @@
/*
* SPDX-FileCopyrightText: 2026 Espressif Systems (Shanghai) CO LTD
*
* SPDX-License-Identifier: Apache-2.0
*/
#include <string.h>
#include "esp_check.h"
#include "esp_heap_caps.h"
#include "soc/clic_reg.h"
#include "esp_sleep.h"
#include "rvsleep-frames.h"
#include "sleep_cpu_retention.h"
extern RvCoreCriticalSleepFrame *rv_core_critical_regs_frame[portNUM_PROCESSORS];
static void * cpu_domain_dev_sleep_frame_alloc_and_init(const cpu_domain_dev_regs_region_t *regions, const int region_num)
{
const int region_sz = sizeof(cpu_domain_dev_regs_region_t) * region_num;
int regs_frame_sz = 0;
for (int num = 0; num < region_num; num++) {
regs_frame_sz += regions[num].end - regions[num].start;
}
void *frame = heap_caps_malloc(sizeof(cpu_domain_dev_sleep_frame_t) + region_sz + regs_frame_sz, MALLOC_CAP_32BIT|MALLOC_CAP_INTERNAL);
if (frame) {
cpu_domain_dev_regs_region_t *region = (cpu_domain_dev_regs_region_t *)(frame + sizeof(cpu_domain_dev_sleep_frame_t));
memcpy(region, regions, region_num * sizeof(cpu_domain_dev_regs_region_t));
void *regs_frame = frame + sizeof(cpu_domain_dev_sleep_frame_t) + region_sz;
memset(regs_frame, 0, regs_frame_sz);
*(cpu_domain_dev_sleep_frame_t *)frame = (cpu_domain_dev_sleep_frame_t) {
.region = region,
.region_num = region_num,
.regs_frame = (uint32_t *)regs_frame
};
}
return frame;
}
static inline void * cpu_domain_clic_sleep_frame_alloc_and_init(uint8_t core_id)
{
const static cpu_domain_dev_regs_region_t regions[portNUM_PROCESSORS][3] = {
[0 ... portNUM_PROCESSORS - 1] = {
{ .start = CLIC_INT_CONFIG_REG, .end = CLIC_INT_CONFIG_REG + 4 },
{ .start = CLIC_INT_THRESH_REG, .end = CLIC_INT_THRESH_REG + 4 },
{ .start = CLIC_INT_CTRL_REG(0), .end = CLIC_INT_CTRL_REG(47) + 4 },
}
};
return cpu_domain_dev_sleep_frame_alloc_and_init(regions[core_id], sizeof(regions[core_id]) / sizeof(cpu_domain_dev_regs_region_t));
}
#if CONFIG_PM_ESP_SLEEP_POWER_DOWN_CPU && !CONFIG_FREERTOS_UNICORE
esp_err_t esp_sleep_cpu_retention_init_impl(sleep_cpu_retention_t *sleep_cpu_retention_ptr, smp_retention_state_t *s_smp_retention_state)
{
for (uint8_t core_id = 0; core_id < portNUM_PROCESSORS; ++core_id) {
if (sleep_cpu_retention_ptr->retent.critical_frame[core_id] == NULL) {
void *frame = heap_caps_calloc(1, RV_SLEEP_CTX_FRMSZ, MALLOC_CAP_32BIT|MALLOC_CAP_INTERNAL);
if (frame == NULL) {
goto err;
}
sleep_cpu_retention_ptr->retent.critical_frame[core_id] = (RvCoreCriticalSleepFrame *)frame;
rv_core_critical_regs_frame[core_id] = (RvCoreCriticalSleepFrame *)frame;
}
if (sleep_cpu_retention_ptr->retent.non_critical_frame[core_id] == NULL) {
void *frame = heap_caps_calloc(1, sizeof(RvCoreNonCriticalSleepFrame), MALLOC_CAP_32BIT|MALLOC_CAP_INTERNAL);
if (frame == NULL) {
goto err;
}
sleep_cpu_retention_ptr->retent.non_critical_frame[core_id] = (RvCoreNonCriticalSleepFrame *)frame;
}
}
for (uint8_t core_id = 0; core_id < portNUM_PROCESSORS; ++core_id) {
if (sleep_cpu_retention_ptr->retent.clic_frame[core_id] == NULL) {
void *frame = cpu_domain_clic_sleep_frame_alloc_and_init(core_id);
if (frame == NULL) {
goto err;
}
sleep_cpu_retention_ptr->retent.clic_frame[core_id] = (cpu_domain_dev_sleep_frame_t *)frame;
}
}
for (uint8_t core_id = 0; core_id < portNUM_PROCESSORS; ++core_id) {
atomic_init(&s_smp_retention_state[core_id], SMP_IDLE);
}
return ESP_OK;
err:
esp_sleep_cpu_retention_deinit();
return ESP_ERR_NO_MEM;
}
#else
esp_err_t esp_sleep_cpu_retention_init_impl(sleep_cpu_retention_t *sleep_cpu_retention_ptr)
{
for (uint8_t core_id = 0; core_id < portNUM_PROCESSORS; ++core_id) {
if (sleep_cpu_retention_ptr->retent.critical_frame[core_id] == NULL) {
void *frame = heap_caps_calloc(1, RV_SLEEP_CTX_FRMSZ, MALLOC_CAP_32BIT|MALLOC_CAP_INTERNAL);
if (frame == NULL) {
goto err;
}
sleep_cpu_retention_ptr->retent.critical_frame[core_id] = (RvCoreCriticalSleepFrame *)frame;
rv_core_critical_regs_frame[core_id] = (RvCoreCriticalSleepFrame *)frame;
}
if (sleep_cpu_retention_ptr->retent.non_critical_frame[core_id] == NULL) {
void *frame = heap_caps_calloc(1, sizeof(RvCoreNonCriticalSleepFrame), MALLOC_CAP_32BIT|MALLOC_CAP_INTERNAL);
if (frame == NULL) {
goto err;
}
sleep_cpu_retention_ptr->retent.non_critical_frame[core_id] = (RvCoreNonCriticalSleepFrame *)frame;
}
}
for (uint8_t core_id = 0; core_id < portNUM_PROCESSORS; ++core_id) {
if (sleep_cpu_retention_ptr->retent.clic_frame[core_id] == NULL) {
void *frame = cpu_domain_clic_sleep_frame_alloc_and_init(core_id);
if (frame == NULL) {
goto err;
}
sleep_cpu_retention_ptr->retent.clic_frame[core_id] = (cpu_domain_dev_sleep_frame_t *)frame;
}
}
return ESP_OK;
err:
esp_sleep_cpu_retention_deinit();
return ESP_ERR_NO_MEM;
}
#endif
esp_err_t esp_sleep_cpu_retention_deinit_impl(sleep_cpu_retention_t *sleep_cpu_retention_ptr)
{
for (uint8_t core_id = 0; core_id < portNUM_PROCESSORS; ++core_id) {
if (sleep_cpu_retention_ptr->retent.critical_frame[core_id]) {
heap_caps_free((void *)sleep_cpu_retention_ptr->retent.critical_frame[core_id]);
sleep_cpu_retention_ptr->retent.critical_frame[core_id] = NULL;
rv_core_critical_regs_frame[core_id] = NULL;
}
if (sleep_cpu_retention_ptr->retent.non_critical_frame[core_id]) {
heap_caps_free((void *)sleep_cpu_retention_ptr->retent.non_critical_frame[core_id]);
sleep_cpu_retention_ptr->retent.non_critical_frame[core_id] = NULL;
}
}
for (uint8_t core_id = 0; core_id < portNUM_PROCESSORS; ++core_id) {
if (sleep_cpu_retention_ptr->retent.clic_frame[core_id]) {
heap_caps_free((void *)sleep_cpu_retention_ptr->retent.clic_frame[core_id]);
sleep_cpu_retention_ptr->retent.clic_frame[core_id] = NULL;
}
}
return ESP_OK;
}

View File

@@ -0,0 +1,57 @@
/*
* SPDX-FileCopyrightText: 2026 Espressif Systems (Shanghai) CO LTD
*
* SPDX-License-Identifier: Apache-2.0
*/
#ifndef __SLEEP_CPU_RETENTION_H__
#define __SLEEP_CPU_RETENTION_H__
#include "rvsleep-frames.h"
#include "freertos/FreeRTOS.h"
#include "esp_err.h"
#if CONFIG_PM_ESP_SLEEP_POWER_DOWN_CPU && !CONFIG_FREERTOS_UNICORE
#include <stdatomic.h>
#include "soc/hp_system_reg.h"
typedef enum {
SMP_IDLE,
SMP_BACKUP_START,
SMP_BACKUP_DONE,
SMP_RESTORE_START,
SMP_RESTORE_DONE,
SMP_SKIP_RETENTION,
} smp_retention_state_t;
#endif
typedef struct {
uint32_t start;
uint32_t end;
} cpu_domain_dev_regs_region_t;
typedef struct {
cpu_domain_dev_regs_region_t *region;
int region_num;
uint32_t *regs_frame;
} cpu_domain_dev_sleep_frame_t;
/**
* Internal structure which holds all requested light sleep cpu retention parameters
*/
typedef struct {
struct {
RvCoreCriticalSleepFrame *critical_frame[portNUM_PROCESSORS];
RvCoreNonCriticalSleepFrame *non_critical_frame[portNUM_PROCESSORS];
cpu_domain_dev_sleep_frame_t *clic_frame[portNUM_PROCESSORS];
} retent;
} sleep_cpu_retention_t;
#if CONFIG_PM_ESP_SLEEP_POWER_DOWN_CPU && !CONFIG_FREERTOS_UNICORE
esp_err_t esp_sleep_cpu_retention_init_impl(sleep_cpu_retention_t *sleep_cpu_retention_ptr, smp_retention_state_t *s_smp_retention_state);
#else
esp_err_t esp_sleep_cpu_retention_init_impl(sleep_cpu_retention_t *sleep_cpu_retention_ptr);
#endif
esp_err_t esp_sleep_cpu_retention_deinit_impl(sleep_cpu_retention_t *sleep_cpu_retention_ptr);
#endif /* #ifndef __SLEEP_CPU_RETENTION_H__ */

View File

@@ -0,0 +1,146 @@
/*
* SPDX-FileCopyrightText: 2026 Espressif Systems (Shanghai) CO LTD
*
* SPDX-License-Identifier: Apache-2.0
*/
#include <string.h>
#include "esp_check.h"
#include "soc/clic_reg.h"
#include "rvsleep-frames.h"
#include "sleep_cpu_retention.h"
extern RvCoreCriticalSleepFrame *rv_core_critical_regs_frame[portNUM_PROCESSORS];
#define R_CONCAT(s1, s2) _R_CONCAT(s1, s2)
#define _R_CONCAT(s1, s2) s1 ## s2
#define CPU_DOMAIN_DEV_SZ0 (CPU_DOMAIN_DEV_END_ADDR0 - CPU_DOMAIN_DEV_START_ADDR0)
#define CPU_DOMAIN_DEV_SZ1 (CPU_DOMAIN_DEV_END_ADDR1 - CPU_DOMAIN_DEV_START_ADDR1)
#define CPU_DOMAIN_DEV_SZ2 (CPU_DOMAIN_DEV_END_ADDR2 - CPU_DOMAIN_DEV_START_ADDR2)
#define CPU_DOMAIN_DEV_SZ3 (CPU_DOMAIN_DEV_END_ADDR3 - CPU_DOMAIN_DEV_START_ADDR3)
#define CPU_DOMAIN_DEV_SZ4 (CPU_DOMAIN_DEV_END_ADDR4 - CPU_DOMAIN_DEV_START_ADDR4)
#define CPU_DOMAIN_DEV_SZ5 (CPU_DOMAIN_DEV_END_ADDR5 - CPU_DOMAIN_DEV_START_ADDR5)
#define CPU_DOMAIN_DEV_SZ6 (CPU_DOMAIN_DEV_END_ADDR6 - CPU_DOMAIN_DEV_START_ADDR6)
#define CPU_DOMAIN_DEV_SZ7 (CPU_DOMAIN_DEV_END_ADDR7 - CPU_DOMAIN_DEV_START_ADDR7)
#define CPU_DOMAIN_DEV_TOTAL_SZ(n) (R_CONCAT(__TOTAL_SZ, n))
#define __TOTAL_SZ0 (sizeof(cpu_domain_dev_sleep_frame_t))
#define __TOTAL_SZ1 ((__TOTAL_SZ0) + (sizeof(cpu_domain_dev_regs_region_t)) + CPU_DOMAIN_DEV_SZ0)
#define __TOTAL_SZ2 ((__TOTAL_SZ1) + (sizeof(cpu_domain_dev_regs_region_t)) + CPU_DOMAIN_DEV_SZ1)
#define __TOTAL_SZ3 ((__TOTAL_SZ2) + (sizeof(cpu_domain_dev_regs_region_t)) + CPU_DOMAIN_DEV_SZ2)
#define __TOTAL_SZ4 ((__TOTAL_SZ3) + (sizeof(cpu_domain_dev_regs_region_t)) + CPU_DOMAIN_DEV_SZ3)
#define __TOTAL_SZ5 ((__TOTAL_SZ4) + (sizeof(cpu_domain_dev_regs_region_t)) + CPU_DOMAIN_DEV_SZ4)
#define __TOTAL_SZ6 ((__TOTAL_SZ5) + (sizeof(cpu_domain_dev_regs_region_t)) + CPU_DOMAIN_DEV_SZ5)
#define __TOTAL_SZ7 ((__TOTAL_SZ6) + (sizeof(cpu_domain_dev_regs_region_t)) + CPU_DOMAIN_DEV_SZ6)
#define __TOTAL_SZ8 ((__TOTAL_SZ7) + (sizeof(cpu_domain_dev_regs_region_t)) + CPU_DOMAIN_DEV_SZ7)
static void * cpu_domain_dev_sleep_frame_alloc_and_init(const cpu_domain_dev_regs_region_t *regions, const int region_num, void * frame)
{
const int region_sz = sizeof(cpu_domain_dev_regs_region_t) * region_num;
int regs_frame_sz = 0;
for (int num = 0; num < region_num; num++) {
regs_frame_sz += regions[num].end - regions[num].start;
}
if (frame) {
cpu_domain_dev_regs_region_t *region = (cpu_domain_dev_regs_region_t *)(frame + sizeof(cpu_domain_dev_sleep_frame_t));
memcpy(region, regions, region_num * sizeof(cpu_domain_dev_regs_region_t));
void *regs_frame = frame + sizeof(cpu_domain_dev_sleep_frame_t) + region_sz;
memset(regs_frame, 0, regs_frame_sz);
*(cpu_domain_dev_sleep_frame_t *)frame = (cpu_domain_dev_sleep_frame_t) {
.region = region,
.region_num = region_num,
.regs_frame = (uint32_t *)regs_frame
};
}
return frame;
}
static inline void * cpu_domain_clic_sleep_frame_alloc_and_init(uint8_t core_id)
{
static DRAM_ATTR cpu_domain_dev_regs_region_t regions[portNUM_PROCESSORS][2] = {
[0 ... portNUM_PROCESSORS - 1] = {
{ .start = CLIC_INT_CONFIG_REG, .end = CLIC_INT_CONFIG_REG + 4 },
{ .start = CLIC_INT_THRESH_REG, .end = CLIC_INT_THRESH_REG + 4 },
{ .start = CLIC_INT_CTRL_REG(0), .end = CLIC_INT_CTRL_REG(47) + 4 },
}
};
static DRAM_ATTR uint8_t sleep_frame[portNUM_PROCESSORS][CPU_DOMAIN_DEV_TOTAL_SZ(2)] __attribute__((aligned(4)));
return cpu_domain_dev_sleep_frame_alloc_and_init(regions[core_id], sizeof(regions[core_id]) / sizeof(regions[core_id][0]), sleep_frame[core_id]);
}
#if CONFIG_PM_ESP_SLEEP_POWER_DOWN_CPU && !CONFIG_FREERTOS_UNICORE
esp_err_t esp_sleep_cpu_retention_init_impl(sleep_cpu_retention_t *sleep_cpu_retention_ptr, smp_retention_state_t *s_smp_retention_state)
{
static DRAM_ATTR uint8_t rv_core_critical_regs[RV_SLEEP_CTX_FRMSZ * portNUM_PROCESSORS] __attribute__((aligned(4)));
static DRAM_ATTR uint8_t rv_core_non_critical_regs[sizeof(RvCoreNonCriticalSleepFrame)* portNUM_PROCESSORS] __attribute__((aligned(4)));
for (uint8_t core_id = 0; core_id < portNUM_PROCESSORS; ++core_id) {
if (sleep_cpu_retention_ptr->retent.critical_frame[core_id] == NULL) {
void * regs = rv_core_critical_regs + core_id * RV_SLEEP_CTX_FRMSZ;
sleep_cpu_retention_ptr->retent.critical_frame[core_id] = (RvCoreCriticalSleepFrame *) regs;
rv_core_critical_regs_frame[core_id] = (RvCoreCriticalSleepFrame *) regs;
}
if (sleep_cpu_retention_ptr->retent.non_critical_frame[core_id] == NULL) {
sleep_cpu_retention_ptr->retent.non_critical_frame[core_id] = (RvCoreNonCriticalSleepFrame *)(rv_core_non_critical_regs+core_id * sizeof(RvCoreNonCriticalSleepFrame));
}
}
for (uint8_t core_id = 0; core_id < portNUM_PROCESSORS; ++core_id) {
if (sleep_cpu_retention_ptr->retent.clic_frame[core_id] == NULL) {
void *frame = cpu_domain_clic_sleep_frame_alloc_and_init(core_id);
sleep_cpu_retention_ptr->retent.clic_frame[core_id] = (cpu_domain_dev_sleep_frame_t *)frame;
}
}
for (uint8_t core_id = 0; core_id < portNUM_PROCESSORS; ++core_id) {
atomic_init(&s_smp_retention_state[core_id], SMP_IDLE);
}
return ESP_OK;
}
#else
esp_err_t esp_sleep_cpu_retention_init_impl(sleep_cpu_retention_t *sleep_cpu_retention_ptr)
{
static DRAM_ATTR uint8_t rv_core_critical_regs[RV_SLEEP_CTX_FRMSZ * portNUM_PROCESSORS] __attribute__((aligned(4)));
static DRAM_ATTR uint8_t rv_core_non_critical_regs[sizeof(RvCoreNonCriticalSleepFrame)* portNUM_PROCESSORS] __attribute__((aligned(4)));
for (uint8_t core_id = 0; core_id < portNUM_PROCESSORS; ++core_id) {
if (sleep_cpu_retention_ptr->retent.critical_frame[core_id] == NULL) {
void * regs = rv_core_critical_regs + core_id * RV_SLEEP_CTX_FRMSZ;
sleep_cpu_retention_ptr->retent.critical_frame[core_id] = (RvCoreCriticalSleepFrame *) regs;
rv_core_critical_regs_frame[core_id] = (RvCoreCriticalSleepFrame *) regs;
}
if (sleep_cpu_retention_ptr->retent.non_critical_frame[core_id] == NULL) {
sleep_cpu_retention_ptr->retent.non_critical_frame[core_id] = (RvCoreNonCriticalSleepFrame *)(rv_core_non_critical_regs+core_id * sizeof(RvCoreNonCriticalSleepFrame));
}
}
for (uint8_t core_id = 0; core_id < portNUM_PROCESSORS; ++core_id) {
if (sleep_cpu_retention_ptr->retent.clic_frame[core_id] == NULL) {
void *frame = cpu_domain_clic_sleep_frame_alloc_and_init(core_id);
sleep_cpu_retention_ptr->retent.clic_frame[core_id] = (cpu_domain_dev_sleep_frame_t *)frame;
}
}
return ESP_OK;
}
#endif
esp_err_t esp_sleep_cpu_retention_deinit_impl(sleep_cpu_retention_t *sleep_cpu_retention_ptr)
{
for (uint8_t core_id = 0; core_id < portNUM_PROCESSORS; ++core_id) {
if (sleep_cpu_retention_ptr->retent.critical_frame[core_id]) {
sleep_cpu_retention_ptr->retent.critical_frame[core_id] = NULL;
rv_core_critical_regs_frame[core_id] = NULL;
}
if (sleep_cpu_retention_ptr->retent.non_critical_frame[core_id]) {
sleep_cpu_retention_ptr->retent.non_critical_frame[core_id] = NULL;
}
}
for (uint8_t core_id = 0; core_id < portNUM_PROCESSORS; ++core_id) {
if (sleep_cpu_retention_ptr->retent.clic_frame[core_id]) {
sleep_cpu_retention_ptr->retent.clic_frame[core_id] = NULL;
}
}
return ESP_OK;
}

View File

@@ -0,0 +1,118 @@
/*
* SPDX-FileCopyrightText: 2026 Espressif Systems (Shanghai) CO LTD
*
* SPDX-License-Identifier: Apache-2.0
*/
#include "rvsleep-frames.h"
#include "freertos/FreeRTOSConfig.h"
#include "sdkconfig.h"
.section .iram1,"ax"
.global rv_core_fpu_save
.type rv_core_fpu_save,@function
.align 4
/* Bit to set in mstatus to enable the FPU */
#define CSR_MSTATUS_FPU_ENABLE (1 << 13)
/* Bit to clear in mstatus to disable the FPU */
#define CSR_MSTATUS_FPU_DISABLE (3 << 13)
.macro fpu_enable reg
li \reg, CSR_MSTATUS_FPU_ENABLE
csrs mstatus, \reg
.endm
rv_core_fpu_save:
fpu_enable t0
mv t0, a0 /* t0 = frame (arg0) */
fsw ft0, RV_SLP_CTX_FPU_FT0(t0)
fsw ft1, RV_SLP_CTX_FPU_FT1(t0)
fsw ft2, RV_SLP_CTX_FPU_FT2(t0)
fsw ft3, RV_SLP_CTX_FPU_FT3(t0)
fsw ft4, RV_SLP_CTX_FPU_FT4(t0)
fsw ft5, RV_SLP_CTX_FPU_FT5(t0)
fsw ft6, RV_SLP_CTX_FPU_FT6(t0)
fsw ft7, RV_SLP_CTX_FPU_FT7(t0)
fsw fs0, RV_SLP_CTX_FPU_FS0(t0)
fsw fs1, RV_SLP_CTX_FPU_FS1(t0)
fsw fa0, RV_SLP_CTX_FPU_FA0(t0)
fsw fa1, RV_SLP_CTX_FPU_FA1(t0)
fsw fa2, RV_SLP_CTX_FPU_FA2(t0)
fsw fa3, RV_SLP_CTX_FPU_FA3(t0)
fsw fa4, RV_SLP_CTX_FPU_FA4(t0)
fsw fa5, RV_SLP_CTX_FPU_FA5(t0)
fsw fa6, RV_SLP_CTX_FPU_FA6(t0)
fsw fa7, RV_SLP_CTX_FPU_FA7(t0)
fsw fs2, RV_SLP_CTX_FPU_FS2(t0)
fsw fs3, RV_SLP_CTX_FPU_FS3(t0)
fsw fs4, RV_SLP_CTX_FPU_FS4(t0)
fsw fs5, RV_SLP_CTX_FPU_FS5(t0)
fsw fs6, RV_SLP_CTX_FPU_FS6(t0)
fsw fs7, RV_SLP_CTX_FPU_FS7(t0)
fsw fs8, RV_SLP_CTX_FPU_FS8(t0)
fsw fs9, RV_SLP_CTX_FPU_FS9(t0)
fsw fs10, RV_SLP_CTX_FPU_FS10(t0)
fsw fs11, RV_SLP_CTX_FPU_FS11(t0)
fsw ft8, RV_SLP_CTX_FPU_FT8(t0)
fsw ft9, RV_SLP_CTX_FPU_FT9(t0)
fsw ft10, RV_SLP_CTX_FPU_FT10(t0)
fsw ft11, RV_SLP_CTX_FPU_FT11(t0)
csrr t1, fcsr
sw t1, RV_SLP_CTX_FPU_FCSR(t0)
/* Chip will go to sleep and FPU will be powered down, not necessary to disable FPU here. */
ret
.size rv_core_fpu_save, . - rv_core_fpu_save
/*
--------------------------------------------------------------------------------
FPU restore: a0 = RvCoreCriticalSleepFrame *. Restore all FP registers from the frame.
--------------------------------------------------------------------------------
*/
.section .iram1,"ax"
.global rv_core_fpu_restore
.type rv_core_fpu_restore,@function
.align 4
rv_core_fpu_restore:
fpu_enable t0
mv t0, a0 /* t0 = frame (arg0) */
flw ft0, RV_SLP_CTX_FPU_FT0(t0)
flw ft1, RV_SLP_CTX_FPU_FT1(t0)
flw ft2, RV_SLP_CTX_FPU_FT2(t0)
flw ft3, RV_SLP_CTX_FPU_FT3(t0)
flw ft4, RV_SLP_CTX_FPU_FT4(t0)
flw ft5, RV_SLP_CTX_FPU_FT5(t0)
flw ft6, RV_SLP_CTX_FPU_FT6(t0)
flw ft7, RV_SLP_CTX_FPU_FT7(t0)
flw fs0, RV_SLP_CTX_FPU_FS0(t0)
flw fs1, RV_SLP_CTX_FPU_FS1(t0)
flw fa0, RV_SLP_CTX_FPU_FA0(t0)
flw fa1, RV_SLP_CTX_FPU_FA1(t0)
flw fa2, RV_SLP_CTX_FPU_FA2(t0)
flw fa3, RV_SLP_CTX_FPU_FA3(t0)
flw fa4, RV_SLP_CTX_FPU_FA4(t0)
flw fa5, RV_SLP_CTX_FPU_FA5(t0)
flw fa6, RV_SLP_CTX_FPU_FA6(t0)
flw fa7, RV_SLP_CTX_FPU_FA7(t0)
flw fs2, RV_SLP_CTX_FPU_FS2(t0)
flw fs3, RV_SLP_CTX_FPU_FS3(t0)
flw fs4, RV_SLP_CTX_FPU_FS4(t0)
flw fs5, RV_SLP_CTX_FPU_FS5(t0)
flw fs6, RV_SLP_CTX_FPU_FS6(t0)
flw fs7, RV_SLP_CTX_FPU_FS7(t0)
flw fs8, RV_SLP_CTX_FPU_FS8(t0)
flw fs9, RV_SLP_CTX_FPU_FS9(t0)
flw fs10, RV_SLP_CTX_FPU_FS10(t0)
flw fs11, RV_SLP_CTX_FPU_FS11(t0)
flw ft8, RV_SLP_CTX_FPU_FT8(t0)
flw ft9, RV_SLP_CTX_FPU_FT9(t0)
flw ft10, RV_SLP_CTX_FPU_FT10(t0)
flw ft11, RV_SLP_CTX_FPU_FT11(t0)
lw t1, RV_SLP_CTX_FPU_FCSR(t0)
csrw fcsr, t1
/* Caller restores mstatus (and thus FPU state); not necessary to disable FPU here. */
ret
.size rv_core_fpu_restore, . - rv_core_fpu_restore

View File

@@ -19,9 +19,10 @@ extern "C" {
#define PMU_UART0_WAKEUP_EN BIT(6)
#define PMU_UART1_WAKEUP_EN BIT(7)
#define PMU_BLE_SOC_WAKEUP_EN BIT(10)
#define PMU_LP_CORE_WAKEUP_EN BIT(11)
#define PMU_LP_CORE_WAKEUP_HP_EN BIT(11)
#define PMU_LP_CORE_TRAP_WAKEUP_EN BIT(12)
#define PMU_USB_WAKEUP_EN BIT(14)
#define PMU_MODEM_WAKEUP_PROTECT BIT(16)
#ifdef __cplusplus
}

View File

@@ -20,10 +20,10 @@ extern "C" {
#define PMU_UART1_WAKEUP_EN BIT(7)
#define PMU_SDIO_WAKEUP_EN BIT(8)
#define PMU_BLE_SOC_WAKEUP_EN BIT(10)
#define PMU_LP_CORE_WAKEUP_EN BIT(11)
#define PMU_LP_CORE_WAKEUP_HP_EN BIT(11)
#define PMU_LP_CORE_TRAP_WAKEUP_EN BIT(12)
#define PMU_USB_WAKEUP_EN BIT(14)
#define PMU_MODEM_WAKEUP_PROTECT BIT(16)
#ifdef __cplusplus
}
#endif

View File

@@ -20,8 +20,9 @@ extern "C" {
#define PMU_UART1_WAKEUP_EN BIT(7)
#define PMU_UART2_WAKEUP_EN BIT(9)
#define PMU_BLE_SOC_WAKEUP_EN BIT(10)
// #define PMU_LP_CORE_WAKEUP_EN BIT(11)
#define PMU_LP_CORE_WAKEUP_HP_EN BIT(11)
#define PMU_USB_WAKEUP_EN BIT(14)
#define PMU_MODEM_WAKEUP_PROTECT BIT(16)
#ifdef __cplusplus
}

View File

@@ -422,7 +422,7 @@ SPM_IRAM_ATTR uint32_t pmu_sleep_start(uint32_t wakeup_opt, uint32_t reject_opt,
#if CONFIG_PM_SLP_SPIRAM_HALFSLEEP_ENABLED
esp_psram_impl_enter_halfsleep_mode();
#endif
mspi_ll_hold_all_psram_pins();
mspi_ll_psram_hold_all_pins();
#endif
s_mpll_freq_mhz_before_sleep = rtc_clk_mpll_get_freq();
if (s_mpll_freq_mhz_before_sleep) {
@@ -519,7 +519,7 @@ SPM_IRAM_ATTR bool pmu_sleep_finish(bool dslp)
#endif
}
#if CONFIG_SPIRAM
mspi_ll_unhold_all_psram_pins();
mspi_ll_psram_unhold_all_pins();
#if CONFIG_PM_SLP_SPIRAM_HALFSLEEP_ENABLED
esp_psram_impl_exit_halfsleep_mode();
#endif

View File

@@ -1,5 +1,5 @@
/*
* SPDX-FileCopyrightText: 2023-2024 Espressif Systems (Shanghai) CO LTD
* SPDX-FileCopyrightText: 2023-2026 Espressif Systems (Shanghai) CO LTD
*
* SPDX-License-Identifier: Apache-2.0
*/
@@ -11,7 +11,7 @@ extern "C" {
#endif
#define PMU_SDIO_WAKEUP_EN BIT(0)
#define PMU_LP_CORE_WAKEUP_EN BIT(1)
#define PMU_LP_CORE_WAKEUP_HP_EN BIT(1)
#define PMU_GPIO_WAKEUP_EN BIT(2)
#define PMU_USB_WAKEUP_EN BIT(3)
#define PMU_UART4_WAKEUP_EN BIT(4)
@@ -31,6 +31,8 @@ extern "C" {
#define PMU_LP_TIMER1_WAKEUP_EN BIT(18)
#define PMU_LP_I2S_WAKEUP_EN BIT(19)
#define PMU_INTERRUPT_WAKEUP BIT(23)
// BIT order in PLL control registers in PMU
#define PMU_CPLL_CTRL BIT(0)
#define PMU_SPLL_CTRL BIT(1)

View File

@@ -82,13 +82,15 @@ bool peripheral_domain_pd_allowed(void)
RETENTION_MODULE_BITMAP_SET(&mask, SLEEP_RETENTION_MODULE_MCPWM2);
RETENTION_MODULE_BITMAP_SET(&mask, SLEEP_RETENTION_MODULE_MCPWM3);
// ESP32S31 supports PCNT sleep retention
RETENTION_MODULE_BITMAP_SET(&mask, SLEEP_RETENTION_MODULE_PCNT0);
RETENTION_MODULE_BITMAP_SET(&mask, SLEEP_RETENTION_MODULE_PCNT1);
// ESP32S31 supports SDM sleep retention
RETENTION_MODULE_BITMAP_SET(&mask, SLEEP_RETENTION_MODULE_SDM0);
// ESP32S31 supports LCDCAM sleep retention
RETENTION_MODULE_BITMAP_SET(&mask, SLEEP_RETENTION_MODULE_LCDCAM);
// ESP32S31 supports JPEG sleep retention
RETENTION_MODULE_BITMAP_SET(&mask, SLEEP_RETENTION_MODULE_JPEG);
const sleep_retention_module_bitmap_t peripheral_domain_inited_modules = sleep_retention_module_bitmap_and(inited_modules, mask);
const sleep_retention_module_bitmap_t peripheral_domain_created_modules = sleep_retention_module_bitmap_and(created_modules, mask);
return sleep_retention_module_bitmap_eq(peripheral_domain_inited_modules, peripheral_domain_created_modules);

View File

@@ -5,6 +5,7 @@
*/
#include <stdint.h>
#include <stdbool.h>
#include <stdlib.h>
#include <esp_types.h>
#include "sdkconfig.h"
@@ -13,6 +14,9 @@
#include "soc/soc.h"
#include "soc/pmu_struct.h"
#include "hal/efuse_hal.h"
#include "hal/efuse_ll.h"
#include "hal/gpio_ll.h"
#include "hal/gpio_types.h"
#include "hal/pmu_hal.h"
#include "pmu_param.h"
#include "esp_private/esp_pmu.h"
@@ -38,9 +42,17 @@ pmu_context_t * __attribute__((weak)) IRAM_ATTR PMU_instance(void)
{
/* It should be explicitly defined in the internal RAM, because this
* instance will be used in pmu_sleep.c */
static DRAM_ATTR pmu_hal_context_t pmu_hal = { .dev = &PMU };
static DRAM_ATTR pmu_hal_context_t pmu_hal = { .dev = NULL };
static DRAM_ATTR pmu_sleep_machine_constant_t pmu_mc = PMU_SLEEP_MC_DEFAULT();
static DRAM_ATTR pmu_context_t pmu_context = { .hal = &pmu_hal, .mc = (void *)&pmu_mc };
if (pmu_hal.dev == NULL) {
pmu_hal.dev = &PMU;
const uint32_t sel = efuse_ll_get_flash_power_sel();
const uint32_t en = efuse_ll_get_flash_power_sel_en();
const int g36 = gpio_ll_get_level(GPIO_LL_GET_HW(0), (uint32_t)GPIO_NUM_36);
pmu_context.flash_ldo_volt_1v8 = (sel == 0 && g36 == 0) || (sel == 1 && en == 0);
}
return &pmu_context;
}
@@ -90,14 +102,14 @@ void pmu_hp_system_init(pmu_context_t *ctx, pmu_hp_mode_t mode, pmu_hp_system_pa
pmu_ll_hp_set_regulator_dbias_select (ctx->hal->dev, mode, anlg->regulator0.dbias_sel);
pmu_ll_hp_set_regulator_dbias_init (ctx->hal->dev, mode, anlg->regulator0.dig_dbias_init);
}
pmu_ll_hp_set_regulator_sleep_memory_xpd (ctx->hal->dev, mode, anlg->regulator0.slp_mem_xpd);
pmu_ll_hp_set_regulator_sleep_memory_dbias(ctx->hal->dev, mode, anlg->regulator0.slp_mem_dbias);
pmu_ll_hp_set_regulator_sleep_logic_xpd (ctx->hal->dev, mode, anlg->regulator0.slp_logic_xpd);
pmu_ll_hp_set_regulator_sleep_logic_dbias (ctx->hal->dev, mode, anlg->regulator0.slp_logic_dbias);
pmu_ll_hp_set_regulator_dbias (ctx->hal->dev, mode, anlg->regulator0.dbias);
pmu_ll_hp_set_regulator_xpd (ctx->hal->dev, mode, anlg->regulator0.xpd);
pmu_ll_hp_set_regulator_sleep_memory_xpd (ctx->hal->dev, mode, anlg->regulator0.slp_mem_xpd);
pmu_ll_hp_set_regulator_sleep_memory_dbias (ctx->hal->dev, mode, anlg->regulator0.slp_mem_dbias);
pmu_ll_hp_set_regulator_sleep_logic_xpd (ctx->hal->dev, mode, anlg->regulator0.slp_logic_xpd);
pmu_ll_hp_set_regulator_sleep_logic_dbias (ctx->hal->dev, mode, anlg->regulator0.slp_logic_dbias);
pmu_ll_hp_set_regulator_dbias (ctx->hal->dev, mode, anlg->regulator0.dbias);
pmu_ll_hp_set_regulator_xpd (ctx->hal->dev, mode, anlg->regulator0.xpd);
pmu_ll_hp_set_regulator_sleep_connect_enable(ctx->hal->dev, mode, anlg->regulator0.slp_connect_en);
pmu_ll_hp_set_regulator_driver_bar (ctx->hal->dev, mode, anlg->regulator1.drv_b);
pmu_ll_hp_set_regulator_driver_bar (ctx->hal->dev, mode, anlg->regulator1.drv_b);
/* Default configuration of hp-system retention sub-system in active, modem
* and sleep modes */
@@ -158,6 +170,8 @@ static inline void pmu_power_domain_force_default(pmu_context_t *ctx)
}
/* Isolate all memory banks while sleeping, avoid memory leakage current */
pmu_ll_hp_set_memory_no_isolate (ctx->hal->dev, 0);
/* Disable memory force pu for memory pd during deep sleep */
pmu_ll_hp_set_memory_power_up (ctx->hal->dev, 0);
pmu_ll_lp_set_power_force_power_up (ctx->hal->dev, false);
pmu_ll_lp_set_power_force_no_reset (ctx->hal->dev, false);
@@ -210,6 +224,4 @@ void pmu_init(void)
pmu_hp_system_init_default(PMU_instance());
pmu_lp_system_init_default(PMU_instance());
pmu_power_domain_force_default(PMU_instance());
WRITE_PERI_REG(PMU_POWER_PD_MEM_CNTL_REG, 0);
}

View File

@@ -38,9 +38,15 @@ ESP_HW_LOG_ATTR_TAG(TAG, "pmu_param");
.clk_power = { \
.i2c_iso_en = 0, \
.i2c_retention = 0, \
.xpd_bb_i2c = 0, \
.xpd_pll_i2c = 0x3, \
.xpd_pll = 0x3 \
.xpd_bb_i2c = 1, \
.xpd_cpll_i2c = 1, \
.xpd_bbpll_i2c = 1, \
.xpd_apll_i2c = 0, \
.xpd_mpll_i2c = 1, \
.xpd_cpll = 1, \
.xpd_bbpll = 1, \
.xpd_apll = 0, \
.xpd_mpll = 1, \
}, \
.xtal = { \
.xpd_xtal = 1 \
@@ -62,9 +68,15 @@ ESP_HW_LOG_ATTR_TAG(TAG, "pmu_param");
.clk_power = { \
.i2c_iso_en = 1, \
.i2c_retention = 1, \
.xpd_bb_i2c = 0, \
.xpd_pll_i2c = 0x3, \
.xpd_pll = 0x3 \
.xpd_bb_i2c = 1, \
.xpd_cpll_i2c = 1, \
.xpd_bbpll_i2c = 1, \
.xpd_apll_i2c = 0, \
.xpd_mpll_i2c = 0, \
.xpd_cpll = 1, \
.xpd_bbpll = 1, \
.xpd_apll = 0, \
.xpd_mpll = 0, \
}, \
.xtal = { \
.xpd_xtal = 1 \
@@ -87,8 +99,14 @@ ESP_HW_LOG_ATTR_TAG(TAG, "pmu_param");
.i2c_iso_en = 1, \
.i2c_retention = 1, \
.xpd_bb_i2c = 0, \
.xpd_pll_i2c = 0, \
.xpd_pll = 0 \
.xpd_cpll_i2c = 0, \
.xpd_bbpll_i2c = 0, \
.xpd_apll_i2c = 0, \
.xpd_mpll_i2c = 0, \
.xpd_cpll = 0, \
.xpd_bbpll = 0, \
.xpd_apll = 0, \
.xpd_mpll = 0, \
}, \
.xtal = { \
.xpd_xtal = 0 \
@@ -178,27 +196,21 @@ const pmu_hp_system_clock_param_t * pmu_hp_system_clock_param_default(pmu_hp_mod
.syscntl = { \
.c_channel = 1, \
.uart_wakeup_en = 0, \
.lp_pad_hold_all = 0, \
.hp_pad_hold_all = 0, \
.dig_pad_slp_sel = 1, \
.lp_pad_hold_all = 1, \
.hp_pad_hold_all = 1, \
.dig_pad_slp_sel = 0, \
.dig_pause_wdt = 1, \
.dig_cpu_stall = 1 \
} \
}
/**
* - hp_pad_hold_all:
* - when top off: must set to 1;
* - when top on: 0 if use pad; 1 if not use pad
* - dig_pad_slp_sel: must set to 1 when sleep
*/
#define PMU_HP_SLEEP_DIGITAL_CONFIG_DEFAULT() { \
.syscntl = { \
.c_channel = 0, \
.uart_wakeup_en = 1, \
.lp_pad_hold_all = 0, \
.lp_pad_hold_all = 1, \
.hp_pad_hold_all = 1, \
.dig_pad_slp_sel = 1, \
.dig_pad_slp_sel = 0, \
.dig_pause_wdt = 1, \
.dig_cpu_stall = 1 \
} \

View File

@@ -16,10 +16,21 @@
#include "soc/pmu_struct.h"
#include "esp_private/esp_pmu.h"
#include "pmu_param.h"
#include "hal/clk_tree_hal.h"
#include "hal/lp_aon_hal.h"
#include "hal/efuse_ll.h"
#include "hal/efuse_hal.h"
#include "hal/mspi_ll.h"
#include "esp_hw_log.h"
#if CONFIG_SPIRAM
#include "esp_private/esp_psram_impl.h"
#include "hal/psram_ctrlr_ll.h"
#endif
#if !SOC_APM_SUPPORTED
#include "hal/apm_hal.h"
#endif
#include "esp_private/rtc_clk.h"
ESP_HW_LOG_ATTR_TAG(TAG, "pmu_sleep");
@@ -28,6 +39,7 @@ ESP_HW_LOG_ATTR_TAG(TAG, "pmu_sleep");
static bool s_pmu_sleep_regdma_backup_enabled;
static uint32_t s_mpll_freq_mhz_before_sleep = 0;
void pmu_sleep_enable_regdma_backup(void)
{
@@ -200,6 +212,14 @@ const pmu_sleep_config_t* pmu_sleep_config_default(
// Get light sleep analog default
pmu_sleep_analog_config_t analog_default = PMU_SLEEP_ANALOG_LSLP_CONFIG_DEFAULT(sleep_flags);
#if !CONFIG_SPIRAM
if (PMU_instance()->flash_ldo_volt_1v8)
#endif
{
analog_default.hp_sys.analog.pd_cur = PMU_PD_CUR_SLEEP_ON;
analog_default.lp_sys[LP(SLEEP)].analog.pd_cur = PMU_PD_CUR_SLEEP_ON;
}
if (!(sleep_flags & PMU_SLEEP_PD_XTAL))
{
// Analog parameters in HP_SLEEP
@@ -238,6 +258,13 @@ static void pmu_sleep_power_init(pmu_context_t *ctx, const pmu_sleep_power_confi
pmu_ll_hp_set_clk_power(ctx->hal->dev, HP(SLEEP), power->hp_sys.clk_power.val);
pmu_ll_hp_set_xtal_xpd (ctx->hal->dev, HP(SLEEP), power->hp_sys.xtal.xpd_xtal);
if (dslp) {
pmu_ll_hp_set_memory_power_on_mask(ctx->hal->dev, 0);
} else {
pmu_ll_hp_set_memory_power_on_mask(ctx->hal->dev, 0xf);
}
pmu_ll_set_rf_power_control(ctx->hal->dev, 0);
pmu_ll_lp_set_dig_power(ctx->hal->dev, LP(ACTIVE), power->lp_sys[LP(ACTIVE)].dig_power.val);
pmu_ll_lp_set_clk_power(ctx->hal->dev, LP(ACTIVE), power->lp_sys[LP(ACTIVE)].clk_power.val);
@@ -249,26 +276,24 @@ static void pmu_sleep_power_init(pmu_context_t *ctx, const pmu_sleep_power_confi
static void pmu_sleep_digital_init(pmu_context_t *ctx, const pmu_sleep_digital_config_t *dig)
{
pmu_ll_hp_set_dig_pad_slp_sel (ctx->hal->dev, HP(SLEEP), dig->syscntl.dig_pad_slp_sel);
pmu_ll_hp_set_hold_all_hp_pad (ctx->hal->dev, HP(SLEEP), dig->syscntl.hp_pad_hold_all);
pmu_ll_hp_set_hold_all_lp_pad (ctx->hal->dev, HP(SLEEP), dig->syscntl.lp_pad_hold_all);
pmu_ll_hp_set_pause_watchdog (ctx->hal->dev, HP(SLEEP), dig->syscntl.dig_pause_wdt);
pmu_ll_hp_set_c_channel_enable (ctx->hal->dev, HP(SLEEP), dig->syscntl.c_channel);
}
static void pmu_sleep_analog_init(pmu_context_t *ctx, const pmu_sleep_analog_config_t *analog, bool dslp)
{
assert(ctx->hal);
pmu_ll_hp_set_current_power_off (ctx->hal->dev, HP(SLEEP), analog->hp_sys.analog.pd_cur);
pmu_ll_hp_set_bias_sleep_enable (ctx->hal->dev, HP(SLEEP), analog->hp_sys.analog.bias_sleep);
pmu_ll_hp_set_regulator_sleep_memory_xpd (ctx->hal->dev, HP(SLEEP), analog->hp_sys.analog.slp_mem_xpd);
pmu_ll_hp_set_regulator_sleep_logic_xpd (ctx->hal->dev, HP(SLEEP), analog->hp_sys.analog.slp_logic_xpd);
pmu_ll_hp_set_regulator_xpd (ctx->hal->dev, HP(SLEEP), analog->hp_sys.analog.xpd);
pmu_ll_hp_set_regulator_sleep_logic_dbias (ctx->hal->dev, HP(SLEEP), analog->hp_sys.analog.slp_logic_dbias);
pmu_ll_hp_set_dbg_atten (ctx->hal->dev, HP(SLEEP), analog->hp_sys.analog.dbg_atten);
pmu_ll_hp_set_regulator_dbias (ctx->hal->dev, HP(SLEEP), analog->hp_sys.analog.dbias);
pmu_ll_hp_set_regulator_driver_bar (ctx->hal->dev, HP(SLEEP), analog->hp_sys.analog.drv_b);
pmu_ll_lp_set_regulator_sleep_dbias(ctx->hal->dev, LP(ACTIVE), analog->lp_sys[LP(ACTIVE)].analog.slp_dbias);
pmu_ll_lp_set_regulator_dbias (ctx->hal->dev, LP(ACTIVE), analog->lp_sys[LP(ACTIVE)].analog.dbias);
pmu_ll_lp_set_regulator_driver_bar (ctx->hal->dev, LP(ACTIVE), analog->lp_sys[LP(ACTIVE)].analog.drv_b);
pmu_ll_hp_set_current_power_off (ctx->hal->dev, HP(SLEEP), analog->hp_sys.analog.pd_cur);
pmu_ll_hp_set_bias_sleep_enable (ctx->hal->dev, HP(SLEEP), analog->hp_sys.analog.bias_sleep);
pmu_ll_hp_set_regulator_sleep_memory_xpd (ctx->hal->dev, HP(SLEEP), analog->hp_sys.analog.slp_mem_xpd);
pmu_ll_hp_set_regulator_sleep_logic_xpd (ctx->hal->dev, HP(SLEEP), analog->hp_sys.analog.slp_logic_xpd);
pmu_ll_hp_set_regulator_xpd (ctx->hal->dev, HP(SLEEP), analog->hp_sys.analog.xpd);
pmu_ll_hp_set_regulator_sleep_logic_dbias (ctx->hal->dev, HP(SLEEP), analog->hp_sys.analog.slp_logic_dbias);
pmu_ll_hp_set_dbg_atten (ctx->hal->dev, HP(SLEEP), analog->hp_sys.analog.dbg_atten);
pmu_ll_hp_set_regulator_dbias (ctx->hal->dev, HP(SLEEP), analog->hp_sys.analog.dbias);
pmu_ll_hp_set_regulator_driver_bar (ctx->hal->dev, HP(SLEEP), analog->hp_sys.analog.drv_b);
pmu_ll_lp_set_current_power_off (ctx->hal->dev, LP(SLEEP), analog->lp_sys[LP(SLEEP)].analog.pd_cur);
pmu_ll_lp_set_bias_sleep_enable (ctx->hal->dev, LP(SLEEP), analog->lp_sys[LP(SLEEP)].analog.bias_sleep);
@@ -278,9 +303,6 @@ static void pmu_sleep_analog_init(pmu_context_t *ctx, const pmu_sleep_analog_con
pmu_ll_lp_set_regulator_dbias (ctx->hal->dev, LP(SLEEP), analog->lp_sys[LP(SLEEP)].analog.dbias);
pmu_ll_lp_set_dbg_atten (ctx->hal->dev, LP(SLEEP), analog->lp_sys[LP(SLEEP)].analog.dbg_atten);
pmu_ll_lp_set_regulator_driver_bar (ctx->hal->dev, LP(SLEEP), analog->lp_sys[LP(SLEEP)].analog.drv_b);
pmu_ll_lp_set_regulator_slp_xpd (ctx->hal->dev, LP(ACTIVE), analog->lp_sys[LP(ACTIVE)].analog.slp_xpd);
pmu_ll_lp_set_regulator_xpd (ctx->hal->dev, LP(ACTIVE), analog->lp_sys[LP(ACTIVE)].analog.xpd);
}
static void pmu_sleep_param_init(pmu_context_t *ctx, const pmu_sleep_param_config_t *param, bool dslp)
@@ -318,6 +340,16 @@ void pmu_sleep_init(const pmu_sleep_config_t *config, bool dslp)
IRAM_ATTR uint32_t pmu_sleep_start(uint32_t wakeup_opt, uint32_t reject_opt, uint32_t lslp_mem_inf_fpu, bool dslp)
{
if (!dslp) {
#if CONFIG_SPIRAM
psram_ctrlr_ll_wait_all_transaction_done();
#if CONFIG_PM_SLP_SPIRAM_HALFSLEEP_ENABLED
esp_psram_impl_enter_halfsleep_mode();
#endif
mspi_ll_psram_hold_all_pins();
#endif
s_mpll_freq_mhz_before_sleep = rtc_clk_mpll_get_freq();
}
lp_aon_hal_inform_wakeup_type(dslp);
pmu_ll_hp_set_wakeup_enable(&PMU, wakeup_opt);
@@ -352,6 +384,26 @@ IRAM_ATTR bool pmu_sleep_finish(bool dslp)
// Wait eFuse memory update done.
while(efuse_ll_get_controller_state() != EFUSE_CONTROLLER_STATE_IDLE);
#endif
if (!dslp) {
if (s_mpll_freq_mhz_before_sleep) {
rtc_clk_mpll_enable();
rtc_clk_mpll_configure(clk_hal_xtal_get_freq_mhz(), s_mpll_freq_mhz_before_sleep, false);
}
#if CONFIG_SPIRAM
mspi_ll_psram_unhold_all_pins();
#if CONFIG_PM_SLP_SPIRAM_HALFSLEEP_ENABLED
esp_psram_impl_exit_halfsleep_mode();
#endif
#endif
}
#if !SOC_APM_SUPPORTED
apm_hal_enable_ctrl_filter_all(false);
#else
ESP_STATIC_ASSERT(0, "TEE/APM retention need to be supported!");
#endif
return pmu_ll_hp_is_sleep_reject(&PMU);
}

View File

@@ -10,11 +10,11 @@
extern "C" {
#endif
#define PMU_SDIO_WAKEUP_EN BIT(0)
#define PMU_LP_CORE_WAKEUP_EN BIT(1)
#define PMU_LP_CORE_WAKEUP_HP_EN BIT(0)
#define PMU_HP_CORE_WAKEUP_LP_EN BIT(1)
#define PMU_GPIO_WAKEUP_EN BIT(2)
#define PMU_USB_WAKEUP_EN BIT(3)
#define PMU_UART4_WAKEUP_EN BIT(4)
#define PMU_INTERRUPT_WAKEUP BIT(4)
#define PMU_UART3_WAKEUP_EN BIT(5)
#define PMU_UART2_WAKEUP_EN BIT(6)
#define PMU_UART1_WAKEUP_EN BIT(7)
@@ -25,14 +25,13 @@ extern "C" {
#define PMU_EXT1_WAKEUP_EN BIT(12)
#define PMU_RTC_TIMER_WAKEUP_EN BIT(13)
#define PMU_BOD_WAKEUP_EN BIT(14)
#define PMU_VBAT_UNDERVOLT_WAKEUP_EN BIT(15)
#define PMU_LP_CORE_TRAP_WAKEUP_EN BIT(16)
#define PMU_ETM_WAKEUP_EN BIT(17)
#define PMU_LP_TIMER1_WAKEUP_EN BIT(18)
#define PMU_LP_I2S_WAKEUP_EN BIT(19)
#define PMU_WIFI_SOC_WAKEUP_EN BIT(22)
#define PMU_WIFI_BEACON_WAKEUP_EN BIT(23)
#define PMU_BLE_SOC_WAKEUP_EN BIT(24)
#define PMU_MODEM_WAKEUP_PROTECT BIT(24)
#define PMU_BLE_SOC_WAKEUP_EN BIT(25)
// BIT order in PLL control registers in PMU

View File

@@ -278,7 +278,7 @@ typedef struct {
.pd_modem_top_pd_en = ((sleep_flags) & PMU_SLEEP_PD_MODEM) ? 1 : 0, \
.pd_hp_cnnt_pd_en = ((sleep_flags) & PMU_SLEEP_PD_CNNT) ? 1 : 0, \
.pd_hp_cpu_pd_en = ((sleep_flags) & PMU_SLEEP_PD_CPU) ? 1 : 0, \
.pd_modem_pwr_pd_en = 0, \
.pd_modem_pwr_pd_en = ((sleep_flags) & PMU_SLEEP_PD_MEM) ? 1 : 0, \
.pd_top_pd_en = ((sleep_flags) & PMU_SLEEP_PD_TOP) ? 1 : 0, \
.pd_hp_mem_pd_en = 0, \
.mem_dslp = 0, \
@@ -330,15 +330,19 @@ typedef struct {
#define PMU_SLEEP_DIGITAL_DSLP_CONFIG_DEFAULT(sleep_flags) { \
.syscntl = { \
.dig_pad_slp_sel = 0, \
.lp_pad_hold_all = (sleep_flags & PMU_SLEEP_PD_LP_PERIPH) ? 1 : 0, \
.lp_pad_hold_all = 1, \
.hp_pad_hold_all = 1, \
.dig_pause_wdt = ((sleep_flags) & RTC_SLEEP_USE_RTC_WDT) ? 0 : 1, \
.c_channel = (sleep_flags & PMU_SLEEP_PD_TOP) ? 0 : 1 \
} \
}
#define PMU_SLEEP_DIGITAL_LSLP_CONFIG_DEFAULT(sleep_flags) { \
.syscntl = { \
.dig_pad_slp_sel = 0, \
.lp_pad_hold_all = (sleep_flags & PMU_SLEEP_PD_LP_PERIPH) ? 1 : 0, \
.dig_pad_slp_sel = ((sleep_flags) & PMU_SLEEP_PD_TOP) ? 0 : 1, \
.lp_pad_hold_all = ((sleep_flags) & PMU_SLEEP_PD_TOP) ? 1 : 0, \
.hp_pad_hold_all = ((sleep_flags) & PMU_SLEEP_PD_TOP) ? 1 : 0, \
.dig_pause_wdt = ((sleep_flags) & RTC_SLEEP_USE_RTC_WDT) ? 0 : 1, \
.c_channel = (sleep_flags & PMU_SLEEP_PD_TOP) ? 0 : 1 \
} \
}
@@ -363,15 +367,6 @@ typedef struct {
.dbias = PMU_HP_DBIAS_LIGHTSLEEP_0V6 \
} \
}, \
.lp_sys[PMU_MODE_LP_ACTIVE] = { \
.analog = { \
.slp_xpd = 0, \
.slp_dbias = 0, \
.xpd = 1, \
.dbias = 29, \
.drv_b = 0x0 \
} \
}, \
.lp_sys[PMU_MODE_LP_SLEEP] = { \
.analog = { \
.drv_b = PMU_LP_DRVB_LIGHTSLEEP, \
@@ -395,15 +390,6 @@ typedef struct {
.dbg_atten = PMU_DBG_HP_DEEPSLEEP \
} \
}, \
.lp_sys[PMU_MODE_LP_ACTIVE] = { \
.analog = { \
.xpd = 1, \
.dbias = 0x1a, \
.slp_xpd = 0, \
.slp_dbias = 0, \
.drv_b = 0x7 \
} \
}, \
.lp_sys[PMU_MODE_LP_SLEEP] = { \
.analog = { \
.drv_b = PMU_LP_DRVB_DEEPSLEEP, \
@@ -501,7 +487,7 @@ typedef struct pmu_sleep_machine_constant {
}, \
.hp = { \
.min_slp_time_us = 450, \
.clock_domain_sync_time_us = 150, \
.clock_domain_sync_time_us = 2, \
.system_dfs_up_work_time_us = 124, \
.analog_wait_time_us = 154, \
.isolate_wait_time_us = 1, \
@@ -509,9 +495,9 @@ typedef struct pmu_sleep_machine_constant {
.power_supply_wait_time_us = 2, \
.power_up_wait_time_us = 2, \
.regdma_s2m_work_time_us = 172, \
.regdma_s2a_work_time_us = 685, \
.regdma_s2a_work_time_us = 750, \
.regdma_m2a_work_time_us = 278, \
.regdma_a2s_work_time_us = 382, \
.regdma_a2s_work_time_us = 400, \
.regdma_rf_on_work_time_us = 70, \
.regdma_rf_off_work_time_us = 23, \
.xtal_wait_stable_time_us = 250, \

View File

@@ -15,6 +15,7 @@
#include "esp_private/rtc_clk.h"
#include "esp_hw_log.h"
#include "esp_rom_sys.h"
#include "esp_sleep.h"
#include "hal/clk_tree_ll.h"
#include "esp_private/sleep_event.h"
#include "esp_private/regi2c_ctrl.h"
@@ -87,19 +88,13 @@ void rtc_clk_slow_src_set(soc_rtc_slow_clk_src_t clk_src)
{
clk_ll_rtc_slow_set_src(clk_src);
esp_rom_delay_us(SOC_DELAY_RTC_SLOW_CLK_SWITCH);
// TODO: IDF-14645
// #ifndef BOOTLOADER_BUILD
// if (clk_src == SOC_RTC_SLOW_CLK_SRC_XTAL32K) {
// esp_sleep_pd_config(ESP_PD_DOMAIN_XTAL32K, ESP_PD_OPTION_ON);
// } else {
// esp_sleep_pd_config(ESP_PD_DOMAIN_XTAL32K, ESP_PD_OPTION_AUTO);
// }
// if (clk_src == SOC_RTC_SLOW_CLK_SRC_RC32K) {
// esp_sleep_pd_config(ESP_PD_DOMAIN_RC32K, ESP_PD_OPTION_ON);
// } else {
// esp_sleep_pd_config(ESP_PD_DOMAIN_RC32K, ESP_PD_OPTION_AUTO);
// }
// #endif
#ifndef BOOTLOADER_BUILD
if (clk_src == SOC_RTC_SLOW_CLK_SRC_XTAL32K) {
esp_sleep_pd_config(ESP_PD_DOMAIN_XTAL32K, ESP_PD_OPTION_ON);
} else {
esp_sleep_pd_config(ESP_PD_DOMAIN_XTAL32K, ESP_PD_OPTION_AUTO);
}
#endif
}
soc_rtc_slow_clk_src_t rtc_clk_slow_src_get(void)

View File

@@ -0,0 +1,81 @@
/*
* SPDX-FileCopyrightText: 2026 Espressif Systems (Shanghai) CO LTD
*
* SPDX-License-Identifier: Apache-2.0
*/
#include <assert.h>
#include <stdint.h>
#include "sdkconfig.h"
#include "soc/soc_caps.h"
#include "esp_attr.h"
#include "esp_cpu.h"
#include "esp_private/sleep_cache.h"
#include "esp_private/cache_utils.h"
#include "hal/cache_ll.h"
#if SOC_PMU_SUPPORTED
#include "esp_private/esp_pmu.h"
#endif
static int s_cache_suspend_cnt = 0;
static uint32_t s_cache_state = 0;
// Must be called from critical sections.
void sleep_cache_suspend(void)
{
s_cache_suspend_cnt++;
if (s_cache_suspend_cnt == 1) {
#if CONFIG_ESP_SLEEP_CACHE_SAFE_ASSERTION && CONFIG_IDF_TARGET_ESP32P4
// The implementation of P4 L2 cache suspend is to shut down MSPI AXI instead of shutting down Cache BUS.
// If the access to external memory hits in the cache, it will not trigger a cache error. So in order to
// fully check the access to external memory, writeback & invalidate is needed here.
Cache_WriteBack_Invalidate_All(CACHE_MAP_MASK);
#endif
spi_flash_disable_cache(esp_cpu_get_core_id(), &s_cache_state);
}
}
// Must be called from critical sections.
void sleep_cache_resume(void)
{
s_cache_suspend_cnt--;
assert(s_cache_suspend_cnt >= 0 && DRAM_STR("cache resume doesn't match suspend ops"));
if (s_cache_suspend_cnt == 0) {
spi_flash_restore_cache(esp_cpu_get_core_id(), s_cache_state);
}
}
#if !CONFIG_SOC_CACHE_INTERNAL_MEM_VIA_L1CACHE && CONFIG_SPIRAM
void sleep_cache_safe_writeback(uint32_t sleep_flags)
{
#if CONFIG_PM_POWER_DOWN_CPU_IN_LIGHT_SLEEP && !SOC_PM_CACHE_RETENTION_BY_PAU && SOC_EXT_MEM_CACHE_TAG_IN_CPU_DOMAIN
#if SOC_PMU_SUPPORTED
/* When SPIRAM is using, we need to use Cache_WriteBack_All to protect SPIRAM data
because the cache powers down when we power down the CPU */
if (sleep_flags & PMU_SLEEP_PD_CPU) {
if (s_cache_suspend_cnt) {
spi_flash_restore_cache(esp_cpu_get_core_id(), s_cache_state);
}
Cache_WriteBack_All();
if (s_cache_suspend_cnt) {
spi_flash_disable_cache(esp_cpu_get_core_id(), &s_cache_state);
}
}
#else
(void)sleep_flags;
#endif
#elif CONFIG_PM_POWER_DOWN_PERIPHERAL_IN_LIGHT_SLEEP && SOC_PM_CACHE_RETENTION_BY_PAU
(void)sleep_flags;
if (s_cache_suspend_cnt) {
spi_flash_restore_cache(esp_cpu_get_core_id(), s_cache_state);
}
Cache_WriteBack_All(CACHE_MAP_L1_DCACHE);
if (s_cache_suspend_cnt) {
spi_flash_disable_cache(esp_cpu_get_core_id(), &s_cache_state);
}
#else
(void)sleep_flags;
#endif
}
#endif

View File

@@ -5,6 +5,7 @@
*/
#include <stddef.h>
#include <stdint.h>
#include <string.h>
#include <sys/lock.h>
#include <sys/param.h>
@@ -27,6 +28,7 @@
#include "hal/rtc_hal.h"
#include "esp_private/esp_gpio_reserve.h"
#include "esp_private/gpio.h"
#include "esp_private/sleep_gpio.h"
#include "esp_private/spi_flash_os.h"
@@ -173,17 +175,99 @@ void esp_sleep_enable_gpio_switch(bool enable)
}
#endif
#if !SOC_GPIO_SUPPORT_HOLD_SINGLE_IO_IN_DSLP
IRAM_ATTR void esp_sleep_isolate_digital_gpio(void)
#if !SOC_GPIO_SUPPORT_HOLD_SINGLE_IO_IN_DSLP || SOC_GPIO_NEED_SOFT_ISOLATE_DURING_PD
IRAM_ATTR static void sleep_gpio_isolate_one_pin(gpio_num_t gpio_num)
{
gpio_hal_context_t gpio_hal = {
.dev = GPIO_HAL_GET_HW(GPIO_PORT_0)
};
#if SOC_GPIO_NEED_SOFT_ISOLATE_DURING_PD
gpio_io_config_t isolate_config = { .pu = 0, .pd = 0, .ie = 0, .oe = 0, .fun_sel = PIN_FUNC_GPIO };
gpio_ll_set_pad_config_for_sleep_isolate(gpio_num, &isolate_config);
#else
gpio_hal_context_t gpio_hal = { .dev = GPIO_HAL_GET_HW(GPIO_PORT_0) };
gpio_hal_input_disable(&gpio_hal, gpio_num);
gpio_hal_output_disable(&gpio_hal, gpio_num);
gpio_hal_pullup_dis(&gpio_hal, gpio_num);
gpio_hal_pulldown_dis(&gpio_hal, gpio_num);
gpio_hal_func_sel(&gpio_hal, gpio_num, PIN_FUNC_GPIO);
#endif
}
#if SOC_GPIO_NEED_SOFT_ISOLATE_DURING_PD
typedef struct {
uint64_t backuped;
uint64_t pu;
uint64_t pd;
uint64_t ie;
uint64_t oe;
uint8_t fun_sel[GPIO_NUM_MAX];
} gpio_isolate_backup_t;
static DRAM_ATTR gpio_isolate_backup_t s_gpio_isolate_backup;
IRAM_ATTR static void sleep_gpio_backup_one_pin(gpio_num_t gpio_num)
{
gpio_hal_context_t gpio_hal = { .dev = GPIO_HAL_GET_HW(GPIO_PORT_0) };
gpio_io_config_t io_config;
gpio_ll_backup_pad_config_for_sleep_isolate(gpio_num, &io_config);
if (io_config.pu) {
s_gpio_isolate_backup.pu |= (1ULL << gpio_num);
}
if (io_config.pd) {
s_gpio_isolate_backup.pd |= (1ULL << gpio_num);
}
if (io_config.ie) {
s_gpio_isolate_backup.ie |= (1ULL << gpio_num);
}
if (gpio_ll_output_is_enabled(gpio_hal.dev, gpio_num)) {
s_gpio_isolate_backup.oe |= (1ULL << gpio_num);
}
s_gpio_isolate_backup.fun_sel[gpio_num] = (uint8_t)io_config.fun_sel;
s_gpio_isolate_backup.backuped |= (1ULL << gpio_num);
}
IRAM_ATTR void esp_sleep_isolate_mspi_gpio(void)
{
DRAM_ATTR static const esp_mspi_io_t s_mspi_pins[] = {
ESP_MSPI_IO_CLK, ESP_MSPI_IO_Q, ESP_MSPI_IO_D, ESP_MSPI_IO_HD, ESP_MSPI_IO_WP
};
for (size_t i = 0; i < sizeof(s_mspi_pins) / sizeof(s_mspi_pins[0]); i++) {
gpio_num_t gpio_num = (gpio_num_t)esp_mspi_get_io(s_mspi_pins[i]);
sleep_gpio_backup_one_pin(gpio_num);
#if CONFIG_ESP_SLEEP_MSPI_NEED_ALL_IO_PU && !SOC_MSPI_HAS_INDEPENT_IOMUX
gpio_hal_context_t gpio_hal = { .dev = GPIO_HAL_GET_HW(GPIO_PORT_0) };
gpio_hal_pullup_en(&gpio_hal, gpio_num);
#else
sleep_gpio_isolate_one_pin(gpio_num);
#endif
}
}
IRAM_ATTR void esp_sleep_restore_isolated_digital_gpio(void)
{
uint64_t backuped = s_gpio_isolate_backup.backuped;
while (backuped) {
gpio_num_t gpio_num = (gpio_num_t)__builtin_ctzll(backuped);
gpio_io_config_t io_config = {
.pu = (s_gpio_isolate_backup.pu >> gpio_num) & 0x1,
.pd = (s_gpio_isolate_backup.pd >> gpio_num) & 0x1,
.ie = (s_gpio_isolate_backup.ie >> gpio_num) & 0x1,
.oe = (s_gpio_isolate_backup.oe >> gpio_num) & 0x1,
.fun_sel = (uint32_t)s_gpio_isolate_backup.fun_sel[gpio_num],
};
gpio_ll_set_pad_config_for_sleep_isolate(gpio_num, &io_config);
backuped &= backuped - 1;
}
}
#endif // SOC_GPIO_NEED_SOFT_ISOLATE_DURING_PD
IRAM_ATTR void esp_sleep_isolate_digital_gpio(bool do_backup)
{
gpio_hal_context_t gpio_hal = { .dev = GPIO_HAL_GET_HW(GPIO_PORT_0) };
#if !SOC_GPIO_SUPPORT_HOLD_SINGLE_IO_IN_DSLP
/* no need to do isolate if digital IOs are not being held in deep sleep */
if (!gpio_hal_deep_sleep_hold_is_en(&gpio_hal)) {
return;
}
#endif
/**
* there is a situation where we cannot isolate digital IO before deep sleep:
@@ -193,42 +277,61 @@ IRAM_ATTR void esp_sleep_isolate_digital_gpio(void)
* the bottom current of deep sleep will be higher than light sleep, and there is no
* reason to use deep sleep at this time.
*/
assert(esp_ptr_internal(&gpio_hal) && "If hold digital IO, the stack of the task calling esp_deep_sleep_start must be in internal ram!");
assert(esp_ptr_internal(esp_cpu_get_sp()) && "If hold digital IO, the stack of the task calling esp_deep_sleep_start must be in internal ram!");
/* isolate digital IO that is not held(keep the configuration of digital IOs held by users) */
for (gpio_num_t gpio_num = GPIO_NUM_0; gpio_num < GPIO_NUM_MAX; gpio_num++) {
if (GPIO_IS_VALID_DIGITAL_IO_PAD(gpio_num) && !gpio_hal_is_digital_io_hold(&gpio_hal, gpio_num)) {
bool is_mspi_io_pad = false;
esp_mspi_io_t mspi_ios[] = { ESP_MSPI_IO_CS0, ESP_MSPI_IO_CLK, ESP_MSPI_IO_Q, ESP_MSPI_IO_D, ESP_MSPI_IO_HD, ESP_MSPI_IO_WP };
for (int i = 0; i < sizeof(mspi_ios) / sizeof(mspi_ios[0]); i++) {
if (esp_mspi_get_io(mspi_ios[i]) == gpio_num) {
is_mspi_io_pad = true;
break;
}
}
// Ignore MSPI and default Console UART io pads, When the CPU executes
// the following instructions to configure the MSPI IO PAD, access on
// the MSPI signal lines (as CPU instruction execution and MSPI access
// operations are asynchronous) may cause the SoC to hang.
if (is_mspi_io_pad || gpio_num == U0RXD_GPIO_NUM || gpio_num == U0TXD_GPIO_NUM) {
continue;
}
/* disable I/O */
gpio_hal_input_disable(&gpio_hal, gpio_num);
gpio_hal_output_disable(&gpio_hal, gpio_num);
/* disable pull up/down */
gpio_hal_pullup_dis(&gpio_hal, gpio_num);
gpio_hal_pulldown_dis(&gpio_hal, gpio_num);
/* make pad work as gpio(otherwise, deep sleep bottom current will rise) */
gpio_hal_func_sel(&gpio_hal, gpio_num, PIN_FUNC_GPIO);
}
DRAM_ATTR static volatile uint64_t s_pad_mask = SOC_GPIO_VALID_DIGITAL_IO_PAD_MASK;
uint64_t pad_mask = s_pad_mask;
#if SOC_GPIO_NEED_SOFT_ISOLATE_DURING_PD
if (do_backup) {
s_gpio_isolate_backup.backuped = 0;
s_gpio_isolate_backup.pu = 0;
s_gpio_isolate_backup.pd = 0;
s_gpio_isolate_backup.ie = 0;
s_gpio_isolate_backup.oe = 0;
}
uint64_t hold_mask = gpio_ll_get_digital_gpio_hold_mask(gpio_hal.dev);
gpio_io_config_t isolate_config = { .pu = 0, .pd = 0, .ie = 0, .oe = 0, .fun_sel = PIN_FUNC_GPIO };
while (pad_mask) {
gpio_num_t gpio_num = (gpio_num_t)__builtin_ctzll(pad_mask);
if (!(hold_mask & (1ULL << gpio_num)) &&
!esp_gpio_is_reserved(BIT64(gpio_num))) {
if (do_backup) {
gpio_io_config_t io_config;
gpio_ll_backup_pad_config_for_sleep_isolate(gpio_num, &io_config);
if (io_config.pu) {
s_gpio_isolate_backup.pu |= (1ULL << gpio_num);
}
if (io_config.pd) {
s_gpio_isolate_backup.pd |= (1ULL << gpio_num);
}
if (io_config.ie) {
s_gpio_isolate_backup.ie |= (1ULL << gpio_num);
}
if (gpio_ll_output_is_enabled(gpio_hal.dev, gpio_num)) {
s_gpio_isolate_backup.oe |= (1ULL << gpio_num);
}
s_gpio_isolate_backup.fun_sel[gpio_num] = (uint8_t)io_config.fun_sel;
s_gpio_isolate_backup.backuped |= (1ULL << gpio_num);
}
gpio_ll_set_pad_config_for_sleep_isolate(gpio_num, &isolate_config);
}
pad_mask &= pad_mask - 1;
}
#else
(void)do_backup;
while (pad_mask) {
gpio_num_t gpio_num = (gpio_num_t)__builtin_ctzll(pad_mask);
if (!gpio_hal_is_digital_io_hold(&gpio_hal, gpio_num) &&
!esp_gpio_is_reserved(BIT64(gpio_num))) {
sleep_gpio_isolate_one_pin(gpio_num);
}
pad_mask &= pad_mask - 1;
}
#endif
}
#endif //!SOC_GPIO_SUPPORT_HOLD_SINGLE_IO_IN_DSLP
#endif //!SOC_GPIO_SUPPORT_HOLD_SINGLE_IO_IN_DSLP || SOC_GPIO_NEED_SOFT_ISOLATE_DURING_PD
#if SOC_DEEP_SLEEP_SUPPORTED
static void esp_deep_sleep_wakeup_io_reset(void)
@@ -250,10 +353,8 @@ static void esp_deep_sleep_wakeup_io_reset(void)
#endif
#if SOC_GPIO_SUPPORT_HP_PERIPH_PD_SLEEP_WAKEUP
gpio_hal_context_t gpio_hal = { .dev = GPIO_HAL_GET_HW(GPIO_PORT_0) };
uint64_t dslp_io_mask = SOC_GPIO_HP_PERIPH_PD_SLEEP_WAKEABLE_MASK;
gpio_hal_context_t gpio_hal = {
.dev = GPIO_HAL_GET_HW(GPIO_PORT_0)
};
while (dslp_io_mask) {
int gpio_num = __builtin_ctzll(dslp_io_mask);
bool wakeup_io_enabled = gpio_hal_wakeup_is_enabled_on_hp_periph_powerdown_sleep(&gpio_hal, gpio_num);

View File

@@ -225,7 +225,7 @@ uint32_t sleep_modem_reject_triggers(void)
{
uint32_t reject_triggers = 0;
#if SOC_PM_SUPPORT_PMU_MODEM_STATE
reject_triggers = (s_sleep_modem.wifi.phy_link != NULL) ? BIT(16) : 0;
reject_triggers = (s_sleep_modem.wifi.phy_link != NULL) ? PMU_MODEM_WAKEUP_PROTECT : 0;
#endif
return reject_triggers;
}

View File

@@ -66,7 +66,6 @@
#include "soc/rtc.h"
#include "hal/cache_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"
@@ -88,11 +87,11 @@
#include "sdkconfig.h"
#include "esp_rom_serial_output.h"
#include "esp_rom_sys.h"
#include "esp_private/cache_utils.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"
@@ -105,7 +104,6 @@
#endif
#ifdef CONFIG_IDF_TARGET_ESP32
#include "esp32/rom/cache.h"
#include "esp32/rom/rtc.h"
#include "esp_private/gpio.h"
#elif CONFIG_IDF_TARGET_ESP32S2
@@ -131,21 +129,22 @@
#include "hal/gpio_ll.h"
#elif CONFIG_IDF_TARGET_ESP32H2
#include "esp32h2/rom/rtc.h"
#include "esp32h2/rom/cache.h"
#include "soc/extmem_reg.h"
#include "hal/gpio_ll.h"
#elif CONFIG_IDF_TARGET_ESP32H21
#include "esp32h21/rom/rtc.h"
#include "esp32h21/rom/cache.h"
#include "hal/gpio_ll.h"
#elif CONFIG_IDF_TARGET_ESP32H4
#include "esp32h4/rom/rtc.h"
#include "esp32h4/rom/cache.h"
#include "hal/gpio_ll.h"
#elif CONFIG_IDF_TARGET_ESP32P4
#include "esp32p4/rom/rtc.h"
#include "hal/gpio_ll.h"
#include "hal/clk_gate_ll.h"
#elif CONFIG_IDF_TARGET_ESP32S31
#include "esp32s31/rom/rtc.h"
#include "hal/gpio_ll.h"
#include "hal/clk_gate_ll.h"
#endif
#if CONFIG_ESP_INT_WDT && CONFIG_ESP32_ECO3_CACHE_LOCK_FIX
@@ -245,6 +244,9 @@
#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)
#endif
// Actually costs 80us, using the fastest slow clock 150K calculation takes about 16 ticks
@@ -288,7 +290,7 @@ typedef struct {
#endif
portMUX_TYPE lock;
uint64_t sleep_duration;
uint32_t wakeup_triggers : 20;
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;
@@ -579,32 +581,6 @@ static void s_do_deep_sleep_phy_callback(void)
}
#endif
static int s_cache_suspend_cnt = 0;
static uint32_t s_cache_state = 0;
// Must be called from critical sections.
static void FORCE_IRAM_ATTR suspend_cache(void) {
s_cache_suspend_cnt++;
if (s_cache_suspend_cnt == 1) {
#if CONFIG_ESP_SLEEP_CACHE_SAFE_ASSERTION && CONFIG_IDF_TARGET_ESP32P4
// The implementation of P4 L2 cache suspend is to shut down MSPI AXI instead of shutting down Cache BUS.
// If the access to external memory hits in the cache, it will not trigger a cache error. So in order to
// fully check the access to external memory, writeback & invalidate is needed here.
Cache_WriteBack_Invalidate_All(CACHE_MAP_MASK);
#endif
spi_flash_disable_cache(esp_cpu_get_core_id(), &s_cache_state);
}
}
// Must be called from critical sections.
static void FORCE_IRAM_ATTR resume_cache(void) {
s_cache_suspend_cnt--;
assert(s_cache_suspend_cnt >= 0 && DRAM_STR("cache resume doesn't match suspend ops"));
if (s_cache_suspend_cnt == 0) {
spi_flash_restore_cache(esp_cpu_get_core_id(), s_cache_state);
}
}
#if SOC_SLEEP_TGWDT_STOP_WORKAROUND
static uint32_t s_stopped_tgwdt_bmap = 0;
#endif
@@ -694,7 +670,7 @@ static SLEEP_FN_ATTR void misc_modules_sleep_prepare(uint32_t sleep_flags, bool
// 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
#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();
}
@@ -711,12 +687,8 @@ static SLEEP_FN_ATTR void misc_modules_sleep_prepare(uint32_t sleep_flags, bool
#if CONFIG_PM_POWER_DOWN_CPU_IN_LIGHT_SLEEP && SOC_PM_CPU_RETENTION_BY_RTCCNTL
sleep_enable_cpu_retention();
#endif
#if CONFIG_PM_POWER_DOWN_CPU_IN_LIGHT_SLEEP && SOC_PM_CPU_RETENTION_BY_SW && SOC_EXT_MEM_CACHE_TAG_IN_CPU_DOMAIN && CONFIG_SPIRAM
/* When using SPIRAM on the ESP32-C5, we need to use Cache_WriteBack_All to protect SPIRAM data
because the cache powers down when we power down the CPU */
if(sleep_flags & PMU_SLEEP_PD_CPU) {
Cache_WriteBack_All();
}
#if !SOC_CACHE_INTERNAL_MEM_VIA_L1CACHE && CONFIG_SPIRAM
sleep_cache_safe_writeback(sleep_flags);
#endif
#if ADC_LL_ANA_CALI_REG_PD_WORKAROUND
adc_hal_i2c_saradc_reg_backup();
@@ -762,7 +734,7 @@ static SLEEP_FN_ATTR void misc_modules_wake_prepare(uint32_t sleep_flags)
#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
#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();
}
@@ -883,16 +855,16 @@ static esp_err_t FORCE_IRAM_ATTR esp_sleep_start_safe(uint32_t sleep_flags, uint
}
#endif
if (deep_sleep) {
#if !SOC_GPIO_SUPPORT_HOLD_SINGLE_IO_IN_DSLP
esp_sleep_isolate_digital_gpio();
#if !SOC_GPIO_SUPPORT_HOLD_SINGLE_IO_IN_DSLP || SOC_GPIO_NEED_SOFT_ISOLATE_DURING_PD
esp_sleep_isolate_digital_gpio(false);
#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);
}
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);
}
#endif
#if ESP_ROM_SUPPORT_DEEP_SLEEP_WAKEUP_STUB && SOC_DEEP_SLEEP_SUPPORTED
@@ -913,7 +885,7 @@ static esp_err_t FORCE_IRAM_ATTR esp_sleep_start_safe(uint32_t sleep_flags, uint
#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 !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
@@ -924,8 +896,13 @@ static esp_err_t FORCE_IRAM_ATTR esp_sleep_start_safe(uint32_t sleep_flags, uint
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(true);
}
#endif
/* Cache Suspend 1: will wait cache idle in cache suspend */
suspend_cache();
sleep_cache_suspend();
if (!(sleep_flags & RTC_SLEEP_PD_VDDSDIO)) {
#if CONFIG_ESP_SLEEP_SET_FLASH_DPD
if (sleep_flags & RTC_SLEEP_FLASH_DPD) {
@@ -948,6 +925,9 @@ static esp_err_t FORCE_IRAM_ATTR esp_sleep_start_safe(uint32_t sleep_flags, uint
/* 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
@@ -1007,6 +987,11 @@ static esp_err_t FORCE_IRAM_ATTR esp_sleep_start_safe(uint32_t sleep_flags, uint
#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
@@ -1014,7 +999,7 @@ static esp_err_t FORCE_IRAM_ATTR esp_sleep_start_safe(uint32_t sleep_flags, uint
}
#endif
/* Cache Resume 1: Resume cache for continue running*/
resume_cache();
sleep_cache_resume();
#if CONFIG_PM_SLP_SPIRAM_HALFSLEEP_ENABLED && CONFIG_SPIRAM_XIP_FROM_PSRAM
// Code outside of esp_sleep_start_safe may be linked to FLASH, 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
@@ -1199,7 +1184,7 @@ static esp_err_t SLEEP_FN_ATTR esp_sleep_start(uint32_t sleep_flags, uint32_t cl
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. */
suspend_cache();
sleep_cache_suspend();
}
#endif
// Restore CPU frequency
@@ -1348,7 +1333,7 @@ static esp_err_t FORCE_IRAM_ATTR deep_sleep_start(bool allow_sleep_rejection)
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 */
resume_cache();
sleep_cache_resume();
#endif
ESP_EARLY_LOGE(TAG, "Deep sleep request is rejected");
} else {
@@ -1434,7 +1419,7 @@ static SLEEP_FN_ATTR esp_err_t esp_light_sleep_inner(uint32_t sleep_flags, uint3
#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 */
resume_cache();
sleep_cache_resume();
}
#endif
@@ -1533,7 +1518,7 @@ esp_err_t esp_light_sleep_start(void)
ignore_check_wakeup_triggers |= RTC_EXT1_TRIG_EN;
#endif
if (!(s_config.wakeup_triggers & ignore_check_wakeup_triggers)) {
suspend_cache();
sleep_cache_suspend();
}
#endif
@@ -1718,7 +1703,7 @@ esp_err_t esp_light_sleep_start(void)
#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)) {
resume_cache();
sleep_cache_resume();
}
#endif

View File

@@ -34,7 +34,8 @@ static __attribute__((unused)) esp_err_t sleep_sys_periph_hp_system_retention_in
static __attribute__((unused)) esp_err_t sleep_sys_periph_tee_apm_retention_init(void *arg)
{
/* TBD for ESP32P4 IDF-10020. */
#ifndef CONFIG_IDF_TARGET_ESP32P4
/* TBD for ESP32S31 IDF-14620. */
#if !defined(CONFIG_IDF_TARGET_ESP32P4) && !defined(CONFIG_IDF_TARGET_ESP32S31)
esp_err_t err = sleep_retention_entries_create(tee_apm_regs_retention, ARRAY_SIZE(tee_apm_regs_retention), REGDMA_LINK_PRI_NON_CRITICAL_TEE_APM, SLEEP_RETENTION_MODULE_SYS_PERIPH);
if (err == ESP_OK) {
err = sleep_retention_entries_create(tee_apm_highpri_regs_retention, ARRAY_SIZE(tee_apm_highpri_regs_retention), REGDMA_LINK_PRI_CRITICAL_TEE_APM, SLEEP_RETENTION_MODULE_SYS_PERIPH);
@@ -74,7 +75,7 @@ static __attribute__((unused)) esp_err_t sleep_sys_periph_flash_spimem_retention
return ESP_OK;
}
#if CONFIG_SPIRAM && CONFIG_IDF_TARGET_ESP32P4
#if CONFIG_SPIRAM && SOC_PSRAM_MEMSPI_IS_INDEPENDENT
/* TODO: PM-205, In the ESP32C5, Flash and PSRAM use the same set of SPIMEM hardware, while in P4, Flash and PSRAM each have their own SPIMEM hardware.
* Its necessary to confirm whether the ESP32C5 can independently manage SPIMEM retention for Flash and PSRAM in software. */
static __attribute__((unused)) esp_err_t sleep_sys_periph_psram_spimem_retention_init(void *arg)
@@ -99,7 +100,7 @@ esp_err_t sleep_sys_periph_cache_retention_init(void)
{
esp_err_t err = sleep_retention_entries_create(cache_regs_retention, ARRAY_SIZE(cache_regs_retention), REGDMA_LINK_PRI_SYS_PERIPH_HIGH, SLEEP_RETENTION_MODULE_SYS_PERIPH);
ESP_RETURN_ON_ERROR(err, TAG, "failed to allocate memory for digital peripherals (Cache) retention");
ESP_LOGI(TAG, "L2 Cache sleep retention initialization");
ESP_LOGD(TAG, "L2 Cache sleep retention initialization");
return ESP_OK;
}
#endif
@@ -109,7 +110,7 @@ esp_err_t sleep_pau_retention_init(void)
{
esp_err_t err = sleep_retention_entries_create(pau_regs_retention, ARRAY_SIZE(pau_regs_retention), REGDMA_LINK_PRI_SYS_PERIPH_LOW, SLEEP_RETENTION_MODULE_SYS_PERIPH);
ESP_RETURN_ON_ERROR(err, TAG, "failed to allocate memory for system (PAU) retention");
ESP_LOGI(TAG, "PAU sleep retention initialization");
ESP_LOGD(TAG, "PAU sleep retention initialization");
return ESP_OK;
}
#endif
@@ -119,7 +120,7 @@ esp_err_t sleep_pvt_retention_init(void)
{
esp_err_t err = sleep_retention_entries_create(pvt_regs_retention, ARRAY_SIZE(pvt_regs_retention), REGDMA_LINK_PRI_SYS_PERIPH_LOW, SLEEP_RETENTION_MODULE_SYS_PERIPH);
ESP_RETURN_ON_ERROR(err, TAG, "failed to allocate memory for system (PVT) retention");
ESP_LOGI(TAG, "PVT sleep retention initialization");
ESP_LOGD(TAG, "PVT sleep retention initialization");
return ESP_OK;
}
#endif
@@ -147,7 +148,7 @@ static __attribute__((unused)) esp_err_t sleep_sys_periph_retention_init(void *a
if(err) goto error;
err = sleep_sys_periph_flash_spimem_retention_init(arg);
if(err) goto error;
#if CONFIG_SPIRAM && CONFIG_IDF_TARGET_ESP32P4
#if CONFIG_SPIRAM && SOC_PSRAM_MEMSPI_IS_INDEPENDENT
err = sleep_sys_periph_psram_spimem_retention_init(arg);
if(err) goto error;
#endif

View File

@@ -20,18 +20,20 @@ void sleep_usb_otg_phy_backup_and_disable(void)
if (!s_usb_utmi_bus_clock_state) {
_usb_utmi_ll_enable_bus_clock(true);
}
usb_dwc_dev_t *hw = USB_DWC_LL_GET_HW(0);
// Forcing BVALID low to ignore the hardware-detected VBUS BVALID signal to suppress USB leakage.
s_usb_dwc_bvalid_override = usb_dwc_ll_get_bvalid_override(&USB_DWC_HS);
usb_dwc_ll_enable_bvalid_override(&USB_DWC_HS, true);
s_usb_utmi_stoppclk_state = usb_dwc_ll_get_stoppclk_st(&USB_DWC_HS);
usb_dwc_ll_set_stoppclk(&USB_DWC_HS, true);
s_usb_dwc_bvalid_override = usb_dwc_ll_get_bvalid_override(hw);
usb_dwc_ll_enable_bvalid_override(hw, true);
s_usb_utmi_stoppclk_state = usb_dwc_ll_get_stoppclk_st(hw);
usb_dwc_ll_set_stoppclk(hw, true);
}
void sleep_usb_otg_phy_restore(void)
{
_usb_utmi_ll_enable_bus_clock(true);
usb_dwc_ll_enable_bvalid_override(&USB_DWC_HS, s_usb_dwc_bvalid_override);
usb_dwc_ll_set_stoppclk(&USB_DWC_HS, s_usb_utmi_stoppclk_state);
usb_dwc_dev_t *hw = USB_DWC_LL_GET_HW(0);
usb_dwc_ll_enable_bvalid_override(hw, s_usb_dwc_bvalid_override);
usb_dwc_ll_set_stoppclk(hw, s_usb_utmi_stoppclk_state);
if (!s_usb_utmi_bus_clock_state) {
_usb_utmi_ll_enable_bus_clock(false);
}

View File

@@ -1,2 +1,2 @@
| Supported Targets | ESP32-C5 | ESP32-C6 | ESP32-C61 | ESP32-H2 | ESP32-H21 | ESP32-H4 | ESP32-P4 |
| ----------------- | -------- | -------- | --------- | -------- | --------- | -------- | -------- |
| Supported Targets | ESP32-C5 | ESP32-C6 | ESP32-C61 | ESP32-H2 | ESP32-H21 | ESP32-H4 | ESP32-P4 | ESP32-S31 |
| ----------------- | -------- | -------- | --------- | -------- | --------- | -------- | -------- | --------- |

View File

@@ -1,2 +1,2 @@
| Supported Targets | ESP32 | ESP32-C2 | ESP32-C3 | ESP32-C5 | ESP32-C6 | ESP32-C61 | ESP32-H2 | ESP32-H21 | ESP32-H4 | ESP32-P4 | ESP32-S2 | ESP32-S3 |
| ----------------- | ----- | -------- | -------- | -------- | -------- | --------- | -------- | --------- | -------- | -------- | -------- | -------- |
| Supported Targets | ESP32 | ESP32-C2 | ESP32-C3 | ESP32-C5 | ESP32-C6 | ESP32-C61 | ESP32-H2 | ESP32-H21 | ESP32-H4 | ESP32-P4 | ESP32-S2 | ESP32-S3 | ESP32-S31 |
| ----------------- | ----- | -------- | -------- | -------- | -------- | --------- | -------- | --------- | -------- | -------- | -------- | -------- | --------- |

View File

@@ -11,6 +11,7 @@
#include "hal/cache_ll.h"
#include "hal/cache_hal.h"
#include "esp_private/sleep_retention.h"
#include "esp_sleep.h"
// Use retention link only when the target supports sleep retention is enabled
#define I80_USE_RETENTION_LINK (SOC_LCDCAM_LCD_SUPPORT_SLEEP_RETENTION && CONFIG_PM_POWER_DOWN_PERIPHERAL_IN_LIGHT_SLEEP)
@@ -128,6 +129,9 @@ esp_err_t esp_lcd_new_i80_bus(const esp_lcd_i80_bus_config_t *bus_config, esp_lc
TAG, "invalid bus width:%d", bus_config->bus_width);
#if !SOC_LCDCAM_LCD_SUPPORT_SLEEP_RETENTION
ESP_RETURN_ON_FALSE(bus_config->flags.allow_pd == 0, ESP_ERR_NOT_SUPPORTED, TAG, "register back up is not supported");
#if SOC_PM_SUPPORT_TOP_PD
esp_sleep_pd_config(ESP_PD_DOMAIN_TOP, ESP_PD_OPTION_ON); //IDF-15652
#endif
#endif // SOC_LCDCAM_LCD_SUPPORT_SLEEP_RETENTION
// allocate i80 bus memory

View File

@@ -40,7 +40,7 @@ entries:
esp_clk:esp_clk_private_unlock (noflash)
if SOC_SYSTIMER_SUPPORTED = y:
systimer (noflash)
if IDF_TARGET_ESP32H21 != y && IDF_TARGET_ESP32H4 != y:
if SOC_ADC_SUPPORTED = y:
sar_periph_ctrl:sar_periph_ctrl_power_disable (noflash)
sar_periph_ctrl:adc_reset_lock_acquire (noflash)
sar_periph_ctrl:adc_reset_lock_release (noflash)

View File

@@ -82,27 +82,6 @@
#elif CONFIG_IDF_TARGET_ESP32S2
/* Minimal divider at which REF_CLK_FREQ can be obtained */
#define REF_CLK_DIV_MIN 2
#elif CONFIG_IDF_TARGET_ESP32S3
/* Minimal divider at which REF_CLK_FREQ can be obtained */
#define REF_CLK_DIV_MIN 2 // TODO: IDF-5660
#elif CONFIG_IDF_TARGET_ESP32C3
#define REF_CLK_DIV_MIN 2
#elif CONFIG_IDF_TARGET_ESP32C2
#define REF_CLK_DIV_MIN 2
#elif CONFIG_IDF_TARGET_ESP32C6
#define REF_CLK_DIV_MIN 2
#elif CONFIG_IDF_TARGET_ESP32C61
#define REF_CLK_DIV_MIN 2
#elif CONFIG_IDF_TARGET_ESP32C5
#define REF_CLK_DIV_MIN 2
#elif CONFIG_IDF_TARGET_ESP32H2
#define REF_CLK_DIV_MIN 2
#elif CONFIG_IDF_TARGET_ESP32H21
#define REF_CLK_DIV_MIN 2
#elif CONFIG_IDF_TARGET_ESP32P4
#define REF_CLK_DIV_MIN 2
#elif CONFIG_IDF_TARGET_ESP32H4
#define REF_CLK_DIV_MIN 2
#endif
#ifdef CONFIG_PM_PROFILING
@@ -451,11 +430,13 @@ esp_err_t esp_pm_configure(const void* vconfig)
return ESP_ERR_INVALID_ARG;
}
#ifdef REF_CLK_DIV_MIN
int xtal_freq_mhz = esp_clk_xtal_freq() / MHZ;
if (min_freq_mhz < xtal_freq_mhz && min_freq_mhz * MHZ / REF_CLK_FREQ < REF_CLK_DIV_MIN) {
ESP_LOGW(TAG, "min_freq_mhz should be >= %d", REF_CLK_FREQ * REF_CLK_DIV_MIN / MHZ);
return ESP_ERR_INVALID_ARG;
}
#endif
if (!rtc_clk_cpu_freq_mhz_to_config(max_freq_mhz, &freq_config)) {
ESP_LOGW(TAG, "invalid max_freq_mhz value (%d)", max_freq_mhz);
@@ -479,7 +460,7 @@ esp_err_t esp_pm_configure(const void* vconfig)
/* Maximum SOC APB clock frequency is 40 MHz, maximum Modem (WiFi,
* Bluetooth, etc..) APB clock frequency is 80 MHz */
int apb_clk_freq = esp_clk_apb_freq() / MHZ;
#if (CONFIG_ESP_WIFI_ENABLED || CONFIG_BT_ENABLED || CONFIG_IEEE802154_ENABLED) && SOC_PHY_SUPPORTED
#if (CONFIG_ESP_WIFI_ENABLED || CONFIG_BT_ENABLED || CONFIG_IEEE802154_ENABLED) && SOC_PHY_SUPPORTED && !SOC_MODEM_APB_CLOCK_IS_INDEPENDENT
apb_clk_freq = MAX(apb_clk_freq, MODEM_REQUIRED_MIN_APB_CLK_FREQ / MHZ);
#endif
int apb_max_freq = MIN(max_freq_mhz, apb_clk_freq); /* CPU frequency in APB_MAX mode */

View File

@@ -5,9 +5,9 @@ components/esp_pm/test_apps:
- if: INCLUDE_DEFAULT == 1
disable:
- if: CONFIG_NAME == "pm_pd_top_sleep" and IDF_TARGET not in ["esp32c5", "esp32c6", "esp32h2", "esp32p4"]
- if: IDF_TARGET in ["esp32h21", "esp32h4", "esp32s31"]
- if: IDF_TARGET in ["esp32h21", "esp32h4"]
temporary: true
reason: not support yet # TODO: [ESP32H21] IDF-11522, [ESP32H4] IDF-12286 [ESP32S31] IDF-14645
reason: not support yet # TODO: [ESP32H21] IDF-11522, [ESP32H4] IDF-12286
depends_components:
- *common_components
- esp_pm

View File

@@ -1,2 +1,2 @@
| Supported Targets | ESP32 | ESP32-C2 | ESP32-C3 | ESP32-C5 | ESP32-C6 | ESP32-C61 | ESP32-H2 | ESP32-P4 | ESP32-S2 | ESP32-S3 |
| ----------------- | ----- | -------- | -------- | -------- | -------- | --------- | -------- | -------- | -------- | -------- |
| Supported Targets | ESP32 | ESP32-C2 | ESP32-C3 | ESP32-C5 | ESP32-C6 | ESP32-C61 | ESP32-H2 | ESP32-P4 | ESP32-S2 | ESP32-S3 | ESP32-S31 |
| ----------------- | ----- | -------- | -------- | -------- | -------- | --------- | -------- | -------- | -------- | -------- | --------- |

View File

@@ -17,7 +17,6 @@ from pytest_embedded_idf.utils import idf_parametrize
indirect=True,
)
@idf_parametrize('target', ['supported_targets'], indirect=['target'])
@pytest.mark.temp_skip_ci(targets=['esp32s31'], reason='s31 bringup on this module is not done')
def test_esp_pm(dut: Dut) -> None:
dut.run_all_single_board_cases()

View File

@@ -569,14 +569,15 @@ FORCE_INLINE_ATTR IRAM_ATTR void ext_mem_init(void)
MSPI_INIT_ATTR void sys_rtc_init(const soc_reset_reason_t *rst_reas)
{
#if SOC_RTC_WDT_SUPPORTED
#if CONFIG_IDF_TARGET_ESP32P4
#if CONFIG_IDF_TARGET_ESP32P4 || CONFIG_IDF_TARGET_ESP32S31
#define RWDT_RESET RESET_REASON_CORE_RWDT
#define MWDT_RESET RESET_REASON_CORE_MWDT
#elif CONFIG_IDF_TARGET_ESP32S31
#define RWDT_RESET RESET_REASON_CORE_RWDT
#define MWDT_RESET RESET_REASON_CORE_MWDT0
#else
#define RWDT_RESET RESET_REASON_CORE_RTC_WDT
#endif
#if CONFIG_IDF_TARGET_ESP32P4
#define MWDT_RESET RESET_REASON_CORE_MWDT
#else
#define MWDT_RESET RESET_REASON_CORE_MWDT0
#endif

View File

@@ -1,5 +1,5 @@
/*
* SPDX-FileCopyrightText: 2019-2024 Espressif Systems (Shanghai) CO LTD
* SPDX-FileCopyrightText: 2019-2026 Espressif Systems (Shanghai) CO LTD
*
* SPDX-License-Identifier: Apache-2.0
*/

View File

@@ -142,6 +142,22 @@ __attribute__((always_inline)) static inline uint32_t efuse_ll_get_controller_st
return EFUSE.status.state;
}
/**
* @brief Flash power select eFuse bit (see VDD_SPI voltage table; not the final rail by itself)
*/
__attribute__((always_inline)) static inline uint32_t efuse_ll_get_flash_power_sel(void)
{
return EFUSE.rd_repeat_data6.pmu_flash_power_sel;
}
/**
* @brief Flash power select valid eFuse bit (1: `flash_power_sel` is valid, 0: not)
*/
__attribute__((always_inline)) static inline uint32_t efuse_ll_get_flash_power_sel_en(void)
{
return EFUSE.rd_repeat_data6.pmu_flash_power_sel_en;
}
/******************* eFuse control functions *************************/
#ifdef __cplusplus

View File

@@ -142,7 +142,6 @@
//Periheral Clock {{
#define APB_CLK_FREQ (SOC_XTAL_FREQ_MHZ * 1000000 )
#define MODEM_REQUIRED_MIN_APB_CLK_FREQ ( 80*1000000 )
#define REF_CLK_FREQ ( 1000000 )
//}}
/* Overall memory map */

View File

@@ -135,7 +135,6 @@
//Periheral Clock {{
#define APB_CLK_FREQ ( 80*1000000 )
#define MODEM_REQUIRED_MIN_APB_CLK_FREQ ( 80*1000000 )
#define REF_CLK_FREQ ( 1000000 )
//}}
/* Overall memory map */

View File

@@ -133,7 +133,6 @@
//Periheral Clock {{
#define APB_CLK_FREQ ( 40*1000000 )
#define MODEM_REQUIRED_MIN_APB_CLK_FREQ ( 80*1000000 )
#define REF_CLK_FREQ ( 1000000 )
//}}
/* Overall memory map */

View File

@@ -54,7 +54,7 @@ extern const regdma_entries_config_t tee_apm_highpri_regs_retention[TEE_APM_HIGH
* This is an internal function of the sleep retention driver, and is not
* useful for external use.
*/
#define IOMUX_RETENTION_LINK_LEN 5
#define IOMUX_RETENTION_LINK_LEN 7
extern const regdma_entries_config_t iomux_regs_retention[IOMUX_RETENTION_LINK_LEN];
/**

View File

@@ -65,7 +65,9 @@ const regdma_entries_config_t iomux_regs_retention[] = {
[4] = {
.config = REGDMA_LINK_ADDR_MAP_INIT(REGDMA_IOMUX_LINK(0x04), GPIO_RETENTION_MAP_BASE, GPIO_RETENTION_MAP_BASE, GPIO_RETENTION_REGS_CNT, 0, 0,
gpio_regs_map[0], gpio_regs_map[1], gpio_regs_map[2], gpio_regs_map[3]), .owner = ENTRY(0) | ENTRY(2)
}, \
},
[5] = { .config = REGDMA_LINK_CONTINUOUS_INIT(REGDMA_IOMUX_LINK(0x05), GPIO_ENABLE_REG, GPIO_ENABLE_W1TS_REG, 1, 0, 0), .owner = ENTRY(0) | ENTRY(2) },
[6] = { .config = REGDMA_LINK_CONTINUOUS_INIT(REGDMA_IOMUX_LINK(0x06), GPIO_ENABLE1_REG, GPIO_ENABLE1_W1TS_REG, 1, 0, 0), .owner = ENTRY(0) | ENTRY(2) },
};
_Static_assert(ARRAY_SIZE(iomux_regs_retention) == IOMUX_RETENTION_LINK_LEN, "Inconsistent IOMUX retention link length definitions");

View File

@@ -136,7 +136,6 @@
//Periheral Clock {{
#define APB_CLK_FREQ ( 40*1000000 )
#define MODEM_REQUIRED_MIN_APB_CLK_FREQ ( 80*1000000 )
#define REF_CLK_FREQ ( 1000000 )
//}}
/* Overall memory map */

View File

@@ -54,7 +54,7 @@ extern const regdma_entries_config_t tee_apm_highpri_regs_retention[TEE_APM_HIGH
* This is an internal function of the sleep retention driver, and is not
* useful for external use.
*/
#define IOMUX_RETENTION_LINK_LEN 4
#define IOMUX_RETENTION_LINK_LEN 6
extern const regdma_entries_config_t iomux_regs_retention[IOMUX_RETENTION_LINK_LEN];
/**

View File

@@ -54,7 +54,9 @@ const regdma_entries_config_t iomux_regs_retention[] = {
[0] = { .config = REGDMA_LINK_CONTINUOUS_INIT(REGDMA_IOMUX_LINK(0x00), REG_IO_MUX_BASE, REG_IO_MUX_BASE, N_REGS_IOMUX_0(), 0, 0), .owner = ENTRY(0) | ENTRY(2) }, /* io_mux */
[1] = { .config = REGDMA_LINK_CONTINUOUS_INIT(REGDMA_IOMUX_LINK(0x01), GPIO_FUNC0_OUT_SEL_CFG_REG, GPIO_FUNC0_OUT_SEL_CFG_REG, N_REGS_IOMUX_1(), 0, 0), .owner = ENTRY(0) | ENTRY(2) },
[2] = { .config = REGDMA_LINK_CONTINUOUS_INIT(REGDMA_IOMUX_LINK(0x02), GPIO_STATUS_NEXT_REG, GPIO_STATUS_NEXT_REG, N_REGS_IOMUX_2(), 0, 0), .owner = ENTRY(0) | ENTRY(2) },
[3] = { .config = REGDMA_LINK_CONTINUOUS_INIT(REGDMA_IOMUX_LINK(0x03), DR_REG_GPIO_BASE, DR_REG_GPIO_BASE, N_REGS_IOMUX_3(), 0, 0), .owner = ENTRY(0) | ENTRY(2) }
[3] = { .config = REGDMA_LINK_CONTINUOUS_INIT(REGDMA_IOMUX_LINK(0x03), DR_REG_GPIO_BASE, DR_REG_GPIO_BASE, N_REGS_IOMUX_3(), 0, 0), .owner = ENTRY(0) | ENTRY(2) },
[4] = { .config = REGDMA_LINK_CONTINUOUS_INIT(REGDMA_IOMUX_LINK(0x04), GPIO_ENABLE_REG, GPIO_ENABLE_W1TS_REG, 1, 0, 0), .owner = ENTRY(0) | ENTRY(2) },
[5] = { .config = REGDMA_LINK_CONTINUOUS_INIT(REGDMA_IOMUX_LINK(0x05), GPIO_ENABLE1_REG, GPIO_ENABLE1_W1TS_REG, 1, 0, 0), .owner = ENTRY(0) | ENTRY(2) },
};
_Static_assert(ARRAY_SIZE(iomux_regs_retention) == IOMUX_RETENTION_LINK_LEN, "Inconsistent IOMUX retention link length definitions");

View File

@@ -133,7 +133,6 @@
//Periheral Clock {{
#define APB_CLK_FREQ ( 40*1000000 )
#define MODEM_REQUIRED_MIN_APB_CLK_FREQ ( 80*1000000 )
#define REF_CLK_FREQ ( 1000000 )
//}}
/* Overall memory map */

View File

@@ -54,7 +54,7 @@ extern const regdma_entries_config_t tee_apm_highpri_regs_retention[TEE_APM_HIGH
* This is an internal function of the sleep retention driver, and is not
* useful for external use.
*/
#define IOMUX_RETENTION_LINK_LEN 7
#define IOMUX_RETENTION_LINK_LEN 9
extern const regdma_entries_config_t iomux_regs_retention[IOMUX_RETENTION_LINK_LEN];
/**

View File

@@ -1,5 +1,5 @@
/*
* SPDX-FileCopyrightText: 2024-2025 Espressif Systems (Shanghai) CO LTD
* SPDX-FileCopyrightText: 2024-2026 Espressif Systems (Shanghai) CO LTD
*
* SPDX-License-Identifier: Apache-2.0 OR MIT
*/
@@ -49,8 +49,6 @@ _Static_assert((ARRAY_SIZE(tee_apm_regs_retention) == TEE_APM_RETENTION_LINK_LEN
#define N_REGS_IOMUX_2() (((GPIO_FUNC121_IN_SEL_CFG_REG - GPIO_FUNC0_IN_SEL_CFG_REG) / 4) + 1)
#define N_REGS_IOMUX_3() (SOC_GPIO_PIN_COUNT)
#define N_REGS_IOMUX_4() (1)
#define N_REGS_IOMUX_5() (1)
#define N_REGS_IOMUX_6() (1)
const regdma_entries_config_t iomux_regs_retention[] = {
[0] = { .config = REGDMA_LINK_CONTINUOUS_INIT(REGDMA_IOMUX_LINK(0x00), IO_MUX_GPIO0_REG, IO_MUX_GPIO0_REG, N_REGS_IOMUX_0(), 0, 0), .owner = ENTRY(0) | ENTRY(2) }, /* io_mux */
@@ -58,8 +56,10 @@ const regdma_entries_config_t iomux_regs_retention[] = {
[2] = { .config = REGDMA_LINK_CONTINUOUS_INIT(REGDMA_IOMUX_LINK(0x02), GPIO_FUNC0_IN_SEL_CFG_REG, GPIO_FUNC0_IN_SEL_CFG_REG, N_REGS_IOMUX_2(), 0, 0), .owner = ENTRY(0) | ENTRY(2) },
[3] = { .config = REGDMA_LINK_CONTINUOUS_INIT(REGDMA_IOMUX_LINK(0x03), GPIO_PIN0_REG, GPIO_PIN0_REG, N_REGS_IOMUX_3(), 0, 0), .owner = ENTRY(0) | ENTRY(2) },
[4] = { .config = REGDMA_LINK_CONTINUOUS_INIT(REGDMA_IOMUX_LINK(0x04), GPIO_STATUS_REG, GPIO_STATUS_REG, N_REGS_IOMUX_4(), 0, 0), .owner = ENTRY(0) | ENTRY(2) },
[5] = { .config = REGDMA_LINK_CONTINUOUS_INIT(REGDMA_IOMUX_LINK(0x05), GPIO_ENABLE_REG, GPIO_ENABLE_REG, N_REGS_IOMUX_5(), 0, 0), .owner = ENTRY(0) | ENTRY(2) },
[6] = { .config = REGDMA_LINK_CONTINUOUS_INIT(REGDMA_IOMUX_LINK(0x06), GPIO_OUT_REG, GPIO_OUT_REG, N_REGS_IOMUX_6(), 0, 0), .owner = ENTRY(0) | ENTRY(2) }
[5] = { .config = REGDMA_LINK_CONTINUOUS_INIT(REGDMA_IOMUX_LINK(0x05), GPIO_ENABLE_REG, GPIO_ENABLE_W1TS_REG, 1, 0, 0), .owner = ENTRY(0) | ENTRY(2) },
[6] = { .config = REGDMA_LINK_CONTINUOUS_INIT(REGDMA_IOMUX_LINK(0x06), GPIO_ENABLE1_REG, GPIO_ENABLE1_W1TS_REG, 1, 0, 0), .owner = ENTRY(0) | ENTRY(2) },
[7] = { .config = REGDMA_LINK_CONTINUOUS_INIT(REGDMA_IOMUX_LINK(0x07), GPIO_OUT_REG, GPIO_OUT_W1TS_REG, 1, 0, 0), .owner = ENTRY(0) | ENTRY(2) },
[8] = { .config = REGDMA_LINK_CONTINUOUS_INIT(REGDMA_IOMUX_LINK(0x08), GPIO_OUT1_REG, GPIO_OUT1_W1TS_REG, 1, 0, 0), .owner = ENTRY(0) | ENTRY(2) },
};
_Static_assert(ARRAY_SIZE(iomux_regs_retention) == IOMUX_RETENTION_LINK_LEN, "Inconsistent IOMUX retention link length definitions");

View File

@@ -135,7 +135,6 @@
//Periheral Clock {{
#define APB_CLK_FREQ ( 32*1000000 )
#define MODEM_REQUIRED_MIN_APB_CLK_FREQ ( 32*1000000 )
#define REF_CLK_FREQ ( 1000000 )
//}}
/* Overall memory map */

View File

@@ -54,7 +54,7 @@ extern const regdma_entries_config_t tee_apm_highpri_regs_retention[TEE_APM_HIGH
* This is an internal function of the sleep retention driver, and is not
* useful for external use.
*/
#define IOMUX_RETENTION_LINK_LEN 4
#define IOMUX_RETENTION_LINK_LEN 5
extern const regdma_entries_config_t iomux_regs_retention[IOMUX_RETENTION_LINK_LEN];
/**

View File

@@ -52,7 +52,8 @@ const regdma_entries_config_t iomux_regs_retention[] = {
[0] = { .config = REGDMA_LINK_CONTINUOUS_INIT(REGDMA_IOMUX_LINK(0x00), REG_IO_MUX_BASE, REG_IO_MUX_BASE, N_REGS_IOMUX_0(), 0, 0), .owner = ENTRY(0) | ENTRY(2) }, /* io_mux */
[1] = { .config = REGDMA_LINK_CONTINUOUS_INIT(REGDMA_IOMUX_LINK(0x01), GPIO_FUNC0_OUT_SEL_CFG_REG, GPIO_FUNC0_OUT_SEL_CFG_REG, N_REGS_IOMUX_1(), 0, 0), .owner = ENTRY(0) | ENTRY(2) },
[2] = { .config = REGDMA_LINK_CONTINUOUS_INIT(REGDMA_IOMUX_LINK(0x02), GPIO_STATUS_NEXT_REG, GPIO_STATUS_NEXT_REG, N_REGS_IOMUX_2(), 0, 0), .owner = ENTRY(0) | ENTRY(2) },
[3] = { .config = REGDMA_LINK_CONTINUOUS_INIT(REGDMA_IOMUX_LINK(0x03), DR_REG_GPIO_BASE, DR_REG_GPIO_BASE, N_REGS_IOMUX_3(), 0, 0), .owner = ENTRY(0) | ENTRY(2) }
[3] = { .config = REGDMA_LINK_CONTINUOUS_INIT(REGDMA_IOMUX_LINK(0x03), DR_REG_GPIO_BASE, DR_REG_GPIO_BASE, N_REGS_IOMUX_3(), 0, 0), .owner = ENTRY(0) | ENTRY(2) },
[4] = { .config = REGDMA_LINK_CONTINUOUS_INIT(REGDMA_IOMUX_LINK(0x04), GPIO_ENABLE_REG, GPIO_ENABLE_W1TS_REG, 1, 0, 0), .owner = ENTRY(0) | ENTRY(2) },
};
_Static_assert(ARRAY_SIZE(iomux_regs_retention) == IOMUX_RETENTION_LINK_LEN, "Inconsistent IOMUX retention link length definitions");

View File

@@ -137,7 +137,6 @@
//Periheral Clock {{
#define APB_CLK_FREQ ( 32*1000000 )
#define MODEM_REQUIRED_MIN_APB_CLK_FREQ ( 32*1000000 )
#define REF_CLK_FREQ ( 1000000 )
//}}
/* Overall memory map */

View File

@@ -54,7 +54,7 @@ extern const regdma_entries_config_t tee_apm_highpri_regs_retention[TEE_APM_HIGH
* This is an internal function of the sleep retention driver, and is not
* useful for external use.
*/
#define IOMUX_RETENTION_LINK_LEN 4
#define IOMUX_RETENTION_LINK_LEN 5
extern const regdma_entries_config_t iomux_regs_retention[IOMUX_RETENTION_LINK_LEN];
/**

View File

@@ -65,6 +65,7 @@ const regdma_entries_config_t iomux_regs_retention[] = {
[1] = { .config = REGDMA_LINK_CONTINUOUS_INIT(REGDMA_IOMUX_LINK(0x01), DR_REG_GPIO_BASE, DR_REG_GPIO_BASE, N_REGS_IOMUX_1(), 0, 0), .owner = ENTRY(0) | ENTRY(2) },
[2] = { .config = REGDMA_LINK_CONTINUOUS_INIT(REGDMA_IOMUX_LINK(0x02), GPIO_FUNC0_IN_SEL_CFG_REG, GPIO_FUNC0_IN_SEL_CFG_REG, N_REGS_IOMUX_2(), 0, 0), .owner = ENTRY(0) | ENTRY(2) },
[3] = { .config = REGDMA_LINK_CONTINUOUS_INIT(REGDMA_IOMUX_LINK(0x03), GPIO_FUNC0_OUT_SEL_CFG_REG, GPIO_FUNC0_OUT_SEL_CFG_REG, N_REGS_IOMUX_3(), 0, 0), .owner = ENTRY(0) | ENTRY(2) },
[4] = { .config = REGDMA_LINK_CONTINUOUS_INIT(REGDMA_IOMUX_LINK(0x04), GPIO_ENABLE_REG, GPIO_ENABLE_W1TS_REG, 1, 0, 0), .owner = ENTRY(0) | ENTRY(2) },
};
_Static_assert(ARRAY_SIZE(iomux_regs_retention) == IOMUX_RETENTION_LINK_LEN, "Inconsistent IOMUX retention link length definitions");

View File

@@ -126,7 +126,6 @@
//Periheral Clock {{
#define APB_CLK_FREQ ( 32*1000000 )
#define MODEM_REQUIRED_MIN_APB_CLK_FREQ ( 32*1000000 )
#define REF_CLK_FREQ ( 1000000 )
//}}
/* Overall memory map */

View File

@@ -54,7 +54,7 @@ extern const regdma_entries_config_t tee_apm_highpri_regs_retention[TEE_APM_HIGH
* This is an internal function of the sleep retention driver, and is not
* useful for external use.
*/
#define IOMUX_RETENTION_LINK_LEN 5
#define IOMUX_RETENTION_LINK_LEN 7
extern const regdma_entries_config_t iomux_regs_retention[IOMUX_RETENTION_LINK_LEN];
/**

View File

@@ -61,7 +61,9 @@ const regdma_entries_config_t iomux_regs_retention[] = {
[1] = { .config = REGDMA_LINK_CONTINUOUS_INIT(REGDMA_IOMUX_LINK(0x01), GPIO_FUNC0_OUT_SEL_CFG_REG, GPIO_FUNC0_OUT_SEL_CFG_REG, N_REGS_IOMUX_1(), 0, 0), .owner = ENTRY(0) | ENTRY(2) },
[2] = { .config = REGDMA_LINK_CONTINUOUS_INIT(REGDMA_IOMUX_LINK(0x02), GPIO_FUNC0_IN_SEL_CFG_REG, GPIO_FUNC0_IN_SEL_CFG_REG, N_REGS_IOMUX_2(), 0, 0), .owner = ENTRY(0) | ENTRY(2) },
[3] = { .config = REGDMA_LINK_CONTINUOUS_INIT(REGDMA_IOMUX_LINK(0x03), GPIO_PIN0_REG, GPIO_PIN0_REG, N_REGS_IOMUX_3(), 0, 0), .owner = ENTRY(0) | ENTRY(2) },
[4] = { .config = REGDMA_LINK_CONTINUOUS_INIT(REGDMA_IOMUX_LINK(0x04), GPIO_OUT_REG, GPIO_OUT_REG, N_REGS_IOMUX_4(), 0, 0), .owner = ENTRY(0) | ENTRY(2) }
[4] = { .config = REGDMA_LINK_CONTINUOUS_INIT(REGDMA_IOMUX_LINK(0x04), GPIO_OUT_REG, GPIO_OUT_W1TS_REG, N_REGS_IOMUX_4(), 0, 0), .owner = ENTRY(0) | ENTRY(2) },
[5] = { .config = REGDMA_LINK_CONTINUOUS_INIT(REGDMA_IOMUX_LINK(0x05), GPIO_ENABLE_REG, GPIO_ENABLE_W1TS_REG, 1, 0, 0), .owner = ENTRY(0) | ENTRY(2) },
[6] = { .config = REGDMA_LINK_CONTINUOUS_INIT(REGDMA_IOMUX_LINK(0x06), GPIO_ENABLE1_REG, GPIO_ENABLE1_W1TS_REG, 1, 0, 0), .owner = ENTRY(0) | ENTRY(2) },
};
_Static_assert(ARRAY_SIZE(iomux_regs_retention) == IOMUX_RETENTION_LINK_LEN, "Inconsistent IOMUX retention link length definitions");

View File

@@ -1171,6 +1171,10 @@ config SOC_USB_FSLS_PHY_NUM
int
default 1
config SOC_USB_OTG_NEED_SOFTWARE_SUSPEND_BEFORE_SLEEP
bool
default y
config SOC_USB_UTMI_PHY_NUM
int
default 1
@@ -1407,6 +1411,10 @@ config SOC_MEMSPI_SUPPORT_CONTROL_DUMMY_OUT
bool
default y
config SOC_PSRAM_MEMSPI_IS_INDEPENDENT
bool
default y
config SOC_SPI_MEM_FLASH_SUPPORT_HPM
bool
default y

View File

@@ -133,7 +133,6 @@
//Periheral Clock {{
#define APB_CLK_FREQ ( 90*1000000 )
#define REF_CLK_FREQ ( 1000000 )
//}}
/* Overall memory map */

View File

@@ -432,6 +432,7 @@
// USB OTG Caps
#define SOC_USB_OTG_PERIPH_NUM (2U)
#define SOC_USB_FSLS_PHY_NUM (1U)
#define SOC_USB_OTG_NEED_SOFTWARE_SUSPEND_BEFORE_SLEEP (1)
// USB PHY Caps
#define SOC_USB_UTMI_PHY_NUM (1U)
@@ -533,6 +534,7 @@
#define SOC_MSPI_HAS_INDEPENT_IOMUX 1
#define SOC_MEMSPI_IS_INDEPENDENT 1
#define SOC_MEMSPI_SUPPORT_CONTROL_DUMMY_OUT 1
#define SOC_PSRAM_MEMSPI_IS_INDEPENDENT 1
#define SOC_SPI_MEM_FLASH_SUPPORT_HPM (1) /*!< Support High Performance Mode */

View File

@@ -52,7 +52,7 @@ extern const regdma_entries_config_t hp_system_regs_retention[HP_SYSTEM_RETENTIO
* This is an internal function of the sleep retention driver, and is not
* useful for external use.
*/
#define IOMUX_RETENTION_LINK_LEN 6
#define IOMUX_RETENTION_LINK_LEN 8
extern const regdma_entries_config_t iomux_regs_retention[IOMUX_RETENTION_LINK_LEN];
/**

View File

@@ -116,11 +116,12 @@ const regdma_entries_config_t iomux_regs_retention[] = {
[3] = { .config = REGDMA_LINK_CONTINUOUS_INIT(REGDMA_IOMUX_LINK(0x03), GPIO_PIN0_REG, GPIO_PIN0_REG, N_REGS_GPIO_PINx(), 0, 0), .owner = ENTRY(0) },
/* GPIO_FUNC1_IN_SEL_CFG_REG ~ GPIO_FUNC255_IN_SEL_CFG_REG & GPIO_FUNC0_OUT_SEL_CFG_REG ~ GPIO_FUNC56_OUT_SEL_CFG_REG */
[4] = { .config = REGDMA_LINK_CONTINUOUS_INIT(REGDMA_IOMUX_LINK(0x04), GPIO_FUNC1_IN_SEL_CFG_REG, GPIO_FUNC1_IN_SEL_CFG_REG, N_REGS_GPIO_FUNCx(), 0, 0), .owner = ENTRY(0) },
[5] = {
.config = REGDMA_LINK_ADDR_MAP_INIT(REGDMA_IOMUX_LINK(0x05), GPIO_RETENTION_REGS_BASE1, GPIO_RETENTION_REGS_BASE1, GPIO_RETENTION_REGS_CNT1, 0, 0, \
gpio_regs_map1[0], gpio_regs_map1[1], gpio_regs_map1[2], gpio_regs_map1[3]), .owner = ENTRY(0)
},
[6] = { .config = REGDMA_LINK_CONTINUOUS_INIT(REGDMA_IOMUX_LINK(0x06), GPIO_ENABLE_REG, GPIO_ENABLE_W1TS_REG, 1, 0, 0), .owner = ENTRY(0) },
[7] = { .config = REGDMA_LINK_CONTINUOUS_INIT(REGDMA_IOMUX_LINK(0x07), GPIO_ENABLE1_REG, GPIO_ENABLE1_W1TS_REG, 1, 0, 0), .owner = ENTRY(0) },
};
_Static_assert(ARRAY_SIZE(iomux_regs_retention) == IOMUX_RETENTION_LINK_LEN, "Inconsistent IOMUX retention link length definitions");

View File

@@ -150,7 +150,6 @@
//Periheral Clock {{
#define APB_CLK_FREQ (80*1000000)
#define MODEM_REQUIRED_MIN_APB_CLK_FREQ (80*1000000)
#define REF_CLK_FREQ (1000000)
//}}
/* Overall memory map */

View File

@@ -227,6 +227,10 @@ config SOC_SPI_FLASH_SUPPORTED
bool
default y
config SOC_SPI_FLASH_HAS_DEDICATED_LDO
bool
default y
config SOC_TOUCH_SENSOR_SUPPORTED
bool
default y
@@ -235,10 +239,22 @@ config SOC_PPA_SUPPORTED
bool
default y
config SOC_LIGHT_SLEEP_SUPPORTED
bool
default y
config SOC_DEEP_SLEEP_SUPPORTED
bool
default y
config SOC_MODEM_CLOCK_SUPPORTED
bool
default y
config SOC_PM_SUPPORTED
bool
default y
config SOC_BITSCRAMBLER_SUPPORTED
bool
default y
@@ -303,6 +319,10 @@ config SOC_XTAL_SUPPORT_40M
bool
default y
config SOC_XTAL_CLOCK_PATH_DEPENDS_ON_TOP_DOMAIN
bool
default y
config SOC_ADC_MAX_CHANNEL_NUM
int
default 10
@@ -443,6 +463,10 @@ config SOC_GPIO_SUPPORT_ETM
bool
default y
config SOC_GPIO_NEED_SOFT_ISOLATE_DURING_PD
bool
default y
config SOC_LP_IO_HAS_INDEPENDENT_WAKEUP_SOURCE
bool
default y
@@ -459,14 +483,6 @@ config SOC_GPIO_OUT_RANGE_MAX
int
default 61
config SOC_GPIO_DEEP_SLEEP_WAKE_VALID_GPIO_MASK
int
default 0
config SOC_GPIO_DEEP_SLEEP_WAKE_SUPPORTED_PIN_CNT
int
default 8
config SOC_GPIO_SUPPORT_FORCE_HOLD
bool
default y
@@ -715,7 +731,7 @@ config SOC_SPI_MEM_SUPPORT_TSUS_TRES_SEPERATE_CTR
bool
default y
config SOC_MSPI_HAS_INDEPENT_IOMUX
config SOC_PSRAM_HAS_DEDICATED_LDO
bool
default y
@@ -723,6 +739,10 @@ config SOC_MEMSPI_IS_INDEPENDENT
bool
default y
config SOC_PSRAM_MEMSPI_IS_INDEPENDENT
bool
default y
config SOC_MEMSPI_ENCRYPTION_ALIGNMENT
int
default 16
@@ -967,6 +987,10 @@ config SOC_MODEM_CLOCK_IS_INDEPENDENT
bool
default y
config SOC_MODEM_APB_CLOCK_IS_INDEPENDENT
bool
default y
config SOC_MODEM_CLOCK_SOC_PLL_SOURCE_CG_SUPPORTED
bool
default y
@@ -1071,6 +1095,10 @@ config SOC_PM_SUPPORT_TOUCH_SENSOR_WAKEUP
bool
default y
config SOC_PM_SUPPORT_CPU_PD
bool
default y
config SOC_PM_SUPPORT_XTAL32K_PD
bool
default y
@@ -1087,6 +1115,10 @@ config SOC_PM_SUPPORT_VDDSDIO_PD
bool
default y
config SOC_PM_SUPPORT_TOP_PD
bool
default y
config SOC_PM_SUPPORT_HP_AON_PD
bool
default y
@@ -1119,6 +1151,10 @@ config SOC_PM_CPU_RETENTION_BY_SW
bool
default y
config SOC_PM_FPU_RETENTION_BY_SW
bool
default y
config SOC_PM_CACHE_RETENTION_BY_PAU
bool
default y

Some files were not shown because too many files have changed in this diff Show More