From 69def0c3ea66607f6ff6ad2c1cd859d834dc638f Mon Sep 17 00:00:00 2001 From: Song Ruo Jing Date: Thu, 22 Jan 2026 17:21:18 +0800 Subject: [PATCH] feat(psram): allow PSRAM to enter halfsleep mode during light sleep --- .../test_apps/gpio/sdkconfig.defaults.esp32p4 | 2 + .../test_apps/i2s/sdkconfig.defaults.esp32p4 | 2 + .../esp32p4/include/hal/psram_ctrlr_ll.h | 16 ++- .../esp_hw_support/port/esp32p4/pmu_sleep.c | 38 ++++--- .../esp_hw_support/port/esp32p4/rtc_time.c | 3 +- components/esp_hw_support/sleep_modes.c | 8 ++ components/esp_pm/Kconfig | 24 +++++ components/esp_psram/CMakeLists.txt | 3 +- .../esp_psram/device/esp_psram_impl_ap_hex.c | 98 ++++++++++++++++++- .../esp_psram/device/esp_psram_impl_ap_quad.c | 16 ++- .../include/esp_private/esp_psram_impl.h | 41 +++++++- .../esp_psram/esp32/esp_psram_impl_quad.c | 17 +++- .../esp_psram/esp32s2/esp_psram_impl_quad.c | 16 ++- .../esp_psram/esp32s3/esp_psram_impl_octal.c | 16 ++- .../test_apps/psram/main/test_psram.c | 17 ++++ 15 files changed, 291 insertions(+), 26 deletions(-) diff --git a/components/esp_driver_gpio/test_apps/gpio/sdkconfig.defaults.esp32p4 b/components/esp_driver_gpio/test_apps/gpio/sdkconfig.defaults.esp32p4 index d2699b2221..59573527fd 100644 --- a/components/esp_driver_gpio/test_apps/gpio/sdkconfig.defaults.esp32p4 +++ b/components/esp_driver_gpio/test_apps/gpio/sdkconfig.defaults.esp32p4 @@ -1,3 +1,5 @@ CONFIG_SPIRAM=y CONFIG_SPIRAM_MODE_HEX=y CONFIG_SPIRAM_MALLOC_ALWAYSINTERNAL=0 +# Disable PSRAM halfsleep feature, otherwise P4 tcm section overflows with COMPILER_OPTIMIZATION_NONE=y +CONFIG_PM_SLP_SPIRAM_HALFSLEEP_ENABLED=n diff --git a/components/esp_driver_i2s/test_apps/i2s/sdkconfig.defaults.esp32p4 b/components/esp_driver_i2s/test_apps/i2s/sdkconfig.defaults.esp32p4 index d2699b2221..59573527fd 100644 --- a/components/esp_driver_i2s/test_apps/i2s/sdkconfig.defaults.esp32p4 +++ b/components/esp_driver_i2s/test_apps/i2s/sdkconfig.defaults.esp32p4 @@ -1,3 +1,5 @@ CONFIG_SPIRAM=y CONFIG_SPIRAM_MODE_HEX=y CONFIG_SPIRAM_MALLOC_ALWAYSINTERNAL=0 +# Disable PSRAM halfsleep feature, otherwise P4 tcm section overflows with COMPILER_OPTIMIZATION_NONE=y +CONFIG_PM_SLP_SPIRAM_HALFSLEEP_ENABLED=n diff --git a/components/esp_hal_mspi/esp32p4/include/hal/psram_ctrlr_ll.h b/components/esp_hal_mspi/esp32p4/include/hal/psram_ctrlr_ll.h index 659a405fb0..6538d54e32 100644 --- a/components/esp_hal_mspi/esp32p4/include/hal/psram_ctrlr_ll.h +++ b/components/esp_hal_mspi/esp32p4/include/hal/psram_ctrlr_ll.h @@ -1,5 +1,5 @@ /* - * SPDX-FileCopyrightText: 2023-2025 Espressif Systems (Shanghai) CO LTD + * SPDX-FileCopyrightText: 2023-2026 Espressif Systems (Shanghai) CO LTD * * SPDX-License-Identifier: Apache-2.0 */ @@ -263,6 +263,20 @@ static inline void psram_ctrlr_ll_enable_hex_data_line_mode(uint32_t mspi_id, bo SPIMEM2.mem_sram_cmd.mem_sdout_hex = en; } +/** + * @brief Get PSRAM hex data line mode enable status + * + * @param mspi_id mspi_id + * + * @return true if hex data line mode is enabled, false otherwise + */ +__attribute__((always_inline)) +static inline bool psram_ctrlr_ll_is_hex_data_line_mode(uint32_t mspi_id) +{ + (void)mspi_id; + return (SPIMEM2.mem_sram_cmd.mem_sdin_hex & SPIMEM2.mem_sram_cmd.mem_sdout_hex); +} + /** * @brief Enable PSRAM AXI master access * diff --git a/components/esp_hw_support/port/esp32p4/pmu_sleep.c b/components/esp_hw_support/port/esp32p4/pmu_sleep.c index ddf451f4e8..8b6cf1cd88 100644 --- a/components/esp_hw_support/port/esp32p4/pmu_sleep.c +++ b/components/esp_hw_support/port/esp32p4/pmu_sleep.c @@ -39,6 +39,7 @@ #if CONFIG_SPIRAM #include "hal/ldo_ll.h" #include "hal/mspi_ll.h" +#include "esp_private/esp_psram_impl.h" #endif #if (CONFIG_ESP_REV_MIN_FULL == 300) @@ -195,7 +196,7 @@ 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 CONFIG_SPIRAM && CONFIG_ESP32P4_SELECTS_REV_LESS_V3 // Adjust analog parameters to keep EXT_LDO PSRAM channel volt outputting during light-sleep. analog_default.hp_sys.analog.pd_cur = PMU_PD_CUR_SLEEP_ON; analog_default.lp_sys[PMU_MODE_LP_SLEEP].analog.pd_cur = PMU_PD_CUR_SLEEP_ON; @@ -408,11 +409,15 @@ TCM_IRAM_ATTR uint32_t pmu_sleep_start(uint32_t wakeup_opt, uint32_t reject_opt, // to be written back so that regdma can get the correct link. // 3. We cannot use the API provided by ROM to invalidate the cache, since it is a function calling that writes data to the stack during // the return process, which results in dirty cachelines in L1 Cache again. - pmu_sleep_cache_sync_items(SMMU_GID_DEFAULT, CACHE_SYNC_WRITEBACK, CACHE_MAP_L1_DCACHE, 0, 0); + pmu_sleep_cache_sync_items(SMMU_GID_DEFAULT, CACHE_SYNC_WRITEBACK, CACHE_MAP_L1_DCACHE, 0, 0); // No PSRAM dirty data after this time write back 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_hold_all_psram_pins(); #endif s_mpll_freq_mhz_before_sleep = rtc_clk_mpll_get_freq(); if (s_mpll_freq_mhz_before_sleep) { @@ -426,10 +431,10 @@ TCM_IRAM_ATTR uint32_t pmu_sleep_start(uint32_t wakeup_opt, uint32_t reject_opt, _psram_ctrlr_ll_enable_core_clock(PSRAM_CTRLR_LL_MSPI_ID_2, false); _psram_ctrlr_ll_enable_module_clock(PSRAM_CTRLR_LL_MSPI_ID_2, false); } - mspi_ll_hold_all_psram_pins(); #endif rtc_clk_mpll_disable(); } + pmu_sleep_cache_sync_items(SMMU_GID_DEFAULT, CACHE_SYNC_WRITEBACK, CACHE_MAP_L1_DCACHE, 0, 0); // No L2 MEM dirty data after this time write back } else { #if CONFIG_P4_REV3_MSPI_CRASH_AFTER_POWER_UP_WORKAROUND if (efuse_hal_chip_revision() == 300) { @@ -493,19 +498,26 @@ TCM_IRAM_ATTR bool pmu_sleep_finish(bool dslp) // Wait eFuse memory update done. while(efuse_ll_get_controller_state() != EFUSE_CONTROLLER_STATE_IDLE); - if (s_mpll_freq_mhz_before_sleep && !dslp) { - rtc_clk_mpll_enable(); - rtc_clk_mpll_configure(clk_hal_xtal_get_freq_mhz(), s_mpll_freq_mhz_before_sleep, true); + 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, true); #if CONFIG_SPIRAM - if (!s_pmu_sleep_regdma_backup_enabled) { - // MSPI2 and MSPI3 share the register for core clock. So we only set MSPI2 here. - // If it's a PD_TOP sleep, psram MSPI core clock will be enabled by REGDMA - _psram_ctrlr_ll_enable_core_clock(PSRAM_CTRLR_LL_MSPI_ID_2, true); - _psram_ctrlr_ll_enable_module_clock(PSRAM_CTRLR_LL_MSPI_ID_2, true); + if (!s_pmu_sleep_regdma_backup_enabled) { + // MSPI2 and MSPI3 share the register for core clock. So we only set MSPI2 here. + // If it's a PD_TOP sleep, psram MSPI core clock will be enabled by REGDMA + _psram_ctrlr_ll_enable_core_clock(PSRAM_CTRLR_LL_MSPI_ID_2, true); + _psram_ctrlr_ll_enable_module_clock(PSRAM_CTRLR_LL_MSPI_ID_2, true); + } + _psram_ctrlr_ll_select_clk_source(PSRAM_CTRLR_LL_MSPI_ID_2, PSRAM_CLK_SRC_MPLL); + _psram_ctrlr_ll_select_clk_source(PSRAM_CTRLR_LL_MSPI_ID_3, PSRAM_CLK_SRC_MPLL); +#endif } - _psram_ctrlr_ll_select_clk_source(PSRAM_CTRLR_LL_MSPI_ID_2, PSRAM_CLK_SRC_MPLL); - _psram_ctrlr_ll_select_clk_source(PSRAM_CTRLR_LL_MSPI_ID_3, PSRAM_CLK_SRC_MPLL); +#if CONFIG_SPIRAM mspi_ll_unhold_all_psram_pins(); +#if CONFIG_PM_SLP_SPIRAM_HALFSLEEP_ENABLED + esp_psram_impl_exit_halfsleep_mode(); +#endif #endif } diff --git a/components/esp_hw_support/port/esp32p4/rtc_time.c b/components/esp_hw_support/port/esp32p4/rtc_time.c index fec13d07a3..d82dafbcef 100644 --- a/components/esp_hw_support/port/esp32p4/rtc_time.c +++ b/components/esp_hw_support/port/esp32p4/rtc_time.c @@ -15,6 +15,7 @@ #include "soc/timer_group_reg.h" #include "esp_rom_sys.h" #include "esp_private/periph_ctrl.h" +#include "esp_attr.h" ESP_LOG_ATTR_TAG(TAG, "rtc_time"); @@ -211,7 +212,7 @@ uint64_t rtc_time_slowclk_to_us(uint64_t rtc_cycles, uint32_t period) return (rtc_cycles * period) >> RTC_CLK_CAL_FRACT; } -uint64_t rtc_time_get(void) +TCM_IRAM_ATTR uint64_t rtc_time_get(void) { return rtc_timer_hal_get_cycle_count(0); } diff --git a/components/esp_hw_support/sleep_modes.c b/components/esp_hw_support/sleep_modes.c index 1d0f601320..f2e5b7faed 100644 --- a/components/esp_hw_support/sleep_modes.c +++ b/components/esp_hw_support/sleep_modes.c @@ -161,6 +161,10 @@ #include "esp_private/sleep_retention.h" #endif +#if CONFIG_PM_SLP_SPIRAM_HALFSLEEP_ENABLED +#include "esp_private/esp_psram_impl.h" +#endif + // If light sleep time is less than that, don't power down flash #define FLASH_PD_MIN_SLEEP_TIME_US 2000 @@ -1382,6 +1386,10 @@ static SLEEP_FN_ATTR esp_err_t esp_light_sleep_inner(uint32_t sleep_flags, uint3 #endif } +#if CONFIG_PM_SLP_SPIRAM_HALFSLEEP_ENABLED + esp_psram_impl_resume_from_halfsleep_mode(s_config.rtc_clk_cal_period); +#endif + #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 */ diff --git a/components/esp_pm/Kconfig b/components/esp_pm/Kconfig index 7f6c3623ae..edb17a8d66 100644 --- a/components/esp_pm/Kconfig +++ b/components/esp_pm/Kconfig @@ -287,4 +287,28 @@ menu "Power Management" Only enabled when SPI_FLASH_FREQ_LIMIT_C5_240MHZ is enabled. This is an internal configuration, automatically set based on SPI Flash configuration. + config PM_SLP_SPIRAM_HALFSLEEP_ENABLED + bool "Enable halfsleep mode for PSRAM in light sleep" + depends on SPIRAM + default y + help + Enable PSRAM halfsleep mode feature to reduce power consumption during light sleep, if the PSRAM used + support halfsleep mode. + + When enabled, the PSRAM chip will be configured to enter halfsleep mode (if applicable) before entering + light sleep. This reduces power consumption by: + 1. Setting refresh rate to 0.5x + 2. Setting PSRAM to halfsleep mode + + The PSRAM will automatically exit halfsleep mode after waking up from light sleep. + + config PM_SLP_SPIRAM_HALFSLEEP_EXIT_WAIT_DELAY + int "The time to wait for PSRAM to be able to access after exiting halfsleep mode (in us)" + depends on SPIRAM + default 150 + range 50 250 + help + Configure the delay time between waking up PSRAM halfsleep mode and the first access to PSRAM. + Refer to the tXHS parameter in datasheet of the used PSRAM to configure this option. + endmenu # "Power Management" diff --git a/components/esp_psram/CMakeLists.txt b/components/esp_psram/CMakeLists.txt index 55badaefc1..5d1ee8437b 100644 --- a/components/esp_psram/CMakeLists.txt +++ b/components/esp_psram/CMakeLists.txt @@ -4,7 +4,7 @@ if(${target} STREQUAL "linux") return() # This component is not supported by the POSIX/Linux simulator endif() -set(includes "include") +set(includes "include" "device/include") if(CONFIG_SOC_SPIRAM_XIP_SUPPORTED) list(APPEND includes xip_impl/include) @@ -37,7 +37,6 @@ endif() idf_component_register(SRCS ${srcs} INCLUDE_DIRS ${includes} - PRIV_INCLUDE_DIRS device/include PRIV_REQUIRES ${priv_requires} LDFRAGMENTS linker.lf) diff --git a/components/esp_psram/device/esp_psram_impl_ap_hex.c b/components/esp_psram/device/esp_psram_impl_ap_hex.c index bf241b43e1..09d9012a98 100644 --- a/components/esp_psram/device/esp_psram_impl_ap_hex.c +++ b/components/esp_psram/device/esp_psram_impl_ap_hex.c @@ -1,5 +1,5 @@ /* - * SPDX-FileCopyrightText: 2019-2025 Espressif Systems (Shanghai) CO LTD + * SPDX-FileCopyrightText: 2019-2026 Espressif Systems (Shanghai) CO LTD * * SPDX-License-Identifier: Apache-2.0 */ @@ -14,6 +14,7 @@ #include "hal/psram_ctrlr_ll.h" #include "hal/mspi_ll.h" #include "clk_ctrl_os.h" +#include "soc/rtc.h" #define AP_HEX_PSRAM_SYNC_READ 0x0000 #define AP_HEX_PSRAM_SYNC_WRITE 0x8080 @@ -62,6 +63,8 @@ #define AP_HEX_PSRAM_REF_DATA 0x5a6b7c8d +#define AP_HEX_PSRAM_ULP_MODE_HALFSLEEP 0xF0 + typedef struct { union { struct { @@ -76,7 +79,7 @@ typedef struct { union { struct { uint8_t vendor_id: 5; - uint8_t rsvd0_2: 2; + uint8_t rsvd5_7: 3; uint8_t ulp: 1; }; uint8_t val; @@ -91,9 +94,9 @@ typedef struct { } mr2; union { struct { - uint8_t rsvd3_7: 4; + uint8_t rsvd0_3: 4; uint8_t srf: 2; - uint8_t rsvd0: 1; + uint8_t rsvd6: 1; uint8_t rbx_en: 1; }; uint8_t val; @@ -106,6 +109,13 @@ typedef struct { }; uint8_t val; } mr4; + union { + struct { + uint8_t rsvd0_3: 4; + uint8_t halfsleep: 4; + }; + uint8_t val; + } mr6; union { struct { uint8_t bl: 2; @@ -507,3 +517,83 @@ esp_err_t esp_psram_impl_get_available_size(uint32_t *out_size_bytes) #endif return (s_psram_size ? ESP_OK : ESP_ERR_INVALID_STATE); } + +/******************************* Halfsleep Mode *******************************/ +static PSRAM_HALFSLEEP_DATA_ATTR struct { + uint8_t mr4; + bool is_hex_line_mode; + uint64_t halfsleep_wakeup_tick; +} s_halfsleep_ctx = {0}; + +PSRAM_HALFSLEEP_SLEEP_CODE_ATTR void esp_psram_impl_enter_halfsleep_mode(void) +{ + // Backup line mode configuration and set to 8-line mode (MR registers R/W not support in hex line mode) + s_halfsleep_ctx.is_hex_line_mode = psram_ctrlr_ll_is_hex_data_line_mode(PSRAM_CTRLR_LL_MSPI_ID_3); + psram_ctrlr_ll_enable_hex_data_line_mode(PSRAM_CTRLR_LL_MSPI_ID_3, false); + // Backup MR4 + psram_ctrlr_ll_common_transaction(PSRAM_CTRLR_LL_MSPI_ID_3, + AP_HEX_PSRAM_REG_READ, AP_HEX_PSRAM_RD_CMD_BITLEN, + 0x4, AP_HEX_PSRAM_ADDR_BITLEN, + AP_HEX_PSRAM_RD_REG_DUMMY_BITLEN, + NULL, 0, + &s_halfsleep_ctx.mr4, 16, + false); + // Set refresh rate to 0.5x + hex_psram_mode_reg_t mode_reg; + mode_reg.mr4.val = s_halfsleep_ctx.mr4; + mode_reg.mr4.rf = 3; // (0:4x 1:1x 3:0.5x) + psram_ctrlr_ll_common_transaction(PSRAM_CTRLR_LL_MSPI_ID_3, + AP_HEX_PSRAM_REG_WRITE, AP_HEX_PSRAM_RD_CMD_BITLEN, + 0x4, AP_HEX_PSRAM_ADDR_BITLEN, + 0, + &mode_reg.mr4.val, 16, + NULL, 0, + false); + // Set halfsleep mode + uint8_t halfsleep_cmd = AP_HEX_PSRAM_ULP_MODE_HALFSLEEP; + psram_ctrlr_ll_common_transaction(PSRAM_CTRLR_LL_MSPI_ID_3, + AP_HEX_PSRAM_REG_WRITE, AP_HEX_PSRAM_RD_CMD_BITLEN, + 0x6, AP_HEX_PSRAM_ADDR_BITLEN, + 0, + &halfsleep_cmd, 16, + NULL, 0, + false); +} + +PSRAM_HALFSLEEP_SLEEP_CODE_ATTR void esp_psram_impl_exit_halfsleep_mode(void) +{ + // Record the tick exiting halfsleep mode + s_halfsleep_ctx.halfsleep_wakeup_tick = rtc_time_get(); + + // Do a SPI dummy write transmission to invalid address to wake up from halfsleep mode + uint8_t null = 0; + psram_ctrlr_ll_common_transaction(PSRAM_CTRLR_LL_MSPI_ID_3, + AP_HEX_PSRAM_REG_WRITE, AP_HEX_PSRAM_WR_CMD_BITLEN, + 0xFF, AP_HEX_PSRAM_ADDR_BITLEN, + 0, + &null, 0, + NULL, 0, + false); +} + +PSRAM_HALFSLEEP_RESUME_CODE_ATTR void esp_psram_impl_resume_from_halfsleep_mode(uint32_t slowclk_period) +{ + uint64_t halfsleep_exit_tick = s_halfsleep_ctx.halfsleep_wakeup_tick + rtc_time_us_to_slowclk(CONFIG_PM_SLP_SPIRAM_HALFSLEEP_EXIT_WAIT_DELAY, slowclk_period); + while (rtc_time_get() < halfsleep_exit_tick) { + // Busy wait for PSRAM to exit halfsleep mode + } + + // Restore MR4 configuration (restore refresh rate) + psram_ctrlr_ll_common_transaction(PSRAM_CTRLR_LL_MSPI_ID_3, + AP_HEX_PSRAM_REG_WRITE, AP_HEX_PSRAM_WR_CMD_BITLEN, + 0x4, AP_HEX_PSRAM_ADDR_BITLEN, + 0, + &s_halfsleep_ctx.mr4, 16, + NULL, 0, + false); + + // Restore hex line mode if it was enabled before + if (s_halfsleep_ctx.is_hex_line_mode) { + psram_ctrlr_ll_enable_hex_data_line_mode(PSRAM_CTRLR_LL_MSPI_ID_3, true); + } +} diff --git a/components/esp_psram/device/esp_psram_impl_ap_quad.c b/components/esp_psram/device/esp_psram_impl_ap_quad.c index ebbd4895c0..b6c4d84f2e 100644 --- a/components/esp_psram/device/esp_psram_impl_ap_quad.c +++ b/components/esp_psram/device/esp_psram_impl_ap_quad.c @@ -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 */ @@ -433,6 +433,20 @@ static void config_psram_spi_phases(void) psram_ctrlr_ll_set_cs_pin(PSRAM_CTRLR_LL_MSPI_ID_0, PSRAM_LL_CS_ID_1); } +/******************************* Halfsleep Mode *******************************/ +// This PSRAM device does not support halfsleep mode +PSRAM_HALFSLEEP_SLEEP_CODE_ATTR void esp_psram_impl_enter_halfsleep_mode(void) +{ +} + +PSRAM_HALFSLEEP_SLEEP_CODE_ATTR void esp_psram_impl_exit_halfsleep_mode(void) +{ +} + +PSRAM_HALFSLEEP_RESUME_CODE_ATTR void esp_psram_impl_resume_from_halfsleep_mode(uint32_t slowclk_period) +{ +} + /*--------------------------------------------------------------------------------- * Following APIs are not required to be IRAM-Safe * diff --git a/components/esp_psram/device/include/esp_private/esp_psram_impl.h b/components/esp_psram/device/include/esp_private/esp_psram_impl.h index 4af953527b..7a607892a0 100644 --- a/components/esp_psram/device/include/esp_private/esp_psram_impl.h +++ b/components/esp_psram/device/include/esp_private/esp_psram_impl.h @@ -1,5 +1,5 @@ /* - * SPDX-FileCopyrightText: 2015-2024 Espressif Systems (Shanghai) CO LTD + * SPDX-FileCopyrightText: 2015-2026 Espressif Systems (Shanghai) CO LTD * * SPDX-License-Identifier: Apache-2.0 */ @@ -8,6 +8,7 @@ #include "esp_err.h" #include "sdkconfig.h" +#include "soc/soc_caps.h" #ifdef __cplusplus extern "C" { @@ -20,6 +21,21 @@ extern "C" { #define PSRAM_SIZE_32MB (32 * 1024 * 1024) #define PSRAM_SIZE_64MB (64 * 1024 * 1024) +#if CONFIG_PM_SLP_SPIRAM_HALFSLEEP_ENABLED +#if SOC_CACHE_INTERNAL_MEM_VIA_L1CACHE +#define PSRAM_HALFSLEEP_DATA_ATTR TCM_DRAM_ATTR +#define PSRAM_HALFSLEEP_SLEEP_CODE_ATTR TCM_IRAM_ATTR +#else +#define PSRAM_HALFSLEEP_DATA_ATTR DRAM_ATTR +#define PSRAM_HALFSLEEP_SLEEP_CODE_ATTR IRAM_ATTR +#endif +#define PSRAM_HALFSLEEP_RESUME_CODE_ATTR IRAM_ATTR +#else +#define PSRAM_HALFSLEEP_DATA_ATTR +#define PSRAM_HALFSLEEP_SLEEP_CODE_ATTR +#define PSRAM_HALFSLEEP_RESUME_CODE_ATTR +#endif + /** * @brief To get the physical psram size in bytes. * @@ -51,6 +67,29 @@ esp_err_t esp_psram_impl_enable(void); */ uint8_t esp_psram_impl_get_cs_io(void); +/** + * @brief Set psram to halfsleep mode to save power consumption + * + * It will also set refresh rate to 0.5x. + */ +void esp_psram_impl_enter_halfsleep_mode(void); + +/** + * @brief Make psram exit halfsleep mode (no wait, wait is done in esp_psram_impl_resume_from_halfsleep_mode) + */ +void esp_psram_impl_exit_halfsleep_mode(void); + +/** + * @brief Resume psram from halfsleep mode (also restore refresh rate) + * + * @param slowclk_period RTC slow clock period, calibration value obtained using rtc_clk_cal + * + * @note PSRAM requires some time (tHSPU) to fully recover from halfsleep mode. + * This function examines the time between exiting halfsleep mode and current time, and busy wait if the time is not long enough. + * This shortens the overall busy loop time if wait is done inside esp_psram_impl_exit_halfsleep_mode. + */ +void esp_psram_impl_resume_from_halfsleep_mode(uint32_t slowclk_period); + #ifdef __cplusplus } #endif diff --git a/components/esp_psram/esp32/esp_psram_impl_quad.c b/components/esp_psram/esp32/esp_psram_impl_quad.c index 8b5dae69f4..8f00287203 100644 --- a/components/esp_psram/esp32/esp_psram_impl_quad.c +++ b/components/esp_psram/esp32/esp_psram_impl_quad.c @@ -1,5 +1,5 @@ /* - * SPDX-FileCopyrightText: 2013-2024 Espressif Systems (Shanghai) CO LTD + * SPDX-FileCopyrightText: 2013-2026 Espressif Systems (Shanghai) CO LTD * * SPDX-License-Identifier: Apache-2.0 */ @@ -1175,4 +1175,19 @@ esp_err_t esp_psram_impl_get_available_size(uint32_t *out_size_bytes) { return esp_psram_impl_get_physical_size(out_size_bytes); } + +/******************************* Halfsleep Mode *******************************/ +// This PSRAM device does not support halfsleep mode +PSRAM_HALFSLEEP_SLEEP_CODE_ATTR void esp_psram_impl_enter_halfsleep_mode(void) +{ +} + +PSRAM_HALFSLEEP_SLEEP_CODE_ATTR void esp_psram_impl_exit_halfsleep_mode(void) +{ +} + +PSRAM_HALFSLEEP_RESUME_CODE_ATTR void esp_psram_impl_resume_from_halfsleep_mode(uint32_t slowclk_period) +{ +} + #endif // CONFIG_SPIRAM diff --git a/components/esp_psram/esp32s2/esp_psram_impl_quad.c b/components/esp_psram/esp32s2/esp_psram_impl_quad.c index e8def0feab..62d53af995 100644 --- a/components/esp_psram/esp32s2/esp_psram_impl_quad.c +++ b/components/esp_psram/esp32s2/esp_psram_impl_quad.c @@ -1,5 +1,5 @@ /* - * SPDX-FileCopyrightText: 2013-2024 Espressif Systems (Shanghai) CO LTD + * SPDX-FileCopyrightText: 2013-2026 Espressif Systems (Shanghai) CO LTD * * SPDX-License-Identifier: Apache-2.0 */ @@ -577,3 +577,17 @@ esp_err_t esp_psram_impl_get_available_size(uint32_t *out_size_bytes) { return esp_psram_impl_get_physical_size(out_size_bytes); } + +/******************************* Halfsleep Mode *******************************/ +// This PSRAM device does not support halfsleep mode +PSRAM_HALFSLEEP_SLEEP_CODE_ATTR void esp_psram_impl_enter_halfsleep_mode(void) +{ +} + +PSRAM_HALFSLEEP_SLEEP_CODE_ATTR void esp_psram_impl_exit_halfsleep_mode(void) +{ +} + +PSRAM_HALFSLEEP_RESUME_CODE_ATTR void esp_psram_impl_resume_from_halfsleep_mode(uint32_t slowclk_period) +{ +} diff --git a/components/esp_psram/esp32s3/esp_psram_impl_octal.c b/components/esp_psram/esp32s3/esp_psram_impl_octal.c index a5e1aaf999..579979d1a4 100644 --- a/components/esp_psram/esp32s3/esp_psram_impl_octal.c +++ b/components/esp_psram/esp32s3/esp_psram_impl_octal.c @@ -1,5 +1,5 @@ /* - * SPDX-FileCopyrightText: 2019-2025 Espressif Systems (Shanghai) CO LTD + * SPDX-FileCopyrightText: 2019-2026 Espressif Systems (Shanghai) CO LTD * * SPDX-License-Identifier: Apache-2.0 */ @@ -456,3 +456,17 @@ esp_err_t esp_psram_impl_get_available_size(uint32_t *out_size_bytes) #endif return (s_psram_size ? ESP_OK : ESP_ERR_INVALID_STATE); } + +/******************************* Halfsleep Mode *******************************/ +// This PSRAM supports halfsleep mode, but not implemented yet +PSRAM_HALFSLEEP_SLEEP_CODE_ATTR void esp_psram_impl_enter_halfsleep_mode(void) +{ +} + +PSRAM_HALFSLEEP_SLEEP_CODE_ATTR void esp_psram_impl_exit_halfsleep_mode(void) +{ +} + +PSRAM_HALFSLEEP_RESUME_CODE_ATTR void esp_psram_impl_resume_from_halfsleep_mode(uint32_t slowclk_period) +{ +} diff --git a/components/esp_psram/test_apps/psram/main/test_psram.c b/components/esp_psram/test_apps/psram/main/test_psram.c index 9a292d9757..7dc2e59755 100644 --- a/components/esp_psram/test_apps/psram/main/test_psram.c +++ b/components/esp_psram/test_apps/psram/main/test_psram.c @@ -17,8 +17,10 @@ #include "esp_private/esp_psram_io.h" #include "esp_psram.h" #include "esp_private/esp_psram_extram.h" +#include "esp_private/esp_psram_impl.h" #include "esp_flash.h" #include "esp_partition.h" +#include "soc/rtc.h" __attribute__((unused)) const static char *TAG = "PSRAM"; @@ -58,6 +60,21 @@ TEST_CASE("stress test psram heap allocable", "[psram][manual][ignore]") } } +#if !CONFIG_SPIRAM_XIP_FROM_PSRAM +TEST_CASE("test psram halfsleep mode (if applicable)", "[psram]") +{ + uint32_t rtc_slow_clk_period = rtc_clk_cal(CLK_CAL_RTC_SLOW, CONFIG_RTC_CLK_CAL_CYCLES); + s_test_psram_heap_allocable(); + for (int i = 0; i < 5; i++) { + esp_psram_impl_enter_halfsleep_mode(); + vTaskDelay(pdMS_TO_TICKS(1000)); + esp_psram_impl_exit_halfsleep_mode(); + esp_psram_impl_resume_from_halfsleep_mode(rtc_slow_clk_period); + s_test_psram_heap_allocable(); + } +} +#endif + #if CONFIG_SPIRAM_XIP_FROM_PSRAM /*--------------------------------------------------------------- SPI1 with XIP