diff --git a/components/esp_hw_support/include/esp_sleep.h b/components/esp_hw_support/include/esp_sleep.h index ec282ab59f..de25aeb268 100644 --- a/components/esp_hw_support/include/esp_sleep.h +++ b/components/esp_hw_support/include/esp_sleep.h @@ -759,6 +759,41 @@ esp_err_t esp_sleep_cpu_retention_init(void); esp_err_t esp_sleep_cpu_retention_deinit(void); #endif // ESP_SLEEP_POWER_DOWN_CPU +#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 Mmu backup initialize when power down TOP domain + * + * @return + * - ESP_OK on success + * - ESP_ERR_NO_MEM not enough retention memory + */ +esp_err_t esp_sleep_mmu_retention_init(void); + +/** + * @brief Mmu backup de-initialize when power down TOP domain + * + * @return + * - ESP_OK on success + * + * Release system retention memory. + */ +esp_err_t esp_sleep_mmu_retention_deinit(void); + +/** + * @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 Configure to isolate all GPIO pins in sleep state */ diff --git a/components/esp_hw_support/lowpower/CMakeLists.txt b/components/esp_hw_support/lowpower/CMakeLists.txt index 3ba4d4d6a8..e92715e94a 100644 --- a/components/esp_hw_support/lowpower/CMakeLists.txt +++ b/components/esp_hw_support/lowpower/CMakeLists.txt @@ -16,6 +16,10 @@ if(CONFIG_PM_POWER_DOWN_CPU_IN_LIGHT_SLEEP OR endif() endif() +if(CONFIG_SOC_PM_MMU_TABLE_RETENTION_WHEN_TOP_PD AND CONFIG_PM_POWER_DOWN_PERIPHERAL_IN_LIGHT_SLEEP) + list(APPEND srcs "cpu_retention/port/${target}/sleep_mmu.c") +endif() + add_prefix(srcs "${CMAKE_CURRENT_LIST_DIR}/" "${srcs}") target_sources(${COMPONENT_LIB} PRIVATE "${srcs}") diff --git a/components/esp_hw_support/lowpower/cpu_retention/port/esp32c5/sleep_cpu.c b/components/esp_hw_support/lowpower/cpu_retention/port/esp32c5/sleep_cpu.c index 5a1aef983f..b82037ce8f 100644 --- a/components/esp_hw_support/lowpower/cpu_retention/port/esp32c5/sleep_cpu.c +++ b/components/esp_hw_support/lowpower/cpu_retention/port/esp32c5/sleep_cpu.c @@ -452,7 +452,6 @@ esp_err_t IRAM_ATTR esp_sleep_cpu_retention(uint32_t (*goto_sleep)(uint32_t, uin /* 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 CONFIG_PM_CHECK_SLEEP_RETENTION_FRAME diff --git a/components/esp_hw_support/lowpower/cpu_retention/port/esp32c5/sleep_mmu.c b/components/esp_hw_support/lowpower/cpu_retention/port/esp32c5/sleep_mmu.c new file mode 100644 index 0000000000..ac2f45da7b --- /dev/null +++ b/components/esp_hw_support/lowpower/cpu_retention/port/esp32c5/sleep_mmu.c @@ -0,0 +1,162 @@ +/* + * SPDX-FileCopyrightText: 2023-2024 Espressif Systems (Shanghai) CO LTD + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#include +#include +#include + +#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" + +static const char *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_impl(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_impl(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; +} + +esp_err_t esp_sleep_mmu_retention_init(void) +{ + return esp_sleep_mmu_retention_init_impl(); +} + +esp_err_t esp_sleep_mmu_retention_deinit(void) +{ + return esp_sleep_mmu_retention_deinit_impl(); +} + +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; +} diff --git a/components/esp_hw_support/sleep_gpio.c b/components/esp_hw_support/sleep_gpio.c index 218d353367..001cdec8af 100644 --- a/components/esp_hw_support/sleep_gpio.c +++ b/components/esp_hw_support/sleep_gpio.c @@ -30,7 +30,7 @@ #include "esp_private/startup_internal.h" #include "bootloader_flash.h" -static const char *TAG = "sleep"; +static const char *TAG = "sleep_gpio"; #if CONFIG_GPIO_ESP32_SUPPORT_SWITCH_SLP_PULL void gpio_sleep_mode_config_apply(void) diff --git a/components/esp_hw_support/sleep_modes.c b/components/esp_hw_support/sleep_modes.c index c1378dfb72..def6f19aca 100644 --- a/components/esp_hw_support/sleep_modes.c +++ b/components/esp_hw_support/sleep_modes.c @@ -181,6 +181,10 @@ #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 (961) +#endif + // Minimal amount of time we can sleep for #define LIGHT_SLEEP_MIN_TIME_US 200 @@ -1005,6 +1009,12 @@ static esp_err_t IRAM_ATTR esp_sleep_start(uint32_t pd_flags, esp_sleep_mode_t m } #endif +#if CONFIG_PM_POWER_DOWN_PERIPHERAL_IN_LIGHT_SLEEP && SOC_PM_MMU_TABLE_RETENTION_WHEN_TOP_PD + if (pd_flags & PMU_SLEEP_PD_TOP) { + esp_sleep_mmu_retention(true); + } +#endif + #if SOC_PMU_SUPPORTED #if SOC_PM_CPU_RETENTION_BY_SW && ESP_SLEEP_POWER_DOWN_CPU esp_sleep_execute_event_callbacks(SLEEP_EVENT_HW_GOTO_SLEEP, (void *)0); @@ -1024,6 +1034,11 @@ static esp_err_t IRAM_ATTR esp_sleep_start(uint32_t pd_flags, esp_sleep_mode_t m result = call_rtc_sleep_start(reject_triggers, config.lslp_mem_inf_fpu, deep_sleep); #endif +#if CONFIG_PM_POWER_DOWN_PERIPHERAL_IN_LIGHT_SLEEP && SOC_PM_MMU_TABLE_RETENTION_WHEN_TOP_PD + if (pd_flags & PMU_SLEEP_PD_TOP) { + esp_sleep_mmu_retention(false); + } +#endif /* Unhold the SPI CS pin */ #if (CONFIG_PM_POWER_DOWN_PERIPHERAL_IN_LIGHT_SLEEP && CONFIG_ESP_SLEEP_FLASH_LEAKAGE_WORKAROUND) #if !CONFIG_IDF_TARGET_ESP32H2 // ESP32H2 TODO IDF-7359 @@ -1343,6 +1358,10 @@ 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(pd_flags, 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 = (pd_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 @@ -2126,6 +2145,9 @@ 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(); diff --git a/components/esp_system/system_init_fn.txt b/components/esp_system/system_init_fn.txt index 535a9d1f35..6e7da69cc9 100644 --- a/components/esp_system/system_init_fn.txt +++ b/components/esp_system/system_init_fn.txt @@ -82,6 +82,7 @@ SECONDARY: 103: esp_security_init in components/esp_security/src/init.c on BIT(0 SECONDARY: 105: esp_sleep_startup_init in components/esp_hw_support/sleep_gpio.c on BIT(0) SECONDARY: 106: sleep_clock_startup_init in components/esp_hw_support/sleep_clock.c on BIT(0) SECONDARY: 107: sleep_sys_periph_startup_init in components/esp_hw_support/sleep_system_peripheral.c on BIT(0) +SECONDARY: 108: sleep_mmu_startup_init in components/esp_hw_support/lowpower/cpu_retention/port/esp32c5/sleep_mmu.c on BIT(0) # app_trace has to be initialized before systemview SECONDARY: 115: esp_apptrace_init in components/app_trace/app_trace.c on ESP_SYSTEM_INIT_ALL_CORES diff --git a/components/soc/esp32c5/include/soc/Kconfig.soc_caps.in b/components/soc/esp32c5/include/soc/Kconfig.soc_caps.in index 6abe798726..d96e6eb98d 100644 --- a/components/soc/esp32c5/include/soc/Kconfig.soc_caps.in +++ b/components/soc/esp32c5/include/soc/Kconfig.soc_caps.in @@ -1247,6 +1247,10 @@ config SOC_PM_MODEM_RETENTION_BY_REGDMA bool default y +config SOC_PM_MMU_TABLE_RETENTION_WHEN_TOP_PD + bool + default y + config SOC_PM_PAU_LINK_NUM int default 5 diff --git a/components/soc/esp32c5/include/soc/soc_caps.h b/components/soc/esp32c5/include/soc/soc_caps.h index 984abfd79c..17d54286f3 100644 --- a/components/soc/esp32c5/include/soc/soc_caps.h +++ b/components/soc/esp32c5/include/soc/soc_caps.h @@ -560,7 +560,7 @@ #define SOC_PM_CPU_RETENTION_BY_SW (1) #define SOC_PM_MODEM_RETENTION_BY_REGDMA (1) -// #define SOC_PM_RETENTION_HAS_CLOCK_BUG (1) +#define SOC_PM_MMU_TABLE_RETENTION_WHEN_TOP_PD (1) #define SOC_PM_PAU_LINK_NUM (5) #define SOC_PM_PAU_REGDMA_LINK_CONFIGURABLE (1)