From 475ec39cb4f8fd98dd8d926c4e3a273048959a5a Mon Sep 17 00:00:00 2001 From: Xiao Xufeng Date: Fri, 8 May 2026 01:58:01 +0800 Subject: [PATCH] fix(mspi): fixed possible boot failure in some builds when psram is enabled A typical scenario is: when XIP on PSRAM enabled, compiler optimization level is Os. Under certain binary layout, boot hangs and backtrace points to `esp_sleep_config_gpio_isolate`. The root cause is that, during PSRAM initialization, it calls esp_gpio_reserve, which happens to place before the reported function. However, after call, there is no barrier before the clock adjustment in `mspi_timing_enter_low_speed_mode`. The clock gets changed when the cache is still fetching data, resulting in the corrupted data in the end of the cache line. This commits add spi_flash_disable_cache as a barrier to make sure the cache transactions is finished before the clock switch. --- .../include/esp_private/mspi_timing_tuning.h | 15 +++++ .../mspi_timing_tuning/mspi_timing_tuning.c | 60 +++++++++++++++++-- .../esp_psram/device/esp_psram_impl_ap_quad.c | 6 +- .../esp_psram/esp32s3/esp_psram_impl_octal.c | 6 +- .../scripts/esp32c5/sec_srv_tbl_default.yml | 8 +++ .../esp_tee/src/esp_secure_service_wrapper.c | 10 ++++ .../main/core/esp_secure_services_iram.c | 10 ++++ .../subproject/main/ld/esp32c5/esp_tee.ld.in | 2 + 8 files changed, 105 insertions(+), 12 deletions(-) diff --git a/components/esp_hw_support/mspi_timing_tuning/include/esp_private/mspi_timing_tuning.h b/components/esp_hw_support/mspi_timing_tuning/include/esp_private/mspi_timing_tuning.h index be59ef7dfb6..55c00908dd8 100644 --- a/components/esp_hw_support/mspi_timing_tuning/include/esp_private/mspi_timing_tuning.h +++ b/components/esp_hw_support/mspi_timing_tuning/include/esp_private/mspi_timing_tuning.h @@ -36,6 +36,21 @@ uint32_t mspi_timing_get_psram_low_speed_freq_mhz(void); */ void mspi_timing_enter_high_speed_mode(bool control_spi1); +/** + * @brief Switch MSPI to low speed while suspending external memory cache to avoid in-flight cache line fills across the + * clock change. + * + * @note Early init only. Not safe for general runtime use: does not coordinate with other cores or freeze cache. + */ +void mspi_timing_enter_low_speed_early(void); + +/** + * @brief Switch MSPI to high speed while suspending external memory cache. + * + * @note Same usage constraints as @ref mspi_timing_enter_low_speed_early. + */ +void mspi_timing_enter_high_speed_early(void); + /** * @brief Switch MSPI into low speed mode / high speed mode. * @note This API is cache safe, it will freeze both D$ and I$ and restore them after MSPI is switched diff --git a/components/esp_hw_support/mspi_timing_tuning/mspi_timing_tuning.c b/components/esp_hw_support/mspi_timing_tuning/mspi_timing_tuning.c index e5451d52165..9a32c922f8a 100644 --- a/components/esp_hw_support/mspi_timing_tuning/mspi_timing_tuning.c +++ b/components/esp_hw_support/mspi_timing_tuning/mspi_timing_tuning.c @@ -21,6 +21,7 @@ #include "hal/cache_ll.h" #include "hal/cache_hal.h" #endif +#include "esp_private/cache_utils.h" #include "esp_private/mspi_timing_tuning.h" #include "esp_private/mspi_timing_config.h" #include "esp_private/mspi_timing_by_mspi_delay.h" @@ -352,10 +353,10 @@ void mspi_timing_flash_tuning(void) { /** * set MSPI related regs to 20mhz configuration, to get reference data from FLASH - * see detailed comments in this function (`mspi_timing_enter_low_speed_mode`) + * see detailed comments in this function (`mspi_timing_enter_low_speed_early`) */ ESP_EARLY_LOGI(TAG, "Enter flash timing tuning"); - mspi_timing_enter_low_speed_mode(true); + mspi_timing_enter_low_speed_early(); #if SOC_MEMSPI_TIMING_TUNING_BY_MSPI_DELAY || SOC_MEMSPI_TIMING_TUNING_BY_FLASH_DELAY mspi_tuning_cfg_drv_t drv = { @@ -383,7 +384,7 @@ void mspi_timing_flash_tuning(void) s_do_tuning(reference_data, &timing_configs, true); - mspi_timing_enter_high_speed_mode(true); + mspi_timing_enter_high_speed_early(); } #else void mspi_timing_flash_tuning(void) @@ -401,10 +402,10 @@ void mspi_timing_psram_tuning(void) { /** * set MSPI related regs to 20mhz configuration, to write reference data to PSRAM - * see detailed comments in this function (`mspi_timing_enter_low_speed_mode`) + * see detailed comments in this function (`mspi_timing_enter_low_speed_early`) */ ESP_EARLY_LOGI(TAG, "Enter psram timing tuning"); - mspi_timing_enter_low_speed_mode(true); + mspi_timing_enter_low_speed_early(); // write data into psram, used to do timing tuning test. uint8_t reference_data[MSPI_TIMING_TEST_DATA_LEN]; @@ -467,7 +468,7 @@ void mspi_timing_psram_tuning(void) s_do_tuning(reference_data, &timing_configs, false); #endif - mspi_timing_enter_high_speed_mode(true); + mspi_timing_enter_high_speed_early(); } #else @@ -606,6 +607,53 @@ void mspi_timing_change_speed_mode_cache_safe(bool switch_down) #endif } + +/*------------------------------------------------------------------------------ + * Early-init MSPI speed switch (see mspi_timing_tuning.h) + *----------------------------------------------------------------------------*/ +#if ESP_TEE_BUILD +#include "riscv/rv_utils.h" +extern void rom_spi_flash_disable_cache(uint32_t cpuid, uint32_t *saved_state); +extern void rom_spi_flash_restore_cache(uint32_t cpuid, uint32_t saved_state); + +static void disable_cache(uint32_t cpuid, uint32_t *saved_state) +{ +#if SOC_BRANCH_PREDICTOR_SUPPORTED + rv_utils_dis_branch_predictor(); +#endif + rom_spi_flash_disable_cache(cpuid, saved_state); +} + +static void restore_cache(uint32_t cpuid, uint32_t saved_state) +{ + rom_spi_flash_restore_cache(cpuid, saved_state); +#if SOC_BRANCH_PREDICTOR_SUPPORTED + rv_utils_en_branch_predictor(); +#endif +} +#else // ESP_TEE_BUILD + +#define disable_cache(cpuid, saved_state) spi_flash_disable_cache(cpuid, saved_state) +#define restore_cache(cpuid, saved_state) spi_flash_restore_cache(cpuid, saved_state) + +#endif + +void mspi_timing_enter_low_speed_early(void) +{ + uint32_t cache_state = 0; + disable_cache(0, &cache_state); + mspi_timing_enter_low_speed_mode(true); + restore_cache(0, cache_state); +} + +void mspi_timing_enter_high_speed_early(void) +{ + uint32_t cache_state = 0; + disable_cache(0, &cache_state); + mspi_timing_enter_high_speed_mode(true); + restore_cache(0, cache_state); +} + /*------------------------------------------------------------------------------ * APIs to inform SPI1 Flash driver of necessary timing configurations *----------------------------------------------------------------------------*/ 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 962261879e2..658920ba3d4 100644 --- a/components/esp_psram/device/esp_psram_impl_ap_quad.c +++ b/components/esp_psram/device/esp_psram_impl_ap_quad.c @@ -345,8 +345,8 @@ esp_err_t esp_psram_impl_enable(void) #endif #if SOC_SPI_MEM_SUPPORT_TIMING_TUNING - //enter MSPI slow mode to init PSRAM device registers - mspi_timing_enter_low_speed_mode(true); + //enter MSPI slow mode to init PSRAM device registers (early init: see mspi_timing_enter_low_speed_early) + mspi_timing_enter_low_speed_early(); #else s_config_psram_clock(true); #endif // SOC_SPI_MEM_SUPPORT_TIMING_TUNING @@ -413,7 +413,7 @@ esp_err_t esp_psram_impl_enable(void) //Configure SPI0 PSRAM related SPI Phases config_psram_spi_phases(); //Back to the high speed mode. Flash/PSRAM clocks are set to the clock that user selected. SPI0/1 registers are all set correctly - mspi_timing_enter_high_speed_mode(true); + mspi_timing_enter_high_speed_early(); #else s_config_psram_clock(false); //Configure SPI0 PSRAM related SPI Phases diff --git a/components/esp_psram/esp32s3/esp_psram_impl_octal.c b/components/esp_psram/esp32s3/esp_psram_impl_octal.c index a5e1aaf999c..b5580fba09a 100644 --- a/components/esp_psram/esp32s3/esp_psram_impl_octal.c +++ b/components/esp_psram/esp32s3/esp_psram_impl_octal.c @@ -342,8 +342,8 @@ esp_err_t esp_psram_impl_enable(void) s_set_psram_cs_timing(); s_configure_psram_ecc(); - //enter MSPI slow mode to init PSRAM device registers - mspi_timing_enter_low_speed_mode(true); + //enter MSPI slow mode to init PSRAM device registers (early init: see mspi_timing_enter_low_speed_early) + mspi_timing_enter_low_speed_early(); //set to variable dummy mode SET_PERI_REG_MASK(SPI_MEM_DDR_REG(1), SPI_MEM_SPI_FMEM_VAR_DUMMY); @@ -378,7 +378,7 @@ esp_err_t esp_psram_impl_enable(void) //Do PSRAM timing tuning, we use SPI1 to do the tuning, and set the SPI0 PSRAM timing related registers accordingly mspi_timing_psram_tuning(); //Back to the high speed mode. Flash/PSRAM clocks are set to the clock that user selected. SPI0/1 registers are all set correctly - mspi_timing_enter_high_speed_mode(true); + mspi_timing_enter_high_speed_early(); /** * Tuning may change SPI1 regs, whereas legacy spi_flash APIs rely on these regs. diff --git a/components/esp_tee/scripts/esp32c5/sec_srv_tbl_default.yml b/components/esp_tee/scripts/esp32c5/sec_srv_tbl_default.yml index 79b062cc903..74ba4b91e10 100644 --- a/components/esp_tee/scripts/esp32c5/sec_srv_tbl_default.yml +++ b/components/esp_tee/scripts/esp32c5/sec_srv_tbl_default.yml @@ -120,6 +120,14 @@ secure_services: type: IDF function: spi_timing_get_flash_timing_param args: 1 + - id: 29 + type: IDF + function: mspi_timing_enter_low_speed_early + args: 0 + - id: 10 + type: IDF + function: mspi_timing_enter_high_speed_early + args: 0 # ID: 30-53 (24) - Interrupt Handling - family: interrupt_handling entries: diff --git a/components/esp_tee/src/esp_secure_service_wrapper.c b/components/esp_tee/src/esp_secure_service_wrapper.c index 655ba201f7c..04f1cc02cf0 100644 --- a/components/esp_tee/src/esp_secure_service_wrapper.c +++ b/components/esp_tee/src/esp_secure_service_wrapper.c @@ -491,6 +491,16 @@ void IRAM_ATTR __wrap_mspi_timing_enter_high_speed_mode(bool control_spi1) esp_tee_service_call(2, SS_MSPI_TIMING_ENTER_HIGH_SPEED_MODE, control_spi1); } +void IRAM_ATTR __wrap_mspi_timing_enter_low_speed_early(void) +{ + esp_tee_service_call(1, SS_MSPI_TIMING_ENTER_LOW_SPEED_EARLY); +} + +void IRAM_ATTR __wrap_mspi_timing_enter_high_speed_early(void) +{ + esp_tee_service_call(1, SS_MSPI_TIMING_ENTER_HIGH_SPEED_EARLY); +} + void IRAM_ATTR __wrap_mspi_timing_change_speed_mode_cache_safe(bool switch_down) { esp_tee_service_call(2, SS_MSPI_TIMING_CHANGE_SPEED_MODE_CACHE_SAFE, switch_down); diff --git a/components/esp_tee/subproject/main/core/esp_secure_services_iram.c b/components/esp_tee/subproject/main/core/esp_secure_services_iram.c index 57c209cbc1c..c5b5092bc2c 100644 --- a/components/esp_tee/subproject/main/core/esp_secure_services_iram.c +++ b/components/esp_tee/subproject/main/core/esp_secure_services_iram.c @@ -552,6 +552,16 @@ void _ss_mspi_timing_enter_high_speed_mode(bool control_spi1) mspi_timing_enter_high_speed_mode(control_spi1); } +void _ss_mspi_timing_enter_low_speed_early(void) +{ + mspi_timing_enter_low_speed_early(); +} + +void _ss_mspi_timing_enter_high_speed_early(void) +{ + mspi_timing_enter_high_speed_early(); +} + void _ss_mspi_timing_change_speed_mode_cache_safe(bool switch_down) { mspi_timing_change_speed_mode_cache_safe(switch_down); diff --git a/components/esp_tee/subproject/main/ld/esp32c5/esp_tee.ld.in b/components/esp_tee/subproject/main/ld/esp32c5/esp_tee.ld.in index 33d63a7ffd0..becd7e7a5b4 100644 --- a/components/esp_tee/subproject/main/ld/esp32c5/esp_tee.ld.in +++ b/components/esp_tee/subproject/main/ld/esp32c5/esp_tee.ld.in @@ -36,6 +36,8 @@ PROVIDE ( esp_tee_app_config = SRAM_REE_SEG_START + 0x2b0 ); PROVIDE ( GDMA = 0x60080000 ); /* SPI Flash functions required from the ROM (refer esp32c5.rom.spiflash.ld) */ +PROVIDE ( rom_spi_flash_disable_cache = 0x40000204 ); +PROVIDE ( rom_spi_flash_restore_cache = 0x40000208 ); PROVIDE ( spi_flash_check_and_flush_cache = 0x40000230 ); PROVIDE ( spi_flash_chip_generic_config_host_io_mode = 0x400002d4 ); PROVIDE ( memspi_host_flush_cache = 0x40000318 );