refactor(spi_flash): Refactor gpspi flash for making it's clock accurate

This commit is contained in:
C.S.M
2025-07-17 16:35:09 +08:00
parent f1ebe5d1ce
commit 332614165b
25 changed files with 552 additions and 15 deletions

View File

@@ -26,6 +26,9 @@
#include "esp_rom_spiflash.h"
#include "bootloader_flash.h"
#include "esp_check.h"
#include "esp_private/esp_clk_tree_common.h"
#include "clk_ctrl_os.h"
#include "soc/soc_caps.h"
__attribute__((unused)) static const char TAG[] = "spi_flash";
@@ -37,6 +40,12 @@ __attribute__((unused)) static const char TAG[] = "spi_flash";
#error "Flash chip size equal or over 32MB memory cannot use driver in ROM"
#endif
#if SOC_PERIPH_CLK_CTRL_SHARED
#define GPSPI_FLASH_RCC_CLOCK_ATOMIC() PERIPH_RCC_ATOMIC()
#else
#define GPSPI_FLASH_RCC_CLOCK_ATOMIC()
#endif
/* This pointer is defined in ROM and extern-ed on targets where CONFIG_SPI_FLASH_ROM_IMPL = y*/
#if !CONFIG_SPI_FLASH_ROM_IMPL
esp_flash_t *esp_flash_default_chip = NULL;
@@ -230,6 +239,120 @@ static esp_err_t acquire_spi_device(const esp_flash_spi_device_config_t *config,
return ret;
}
#if GPSPI_FLASH_LL_SUPPORT_CLK_SRC_PRE_DIV
static uint32_t s_spi_find_clock_src_pre_div(uint32_t src_freq, uint32_t target_freq)
{
// pre division must be even and at least 2
uint32_t min_div = ((src_freq / GPSPI_FLASH_LL_PERIPHERAL_FREQUENCY_MHZ) + 1) & (~0x01UL);
min_div = min_div < 2 ? 2 : min_div;
uint32_t total_div = src_freq / target_freq;
// Loop the `div` to find a divisible value of `total_div`
for (uint32_t pre_div = min_div; pre_div <= total_div; pre_div += 2) {
if ((total_div % pre_div) || (total_div / pre_div) > GPSPI_FLASH_LL_PERIPH_CLK_DIV_MAX) {
continue;
}
return pre_div;
}
return min_div;
}
#endif //GPSPI_FLASH_LL_SUPPORT_CLK_SRC_PRE_DIV
/**
* Configure GPSPI clock source and frequency for flash device
*
* @param config Flash device configuration
* @return Clock source frequency in MHz
*/
static uint32_t init_gpspi_clock(esp_flash_t *chip, const esp_flash_spi_device_config_t *config)
{
#if !CONFIG_IDF_TARGET_ESP32
// Get clock source frequency
uint32_t clk_src_freq = 0;
spi_clock_source_t clk_src = config->clock_source ? config->clock_source : SPI_CLK_SRC_DEFAULT;
#if SOC_SPI_SUPPORT_CLK_RC_FAST
if (config->clock_source == SPI_CLK_SRC_RC_FAST) {
periph_rtc_dig_clk8m_enable();
}
#endif
esp_clk_tree_enable_src(clk_src, true);
esp_clk_tree_src_get_freq_hz(clk_src, ESP_CLK_TREE_SRC_FREQ_PRECISION_CACHED, &clk_src_freq);
// Enable GPSPI clock
GPSPI_FLASH_RCC_CLOCK_ATOMIC() {
gpspi_flash_ll_enable_clock(spi_flash_ll_get_hw(config->host_id), true);
gpspi_flash_ll_set_clk_source(spi_flash_ll_get_hw(config->host_id), clk_src);
}
// Store clock source in chip for later cleanup
chip->clock_source = clk_src;
// Calculate final clock source frequency
uint32_t final_freq_mhz;
#if GPSPI_FLASH_LL_SUPPORT_CLK_SRC_PRE_DIV
uint32_t pre_div = s_spi_find_clock_src_pre_div(clk_src_freq, GPSPI_FLASH_LL_PERIPHERAL_FREQUENCY_MHZ * 1000 * 1000);
gpspi_flash_ll_clk_source_pre_div(spi_flash_ll_get_hw(config->host_id), pre_div / 2, 2);
final_freq_mhz = clk_src_freq / (1 * 1000 * 1000) / (pre_div);
#else
final_freq_mhz = clk_src_freq / (1 * 1000 * 1000);
#endif
return final_freq_mhz;
#else
// Do nothing for ESP32
return SPI_FLASH_LL_CLOCK_FREQUENCY_MHZ;
#endif // !CONFIG_IDF_TARGET_ESP32
}
#if !CONFIG_IDF_TARGET_ESP32
/**
* Get host_id from esp_flash_t chip pointer
*
* @param chip Flash chip pointer
* @return host_id or -1 if invalid
*/
static int get_host_id_from_chip(esp_flash_t *chip)
{
if (!chip || !chip->host) {
return -1;
}
spi_flash_hal_context_t* ctx = (spi_flash_hal_context_t*)chip->host;
return spi_flash_ll_hw_get_id(ctx->spi);
}
#endif // !CONFIG_IDF_TARGET_ESP32
static void deinit_gpspi_clock(esp_flash_t *chip)
{
#if !CONFIG_IDF_TARGET_ESP32
if (!chip) {
return;
}
int host_id = get_host_id_from_chip(chip);
if (host_id < 0) {
return;
}
// Disable GPSPI clock
GPSPI_FLASH_RCC_CLOCK_ATOMIC() {
gpspi_flash_ll_enable_clock(spi_flash_ll_get_hw(host_id), false);
}
// Disable the clock source
esp_clk_tree_enable_src(chip->clock_source, false);
#if SOC_SPI_SUPPORT_CLK_RC_FAST
// Disable RC_FAST clock if it was used
if (chip->clock_source == SPI_CLK_SRC_RC_FAST) {
periph_rtc_dig_clk8m_disable();
}
#endif
#endif // !CONFIG_IDF_TARGET_ESP32
}
esp_err_t spi_bus_add_flash_device(esp_flash_t **out_chip, const esp_flash_spi_device_config_t *config)
{
if (out_chip == NULL) {
@@ -286,7 +409,8 @@ esp_err_t spi_bus_add_flash_device(esp_flash_t **out_chip, const esp_flash_spi_d
.freq_mhz = config->freq_mhz,
};
host_cfg.clock_src_freq = spi_flash_ll_get_source_clock_freq_mhz(host_cfg.host_id);
// Init the gpspi clock
host_cfg.clock_src_freq = init_gpspi_clock(chip, config);
err = memspi_host_init_pointers(host, &host_cfg);
if (err != ESP_OK) {
@@ -310,6 +434,9 @@ esp_err_t spi_bus_remove_flash_device(esp_flash_t *chip)
return ESP_ERR_INVALID_ARG;
}
// Disable GPSPI clocks before cleanup
deinit_gpspi_clock(chip);
spi_bus_lock_dev_handle_t dev_handle = NULL;
esp_flash_deinit_os_functions(chip, &dev_handle);
if (dev_handle) {