mirror of
https://github.com/espressif/esp-idf.git
synced 2026-04-27 19:13:21 +00:00
feat(spi_flash): implement dynamic CPU frequency switching workaround for encrypted writes
This commit implements a workaround that allows ESP32-C5 to run at 240MHz CPU frequency normally, while automatically reducing CPU frequency during encrypted flash writes to ensure correct operation. The frequency limit is chip revision dependent: - v1.2 and above: limited to 160MHz during encrypted writes - v1.0 and below: limited to 80MHz during encrypted writes Key implementation details: - Frequency limiting is triggered automatically when esp_flash_write_encrypted() is called - Uses start() flags (ESP_FLASH_START_FLAG_LIMIT_CPU_FREQ) to integrate with OS layer - Works with both PM enabled and disabled configurations - Frequency is automatically restored after encrypted write completes - For ESP32-C5 with 120MHz flash, Flash clock and timing registers are adjusted when CPU frequency is reduced to 80MHz - SPI1 timing registers are configured during frequency switching since encrypted writes use SPI1 and must work correctly at reduced CPU frequencies Code improvements: - Use SOC_MSPI_FREQ_AXI_CONSTRAINED capability macro instead of hardcoded chip checks - Control workaround via Kconfig (CONFIG_PM_WORKAROUND_FREQ_LIMIT_ENABLED) instead of hardcoded macros - Add comprehensive test cases covering various PM configurations and edge cases This workaround enables ESP32-C5 applications to benefit from 240MHz CPU performance while maintaining reliable encrypted flash write functionality.
This commit is contained in:
@@ -7,6 +7,7 @@
|
||||
#include <sys/param.h>
|
||||
#include <inttypes.h>
|
||||
#include <string.h>
|
||||
#include <assert.h>
|
||||
#include "sdkconfig.h"
|
||||
#include "esp_check.h"
|
||||
#include "esp_log.h"
|
||||
@@ -17,25 +18,31 @@
|
||||
#include "esp_private/mspi_timing_tuning.h"
|
||||
#include "esp_private/esp_clk_utils.h"
|
||||
|
||||
// Not directly divide to avoid truncation issue
|
||||
// DIG-498
|
||||
#if CONFIG_IDF_TARGET_ESP32P4
|
||||
#define BELOW_FREQ_THRESHOLD(freq) ((freq) < CONFIG_SPIRAM_SPEED)
|
||||
#elif CONFIG_IDF_TARGET_ESP32C5 || CONFIG_IDF_TARGET_ESP32C61 || CONFIG_IDF_TARGET_ESP32H4
|
||||
#define BELOW_FREQ_THRESHOLD(freq) ((freq) * 8 < CONFIG_SPIRAM_SPEED)
|
||||
#endif
|
||||
|
||||
#if !CONFIG_APP_BUILD_TYPE_PURE_RAM_APP
|
||||
void esp_clk_utils_mspi_speed_mode_sync_before_cpu_freq_switching(uint32_t target_cpu_src_freq, uint32_t target_cpu_freq)
|
||||
{
|
||||
#if MSPI_TIMING_LL_FLASH_CPU_CLK_SRC_BINDED
|
||||
(void) target_cpu_freq;
|
||||
/* For ESP32S3, the clock source of MSPI is same as the CPU. When CPU use XTAL as clock source, we need to sync the
|
||||
* MSPI speed mode. */
|
||||
if (target_cpu_src_freq <= clk_ll_xtal_load_freq_mhz()) {
|
||||
mspi_timing_change_speed_mode_cache_safe(true);
|
||||
}
|
||||
#elif CONFIG_IDF_TARGET_ESP32P4
|
||||
(void) target_cpu_src_freq;
|
||||
/**
|
||||
* Workaround for ESP32P4,
|
||||
* f_cpu >= f_mspi
|
||||
#elif SOC_SPI_MEM_PSRAM_FREQ_AXI_CONSTRAINED && CONFIG_SPIRAM
|
||||
/* On chips with AXI bus, currently there is a restriction that AXI frequency (usually equals to a portion of CPU
|
||||
* frequency) needs to be greater than or equal to MSPI PSRAM frequency to avoid writing MSPI FIFO overflow.
|
||||
*/
|
||||
if (((target_cpu_freq) < CONFIG_ESPTOOLPY_FLASHFREQ_VAL)
|
||||
#if CONFIG_SPIRAM
|
||||
|| ((target_cpu_freq) < CONFIG_SPIRAM_SPEED)
|
||||
#endif
|
||||
) {
|
||||
if (BELOW_FREQ_THRESHOLD(target_cpu_freq)) {
|
||||
// Before switching to low speed mode, verify CPU frequency meets the constraint
|
||||
assert(target_cpu_freq >= mspi_timing_get_psram_low_speed_freq_mhz());
|
||||
mspi_timing_change_speed_mode_cache_safe(true);
|
||||
}
|
||||
#else
|
||||
@@ -51,17 +58,11 @@ void esp_clk_utils_mspi_speed_mode_sync_after_cpu_freq_switching(uint32_t target
|
||||
if (target_cpu_src_freq > clk_ll_xtal_load_freq_mhz()) {
|
||||
mspi_timing_change_speed_mode_cache_safe(false);
|
||||
}
|
||||
#elif CONFIG_IDF_TARGET_ESP32P4
|
||||
(void) target_cpu_src_freq;
|
||||
/**
|
||||
* Workaround for ESP32P4,
|
||||
* f_cpu >= f_mspi
|
||||
#elif SOC_SPI_MEM_PSRAM_FREQ_AXI_CONSTRAINED && CONFIG_SPIRAM
|
||||
/* On chips with AXI bus, currently there is a restriction that AXI frequency (usually equals to a portion of CPU
|
||||
* frequency) needs to be greater than or equal to MSPI PSRAM frequency to avoid writing MSPI FIFO overflow.
|
||||
*/
|
||||
if (((target_cpu_freq) >= CONFIG_ESPTOOLPY_FLASHFREQ_VAL)
|
||||
#if CONFIG_SPIRAM
|
||||
&& ((target_cpu_freq) >= CONFIG_SPIRAM_SPEED)
|
||||
#endif
|
||||
) {
|
||||
if (!BELOW_FREQ_THRESHOLD(target_cpu_freq)) {
|
||||
mspi_timing_change_speed_mode_cache_safe(false);
|
||||
}
|
||||
#else
|
||||
|
||||
+6
-1
@@ -24,6 +24,12 @@ extern "C" {
|
||||
*/
|
||||
void mspi_timing_enter_low_speed_mode(bool control_spi1);
|
||||
|
||||
/**
|
||||
* @brief Get PSRAM frequency in low speed mode (MHz)
|
||||
* @return PSRAM frequency in MHz when in low speed mode
|
||||
*/
|
||||
uint32_t mspi_timing_get_psram_low_speed_freq_mhz(void);
|
||||
|
||||
/**
|
||||
* @brief Make MSPI work under the frequency as users set, may add certain delays to MSPI RX direction to meet timing requirements.
|
||||
* @param control_spi1 Select whether to control SPI1. For tuning, we need to use SPI1. After tuning (during startup stage), let the flash driver to control SPI1
|
||||
@@ -54,7 +60,6 @@ void mspi_timing_psram_tuning(void);
|
||||
*/
|
||||
void mspi_timing_set_pin_drive_strength(void);
|
||||
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
@@ -481,6 +481,11 @@ void mspi_timing_psram_tuning(void)
|
||||
/*------------------------------------------------------------------------------
|
||||
* APIs to make SPI0 (and SPI1) FLASH work for high/low freq
|
||||
*----------------------------------------------------------------------------*/
|
||||
uint32_t mspi_timing_get_psram_low_speed_freq_mhz(void)
|
||||
{
|
||||
return 20;
|
||||
}
|
||||
|
||||
void mspi_timing_enter_low_speed_mode(bool control_spi1)
|
||||
{
|
||||
#if MSPI_TIMING_LL_FLASH_CLK_SRC_CHANGEABLE
|
||||
@@ -500,14 +505,14 @@ void mspi_timing_enter_low_speed_mode(bool control_spi1)
|
||||
* Should be extended to other no-timing-tuning chips if needed. e.g.:
|
||||
* we still need to turn down Flash / PSRAM clock speed at a certain period of time
|
||||
*/
|
||||
mspi_timing_config_set_flash_clock(20, MSPI_TIMING_SPEED_MODE_LOW_PERF, control_spi1);
|
||||
mspi_timing_config_set_psram_clock(20, MSPI_TIMING_SPEED_MODE_LOW_PERF, control_spi1);
|
||||
#endif //#if SOC_SPI_MEM_SUPPORT_TIMING_TUNING
|
||||
|
||||
uint32_t low_speed_freq_mhz = mspi_timing_get_psram_low_speed_freq_mhz();
|
||||
mspi_timing_config_set_flash_clock(low_speed_freq_mhz, MSPI_TIMING_SPEED_MODE_LOW_PERF, control_spi1);
|
||||
mspi_timing_config_set_psram_clock(low_speed_freq_mhz, MSPI_TIMING_SPEED_MODE_LOW_PERF, control_spi1);
|
||||
#if MSPI_TIMING_FLASH_NEEDS_TUNING || MSPI_TIMING_PSRAM_NEEDS_TUNING
|
||||
mspi_timing_flash_config_clear_tuning_regs(control_spi1);
|
||||
mspi_timing_psram_config_clear_tuning_regs(control_spi1);
|
||||
#endif //#if MSPI_TIMING_FLASH_NEEDS_TUNING || MSPI_TIMING_PSRAM_NEEDS_TUNING
|
||||
#endif //#if SOC_SPI_MEM_SUPPORT_TIMING_TUNING
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -568,10 +573,24 @@ void mspi_timing_change_speed_mode_cache_safe(bool switch_down)
|
||||
|
||||
if (switch_down) {
|
||||
//enter MSPI low speed mode, extra delays should be removed
|
||||
#if CONFIG_IDF_TARGET_ESP32C5
|
||||
// ESP32-C5 needs to perform encrypted flash writes even when CPU frequency is reduced.
|
||||
// Since encrypted writes use SPI1, we need to configure SPI1 timing registers as well
|
||||
// during runtime frequency switching to ensure proper operation.
|
||||
mspi_timing_enter_low_speed_mode(true);
|
||||
#else
|
||||
mspi_timing_enter_low_speed_mode(false);
|
||||
#endif
|
||||
} else {
|
||||
//enter MSPI high speed mode, extra delays should be considered
|
||||
#if CONFIG_IDF_TARGET_ESP32C5
|
||||
// ESP32-C5 needs to perform encrypted flash writes even when CPU frequency is reduced.
|
||||
// Since encrypted writes use SPI1, we need to configure SPI1 timing registers as well
|
||||
// during runtime frequency switching to ensure proper operation.
|
||||
mspi_timing_enter_high_speed_mode(true);
|
||||
#else
|
||||
mspi_timing_enter_high_speed_mode(false);
|
||||
#endif
|
||||
}
|
||||
|
||||
#if SOC_CACHE_FREEZE_SUPPORTED
|
||||
|
||||
@@ -1,10 +1,21 @@
|
||||
idf_build_get_property(target IDF_TARGET)
|
||||
set(priv_requires esp_system esp_driver_gpio esp_timer)
|
||||
|
||||
if(${target} STREQUAL "linux")
|
||||
return() # This component is not supported by the POSIX/Linux simulator
|
||||
endif()
|
||||
|
||||
idf_component_register(SRCS "pm_locks.c" "pm_trace.c" "pm_impl.c"
|
||||
set(srcs "pm_locks.c" "pm_trace.c" "pm_impl.c")
|
||||
if(CONFIG_PM_WORKAROUND_FREQ_LIMIT_ENABLED)
|
||||
list(APPEND srcs "pm_c5_flash_freq_limit.c")
|
||||
endif()
|
||||
|
||||
if(${target} STREQUAL "esp32c5")
|
||||
# pm_c5_flash_freq_limit.c needs spi_flash header when CONFIG_PM_WORKAROUND_FREQ_LIMIT_ENABLED is enabled
|
||||
list(APPEND priv_requires spi_flash)
|
||||
endif()
|
||||
|
||||
idf_component_register(SRCS ${srcs}
|
||||
INCLUDE_DIRS include
|
||||
PRIV_REQUIRES esp_system esp_driver_gpio esp_timer
|
||||
PRIV_REQUIRES "${priv_requires}"
|
||||
LDFRAGMENTS linker.lf)
|
||||
|
||||
@@ -239,4 +239,12 @@ menu "Power Management"
|
||||
NOTE: Enabling these callbacks may change sleep duration calculations based on time spent in callback and
|
||||
hence it is highly recommended to keep them as short as possible
|
||||
|
||||
config PM_WORKAROUND_FREQ_LIMIT_ENABLED
|
||||
bool
|
||||
default y if SPI_FLASH_FREQ_LIMIT_C5_240MHZ
|
||||
help
|
||||
Workaround for frequency limit during encrypted flash writes.
|
||||
Only enabled when SPI_FLASH_FREQ_LIMIT_C5_240MHZ is enabled.
|
||||
This is an internal configuration, automatically set based on SPI Flash configuration.
|
||||
|
||||
endmenu # "Power Management"
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* SPDX-FileCopyrightText: 2016-2023 Espressif Systems (Shanghai) CO LTD
|
||||
* SPDX-FileCopyrightText: 2016-2025 Espressif Systems (Shanghai) CO LTD
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
@@ -15,7 +15,6 @@
|
||||
|
||||
#include "soc/rtc.h"
|
||||
#include "esp_pm.h"
|
||||
#include "esp_timer.h"
|
||||
#include "sdkconfig.h"
|
||||
|
||||
#ifdef __cplusplus
|
||||
@@ -26,11 +25,11 @@ extern "C" {
|
||||
* This is an enum of possible power modes supported by the implementation
|
||||
*/
|
||||
typedef enum {
|
||||
PM_MODE_LIGHT_SLEEP,//!< Light sleep
|
||||
PM_MODE_APB_MIN, //!< Idle (no CPU frequency or APB frequency locks)
|
||||
PM_MODE_APB_MAX, //!< Maximum APB frequency mode
|
||||
PM_MODE_CPU_MAX, //!< Maximum CPU frequency mode
|
||||
PM_MODE_COUNT //!< Number of items
|
||||
PM_MODE_LIGHT_SLEEP, //!< Light sleep
|
||||
PM_MODE_APB_MIN, //!< Idle (no CPU frequency or APB frequency locks)
|
||||
PM_MODE_APB_MAX, //!< Maximum APB frequency mode
|
||||
PM_MODE_CPU_MAX, //!< Maximum CPU frequency mode
|
||||
PM_MODE_COUNT //!< Number of items
|
||||
} pm_mode_t;
|
||||
|
||||
/**
|
||||
@@ -141,8 +140,18 @@ esp_err_t esp_pm_register_skip_light_sleep_callback(skip_light_sleep_cb_t cb);
|
||||
*/
|
||||
esp_err_t esp_pm_unregister_skip_light_sleep_callback(skip_light_sleep_cb_t cb);
|
||||
|
||||
|
||||
/**
|
||||
* @brief Initialize flash frequency limit
|
||||
*
|
||||
* This function initializes the flash frequency limit.
|
||||
* @note This function is only available when CONFIG_PM_WORKAROUND_FREQ_LIMIT_ENABLED is enabled.
|
||||
*/
|
||||
void esp_pm_flash_freq_limit_init(void);
|
||||
|
||||
#ifdef CONFIG_PM_PROFILING
|
||||
#define WITH_PROFILING
|
||||
#include "esp_timer.h"
|
||||
#endif
|
||||
|
||||
#ifdef WITH_PROFILING
|
||||
|
||||
@@ -0,0 +1,59 @@
|
||||
/*
|
||||
* SPDX-FileCopyrightText: 2016-2025 Espressif Systems (Shanghai) CO LTD
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <stdint.h>
|
||||
#include "sdkconfig.h"
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
#if CONFIG_PM_WORKAROUND_FREQ_LIMIT_ENABLED
|
||||
|
||||
/**
|
||||
* @brief Initialize and pre-calculate forced CPU_MAX frequency configuration (private function for spi_flash)
|
||||
*
|
||||
* This function pre-calculates and stores the forced CPU_MAX frequency configuration
|
||||
* based on the given frequency limit. The configuration is computed once during
|
||||
* initialization and reused during runtime for better performance.
|
||||
*
|
||||
* @param limit_freq_mhz Frequency limit in MHz
|
||||
* @note This is a private function, only for use by spi_flash component.
|
||||
* @note Must be called during initialization before esp_pm_impl_cpu_max_freq_force().
|
||||
* @note This function is only available when CONFIG_PM_WORKAROUND_FREQ_LIMIT_ENABLED is enabled and CONFIG_PM_ENABLE is enabled.
|
||||
*/
|
||||
void esp_pm_impl_cpu_max_freq_force_init(uint32_t limit_freq_mhz);
|
||||
|
||||
/**
|
||||
* @brief Force CPU_MAX frequency to pre-configured limit (private function for spi_flash)
|
||||
*
|
||||
* This function activates the pre-configured forced CPU_MAX frequency limit.
|
||||
* When forced, all reads of CPU_MAX frequency will use the pre-configured value
|
||||
* instead of the configured value.
|
||||
*
|
||||
* @note This is a private function, only for use by spi_flash component.
|
||||
* @note The forced frequency configuration must be pre-calculated during initialization.
|
||||
* @note This function is only available when CONFIG_PM_WORKAROUND_FREQ_LIMIT_ENABLED is enabled and CONFIG_PM_ENABLE is enabled.
|
||||
*/
|
||||
void esp_pm_impl_cpu_max_freq_force(void);
|
||||
|
||||
/**
|
||||
* @brief Unforce CPU_MAX frequency (private function for spi_flash)
|
||||
*
|
||||
* This function removes the forced CPU_MAX frequency, allowing the configured
|
||||
* value to be used again.
|
||||
* @note This is a private function, only for use by spi_flash component.
|
||||
* @note This function is only available when CONFIG_PM_WORKAROUND_FREQ_LIMIT_ENABLED is enabled and CONFIG_PM_ENABLE is enabled.
|
||||
*/
|
||||
void esp_pm_impl_cpu_max_freq_unforce(void);
|
||||
|
||||
#endif // CONFIG_PM_WORKAROUND_FREQ_LIMIT_ENABLED
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
@@ -0,0 +1,168 @@
|
||||
/*
|
||||
* SPDX-FileCopyrightText: 2025 Espressif Systems (Shanghai) CO LTD
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
//This file implements esp_flash_freq_limit_cb and esp_flash_freq_unlimit_cb callbacks to limit and unlimit CPU
|
||||
//frequency when encrypted flash writes are performed.
|
||||
|
||||
#include "sdkconfig.h"
|
||||
|
||||
#include "esp_log.h"
|
||||
#include "esp_attr.h"
|
||||
#include "esp_rom_sys.h"
|
||||
#include "soc/chip_revision.h"
|
||||
#include "soc/rtc.h"
|
||||
#include "hal/efuse_hal.h"
|
||||
|
||||
#include "esp_private/pm_impl_freq_limit.h"
|
||||
#include "esp_private/spi_flash_freq_limit_cbs.h"
|
||||
#include "esp_private/esp_clk_utils.h"
|
||||
|
||||
/**
|
||||
* @brief Get the frequency limit for flash encryption lock based on chip revision
|
||||
*
|
||||
* @return uint32_t Frequency limit (MHz)
|
||||
*
|
||||
* Logic:
|
||||
* - v1.2+: limit to 160MHz
|
||||
* - v1.0: limit to 80MHz
|
||||
*/
|
||||
static uint32_t IRAM_ATTR get_encrypt_lock_freq_limit(void)
|
||||
{
|
||||
unsigned int chip_revision = efuse_hal_chip_revision();
|
||||
if (ESP_CHIP_REV_ABOVE(chip_revision, 102)) {
|
||||
return 160;
|
||||
} else {
|
||||
return 80;
|
||||
}
|
||||
}
|
||||
|
||||
void esp_pm_flash_freq_limit_init(void)
|
||||
{
|
||||
uint32_t limit_freq_mhz = get_encrypt_lock_freq_limit();
|
||||
ESP_EARLY_LOGW("spi_flash", "CPU frequency is set to 240MHz. esp_flash_write_encrypted() will automatically limit CPU frequency to %dMHz during execution.", limit_freq_mhz);
|
||||
#ifdef CONFIG_PM_ENABLE
|
||||
/* Pre-calculate and store forced frequency configuration during initialization.
|
||||
* This is done here to avoid runtime calculation overhead in lock/unlock functions.
|
||||
*/
|
||||
esp_pm_impl_cpu_max_freq_force_init(limit_freq_mhz);
|
||||
#endif
|
||||
}
|
||||
|
||||
#if !CONFIG_PM_ENABLE
|
||||
/* Saved original frequency for !PM_ENABLE case (0 means no change was made) */
|
||||
static uint32_t s_saved_freq_mhz = 0;
|
||||
|
||||
// Switch CPU frequency with all necessary synchronization (for !PM_ENABLE case)
|
||||
// Similar to do_switch() but without PM-specific state management
|
||||
static void IRAM_ATTR esp_pm_impl_switch_cpu_freq(const rtc_cpu_freq_config_t *old_config, const rtc_cpu_freq_config_t *new_config)
|
||||
{
|
||||
if (new_config->freq_mhz != old_config->freq_mhz) {
|
||||
//No need to run on_freq_update for ccount, since that's not supported on C5.
|
||||
#if !CONFIG_APP_BUILD_TYPE_PURE_RAM_APP
|
||||
/* Synchronize MSPI speed mode before CPU frequency switching */
|
||||
esp_clk_utils_mspi_speed_mode_sync_before_cpu_freq_switching(new_config->source_freq_mhz, new_config->freq_mhz);
|
||||
#endif
|
||||
|
||||
//Not taking s_time_update_lock since C5 have fixed divider for systimer
|
||||
rtc_clk_cpu_freq_set_config_fast(new_config);
|
||||
|
||||
#if !CONFIG_APP_BUILD_TYPE_PURE_RAM_APP
|
||||
/* Synchronize MSPI speed mode after CPU frequency switching */
|
||||
esp_clk_utils_mspi_speed_mode_sync_after_cpu_freq_switching(new_config->source_freq_mhz, new_config->freq_mhz);
|
||||
#endif
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Limit CPU frequency to target frequency (for !PM_ENABLE case only)
|
||||
*
|
||||
* This function automatically reads the current CPU frequency, saves it,
|
||||
* and switches to the target frequency if current frequency is higher.
|
||||
* The original frequency can be restored by calling unlimit_cpu_freq().
|
||||
*
|
||||
* @param target_freq_mhz Target frequency limit in MHz
|
||||
* @note limit->unlimit cannot be nested, and this function must not be called concurrently
|
||||
* @note This is a private function, only for use by spi_flash component.
|
||||
* @note This function is only available when CONFIG_PM_WORKAROUND_FREQ_LIMIT_ENABLED is enabled and CONFIG_PM_ENABLE is not set.
|
||||
*/
|
||||
static void IRAM_ATTR limit_cpu_freq(uint32_t target_freq_mhz)
|
||||
{
|
||||
/* PM not enabled, directly switch frequency and save original frequency */
|
||||
rtc_cpu_freq_config_t old_config, new_config;
|
||||
rtc_clk_cpu_freq_get_config(&old_config);
|
||||
|
||||
if (old_config.freq_mhz > target_freq_mhz) {
|
||||
s_saved_freq_mhz = old_config.freq_mhz;
|
||||
if (rtc_clk_cpu_freq_mhz_to_config(target_freq_mhz, &new_config)) {
|
||||
/* Use PM implementation function to switch frequency with all necessary synchronization */
|
||||
esp_pm_impl_switch_cpu_freq(&old_config, &new_config);
|
||||
const unsigned wait_clock_stable_us = 10;
|
||||
esp_rom_delay_us(wait_clock_stable_us);
|
||||
}
|
||||
} else {
|
||||
s_saved_freq_mhz = 0; /* No change needed */
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Unlimit CPU frequency, restoring to original frequency (for !PM_ENABLE case only)
|
||||
*
|
||||
* This function restores the CPU frequency to the value saved by
|
||||
* limit_cpu_freq(). If no frequency change was made, this function
|
||||
* does nothing.
|
||||
*
|
||||
* @note limit->unlimit cannot be nested, and this function must not be called concurrently
|
||||
* @note This is a private function, only for use by spi_flash component.
|
||||
* @note This function is only available when CONFIG_PM_WORKAROUND_FREQ_LIMIT_ENABLED is enabled and CONFIG_PM_ENABLE is not set.
|
||||
*/
|
||||
static void IRAM_ATTR unlimit_cpu_freq(void)
|
||||
{
|
||||
/* PM not enabled, restore to original frequency */
|
||||
if (s_saved_freq_mhz > 0) {
|
||||
rtc_cpu_freq_config_t old_config, new_config;
|
||||
rtc_clk_cpu_freq_get_config(&old_config);
|
||||
if (rtc_clk_cpu_freq_mhz_to_config(s_saved_freq_mhz, &new_config)) {
|
||||
/* Use PM implementation function to switch frequency with all necessary synchronization */
|
||||
esp_pm_impl_switch_cpu_freq(&old_config, &new_config);
|
||||
const unsigned wait_clock_stable_us = 10;
|
||||
esp_rom_delay_us(wait_clock_stable_us);
|
||||
}
|
||||
}
|
||||
/* If s_saved_freq_mhz == 0, no change was made, so no need to restore */
|
||||
s_saved_freq_mhz = 0;
|
||||
}
|
||||
#endif // !CONFIG_PM_ENABLE
|
||||
|
||||
void IRAM_ATTR esp_flash_freq_limit_cb(void)
|
||||
{
|
||||
/* Limit the frequency */
|
||||
#ifdef CONFIG_PM_ENABLE
|
||||
/* PM enabled, use force mechanism */
|
||||
esp_pm_impl_cpu_max_freq_force();
|
||||
#else
|
||||
/* PM not enabled, use limit mechanism */
|
||||
uint32_t limit_freq_mhz = get_encrypt_lock_freq_limit();
|
||||
/* Call PM implementation function to limit CPU frequency.
|
||||
* This function automatically reads current frequency, saves it, and switches to target frequency.
|
||||
*/
|
||||
limit_cpu_freq(limit_freq_mhz);
|
||||
#endif
|
||||
}
|
||||
|
||||
void IRAM_ATTR esp_flash_freq_unlimit_cb(void)
|
||||
{
|
||||
/* Restore the frequency */
|
||||
#ifdef CONFIG_PM_ENABLE
|
||||
/* PM enabled, use unforce mechanism */
|
||||
esp_pm_impl_cpu_max_freq_unforce();
|
||||
#else
|
||||
/* PM not enabled, use unlimit mechanism */
|
||||
/* Call PM implementation function to unlimit CPU frequency.
|
||||
* This function restores the original frequency saved by limit_cpu_freq().
|
||||
*/
|
||||
unlimit_cpu_freq();
|
||||
#endif
|
||||
}
|
||||
@@ -10,6 +10,7 @@
|
||||
#include <stdint.h>
|
||||
#include <sys/lock.h>
|
||||
#include <sys/param.h>
|
||||
#include <assert.h>
|
||||
|
||||
#include "sdkconfig.h"
|
||||
#include "esp_attr.h"
|
||||
@@ -33,6 +34,7 @@
|
||||
|
||||
#include "freertos/FreeRTOS.h"
|
||||
#include "freertos/task.h"
|
||||
#include "freertos/portmacro.h"
|
||||
#if CONFIG_FREERTOS_SYSTICK_USES_CCOUNT
|
||||
#include "xtensa_timer.h"
|
||||
#include "xtensa/core-macros.h"
|
||||
@@ -41,6 +43,7 @@
|
||||
#include "esp_private/pm_impl.h"
|
||||
#include "esp_private/pm_trace.h"
|
||||
#include "esp_private/esp_timer_private.h"
|
||||
#include "esp_timer.h"
|
||||
#include "esp_private/esp_clk.h"
|
||||
#include "esp_private/esp_clk_tree_common.h"
|
||||
#include "esp_private/sleep_cpu.h"
|
||||
@@ -50,7 +53,7 @@
|
||||
#include "esp_private/esp_clk_utils.h"
|
||||
#include "esp_sleep.h"
|
||||
#include "esp_memory_utils.h"
|
||||
|
||||
#include "esp_rom_sys.h"
|
||||
|
||||
#if SOC_PERIPH_CLK_CTRL_SHARED
|
||||
#define HP_UART_SRC_CLK_ATOMIC() PERIPH_RCC_ATOMIC()
|
||||
@@ -167,6 +170,16 @@ static esp_pm_lock_handle_t s_rtos_lock_handle[CONFIG_FREERTOS_NUMBER_OF_CORES];
|
||||
*/
|
||||
static rtc_cpu_freq_config_t s_cpu_freq_by_mode[PM_MODE_COUNT];
|
||||
|
||||
#if CONFIG_PM_WORKAROUND_FREQ_LIMIT_ENABLED
|
||||
/* Forced CPU_MAX frequency configuration.
|
||||
* s_cpu_max_freq_forced indicates whether CPU_MAX frequency is forced.
|
||||
* s_cpu_max_freq_force_config stores the forced frequency configuration.
|
||||
* Protected by s_switch_lock, same as s_cpu_freq_by_mode.
|
||||
*/
|
||||
static bool s_cpu_max_freq_forced = false;
|
||||
static rtc_cpu_freq_config_t s_cpu_max_freq_force_config;
|
||||
#endif
|
||||
|
||||
/* Whether automatic light sleep is enabled */
|
||||
static bool s_light_sleep_en = false;
|
||||
|
||||
@@ -392,6 +405,32 @@ static esp_err_t esp_pm_sleep_configure(const esp_pm_config_t *config)
|
||||
return err;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Get frequency configuration for a given mode, considering forced frequency
|
||||
*
|
||||
* This function returns a pointer to the frequency configuration for the given mode.
|
||||
* For PM_MODE_CPU_MAX, if s_cpu_max_freq_forced is true, it returns
|
||||
* a pointer to s_cpu_max_freq_force_config. Otherwise, it returns a pointer to
|
||||
* s_cpu_freq_by_mode[mode].
|
||||
*
|
||||
* @param mode Power mode to get configuration for
|
||||
* @note Must be called with s_switch_lock held
|
||||
* @note Must be in IRAM when called from ISR context (e.g., do_switch)
|
||||
* @return rtc_cpu_freq_config_t* Pointer to frequency configuration for the given mode
|
||||
*/
|
||||
static inline IRAM_ATTR rtc_cpu_freq_config_t *get_cpu_freq_config_by_mode(pm_mode_t mode)
|
||||
{
|
||||
#if CONFIG_PM_WORKAROUND_FREQ_LIMIT_ENABLED
|
||||
if (mode == PM_MODE_CPU_MAX && s_cpu_max_freq_forced) {
|
||||
return &s_cpu_max_freq_force_config;
|
||||
} else {
|
||||
return &s_cpu_freq_by_mode[mode];
|
||||
}
|
||||
#else
|
||||
return &s_cpu_freq_by_mode[mode];
|
||||
#endif
|
||||
}
|
||||
|
||||
esp_err_t esp_pm_configure(const void* vconfig)
|
||||
{
|
||||
#ifndef CONFIG_PM_ENABLE
|
||||
@@ -641,18 +680,19 @@ static void IRAM_ATTR do_switch(pm_mode_t new_mode)
|
||||
s_is_switching = true;
|
||||
bool config_changed = s_config_changed;
|
||||
s_config_changed = false;
|
||||
portENTER_CRITICAL_ISR(&s_cpu_freq_switch_lock[core_id]);
|
||||
portEXIT_CRITICAL_ISR(&s_switch_lock);
|
||||
|
||||
rtc_cpu_freq_config_t new_config = s_cpu_freq_by_mode[new_mode];
|
||||
rtc_cpu_freq_config_t new_config = *get_cpu_freq_config_by_mode(new_mode);
|
||||
rtc_cpu_freq_config_t old_config;
|
||||
|
||||
if (!config_changed) {
|
||||
old_config = s_cpu_freq_by_mode[s_mode];
|
||||
old_config = *get_cpu_freq_config_by_mode(s_mode);
|
||||
} else {
|
||||
rtc_clk_cpu_freq_get_config(&old_config);
|
||||
}
|
||||
|
||||
portENTER_CRITICAL_ISR(&s_cpu_freq_switch_lock[core_id]);
|
||||
portEXIT_CRITICAL_ISR(&s_switch_lock);
|
||||
|
||||
if (new_config.freq_mhz != old_config.freq_mhz) {
|
||||
uint32_t old_ticks_per_us = old_config.freq_mhz;
|
||||
uint32_t new_ticks_per_us = new_config.freq_mhz;
|
||||
@@ -865,6 +905,7 @@ void vApplicationSleep( TickType_t xExpectedIdleTime )
|
||||
void esp_pm_impl_dump_stats(FILE* out)
|
||||
{
|
||||
pm_time_t time_in_mode[PM_MODE_COUNT];
|
||||
uint32_t freq_mhz_by_mode[PM_MODE_COUNT];
|
||||
|
||||
portENTER_CRITICAL_ISR(&s_switch_lock);
|
||||
memcpy(time_in_mode, s_time_in_mode, sizeof(time_in_mode));
|
||||
@@ -874,6 +915,10 @@ void esp_pm_impl_dump_stats(FILE* out)
|
||||
bool light_sleep_en = s_light_sleep_en;
|
||||
uint32_t light_sleep_counts = s_light_sleep_counts;
|
||||
uint32_t light_sleep_reject_counts = s_light_sleep_reject_counts;
|
||||
// Read all frequency configs while holding s_switch_lock
|
||||
for (int i = 0; i < PM_MODE_COUNT; ++i) {
|
||||
freq_mhz_by_mode[i] = get_cpu_freq_config_by_mode(i)->freq_mhz;
|
||||
}
|
||||
portEXIT_CRITICAL_ISR(&s_switch_lock);
|
||||
|
||||
time_in_mode[cur_mode] += now - last_mode_change_time;
|
||||
@@ -887,7 +932,7 @@ void esp_pm_impl_dump_stats(FILE* out)
|
||||
}
|
||||
fprintf(out, "%-8s %-3"PRIu32"M%-7s %-10lld %-2d%%\n",
|
||||
s_mode_names[i],
|
||||
s_cpu_freq_by_mode[i].freq_mhz,
|
||||
freq_mhz_by_mode[i],
|
||||
"", //Empty space to align columns
|
||||
time_in_mode[i],
|
||||
(int) (time_in_mode[i] * 100 / now));
|
||||
@@ -904,7 +949,7 @@ int esp_pm_impl_get_cpu_freq(pm_mode_t mode)
|
||||
int freq_mhz;
|
||||
if (mode >= PM_MODE_LIGHT_SLEEP && mode < PM_MODE_COUNT) {
|
||||
portENTER_CRITICAL(&s_switch_lock);
|
||||
freq_mhz = s_cpu_freq_by_mode[mode].freq_mhz;
|
||||
freq_mhz = get_cpu_freq_config_by_mode(mode)->freq_mhz;
|
||||
portEXIT_CRITICAL(&s_switch_lock);
|
||||
} else {
|
||||
abort();
|
||||
@@ -1050,3 +1095,42 @@ void esp_pm_impl_waiti(void)
|
||||
esp_cpu_wait_for_intr();
|
||||
#endif // CONFIG_FREERTOS_USE_TICKLESS_IDLE
|
||||
}
|
||||
|
||||
#if CONFIG_PM_WORKAROUND_FREQ_LIMIT_ENABLED && CONFIG_PM_ENABLE
|
||||
void esp_pm_impl_cpu_max_freq_force_init(uint32_t limit_freq_mhz)
|
||||
{
|
||||
// Pre-calculate frequency config (done outside critical section)
|
||||
rtc_cpu_freq_config_t force_config;
|
||||
bool res = rtc_clk_cpu_freq_mhz_to_config(limit_freq_mhz, &force_config);
|
||||
assert(res && "Failed to convert forced CPU_MAX frequency to config");
|
||||
|
||||
// Store the pre-calculated config in critical section
|
||||
portENTER_CRITICAL(&s_switch_lock);
|
||||
s_cpu_max_freq_force_config = force_config;
|
||||
portEXIT_CRITICAL(&s_switch_lock);
|
||||
}
|
||||
|
||||
void IRAM_ATTR esp_pm_impl_cpu_max_freq_force(void)
|
||||
{
|
||||
portENTER_CRITICAL_SAFE(&s_switch_lock);
|
||||
// Activate pre-configured forced frequency (no calculation needed at runtime)
|
||||
s_cpu_max_freq_forced = true;
|
||||
s_config_changed = true;
|
||||
portEXIT_CRITICAL_SAFE(&s_switch_lock);
|
||||
do_switch(PM_MODE_CPU_MAX);
|
||||
const unsigned wait_clock_stable_us = 10;
|
||||
esp_rom_delay_us(wait_clock_stable_us);
|
||||
}
|
||||
|
||||
void IRAM_ATTR esp_pm_impl_cpu_max_freq_unforce(void)
|
||||
{
|
||||
portENTER_CRITICAL_SAFE(&s_switch_lock);
|
||||
s_cpu_max_freq_forced = false;
|
||||
s_config_changed = true;
|
||||
portEXIT_CRITICAL_SAFE(&s_switch_lock);
|
||||
do_switch(PM_MODE_CPU_MAX);
|
||||
const unsigned wait_clock_stable_us = 10;
|
||||
esp_rom_delay_us(wait_clock_stable_us);
|
||||
}
|
||||
|
||||
#endif // CONFIG_PM_WORKAROUND_FREQ_LIMIT_ENABLED && CONFIG_PM_ENABLE
|
||||
|
||||
@@ -20,6 +20,13 @@ choice ESP_DEFAULT_CPU_FREQ_MHZ
|
||||
# Please see SoC Errata document for details.
|
||||
depends on !SECURE_FLASH_ENC_ENABLED
|
||||
bool "240 MHz"
|
||||
help
|
||||
When 240MHz is selected, esp_flash_write_encrypted() will automatically limit CPU frequency during
|
||||
execution:
|
||||
- v1.2 and above chips: limited to 160MHz
|
||||
- v1.0 and below chips: limited to 80MHz
|
||||
When 160MHz or lower is selected, no frequency limiting occurs during encrypted writes. Please refer to
|
||||
the ESP32-C5 SoC Errata document for more details.
|
||||
endchoice
|
||||
|
||||
config ESP_DEFAULT_CPU_FREQ_MHZ
|
||||
|
||||
@@ -31,7 +31,7 @@
|
||||
#include "private/esp_coexist_internal.h"
|
||||
#endif
|
||||
|
||||
#if CONFIG_PM_ENABLE
|
||||
#if CONFIG_PM_ENABLE || CONFIG_PM_WORKAROUND_FREQ_LIMIT_ENABLED
|
||||
#include "esp_pm.h"
|
||||
#include "esp_private/pm_impl.h"
|
||||
#endif
|
||||
@@ -120,6 +120,9 @@ ESP_SYSTEM_INIT_FN(init_flash, CORE, BIT(0), 130)
|
||||
#endif // CONFIG_SPI_FLASH_BROWNOUT_RESET
|
||||
// The log library will call the registered callback function to check if the cache is disabled.
|
||||
esp_log_util_set_cache_enabled_cb(spi_flash_cache_enabled);
|
||||
#if CONFIG_PM_WORKAROUND_FREQ_LIMIT_ENABLED
|
||||
esp_pm_flash_freq_limit_init();
|
||||
#endif // CONFIG_PM_WORKAROUND_FREQ_LIMIT_ENABLED
|
||||
return ESP_OK;
|
||||
}
|
||||
#endif // !CONFIG_APP_BUILD_TYPE_PURE_RAM_APP
|
||||
|
||||
@@ -1119,6 +1119,10 @@ config SOC_SPI_MEM_SUPPORT_TSUS_TRES_SEPERATE_CTR
|
||||
bool
|
||||
default y
|
||||
|
||||
config SOC_SPI_MEM_PSRAM_FREQ_AXI_CONSTRAINED
|
||||
bool
|
||||
default y
|
||||
|
||||
config SOC_MEMSPI_TIMING_TUNING_BY_MSPI_DELAY
|
||||
bool
|
||||
default y
|
||||
|
||||
@@ -453,6 +453,7 @@
|
||||
#define SOC_SPI_MEM_SUPPORT_CACHE_32BIT_ADDR_MAP (1)
|
||||
#define SOC_SPI_MEM_SUPPORT_TIMING_TUNING (1)
|
||||
#define SOC_SPI_MEM_SUPPORT_TSUS_TRES_SEPERATE_CTR (1)
|
||||
#define SOC_SPI_MEM_PSRAM_FREQ_AXI_CONSTRAINED (1)
|
||||
#define SOC_MEMSPI_TIMING_TUNING_BY_MSPI_DELAY (1)
|
||||
|
||||
#define SOC_MEMSPI_SRC_FREQ_120M_SUPPORTED 1
|
||||
|
||||
@@ -815,6 +815,10 @@ config SOC_SPI_MEM_SUPPORT_TSUS_TRES_SEPERATE_CTR
|
||||
bool
|
||||
default y
|
||||
|
||||
config SOC_SPI_MEM_PSRAM_FREQ_AXI_CONSTRAINED
|
||||
bool
|
||||
default y
|
||||
|
||||
config SOC_MEMSPI_TIMING_TUNING_BY_MSPI_DELAY
|
||||
bool
|
||||
default y
|
||||
|
||||
@@ -341,6 +341,7 @@
|
||||
#define SOC_SPI_MEM_SUPPORT_WRAP (1)
|
||||
#define SOC_SPI_MEM_SUPPORT_TIMING_TUNING (1)
|
||||
#define SOC_SPI_MEM_SUPPORT_TSUS_TRES_SEPERATE_CTR (1)
|
||||
#define SOC_SPI_MEM_PSRAM_FREQ_AXI_CONSTRAINED (1)
|
||||
#define SOC_MEMSPI_TIMING_TUNING_BY_MSPI_DELAY (1)
|
||||
|
||||
#define SOC_MEMSPI_SRC_FREQ_80M_SUPPORTED 1
|
||||
|
||||
@@ -715,6 +715,10 @@ config SOC_SPI_MEM_SUPPORT_WRAP
|
||||
bool
|
||||
default y
|
||||
|
||||
config SOC_SPI_MEM_PSRAM_FREQ_AXI_CONSTRAINED
|
||||
bool
|
||||
default y
|
||||
|
||||
config SOC_SYSTIMER_COUNTER_NUM
|
||||
int
|
||||
default 2
|
||||
|
||||
@@ -394,6 +394,7 @@
|
||||
#define SOC_SPI_MEM_SUPPORT_SW_SUSPEND (1)
|
||||
#define SOC_SPI_MEM_SUPPORT_CHECK_SUS (1)
|
||||
#define SOC_SPI_MEM_SUPPORT_WRAP (1)
|
||||
#define SOC_SPI_MEM_PSRAM_FREQ_AXI_CONSTRAINED (1)
|
||||
|
||||
/*-------------------------- SYSTIMER CAPS ----------------------------------*/
|
||||
#define SOC_SYSTIMER_COUNTER_NUM 2 // Number of counter units
|
||||
|
||||
@@ -1499,6 +1499,10 @@ config SOC_SPI_MEM_SUPPORT_CACHE_32BIT_ADDR_MAP
|
||||
bool
|
||||
default y
|
||||
|
||||
config SOC_SPI_MEM_PSRAM_FREQ_AXI_CONSTRAINED
|
||||
bool
|
||||
default y
|
||||
|
||||
config SOC_SPI_MEM_SUPPORT_TSUS_TRES_SEPERATE_CTR
|
||||
bool
|
||||
default y
|
||||
|
||||
@@ -572,6 +572,7 @@
|
||||
#define SOC_MEMSPI_TIMING_TUNING_BY_DQS (1)
|
||||
#define SOC_MEMSPI_TIMING_TUNING_BY_FLASH_DELAY (1)
|
||||
#define SOC_SPI_MEM_SUPPORT_CACHE_32BIT_ADDR_MAP (1)
|
||||
#define SOC_SPI_MEM_PSRAM_FREQ_AXI_CONSTRAINED (1)
|
||||
#define SOC_SPI_MEM_SUPPORT_TSUS_TRES_SEPERATE_CTR (1)
|
||||
|
||||
#define SOC_SPI_PERIPH_SUPPORT_CONTROL_DUMMY_OUT (1)
|
||||
|
||||
@@ -458,4 +458,11 @@ menu "SPI Flash driver"
|
||||
application is not using flash encryption feature and is in need of some additional
|
||||
memory from IRAM region (~1KB) then this config can be disabled.
|
||||
|
||||
config SPI_FLASH_FREQ_LIMIT_C5_240MHZ
|
||||
bool
|
||||
default y if IDF_TARGET_ESP32C5 && ESP_DEFAULT_CPU_FREQ_MHZ_240
|
||||
help
|
||||
Enable frequency limit workaround for encrypted flash writes on ESP32-C5 at 240MHz default CPU frequency.
|
||||
This is an internal configuration, automatically set based on target chip and CPU frequency.
|
||||
|
||||
endmenu
|
||||
|
||||
@@ -135,15 +135,6 @@ static ESP_LOG_ATTR const char io_mode_str[][IO_STR_LEN] = {
|
||||
|
||||
_Static_assert(sizeof(io_mode_str)/IO_STR_LEN == SPI_FLASH_READ_MODE_MAX, "the io_mode_str should be consistent with the esp_flash_io_mode_t defined in spi_flash_types.h");
|
||||
|
||||
esp_err_t esp_flash_read_chip_id(esp_flash_t* chip, uint32_t* flash_id);
|
||||
|
||||
#if !CONFIG_SPI_FLASH_ROM_IMPL || ESP_ROM_HAS_ENCRYPTED_WRITES_USING_LEGACY_DRV
|
||||
static esp_err_t spiflash_start_default(esp_flash_t *chip);
|
||||
static esp_err_t spiflash_end_default(esp_flash_t *chip, esp_err_t err);
|
||||
static esp_err_t check_chip_pointer_default(esp_flash_t **inout_chip);
|
||||
static esp_err_t flash_end_flush_cache(esp_flash_t* chip, esp_err_t err, bool bus_acquired, uint32_t address, uint32_t length);
|
||||
#endif // !CONFIG_SPI_FLASH_ROM_IMPL || ESP_ROM_HAS_ENCRYPTED_WRITES_USING_LEGACY_DRV
|
||||
|
||||
typedef struct {
|
||||
esp_err_t (*start)(esp_flash_t *chip);
|
||||
esp_err_t (*end)(esp_flash_t *chip, esp_err_t err);
|
||||
@@ -151,19 +142,70 @@ typedef struct {
|
||||
esp_err_t (*flash_end_flush_cache)(esp_flash_t* chip, esp_err_t err, bool bus_acquired, uint32_t address, uint32_t length);
|
||||
} rom_spiflash_api_func_t;
|
||||
|
||||
|
||||
esp_err_t esp_flash_read_chip_id(esp_flash_t* chip, uint32_t* flash_id);
|
||||
|
||||
#if CONFIG_SPI_FLASH_ROM_IMPL
|
||||
extern rom_spiflash_api_func_t *esp_flash_api_funcs;
|
||||
#define rom_spiflash_api_funcs esp_flash_api_funcs
|
||||
#else
|
||||
#define rom_spiflash_api_funcs esp_flash_api_funcs_patched_ptr
|
||||
#endif
|
||||
|
||||
#if !CONFIG_SPI_FLASH_ROM_IMPL || ESP_ROM_HAS_ENCRYPTED_WRITES_USING_LEGACY_DRV
|
||||
// API funcs case 1 & 2
|
||||
static esp_err_t spiflash_start_default(esp_flash_t *chip);
|
||||
static esp_err_t spiflash_end_default(esp_flash_t *chip, esp_err_t err);
|
||||
static esp_err_t check_chip_pointer_default(esp_flash_t **inout_chip);
|
||||
static esp_err_t flash_end_flush_cache(esp_flash_t* chip, esp_err_t err, bool bus_acquired, uint32_t address, uint32_t length);
|
||||
|
||||
// These functions can be placed in the ROM. For now we use the code in IDF.
|
||||
DRAM_ATTR static rom_spiflash_api_func_t default_spiflash_rom_api = {
|
||||
DRAM_ATTR static rom_spiflash_api_func_t esp_flash_api_funcs_patched = {
|
||||
.start = spiflash_start_default,
|
||||
.end = spiflash_end_default,
|
||||
.chip_check = check_chip_pointer_default,
|
||||
.flash_end_flush_cache = flash_end_flush_cache,
|
||||
};
|
||||
|
||||
DRAM_ATTR rom_spiflash_api_func_t *rom_spiflash_api_funcs = &default_spiflash_rom_api;
|
||||
#else
|
||||
extern rom_spiflash_api_func_t *esp_flash_api_funcs;
|
||||
#define rom_spiflash_api_funcs esp_flash_api_funcs
|
||||
# if !CONFIG_SPI_FLASH_ROM_IMPL
|
||||
// API funcs case 1: Not using ROM - define our own pointer and all functions
|
||||
DRAM_ATTR static rom_spiflash_api_func_t *esp_flash_api_funcs_patched_ptr = &esp_flash_api_funcs_patched;
|
||||
|
||||
# else // CONFIG_SPI_FLASH_ROM_IMPL
|
||||
// API funcs case 2: Using ROM APIs but patch all api_funcs by updating esp_flash_api_funcs from ROM
|
||||
void esp_flash_rom_api_funcs_init(void)
|
||||
{
|
||||
// Point esp_flash_api_funcs to our default structure
|
||||
esp_flash_api_funcs = &esp_flash_api_funcs_patched;
|
||||
}
|
||||
|
||||
# endif // CONFIG_SPI_FLASH_ROM_IMPL
|
||||
|
||||
#else // CONFIG_SPI_FLASH_ROM_IMPL && !ESP_ROM_HAS_ENCRYPTED_WRITES_USING_LEGACY_DRV
|
||||
// Using ROM implementation
|
||||
|
||||
# if CONFIG_SPI_FLASH_FREQ_LIMIT_C5_240MHZ
|
||||
// API funcs case 3: Using ROM APIs but patch start function to support flags parameter
|
||||
static esp_err_t spiflash_start_default(esp_flash_t *chip);
|
||||
DRAM_ATTR static rom_spiflash_api_func_t esp_flash_api_funcs_patched;
|
||||
|
||||
// Copy ROM structure to RAM and patch start function to support flags
|
||||
void esp_flash_rom_api_funcs_init(void)
|
||||
{
|
||||
rom_spiflash_api_func_t *rom_ptr = esp_flash_api_funcs;
|
||||
memcpy(&esp_flash_api_funcs_patched, rom_ptr, sizeof(rom_spiflash_api_func_t));
|
||||
esp_flash_api_funcs_patched.start = spiflash_start_default;
|
||||
esp_flash_api_funcs = &esp_flash_api_funcs_patched;
|
||||
}
|
||||
|
||||
# else
|
||||
// API funcs case 4: Using All ROM APIs directly
|
||||
void esp_flash_rom_api_funcs_init(void)
|
||||
{
|
||||
// Do nothing
|
||||
}
|
||||
|
||||
# endif // CONFIG_SPI_FLASH_FREQ_LIMIT_C5_240MHZ
|
||||
#endif // !CONFIG_SPI_FLASH_ROM_IMPL || ESP_ROM_HAS_ENCRYPTED_WRITES_USING_LEGACY_DRV
|
||||
|
||||
/* Static function to notify OS of a new SPI flash operation.
|
||||
@@ -171,11 +213,13 @@ extern rom_spiflash_api_func_t *esp_flash_api_funcs;
|
||||
If returns an error result, caller must abort. If returns ESP_OK, caller must
|
||||
call rom_spiflash_api_funcs->end() before returning.
|
||||
*/
|
||||
#if !CONFIG_SPI_FLASH_ROM_IMPL || ESP_ROM_HAS_ENCRYPTED_WRITES_USING_LEGACY_DRV
|
||||
static esp_err_t spiflash_start_default(esp_flash_t *chip)
|
||||
#if !CONFIG_SPI_FLASH_ROM_IMPL || ESP_ROM_HAS_ENCRYPTED_WRITES_USING_LEGACY_DRV || CONFIG_SPI_FLASH_FREQ_LIMIT_C5_240MHZ
|
||||
//Avoid constprop issue that place this function into flash.
|
||||
__attribute__((optimize("O0"))) //IDF-14941
|
||||
static esp_err_t spiflash_start_core(esp_flash_t *chip, uint32_t flags)
|
||||
{
|
||||
if (chip->os_func != NULL && chip->os_func->start != NULL) {
|
||||
esp_err_t err = chip->os_func->start(chip->os_func_data);
|
||||
esp_err_t err = chip->os_func->start(chip->os_func_data, flags);
|
||||
if (err != ESP_OK) {
|
||||
return err;
|
||||
}
|
||||
@@ -184,6 +228,13 @@ static esp_err_t spiflash_start_default(esp_flash_t *chip)
|
||||
return ESP_OK;
|
||||
}
|
||||
|
||||
static esp_err_t spiflash_start_default(esp_flash_t *chip)
|
||||
{
|
||||
return spiflash_start_core(chip, 0);
|
||||
}
|
||||
#endif //!CONFIG_SPI_FLASH_ROM_IMPL || ESP_ROM_HAS_ENCRYPTED_WRITES_USING_LEGACY_DRV || CONFIG_SPI_FLASH_FREQ_LIMIT_C5_240MHZ
|
||||
|
||||
#if !CONFIG_SPI_FLASH_ROM_IMPL || ESP_ROM_HAS_ENCRYPTED_WRITES_USING_LEGACY_DRV
|
||||
/* Static function to notify OS that SPI flash operation is complete.
|
||||
*/
|
||||
static esp_err_t spiflash_end_default(esp_flash_t *chip, esp_err_t err)
|
||||
@@ -1235,54 +1286,20 @@ esp_err_t esp_flash_set_io_mode(esp_flash_t* chip, bool qe)
|
||||
}
|
||||
#endif //CONFIG_SPI_FLASH_ROM_IMPL
|
||||
|
||||
#if CONFIG_IDF_TARGET_ESP32C5
|
||||
// Hardware workaround: ESP32-C5 encrypted flash writes require CPU freq ≤ 160 MHz
|
||||
#include "soc/rtc.h"
|
||||
|
||||
static int s_esp32c5_saved_cpu_freq_mhz;
|
||||
|
||||
static IRAM_ATTR void esp32c5_freq_limit_acquire(void)
|
||||
#if !(CONFIG_SPI_FLASH_ROM_IMPL && !ESP_ROM_HAS_ENCRYPTED_WRITES_USING_LEGACY_DRV) || CONFIG_SPI_FLASH_FREQ_LIMIT_C5_240MHZ
|
||||
// use `esp_flash_write_encrypted` ROM version on chips later than C3, S3
|
||||
// For ESP32-C5, use IDF implementation when CPU frequency is 240MHz (calling start() with arg is required)
|
||||
FORCE_INLINE_ATTR esp_err_t s_encryption_write_lock(esp_flash_t *chip)
|
||||
{
|
||||
rtc_cpu_freq_config_t old_config, new_config;
|
||||
rtc_clk_cpu_freq_get_config(&old_config);
|
||||
|
||||
if (old_config.freq_mhz <= 160) {
|
||||
s_esp32c5_saved_cpu_freq_mhz = 0; // No change needed
|
||||
return;
|
||||
}
|
||||
s_esp32c5_saved_cpu_freq_mhz = old_config.freq_mhz;
|
||||
|
||||
if (rtc_clk_cpu_freq_mhz_to_config(160, &new_config)) {
|
||||
rtc_clk_cpu_freq_set_config_fast(&new_config);
|
||||
}
|
||||
}
|
||||
|
||||
static IRAM_ATTR void esp32c5_freq_limit_release(void)
|
||||
{
|
||||
if (s_esp32c5_saved_cpu_freq_mhz == 0) {
|
||||
return; // No change was made
|
||||
}
|
||||
|
||||
rtc_cpu_freq_config_t new_config;
|
||||
|
||||
if (rtc_clk_cpu_freq_mhz_to_config(s_esp32c5_saved_cpu_freq_mhz, &new_config)) {
|
||||
rtc_clk_cpu_freq_set_config_fast(&new_config);
|
||||
}
|
||||
|
||||
s_esp32c5_saved_cpu_freq_mhz = 0;
|
||||
}
|
||||
#endif // CONFIG_IDF_TARGET_ESP32C5
|
||||
|
||||
#if !(CONFIG_SPI_FLASH_ROM_IMPL && !ESP_ROM_HAS_ENCRYPTED_WRITES_USING_LEGACY_DRV)
|
||||
// use `esp_flash_write_encrypted` ROM version on chips later than C3 and S3
|
||||
FORCE_INLINE_ATTR esp_err_t s_encryption_write_lock(esp_flash_t *chip) {
|
||||
#if CONFIG_IDF_TARGET_ESP32C5
|
||||
esp32c5_freq_limit_acquire();
|
||||
#endif
|
||||
#if CONFIG_IDF_TARGET_ESP32S2
|
||||
esp_crypto_dma_lock_acquire();
|
||||
#endif //CONFIG_IDF_TARGET_ESP32S2
|
||||
#if CONFIG_SPI_FLASH_FREQ_LIMIT_C5_240MHZ
|
||||
// Use start_core with LIMIT_CPU_FREQ flag to trigger freq_limit_lock in OS layer
|
||||
return spiflash_start_core(chip, ESP_FLASH_START_FLAG_LIMIT_CPU_FREQ);
|
||||
#else
|
||||
return rom_spiflash_api_funcs->start(chip);
|
||||
#endif
|
||||
}
|
||||
|
||||
FORCE_INLINE_ATTR esp_err_t s_encryption_write_unlock(esp_flash_t *chip) {
|
||||
@@ -1290,9 +1307,6 @@ FORCE_INLINE_ATTR esp_err_t s_encryption_write_unlock(esp_flash_t *chip) {
|
||||
#if CONFIG_IDF_TARGET_ESP32S2
|
||||
esp_crypto_dma_lock_release();
|
||||
#endif //CONFIG_IDF_TARGET_ESP32S2
|
||||
#if CONFIG_IDF_TARGET_ESP32C5
|
||||
esp32c5_freq_limit_release();
|
||||
#endif
|
||||
return err;
|
||||
}
|
||||
|
||||
@@ -1535,16 +1549,9 @@ esp_err_t IRAM_ATTR esp_flash_write_encrypted(esp_flash_t *chip, uint32_t addres
|
||||
if (length > chip->size - address) {
|
||||
return ESP_ERR_INVALID_ARG;
|
||||
}
|
||||
#if CONFIG_IDF_TARGET_ESP32C5
|
||||
esp32c5_freq_limit_acquire();
|
||||
#endif
|
||||
err = rom_esp_flash_write_encrypted(chip, address, buffer, length);
|
||||
#if CONFIG_IDF_TARGET_ESP32C5
|
||||
esp32c5_freq_limit_release();
|
||||
#endif
|
||||
return err;
|
||||
return rom_esp_flash_write_encrypted(chip, address, buffer, length);
|
||||
}
|
||||
#endif // !(CONFIG_SPI_FLASH_ROM_IMPL && !ESP_ROM_HAS_ENCRYPTED_WRITES_USING_LEGACY_DRV)
|
||||
#endif // !(CONFIG_SPI_FLASH_ROM_IMPL && !ESP_ROM_HAS_ENCRYPTED_WRITES_USING_LEGACY_DRV) || CONFIG_SPI_FLASH_FREQ_LIMIT_C5_240MHZ
|
||||
|
||||
//init suspend mode cmd, uses internal.
|
||||
esp_err_t esp_flash_suspend_cmd_init(esp_flash_t* chip)
|
||||
|
||||
@@ -145,6 +145,19 @@ esp_flash_t *esp_flash_default_chip = NULL;
|
||||
#endif //!CONFIG_SPI_FLASH_AUTO_SUSPEND
|
||||
#endif // Other target
|
||||
|
||||
// Dynamic flash configuration is only needed when:
|
||||
// 1. Frequency limit workaround is enabled (CONFIG_SPI_FLASH_FREQ_LIMIT_C5_240MHZ)
|
||||
// 2. Flash frequency requires timing tuning (80MHz or 120MHz, i.e., > 40MHz)
|
||||
// 3. CPU frequency reduction will trigger MSPI timing tuning to enter low speed mode
|
||||
// This happens when: SOC_SPI_MEM_PSRAM_FREQ_AXI_CONSTRAINED && CONFIG_SPIRAM &&
|
||||
// (target_cpu_freq < CONFIG_SPIRAM_SPEED)
|
||||
// Note: The runtime check for CPU freq < PSRAM speed is done in clk_utils.c,
|
||||
// which calls mspi_timing_change_speed_mode_cache_safe(true) to enter low speed mode.
|
||||
// For ESP32-C5, if PSRAM is enabled and CPU freq < PSRAM speed, timing tuning will be disabled.
|
||||
#define C5_NEEDS_DYNAMIC_CONFIG (CONFIG_SPI_FLASH_FREQ_LIMIT_C5_240MHZ && CONFIG_SPIRAM && \
|
||||
(CONFIG_ESPTOOLPY_FLASHFREQ_80M || CONFIG_ESPTOOLPY_FLASHFREQ_120M))
|
||||
|
||||
|
||||
static IRAM_ATTR NOINLINE_ATTR void cs_initialize(esp_flash_t *chip, const esp_flash_spi_device_config_t *config, bool cs_use_iomux, int cs_id)
|
||||
{
|
||||
//Not using spicommon_cs_initialize since we don't want to put the whole
|
||||
@@ -160,7 +173,7 @@ static IRAM_ATTR NOINLINE_ATTR void cs_initialize(esp_flash_t *chip, const esp_f
|
||||
|
||||
//To avoid the panic caused by flash data line conflicts during cs line
|
||||
//initialization, disable the cache temporarily
|
||||
chip->os_func->start(chip->os_func_data);
|
||||
chip->os_func->start(chip->os_func_data, 0);
|
||||
gpio_hal_input_enable(&gpio_hal, cs_io_num);
|
||||
if (cs_use_iomux) {
|
||||
gpio_hal_func_sel(&gpio_hal, cs_io_num, spics_func);
|
||||
@@ -554,6 +567,16 @@ esp_err_t esp_flash_init_default_chip(void)
|
||||
if (err != ESP_OK) {
|
||||
return err;
|
||||
}
|
||||
#if C5_NEEDS_DYNAMIC_CONFIG
|
||||
err = memspi_host_init_c5_dynamic_config(&esp_flash_default_host);
|
||||
if (err != ESP_OK) {
|
||||
return err;
|
||||
}
|
||||
#endif
|
||||
|
||||
#if CONFIG_SPI_FLASH_ROM_IMPL
|
||||
esp_flash_rom_api_funcs_init();
|
||||
#endif // CONFIG_SPI_FLASH_ROM_IMPL
|
||||
|
||||
// ROM TODO: account for non-standard default pins in efuse
|
||||
// ROM TODO: to account for chips which are slow to power on, maybe keep probing in a loop here
|
||||
@@ -615,6 +638,7 @@ esp_err_t esp_flash_app_init(void)
|
||||
|
||||
spi_flash_init_lock();
|
||||
spi_flash_guard_set(&g_flash_guard_default_ops);
|
||||
|
||||
#if CONFIG_SPI_FLASH_ENABLE_COUNTERS
|
||||
esp_flash_reset_counters();
|
||||
#endif
|
||||
|
||||
@@ -38,11 +38,17 @@ typedef struct {
|
||||
* risk.
|
||||
*/
|
||||
typedef struct {
|
||||
/**
|
||||
* Flags for start function
|
||||
*/
|
||||
/** Limit CPU frequency during flash operations (ESP32-C5 only, 240MHz).
|
||||
*/
|
||||
#define ESP_FLASH_START_FLAG_LIMIT_CPU_FREQ BIT(0)
|
||||
/**
|
||||
* Called before commencing any flash operation. Does not need to be
|
||||
* recursive (ie is called at most once for each call to 'end').
|
||||
*/
|
||||
esp_err_t (*start)(void *arg);
|
||||
esp_err_t (*start)(void *arg, uint32_t flags);
|
||||
|
||||
/** Called after completing any flash operation. */
|
||||
esp_err_t (*end)(void *arg);
|
||||
|
||||
@@ -117,6 +117,16 @@ esp_err_t esp_flash_app_disable_os_functions(esp_flash_t* chip);
|
||||
*/
|
||||
esp_err_t esp_flash_set_dangerous_write_protection(esp_flash_t *chip, const bool protect);
|
||||
|
||||
#if CONFIG_SPI_FLASH_ROM_IMPL
|
||||
/**
|
||||
* @brief Initialize ROM API functions structure
|
||||
*
|
||||
* This function initializes the ROM API functions structure, either by pointing
|
||||
* to a custom structure or by patching the ROM structure in RAM.
|
||||
*/
|
||||
void esp_flash_rom_api_funcs_init(void);
|
||||
#endif // CONFIG_SPI_FLASH_ROM_IMPL
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
@@ -0,0 +1,49 @@
|
||||
/*
|
||||
* SPDX-FileCopyrightText: 2025 Espressif Systems (Shanghai) CO LTD
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "sdkconfig.h"
|
||||
#include "esp_flash.h"
|
||||
#include "esp_attr.h"
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
#if CONFIG_SPI_FLASH_FREQ_LIMIT_C5_240MHZ
|
||||
/**
|
||||
* @brief Callback to limit CPU frequency for flash encryption writes (ESP32-C5 only, 240MHz)
|
||||
*
|
||||
* This function limits CPU frequency to <= 160MHz during encrypted flash writes.
|
||||
* It should be called before starting an encrypted flash write operation.
|
||||
*
|
||||
* @note This is an internal function, not exposed to users.
|
||||
* @note This function is placed in IRAM (implementation uses IRAM_ATTR).
|
||||
* @note This function is called from spi_flash_os_func_app.c::spi1_start() after cache
|
||||
* and scheduler locks are acquired. As a result, there is no concurrency concern
|
||||
* and no need for internal locking or reference counting.
|
||||
*/
|
||||
void esp_flash_freq_limit_cb(void);
|
||||
|
||||
/**
|
||||
* @brief Callback to unlimit CPU frequency after flash encryption writes (ESP32-C5 only, 240MHz)
|
||||
*
|
||||
* This function restores the CPU frequency after an encrypted flash write operation.
|
||||
* It should be called after completing an encrypted flash write operation.
|
||||
*
|
||||
* @note This is an internal function, not exposed to users.
|
||||
* @note This function is placed in IRAM (implementation uses IRAM_ATTR).
|
||||
* @note This function is called from spi_flash_os_func_app.c::spi1_end() before cache
|
||||
* and scheduler locks are released. As a result, there is no concurrency concern
|
||||
* and no need for internal locking or reference counting.
|
||||
*/
|
||||
void esp_flash_freq_unlimit_cb(void);
|
||||
#endif // CONFIG_SPI_FLASH_FREQ_LIMIT_C5_240MHZ
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
@@ -48,6 +48,17 @@ typedef spi_flash_hal_context_t memspi_host_inst_t;
|
||||
*/
|
||||
esp_err_t memspi_host_init_pointers(memspi_host_inst_t *host, const memspi_host_config_t *cfg);
|
||||
|
||||
/**
|
||||
* Initialize the memory SPI host for ESP32-C5 dynamic configuration.
|
||||
*
|
||||
* @param host Pointer to the host structure.
|
||||
*
|
||||
* @note This function is available only when CONFIG_SPI_FLASH_FREQ_LIMIT_C5_240MHZ is defined.
|
||||
* @note This function can only be called once at startup, after memspi_host_init_pointers() is called.
|
||||
* @return always return ESP_OK
|
||||
*/
|
||||
esp_err_t memspi_host_init_c5_dynamic_config(memspi_host_inst_t *host);
|
||||
|
||||
/*******************************************************************************
|
||||
* NOTICE
|
||||
* Rest part of this file are part of the HAL layer
|
||||
@@ -161,7 +172,7 @@ int memspi_host_read_data_slicer(spi_flash_host_inst_t *host, uint32_t address,
|
||||
|
||||
/**
|
||||
* @brief Slicer for write data used in non-encrypted regions. This slicer limit the length to the
|
||||
* maximum size the host supports, and truncate if the write data lie accross the page boundary
|
||||
* maximum size the host supports, and truncate if the write data lie across the page boundary
|
||||
* (256 bytes)
|
||||
*
|
||||
* @param address Flash address to write
|
||||
|
||||
@@ -45,12 +45,15 @@ entries:
|
||||
if SPI_FLASH_VERIFY_WRITE = y:
|
||||
esp_flash_api: s_verify_write (noflash)
|
||||
|
||||
if SPI_FLASH_ROM_IMPL = n || ESP_ROM_HAS_ENCRYPTED_WRITES_USING_LEGACY_DRV = y:
|
||||
if SPI_FLASH_ROM_IMPL = n || ESP_ROM_HAS_ENCRYPTED_WRITES_USING_LEGACY_DRV = y || SPI_FLASH_FREQ_LIMIT_C5_240MHZ = y:
|
||||
esp_flash_api: spiflash_start_default (noflash)
|
||||
esp_flash_api: spiflash_start_core (noflash)
|
||||
esp_flash_api: esp_flash_write_encrypted (noflash)
|
||||
|
||||
if SPI_FLASH_ROM_IMPL = n || ESP_ROM_HAS_ENCRYPTED_WRITES_USING_LEGACY_DRV = y:
|
||||
esp_flash_api: spiflash_end_default (noflash)
|
||||
esp_flash_api: check_chip_pointer_default (noflash)
|
||||
esp_flash_api: flash_end_flush_cache (noflash)
|
||||
esp_flash_api: esp_flash_write_encrypted (noflash)
|
||||
|
||||
if SPI_FLASH_ROM_IMPL = n:
|
||||
esp_flash_api: esp_flash_get_size (noflash)
|
||||
|
||||
@@ -4,6 +4,7 @@
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
#include "sdkconfig.h"
|
||||
#include "soc/soc_caps.h"
|
||||
#include "spi_flash_defs.h"
|
||||
#include "memspi_host_driver.h"
|
||||
@@ -12,12 +13,13 @@
|
||||
#include "esp_private/cache_utils.h"
|
||||
#include "esp_flash_partitions.h"
|
||||
#include "esp_memory_utils.h"
|
||||
#include "hal/mspi_ll.h"
|
||||
|
||||
|
||||
#define SPI_FLASH_HAL_MAX_WRITE_BYTES 64
|
||||
#define SPI_FLASH_HAL_MAX_READ_BYTES 64
|
||||
|
||||
DRAM_ATTR static const spi_flash_host_driver_t esp_flash_default_host = ESP_FLASH_DEFAULT_HOST_DRIVER();
|
||||
DRAM_ATTR static spi_flash_host_driver_t esp_flash_default_host = ESP_FLASH_DEFAULT_HOST_DRIVER();
|
||||
|
||||
#if SOC_MEMSPI_IS_INDEPENDENT
|
||||
extern void spi_flash_hal_gpspi_poll_cmd_done(spi_flash_host_inst_t *host);
|
||||
@@ -71,9 +73,9 @@ esp_err_t memspi_host_init_pointers(memspi_host_inst_t *host, const memspi_host_
|
||||
}
|
||||
|
||||
#if SOC_MEMSPI_IS_INDEPENDENT
|
||||
if (cfg->host_id == SPI1_HOST)
|
||||
if (cfg->host_id == SPI1_HOST) {
|
||||
host->inst.driver = &esp_flash_default_host;
|
||||
else {
|
||||
} else {
|
||||
host->inst.driver = &esp_flash_gpspi_host;
|
||||
}
|
||||
#else
|
||||
@@ -253,3 +255,52 @@ int memspi_host_read_data_slicer(spi_flash_host_inst_t *host, uint32_t address,
|
||||
}
|
||||
|
||||
#endif // CONFIG_SPI_FLASH_ROM_IMPL
|
||||
|
||||
#if CONFIG_SPI_FLASH_FREQ_LIMIT_C5_240MHZ
|
||||
// Dynamic flash configuration based on timing tuning state:
|
||||
// 1. The timing tuning system automatically sets core clock and timing parameters (including din_mode/num, etc.).
|
||||
// It also generates configurations such as clock configuration and dummy configuration for driver use.
|
||||
// Without calling this function, the driver gets these configurations during initialization and uses them forever.
|
||||
// The timing tuning provides clock configuration (frequency division) and dummy count.
|
||||
// 2. When dynamic configuration is needed, call this function. The function supports two states:
|
||||
// - Timing tuned state (high speed): Restore the values generated by the timing tuning system to the driver,
|
||||
// as if there was no frequency reduction.
|
||||
// - Without timing tuning state (low speed): Set configuration to maximum frequency (40 MHz on C5), dummy=0.
|
||||
static uint32_t s_high_speed_clock_reg;
|
||||
static uint32_t s_high_speed_extra_dummy;
|
||||
|
||||
ESP_STATIC_ASSERT(MSPI_TIMING_LL_CORE_CLOCK_MHZ_DEFAULT == 80);
|
||||
#define LOW_SPEED_DIV (MSPI_TIMING_LL_CORE_CLOCK_MHZ_DEFAULT/40)
|
||||
static esp_err_t spi_flash_hal_device_config_c5(spi_flash_host_inst_t *host)
|
||||
{
|
||||
spi_flash_hal_context_t* ctx = (spi_flash_hal_context_t*)host;
|
||||
|
||||
// Note: SPI0 and SPI1 have separate TIMING_CALI_REG registers, but SPI0's register reflects the timing tuning state
|
||||
// since SPI0 and SPI1 share din_mode/din_num registers and timing tuning is configured on SPI0
|
||||
uint32_t timing_cali_reg = REG_READ(SPI_MEM_TIMING_CALI_REG(MSPI_TIMING_LL_MSPI_ID_0));
|
||||
bool is_low_speed = !(timing_cali_reg & SPI_MEM_TIMING_CALI_M);
|
||||
|
||||
if (is_low_speed) {
|
||||
// Low speed mode: Set to safe division, dummy=0
|
||||
uint32_t low_speed_clock_reg = mspi_timing_ll_calculate_clock_reg(LOW_SPEED_DIV);
|
||||
ctx->clock_conf.spimem = low_speed_clock_reg;
|
||||
ctx->extra_dummy = 0;
|
||||
} else {
|
||||
// High speed mode: Restore timing tuning values
|
||||
ctx->clock_conf.spimem = s_high_speed_clock_reg;
|
||||
ctx->extra_dummy = s_high_speed_extra_dummy;
|
||||
}
|
||||
return spi_flash_hal_device_config(host);
|
||||
}
|
||||
|
||||
esp_err_t memspi_host_init_c5_dynamic_config(memspi_host_inst_t *host)
|
||||
{
|
||||
assert(s_high_speed_clock_reg == 0 && s_high_speed_extra_dummy == 0);
|
||||
|
||||
spi_flash_hal_context_t* ctx = (spi_flash_hal_context_t*)host;
|
||||
s_high_speed_clock_reg = ctx->clock_conf.spimem;
|
||||
s_high_speed_extra_dummy = ctx->extra_dummy;
|
||||
esp_flash_default_host.dev_config = spi_flash_hal_device_config_c5;
|
||||
return ESP_OK;
|
||||
}
|
||||
#endif
|
||||
|
||||
@@ -20,9 +20,12 @@
|
||||
#include "esp_rom_sys.h"
|
||||
#include "esp_private/spi_flash_os.h"
|
||||
#include "esp_private/cache_utils.h"
|
||||
|
||||
#include "esp_private/spi_share_hw_ctrl.h"
|
||||
|
||||
// For C5 encrypted write workaround
|
||||
// Functions are only available when CONFIG_SPI_FLASH_FREQ_LIMIT_C5_240MHZ is true
|
||||
#include "esp_private/spi_flash_freq_limit_cbs.h"
|
||||
|
||||
#define SPI_FLASH_CACHE_NO_DISABLE (CONFIG_SPI_FLASH_AUTO_SUSPEND || (CONFIG_SPIRAM_FETCH_INSTRUCTIONS && CONFIG_SPIRAM_RODATA) || CONFIG_APP_BUILD_TYPE_RAM)
|
||||
static const char TAG[] = "spi_flash";
|
||||
|
||||
@@ -53,6 +56,7 @@ typedef struct {
|
||||
bool no_protect; //to decide whether to check protected region (for the main chip) or not.
|
||||
uint32_t acquired_since_us; // Time since last explicit yield()
|
||||
uint32_t released_since_us; // Time since last end() (implicit yield)
|
||||
uint32_t start_flags; // Flags passed to start() function, used to determine if freq_limit was called
|
||||
} app_func_arg_t;
|
||||
|
||||
static inline void on_spi_released(app_func_arg_t* ctx);
|
||||
@@ -90,21 +94,27 @@ static IRAM_ATTR esp_err_t release_spi_bus_lock(void *arg)
|
||||
return spi_bus_lock_acquire_end(((app_func_arg_t *)arg)->dev_lock);
|
||||
}
|
||||
|
||||
static esp_err_t spi23_start(void *arg){
|
||||
static esp_err_t spi23_start(void *arg, uint32_t flags)
|
||||
{
|
||||
(void)flags;
|
||||
esp_err_t ret = acquire_spi_bus_lock(arg);
|
||||
on_spi_acquired((app_func_arg_t*)arg);
|
||||
return ret;
|
||||
}
|
||||
|
||||
static esp_err_t spi23_end(void *arg){
|
||||
static esp_err_t spi23_end(void *arg)
|
||||
{
|
||||
esp_err_t ret = release_spi_bus_lock(arg);
|
||||
on_spi_released((app_func_arg_t*)arg);
|
||||
return ret;
|
||||
}
|
||||
|
||||
static IRAM_ATTR esp_err_t spi1_start(void *arg)
|
||||
static IRAM_ATTR esp_err_t spi1_start(void *arg, uint32_t flags)
|
||||
{
|
||||
esp_err_t ret = ESP_OK;
|
||||
app_func_arg_t* ctx = (app_func_arg_t*)arg;
|
||||
ctx->start_flags = flags;
|
||||
|
||||
/**
|
||||
* There are three ways for ESP Flash API lock:
|
||||
* 1. spi bus lock, this is used when SPI1 is shared with GPSPI Master Driver
|
||||
@@ -136,13 +146,28 @@ static IRAM_ATTR esp_err_t spi1_start(void *arg)
|
||||
}
|
||||
#endif // CONFIG_SPI_FLASH_DISABLE_SCHEDULER_IN_SUSPEND
|
||||
|
||||
on_spi_acquired((app_func_arg_t*)arg);
|
||||
#if CONFIG_SPI_FLASH_FREQ_LIMIT_C5_240MHZ
|
||||
if (flags & ESP_FLASH_START_FLAG_LIMIT_CPU_FREQ) {
|
||||
esp_flash_freq_limit_cb();
|
||||
}
|
||||
#endif
|
||||
|
||||
on_spi_acquired(ctx);
|
||||
return ret;
|
||||
}
|
||||
|
||||
static IRAM_ATTR esp_err_t spi1_end(void *arg)
|
||||
{
|
||||
esp_err_t ret = ESP_OK;
|
||||
app_func_arg_t* ctx = (app_func_arg_t*)arg;
|
||||
|
||||
// Call freq_limit_unlock if needed, before releasing the lock
|
||||
#if CONFIG_SPI_FLASH_FREQ_LIMIT_C5_240MHZ
|
||||
uint32_t flags = ctx->start_flags;
|
||||
if (flags & ESP_FLASH_START_FLAG_LIMIT_CPU_FREQ) {
|
||||
esp_flash_freq_unlimit_cb();
|
||||
}
|
||||
#endif
|
||||
|
||||
/**
|
||||
* There are three ways for ESP Flash API lock, see `spi1_start`
|
||||
@@ -166,7 +191,7 @@ static IRAM_ATTR esp_err_t spi1_end(void *arg)
|
||||
}
|
||||
#endif // CONFIG_SPI_FLASH_DISABLE_SCHEDULER_IN_SUSPEND
|
||||
|
||||
on_spi_released((app_func_arg_t*)arg);
|
||||
on_spi_released(ctx);
|
||||
return ret;
|
||||
}
|
||||
|
||||
|
||||
@@ -15,7 +15,7 @@
|
||||
#include "hal/cache_ll.h"
|
||||
#include "soc/soc_caps.h"
|
||||
|
||||
static IRAM_ATTR esp_err_t start(void *arg)
|
||||
static IRAM_ATTR esp_err_t start(void *arg, uint32_t flags)
|
||||
{
|
||||
#if SOC_BRANCH_PREDICTOR_SUPPORTED
|
||||
//branch predictor will start cache request as well
|
||||
|
||||
@@ -24,6 +24,15 @@ components/spi_flash/test_apps/esp_flash_blockdev:
|
||||
depends_components:
|
||||
- spi_flash
|
||||
|
||||
components/spi_flash/test_apps/esp_flash_freq_limit:
|
||||
enable:
|
||||
- if: IDF_TARGET == "esp32c5"
|
||||
depends_components:
|
||||
- spi_flash
|
||||
- esp_pm
|
||||
- esp_driver_gptimer
|
||||
- esp_hw_support
|
||||
|
||||
components/spi_flash/test_apps/esp_flash_stress:
|
||||
disable:
|
||||
- if: IDF_TARGET == "esp32h4"
|
||||
|
||||
@@ -0,0 +1,17 @@
|
||||
# This is the project CMakeLists.txt file for the test subproject
|
||||
cmake_minimum_required(VERSION 3.22)
|
||||
|
||||
set(EXTRA_COMPONENT_DIRS "$ENV{IDF_PATH}/tools/test_apps/components")
|
||||
|
||||
# "Trim" the build. Include the minimal set of components, main, and anything it depends on. We also depend on
|
||||
# esptool_py as we set CONFIG_ESPTOOLPY_... options.
|
||||
set(COMPONENTS main esptool_py)
|
||||
|
||||
include($ENV{IDF_PATH}/tools/cmake/project.cmake)
|
||||
|
||||
project(test_esp_flash_freq_limit)
|
||||
|
||||
message(STATUS "Checking memspi registers are not read-write by half-word")
|
||||
include($ENV{IDF_PATH}/tools/ci/check_register_rw_half_word.cmake)
|
||||
check_register_rw_half_word(SOC_MODULES "spi_mem*" "spi1_mem*"
|
||||
HAL_MODULES "spimem_flash")
|
||||
@@ -0,0 +1,2 @@
|
||||
| Supported Targets | ESP32-C5 |
|
||||
| ----------------- | -------- |
|
||||
@@ -0,0 +1,9 @@
|
||||
set(srcs "test_app_main.c"
|
||||
"test_esp_flash_freq_limit.c")
|
||||
|
||||
# In order for the cases defined by `TEST_CASE` to be linked into the final elf,
|
||||
# the component can be registered as WHOLE_ARCHIVE
|
||||
idf_component_register(SRCS ${srcs}
|
||||
PRIV_REQUIRES unity test_utils spi_flash esp_pm bootloader_support freertos esp_timer
|
||||
esp_driver_gptimer esp_hw_support esp_psram
|
||||
WHOLE_ARCHIVE)
|
||||
@@ -0,0 +1,25 @@
|
||||
/*
|
||||
* SPDX-FileCopyrightText: 2025 Espressif Systems (Shanghai) CO LTD
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
#include "unity.h"
|
||||
#include "unity_test_utils.h"
|
||||
|
||||
#define TEST_MEMORY_LEAK_THRESHOLD (700)
|
||||
|
||||
void setUp(void)
|
||||
{
|
||||
unity_utils_record_free_mem();
|
||||
}
|
||||
|
||||
void tearDown(void)
|
||||
{
|
||||
unity_utils_evaluate_leaks_direct(TEST_MEMORY_LEAK_THRESHOLD);
|
||||
}
|
||||
|
||||
void app_main(void)
|
||||
{
|
||||
unity_run_menu();
|
||||
}
|
||||
+1250
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,5 @@
|
||||
# Name, Type, SubType, Offset, Size, Flags
|
||||
# Note: if you have increased the bootloader size, make sure to update the offsets to avoid overlap
|
||||
nvs, data, nvs, 0x9000, 0x6000,
|
||||
factory, 0, 0, 0x10000, 1M
|
||||
flash_test, data, fat, , 700K
|
||||
|
@@ -0,0 +1,23 @@
|
||||
# SPDX-FileCopyrightText: 2025 Espressif Systems (Shanghai) CO LTD
|
||||
# SPDX-License-Identifier: Apache-2.0
|
||||
import pytest
|
||||
from pytest_embedded import Dut
|
||||
from pytest_embedded_idf.utils import idf_parametrize
|
||||
|
||||
|
||||
@pytest.mark.generic
|
||||
@pytest.mark.parametrize(
|
||||
'config',
|
||||
[
|
||||
'pm_disabled_240mhz',
|
||||
'pm_disabled_160mhz',
|
||||
'pm_enabled_240mhz',
|
||||
'pm_enabled_160mhz',
|
||||
'pm_disabled_240mhz_xip_psram',
|
||||
'pm_enabled_240mhz_xip_psram',
|
||||
],
|
||||
indirect=True,
|
||||
)
|
||||
@idf_parametrize('target', ['esp32c5'], indirect=['target'])
|
||||
def test_esp_flash_freq_limit(dut: Dut) -> None:
|
||||
dut.run_all_single_board_cases(group='esp_flash_freq_limit', timeout=10)
|
||||
@@ -0,0 +1,2 @@
|
||||
CONFIG_PM_ENABLE=n
|
||||
CONFIG_ESP_DEFAULT_CPU_FREQ_MHZ_160=y
|
||||
@@ -0,0 +1,2 @@
|
||||
CONFIG_PM_ENABLE=n
|
||||
CONFIG_ESP_DEFAULT_CPU_FREQ_MHZ_240=y
|
||||
+8
@@ -0,0 +1,8 @@
|
||||
CONFIG_PM_ENABLE=n
|
||||
CONFIG_ESP_DEFAULT_CPU_FREQ_MHZ_240=y
|
||||
CONFIG_ESPTOOLPY_FLASHFREQ_120M=y
|
||||
CONFIG_SPIRAM=y
|
||||
CONFIG_SPIRAM_BOOT_HW_INIT=y
|
||||
CONFIG_SPIRAM_BOOT_INIT=y
|
||||
CONFIG_SPIRAM_XIP_FROM_PSRAM=y
|
||||
CONFIG_SPIRAM_SPEED_120M=y
|
||||
@@ -0,0 +1,6 @@
|
||||
CONFIG_FREERTOS_USE_TICKLESS_IDLE=y
|
||||
CONFIG_COMPILER_OPTIMIZATION_SIZE=y
|
||||
CONFIG_BOOTLOADER_COMPILER_OPTIMIZATION_SIZE=y
|
||||
CONFIG_COMPILER_OPTIMIZATION_ASSERTIONS_SILENT=y
|
||||
CONFIG_PM_ENABLE=y
|
||||
CONFIG_ESP_DEFAULT_CPU_FREQ_MHZ_160=y
|
||||
@@ -0,0 +1,6 @@
|
||||
CONFIG_FREERTOS_USE_TICKLESS_IDLE=y
|
||||
CONFIG_COMPILER_OPTIMIZATION_SIZE=y
|
||||
CONFIG_BOOTLOADER_COMPILER_OPTIMIZATION_SIZE=y
|
||||
CONFIG_COMPILER_OPTIMIZATION_ASSERTIONS_SILENT=y
|
||||
CONFIG_PM_ENABLE=y
|
||||
CONFIG_ESP_DEFAULT_CPU_FREQ_MHZ_240=y
|
||||
+12
@@ -0,0 +1,12 @@
|
||||
CONFIG_FREERTOS_USE_TICKLESS_IDLE=y
|
||||
CONFIG_COMPILER_OPTIMIZATION_SIZE=y
|
||||
CONFIG_BOOTLOADER_COMPILER_OPTIMIZATION_SIZE=y
|
||||
CONFIG_COMPILER_OPTIMIZATION_ASSERTIONS_SILENT=y
|
||||
CONFIG_PM_ENABLE=y
|
||||
CONFIG_ESP_DEFAULT_CPU_FREQ_MHZ_240=y
|
||||
CONFIG_ESPTOOLPY_FLASHFREQ_120M=y
|
||||
CONFIG_SPIRAM=y
|
||||
CONFIG_SPIRAM_BOOT_HW_INIT=y
|
||||
CONFIG_SPIRAM_BOOT_INIT=y
|
||||
CONFIG_SPIRAM_XIP_FROM_PSRAM=y
|
||||
CONFIG_SPIRAM_SPEED_120M=y
|
||||
+13
@@ -0,0 +1,13 @@
|
||||
CONFIG_FREERTOS_USE_TICKLESS_IDLE=y
|
||||
CONFIG_COMPILER_OPTIMIZATION_SIZE=y
|
||||
CONFIG_BOOTLOADER_COMPILER_OPTIMIZATION_SIZE=y
|
||||
CONFIG_COMPILER_OPTIMIZATION_ASSERTIONS_SILENT=y
|
||||
CONFIG_PM_ENABLE=y
|
||||
CONFIG_ESP_DEFAULT_CPU_FREQ_MHZ_240=y
|
||||
CONFIG_ESPTOOLPY_FLASHFREQ_120M=y
|
||||
CONFIG_SPIRAM=y
|
||||
CONFIG_SPIRAM_BOOT_HW_INIT=y
|
||||
CONFIG_SPIRAM_BOOT_INIT=y
|
||||
CONFIG_SPIRAM_XIP_FROM_PSRAM=y
|
||||
CONFIG_SPIRAM_SPEED_120M=y
|
||||
CONFIG_SPI_FLASH_ROM_IMPL=y
|
||||
@@ -0,0 +1,2 @@
|
||||
CONFIG_PARTITION_TABLE_CUSTOM=y
|
||||
CONFIG_PARTITION_TABLE_CUSTOM_FILENAME="partitions.csv"
|
||||
@@ -303,6 +303,7 @@ SPI Flash Driver
|
||||
- Deprecated API ``spi_flash_dump_counters`` has been removed. Please use :cpp:func:`esp_flash_dump_counters` instead.
|
||||
- Deprecated API ``spi_flash_get_counters`` has been removed. Please use :cpp:func:`esp_flash_get_counters` instead.
|
||||
- Deprecated API ``spi_flash_reset_counters`` has been removed. Please use :cpp:func:`esp_flash_reset_counters` instead.
|
||||
- New argument ``flags`` is added to ``esp_flash_os_functions_t::start``. Caller and implementer should handle this argument properly.
|
||||
- Kconfig option ``CONFIG_SPI_FLASH_ROM_DRIVER_PATCH`` has been removed. Considering that this option is unlikely to be widely used by users and may cause serious issues if misused, it has been decided to remove it.
|
||||
|
||||
.. note::
|
||||
|
||||
@@ -303,6 +303,7 @@ SPI flash 驱动
|
||||
- 已弃用的 API ``spi_flash_dump_counters`` 已被移除。请改用 :cpp:func:`esp_flash_dump_counters`。
|
||||
- 已弃用的 API ``spi_flash_get_counters`` 已被移除。请改用 :cpp:func:`esp_flash_get_counters`。
|
||||
- 已弃用的 API ``spi_flash_reset_counters`` 已被移除。请改用 :cpp:func:`esp_flash_reset_counters`。
|
||||
- ``esp_flash_os_functions_t::start`` 新增了一个参数 ``flags``。调用者和实现者应正确处理此参数。
|
||||
- Kconfig 选项 ``CONFIG_SPI_FLASH_ROM_DRIVER_PATCH`` 已被移除,考虑到这个选项不会被广泛被用户使用,且有因误用而导致出现严重的问题,遂决定移除。
|
||||
|
||||
.. note::
|
||||
|
||||
Reference in New Issue
Block a user