Merge branch 'bugfix/add_support_for_mspi_to_work_with_cpu_clock_switch' into 'master'

mspi: make cpu clock source switch safe

Closes IDFCI-902

See merge request espressif/esp-idf!15557
This commit is contained in:
Michael (XIAO Xufeng) 2021-10-20 08:21:53 +00:00
commit 390f71cbcb
8 changed files with 81 additions and 70 deletions

View File

@ -21,18 +21,7 @@
#include "esp_attr.h"
#include "esp_efuse.h"
#include "esp_efuse_table.h"
#ifndef BOOTLOADER_BUILD
/**
* TODO: IDF-3204
* Temporarily solution. Depends on MSPI
* Final solution: the rtc should not depend on MSPI. We should do rtc related before Flash init
*/
#include "esp_private/spi_flash_os.h"
#include "esp32s3/rom/cache.h"
#include "freertos/portmacro.h"
portMUX_TYPE rtc_init_spinlock = (portMUX_TYPE)portMUX_INITIALIZER_UNLOCKED;
#endif
#define RTC_CNTL_MEM_FORCE_NOISO (RTC_CNTL_SLOWMEM_FORCE_NOISO | RTC_CNTL_FASTMEM_FORCE_NOISO)
@ -248,39 +237,23 @@ static void set_ocode_by_efuse(int calib_version)
REGI2C_WRITE_MASK(I2C_ULP, I2C_ULP_IR_FORCE_CODE, 1);
}
#ifndef BOOTLOADER_BUILD
//TODO: IDF-3204
//Temporary solution, these 2 functions should be defined elsewhere, because similar operations are also needed elsewhere
//Final solution: the rtc should not depend on MSPI. We should do rtc related before Flash init
static void IRAM_ATTR enter_mspi_low_speed_mode_safe(void)
{
portENTER_CRITICAL(&rtc_init_spinlock);
Cache_Freeze_ICache_Enable(1);
Cache_Freeze_DCache_Enable(1);
spi_timing_enter_mspi_low_speed_mode(false);
Cache_Freeze_DCache_Disable();
Cache_Freeze_ICache_Disable();
portEXIT_CRITICAL(&rtc_init_spinlock);
}
static void IRAM_ATTR enter_mspi_high_speed_mode_safe(void)
{
portENTER_CRITICAL(&rtc_init_spinlock);
Cache_Freeze_ICache_Enable(1);
Cache_Freeze_DCache_Enable(1);
spi_timing_enter_mspi_high_speed_mode(false);
Cache_Freeze_DCache_Disable();
Cache_Freeze_ICache_Disable();
portEXIT_CRITICAL(&rtc_init_spinlock);
}
#endif
//TODO: IDF-3204
//This function will change the system clock source to XTAL. Under lower frequency (e.g. XTAL), MSPI timing tuning configures should be modified accordingly.
static void IRAM_ATTR calibrate_ocode(void)
/**
* TODO: IDF-4141
* 1. This function will change the system clock source to XTAL. Under lower frequency (e.g. XTAL), MSPI timing tuning configures should be modified accordingly.
* 2. RTC related should be done before SPI0 initialisation
*/
static void calibrate_ocode(void)
{
#ifndef BOOTLOADER_BUILD
enter_mspi_low_speed_mode_safe();
/**
* Background:
* 1. Following code will switch the system clock to XTAL first, to self-calibrate the OCode.
* 2. For some of the MSPI high frequency setting (e.g. 80M DDR mode Flash or PSRAM), timing tuning is required.
* Certain delay will be added to the MSPI RX direction.
*
* When CPU clock switches down, the delay should be cleared. Therefore here we call this function to remove the delays.
*/
spi_timing_change_speed_mode_cache_safe(true);
#endif
/*
Bandgap output voltage is not precise when calibrate o-code by hardware sometimes, so need software o-code calibration (must turn off PLL).
@ -330,6 +303,7 @@ static void IRAM_ATTR calibrate_ocode(void)
}
rtc_clk_cpu_freq_set_config(&old_config);
#ifndef BOOTLOADER_BUILD
enter_mspi_high_speed_mode_safe();
//System clock is switched back to PLL. Here we switch to the MSPI high speed mode, add the delays back
spi_timing_change_speed_mode_cache_safe(false);
#endif
}

View File

@ -359,25 +359,20 @@ void spi_timing_config_psram_tune_dummy(uint8_t extra_dummy)
#endif //#if SPI_TIMING_FLASH_NEEDS_TUNING || SPI_TIMING_PSRAM_NEEDS_TUNING
static bool spi_timing_config_cs_setup_enable(void)
/*-------------------------------------------------------------------------------------------------
* To let upper lay (spi_flash_timing_tuning.c) to know the necessary timing registers
*-------------------------------------------------------------------------------------------------*/
static bool s_get_cs_setup_enable(void)
{
return REG_GET_BIT(SPI_MEM_USER_REG(0), SPI_MEM_CS_SETUP);
}
static bool spi_timing_config_cs_hold_enable(void)
static bool s_get_cs_hold_enable(void)
{
return REG_GET_BIT(SPI_MEM_USER_REG(0), SPI_MEM_CS_HOLD);
}
bool spi_timine_config_flash_is_tuned(void)
{
#if SPI_TIMING_FLASH_NEEDS_TUNING || SPI_TIMING_PSRAM_NEEDS_TUNING
return true;
#else
return false;
#endif
}
/**
* Get the SPI1 Flash CS timing setting. The setup time and hold time are both realistic cycles.
* @note On ESP32-S3, SPI0/1 share the Flash CS timing registers. Therefore, we should not change these values.
@ -392,12 +387,12 @@ void spi_timing_config_get_cs_timing(uint8_t *setup_time, uint32_t *hold_time)
* The logic here is, if setup_en / hold_en is false, then we return the realistic cycle number,
* which is 0. If true, then the realistic cycle number is (reg_value + 1)
*/
if (spi_timing_config_cs_setup_enable()) {
if (s_get_cs_setup_enable()) {
*setup_time += 1;
} else {
*setup_time = 0;
}
if (spi_timing_config_cs_hold_enable()) {
if (s_get_cs_hold_enable()) {
*hold_time += 1;
} else {
*hold_time = 0;

View File

@ -245,7 +245,7 @@ void spi_timing_config_psram_tune_dummy(uint8_t extra_dummy);
/**
* SPI1 register info get APIs. These APIs inform `spi_flash_timing_tuning.c` (driver layer) of the SPI1 flash settings.
* In this way, other components (e.g.: esp_flash driver) can get the info from it.
* In this way, other components (e.g.: esp_flash driver) can get the info from it (`spi_flash_timing_tuning.c`).
*/
void spi_timing_config_get_cs_timing(uint8_t *setup_time, uint32_t *hold_time);
uint32_t spi_timing_config_get_flash_clock_reg(void);

View File

@ -296,7 +296,7 @@ esp_err_t esp_flash_init_default_chip(void)
// For chips need time tuning, get value directely from system here.
#if SOC_SPI_MEM_SUPPORT_TIME_TUNING
if (spi_timine_config_flash_is_tuned()) {
if (spi_timing_is_tuned()) {
cfg.using_timing_tuning = 1;
spi_timing_get_flash_timing_param(&cfg.timing_reg);
}

View File

@ -45,17 +45,26 @@ extern "C" {
esp_err_t spi_flash_init_chip_state(void);
/**
* @brief Make MSPI work under 20Mhz
* @brief Make MSPI work under 20Mhz, remove the timing tuning required delays.
* @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
*/
void spi_timing_enter_mspi_low_speed_mode(bool control_spi1);
/**
* @brief Make MSPI work under the frequency as users set
* @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
*/
void spi_timing_enter_mspi_high_speed_mode(bool control_spi1);
/**
* @brief Switch MSPI into low speed mode / high speed mode.
* @note This API is cache safe, it will freeze both D$ and I$ and restore them after MSPI is switched
* @note For some of the MSPI high frequency settings (e.g. 80M DDR mode Flash or PSRAM), timing tuning is required.
* Certain delays will be added to the MSPI RX direction. When CPU clock switches from PLL to XTAL, should call
* this API first to enter MSPI low speed mode to remove the delays, and vice versa.
*/
void spi_timing_change_speed_mode_cache_safe(bool switch_down);
/**
* @brief Tune MSPI flash timing to make it work under high frequency
*/
@ -90,9 +99,9 @@ esp_err_t esp_flash_init_main(esp_flash_t *chip);
void spi_timing_get_flash_timing_param(spi_flash_hal_timing_config_t *out_timing_config);
/**
* @brief Judge if the flash in tuned
* @brief Get the knowledge if the MSPI timing is tuned or not
*/
bool spi_timine_config_flash_is_tuned(void);
bool spi_timing_is_tuned(void);
/**
* @brief Set Flash chip specifically required MSPI register settings here

View File

@ -17,6 +17,7 @@
#include "soc/soc.h"
#if CONFIG_IDF_TARGET_ESP32S3
#include "esp32s3/spi_timing_config.h"
#include "esp32s3/rom/cache.h"
#endif
#define ARRAY_SIZE(arr) (sizeof(arr)/sizeof(*(arr)))
@ -377,6 +378,7 @@ void spi_timing_psram_tuning(void)
}
#endif //SPI_TIMING_PSRAM_NEEDS_TUNING
/*------------------------------------------------------------------------------
* APIs to make SPI0 (and SPI1) FLASH work for high/low freq
*----------------------------------------------------------------------------*/
@ -460,9 +462,33 @@ void spi_timing_enter_mspi_high_speed_mode(bool control_spi1)
#endif
}
/**
* Should be only used by SPI1 Flash driver to know the necessary timing registers
*/
void spi_timing_change_speed_mode_cache_safe(bool switch_down)
{
Cache_Freeze_ICache_Enable(1);
Cache_Freeze_DCache_Enable(1);
if (switch_down) {
//enter MSPI low speed mode, extra delays should be removed
spi_timing_enter_mspi_low_speed_mode(false);
} else {
//enter MSPI high speed mode, extra delays should be considered
spi_timing_enter_mspi_high_speed_mode(false);
}
Cache_Freeze_DCache_Disable();
Cache_Freeze_ICache_Disable();
}
/*------------------------------------------------------------------------------
* APIs to inform SPI1 Flash driver of necessary timing configurations
*----------------------------------------------------------------------------*/
bool spi_timing_is_tuned(void)
{
#if SPI_TIMING_FLASH_NEEDS_TUNING || SPI_TIMING_PSRAM_NEEDS_TUNING
return true;
#else
return false;
#endif
}
#if SPI_TIMING_FLASH_NEEDS_TUNING || SPI_TIMING_PSRAM_NEEDS_TUNING
void spi_timing_get_flash_timing_param(spi_flash_hal_timing_config_t *out_timing_config)
{

View File

@ -17,7 +17,6 @@
#include "esp32s3/rom/opi_flash.h"
#endif
const static char *TAG = "SPI0";
//-----------------------------------------SPI0 PSRAM TEST-----------------------------------------------//
#if CONFIG_SPIRAM
@ -80,8 +79,7 @@ extern void spi_flash_enable_interrupts_caches_and_other_cpu(void);
static DRAM_ATTR uint8_t rd_buf[SPI1_FLASH_TEST_LEN];
static DRAM_ATTR uint8_t wr_buf[SPI1_FLASH_TEST_LEN];
static IRAM_ATTR esp_err_t spi1_flash_test(void)
static NOINLINE_ATTR IRAM_ATTR esp_err_t spi1_flash_test(void)
{
printf(DRAM_STR("----------SPI1 Flash Test----------\n"));
@ -124,7 +122,7 @@ static IRAM_ATTR esp_err_t spi1_flash_test(void)
#define SPI0_FLASH_TEST_BUF {0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0A, 0x0B, 0x0C, 0x0D, 0x0E, 0x0F, \
0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, 0x18, 0x19, 0x1A, 0x1B, 0x1C, 0x1D, 0x1E, 0x1F}
uint8_t flash_rd_buf[SPI0_FLASH_TEST_LEN] __attribute__((section (".flash.rodata"))) = SPI0_FLASH_TEST_BUF;
static const uint8_t flash_rd_buf[SPI0_FLASH_TEST_LEN] = SPI0_FLASH_TEST_BUF;
extern int _flash_rodata_start;
extern int _rodata_reserved_end;
@ -133,7 +131,7 @@ static IRAM_ATTR esp_err_t spi0_flash_test(void)
{
printf("----------SPI0 Flash Test----------\n");
//Check if the flash_rd_buf is in .rodata
ESP_RETURN_ON_ERROR(((intptr_t)flash_rd_buf >= (intptr_t)_flash_rodata_start) && ((intptr_t)flash_rd_buf < (intptr_t)_rodata_reserved_end), TAG, "psram_rd_buf not in rodata");
assert(((intptr_t)flash_rd_buf >= (intptr_t)&_flash_rodata_start) && ((intptr_t)flash_rd_buf < (intptr_t)&_rodata_reserved_end));
uint8_t cmp_buf[SPI0_FLASH_TEST_LEN] = SPI0_FLASH_TEST_BUF;

View File

@ -0,0 +1,9 @@
# Legacy, F4R4, Flash 120M SDR, PSRAM disable, compiler -Os and silent
CONFIG_COMPILER_OPTIMIZATION_SIZE=y
CONFIG_BOOTLOADER_COMPILER_OPTIMIZATION_SIZE=y
CONFIG_COMPILER_OPTIMIZATION_ASSERTIONS_SILENT=y
CONFIG_SPI_FLASH_USE_LEGACY_IMPL=y
CONFIG_ESPTOOLPY_FLASHFREQ_120M=y
CONFIG_ESPTOOLPY_FLASHSIZE_4MB=y