Files
esp-idf/components/spi_flash/include/memspi_host_driver.h
T
Xiao Xufeng 230ee88d99 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.
2025-12-16 17:42:44 +08:00

186 lines
6.7 KiB
C

/*
* SPDX-FileCopyrightText: 2015-2022 Espressif Systems (Shanghai) CO LTD
*
* SPDX-License-Identifier: Apache-2.0
*/
#pragma once
#include "hal/spi_flash_hal.h"
/** Default configuration for the memspi (high speed version) */
#define ESP_FLASH_DEFAULT_HOST_DRIVER() (spi_flash_host_driver_t) { \
.dev_config = spi_flash_hal_device_config, \
.common_command = spi_flash_hal_common_command, \
.read_id = memspi_host_read_id_hs, \
.erase_chip = spi_flash_hal_erase_chip, \
.erase_sector = spi_flash_hal_erase_sector, \
.erase_block = spi_flash_hal_erase_block, \
.read_status = memspi_host_read_status_hs, \
.set_write_protect = spi_flash_hal_set_write_protect, \
.supports_direct_write = spi_flash_hal_supports_direct_write, \
.supports_direct_read = spi_flash_hal_supports_direct_read, \
.program_page = spi_flash_hal_program_page, \
.write_data_slicer = memspi_host_write_data_slicer, \
.read = spi_flash_hal_read, \
.read_data_slicer = memspi_host_read_data_slicer, \
.host_status = spi_flash_hal_check_status, \
.configure_host_io_mode = spi_flash_hal_configure_host_io_mode, \
.poll_cmd_done = spi_flash_hal_poll_cmd_done, \
.flush_cache = memspi_host_flush_cache, \
.check_suspend = NULL, \
.resume = spi_flash_hal_resume, \
.suspend = spi_flash_hal_suspend,\
.sus_setup = spi_flash_hal_setup_read_suspend,\
}
/// configuration for the memspi host
typedef spi_flash_hal_config_t memspi_host_config_t;
/// context for the memspi host
typedef spi_flash_hal_context_t memspi_host_inst_t;
/**
* Initialize the memory SPI host.
*
* @param host Pointer to the host structure.
* @param cfg Pointer to configuration structure
*
* @return always return ESP_OK
*/
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
* The HAL is not public api, don't use in application code.
* See readme.md in hal/include/hal/readme.md
******************************************************************************/
/**
* @brief Read the Status Register read from RDSR (05h).
*
* High speed implementation of RDID through memspi interface relying on the
* ``common_command``.
*
* @param host The driver context.
* @param id Output of the read ID from the slave.
*
* @return
* - ESP_OK: if success
* - ESP_ERR_FLASH_NO_RESPONSE: if no response from chip
* - or other cases from ``spi_hal_common_command``
*/
esp_err_t memspi_host_read_id_hs(spi_flash_host_inst_t *host, uint32_t *id);
/**
* High speed implementation of RDSR through memspi interface relying on the
* ``common_command``.
*
* @param host The driver context.
* @param id Output of the read ID from the slave.
*
* @return
* - ESP_OK: if success
* - or other cases from ``spi_hal_common_command``
*/
esp_err_t memspi_host_read_status_hs(spi_flash_host_inst_t *host, uint8_t *out_sr);
/**
* Flush the cache (if needed) after the contents are modified.
*
* @param host The driver context.
* @param addr Start address of the modified region
* @param size Size of the region modified.
*
* @return always ESP_OK.
*/
esp_err_t memspi_host_flush_cache(spi_flash_host_inst_t *host, uint32_t addr, uint32_t size);
/**
* Erase contents of entire chip.
*
* @param host The driver context.
*/
void memspi_host_erase_chip(spi_flash_host_inst_t *host);
/**
* Erase a sector starting from a given address. For 24bit address only.
*
* @param host The driver context.
* @param start_address Starting address of the sector.
*/
void memspi_host_erase_sector(spi_flash_host_inst_t *host, uint32_t start_address);
/**
* Erase a block starting from a given address. For 24bit address only.
*
* @param host The driver context.
* @param start_address Starting address of the block.
*/
void memspi_host_erase_block(spi_flash_host_inst_t *host, uint32_t start_address);
/**
* Program a page with contents of a buffer. For 24bit address only.
*
* @param host The driver context.
* @param buffer Buffer which contains the data to be flashed.
* @param address Starting address of where to flash the data.
* @param length The number of bytes to flash.
*/
void memspi_host_program_page(spi_flash_host_inst_t *host, const void *buffer, uint32_t address, uint32_t length);
/**
* Set ability to write to chip.
*
* @param host The driver context.
* @param wp Enable or disable write protect (true - enable, false - disable).
*/
esp_err_t memspi_host_set_write_protect(spi_flash_host_inst_t *host, bool wp);
/**
* Read data to buffer.
*
* @param host The driver context.
* @param buffer Buffer which contains the data to be read.
* @param address Starting address of where to read the data.
* @param length The number of bytes to read.
*/
esp_err_t memspi_host_read(spi_flash_host_inst_t *host, void *buffer, uint32_t address, uint32_t read_len);
/**
* @brief Slicer for read data used in non-encrypted regions. This slicer does nothing but
* limit the length to the maximum size the host supports.
*
* @param address Flash address to read
* @param len Length to read
* @param align_address Output of the address to read, should be equal to the input `address`
* @param page_size Physical SPI flash page size
*
* @return Length that can actually be read in one `read` call in `spi_flash_host_driver_t`.
*/
int memspi_host_read_data_slicer(spi_flash_host_inst_t *host, uint32_t address, uint32_t len, uint32_t *align_address, uint32_t page_size);
/**
* @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 across the page boundary
* (256 bytes)
*
* @param address Flash address to write
* @param len Length to write
* @param align_address Output of the address to write, should be equal to the input `address`
* @param page_size Physical SPI flash page size
*
* @return Length that can actually be written in one `program_page` call in `spi_flash_host_driver_t`.
*/
int memspi_host_write_data_slicer(spi_flash_host_inst_t *host, uint32_t address, uint32_t len, uint32_t *align_address, uint32_t page_size);