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:
Xiao Xufeng
2025-12-16 17:42:44 +08:00
parent c4fda6cb2f
commit 230ee88d99
50 changed files with 2114 additions and 128 deletions
+31 -6
View File
@@ -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;
}