mirror of
https://github.com/espressif/esp-idf.git
synced 2026-10-01 18:50:34 +03: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:
@@ -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)
|
||||
|
||||
Reference in New Issue
Block a user