diff --git a/components/esp_hw_support/CMakeLists.txt b/components/esp_hw_support/CMakeLists.txt index 9d25fc6f39..9f1dd7912e 100644 --- a/components/esp_hw_support/CMakeLists.txt +++ b/components/esp_hw_support/CMakeLists.txt @@ -98,8 +98,11 @@ if(NOT BOOTLOADER_BUILD) list(APPEND srcs "modem_clock.c") endif() - if(CONFIG_SOC_MEMSPI_SRC_FREQ_120M) - list(APPEND srcs "mspi_timing_tuning.c" "port/${target}/mspi_timing_config.c") + if(NOT CONFIG_APP_BUILD_TYPE_PURE_RAM_APP) + list(APPEND srcs "mspi_timing_tuning.c" "mspi_timing_config.c") + if(CONFIG_SOC_MEMSPI_TIMING_TUNING_BY_MSPI_DELAY) + list(APPEND srcs "mspi_timing_by_mspi_delay.c") + endif() endif() if(CONFIG_SOC_RTC_FAST_MEM_SUPPORTED) diff --git a/components/esp_hw_support/include/esp_private/mspi_timing_types.h b/components/esp_hw_support/include/esp_private/mspi_timing_types.h new file mode 100644 index 0000000000..488ee11174 --- /dev/null +++ b/components/esp_hw_support/include/esp_private/mspi_timing_types.h @@ -0,0 +1,30 @@ +/* + * SPDX-FileCopyrightText: 2023 Espressif Systems (Shanghai) CO LTD + * + * SPDX-License-Identifier: Apache-2.0 + */ + +/** + * @brief + * This file is for MSPI timinig tuning private APIs + */ + +#pragma once + +#ifdef __cplusplus +extern "C" { +#endif + +/** + * @brief MSPI Timing speed mode + */ +typedef enum { + MSPI_TIMING_SPEED_MODE_LOW_PERF, /*!< Low performance speed mode, this mode is safe for all the scenarios, + unless the MSPI attached devices (Flash, PSRAM) are powered down. + As a tradeoff, the performance of the MSPI (devices) are swithed to a very low speed */ + MSPI_TIMING_SPEED_MODE_NORMAL_PERF, /*!< Normal performance speed mode, MSPI speed is the same as you configured in menuconfig */ +} mspi_timing_speed_mode_t; + +#ifdef __cplusplus +} +#endif diff --git a/components/esp_hw_support/linker.lf b/components/esp_hw_support/linker.lf index 4680358c2c..d6fa558fd0 100644 --- a/components/esp_hw_support/linker.lf +++ b/components/esp_hw_support/linker.lf @@ -38,6 +38,8 @@ entries: gdma: gdma_reset (noflash) if SOC_SYSTIMER_SUPPORTED = y: systimer (noflash) - if SOC_MEMSPI_SRC_FREQ_120M = y: + if APP_BUILD_TYPE_PURE_RAM_APP = n: mspi_timing_tuning (noflash) mspi_timing_config (noflash) + if SOC_MEMSPI_TIMING_TUNING_BY_MSPI_DELAY = y: + mspi_timing_by_mspi_delay (noflash) diff --git a/components/esp_hw_support/mspi_timing_by_mspi_delay.c b/components/esp_hw_support/mspi_timing_by_mspi_delay.c new file mode 100644 index 0000000000..abd26d4019 --- /dev/null +++ b/components/esp_hw_support/mspi_timing_by_mspi_delay.c @@ -0,0 +1,615 @@ +/* + * SPDX-FileCopyrightText: 2023 Espressif Systems (Shanghai) CO LTD + * + * SPDX-License-Identifier: Apache-2.0 + */ + +/** + * @brief + * + * This file contains configuration APIs doing MSPI timing tuning by MSPI delay + * This file will only be built, when `SOC_MEMSPI_TIMING_TUNING_BY_MSPI_DELAY == 1` + */ + +#include +#include "sdkconfig.h" +#include "string.h" +#include "esp_attr.h" +#include "esp_err.h" +#include "esp_types.h" +#include "esp_log.h" +#include "soc/rtc.h" +#include "hal/mspi_timing_tuning_ll.h" +#include "hal/clk_tree_ll.h" +#include "hal/regi2c_ctrl_ll.h" +#include "mspi_timing_config.h" +#include "mspi_timing_by_mspi_delay.h" +#include "bootloader_flash.h" +#include "esp32s3/rom/spi_flash.h" +#include "esp32s3/rom/opi_flash.h" + +#define OPI_PSRAM_SYNC_READ 0x0000 +#define OPI_PSRAM_SYNC_WRITE 0x8080 +#define OCT_PSRAM_RD_DUMMY_NUM (2*(10-1)) +#define OCT_PSRAM_WR_DUMMY_NUM (2*(5-1)) + +#define QPI_PSRAM_FAST_READ 0XEB +#define QPI_PSRAM_WRITE 0X38 +#define QPI_PSRAM_FAST_READ_DUMMY 6 + +#define NOT_INIT_INT 127 + +/////////////////////////////////////////TIMING TUNING IS NEEDED////////////////////////////////////////////// +////////////////////////////////////////////////////////////////////////////////////////////////////////////// + +#if MSPI_TIMING_FLASH_NEEDS_TUNING || MSPI_TIMING_PSRAM_NEEDS_TUNING +const static char *TAG = "MSPI Timing"; + +//If one of the FLASH / PSRAM or both of them need timing tuning, we should build following code +typedef enum { + PSRAM_CMD_QPI, + PSRAM_CMD_SPI, +} psram_cmd_mode_t; + +static uint8_t s_rom_flash_extra_dummy[2] = {NOT_INIT_INT, NOT_INIT_INT}; + +#if CONFIG_SPIRAM_MODE_QUAD +static uint8_t s_psram_extra_dummy; +extern void psram_exec_cmd(int spi_num, psram_cmd_mode_t mode, + uint32_t cmd, int cmd_bit_len, + uint32_t addr, int addr_bit_len, + int dummy_bits, + uint8_t* mosi_data, int mosi_bit_len, + uint8_t* miso_data, int miso_bit_len, + uint32_t cs_mask, + bool is_write_erase_operation); +#endif + +//-------------------------------------FLASH timing tuning register config-------------------------------------// +void mspi_timing_get_flash_tuning_configs(mspi_timing_config_t *config) +{ +#if MSPI_TIMING_FLASH_DTR_MODE +#define FLASH_MODE DTR_MODE +#else //MSPI_TIMING_FLASH_STR_MODE +#define FLASH_MODE STR_MODE +#endif + +#if CONFIG_ESPTOOLPY_FLASHFREQ_80M + *config = MSPI_TIMING_FLASH_GET_TUNING_CONFIG(MSPI_TIMING_CORE_CLOCK_MHZ, 80, FLASH_MODE); +#elif CONFIG_ESPTOOLPY_FLASHFREQ_120M + *config = MSPI_TIMING_FLASH_GET_TUNING_CONFIG(MSPI_TIMING_CORE_CLOCK_MHZ, 120, FLASH_MODE); +#else + assert(false && "should never reach here"); +#endif + +#undef FLASH_MODE +} + +void mspi_timing_flash_init(uint32_t flash_freq_mhz) +{ + mspi_timing_config_set_flash_clock(flash_freq_mhz, MSPI_TIMING_SPEED_MODE_NORMAL_PERF, true); + + //Power on HCLK + mspi_timinng_ll_enable_flash_hclk(0); +} + +static void s_set_flash_din_mode_num(uint8_t spi_num, uint8_t din_mode, uint8_t din_num) +{ + mspi_timing_ll_set_flash_din_mode(spi_num, din_mode); + mspi_timing_ll_set_flash_din_num(spi_num, din_num); +} + +static uint32_t spi_timing_config_get_dummy(void) +{ + mspi_timing_ll_flash_mode_t mode = mspi_timing_ll_get_flash_mode(0); + if (mode == MSPI_TIMING_LL_FLASH_OPI_MODE) { + abort(); + } + +#if CONFIG_SPI_FLASH_HPM_ENABLE + if (spi_flash_hpm_dummy_adjust()) { // HPM is enabled + const spi_flash_hpm_dummy_conf_t *hpm_dummy = spi_flash_hpm_get_dummy(); + switch (mode) { + case MSPI_TIMING_LL_FLASH_QIO_MODE: + return hpm_dummy->qio_dummy - 1; + case MSPI_TIMING_LL_FLASH_QUAD_MODE: + return hpm_dummy->qout_dummy - 1; + case MSPI_TIMING_LL_FLASH_DIO_MODE: + return hpm_dummy->dio_dummy - 1; + case MSPI_TIMING_LL_FLASH_DUAL_MODE: + return hpm_dummy->dout_dummy - 1; + case MSPI_TIMING_LL_FLASH_FAST_MODE: + return hpm_dummy->fastrd_dummy - 1; + case MSPI_TIMING_LL_FLASH_SLOW_MODE: + return 0; + default: + abort(); + } + } else +#endif + { // HPM is not enabled + switch (mode) { + case MSPI_TIMING_LL_FLASH_QIO_MODE: + return SPI1_R_QIO_DUMMY_CYCLELEN; + case MSPI_TIMING_LL_FLASH_QUAD_MODE: + return SPI1_R_FAST_DUMMY_CYCLELEN; + case MSPI_TIMING_LL_FLASH_DIO_MODE: + return SPI1_R_DIO_DUMMY_CYCLELEN; + case MSPI_TIMING_LL_FLASH_DUAL_MODE: + return SPI1_R_FAST_DUMMY_CYCLELEN; + case MSPI_TIMING_LL_FLASH_FAST_MODE: + return SPI1_R_FAST_DUMMY_CYCLELEN; + case MSPI_TIMING_LL_FLASH_SLOW_MODE: + return 0; + default: + abort(); + } + } +} + +static void s_set_flash_extra_dummy(uint8_t spi_num, uint8_t extra_dummy) +{ + if (bootloader_flash_is_octal_mode_enabled()) { + mspi_timing_ll_set_octal_flash_extra_dummy(spi_num, extra_dummy); + return; + } + /** + * HW workaround: + * The `SPI_MEM_TIMING_CALI_REG` register is only used for OPI on 728 + * Here we only need to update this global variable for extra dummy. Since we use the ROM Flash API, which will set the dummy based on this. + * We only initialise the SPI0. And leave the SPI1 for flash driver to configure. + */ + if (s_rom_flash_extra_dummy[spi_num] == NOT_INIT_INT) { + s_rom_flash_extra_dummy[spi_num] = g_rom_spiflash_dummy_len_plus[spi_num]; + } + g_rom_spiflash_dummy_len_plus[spi_num] = s_rom_flash_extra_dummy[spi_num] + extra_dummy; + + // Only Quad Flash will run into this branch. + uint32_t dummy = spi_timing_config_get_dummy(); + mspi_timing_ll_set_quad_flash_dummy(spi_num, dummy + g_rom_spiflash_dummy_len_plus[spi_num]); +} + +void mspi_timing_config_flash_set_tuning_regs(const void *timing_params) +{ + const mspi_timing_tuning_param_t *params = (const mspi_timing_tuning_param_t *)timing_params; + /** + * 1. SPI_MEM_DINx_MODE(1), SPI_MEM_DINx_NUM(1) are meaningless + * SPI0 and SPI1 share the SPI_MEM_DINx_MODE(0), SPI_MEM_DINx_NUM(0) for FLASH timing tuning + * 2. We use SPI1 to get the best Flash timing tuning (mode and num) config + */ + s_set_flash_din_mode_num(0, params->spi_din_mode, params->spi_din_num); + s_set_flash_extra_dummy(1, params->extra_dummy_len); +} + +//-------------------------------------------FLASH Read/Write------------------------------------------// +void mspi_timing_config_flash_read_data(uint8_t *buf, uint32_t addr, uint32_t len) +{ + if (bootloader_flash_is_octal_mode_enabled()) { + // note that in spi_flash_read API, there is a wait-idle stage, since flash can only be read in idle state. + // but after we change the timing settings, we might not read correct idle status via RDSR. + // so, here we should use a read API that won't check idle status. + mspi_timing_ll_clear_fifo(1); + esp_rom_opiflash_read_raw(addr, buf, len); + } else { + esp_rom_spiflash_read(addr, (uint32_t *)buf, len); + } +} + +//-------------------------------------PSRAM timing tuning register config-------------------------------------// +void mspi_timing_get_psram_tuning_configs(mspi_timing_config_t *config) +{ +#if MSPI_TIMING_PSRAM_DTR_MODE +#define PSRAM_MODE DTR_MODE +#else //MSPI_TIMING_PSRAM_STR_MODE +#define PSRAM_MODE STR_MODE +#endif + +#if CONFIG_SPIRAM_SPEED_80M + *config = MSPI_TIMING_PSRAM_GET_TUNING_CONFIG(MSPI_TIMING_CORE_CLOCK_MHZ, 80, PSRAM_MODE); +#elif CONFIG_SPIRAM_SPEED_120M + *config = MSPI_TIMING_PSRAM_GET_TUNING_CONFIG(MSPI_TIMING_CORE_CLOCK_MHZ, 120, PSRAM_MODE); +#else + assert(false && "should never reach here"); +#endif + +#undef PSRAM_MODE +} + +void mspi_timing_psram_init(uint32_t psram_freq_mhz) +{ + mspi_timing_config_set_flash_clock(psram_freq_mhz, MSPI_TIMING_SPEED_MODE_NORMAL_PERF, true); + + //Power on HCLK + mspi_timinng_ll_enable_psram_hclk(0); +} + +static void s_set_psram_din_mode_num(uint8_t spi_num, uint8_t din_mode, uint8_t din_num) +{ + mspi_timing_ll_set_psram_din_mode(spi_num, din_mode); + mspi_timing_ll_set_psram_din_num(spi_num, din_num); +} + +static void s_set_psram_extra_dummy(uint8_t spi_num, uint8_t extra_dummy) +{ +#if CONFIG_SPIRAM_MODE_OCT + mspi_timing_ll_set_octal_psram_extra_dummy(spi_num, extra_dummy); +#elif CONFIG_SPIRAM_MODE_QUAD + //HW workaround: Use normal dummy register to set extra dummy, the calibration dedicated extra dummy register doesn't work for quad mode + mspi_timing_ll_set_quad_psram_dummy(spi_num, (QPI_PSRAM_FAST_READ_DUMMY + extra_dummy - 1)); +#endif +} + +void mspi_timing_config_psram_set_tuning_regs(const void *timing_params) +{ + const mspi_timing_tuning_param_t *params = (const mspi_timing_tuning_param_t *)timing_params; + /** + * 1. SPI_MEM_SPI_SMEM_DINx_MODE(1), SPI_MEM_SPI_SMEM_DINx_NUM(1) are meaningless + * SPI0 and SPI1 share the SPI_MEM_SPI_SMEM_DINx_MODE(0), SPI_MEM_SPI_SMEM_DINx_NUM(0) for PSRAM timing tuning + * 2. We use SPI1 to get the best PSRAM timing tuning (mode and num) config + */ + s_set_psram_din_mode_num(0, params->spi_din_mode, params->spi_din_num); + +#if CONFIG_SPIRAM_MODE_OCT + //On 728, for SPI1, flash and psram share the extra dummy register + s_set_flash_extra_dummy(1, params->extra_dummy_len); +#elif CONFIG_SPIRAM_MODE_QUAD + //Update this `s_psram_extra_dummy`, the `s_psram_read_data` will set dummy according to this `s_psram_extra_dummy` + s_psram_extra_dummy = params->extra_dummy_len; + mspi_timing_ll_set_quad_flash_dummy(1, params->extra_dummy_len - 1); +#endif +} + +//-------------------------------------------PSRAM Read/Write------------------------------------------// +static void s_psram_write_data(uint8_t *buf, uint32_t addr, uint32_t len) +{ +#if CONFIG_SPIRAM_MODE_OCT + esp_rom_opiflash_exec_cmd(1, ESP_ROM_SPIFLASH_OPI_DTR_MODE, + OPI_PSRAM_SYNC_WRITE, 16, + addr, 32, + OCT_PSRAM_WR_DUMMY_NUM, + buf, len * 8, + NULL, 0, + BIT(1), + false); +#elif CONFIG_SPIRAM_MODE_QUAD + psram_exec_cmd(1, 0, + QPI_PSRAM_WRITE, 8, + addr, 24, + 0, + buf, len * 8, + NULL, 0, + SPI_MEM_CS1_DIS_M, + false); +#endif +} + +static void s_psram_read_data(uint8_t *buf, uint32_t addr, uint32_t len) +{ +#if CONFIG_SPIRAM_MODE_OCT + mspi_timing_ll_clear_fifo(1); + esp_rom_opiflash_exec_cmd(1, ESP_ROM_SPIFLASH_OPI_DTR_MODE, + OPI_PSRAM_SYNC_READ, 16, + addr, 32, + OCT_PSRAM_RD_DUMMY_NUM, + NULL, 0, + buf, len * 8, + BIT(1), + false); +#elif CONFIG_SPIRAM_MODE_QUAD + psram_exec_cmd(1, 0, + QPI_PSRAM_FAST_READ, 8, + addr, 24, + QPI_PSRAM_FAST_READ_DUMMY + s_psram_extra_dummy, + NULL, 0, + buf, len * 8, + SPI_MEM_CS1_DIS_M, + false); +#endif +} + +static void s_psram_execution(uint8_t *buf, uint32_t addr, uint32_t len, bool is_read) +{ + while (len) { + uint32_t length = MIN(len, 32); + if (is_read) { + s_psram_read_data(buf, addr, length); + } else { + s_psram_write_data(buf, addr, length); + } + addr += length; + buf += length; + len -= length; + } +} + +void mspi_timing_config_psram_write_data(uint8_t *buf, uint32_t addr, uint32_t len) +{ + s_psram_execution(buf, addr, len, false); +} + +void mspi_timing_config_psram_read_data(uint8_t *buf, uint32_t addr, uint32_t len) +{ + s_psram_execution(buf, addr, len, true); +} + +/*------------------------------------------------------------------------------------------------- + * Best Timing Tuning Params Selection + *-------------------------------------------------------------------------------------------------*/ +#if (MSPI_TIMING_FLASH_DTR_MODE || MSPI_TIMING_PSRAM_DTR_MODE) && (MSPI_TIMING_CORE_CLOCK_MHZ == 240) +static bool get_working_pll_freq(const uint8_t *reference_data, bool is_flash, uint32_t *out_max_freq, uint32_t *out_min_freq) +{ + uint8_t read_data[MSPI_TIMING_TEST_DATA_LEN] = {0}; + rtc_cpu_freq_config_t previous_config; + rtc_clk_cpu_freq_get_config(&previous_config); + + uint32_t big_num = MSPI_TIMING_PLL_FREQ_SCAN_RANGE_MHZ_MAX * 2; //This number should be larger than MSPI_TIMING_PLL_FREQ_SCAN_RANGE_MHZ_MAX, for error handling + uint32_t max_freq = 0; + uint32_t min_freq = big_num; + rtc_xtal_freq_t xtal_freq = rtc_clk_xtal_freq_get(); + + //BBPLL CALIBRATION START + regi2c_ctrl_ll_bbpll_calibration_start(); + for (int pll_mhz_tuning = MSPI_TIMING_PLL_FREQ_SCAN_RANGE_MHZ_MIN; pll_mhz_tuning <= MSPI_TIMING_PLL_FREQ_SCAN_RANGE_MHZ_MAX; pll_mhz_tuning += 8) { + /** + * pll_mhz = xtal_mhz * (oc_div + 4) / (oc_ref_div + 1) + */ + clk_ll_bbpll_set_frequency_for_mspi_tuning(xtal_freq, pll_mhz_tuning, ((pll_mhz_tuning / 4) - 4), 9); + + memset(read_data, 0, MSPI_TIMING_TEST_DATA_LEN); + if (is_flash) { + mspi_timing_config_flash_read_data(read_data, MSPI_TIMING_FLASH_TEST_DATA_ADDR, MSPI_TIMING_TEST_DATA_LEN); + } else { + mspi_timing_config_psram_read_data(read_data, MSPI_TIMING_PSRAM_TEST_DATA_ADDR, MSPI_TIMING_TEST_DATA_LEN); + } + + if (memcmp(read_data, reference_data, MSPI_TIMING_TEST_DATA_LEN) == 0) { + max_freq = MAX(pll_mhz_tuning, max_freq); + min_freq = MIN(pll_mhz_tuning, min_freq); + + //Continue to find successful cases + continue; + } + + if (max_freq != 0) { + //The first fail case after successful case(s) is the end + break; + } + + //If no break, no successful case found, continue to find successful cases + } + + //restore PLL config + clk_ll_bbpll_set_freq_mhz(previous_config.source_freq_mhz); + clk_ll_bbpll_set_config(previous_config.source_freq_mhz, xtal_freq); + + //WAIT CALIBRATION DONE + while(!regi2c_ctrl_ll_bbpll_calibration_is_done()); + + //BBPLL CALIBRATION STOP + regi2c_ctrl_ll_bbpll_calibration_stop(); + + + *out_max_freq = max_freq; + *out_min_freq = min_freq; + + return (max_freq != 0); +} +#endif //Frequency Scanning + +static uint32_t s_select_best_tuning_config_dtr(const mspi_timing_config_t *configs, uint32_t consecutive_length, uint32_t end, const uint8_t *reference_data, bool is_flash) +{ +#if (MSPI_TIMING_CORE_CLOCK_MHZ == 160) + //Core clock 160M DTR best point scheme + uint32_t best_point; + + //Define these magic number in macros in `spi_timing_config.h`. TODO: IDF-3663 + if (consecutive_length <= 2 || consecutive_length >= 6) { + //tuning is FAIL, select default point, and generate a warning + best_point = configs->default_config_id; + ESP_EARLY_LOGW(TAG, "tuning fail, best point is fallen back to index %d", best_point); + } else if (consecutive_length <= 4) { + //consecutive length : 3 or 4 + best_point = end - 1; + ESP_EARLY_LOGD(TAG,"tuning success, best point is index %d", best_point); + } else { + //consecutive point list length equals 5 + best_point = end - 2; + ESP_EARLY_LOGD(TAG,"tuning success, best point is index %d", best_point); + } + + return best_point; + +#elif (MSPI_TIMING_CORE_CLOCK_MHZ == 240) + + uint32_t best_point = 0; + uint32_t current_point = end + 1 - consecutive_length; + bool ret = false; + + //This `max_freq` is the max pll frequency that per MSPI timing tuning config can work + uint32_t max_freq = 0; + uint32_t temp_max_freq = 0; + uint32_t temp_min_freq = 0; + + for (; current_point <= end; current_point++) { + if (is_flash) { + mspi_timing_config_flash_set_tuning_regs(&(configs->tuning_config_table[current_point])); + } else { + mspi_timing_config_psram_set_tuning_regs(&(configs->tuning_config_table[current_point])); + } + + ret = get_working_pll_freq(reference_data, is_flash, &temp_max_freq, &temp_min_freq); + if (ret && temp_min_freq <= MSPI_TIMING_PLL_FREQ_SCAN_THRESH_MHZ_LOW && temp_max_freq >= MSPI_TIMING_PLL_FREQ_SCAN_THRESH_MHZ_HIGH && temp_max_freq > max_freq) { + max_freq = temp_max_freq; + best_point = current_point; + } + ESP_EARLY_LOGD(TAG, "sample point %d, max pll is %d mhz, min pll is %d\n", current_point, temp_max_freq, temp_min_freq); + } + if (max_freq == 0) { + ESP_EARLY_LOGW(TAG, "freq scan tuning fail, best point is fallen back to index %d", end + 1 - consecutive_length); + best_point = end + 1 - consecutive_length; + } else { + ESP_EARLY_LOGD(TAG, "freq scan success, max pll is %dmhz, best point is index %d", max_freq, best_point); + } + + return best_point; + +#else + //won't reach here + abort(); +#endif +} + +static uint32_t s_select_best_tuning_config_str(const mspi_timing_config_t *configs, uint32_t consecutive_length, uint32_t end) +{ +#if (MSPI_TIMING_CORE_CLOCK_MHZ == 120 || MSPI_TIMING_CORE_CLOCK_MHZ == 240) + ESP_EARLY_LOGW("FLASH/PSRAM", "DO NOT USE FOR MASS PRODUCTION! Timing parameters may be updated in future IDF version."); + + //STR best point scheme + uint32_t best_point; + + if (consecutive_length <= 2|| consecutive_length >= 5) { + //tuning is FAIL, select default point, and generate a warning + best_point = configs->default_config_id; + ESP_EARLY_LOGW(TAG, "tuning fail, best point is fallen back to index %d", best_point); + } else { + //consecutive length : 3 or 4 + best_point = end - consecutive_length / 2; + ESP_EARLY_LOGD(TAG,"tuning success, best point is index %d", best_point); + } + + return best_point; +#else + //won't reach here + abort(); +#endif +} + +static uint32_t s_select_best_tuning_config(const mspi_timing_config_t *configs, uint32_t consecutive_length, uint32_t end, const uint8_t *reference_data, bool is_ddr, bool is_flash) +{ + uint32_t best_point = 0; + if (is_ddr) { + best_point = s_select_best_tuning_config_dtr(configs, consecutive_length, end, reference_data, is_flash); + } else { + best_point = s_select_best_tuning_config_str(configs, consecutive_length, end); + } + + return best_point; +} + +uint32_t mspi_timing_flash_select_best_tuning_config(const void *configs, uint32_t consecutive_length, uint32_t end, const uint8_t *reference_data, bool is_ddr) +{ + const mspi_timing_config_t *timing_configs = (const mspi_timing_config_t *)configs; + return s_select_best_tuning_config(timing_configs, consecutive_length, end, reference_data, is_ddr, true); +} + +uint32_t mspi_timing_psram_select_best_tuning_config(const void *configs, uint32_t consecutive_length, uint32_t end, const uint8_t *reference_data, bool is_ddr) +{ + const mspi_timing_config_t *timing_configs = (const mspi_timing_config_t *)configs; + return s_select_best_tuning_config(timing_configs, consecutive_length, end, reference_data, is_ddr, false); +} + +static mspi_timing_tuning_param_t s_flash_best_timing_tuning_config; +static mspi_timing_tuning_param_t s_psram_best_timing_tuning_config; + +void mspi_timing_flash_set_best_tuning_config(const void *timing_params) +{ + const mspi_timing_tuning_param_t *params = (const mspi_timing_tuning_param_t *)timing_params; + s_flash_best_timing_tuning_config = *params; +} + +void mspi_timing_psram_set_best_tuning_config(const void *timing_params) +{ + const mspi_timing_tuning_param_t *params = (const mspi_timing_tuning_param_t *)timing_params; + s_psram_best_timing_tuning_config = *params; +} + + +/*------------------------------------------------------------------------------------------------- + * Best Timing Tuning Params Clear / Set + *-------------------------------------------------------------------------------------------------*/ +void mspi_timing_flash_config_clear_tuning_regs(bool control_both_mspi) +{ + s_set_flash_din_mode_num(0, 0, 0); //SPI0 and SPI1 share the registers for flash din mode and num setting, so we only set SPI0's reg + s_set_flash_extra_dummy(0, 0); + + //Won't touch SPI1 registers if not control_both_mspi + if (control_both_mspi) { + s_set_flash_extra_dummy(1, 0); + } +} + +void mspi_timing_flash_config_set_tuning_regs(bool control_both_mspi) +{ + //SPI0 and SPI1 share the registers for flash din mode and num setting, so we only set SPI0's reg + s_set_flash_din_mode_num(0, s_flash_best_timing_tuning_config.spi_din_mode, s_flash_best_timing_tuning_config.spi_din_num); + s_set_flash_extra_dummy(0, s_flash_best_timing_tuning_config.extra_dummy_len); + if (control_both_mspi) { + s_set_flash_extra_dummy(1, s_flash_best_timing_tuning_config.extra_dummy_len); + } else { + //Won't touch SPI1 registers + } +} + +void mspi_timing_psram_config_clear_tuning_regs(bool control_both_mspi) +{ + (void)control_both_mspi; //for compatibility + s_set_psram_din_mode_num(0, 0, 0); + s_set_psram_extra_dummy(0, 0); +} + +void mspi_timing_psram_config_set_tuning_regs(bool control_both_mspi) +{ + (void)control_both_mspi; //for compatibility + s_set_psram_din_mode_num(0, s_psram_best_timing_tuning_config.spi_din_mode, s_psram_best_timing_tuning_config.spi_din_num); + s_set_psram_extra_dummy(0, s_psram_best_timing_tuning_config.extra_dummy_len); +} +#endif //#if MSPI_TIMING_FLASH_NEEDS_TUNING || MSPI_TIMING_PSRAM_NEEDS_TUNING + + +/*------------------------------------------------------------------------------------------------- + * To let upper lay (spi_flash_timing_tuning.c) to know the necessary timing registers + *-------------------------------------------------------------------------------------------------*/ +/** + * 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. + * @note This function inform `spi_flash_timing_tuning.c` (driver layer) of the cycle, + * and other component (esp_flash driver) should get these cycle and configure the registers accordingly. + */ +void mspi_timing_config_get_cs_timing(uint8_t *setup_time, uint32_t *hold_time) +{ + *setup_time = mspi_timing_ll_get_cs_setup_val(0); + *hold_time = mspi_timing_ll_get_cs_hold_val(0); + /** + * 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 (mspi_timing_ll_is_cs_setup_enabled(0)) { + *setup_time += 1; + } else { + *setup_time = 0; + } + if (mspi_timing_ll_is_cs_hold_enabled(0)) { + *hold_time += 1; + } else { + *hold_time = 0; + } +} + +/** + * Get the SPI1 Flash clock setting. + * @note Similarly, this function inform `spi_flash_timing_tuning.c` (driver layer) of the clock setting, + * and other component (esp_flash driver) should get these and configure the registers accordingly. + */ +uint32_t mspi_timing_config_get_flash_clock_reg(void) +{ + return mspi_timing_ll_get_clock_reg(1); +} + +uint8_t mspi_timing_config_get_flash_extra_dummy(void) +{ +#if MSPI_TIMING_FLASH_NEEDS_TUNING + return s_flash_best_timing_tuning_config.extra_dummy_len; +#else + return 0; +#endif +} diff --git a/components/esp_hw_support/mspi_timing_by_mspi_delay.h b/components/esp_hw_support/mspi_timing_by_mspi_delay.h new file mode 100644 index 0000000000..b62ff74f1f --- /dev/null +++ b/components/esp_hw_support/mspi_timing_by_mspi_delay.h @@ -0,0 +1,244 @@ +/* + * SPDX-FileCopyrightText: 2023 Espressif Systems (Shanghai) CO LTD + * + * SPDX-License-Identifier: Apache-2.0 + */ + +/** + * @brief + * + * This file contains configuration APIs doing MSPI timing tuning by MSPI delay + */ +#pragma once + +#include +#include "soc/soc_caps.h" +#if SOC_MEMSPI_TIMING_TUNING_BY_MSPI_DELAY +#include "mspi_timing_tuning_configs.h" +#endif + +#ifdef __cplusplus +extern "C" { +#endif + +#if SOC_MEMSPI_TIMING_TUNING_BY_MSPI_DELAY + +#define IS_DDR 1 +#define IS_SDR (!IS_DDR) + +/** + * MSPI timing tuning registers. + * Upper layer rely on these 3 registers to tune the timing. + */ +typedef struct { + uint8_t spi_din_mode; // input signal delay mode + uint8_t spi_din_num; // input signal delay number + uint8_t extra_dummy_len; // extra dummy length +} mspi_timing_tuning_param_t; + +/** + * MSPI timing tuning configurations + */ +typedef struct { + mspi_timing_tuning_param_t tuning_config_table[MSPI_TIMING_CONFIG_NUM_DEFAULT]; // Available timing tuning configs + uint32_t available_config_num; // Available timing tuning config numbers + uint32_t default_config_id; // If tuning fails, we use this one as default +} mspi_timing_config_t; + + +#if MSPI_TIMING_FLASH_NEEDS_TUNING || MSPI_TIMING_PSRAM_NEEDS_TUNING +/*------------------------------------------------------------------------------------------------- + * Timing Required APIs + *-------------------------------------------------------------------------------------------------*/ +/** + * @brief Get Flash tuning all configurations + * + * @param[out] config Pointer to Flash tuning configurations + */ +void mspi_timing_get_flash_tuning_configs(mspi_timing_config_t *config); + +/** + * @brief Init MSPI for Flash timing tuning + * + * @param[in] flash_freq_mhz Flash frequency in MHz + */ +void mspi_timing_flash_init(uint32_t flash_freq_mhz); + +/** + * @brief Tune Flash timing registers for SPI1 accessing Flash + * + * @param[in] params Timing parameters + */ +void mspi_timing_config_flash_set_tuning_regs(const void *timing_params); + +/** + * @brief Configure Flash to read data via SPI1 + * + * @param[out] buf buffer + * @param[in] addr address + * @param[in] len length + */ +void mspi_timing_config_flash_read_data(uint8_t *buf, uint32_t addr, uint32_t len); + +/** + * @brief Get PSRAM tuning all configurations + * + * @param[out] config Pointer to PSRAM tuning configurations + */ +void mspi_timing_get_psram_tuning_configs(mspi_timing_config_t *config); + +/** + * @brief Init MSPI for PSRAM timing tuning + * + * @param[in] psram_freq_mhz PSRAM frequency in MHz + */ +void mspi_timing_psram_init(uint32_t psram_freq_mhz); + +/** + * @brief Tune PSRAM timing registers for SPI1 accessing PSRAM + * + * @param[in] params Timing parameters + */ +void mspi_timing_config_psram_set_tuning_regs(const void *timing_params); + +/** + * @brief Configure PSRAM to write data via SPI1 + * + * @param[in] buf buffer + * @param[in] addr address + * @param[in] len length + */ +void mspi_timing_config_psram_write_data(uint8_t *buf, uint32_t addr, uint32_t len); + +/** + * @brief Configure PSRAM to read data via SPI1 + * + * @param[out] buf buffer + * @param[in] addr address + * @param[in] len length + */ +void mspi_timing_config_psram_read_data(uint8_t *buf, uint32_t addr, uint32_t len); + + +/*------------------------------------------------------------------------------------------------- + * Best Timing Tuning Params Selection + *-------------------------------------------------------------------------------------------------*/ +/** + * @brief Select Flash best tuning configuration + * + * @param[in] configs Timing tuning configuration table + * @param[in] consecutive_length Length of the consecutive successful sample results + * @param[in] end End of the consecutive successful sample results + * @param[in] reference_data Reference data + * @param[in] is_ddr DDR or SDR + * + * @return Best config ID + */ +uint32_t mspi_timing_flash_select_best_tuning_config(const void *configs, uint32_t consecutive_length, uint32_t end, const uint8_t *reference_data, bool is_ddr); + +/** + * @brief Set best Flash tuning configs. + * After this, calling `mspi_timing_enter_high_speed_mode` will set these configs correctly + * + * @param[in] timing_params Timing tuning parameters + */ +void mspi_timing_flash_set_best_tuning_config(const void *timing_params); + +/** + * @brief Select PSRAM best tuning configuration + * + * @param[in] configs Timing tuning configuration table + * @param[in] consecutive_length Length of the consecutive successful sample results + * @param[in] end End of the consecutive successful sample results + * @param[in] reference_data Reference data + * @param[in] is_ddr DDR or SDR + * + * @return Best config ID + */ +uint32_t mspi_timing_psram_select_best_tuning_config(const void *configs, uint32_t consecutive_length, uint32_t end, const uint8_t *reference_data, bool is_ddr); + +/** + * @brief Set best PSRAM tuning configs. + * After this, calling `mspi_timing_enter_high_speed_mode` will set these configs correctly + * + * @param[in] timing_params Timing tuning parameters + */ +void mspi_timing_psram_set_best_tuning_config(const void *timing_params); + + +/*------------------------------------------------------------------------------------------------- + * Best Timing Tuning Params Clear / Set + *-------------------------------------------------------------------------------------------------*/ +/** + * @brief Clear Flash timing tuning settings + * + * This is used when the system is going into low speed mode / MSPI doesn't need to be run in high speed + * + * @param[in] control_both_mspi Control SPI1 as well + */ +void mspi_timing_flash_config_clear_tuning_regs(bool control_both_mspi); + +/** + * @brief Set Flash timing tuning settings + * + * This is used when the system is going to high speed mode / MSPI needs to be run in high speed + * + * @param[in] control_both_mspi Control SPI1 as well + */ +void mspi_timing_flash_config_set_tuning_regs(bool control_both_mspi); + +/** + * @brief Clear PSRAM timing tuning settings + * + * This is used when the system is going into low speed mode / MSPI doesn't need to be run in high speed + * + * @param[in] control_both_mspi Control SPI1 as well + */ +void mspi_timing_psram_config_clear_tuning_regs(bool control_both_mspi); + +/** + * @brief Set PSRAM timing tuning settings + * + * This is used when the system is going to high speed mode / MSPI needs to be run in high speed + * + * @param[in] control_both_mspi Control SPI1 as well + */ +void mspi_timing_psram_config_set_tuning_regs(bool control_both_mspi); + + +/*------------------------------------------------------------------------------------------------- + * APIs for coordination with ESP Flash driver + *-------------------------------------------------------------------------------------------------*/ +/** + * 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 (`spi_flash_timing_tuning.c`). + */ + +/** + * @brief Get CS timing + * + * @param[out] setup_time Setup time + * @param[out] hold_time Hold time + */ +void mspi_timing_config_get_cs_timing(uint8_t *setup_time, uint32_t *hold_time); + +/** + * @brief Get Flash clock reg val + * + * @return Flash clock reg val + */ +uint32_t mspi_timing_config_get_flash_clock_reg(void); + +/** + * @brief Get Flash extra dummy len + * + * @return Flash extra dummy + */ +uint8_t mspi_timing_config_get_flash_extra_dummy(void); +#endif //#if MSPI_TIMING_FLASH_NEEDS_TUNING || MSPI_TIMING_PSRAM_NEEDS_TUNING + +#endif //#if SOC_MEMSPI_TIMING_TUNING_BY_MSPI_DELAY + +#ifdef __cplusplus +} +#endif diff --git a/components/esp_hw_support/mspi_timing_config.c b/components/esp_hw_support/mspi_timing_config.c new file mode 100644 index 0000000000..9b55166eb4 --- /dev/null +++ b/components/esp_hw_support/mspi_timing_config.c @@ -0,0 +1,79 @@ +/* + * SPDX-FileCopyrightText: 2019-2023 Espressif Systems (Shanghai) CO LTD + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#include +#include "sdkconfig.h" +#include "esp_attr.h" +#include "esp_err.h" +#include "esp_types.h" +#include "esp_log.h" +#include "soc/soc_caps.h" +#include "mspi_timing_config.h" +#if SOC_MEMSPI_TIMING_TUNING_BY_MSPI_DELAY +#include "mspi_timing_tuning_configs.h" +#include "hal/mspi_timing_tuning_ll.h" +#endif + + +#if SOC_MEMSPI_CORE_CLK_SHARED_WITH_PSRAM +#define FLASH_LOW_SPEED_CORE_CLOCK_MHZ MSPI_TIMING_LL_CORE_CLOCK_MHZ_DEFAULT +#define FLASH_HIGH_SPEED_CORE_CLOCK_MHZ MSPI_TIMING_CORE_CLOCK_MHZ +#define PSRAM_LOW_SPEED_CORE_CLOCK_MHZ MSPI_TIMING_LL_CORE_CLOCK_MHZ_DEFAULT +#define PSRAM_HIGH_SPEED_CORE_CLOCK_MHZ MSPI_TIMING_CORE_CLOCK_MHZ +#endif //SOC_MEMSPI_CORE_CLK_SHARED_WITH_PSRAM + + +#if SOC_SPI_MEM_SUPPORT_TIMING_TUNING +/** + * Currently we only need these on chips with timing tuning + */ +//-------------------------------------MSPI Clock Setting-------------------------------------// +static void s_mspi_flash_set_core_clock(uint8_t spi_num, uint32_t core_clock_mhz) +{ + mspi_timing_ll_set_core_clock(spi_num, core_clock_mhz); +} + +static void s_mspi_psram_set_core_clock(uint8_t spi_num, uint32_t core_clock_mhz) +{ + mspi_timing_ll_set_core_clock(spi_num, core_clock_mhz); +} + +void mspi_timing_config_set_flash_clock(uint32_t flash_freq_mhz, mspi_timing_speed_mode_t speed_mode, bool control_both_mspi) +{ + uint32_t core_clock_mhz = 0; + if (speed_mode == MSPI_TIMING_SPEED_MODE_LOW_PERF) { + core_clock_mhz = FLASH_LOW_SPEED_CORE_CLOCK_MHZ; + } else { + core_clock_mhz = FLASH_HIGH_SPEED_CORE_CLOCK_MHZ; + } + //SPI0 and SPI1 share the register for core clock. So we only set SPI0 here. + s_mspi_flash_set_core_clock(0, core_clock_mhz); + + uint32_t freqdiv = core_clock_mhz / flash_freq_mhz; + assert(freqdiv > 0); + mspi_timing_ll_set_flash_clock(0, freqdiv); + if (control_both_mspi) { + mspi_timing_ll_set_flash_clock(1, freqdiv); + } +} + +void mspi_timing_config_set_psram_clock(uint32_t psram_freq_mhz, mspi_timing_speed_mode_t speed_mode, bool control_both_mspi) +{ + (void)control_both_mspi; // for compatibility + uint32_t core_clock_mhz = 0; + if (speed_mode == MSPI_TIMING_SPEED_MODE_LOW_PERF) { + core_clock_mhz = PSRAM_LOW_SPEED_CORE_CLOCK_MHZ; + } else { + core_clock_mhz = PSRAM_HIGH_SPEED_CORE_CLOCK_MHZ; + } + //SPI0 and SPI1 share the register for core clock. So we only set SPI0 here. + s_mspi_psram_set_core_clock(0, core_clock_mhz); + + uint32_t freqdiv = core_clock_mhz / psram_freq_mhz; + assert(freqdiv > 0); + mspi_timing_ll_set_psram_clock(0, freqdiv); +} +#endif //#if SOC_SPI_MEM_SUPPORT_TIMING_TUNING diff --git a/components/esp_hw_support/mspi_timing_config.h b/components/esp_hw_support/mspi_timing_config.h index 78de3dadf9..5d2b411d5e 100644 --- a/components/esp_hw_support/mspi_timing_config.h +++ b/components/esp_hw_support/mspi_timing_config.h @@ -7,179 +7,32 @@ #pragma once #include -#include "mspi_timing_tuning_configs.h" +#include "esp_private/mspi_timing_types.h" #ifdef __cplusplus extern "C" { #endif - -/** - * SPI timing tuning registers. - * Upper layer rely on these 3 registers to tune the timing. - */ -typedef struct { - uint8_t spi_din_mode; /*!< input signal delay mode*/ - uint8_t spi_din_num; /*!< input signal delay number */ - uint8_t extra_dummy_len; /*!< extra dummy length*/ -} mspi_timing_tuning_param_t; - -typedef struct { - mspi_timing_tuning_param_t tuning_config_table[MSPI_TIMING_CONFIG_NUM_DEFAULT]; //available timing tuning configs - uint32_t available_config_num; - uint32_t default_config_id; //If tuning fails, we use this one as default -} mspi_timing_config_t; - -/** - * The SPI FLASH module clock and SPI PSRAM module clock is divided from the SPI core clock, core clock is from system clock: - * - * PLL ----| |---- FLASH Module Clock - * XTAL ----|----> Core Clock ---->| - * RTC8M ----| |---- PSRAM Module Clock - * - */ -typedef enum { - MSPI_TIMING_CONFIG_CORE_CLOCK_80M, - MSPI_TIMING_CONFIG_CORE_CLOCK_120M, - MSPI_TIMING_CONFIG_CORE_CLOCK_160M, - MSPI_TIMING_CONFIG_CORE_CLOCK_240M -} mspi_timing_config_core_clock_t; - - -//-------------------------------------- Generic Config APIs --------------------------------------// -/** - * @brief Get required core clock, under current sdkconfig (Flash / PSRAM mode, speed, etc.) - */ -mspi_timing_config_core_clock_t mspi_timing_config_get_core_clock(void); - -/** - * @brief Set MSPI core clock - * - * @param spi_num SPI0 / 1 - * @param core_clock core clock - */ -void mspi_timing_config_set_core_clock(uint8_t spi_num, mspi_timing_config_core_clock_t core_clock); - -/** - * @brief Set MSPI Flash module clock - * - * @param spi_num SPI0 / 1 - * @param freqdiv Freq divider - */ -void mspi_timing_config_set_flash_clock(uint8_t spi_num, uint32_t freqdiv); - -/** - * @brief Set MSPI Flash Din Mode and Din Num - * - * @param spi_num SPI0 / 1 - * @param din_mode Din mode - * @param din_num Din num - */ -void mspi_timing_config_flash_set_din_mode_num(uint8_t spi_num, uint8_t din_mode, uint8_t din_num); - -/** - * @brief Set MSPI Flash extra dummy - * - * @param spi_num SPI0 / 1 - * @param extra_dummy extra dummy - */ -void mspi_timing_config_flash_set_extra_dummy(uint8_t spi_num, uint8_t extra_dummy); - -/** - * @brief Configure Flash to read data via SPI1 - * - * @param buf buffer - * @param addr address - * @param len length - */ -void mspi_timing_config_flash_read_data(uint8_t *buf, uint32_t addr, uint32_t len); - -/** - * @brief Set MSPI PSRAM module clock - * - * @param spi_num SPI0 / 1 - * @param freqdiv Freq divider - */ -void mspi_timing_config_set_psram_clock(uint8_t spi_num, uint32_t freqdiv); - -/** - * @brief Set MSPI PSRAM Din Mode and Din Num - * - * @param spi_num SPI0 / 1 - * @param din_mode Din mode - * @param din_num Din num - */ -void mspi_timing_config_psram_set_din_mode_num(uint8_t spi_num, uint8_t din_mode, uint8_t din_num); - -/** - * @brief Set MSPI PSRAM extra dummy - * - * @param spi_num SPI0 / 1 - * @param extra_dummy extra dummy - */ -void mspi_timing_config_psram_set_extra_dummy(uint8_t spi_num, uint8_t extra_dummy); - -/** - * @brief Configure PSRAM to write data via SPI1 - * - * @param buf buffer - * @param addr address - * @param len length - */ -void mspi_timing_config_psram_write_data(uint8_t *buf, uint32_t addr, uint32_t len); - -/** - * @brief Configure PSRAM to read data via SPI1 - * - * @param buf buffer - * @param addr address - * @param len length - */ -void mspi_timing_config_psram_read_data(uint8_t *buf, uint32_t addr, uint32_t len); - /*------------------------------------------------------------------------------------------------- - * SPI1 Timing Tuning APIs - * - * These APIs are only used in `mspi_timing_tuning.c` for configuring SPI1 timing - * tuning related registers to find best tuning parameter for Flash and PSRAM + * Generic Config APIs *-------------------------------------------------------------------------------------------------*/ /** - * @brief Tune Flash timing registers for SPI1 accessing Flash + * @brief Set Flash module clock * - * @param[in] params Timing parameters + * @param flash_freq_mhz Flash clock frequency in MHz + * @param speed_mode Speed mode + * @param control_both_mspi Control SPI1 as well */ -void mspi_timing_config_flash_set_tuning_regs(const mspi_timing_tuning_param_t *params); +void mspi_timing_config_set_flash_clock(uint32_t flash_freq_mhz, mspi_timing_speed_mode_t speed_mode, bool control_both_mspi); /** - * @brief Tune PSRAM timing registers for SPI1 accessing PSRAM + * @brief Set PSRAM module clock * - * @param[in] params Timing parameters + * @param psram_freq_mhz PSRAM clock frequency in MHz + * @param speed_mode Speed mode + * @param control_both_mspi Not used, for compatibility */ -void mspi_timing_config_psram_set_tuning_regs(const mspi_timing_tuning_param_t *params); - - -/*------------------------------------------------------------------------------------------------- - * APIs for coordination with ESP Flash driver - *-------------------------------------------------------------------------------------------------*/ -/** - * SPI1 register info get APIs. These APIs inform `mspi_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 (`mspi_timing_tuning.c`). - */ - -/** - * @brief Get CS timing - * - * @param[out] setup_time Setup time - * @param[out] hold_time Hold time - */ -void mspi_timing_config_get_cs_timing(uint8_t *setup_time, uint32_t *hold_time); - -/** - * @brief Get Flash clock reg val - * - * @return Flash clock reg val - */ -uint32_t mspi_timing_config_get_flash_clock_reg(void); +void mspi_timing_config_set_psram_clock(uint32_t psram_freq_mhz, mspi_timing_speed_mode_t speed_mode, bool control_both_mspi); #ifdef __cplusplus } diff --git a/components/esp_hw_support/mspi_timing_tuning.c b/components/esp_hw_support/mspi_timing_tuning.c index 64f05074b9..246a565b55 100644 --- a/components/esp_hw_support/mspi_timing_tuning.c +++ b/components/esp_hw_support/mspi_timing_tuning.c @@ -11,122 +11,204 @@ #include "esp_err.h" #include "esp_types.h" #include "esp_log.h" -#include "soc/spi_mem_reg.h" #include "soc/io_mux_reg.h" -#include "esp_private/mspi_timing_tuning.h" #include "soc/soc.h" -#include "soc/rtc.h" #include "hal/spi_flash_hal.h" -#include "hal/mspi_timing_tuning_ll.h" -#include "hal/clk_tree_ll.h" -#include "hal/regi2c_ctrl_ll.h" +#include "hal/cache_hal.h" +#include "esp_private/mspi_timing_tuning.h" #include "mspi_timing_config.h" -#if CONFIG_IDF_TARGET_ESP32S3 -#include "esp32s3/rom/cache.h" +#include "mspi_timing_by_mspi_delay.h" +#if SOC_MEMSPI_TIMING_TUNING_BY_MSPI_DELAY +#include "mspi_timing_tuning_configs.h" +#include "hal/mspi_timing_tuning_ll.h" +#endif +#if SOC_MEMSPI_CLK_SRC_IS_INDEPENDENT +#include "hal/spimem_flash_ll.h" #endif -#if MSPI_TIMING_FLASH_NEEDS_TUNING || MSPI_TIMING_PSRAM_NEEDS_TUNING -const static char *TAG = "MSPI Timing"; -static mspi_timing_tuning_param_t s_flash_best_timing_tuning_config; -static mspi_timing_tuning_param_t s_psram_best_timing_tuning_config; -#endif - -/*------------------------------------------------------------------------------ - * Common settings - *----------------------------------------------------------------------------*/ -void mspi_timing_set_pin_drive_strength(void) -{ - //For now, set them all to 3. Need to check after QVL test results are out. TODO: IDF-3663 - //Set default clk - mspi_timing_ll_set_all_pin_drive(0, 3); -} - -/*------------------------------------------------------------------------------ - * Static functions to get clock configs - *----------------------------------------------------------------------------*/ -static mspi_timing_config_core_clock_t get_mspi_core_clock(void) -{ - return mspi_timing_config_get_core_clock(); -} - -static uint32_t get_flash_clock_divider(void) -{ -#if CONFIG_ESPTOOLPY_FLASHFREQ_20M - return MSPI_TIMING_CORE_CLOCK_MHZ / 20; -#elif CONFIG_ESPTOOLPY_FLASHFREQ_40M - return MSPI_TIMING_CORE_CLOCK_MHZ / 40; +#if CONFIG_ESPTOOLPY_FLASHFREQ_120M +#define FLASH_FREQUENCY_MHZ 120 #elif CONFIG_ESPTOOLPY_FLASHFREQ_80M - return MSPI_TIMING_CORE_CLOCK_MHZ / 80; -#elif CONFIG_ESPTOOLPY_FLASHFREQ_120M - return MSPI_TIMING_CORE_CLOCK_MHZ / 120; -#else - abort(); +#define FLASH_FREQUENCY_MHZ 80 +#elif CONFIG_ESPTOOLPY_FLASHFREQ_64M +#define FLASH_FREQUENCY_MHZ 64 +#elif CONFIG_ESPTOOLPY_FLASHFREQ_60M +#define FLASH_FREQUENCY_MHZ 60 +#elif CONFIG_ESPTOOLPY_FLASHFREQ_48M +#define FLASH_FREQUENCY_MHZ 48 +#elif CONFIG_ESPTOOLPY_FLASHFREQ_40M +#define FLASH_FREQUENCY_MHZ 40 +#elif CONFIG_ESPTOOLPY_FLASHFREQ_32M +#define FLASH_FREQUENCY_MHZ 32 +#elif CONFIG_ESPTOOLPY_FLASHFREQ_30M +#define FLASH_FREQUENCY_MHZ 30 +#elif CONFIG_ESPTOOLPY_FLASHFREQ_26M +#define FLASH_FREQUENCY_MHZ 26 +#elif CONFIG_ESPTOOLPY_FLASHFREQ_24M +#define FLASH_FREQUENCY_MHZ 24 +#elif CONFIG_ESPTOOLPY_FLASHFREQ_20M +#define FLASH_FREQUENCY_MHZ 20 +#elif CONFIG_ESPTOOLPY_FLASHFREQ_16M +#define FLASH_FREQUENCY_MHZ 16 +#elif CONFIG_ESPTOOLPY_FLASHFREQ_15M +#define FLASH_FREQUENCY_MHZ 15 #endif -} -static uint32_t get_psram_clock_divider(void) -{ -#if CONFIG_SPIRAM_SPEED_40M - return MSPI_TIMING_CORE_CLOCK_MHZ / 40; -#elif CONFIG_SPIRAM_SPEED_80M - return MSPI_TIMING_CORE_CLOCK_MHZ / 80; -#elif CONFIG_SPIRAM_SPEED_120M - return MSPI_TIMING_CORE_CLOCK_MHZ / 120; -#else - //Will enter this branch only if PSRAM is not enable - return 0; -#endif -} - -#if MSPI_TIMING_FLASH_NEEDS_TUNING || MSPI_TIMING_PSRAM_NEEDS_TUNING -/*------------------------------------------------------------------------------ - * Static functions to do timing tuning - *----------------------------------------------------------------------------*/ /** - * Set timing tuning regs, in order to get successful sample points + * @brief MSPI timing tuning type */ -static void init_spi1_for_tuning(bool is_flash) +typedef enum { + MSPI_TIMING_TUNING_MSPI_DIN_DUMMY, //tune by mspi din and dummy +} mspi_timing_tuning_t; + +typedef struct mspi_tuning_cfg_drv_s mspi_tuning_cfg_drv_t; +__attribute__((unused)) const static char *TAG = "MSPI Timing"; + +struct mspi_tuning_cfg_drv_s { + + /** + * @brief Flash tuning scheme type + */ + mspi_timing_tuning_t flash_tuning_type; + + /** + * @brief Init MSPI for Flash timing tuning + * + * @param[in] flash_freq_mhz Flash frequency in MHz + */ + void (*flash_init_mspi)(uint32_t flash_freq_mhz); + + /** + * @brief Configure MSPI for Flash timing tuning + * + * @param[in] params Timing tuning parameters + */ + void (*flash_tune_mspi)(const void *params); + + /** + * @brief Flash read + * + * @param[in] buf Read buffer + * @param[in] addr Read address + * @param[in] len Read length + */ + void (*flash_read)(uint8_t *buf, uint32_t addr, uint32_t len); + + /** + * @brief Select best tuning configs for Flash + * + * @param[in] configs Timing tuning configurations + * @param[in] consecutive_length Length of the consecutive successful sample results + * @param[in] end End of the consecutive successful sample results + * @param[in] reference_data Reference data + * @param[in] is_ddr DDR or SDR + * + * @return Best config ID + */ + uint32_t (*flash_select_best_tuning_config)(const void *configs, uint32_t consecutive_length, uint32_t end, const uint8_t *reference_data, bool is_ddr); + + /** + * @brief Set best Flash tuning configs. + * After this, calling `mspi_timing_enter_high_speed_mode` will set these configs correctly + * + * @param[in] params Timing tuning parameters + */ + void (*flash_set_best_tuning_config)(const void *params); + + /** + * @brief PSRAM tuning scheme type + */ + mspi_timing_tuning_t psram_tuning_type; + + /** + * @brief Init MSPI for PSRAM timing tuning + * + * @param[in] flash_freq_mhz PSRAM frequency in MHz + */ + void (*psram_init_mspi)(uint32_t psram_freq_mhz); + + /** + * @brief Configure MSPI for PSRAM timing tuning + * + * @param[in] params Timing tuning parameters + */ + void (*psram_tune_mspi)(const void *params); + + /** + * @brief PSRAM read + * + * @param[in] buf Read buffer + * @param[in] addr Read address + * @param[in] len Read length + */ + void (*psram_read)(uint8_t *buf, uint32_t addr, uint32_t len); + + /** + * @brief Select best tuning configs for PSRAM + * + * @param[in] configs Timing tuning configurations + * @param[in] consecutive_length Length of the consecutive successful sample results + * @param[in] end End of the consecutive successful sample results + * @param[in] reference_data Reference data + * @param[in] is_ddr DDR or SDR + * + * @return Best config ID + */ + uint32_t (*psram_select_best_tuning_config)(const void *configs, uint32_t consecutive_length, uint32_t end, const uint8_t *reference_data, bool is_ddr); + + /** + * @brief Set best PSRAM tuning configs. + * After this, calling `mspi_timing_enter_high_speed_mode` will set these configs correctly + * + * @param[in] params Timing tuning parameters + */ + void (*psram_set_best_tuning_config)(const void *params); +}; + +static mspi_tuning_cfg_drv_t s_tuning_cfg_drv = {}; + +void s_register_config_driver(mspi_tuning_cfg_drv_t *cfg_drv, bool is_flash) { - //Get required core clock and module clock settings - mspi_timing_config_core_clock_t core_clock = get_mspi_core_clock(); - //Set SPI1 core clock. SPI0 and SPI1 share the register for core clock. So we only set SPI0 here. - mspi_timing_config_set_core_clock(0, core_clock); - //Set SPI1 module clock as required if (is_flash) { - uint32_t flash_div = get_flash_clock_divider(); - mspi_timing_config_set_flash_clock(1, flash_div); - //Power on HCLK - mspi_timinng_ll_enable_flash_hclk(0); + s_tuning_cfg_drv.flash_tuning_type = cfg_drv->flash_tuning_type; + s_tuning_cfg_drv.flash_init_mspi = cfg_drv->flash_init_mspi; + s_tuning_cfg_drv.flash_tune_mspi = cfg_drv->flash_tune_mspi; + s_tuning_cfg_drv.flash_read = cfg_drv->flash_read; + s_tuning_cfg_drv.flash_select_best_tuning_config = cfg_drv->flash_select_best_tuning_config; + s_tuning_cfg_drv.flash_set_best_tuning_config = cfg_drv->flash_set_best_tuning_config; } else { - //We use SPI1 Flash to tune PSRAM, PSRAM timing related regs do nothing on SPI1 - uint32_t psram_div = get_psram_clock_divider(); - mspi_timing_config_set_flash_clock(1, psram_div); - //Power on HCLK - mspi_timinng_ll_enable_psram_hclk(0); + s_tuning_cfg_drv.psram_tuning_type = cfg_drv->psram_tuning_type; + s_tuning_cfg_drv.psram_init_mspi = cfg_drv->psram_init_mspi; + s_tuning_cfg_drv.psram_tune_mspi = cfg_drv->psram_tune_mspi; + s_tuning_cfg_drv.psram_read = cfg_drv->psram_read; + s_tuning_cfg_drv.psram_select_best_tuning_config = cfg_drv->psram_select_best_tuning_config; + s_tuning_cfg_drv.psram_set_best_tuning_config = cfg_drv->psram_set_best_tuning_config; } } +#if MSPI_TIMING_FLASH_NEEDS_TUNING || MSPI_TIMING_PSRAM_NEEDS_TUNING /** - * We use different SPI1 timing tuning config to read data to see if current MSPI sampling is successful. + * We use different MSPI timing tuning config to read data to see if current MSPI sampling is successful. * The sampling result will be stored in an array. In this array, successful item will be 1, failed item will be 0. */ -static void sweep_for_success_sample_points(const uint8_t *reference_data, const mspi_timing_config_t *config, bool is_flash, uint8_t *out_array) +static void s_sweep_for_success_sample_points(uint8_t *reference_data, void *config, bool is_flash, uint8_t *out_array) { + const mspi_timing_config_t *timing_config = (const mspi_timing_config_t *)config; uint32_t config_idx = 0; uint8_t read_data[MSPI_TIMING_TEST_DATA_LEN] = {0}; - for (config_idx = 0; config_idx < config->available_config_num; config_idx++) { + for (config_idx = 0; config_idx < timing_config->available_config_num; config_idx++) { memset(read_data, 0, MSPI_TIMING_TEST_DATA_LEN); #if MSPI_TIMING_FLASH_NEEDS_TUNING if (is_flash) { - mspi_timing_config_flash_set_tuning_regs(&(config->tuning_config_table[config_idx])); - mspi_timing_config_flash_read_data(read_data, MSPI_TIMING_FLASH_TEST_DATA_ADDR, sizeof(read_data)); + s_tuning_cfg_drv.flash_tune_mspi(&(timing_config->tuning_config_table[config_idx])); + s_tuning_cfg_drv.flash_read(read_data, MSPI_TIMING_FLASH_TEST_DATA_ADDR, sizeof(read_data)); } #endif #if MSPI_TIMING_PSRAM_NEEDS_TUNING if (!is_flash) { - mspi_timing_config_psram_set_tuning_regs(&(config->tuning_config_table[config_idx])); - mspi_timing_config_psram_read_data(read_data, MSPI_TIMING_PSRAM_TEST_DATA_ADDR, MSPI_TIMING_TEST_DATA_LEN); + s_tuning_cfg_drv.psram_tune_mspi(&(timing_config->tuning_config_table[config_idx])); + s_tuning_cfg_drv.psram_read(read_data, MSPI_TIMING_PSRAM_TEST_DATA_ADDR, MSPI_TIMING_TEST_DATA_LEN); } #endif if (memcmp(reference_data, read_data, sizeof(read_data)) == 0) { @@ -135,6 +217,7 @@ static void sweep_for_success_sample_points(const uint8_t *reference_data, const } else { ESP_EARLY_LOGD(TAG, "%d, bad", config_idx); } + } } @@ -144,7 +227,7 @@ static void sweep_for_success_sample_points(const uint8_t *reference_data, const * out_length: 3 * outout_end_index: 6 */ -static void find_max_consecutive_success_points(uint8_t *array, uint32_t size, uint32_t *out_length, uint32_t *out_end_index) +static void s_find_max_consecutive_success_points(uint8_t *array, uint32_t size, uint32_t *out_length, uint32_t *out_end_index) { uint32_t max = 0; uint32_t match_num = 0; @@ -168,186 +251,32 @@ static void find_max_consecutive_success_points(uint8_t *array, uint32_t size, u *out_end_index = match_num == size ? size : end; } -#if (MSPI_TIMING_FLASH_DTR_MODE || MSPI_TIMING_PSRAM_DTR_MODE) && (MSPI_TIMING_CORE_CLOCK_MHZ == 240) -static bool get_working_pll_freq(const uint8_t *reference_data, bool is_flash, uint32_t *out_max_freq, uint32_t *out_min_freq) -{ - uint8_t read_data[MSPI_TIMING_TEST_DATA_LEN] = {0}; - rtc_cpu_freq_config_t previous_config; - rtc_clk_cpu_freq_get_config(&previous_config); - - uint32_t big_num = MSPI_TIMING_PLL_FREQ_SCAN_RANGE_MHZ_MAX * 2; //This number should be larger than MSPI_TIMING_PLL_FREQ_SCAN_RANGE_MHZ_MAX, for error handling - uint32_t max_freq = 0; - uint32_t min_freq = big_num; - rtc_xtal_freq_t xtal_freq = rtc_clk_xtal_freq_get(); - - //BBPLL CALIBRATION START - regi2c_ctrl_ll_bbpll_calibration_start(); - for (int pll_mhz_tuning = MSPI_TIMING_PLL_FREQ_SCAN_RANGE_MHZ_MIN; pll_mhz_tuning <= MSPI_TIMING_PLL_FREQ_SCAN_RANGE_MHZ_MAX; pll_mhz_tuning += 8) { - /** - * pll_mhz = xtal_mhz * (oc_div + 4) / (oc_ref_div + 1) - */ - clk_ll_bbpll_set_frequency_for_mspi_tuning(xtal_freq, pll_mhz_tuning, ((pll_mhz_tuning / 4) - 4), 9); - - memset(read_data, 0, MSPI_TIMING_TEST_DATA_LEN); - if (is_flash) { - mspi_timing_config_flash_read_data(read_data, MSPI_TIMING_FLASH_TEST_DATA_ADDR, MSPI_TIMING_TEST_DATA_LEN); - } else { - mspi_timing_config_psram_read_data(read_data, MSPI_TIMING_PSRAM_TEST_DATA_ADDR, MSPI_TIMING_TEST_DATA_LEN); - } - - if (memcmp(read_data, reference_data, MSPI_TIMING_TEST_DATA_LEN) == 0) { - max_freq = MAX(pll_mhz_tuning, max_freq); - min_freq = MIN(pll_mhz_tuning, min_freq); - - //Continue to find successful cases - continue; - } - - if (max_freq != 0) { - //The first fail case after successful case(s) is the end - break; - } - - //If no break, no successful case found, continue to find successful cases - } - - //restore PLL config - clk_ll_bbpll_set_freq_mhz(previous_config.source_freq_mhz); - clk_ll_bbpll_set_config(previous_config.source_freq_mhz, xtal_freq); - - //WAIT CALIBRATION DONE - while(!regi2c_ctrl_ll_bbpll_calibration_is_done()); - - //BBPLL CALIBRATION STOP - regi2c_ctrl_ll_bbpll_calibration_stop(); - - - *out_max_freq = max_freq; - *out_min_freq = min_freq; - - return (max_freq != 0); -} -#endif //Frequency Scanning - -#if MSPI_TIMING_FLASH_DTR_MODE || MSPI_TIMING_PSRAM_DTR_MODE -static uint32_t select_best_tuning_config_dtr(mspi_timing_config_t *config, uint32_t consecutive_length, uint32_t end, const uint8_t *reference_data, bool is_flash) -{ -#if (MSPI_TIMING_CORE_CLOCK_MHZ == 160) - //Core clock 160M DTR best point scheme - (void) reference_data; - (void) is_flash; - uint32_t best_point = 0; - - //These numbers will probably be same on other chips, if this version of algorithm is utilised - if (consecutive_length <= 2 || consecutive_length >= 6) { - //tuning is FAIL, select default point, and generate a warning - best_point = config->default_config_id; - ESP_EARLY_LOGW(TAG, "tuning fail, best point is fallen back to index %d", best_point); - } else if (consecutive_length <= 4) { - //consecutive length : 3 or 4 - best_point = end - 1; - ESP_EARLY_LOGD(TAG,"tuning success, best point is index %d", best_point); - } else { - //consecutive point list length equals 5 - best_point = end - 2; - ESP_EARLY_LOGD(TAG,"tuning success, best point is index %d", best_point); - } - - return best_point; - -#elif (MSPI_TIMING_CORE_CLOCK_MHZ == 240) - - uint32_t best_point = 0; - uint32_t current_point = end + 1 - consecutive_length; - bool ret = false; - - //This `max_freq` is the max pll frequency that per MSPI timing tuning config can work - uint32_t max_freq = 0; - uint32_t temp_max_freq = 0; - uint32_t temp_min_freq = 0; - - for (; current_point <= end; current_point++) { - if (is_flash) { - mspi_timing_config_flash_set_tuning_regs(&(config->tuning_config_table[current_point])); - } else { - mspi_timing_config_psram_set_tuning_regs(&(config->tuning_config_table[current_point])); - } - - ret = get_working_pll_freq(reference_data, is_flash, &temp_max_freq, &temp_min_freq); - if (ret && temp_min_freq <= MSPI_TIMING_PLL_FREQ_SCAN_THRESH_MHZ_LOW && temp_max_freq >= MSPI_TIMING_PLL_FREQ_SCAN_THRESH_MHZ_HIGH && temp_max_freq > max_freq) { - max_freq = temp_max_freq; - best_point = current_point; - } - ESP_EARLY_LOGD(TAG, "sample point %d, max pll is %d mhz, min pll is %d\n", current_point, temp_max_freq, temp_min_freq); - } - if (max_freq == 0) { - ESP_EARLY_LOGW(TAG, "freq scan tuning fail, best point is fallen back to index %d", end + 1 - consecutive_length); - best_point = end + 1 - consecutive_length; - } else { - ESP_EARLY_LOGD(TAG, "freq scan success, max pll is %dmhz, best point is index %d", max_freq, best_point); - } - - return best_point; - -#else - //won't reach here - abort(); -#endif -} -#endif - -#if MSPI_TIMING_FLASH_STR_MODE || MSPI_TIMING_PSRAM_STR_MODE -static uint32_t select_best_tuning_config_str(mspi_timing_config_t *config, uint32_t consecutive_length, uint32_t end) -{ -#if (MSPI_TIMING_CORE_CLOCK_MHZ == 120 || MSPI_TIMING_CORE_CLOCK_MHZ == 240) - ESP_EARLY_LOGW("FLASH/PSRAM", "DO NOT USE FOR MASS PRODUCTION! Timing parameters may be updated in future IDF version."); - - //STR best point scheme - uint32_t best_point; - - if (consecutive_length <= 2|| consecutive_length >= 5) { - //tuning is FAIL, select default point, and generate a warning - best_point = config->default_config_id; - ESP_EARLY_LOGW(TAG, "tuning fail, best point is fallen back to index %d", best_point); - } else { - //consecutive length : 3 or 4 - best_point = end - consecutive_length / 2; - ESP_EARLY_LOGD(TAG,"tuning success, best point is index %d", best_point); - } - - return best_point; -#else - //won't reach here - abort(); -#endif -} -#endif - -static void select_best_tuning_config(mspi_timing_config_t *config, uint32_t consecutive_length, uint32_t end, const uint8_t *reference_data, bool is_flash) +static void s_select_best_tuning_config(mspi_timing_config_t *config, uint32_t consecutive_length, uint32_t end, const uint8_t *reference_data, bool is_flash) { + const mspi_timing_config_t *timing_config = (const mspi_timing_config_t *)config; uint32_t best_point = 0; if (is_flash) { #if MSPI_TIMING_FLASH_DTR_MODE - best_point = select_best_tuning_config_dtr(config, consecutive_length, end, reference_data, is_flash); + best_point = s_tuning_cfg_drv.flash_select_best_tuning_config(timing_config, consecutive_length, end, reference_data, IS_DDR); #elif MSPI_TIMING_FLASH_STR_MODE - best_point = select_best_tuning_config_str(config, consecutive_length, end); + best_point = s_tuning_cfg_drv.flash_select_best_tuning_config(timing_config, consecutive_length, end, NULL, IS_SDR); #endif - s_flash_best_timing_tuning_config = config->tuning_config_table[best_point]; + s_tuning_cfg_drv.flash_set_best_tuning_config(&(timing_config->tuning_config_table[best_point])); } else { #if MSPI_TIMING_PSRAM_DTR_MODE - best_point = select_best_tuning_config_dtr(config, consecutive_length, end, reference_data, is_flash); + best_point = s_tuning_cfg_drv.psram_select_best_tuning_config(timing_config, consecutive_length, end, reference_data, IS_DDR); #elif MSPI_TIMING_PSRAM_STR_MODE - best_point = select_best_tuning_config_str(config, consecutive_length, end); + best_point = s_tuning_cfg_drv.psram_select_best_tuning_config(timing_config, consecutive_length, end, NULL, IS_SDR); #endif - s_psram_best_timing_tuning_config = config->tuning_config_table[best_point]; + s_tuning_cfg_drv.psram_set_best_tuning_config(&(timing_config->tuning_config_table[best_point])); } } -static void do_tuning(const uint8_t *reference_data, mspi_timing_config_t *timing_config, bool is_flash) +static void s_do_tuning(uint8_t *reference_data, void *timing_config, bool is_flash) { /** - * We use SPI1 to tune the timing: - * 1. Get all SPI1 sampling results. + * We use MSPI to tune the timing: + * 1. Get all MSPI sampling results. * 2. Find the longest consecutive successful sampling points from the result above. * 3. The middle one will be the best sampling point. */ @@ -355,10 +284,20 @@ static void do_tuning(const uint8_t *reference_data, mspi_timing_config_t *timin uint32_t last_success_point = 0; uint8_t sample_result[MSPI_TIMING_CONFIG_NUM_DEFAULT] = {0}; - init_spi1_for_tuning(is_flash); - sweep_for_success_sample_points(reference_data, timing_config, is_flash, sample_result); - find_max_consecutive_success_points(sample_result, MSPI_TIMING_CONFIG_NUM_DEFAULT, &consecutive_length, &last_success_point); - select_best_tuning_config(timing_config, consecutive_length, last_success_point, reference_data, is_flash); +#if MSPI_TIMING_FLASH_NEEDS_TUNING + if (is_flash) { + s_tuning_cfg_drv.flash_init_mspi(FLASH_FREQUENCY_MHZ); + } +#endif +#if MSPI_TIMING_PSRAM_NEEDS_TUNING + if (!is_flash) { + s_tuning_cfg_drv.psram_init_mspi(CONFIG_SPIRAM_SPEED); + } +#endif + + s_sweep_for_success_sample_points(reference_data, timing_config, is_flash, sample_result); + s_find_max_consecutive_success_points(sample_result, MSPI_TIMING_CONFIG_NUM_DEFAULT, &consecutive_length, &last_success_point); + s_select_best_tuning_config(timing_config, consecutive_length, last_success_point, reference_data, is_flash); } #endif //#if MSPI_TIMING_FLASH_NEEDS_TUNING || MSPI_TIMING_PSRAM_NEEDS_TUNING @@ -367,44 +306,39 @@ static void do_tuning(const uint8_t *reference_data, mspi_timing_config_t *timin * FLASH Timing Tuning *----------------------------------------------------------------------------*/ #if MSPI_TIMING_FLASH_NEEDS_TUNING -static void get_flash_tuning_configs(mspi_timing_config_t *config) -{ -#if MSPI_TIMING_FLASH_DTR_MODE -#define FLASH_MODE DTR_MODE -#else //MSPI_TIMING_FLASH_STR_MODE -#define FLASH_MODE STR_MODE -#endif - -#if CONFIG_ESPTOOLPY_FLASHFREQ_20M - *config = MSPI_TIMING_FLASH_GET_TUNING_CONFIG(MSPI_TIMING_CORE_CLOCK_MHZ, 20, FLASH_MODE); -#elif CONFIG_ESPTOOLPY_FLASHFREQ_40M - *config = MSPI_TIMING_FLASH_GET_TUNING_CONFIG(MSPI_TIMING_CORE_CLOCK_MHZ, 40, FLASH_MODE); -#elif CONFIG_ESPTOOLPY_FLASHFREQ_80M - *config = MSPI_TIMING_FLASH_GET_TUNING_CONFIG(MSPI_TIMING_CORE_CLOCK_MHZ, 80, FLASH_MODE); -#elif CONFIG_ESPTOOLPY_FLASHFREQ_120M - *config = MSPI_TIMING_FLASH_GET_TUNING_CONFIG(MSPI_TIMING_CORE_CLOCK_MHZ, 120, FLASH_MODE); -#endif - -#undef FLASH_MODE -} - void mspi_timing_flash_tuning(void) { /** - * set SPI01 related regs to 20mhz configuration, to get reference data from FLASH + * set MSPI related regs to 20mhz configuration, to get reference data from FLASH * see detailed comments in this function (`mspi_timing_enter_low_speed_mode`) */ mspi_timing_enter_low_speed_mode(true); +#if SOC_MEMSPI_TIMING_TUNING_BY_MSPI_DELAY + mspi_tuning_cfg_drv_t drv = { + .flash_tuning_type = MSPI_TIMING_TUNING_MSPI_DIN_DUMMY, + .flash_init_mspi = mspi_timing_flash_init, + .flash_tune_mspi = mspi_timing_config_flash_set_tuning_regs, + .flash_read = mspi_timing_config_flash_read_data, + .flash_select_best_tuning_config = mspi_timing_flash_select_best_tuning_config, + .flash_set_best_tuning_config = mspi_timing_flash_set_best_tuning_config, + }; + bool is_flash = true; + s_register_config_driver(&drv, is_flash); + //Disable the variable dummy mode when doing timing tuning mspi_timing_ll_enable_flash_variable_dummy(1, false); //GD flash will read error in variable mode with 20MHz uint8_t reference_data[MSPI_TIMING_TEST_DATA_LEN] = {0}; - mspi_timing_config_flash_read_data(reference_data, MSPI_TIMING_FLASH_TEST_DATA_ADDR, sizeof(reference_data)); - mspi_timing_config_t timing_configs = {0}; - get_flash_tuning_configs(&timing_configs); + s_tuning_cfg_drv.flash_read(reference_data, MSPI_TIMING_FLASH_TEST_DATA_ADDR, sizeof(reference_data)); + + mspi_timing_config_t timing_configs = {0}; + mspi_timing_get_flash_tuning_configs(&timing_configs); + +#endif //SOC_MEMSPI_TIMING_TUNING_BY_MSPI_DELAY + + s_do_tuning(reference_data, &timing_configs, true); - do_tuning(reference_data, &timing_configs, true); mspi_timing_enter_high_speed_mode(true); } #else @@ -419,46 +353,42 @@ void mspi_timing_flash_tuning(void) * PSRAM Timing Tuning *----------------------------------------------------------------------------*/ #if MSPI_TIMING_PSRAM_NEEDS_TUNING -static void get_psram_tuning_configs(mspi_timing_config_t *config) -{ -#if MSPI_TIMING_PSRAM_DTR_MODE -#define PSRAM_MODE DTR_MODE -#else //MSPI_TIMING_PSRAM_STR_MODE -#define PSRAM_MODE STR_MODE -#endif - -#if CONFIG_SPIRAM_SPEED_40M - *config = MSPI_TIMING_PSRAM_GET_TUNING_CONFIG(MSPI_TIMING_CORE_CLOCK_MHZ, 40, PSRAM_MODE); -#elif CONFIG_SPIRAM_SPEED_80M - *config = MSPI_TIMING_PSRAM_GET_TUNING_CONFIG(MSPI_TIMING_CORE_CLOCK_MHZ, 80, PSRAM_MODE); -#elif CONFIG_SPIRAM_SPEED_120M - *config = MSPI_TIMING_PSRAM_GET_TUNING_CONFIG(MSPI_TIMING_CORE_CLOCK_MHZ, 120, PSRAM_MODE); -#endif - -#undef PSRAM_MODE -} - void mspi_timing_psram_tuning(void) { /** - * set SPI01 related regs to 20mhz configuration, to write reference data to PSRAM + * set MSPI related regs to 20mhz configuration, to write reference data to PSRAM * see detailed comments in this function (`mspi_timing_enter_low_speed_mode`) */ mspi_timing_enter_low_speed_mode(true); +#if SOC_MEMSPI_TIMING_TUNING_BY_MSPI_DELAY // write data into psram, used to do timing tuning test. uint8_t reference_data[MSPI_TIMING_TEST_DATA_LEN]; for (int i=0; i < MSPI_TIMING_TEST_DATA_LEN/4; i++) { ((uint32_t *)reference_data)[i] = 0xa5ff005a; } mspi_timing_config_psram_write_data(reference_data, MSPI_TIMING_PSRAM_TEST_DATA_ADDR, MSPI_TIMING_TEST_DATA_LEN); + + mspi_tuning_cfg_drv_t drv = { + .psram_tuning_type = MSPI_TIMING_TUNING_MSPI_DIN_DUMMY, + .psram_init_mspi = mspi_timing_psram_init, + .psram_tune_mspi = mspi_timing_config_psram_set_tuning_regs, + .psram_read = mspi_timing_config_psram_read_data, + .psram_select_best_tuning_config = mspi_timing_psram_select_best_tuning_config, + .psram_set_best_tuning_config = mspi_timing_psram_set_best_tuning_config, + }; + bool is_flash = false; + s_register_config_driver(&drv, is_flash); + mspi_timing_config_t timing_configs = {0}; - get_psram_tuning_configs(&timing_configs); + mspi_timing_get_psram_tuning_configs(&timing_configs); +#endif //#if SOC_MEMSPI_TIMING_TUNING_BY_MSPI_DELAY //Disable the variable dummy mode when doing timing tuning mspi_timing_ll_enable_flash_variable_dummy(1, false); //Get required config, and set them to PSRAM related registers - do_tuning(reference_data, &timing_configs, false); + s_do_tuning(reference_data, &timing_configs, false); + mspi_timing_enter_high_speed_mode(true); } @@ -473,24 +403,13 @@ void mspi_timing_psram_tuning(void) /*------------------------------------------------------------------------------ * APIs to make SPI0 (and SPI1) FLASH work for high/low freq *----------------------------------------------------------------------------*/ -#if MSPI_TIMING_FLASH_NEEDS_TUNING || MSPI_TIMING_PSRAM_NEEDS_TUNING -static void clear_timing_tuning_regs(bool control_spi1) -{ - mspi_timing_config_flash_set_din_mode_num(0, 0, 0); //SPI0 and SPI1 share the registers for flash din mode and num setting, so we only set SPI0's reg - mspi_timing_config_flash_set_extra_dummy(0, 0); - if (control_spi1) { - mspi_timing_config_flash_set_extra_dummy(1, 0); - } else { - //Won't touch SPI1 registers - } - - mspi_timing_config_psram_set_din_mode_num(0, 0, 0); - mspi_timing_config_psram_set_extra_dummy(0, 0); -} -#endif //#if MSPI_TIMING_FLASH_NEEDS_TUNING || MSPI_TIMING_PSRAM_NEEDS_TUNING - void mspi_timing_enter_low_speed_mode(bool control_spi1) { +#if SOC_MEMSPI_FLASH_CLK_SRC_IS_INDEPENDENT + spimem_flash_ll_set_clock_source(MSPI_CLK_SRC_ROM_DEFAULT); +#endif //SOC_MEMSPI_FLASH_CLK_SRC_IS_INDEPENDENT + +#if SOC_SPI_MEM_SUPPORT_TIMING_TUNING /** * Here we are going to slow the SPI1 frequency to 20Mhz, so we need to set SPI1 din_num and din_mode regs. * @@ -498,41 +417,23 @@ void mspi_timing_enter_low_speed_mode(bool control_spi1) * 0, if the SPI0 flash module clock is still in high freq, it may not work correctly. * * Therefore, here we need to slow both the SPI0 and SPI1 and related timing tuning regs to 20Mhz configuration. + * + * Currently we only need to change these clocks on chips with timing tuning + * Should be extended to other no-timing-tuning chips if needed. e.g.: + * we still need to turn down Flash / PSRAM clock speed at a certain period of time */ - - //Switch SPI1 and SPI0 clock as 20MHz, set its SPIMEM core clock as 80M and set clock division as 4 - mspi_timing_config_set_core_clock(0, MSPI_TIMING_CONFIG_CORE_CLOCK_80M); //SPI0 and SPI1 share the register for core clock. So we only set SPI0 here. - mspi_timing_config_set_flash_clock(0, 4); - if (control_spi1) { - //After tuning, won't touch SPI1 again - mspi_timing_config_set_flash_clock(1, 4); - } - - //Set PSRAM module clock - mspi_timing_config_set_psram_clock(0, 4); + mspi_timing_config_set_flash_clock(20, MSPI_TIMING_SPEED_MODE_LOW_PERF, control_spi1); + mspi_timing_config_set_psram_clock(20, MSPI_TIMING_SPEED_MODE_LOW_PERF, control_spi1); +#endif //#if SOC_SPI_MEM_SUPPORT_TIMING_TUNING #if MSPI_TIMING_FLASH_NEEDS_TUNING || MSPI_TIMING_PSRAM_NEEDS_TUNING - clear_timing_tuning_regs(control_spi1); -#endif -} - -#if MSPI_TIMING_FLASH_NEEDS_TUNING || MSPI_TIMING_PSRAM_NEEDS_TUNING -static void set_timing_tuning_regs_as_required(bool control_spi1) -{ - //SPI0 and SPI1 share the registers for flash din mode and num setting, so we only set SPI0's reg - mspi_timing_config_flash_set_din_mode_num(0, s_flash_best_timing_tuning_config.spi_din_mode, s_flash_best_timing_tuning_config.spi_din_num); - mspi_timing_config_flash_set_extra_dummy(0, s_flash_best_timing_tuning_config.extra_dummy_len); - if (control_spi1) { - mspi_timing_config_flash_set_extra_dummy(1, s_flash_best_timing_tuning_config.extra_dummy_len); - } - - mspi_timing_config_psram_set_din_mode_num(0, s_psram_best_timing_tuning_config.spi_din_mode, s_psram_best_timing_tuning_config.spi_din_num); - mspi_timing_config_psram_set_extra_dummy(0, s_psram_best_timing_tuning_config.extra_dummy_len); -} + mspi_timing_flash_config_clear_tuning_regs(control_spi1); + mspi_timing_psram_config_clear_tuning_regs(control_spi1); #endif //#if MSPI_TIMING_FLASH_NEEDS_TUNING || MSPI_TIMING_PSRAM_NEEDS_TUNING +} /** - * Set SPI0 FLASH and PSRAM module clock, din_num, din_mode and extra dummy, + * Set FLASH and PSRAM module clock, din_num, din_mode and extra dummy, * according to the configuration got from timing tuning function (`calculate_best_flash_tuning_config`). * iF control_spi1 == 1, will also update SPI1 timing registers. Should only be set to 1 when do tuning. * @@ -540,29 +441,41 @@ static void set_timing_tuning_regs_as_required(bool control_spi1) */ void mspi_timing_enter_high_speed_mode(bool control_spi1) { - mspi_timing_config_core_clock_t core_clock = get_mspi_core_clock(); - uint32_t flash_div = get_flash_clock_divider(); - uint32_t psram_div = get_psram_clock_divider(); +#if SOC_MEMSPI_FLASH_CLK_SRC_IS_INDEPENDENT + spimem_flash_ll_set_clock_source(MSPI_CLK_SRC_DEFAULT); +#endif //SOC_MEMSPI_FLASH_CLK_SRC_IS_INDEPENDENT - //Set SPI01 core clock - mspi_timing_config_set_core_clock(0, core_clock); //SPI0 and SPI1 share the register for core clock. So we only set SPI0 here. - //Set FLASH module clock - mspi_timing_config_set_flash_clock(0, flash_div); - if (control_spi1) { - mspi_timing_config_set_flash_clock(1, flash_div); - } - //Set PSRAM module clock - mspi_timing_config_set_psram_clock(0, psram_div); +#if SOC_SPI_MEM_SUPPORT_TIMING_TUNING + /** + * Currently we only need to change these clocks on chips with timing tuning + * Should be extended to other no-timing-tuning chips if needed. e.g.: + * we still need to turn down Flash / PSRAM clock speed at a certain period of time + */ + mspi_timing_config_set_flash_clock(FLASH_FREQUENCY_MHZ, MSPI_TIMING_SPEED_MODE_NORMAL_PERF, control_spi1); +#if CONFIG_SPIRAM + mspi_timing_config_set_psram_clock(CONFIG_SPIRAM_SPEED, MSPI_TIMING_SPEED_MODE_NORMAL_PERF, control_spi1); +#endif //#if CONFIG_SPIRAM +#endif //#if SOC_SPI_MEM_SUPPORT_TIMING_TUNING #if MSPI_TIMING_FLASH_NEEDS_TUNING || MSPI_TIMING_PSRAM_NEEDS_TUNING - set_timing_tuning_regs_as_required(true); -#endif + mspi_timing_flash_config_set_tuning_regs(control_spi1); + mspi_timing_psram_config_set_tuning_regs(control_spi1); +#endif //#if MSPI_TIMING_FLASH_NEEDS_TUNING || MSPI_TIMING_PSRAM_NEEDS_TUNING } void mspi_timing_change_speed_mode_cache_safe(bool switch_down) { - Cache_Freeze_ICache_Enable(1); - Cache_Freeze_DCache_Enable(1); + /** + * If a no-cache-freeze-supported chip needs timing tuning, add a protection way: + * - spinlock + * - or other way + * + * for preventing concurrent from MSPI to external memory + */ +#if SOC_CACHE_FREEZE_SUPPORTED + cache_hal_freeze(CACHE_TYPE_ALL); +#endif //#if SOC_CACHE_FREEZE_SUPPORTED + if (switch_down) { //enter MSPI low speed mode, extra delays should be removed mspi_timing_enter_low_speed_mode(false); @@ -570,8 +483,10 @@ void mspi_timing_change_speed_mode_cache_safe(bool switch_down) //enter MSPI high speed mode, extra delays should be considered mspi_timing_enter_high_speed_mode(false); } - Cache_Freeze_DCache_Disable(); - Cache_Freeze_ICache_Disable(); + +#if SOC_CACHE_FREEZE_SUPPORTED + cache_hal_unfreeze(CACHE_TYPE_ALL); +#endif //#if SOC_CACHE_FREEZE_SUPPORTED } /*------------------------------------------------------------------------------ @@ -595,7 +510,7 @@ void spi_timing_get_flash_timing_param(spi_flash_hal_timing_config_t *out_timing // Get extra dummy length here. Therefore, no matter what freq, or mode. // If it needs tuning, it will return correct extra dummy len. If no tuning, it will return 0. - out_timing_config->extra_dummy = s_flash_best_timing_tuning_config.extra_dummy_len; + out_timing_config->extra_dummy = mspi_timing_config_get_flash_extra_dummy(); // Get CS setup/hold value here. mspi_timing_config_get_cs_timing(&out_timing_config->cs_setup, &out_timing_config->cs_hold); @@ -607,3 +522,15 @@ void spi_timing_get_flash_timing_param(spi_flash_hal_timing_config_t *out_timing abort(); } #endif // MSPI_TIMING_FLASH_NEEDS_TUNING || MSPI_TIMING_PSRAM_NEEDS_TUNING + +/*------------------------------------------------------------------------------ + * Common settings + *----------------------------------------------------------------------------*/ +void mspi_timing_set_pin_drive_strength(void) +{ +#if SOC_MEMSPI_TIMING_TUNING_BY_MSPI_DELAY + //For now, set them all to 3. Need to check after QVL test results are out. TODO: IDF-3663 + //Set default pin drive + mspi_timing_ll_set_all_pin_drive(0, 3); +#endif // #if SOC_MEMSPI_TIMING_TUNING_BY_MSPI_DELAY +} diff --git a/components/esp_hw_support/port/esp32s3/mspi_timing_config.c b/components/esp_hw_support/port/esp32s3/mspi_timing_config.c deleted file mode 100644 index 7beecc59c1..0000000000 --- a/components/esp_hw_support/port/esp32s3/mspi_timing_config.c +++ /dev/null @@ -1,363 +0,0 @@ -/* - * SPDX-FileCopyrightText: 2019-2023 Espressif Systems (Shanghai) CO LTD - * - * SPDX-License-Identifier: Apache-2.0 - */ - -#include -#include "sdkconfig.h" -#include "string.h" -#include "esp_attr.h" -#include "esp_err.h" -#include "esp_types.h" -#include "esp_log.h" -#include "soc/spi_mem_reg.h" -#include "hal/mspi_timing_tuning_ll.h" -#include "../../mspi_timing_config.h" -#include "bootloader_flash.h" -#include "esp32s3/rom/spi_flash.h" -#include "esp32s3/rom/opi_flash.h" - -#define OPI_PSRAM_SYNC_READ 0x0000 -#define OPI_PSRAM_SYNC_WRITE 0x8080 -#define OCT_PSRAM_RD_DUMMY_NUM (2*(10-1)) -#define OCT_PSRAM_WR_DUMMY_NUM (2*(5-1)) - -#define QPI_PSRAM_FAST_READ 0XEB -#define QPI_PSRAM_WRITE 0X38 -#define QPI_PSRAM_FAST_READ_DUMMY 6 - -#define NOT_INIT_INT 127 - -//-------------------------------------MSPI Clock Setting-------------------------------------// -mspi_timing_config_core_clock_t mspi_timing_config_get_core_clock(void) -{ - switch (MSPI_TIMING_CORE_CLOCK_MHZ) { - case 80: - return MSPI_TIMING_CONFIG_CORE_CLOCK_80M; - case 120: - return MSPI_TIMING_CONFIG_CORE_CLOCK_120M; - case 160: - return MSPI_TIMING_CONFIG_CORE_CLOCK_160M; - case 240: - return MSPI_TIMING_CONFIG_CORE_CLOCK_240M; - default: - abort(); - } -} - -void mspi_timing_config_set_core_clock(uint8_t spi_num, mspi_timing_config_core_clock_t core_clock) -{ - uint32_t reg_val = 0; - - switch (core_clock) { - case MSPI_TIMING_CONFIG_CORE_CLOCK_80M: - reg_val = 0; - break; - case MSPI_TIMING_CONFIG_CORE_CLOCK_120M: - reg_val = 1; - break; - case MSPI_TIMING_CONFIG_CORE_CLOCK_160M: - reg_val = 2; - break; - case MSPI_TIMING_CONFIG_CORE_CLOCK_240M: - reg_val = 3; - break; - default: - abort(); - } - - mspi_timing_ll_set_core_clock_divider(spi_num, reg_val); -} - -void mspi_timing_config_set_flash_clock(uint8_t spi_num, uint32_t freqdiv) -{ - assert(freqdiv > 0); - mspi_timing_ll_set_flash_clock(spi_num, freqdiv); -} - -void mspi_timing_config_set_psram_clock(uint8_t spi_num, uint32_t freqdiv) -{ - mspi_timing_ll_set_psram_clock(spi_num, freqdiv); -} - -/////////////////////////////////////////TIMING TUNING IS NEEDED////////////////////////////////////////////// -////////////////////////////////////////////////////////////////////////////////////////////////////////////// -#if MSPI_TIMING_FLASH_NEEDS_TUNING || MSPI_TIMING_PSRAM_NEEDS_TUNING -//If one of the FLASH / PSRAM or both of them need timing tuning, we should build following code -typedef enum { - PSRAM_CMD_QPI, - PSRAM_CMD_SPI, -} psram_cmd_mode_t; - -static uint8_t s_rom_flash_extra_dummy[2] = {NOT_INIT_INT, NOT_INIT_INT}; - -#if CONFIG_SPIRAM_MODE_QUAD -static uint8_t s_psram_extra_dummy; -extern void psram_exec_cmd(int spi_num, psram_cmd_mode_t mode, - uint32_t cmd, int cmd_bit_len, - uint32_t addr, int addr_bit_len, - int dummy_bits, - uint8_t* mosi_data, int mosi_bit_len, - uint8_t* miso_data, int miso_bit_len, - uint32_t cs_mask, - bool is_write_erase_operation); -#endif - -//-------------------------------------FLASH timing tuning register config-------------------------------------// -void mspi_timing_config_flash_set_din_mode_num(uint8_t spi_num, uint8_t din_mode, uint8_t din_num) -{ - mspi_timing_ll_set_flash_din_mode(spi_num, din_mode); - mspi_timing_ll_set_flash_din_num(spi_num, din_num); -} - -static uint32_t spi_timing_config_get_dummy(void) -{ - mspi_timing_ll_flash_mode_t mode = mspi_timing_ll_get_flash_mode(0); - if (mode == MSPI_TIMING_LL_FLASH_OPI_MODE) { - abort(); - } - -#if CONFIG_SPI_FLASH_HPM_ENABLE - if (spi_flash_hpm_dummy_adjust()) { // HPM is enabled - const spi_flash_hpm_dummy_conf_t *hpm_dummy = spi_flash_hpm_get_dummy(); - switch (mode) { - case MSPI_TIMING_LL_FLASH_QIO_MODE: - return hpm_dummy->qio_dummy - 1; - case MSPI_TIMING_LL_FLASH_QUAD_MODE: - return hpm_dummy->qout_dummy - 1; - case MSPI_TIMING_LL_FLASH_DIO_MODE: - return hpm_dummy->dio_dummy - 1; - case MSPI_TIMING_LL_FLASH_DUAL_MODE: - return hpm_dummy->dout_dummy - 1; - case MSPI_TIMING_LL_FLASH_FAST_MODE: - return hpm_dummy->fastrd_dummy - 1; - case MSPI_TIMING_LL_FLASH_SLOW_MODE: - return 0; - default: - abort(); - } - } else -#endif - { // HPM is not enabled - switch (mode) { - case MSPI_TIMING_LL_FLASH_QIO_MODE: - return SPI1_R_QIO_DUMMY_CYCLELEN; - case MSPI_TIMING_LL_FLASH_QUAD_MODE: - return SPI1_R_FAST_DUMMY_CYCLELEN; - case MSPI_TIMING_LL_FLASH_DIO_MODE: - return SPI1_R_DIO_DUMMY_CYCLELEN; - case MSPI_TIMING_LL_FLASH_DUAL_MODE: - return SPI1_R_FAST_DUMMY_CYCLELEN; - case MSPI_TIMING_LL_FLASH_FAST_MODE: - return SPI1_R_FAST_DUMMY_CYCLELEN; - case MSPI_TIMING_LL_FLASH_SLOW_MODE: - return 0; - default: - abort(); - } - } -} - -void mspi_timing_config_flash_set_extra_dummy(uint8_t spi_num, uint8_t extra_dummy) -{ - if (bootloader_flash_is_octal_mode_enabled()) { - mspi_timing_ll_set_octal_flash_extra_dummy(spi_num, extra_dummy); - return; - } - /** - * HW workaround: - * The `SPI_MEM_TIMING_CALI_REG` register is only used for OPI on 728 - * Here we only need to update this global variable for extra dummy. Since we use the ROM Flash API, which will set the dummy based on this. - * We only initialise the SPI0. And leave the SPI1 for flash driver to configure. - */ - if (s_rom_flash_extra_dummy[spi_num] == NOT_INIT_INT) { - s_rom_flash_extra_dummy[spi_num] = g_rom_spiflash_dummy_len_plus[spi_num]; - } - g_rom_spiflash_dummy_len_plus[spi_num] = s_rom_flash_extra_dummy[spi_num] + extra_dummy; - - // Only Quad Flash will run into this branch. - uint32_t dummy = spi_timing_config_get_dummy(); - mspi_timing_ll_set_quad_flash_dummy(spi_num, dummy + g_rom_spiflash_dummy_len_plus[spi_num]); -} - -//-------------------------------------PSRAM timing tuning register config-------------------------------------// -void mspi_timing_config_psram_set_din_mode_num(uint8_t spi_num, uint8_t din_mode, uint8_t din_num) -{ - mspi_timing_ll_set_psram_din_mode(spi_num, din_mode); - mspi_timing_ll_set_psram_din_num(spi_num, din_num); -} - -void mspi_timing_config_psram_set_extra_dummy(uint8_t spi_num, uint8_t extra_dummy) -{ -#if CONFIG_SPIRAM_MODE_OCT - mspi_timing_ll_set_octal_psram_extra_dummy(spi_num, extra_dummy); -#elif CONFIG_SPIRAM_MODE_QUAD - //HW workaround: Use normal dummy register to set extra dummy, the calibration dedicated extra dummy register doesn't work for quad mode - mspi_timing_ll_set_quad_psram_dummy(spi_num, (QPI_PSRAM_FAST_READ_DUMMY + extra_dummy - 1)); -#endif -} - -//-------------------------------------------FLASH/PSRAM Read/Write------------------------------------------// -void mspi_timing_config_flash_read_data(uint8_t *buf, uint32_t addr, uint32_t len) -{ - if (bootloader_flash_is_octal_mode_enabled()) { - // note that in spi_flash_read API, there is a wait-idle stage, since flash can only be read in idle state. - // but after we change the timing settings, we might not read correct idle status via RDSR. - // so, here we should use a read API that won't check idle status. - mspi_timing_ll_clear_fifo(1); - esp_rom_opiflash_read_raw(addr, buf, len); - } else { - esp_rom_spiflash_read(addr, (uint32_t *)buf, len); - } -} - -static void s_psram_write_data(uint8_t *buf, uint32_t addr, uint32_t len) -{ -#if CONFIG_SPIRAM_MODE_OCT - esp_rom_opiflash_exec_cmd(1, ESP_ROM_SPIFLASH_OPI_DTR_MODE, - OPI_PSRAM_SYNC_WRITE, 16, - addr, 32, - OCT_PSRAM_WR_DUMMY_NUM, - buf, len * 8, - NULL, 0, - BIT(1), - false); -#elif CONFIG_SPIRAM_MODE_QUAD - psram_exec_cmd(1, 0, - QPI_PSRAM_WRITE, 8, - addr, 24, - 0, - buf, len * 8, - NULL, 0, - SPI_MEM_CS1_DIS_M, - false); -#endif -} - -static void s_psram_read_data(uint8_t *buf, uint32_t addr, uint32_t len) -{ -#if CONFIG_SPIRAM_MODE_OCT - mspi_timing_ll_clear_fifo(1); - esp_rom_opiflash_exec_cmd(1, ESP_ROM_SPIFLASH_OPI_DTR_MODE, - OPI_PSRAM_SYNC_READ, 16, - addr, 32, - OCT_PSRAM_RD_DUMMY_NUM, - NULL, 0, - buf, len * 8, - BIT(1), - false); -#elif CONFIG_SPIRAM_MODE_QUAD - psram_exec_cmd(1, 0, - QPI_PSRAM_FAST_READ, 8, - addr, 24, - QPI_PSRAM_FAST_READ_DUMMY + s_psram_extra_dummy, - NULL, 0, - buf, len * 8, - SPI_MEM_CS1_DIS_M, - false); -#endif -} - -static void s_psram_execution(uint8_t *buf, uint32_t addr, uint32_t len, bool is_read) -{ - while (len) { - uint32_t length = MIN(len, 32); - if (is_read) { - s_psram_read_data(buf, addr, length); - } else { - s_psram_write_data(buf, addr, length); - } - addr += length; - buf += length; - len -= length; - } -} - -void mspi_timing_config_psram_write_data(uint8_t *buf, uint32_t addr, uint32_t len) -{ - s_psram_execution(buf, addr, len, false); -} - -void mspi_timing_config_psram_read_data(uint8_t *buf, uint32_t addr, uint32_t len) -{ - s_psram_execution(buf, addr, len, true); -} - - -/*------------------------------------------------------------------------------------------------- - * SPI1 Timing Tuning APIs - * These APIs are only used in `spi_flash_timing_tuning.c/sweep_for_success_sample_points()` for - * configuring SPI1 timing tuning related registers to find best tuning parameter - *-------------------------------------------------------------------------------------------------*/ -void mspi_timing_config_flash_set_tuning_regs(const mspi_timing_tuning_param_t *params) -{ - /** - * 1. SPI_MEM_DINx_MODE(1), SPI_MEM_DINx_NUM(1) are meaningless - * SPI0 and SPI1 share the SPI_MEM_DINx_MODE(0), SPI_MEM_DINx_NUM(0) for FLASH timing tuning - * 2. We use SPI1 to get the best Flash timing tuning (mode and num) config - */ - mspi_timing_config_flash_set_din_mode_num(0, params->spi_din_mode, params->spi_din_num); - mspi_timing_config_flash_set_extra_dummy(1, params->extra_dummy_len); -} - -void mspi_timing_config_psram_set_tuning_regs(const mspi_timing_tuning_param_t *params) -{ - /** - * 1. SPI_MEM_SPI_SMEM_DINx_MODE(1), SPI_MEM_SPI_SMEM_DINx_NUM(1) are meaningless - * SPI0 and SPI1 share the SPI_MEM_SPI_SMEM_DINx_MODE(0), SPI_MEM_SPI_SMEM_DINx_NUM(0) for PSRAM timing tuning - * 2. We use SPI1 to get the best PSRAM timing tuning (mode and num) config - */ - mspi_timing_config_psram_set_din_mode_num(0, params->spi_din_mode, params->spi_din_num); - -#if CONFIG_SPIRAM_MODE_OCT - //On 728, for SPI1, flash and psram share the extra dummy register - mspi_timing_config_flash_set_extra_dummy(1, params->extra_dummy_len); -#elif CONFIG_SPIRAM_MODE_QUAD - //Update this `s_psram_extra_dummy`, the `s_psram_read_data` will set dummy according to this `s_psram_extra_dummy` - s_psram_extra_dummy = params->extra_dummy_len; - mspi_timing_ll_set_quad_flash_dummy(1, s_psram_extra_dummy - 1); -#endif -} - -#endif //#if MSPI_TIMING_FLASH_NEEDS_TUNING || MSPI_TIMING_PSRAM_NEEDS_TUNING - - -/*------------------------------------------------------------------------------------------------- - * To let upper lay (spi_flash_timing_tuning.c) to know the necessary timing registers - *-------------------------------------------------------------------------------------------------*/ -/** - * 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. - * @note This function inform `spi_flash_timing_tuning.c` (driver layer) of the cycle, - * and other component (esp_flash driver) should get these cycle and configure the registers accordingly. - */ -void mspi_timing_config_get_cs_timing(uint8_t *setup_time, uint32_t *hold_time) -{ - *setup_time = mspi_timing_ll_get_cs_setup_val(0); - *hold_time = mspi_timing_ll_get_cs_hold_val(0); - /** - * 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 (mspi_timing_ll_is_cs_setup_enabled(0)) { - *setup_time += 1; - } else { - *setup_time = 0; - } - if (mspi_timing_ll_is_cs_hold_enabled(0)) { - *hold_time += 1; - } else { - *hold_time = 0; - } -} - -/** - * Get the SPI1 Flash clock setting. - * @note Similarly, this function inform `spi_flash_timing_tuning.c` (driver layer) of the clock setting, - * and other component (esp_flash driver) should get these and configure the registers accordingly. - */ -uint32_t mspi_timing_config_get_flash_clock_reg(void) -{ - return mspi_timing_ll_get_clock_reg(1); -} diff --git a/components/esp_hw_support/port/esp32s3/mspi_timing_tuning_configs.h b/components/esp_hw_support/port/esp32s3/mspi_timing_tuning_configs.h index 274dbd3dec..aa4dfabe25 100644 --- a/components/esp_hw_support/port/esp32s3/mspi_timing_tuning_configs.h +++ b/components/esp_hw_support/port/esp32s3/mspi_timing_tuning_configs.h @@ -239,6 +239,7 @@ ESP_STATIC_ASSERT(CHECK_POWER_OF_2(MSPI_TIMING_CORE_CLOCK_MHZ / MSPI_TIMING_PSRA #define MSPI_TIMING_PSRAM_CONFIG_NUM_CORE_CLK_120M_MODULE_CLK_120M_STR_MODE 12 #define MSPI_TIMING_PSRAM_DEFAULT_CONFIG_ID_CORE_CLK_120M_MODULE_CLK_120M_STR_MODE 2 + //PSRAM: core clock 240M, module clock 120M, DTR mode #define MSPI_TIMING_PSRAM_CONFIG_TABLE_CORE_CLK_240M_MODULE_CLK_120M_DTR_MODE {{0, 0, 0}, {4, 1, 2}, {1, 0, 1}, {4, 0, 2}, {0, 0, 1}, {4, 1, 3}, {1, 0, 2}, {4, 0, 3}, {0, 0, 2}, {4, 1, 4}, {1, 0, 3}, {4, 0, 4}, {0, 0, 3}, {4, 1, 5}} #define MSPI_TIMING_PSRAM_CONFIG_NUM_CORE_CLK_240M_MODULE_CLK_120M_DTR_MODE 14 diff --git a/components/esp_hw_support/test_apps/mspi/sdkconfig.ci.f8r8_120ddr b/components/esp_hw_support/test_apps/mspi/sdkconfig.ci.f8r8_120ddr deleted file mode 100644 index 9289b41d45..0000000000 --- a/components/esp_hw_support/test_apps/mspi/sdkconfig.ci.f8r8_120ddr +++ /dev/null @@ -1,6 +0,0 @@ -# F8R8, Flash 120M DDR, PSRAM disable - -CONFIG_ESPTOOLPY_OCT_FLASH=y -CONFIG_ESPTOOLPY_FLASH_SAMPLE_MODE_DTR=y -CONFIG_ESPTOOLPY_FLASHFREQ_120M=y -CONFIG_ESPTOOLPY_FLASHSIZE_4MB=y diff --git a/components/esp_hw_support/test_apps/mspi/sdkconfig.ci.f8r8_120ddr_120ddr b/components/esp_hw_support/test_apps/mspi/sdkconfig.ci.f8r8_120ddr_120ddr deleted file mode 100644 index 290f4c92d9..0000000000 --- a/components/esp_hw_support/test_apps/mspi/sdkconfig.ci.f8r8_120ddr_120ddr +++ /dev/null @@ -1,10 +0,0 @@ -# F8R8, Flash 120M DDR, PSRAM 120M DDR - -CONFIG_ESPTOOLPY_OCT_FLASH=y -CONFIG_ESPTOOLPY_FLASH_SAMPLE_MODE_DTR=y -CONFIG_ESPTOOLPY_FLASHFREQ_120M=y -CONFIG_ESPTOOLPY_FLASHSIZE_4MB=y - -CONFIG_SPIRAM=y -CONFIG_SPIRAM_MODE_OCT=y -CONFIG_SPIRAM_SPEED_120M=y diff --git a/components/hal/esp32s3/include/hal/clk_tree_ll.h b/components/hal/esp32s3/include/hal/clk_tree_ll.h index c333775d6b..369a4a2ee8 100644 --- a/components/hal/esp32s3/include/hal/clk_tree_ll.h +++ b/components/hal/esp32s3/include/hal/clk_tree_ll.h @@ -722,6 +722,7 @@ void clk_ll_bbpll_set_frequency_for_mspi_tuning(rtc_xtal_freq_t xtal_freq, int p REGI2C_WRITE_MASK(I2C_BBPLL, I2C_BBPLL_OC_DLREF_SEL, 1); } + #ifdef __cplusplus } #endif diff --git a/components/hal/esp32s3/include/hal/mspi_timing_tuning_ll.h b/components/hal/esp32s3/include/hal/mspi_timing_tuning_ll.h index e1eade8941..0b047ec747 100644 --- a/components/hal/esp32s3/include/hal/mspi_timing_tuning_ll.h +++ b/components/hal/esp32s3/include/hal/mspi_timing_tuning_ll.h @@ -35,6 +35,8 @@ extern "C" { #define MSPI_TIMING_LL_FLASH_FAST_MODE_MASK (SPI_MEM_FASTRD_MODE) #define MSPI_TIMING_LL_FLASH_SLOW_MODE_MASK 0 +#define MSPI_TIMING_LL_CORE_CLOCK_MHZ_DEFAULT 80 + typedef enum { MSPI_TIMING_LL_FLASH_OPI_MODE = BIT(0), MSPI_TIMING_LL_FLASH_QIO_MODE = BIT(1), @@ -133,15 +135,34 @@ static inline void mspi_timing_ll_enable_flash_variable_dummy(uint8_t spi_num, b } /** - * Set MSPI core clock divider + * Set MSPI core clock * - * @param spi_num SPI0 / SPI1 - * @param val Divider value + * @param spi_num SPI0 / SPI1 + * @param core_clk_mhz core clock mhz */ __attribute__((always_inline)) -static inline void mspi_timing_ll_set_core_clock_divider(uint8_t spi_num, uint32_t val) +static inline void mspi_timing_ll_set_core_clock(uint8_t spi_num, uint32_t core_clk_mhz) { - REG_SET_FIELD(SPI_MEM_CORE_CLK_SEL_REG(spi_num), SPI_MEM_CORE_CLK_SEL, val); + uint32_t reg_val = 0; + + switch (core_clk_mhz) { + case 80: + reg_val = 0; + break; + case 120: + reg_val = 1; + break; + case 160: + reg_val = 2; + break; + case 240: + reg_val = 3; + break; + default: + HAL_ASSERT(false); + } + + REG_SET_FIELD(SPI_MEM_CORE_CLK_SEL_REG(spi_num), SPI_MEM_CORE_CLK_SEL, reg_val); } /** diff --git a/components/hal/spi_flash_hal.c b/components/hal/spi_flash_hal.c index aea33eb0c8..75992468b8 100644 --- a/components/hal/spi_flash_hal.c +++ b/components/hal/spi_flash_hal.c @@ -70,7 +70,7 @@ static inline int get_dummy_n(bool gpio_is_used, int input_delay_ns, int eff_clk return apb_period_n / apbclk_n; } -#if SOC_SPI_MEM_SUPPORT_TIME_TUNING +#if SOC_SPI_MEM_SUPPORT_TIMING_TUNING static inline int extra_dummy_under_timing_tuning(const spi_flash_hal_config_t *cfg) { bool main_flash = (cfg->host_id == SPI1_HOST && cfg->cs_num == 0); @@ -88,7 +88,7 @@ static inline int extra_dummy_under_timing_tuning(const spi_flash_hal_config_t * return extra_dummy; } -#endif //SOC_SPI_MEM_SUPPORT_TIME_TUNING +#endif //SOC_SPI_MEM_SUPPORT_TIMING_TUNING esp_err_t spi_flash_hal_init(spi_flash_hal_context_t *data_out, const spi_flash_hal_config_t *cfg) { @@ -104,12 +104,12 @@ esp_err_t spi_flash_hal_init(spi_flash_hal_context_t *data_out, const spi_flash_ .cs_setup = cfg->cs_setup, .base_io_mode = cfg->default_io_mode, }; -#if SOC_SPI_MEM_SUPPORT_TIME_TUNING +#if SOC_SPI_MEM_SUPPORT_TIMING_TUNING if (cfg->using_timing_tuning) { data_out->extra_dummy = extra_dummy_under_timing_tuning(cfg); data_out->clock_conf = cfg->clock_config; } else -#endif // SOC_SPI_MEM_SUPPORT_TIME_TUNING +#endif // SOC_SPI_MEM_SUPPORT_TIMING_TUNING { data_out->extra_dummy = get_dummy_n(!cfg->iomux, cfg->input_delay_ns, APB_CLK_FREQ/get_flash_clock_divider(cfg)); data_out->clock_conf = (spi_flash_ll_clock_reg_t)spi_flash_cal_clock(cfg); diff --git a/components/hal/spi_flash_hal_common.inc b/components/hal/spi_flash_hal_common.inc index c766d61f42..11ba34d2f9 100644 --- a/components/hal/spi_flash_hal_common.inc +++ b/components/hal/spi_flash_hal_common.inc @@ -65,7 +65,7 @@ esp_err_t spi_flash_hal_device_config(spi_flash_host_inst_t *host) spi_flash_hal_disable_auto_resume_mode(host); } #endif //SOC_SPI_MEM_SUPPORT_AUTO_SUSPEND -#if SOC_SPI_MEM_SUPPORT_TIME_TUNING +#if SOC_SPI_MEM_SUPPORT_TIMING_TUNING // Always keep the extra dummy on SPI1 is 0, add extra dummy to user dummy spimem_flash_ll_set_extra_dummy((spi_mem_dev_t*)dev, 0); #endif diff --git a/components/soc/esp32h2/include/soc/Kconfig.soc_caps.in b/components/soc/esp32h2/include/soc/Kconfig.soc_caps.in index 61c58b8bfc..b57e04b2e9 100644 --- a/components/soc/esp32h2/include/soc/Kconfig.soc_caps.in +++ b/components/soc/esp32h2/include/soc/Kconfig.soc_caps.in @@ -879,7 +879,7 @@ config SOC_MEMSPI_SRC_FREQ_16M_SUPPORTED bool default y -config SOC_MEMSPI_CLOCK_IS_INDEPENDENT +config SOC_MEMSPI_FLASH_CLK_SRC_IS_INDEPENDENT bool default y diff --git a/components/soc/esp32h2/include/soc/soc_caps.h b/components/soc/esp32h2/include/soc/soc_caps.h index 48ad55dee1..2cf23e2852 100644 --- a/components/soc/esp32h2/include/soc/soc_caps.h +++ b/components/soc/esp32h2/include/soc/soc_caps.h @@ -380,7 +380,7 @@ #define SOC_MEMSPI_SRC_FREQ_64M_SUPPORTED 1 #define SOC_MEMSPI_SRC_FREQ_32M_SUPPORTED 1 #define SOC_MEMSPI_SRC_FREQ_16M_SUPPORTED 1 -#define SOC_MEMSPI_CLOCK_IS_INDEPENDENT 1 +#define SOC_MEMSPI_FLASH_CLK_SRC_IS_INDEPENDENT 1 /*-------------------------- SYSTIMER CAPS ----------------------------------*/ #define SOC_SYSTIMER_COUNTER_NUM 2 // Number of counter units diff --git a/components/soc/esp32s3/include/soc/Kconfig.soc_caps.in b/components/soc/esp32s3/include/soc/Kconfig.soc_caps.in index c83e8d3403..8352676154 100644 --- a/components/soc/esp32s3/include/soc/Kconfig.soc_caps.in +++ b/components/soc/esp32s3/include/soc/Kconfig.soc_caps.in @@ -1187,7 +1187,7 @@ config SOC_SPI_MEM_SUPPORT_OPI_MODE bool default y -config SOC_SPI_MEM_SUPPORT_TIME_TUNING +config SOC_SPI_MEM_SUPPORT_TIMING_TUNING bool default y @@ -1199,6 +1199,14 @@ config SOC_SPI_MEM_SUPPORT_WRAP bool default y +config SOC_MEMSPI_TIMING_TUNING_BY_MSPI_DELAY + bool + default y + +config SOC_MEMSPI_CORE_CLK_SHARED_WITH_PSRAM + bool + default y + config SOC_COEX_HW_PTI bool default y diff --git a/components/soc/esp32s3/include/soc/soc_caps.h b/components/soc/esp32s3/include/soc/soc_caps.h index 80f60442eb..2486847850 100644 --- a/components/soc/esp32s3/include/soc/soc_caps.h +++ b/components/soc/esp32s3/include/soc/soc_caps.h @@ -477,9 +477,11 @@ #define SOC_SPI_MEM_SUPPORT_AUTO_RESUME (1) #define SOC_SPI_MEM_SUPPORT_SW_SUSPEND (1) #define SOC_SPI_MEM_SUPPORT_OPI_MODE (1) -#define SOC_SPI_MEM_SUPPORT_TIME_TUNING (1) +#define SOC_SPI_MEM_SUPPORT_TIMING_TUNING (1) #define SOC_SPI_MEM_SUPPORT_CONFIG_GPIO_BY_EFUSE (1) #define SOC_SPI_MEM_SUPPORT_WRAP (1) +#define SOC_MEMSPI_TIMING_TUNING_BY_MSPI_DELAY (1) +#define SOC_MEMSPI_CORE_CLK_SHARED_WITH_PSRAM (1) /*-------------------------- COEXISTENCE HARDWARE PTI CAPS -------------------------------*/ #define SOC_COEX_HW_PTI (1) diff --git a/components/spi_flash/esp_flash_spi_init.c b/components/spi_flash/esp_flash_spi_init.c index eb56116fef..cda8449b68 100644 --- a/components/spi_flash/esp_flash_spi_init.c +++ b/components/spi_flash/esp_flash_spi_init.c @@ -364,12 +364,12 @@ 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 SOC_SPI_MEM_SUPPORT_TIMING_TUNING if (spi_timing_is_tuned()) { cfg.using_timing_tuning = 1; spi_timing_get_flash_timing_param(&cfg.timing_reg); } - #endif // SOC_SPI_MEM_SUPPORT_TIME_TUNING + #endif // SOC_SPI_MEM_SUPPORT_TIMING_TUNING cfg.clock_src_freq = spi_flash_ll_get_source_clock_freq_mhz(cfg.host_id);