Merge branch 'bugfix/esp_idf_c5_eco3_cpu_lockup_v6.0' into 'release/v6.0'

change: support top pd during sleep for esp32c5 eco3 and remove sleep mmu related flow (v6.0)

See merge request espressif/esp-idf!43585
This commit is contained in:
Jiang Jiang Jian
2026-02-01 17:31:54 +08:00
39 changed files with 391 additions and 262 deletions
@@ -1,5 +1,5 @@
/*
* SPDX-FileCopyrightText: 2022-2025 Espressif Systems (Shanghai) CO LTD
* SPDX-FileCopyrightText: 2022-2026 Espressif Systems (Shanghai) CO LTD
*
* SPDX-License-Identifier: Apache-2.0
*/
@@ -172,22 +172,6 @@ esp_err_t esp_deep_sleep_register_phy_hook(esp_deep_sleep_cb_t new_dslp_cb);
void esp_deep_sleep_deregister_phy_hook(esp_deep_sleep_cb_t old_dslp_cb);
#endif
#if CONFIG_PM_POWER_DOWN_PERIPHERAL_IN_LIGHT_SLEEP && SOC_PM_MMU_TABLE_RETENTION_WHEN_TOP_PD
/**
* @brief Backup or restore the MMU when the top domain is powered down.
* @param backup_or_restore decide to backup mmu or restore mmu
*/
void esp_sleep_mmu_retention(bool backup_or_restore);
/**
* @brief Whether to allow the top domain to be powered off due to mmu domain requiring retention.
*
* In light sleep mode, only when the system can provide enough memory
* for mmu retention, the top power domain can be powered off.
*/
bool mmu_domain_pd_allowed(void);
#endif
/**
* @brief Notify the sleep process that `sleep_time_overhead_out` needs to be remeasured, which must be called
* in the following scenarios:
@@ -29,10 +29,6 @@ if(CONFIG_P4_REV3_MSPI_CRASH_AFTER_POWER_UP_WORKAROUND)
APPEND PROPERTY INTERFACE_LINK_LIBRARIES "-u p4_rev3_mspi_workaround")
endif()
if(CONFIG_SOC_PM_MMU_TABLE_RETENTION_WHEN_TOP_PD AND CONFIG_PM_POWER_DOWN_PERIPHERAL_IN_LIGHT_SLEEP)
list(APPEND srcs "port/${target}/sleep_mmu.c")
endif()
if((CONFIG_SOC_PM_SUPPORT_MODEM_PD OR CONFIG_SOC_PM_SUPPORT_TOP_PD) AND CONFIG_SOC_PAU_SUPPORTED)
list(APPEND srcs "port/${target}/sleep_clock.c")
endif()
@@ -1,152 +0,0 @@
/*
* SPDX-FileCopyrightText: 2023-2024 Espressif Systems (Shanghai) CO LTD
*
* SPDX-License-Identifier: Apache-2.0
*/
#include <stddef.h>
#include <string.h>
#include <inttypes.h>
#include "esp_attr.h"
#include "esp_check.h"
#include "esp_sleep.h"
#include "esp_log.h"
#include "esp_heap_caps.h"
#include "soc/soc_caps.h"
#include "sdkconfig.h"
#include "soc/spi_mem_reg.h"
#include "esp_private/startup_internal.h"
ESP_LOG_ATTR_TAG(TAG, "sleep_mmu");
typedef struct {
uint32_t start;
uint32_t end;
} mmu_domain_dev_regs_region_t;
typedef struct {
mmu_domain_dev_regs_region_t *region;
int region_num;
uint32_t *regs_frame;
} mmu_domain_dev_sleep_frame_t;
/**
* Internal structure which holds all requested light sleep mmu retention parameters
*/
typedef struct {
struct {
mmu_domain_dev_sleep_frame_t *mmu_table_frame;
} retent;
} sleep_mmu_retention_t;
static DRAM_ATTR __attribute__((unused)) sleep_mmu_retention_t s_mmu_retention;
static void * mmu_domain_dev_sleep_frame_alloc_and_init(const mmu_domain_dev_regs_region_t *regions, const int region_num)
{
const int region_sz = sizeof(mmu_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(mmu_domain_dev_sleep_frame_t) + region_sz + regs_frame_sz, MALLOC_CAP_32BIT|MALLOC_CAP_INTERNAL);
if (frame) {
mmu_domain_dev_regs_region_t *region = (mmu_domain_dev_regs_region_t *)(frame + sizeof(mmu_domain_dev_sleep_frame_t));
memcpy(region, regions, region_num * sizeof(mmu_domain_dev_regs_region_t));
void *regs_frame = frame + sizeof(mmu_domain_dev_sleep_frame_t) + region_sz;
memset(regs_frame, 0, regs_frame_sz);
*(mmu_domain_dev_sleep_frame_t *)frame = (mmu_domain_dev_sleep_frame_t) {
.region = region,
.region_num = region_num,
.regs_frame = (uint32_t *)regs_frame
};
}
return frame;
}
static inline void * mmu_domain_mmu_table_sleep_frame_alloc_and_init(void)
{
#define MMU_TABLE_SIZE (512 * 4)
const static mmu_domain_dev_regs_region_t regions[] = {
{ .start = SPI_MEM_MMU_ITEM_CONTENT_REG(0), .end = SPI_MEM_MMU_ITEM_CONTENT_REG(0) + MMU_TABLE_SIZE}
};
return mmu_domain_dev_sleep_frame_alloc_and_init(regions, sizeof(regions) / sizeof(regions[0]));
}
static IRAM_ATTR void mmu_domain_dev_regs_save(mmu_domain_dev_sleep_frame_t *frame)
{
assert(frame);
mmu_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) {
REG_WRITE(SPI_MEM_MMU_ITEM_INDEX_REG(0), offset);
regs_frame[offset++] = REG_READ(SPI_MEM_MMU_ITEM_CONTENT_REG(0));
}
}
}
static IRAM_ATTR void mmu_domain_dev_regs_restore(mmu_domain_dev_sleep_frame_t *frame)
{
assert(frame);
mmu_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) {
REG_WRITE(SPI_MEM_MMU_ITEM_INDEX_REG(0), offset);
REG_WRITE(SPI_MEM_MMU_ITEM_CONTENT_REG(0),regs_frame[offset++]);
}
}
}
IRAM_ATTR void esp_sleep_mmu_retention(bool backup_or_restore)
{
if (backup_or_restore) {
mmu_domain_dev_regs_save(s_mmu_retention.retent.mmu_table_frame);
} else {
mmu_domain_dev_regs_restore(s_mmu_retention.retent.mmu_table_frame);
}
}
static esp_err_t esp_sleep_mmu_retention_deinit(void)
{
if (s_mmu_retention.retent.mmu_table_frame) {
heap_caps_free((void *)s_mmu_retention.retent.mmu_table_frame);
s_mmu_retention.retent.mmu_table_frame = NULL;
}
return ESP_OK;
}
static esp_err_t esp_sleep_mmu_retention_init(void)
{
if (s_mmu_retention.retent.mmu_table_frame == NULL) {
void *frame = mmu_domain_mmu_table_sleep_frame_alloc_and_init();
if (frame == NULL) {
goto err;
}
s_mmu_retention.retent.mmu_table_frame = (mmu_domain_dev_sleep_frame_t *)frame;
}
return ESP_OK;
err:
esp_sleep_mmu_retention_deinit();
return ESP_ERR_NO_MEM;
}
bool mmu_domain_pd_allowed(void)
{
return (s_mmu_retention.retent.mmu_table_frame != NULL);
}
ESP_SYSTEM_INIT_FN(sleep_mmu_startup_init, SECONDARY, BIT(0), 108)
{
esp_err_t ret;
ret = esp_sleep_mmu_retention_init();
if (ret != ESP_OK) {
ESP_EARLY_LOGW(TAG, "Failed to enable TOP power down during light sleep.");
}
return ESP_OK;
}
+5 -21
View File
@@ -232,10 +232,6 @@
#define DEEP_SLEEP_TIME_OVERHEAD_US (250 + 100 * 240 / CONFIG_ESP_DEFAULT_CPU_FREQ_MHZ)
#endif
#if SOC_PM_MMU_TABLE_RETENTION_WHEN_TOP_PD
#define SLEEP_MMU_TABLE_RETENTION_OVERHEAD_US (1220)
#endif
#define RTC_MODULE_SLEEP_PREPARE_CYCLES (6)
#define CHECK_SOURCE(source, value, mask) ((s_config.wakeup_triggers & mask) && \
@@ -885,9 +881,6 @@ static esp_err_t FORCE_IRAM_ATTR esp_sleep_start_safe(uint32_t sleep_flags, uint
#if CONFIG_PM_POWER_DOWN_PERIPHERAL_IN_LIGHT_SLEEP
if (sleep_flags & PMU_SLEEP_PD_TOP) {
#if SOC_PM_MMU_TABLE_RETENTION_WHEN_TOP_PD
esp_sleep_mmu_retention(true);
#endif
#if CONFIG_IDF_TARGET_ESP32P4 && (CONFIG_ESP_REV_MIN_FULL == 300)
sleep_retention_do_extra_retention(true);
#endif
@@ -915,9 +908,6 @@ static esp_err_t FORCE_IRAM_ATTR esp_sleep_start_safe(uint32_t sleep_flags, uint
#if CONFIG_PM_POWER_DOWN_PERIPHERAL_IN_LIGHT_SLEEP
if (sleep_flags & PMU_SLEEP_PD_TOP) {
#if SOC_PM_MMU_TABLE_RETENTION_WHEN_TOP_PD
esp_sleep_mmu_retention(false);
#endif
#if SOC_PM_RETENTION_SW_TRIGGER_REGDMA
sleep_retention_do_system_retention(false);
#endif
@@ -1449,10 +1439,6 @@ esp_err_t esp_light_sleep_start(void)
int sleep_time_sw_adjustment = LIGHT_SLEEP_TIME_OVERHEAD_US + sleep_time_overhead_in + s_config.sleep_time_overhead_out;
int sleep_time_hw_adjustment = pmu_sleep_calculate_hw_wait_time(sleep_flags, rtc_clk_slow_src_get(), s_config.rtc_clk_cal_period, s_config.fast_clk_cal_period);
s_config.sleep_time_adjustment = sleep_time_sw_adjustment + sleep_time_hw_adjustment;
#if SOC_PM_MMU_TABLE_RETENTION_WHEN_TOP_PD
int sleep_time_sw_mmu_table_restore = (sleep_flags & PMU_SLEEP_PD_TOP) ? SLEEP_MMU_TABLE_RETENTION_OVERHEAD_US : 0;
s_config.sleep_time_adjustment += sleep_time_sw_mmu_table_restore;
#endif
#else
uint32_t rtc_cntl_xtl_buf_wait_slp_cycles = rtc_time_us_to_slowclk(RTC_CNTL_XTL_BUF_WAIT_SLP_US, s_config.rtc_clk_cal_period);
s_config.sleep_time_adjustment = LIGHT_SLEEP_TIME_OVERHEAD_US + sleep_time_overhead_in + s_config.sleep_time_overhead_out
@@ -2563,9 +2549,6 @@ FORCE_INLINE_ATTR bool top_domain_pd_allowed(void) {
top_pd_allowed &= cpu_domain_pd_allowed();
#else
top_pd_allowed = false;
#endif
#if CONFIG_PM_POWER_DOWN_PERIPHERAL_IN_LIGHT_SLEEP && SOC_PM_MMU_TABLE_RETENTION_WHEN_TOP_PD
top_pd_allowed &= mmu_domain_pd_allowed();
#endif
top_pd_allowed &= clock_domain_pd_allowed();
top_pd_allowed &= peripheral_domain_pd_allowed();
@@ -2575,10 +2558,11 @@ FORCE_INLINE_ATTR bool top_domain_pd_allowed(void) {
#if SOC_XTAL_CLOCK_PATH_DEPENDS_ON_TOP_DOMAIN
top_pd_allowed &= (s_config.domain[ESP_PD_DOMAIN_XTAL].pd_option != ESP_PD_OPTION_ON);
#endif
#if SOC_PM_TOP_PD_NOT_ALLOWED
// TODO: PM-436, Need to use efuse_hal_chip_revision() to determine whether
// the TOP domain power-down is allowed
top_pd_allowed = false;
#if CONFIG_IDF_TARGET_ESP32C5
if (!ESP_CHIP_REV_ABOVE(efuse_hal_chip_revision(), 102)) {
// ESP32C5 chips lower than v1.2 are not supported to power down the TOP domain
top_pd_allowed = false;
}
#endif
return top_pd_allowed;
@@ -92,7 +92,7 @@ TEST_CASE("verify esp_random() bit balance 0/1 ratio", "[random]")
#endif
TEST_ESP_OK(esp_sleep_enable_timer_wakeup(1 * 1000 * 1000));
TEST_ESP_OK(esp_light_sleep_start());
#if CONFIG_PM_POWER_DOWN_PERIPHERAL_IN_LIGHT_SLEEP && !SOC_PM_TOP_PD_NOT_ALLOWED
#if CONFIG_PM_POWER_DOWN_PERIPHERAL_IN_LIGHT_SLEEP
// check if the power domain also is powered down
TEST_ASSERT_EQUAL(PMU_SLEEP_PD_TOP, (sleep_ctx.sleep_flags) & PMU_SLEEP_PD_TOP);
#endif
@@ -1,15 +1,41 @@
# SPDX-FileCopyrightText: 2022-2025 Espressif Systems (Shanghai) CO LTD
# SPDX-FileCopyrightText: 2022-2026 Espressif Systems (Shanghai) CO LTD
# SPDX-License-Identifier: CC0-1.0
import pytest
from pytest_embedded import Dut
from pytest_embedded_idf.utils import idf_parametrize
from pytest_embedded_idf.utils import soc_filtered_targets
@pytest.mark.generic
@idf_parametrize(
'config,target',
[('single_core_esp32', 'esp32'), ('default', 'supported_targets'), ('release', 'supported_targets')],
[
('single_core_esp32', 'esp32'),
*(('default', target) for target in soc_filtered_targets('IDF_TARGET not in ["esp32c5"]')),
*(('release', target) for target in soc_filtered_targets('IDF_TARGET not in ["esp32c5"]')),
],
indirect=['config', 'target'],
)
def test_esp_hw_support(dut: Dut) -> None:
dut.run_all_single_board_cases(timeout=180)
@pytest.mark.generic
@idf_parametrize(
'config,target',
[('default', 'esp32c5')],
indirect=['config', 'target'],
)
def test_esp_hw_support_esp32c5(dut: Dut) -> None:
dut.run_all_single_board_cases(timeout=180)
@pytest.mark.generic
@pytest.mark.esp32c5_eco3
@idf_parametrize(
'config,target',
[('release', 'esp32c5')],
indirect=['config', 'target'],
)
def test_esp_hw_support_esp32c5_eco3(dut: Dut) -> None:
dut.run_all_single_board_cases(timeout=180)