From 6c0aebe2794cc98134e2703b1a57cb460eb04e1d Mon Sep 17 00:00:00 2001 From: Cao Sen Miao Date: Wed, 1 Sep 2021 15:58:15 +0800 Subject: [PATCH] esp_flash: add opi flash support in esp_flash chip driver, for MXIC --- components/esp_system/port/cpu_start.c | 4 +- components/esptool_py/Kconfig.projbuild | 58 +-- .../hal/esp32s3/include/hal/spimem_flash_ll.h | 21 +- components/hal/include/hal/spi_flash_hal.h | 31 +- components/hal/include/hal/spi_flash_types.h | 6 +- components/hal/spi_flash_hal.c | 45 +- components/hal/spi_flash_hal_common.inc | 6 +- components/soc/esp32s3/include/soc/soc_caps.h | 3 + components/spi_flash/CMakeLists.txt | 1 + components/spi_flash/Kconfig | 9 + .../spi_flash/esp32s3/spi_timing_config.c | 55 +++ .../spi_flash/esp32s3/spi_timing_config.h | 8 + components/spi_flash/esp_flash_api.c | 96 +++- components/spi_flash/esp_flash_spi_init.c | 26 +- components/spi_flash/flash_ops.c | 7 +- .../include/spi_flash/spi_flash_defs.h | 4 + .../spi_flash/include/spi_flash_chip_driver.h | 14 +- .../include/spi_flash_chip_generic.h | 15 +- .../spi_flash/include/spi_flash_chip_mxic.h | 7 + .../spi_flash/include/spi_flash_private.h | 31 +- components/spi_flash/linker.lf | 3 + components/spi_flash/memspi_host_driver.c | 32 +- components/spi_flash/spi_flash_chip_boya.c | 1 + components/spi_flash/spi_flash_chip_drivers.c | 3 + components/spi_flash/spi_flash_chip_gd.c | 1 + components/spi_flash/spi_flash_chip_generic.c | 22 +- components/spi_flash/spi_flash_chip_issi.c | 1 + components/spi_flash/spi_flash_chip_mxic.c | 56 +-- .../spi_flash/spi_flash_chip_mxic_opi.c | 420 ++++++++++++++++++ components/spi_flash/spi_flash_chip_winbond.c | 7 +- .../spi_flash/spi_flash_timing_tuning.c | 30 +- components/spi_flash/test/test_esp_flash.c | 6 +- components/spi_flash/test/test_read_write.c | 40 +- 33 files changed, 904 insertions(+), 165 deletions(-) create mode 100644 components/spi_flash/spi_flash_chip_mxic_opi.c diff --git a/components/esp_system/port/cpu_start.c b/components/esp_system/port/cpu_start.c index 5b76be681c..dc2f51a5e0 100644 --- a/components/esp_system/port/cpu_start.c +++ b/components/esp_system/port/cpu_start.c @@ -372,6 +372,9 @@ void IRAM_ATTR call_start_cpu0(void) #endif // CONFIG_IDF_TARGET_ESP32S3 || CONFIG_IDF_TARGET_ESP32C3 || CONFIG_IDF_TARGET_ESP32H2 esp_mspi_pin_init(); + // For Octal flash, it's hard to implement a read_id function in OPI mode for all vendors. + // So we have to read it here in SPI mode, before entering the OPI mode. + bootloader_flash_update_id(); #if CONFIG_ESPTOOLPY_OCT_FLASH bool efuse_opflash_en = REG_GET_FIELD(EFUSE_RD_REPEAT_DATA3_REG, EFUSE_FLASH_TYPE); if (!efuse_opflash_en) { @@ -571,7 +574,6 @@ void IRAM_ATTR call_start_cpu0(void) } #endif - bootloader_flash_update_id(); // Read the application binary image header. This will also decrypt the header if the image is encrypted. __attribute__((unused)) esp_image_header_t fhdr = {0}; #ifdef CONFIG_APP_BUILD_TYPE_ELF_RAM diff --git a/components/esptool_py/Kconfig.projbuild b/components/esptool_py/Kconfig.projbuild index 61f63fda44..1e0b35beb8 100644 --- a/components/esptool_py/Kconfig.projbuild +++ b/components/esptool_py/Kconfig.projbuild @@ -75,8 +75,33 @@ menu "Serial flasher config" bool "MXIC OPI FLASH(MX25UM25645G)" endchoice + choice ESPTOOLPY_FLASHMODE + prompt "Flash SPI mode" + default ESPTOOLPY_FLASHMODE_DIO + default ESPTOOLPY_FLASHMODE_OPI if ESPTOOLPY_OCT_FLASH + help + Mode the flash chip is flashed in, as well as the default mode for the + binary to run in. + + config ESPTOOLPY_FLASHMODE_QIO + depends on !ESPTOOLPY_OCT_FLASH + bool "QIO" + config ESPTOOLPY_FLASHMODE_QOUT + depends on !ESPTOOLPY_OCT_FLASH + bool "QOUT" + config ESPTOOLPY_FLASHMODE_DIO + depends on !ESPTOOLPY_OCT_FLASH + bool "DIO" + config ESPTOOLPY_FLASHMODE_DOUT + depends on !ESPTOOLPY_OCT_FLASH + bool "DOUT" + config ESPTOOLPY_FLASHMODE_OPI + depends on ESPTOOLPY_OCT_FLASH + bool "OPI" + endchoice + choice ESPTOOLPY_FLASHMODE_OCT - depends on ESPTOOLPY_OCT_FLASH + depends on ESPTOOLPY_FLASHMODE_OPI prompt "Flash OPI mode" default ESPTOOLPY_FLASHMODE_OPI_DTR @@ -88,28 +113,6 @@ menu "Serial flasher config" bool "OPI_DTR" endchoice - choice ESPTOOLPY_FLASHMODE - prompt "Flash SPI mode" - default ESPTOOLPY_FLASHMODE_DIO - depends on !ESPTOOLPY_OCT_FLASH - help - Mode the flash chip is flashed in, as well as the default mode for the - binary to run in. - - config ESPTOOLPY_FLASHMODE_QIO - bool "QIO" - config ESPTOOLPY_FLASHMODE_QOUT - bool "QOUT" - config ESPTOOLPY_FLASHMODE_DIO - bool "DIO" - config ESPTOOLPY_FLASHMODE_DOUT - bool "DOUT" - config ESPTOOLPY_FLASHMODE_FASTRD - bool "FASTRD" - config ESPTOOLPY_FLASHMODE_SLOWRD - bool "SLOWRD" - endchoice - # Note: we use esptool.py to flash bootloader in # dio mode for QIO/QOUT, bootloader then upgrades # itself to quad mode during initialisation @@ -119,10 +122,11 @@ menu "Serial flasher config" default "dio" if ESPTOOLPY_FLASHMODE_QOUT default "dio" if ESPTOOLPY_FLASHMODE_DIO default "dout" if ESPTOOLPY_FLASHMODE_DOUT - default "dout" if ESPTOOLPY_FLASHMODE_FASTRD - default "dout" if ESPTOOLPY_FLASHMODE_SLOWRD - default "dout" if ESPTOOLPY_FLASHMODE_OPI_STR - default "dout" if ESPTOOLPY_FLASHMODE_OPI_DTR + # The 1st and 2nd bootloader doesn't support opi mode, + # using fastrd instead. For now the ESPTOOL doesn't support + # fasted (see ESPTOOL-274), using dout instead. In ROM the flash mode + # information get from efuse, so don't care this dout choice. + default "dout" if ESPTOOLPY_FLASHMODE_OPI choice ESPTOOLPY_FLASHFREQ prompt "Flash SPI speed" diff --git a/components/hal/esp32s3/include/hal/spimem_flash_ll.h b/components/hal/esp32s3/include/hal/spimem_flash_ll.h index d435cbfaf7..ea394d2f47 100644 --- a/components/hal/esp32s3/include/hal/spimem_flash_ll.h +++ b/components/hal/esp32s3/include/hal/spimem_flash_ll.h @@ -382,7 +382,8 @@ static inline void spimem_flash_ll_set_cs_pin(spi_mem_dev_t *dev, int pin) static inline void spimem_flash_ll_set_read_mode(spi_mem_dev_t *dev, esp_flash_io_mode_t read_mode) { typeof (dev->ctrl) ctrl = dev->ctrl; - ctrl.val &= ~(SPI_MEM_FREAD_QIO_M | SPI_MEM_FREAD_QUAD_M | SPI_MEM_FREAD_DIO_M | SPI_MEM_FREAD_DUAL_M); + ctrl.val &= ~(SPI_MEM_FREAD_QIO_M | SPI_MEM_FREAD_QUAD_M | SPI_MEM_FREAD_DIO_M | SPI_MEM_FREAD_DUAL_M | SPI_MEM_FCMD_OCT | SPI_MEM_FADDR_OCT | SPI_MEM_FDIN_OCT | SPI_MEM_FDOUT_OCT); + dev->ddr.fmem_ddr_en = 0; ctrl.val |= SPI_MEM_FASTRD_MODE_M; switch (read_mode) { case SPI_FLASH_FASTRD: @@ -403,6 +404,19 @@ static inline void spimem_flash_ll_set_read_mode(spi_mem_dev_t *dev, esp_flash_i case SPI_FLASH_SLOWRD: ctrl.fastrd_mode = 0; break; + case SPI_FLASH_OPI_STR: + ctrl.faddr_oct = 1; + ctrl.fcmd_oct = 1; + ctrl.fdin_oct = 1; + ctrl.fdout_oct = 1; + break; + case SPI_FLASH_OPI_DTR: + ctrl.faddr_oct = 1; + ctrl.fcmd_oct = 1; + ctrl.fdin_oct = 1; + ctrl.fdout_oct = 1; + dev->ddr.fmem_ddr_en = 1; + break; default: abort(); } @@ -546,6 +560,11 @@ static inline void spimem_flash_ll_set_cs_setup(spi_mem_dev_t *dev, uint32_t cs_ dev->ctrl2.cs_setup_time = cs_setup_time - 1; } +static inline void spimem_flash_ll_set_extra_dummy(spi_mem_dev_t *dev, uint32_t extra_dummy) +{ + dev->timing_cali.extra_dummy_cyclelen = extra_dummy; +} + #ifdef __cplusplus } #endif diff --git a/components/hal/include/hal/spi_flash_hal.h b/components/hal/include/hal/spi_flash_hal.h index dbbb5ff53b..ae37016fa2 100644 --- a/components/hal/include/hal/spi_flash_hal.h +++ b/components/hal/include/hal/spi_flash_hal.h @@ -51,20 +51,41 @@ typedef struct { uint32_t flags; ///< Flags for configurations with one set of driver code. (e.g. QPI mode, auto-suspend mode, 64-bit address mode, etc.) #define SPI_FLASH_HOST_CONTEXT_FLAG_AUTO_SUSPEND BIT(0) ///< When the auto-suspend is setup in configuration. #define SPI_FLASH_HOST_CONTEXT_FLAG_AUTO_RESUME BIT(1) ///< Setup auto-resume feature. +#define SPI_FLASH_HOST_CONTEXT_FLAG_OCTAL_MODE BIT(2) ///< Flash works under octal spi mode. spi_flash_sus_cmd_conf sus_cfg; ///< To store suspend command/mask information. + uint32_t slicer_flags; /// Slicer flags for configuring how to slice data correctly while reading or writing. +#define SPI_FLASH_HOST_CONTEXT_SLICER_FLAG_DTR BIT(0) ///< Slice data according to DTR mode, the address and length must be even (A0=0). } spi_flash_hal_context_t; -_Static_assert(sizeof(spi_flash_hal_context_t) == 36, "size of spi_flash_hal_context_t incorrect. Please check data compatibility with the ROM"); +_Static_assert(sizeof(spi_flash_hal_context_t) == 40, "size of spi_flash_hal_context_t incorrect. Please check data compatibility with the ROM"); + +/// This struct provide MSPI Flash necessary timing related config, should be consistent with that in union in `spi_flash_hal_config_t`. +typedef struct { + uint32_t extra_dummy; + uint32_t cs_hold; + uint8_t cs_setup; + spi_flash_ll_clock_reg_t clock_config; +} spi_flash_hal_timing_config_t; /// Configuration structure for the SPI driver. typedef struct { - spi_host_device_t host_id; ///< SPI peripheral ID. - int cs_num; ///< Which cs pin is used, 0-(SOC_SPI_PERIPH_CS_NUM-1). + union { + struct { + uint32_t extra_dummy; ///< extra dummy for timing compensation. + uint32_t cs_hold; ///< CS hold time config used by the host + uint8_t cs_setup; ///< (cycles-1) of prepare phase by spi clock + spi_flash_ll_clock_reg_t clock_config; ///< (optional) Clock configuration for Octal flash. + }; + spi_flash_hal_timing_config_t timing_reg; ///< Reconfigure timing tuning regs. + }; bool iomux; ///< Whether the IOMUX is used, used for timing compensation. int input_delay_ns; ///< Input delay on the MISO pin after the launch clock, used for timing compensation. esp_flash_speed_t speed;///< SPI flash clock speed to work at. - uint32_t cs_hold; ///< CS hold time config used by the host - uint8_t cs_setup; ///< (cycles-1) of prepare phase by spi clock + spi_host_device_t host_id; ///< SPI peripheral ID. + int cs_num; ///< Which cs pin is used, 0-(SOC_SPI_PERIPH_CS_NUM-1). bool auto_sus_en; ///< Auto suspend feature enable bit 1: enable, 0: disable. + bool octal_mode_en; ///< Octal spi flash mode enable bit 1: enable, 0: disable. + bool using_timing_tuning; ///< System exist SPI0/1 timing tuning, using value from system directely if set to 1. + esp_flash_io_mode_t default_io_mode; ///< Default flash io mode. } spi_flash_hal_config_t; /** diff --git a/components/hal/include/hal/spi_flash_types.h b/components/hal/include/hal/spi_flash_types.h index 0cc605595f..c9d5035e60 100644 --- a/components/hal/include/hal/spi_flash_types.h +++ b/components/hal/include/hal/spi_flash_types.h @@ -37,6 +37,7 @@ typedef struct { #define SPI_FLASH_TRANS_FLAG_BYTE_SWAP BIT(2) ///< Used for DTR mode, to swap the bytes of a pair of rising/falling edge uint16_t command; ///< Command to send uint8_t dummy_bitlen; ///< Basic dummy bits to use + uint32_t io_mode; ///< Flash working mode when `SPI_FLASH_IGNORE_BASEIO` is specified. } spi_flash_trans_t; /** @@ -54,6 +55,7 @@ typedef enum { ESP_FLASH_26MHZ, ///< The flash runs under 26MHz ESP_FLASH_40MHZ, ///< The flash runs under 40MHz ESP_FLASH_80MHZ, ///< The flash runs under 80MHz + ESP_FLASH_120MHZ, ///< The flash runs under 120MHz, 120MHZ can only be used by main flash after timing tuning in system. Do not use this directely in any API. ESP_FLASH_SPEED_MAX, ///< The maximum frequency supported by the host is ``ESP_FLASH_SPEED_MAX-1``. } esp_flash_speed_t; @@ -71,7 +73,9 @@ typedef enum { SPI_FLASH_DIO, ///< Both address & data transferred using dual I/O SPI_FLASH_QOUT, ///< Data read using quad I/O SPI_FLASH_QIO, ///< Both address & data transferred using quad I/O - +#define SPI_FLASH_OPI_FLAG 16 ///< A flag for flash work in opi mode, the io mode below are opi, above are SPI/QSPI mode. DO NOT use this value in any API. + SPI_FLASH_OPI_STR = SPI_FLASH_OPI_FLAG,///< Only support on OPI flash, flash read and write under STR mode + SPI_FLASH_OPI_DTR,///< Only support on OPI flash, flash read and write under DTR mode SPI_FLASH_READ_MODE_MAX, ///< The fastest io mode supported by the host is ``ESP_FLASH_READ_MODE_MAX-1``. } esp_flash_io_mode_t; diff --git a/components/hal/spi_flash_hal.c b/components/hal/spi_flash_hal.c index d2dea8ebf8..d417eccf20 100644 --- a/components/hal/spi_flash_hal.c +++ b/components/hal/spi_flash_hal.c @@ -71,6 +71,26 @@ 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 +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); + int extra_dummy = 0; + if (main_flash) { + /** + * For Octal Flash, the dummy is `usr_dummy` + `extra_dummy`, they are in two different regs, we don't touch `extra_dummy` here, so set extra_dummy 0. + * Instead, for both Quad and Octal Flash, we use `usr_dummy` and set the whole dummy length (usr_dummy + extra_dummy) to this register. + */ + extra_dummy = cfg->extra_dummy; + } else { + // TODO: for other flash chips, dummy get logic implement here. Currently, still calculate extra dummy by itself. + abort(); + } + + return extra_dummy; +} +#endif //SOC_SPI_MEM_SUPPORT_TIME_TUNING + esp_err_t spi_flash_hal_init(spi_flash_hal_context_t *data_out, const spi_flash_hal_config_t *cfg) { if (!esp_ptr_internal(data_out) && cfg->host_id == SPI1_HOST) { @@ -87,16 +107,37 @@ esp_err_t spi_flash_hal_init(spi_flash_hal_context_t *data_out, const spi_flash_ .inst = data_out->inst, // Keeps the function pointer table .spi = spi_flash_ll_get_hw(cfg->host_id), .cs_num = cfg->cs_num, - .extra_dummy = get_dummy_n(!cfg->iomux, cfg->input_delay_ns, APB_CLK_FREQ/clock_cfg->div), - .clock_conf = clock_cfg->clock_reg_val, .cs_hold = cfg->cs_hold, .cs_setup = cfg->cs_setup, + .base_io_mode = cfg->default_io_mode, }; +#if SOC_SPI_MEM_SUPPORT_TIME_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 + { + data_out->extra_dummy = get_dummy_n(!cfg->iomux, cfg->input_delay_ns, APB_CLK_FREQ/clock_cfg->div); + data_out->clock_conf = clock_cfg->clock_reg_val; + } + + if (cfg->auto_sus_en) { data_out->flags |= SPI_FLASH_HOST_CONTEXT_FLAG_AUTO_SUSPEND; data_out->flags |= SPI_FLASH_HOST_CONTEXT_FLAG_AUTO_RESUME; } +#if SOC_SPI_MEM_SUPPORT_OPI_MODE + if (cfg->octal_mode_en) { + data_out->flags |= SPI_FLASH_HOST_CONTEXT_FLAG_OCTAL_MODE; + } + + if (cfg->default_io_mode == SPI_FLASH_OPI_DTR) { + data_out->slicer_flags |= SPI_FLASH_HOST_CONTEXT_SLICER_FLAG_DTR; + } +#endif + HAL_LOGD(TAG, "extra_dummy: %d", data_out->extra_dummy); return ESP_OK; } diff --git a/components/hal/spi_flash_hal_common.inc b/components/hal/spi_flash_hal_common.inc index c4a47ac094..60fed70431 100644 --- a/components/hal/spi_flash_hal_common.inc +++ b/components/hal/spi_flash_hal_common.inc @@ -65,6 +65,10 @@ 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 + // 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 #else gpspi_flash_ll_set_hold_pol(dev, 1); #endif //GPSPI_BUILD @@ -139,7 +143,7 @@ esp_err_t spi_flash_hal_common_command(spi_flash_host_inst_t *host, spi_flash_tr command = trans->command; dummy_bitlen = trans->dummy_bitlen; if ((trans->flags & SPI_FLASH_TRANS_FLAG_IGNORE_BASEIO) != 0) { - io_mode = ((spi_flash_hal_context_t*)host)->base_io_mode; + io_mode = trans->io_mode; } } diff --git a/components/soc/esp32s3/include/soc/soc_caps.h b/components/soc/esp32s3/include/soc/soc_caps.h index 2260b176c6..348db5375c 100644 --- a/components/soc/esp32s3/include/soc/soc_caps.h +++ b/components/soc/esp32s3/include/soc/soc_caps.h @@ -262,6 +262,9 @@ #define SOC_SPI_MEM_SUPPORT_AUTO_SUSPEND (1) #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) + /*-------------------------- COEXISTENCE HARDWARE PTI CAPS -------------------------------*/ #define SOC_COEX_HW_PTI (1) diff --git a/components/spi_flash/CMakeLists.txt b/components/spi_flash/CMakeLists.txt index 5648ed5980..9f0263f964 100644 --- a/components/spi_flash/CMakeLists.txt +++ b/components/spi_flash/CMakeLists.txt @@ -34,6 +34,7 @@ else() "spi_flash_chip_gd.c" "spi_flash_chip_winbond.c" "spi_flash_chip_boya.c" + "spi_flash_chip_mxic_opi.c" "memspi_host_driver.c") list(APPEND cache_srcs diff --git a/components/spi_flash/Kconfig b/components/spi_flash/Kconfig index ea1970a76d..32a2544f1a 100644 --- a/components/spi_flash/Kconfig +++ b/components/spi_flash/Kconfig @@ -248,6 +248,15 @@ menu "SPI Flash driver" given by ``chip_drv`` member of the chip struct. This adds support for variant chips, however will extend detecting time. + config SPI_FLASH_SUPPORT_MXIC_OPI_CHIP + bool "mxic (opi)" + depends on IDF_TARGET_ESP32S3 + default y + help + Enable this to support auto detection of Octal MXIC chips if chip vendor not directly + given by ``chip_drv`` member of the chip struct. This adds support for variant + chips, however will extend detecting time. + endmenu #auto detect flash chips config SPI_FLASH_ENABLE_ENCRYPTED_READ_WRITE diff --git a/components/spi_flash/esp32s3/spi_timing_config.c b/components/spi_flash/esp32s3/spi_timing_config.c index 5246d07272..75745cdfd7 100644 --- a/components/spi_flash/esp32s3/spi_timing_config.c +++ b/components/spi_flash/esp32s3/spi_timing_config.c @@ -358,3 +358,58 @@ void spi_timing_config_psram_tune_dummy(uint8_t extra_dummy) } #endif //#if SPI_TIMING_FLASH_NEEDS_TUNING || SPI_TIMING_PSRAM_NEEDS_TUNING + +static bool spi_timing_config_cs_setup_enable(void) +{ + return REG_GET_BIT(SPI_MEM_USER_REG(0), SPI_MEM_CS_SETUP); +} + +static bool spi_timing_config_cs_hold_enable(void) +{ + return REG_GET_BIT(SPI_MEM_USER_REG(0), SPI_MEM_CS_HOLD); +} + +bool spi_timine_config_flash_is_tuned(void) +{ +#if SPI_TIMING_FLASH_NEEDS_TUNING || SPI_TIMING_PSRAM_NEEDS_TUNING + return true; +#else + return false; +#endif +} + +/** + * Get the SPI1 Flash CS timing setting. The setup time and hold time are both realistic cycles. + * @note On ESP32-S3, SPI0/1 share the Flash CS timing registers. Therefore, we should not change these values. + * @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 spi_timing_config_get_cs_timing(uint8_t *setup_time, uint32_t *hold_time) +{ + *setup_time = REG_GET_FIELD(SPI_MEM_CTRL2_REG(0), SPI_MEM_CS_SETUP_TIME); + *hold_time = REG_GET_FIELD(SPI_MEM_CTRL2_REG(0), SPI_MEM_CS_HOLD_TIME); + /** + * The logic here is, if setup_en / hold_en is false, then we return the realistic cycle number, + * which is 0. If true, then the realistic cycle number is (reg_value + 1) + */ + if (spi_timing_config_cs_setup_enable()) { + *setup_time += 1; + } else { + *setup_time = 0; + } + if (spi_timing_config_cs_hold_enable()) { + *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 spi_timing_config_get_flash_clock_reg(void) +{ + return READ_PERI_REG(SPI_MEM_CLOCK_REG(1)); +} diff --git a/components/spi_flash/esp32s3/spi_timing_config.h b/components/spi_flash/esp32s3/spi_timing_config.h index 404b51f46c..cd7d06c683 100644 --- a/components/spi_flash/esp32s3/spi_timing_config.h +++ b/components/spi_flash/esp32s3/spi_timing_config.h @@ -242,6 +242,14 @@ void spi_timing_config_flash_tune_din_num_mode(uint8_t din_mode, uint8_t din_num void spi_timing_config_flash_tune_dummy(uint8_t extra_dummy); void spi_timing_config_psram_tune_din_num_mode(uint8_t din_mode, uint8_t din_num); void spi_timing_config_psram_tune_dummy(uint8_t extra_dummy); + +/** + * SPI1 register info get APIs. These APIs inform `spi_flash_timing_tuning.c` (driver layer) of the SPI1 flash settings. + * In this way, other components (e.g.: esp_flash driver) can get the info from it. + */ +void spi_timing_config_get_cs_timing(uint8_t *setup_time, uint32_t *hold_time); +uint32_t spi_timing_config_get_flash_clock_reg(void); + #ifdef __cplusplus } #endif diff --git a/components/spi_flash/esp_flash_api.c b/components/spi_flash/esp_flash_api.c index f077d7eb33..dacfda2580 100644 --- a/components/spi_flash/esp_flash_api.c +++ b/components/spi_flash/esp_flash_api.c @@ -27,6 +27,17 @@ #if CONFIG_IDF_TARGET_ESP32S2 #include "esp_crypto_lock.h" // for locking flash encryption peripheral #endif //CONFIG_IDF_TARGET_ESP32S2 +#if CONFIG_IDF_TARGET_ESP32 +#include "esp32/rom/spi_flash.h" +#elif CONFIG_IDF_TARGET_ESP32S2 +#include "esp32s2/rom/spi_flash.h" +#elif CONFIG_IDF_TARGET_ESP32S3 +#include "esp32s3/rom/spi_flash.h" +#elif CONFIG_IDF_TARGET_ESP32C3 +#include "esp32c3/rom/spi_flash.h" +#elif CONFIG_IDF_TARGET_ESP32H2 +#include "esp32h2/rom/spi_flash.h" +#endif static const char TAG[] = "spi_flash"; @@ -58,7 +69,7 @@ static const char TAG[] = "spi_flash"; } while(0) #endif // CONFIG_SPI_FLASH_DANGEROUS_WRITE_ALLOWED -#define IO_STR_LEN 7 +#define IO_STR_LEN 10 static const char io_mode_str[][IO_STR_LEN] = { "slowrd", @@ -67,9 +78,12 @@ static const char io_mode_str[][IO_STR_LEN] = { "dio", "qout", "qio", + [6 ... 15] = "not used", // reserved io mode for future, not used currently. + "opi_str", + "opi_dtr", }; -_Static_assert(sizeof(io_mode_str)/IO_STR_LEN == SPI_FLASH_READ_MODE_MAX, "the io_mode_str should be consistent with the esp_flash_io_mode_t defined in spi_flash_ll.h"); +_Static_assert(sizeof(io_mode_str)/IO_STR_LEN == SPI_FLASH_READ_MODE_MAX, "the io_mode_str should be consistent with the esp_flash_io_mode_t defined in spi_flash_types.h"); esp_err_t esp_flash_read_chip_id(esp_flash_t* chip, uint32_t* flash_id); @@ -247,6 +261,84 @@ esp_err_t IRAM_ATTR esp_flash_init(esp_flash_t *chip) return rom_spiflash_api_funcs->end(chip, err); } +// Note: This function is only used for internal. Only call this function to initialize the main flash. +// (flash chip on SPI1 CS0) +esp_err_t IRAM_ATTR esp_flash_init_main(esp_flash_t *chip) +{ + // Chip init flow + // 1. Read chip id + // 2. (optional) Detect chip vendor + // 3. Get basic parameters of the chip (size, dummy count, etc.) + // 4. Init chip into desired mode (without breaking the cache!) + esp_err_t err = ESP_OK; + bool octal_mode = (chip->read_mode >= SPI_FLASH_OPI_FLAG); + if (chip == NULL || chip->host == NULL || chip->host->driver == NULL || + ((memspi_host_inst_t*)chip->host)->spi == NULL) { + return ESP_ERR_INVALID_ARG; + } + + //read chip id + // This can indicate the MSPI support OPI, if the flash works on MSPI in OPI mode, we directly bypass read id. + uint32_t flash_id = 0; + if (octal_mode) { + // bypass the reading but get the flash_id from the ROM variable, to avoid resetting the chip to QSPI mode and read the ID again + flash_id = g_rom_flashchip.device_id; + } else { + int retries = 10; + do { + err = esp_flash_read_chip_id(chip, &flash_id); + } while (err == ESP_ERR_FLASH_NOT_INITIALISED && retries-- > 0); + } + + if (err != ESP_OK) { + return err; + } + chip->chip_id = flash_id; + + if (!esp_flash_chip_driver_initialized(chip)) { + // Detect chip_drv + err = detect_spi_flash_chip(chip); + if (err != ESP_OK) { + return err; + } + } + + // Detect flash size + uint32_t size; + err = esp_flash_get_size(chip, &size); + if (err != ESP_OK) { + ESP_LOGE(TAG, "failed to get chip size"); + return err; + } + + if (chip->chip_drv->get_chip_caps == NULL) { + // chip caps get failed, pass the flash capability check. + ESP_LOGW(TAG, "get_chip_caps function pointer hasn't been initialized"); + } else { + if (((chip->chip_drv->get_chip_caps(chip) & SPI_FLASH_CHIP_CAP_32MB_SUPPORT) == 0) && (size > (16 *1024 * 1024))) { + ESP_LOGW(TAG, "Detected flash size > 16 MB, but access beyond 16 MB is not supported for this flash model yet."); + size = (16 * 1024 * 1024); + } + } + + ESP_LOGI(TAG, "flash io: %s", io_mode_str[chip->read_mode]); + err = rom_spiflash_api_funcs->start(chip); + if (err != ESP_OK) { + return err; + } + + if (err == ESP_OK && !octal_mode) { + // Try to set the flash mode to whatever default mode was chosen + err = chip->chip_drv->set_io_mode(chip); + if (err == ESP_ERR_FLASH_NO_RESPONSE && !esp_flash_is_quad_mode(chip)) { + //some chips (e.g. Winbond) don't support to clear QE, treat as success + err = ESP_OK; + } + } + // Done: all fields on 'chip' are initialised + return rom_spiflash_api_funcs->end(chip, err); +} + static esp_err_t IRAM_ATTR read_id_core(esp_flash_t* chip, uint32_t* out_id, bool sanity_check) { bool installed = esp_flash_chip_driver_initialized(chip); diff --git a/components/spi_flash/esp_flash_spi_init.c b/components/spi_flash/esp_flash_spi_init.c index 68c26f8102..79605e56c8 100644 --- a/components/spi_flash/esp_flash_spi_init.c +++ b/components/spi_flash/esp_flash_spi_init.c @@ -27,6 +27,7 @@ #include "hal/gpio_hal.h" #include "esp_flash_internal.h" #include "esp_rom_gpio.h" +#include "spi_flash_private.h" #if CONFIG_IDF_TARGET_ESP32 #include "esp32/rom/spi_flash.h" #elif CONFIG_IDF_TARGET_ESP32S2 @@ -56,6 +57,8 @@ esp_flash_t *esp_flash_default_chip = NULL; #define DEFAULT_FLASH_SPEED ESP_FLASH_26MHZ #elif defined CONFIG_ESPTOOLPY_FLASHFREQ_20M #define DEFAULT_FLASH_SPEED ESP_FLASH_20MHZ +#elif defined CONFIG_ESPTOOLPY_FLASHFREQ_120M +#define DEFAULT_FLASH_SPEED ESP_FLASH_120MHZ #else #error Flash frequency not defined! Check the ``CONFIG_ESPTOOLPY_FLASHFREQ_*`` options. #endif @@ -68,10 +71,10 @@ esp_flash_t *esp_flash_default_chip = NULL; #define DEFAULT_FLASH_MODE SPI_FLASH_DIO #elif defined(CONFIG_ESPTOOLPY_FLASHMODE_DOUT) #define DEFAULT_FLASH_MODE SPI_FLASH_DOUT -#elif defined(CONFIG_ESPTOOLPY_FLASHMODE_FASTRD) -#define DEFAULT_FLASH_MODE SPI_FLASH_FASTRD -#elif defined(CONFIG_ESPTOOLPY_FLASHMODE_SLOWRD) -#define DEFAULT_FLASH_MODE SPI_FLASH_SLOWRD +#elif defined(CONFIG_ESPTOOLPY_FLASHMODE_OPI_STR) +#define DEFAULT_FLASH_MODE SPI_FLASH_OPI_STR +#elif defined(CONFIG_ESPTOOLPY_FLASHMODE_OPI_DTR) +#define DEFAULT_FLASH_MODE SPI_FLASH_OPI_DTR #else #define DEFAULT_FLASH_MODE SPI_FLASH_FASTRD #endif @@ -294,6 +297,19 @@ esp_err_t esp_flash_init_default_chip(void) cfg.iomux = esp_rom_efuse_get_flash_gpio_info() == 0 ? true : false; #endif + #if CONFIG_ESPTOOLPY_OCT_FLASH + cfg.octal_mode_en = 1; + cfg.default_io_mode = DEFAULT_FLASH_MODE; + #endif + + // For chips need time tuning, get value directely from system here. + #if SOC_SPI_MEM_SUPPORT_TIME_TUNING + if (spi_timine_config_flash_is_tuned()) { + cfg.using_timing_tuning = 1; + spi_timing_get_flash_timing_param(&cfg.timing_reg); + } + #endif // SOC_SPI_MEM_SUPPORT_TIME_TUNING + //the host is already initialized, only do init for the data and load it to the host esp_err_t err = memspi_host_init_pointers(&esp_flash_default_host, &cfg); if (err != ESP_OK) { @@ -302,7 +318,7 @@ esp_err_t esp_flash_init_default_chip(void) // ROM TODO: account for non-standard default pins in efuse // ROM TODO: to account for chips which are slow to power on, maybe keep probing in a loop here - err = esp_flash_init(&default_chip); + err = esp_flash_init_main(&default_chip); if (err != ESP_OK) { return err; } diff --git a/components/spi_flash/flash_ops.c b/components/spi_flash/flash_ops.c index 66dfb38673..116f07ec31 100644 --- a/components/spi_flash/flash_ops.c +++ b/components/spi_flash/flash_ops.c @@ -56,7 +56,6 @@ #include "cache_utils.h" #include "esp_flash.h" #include "esp_attr.h" -#include "spi_flash_private.h" #include "bootloader_flash.h" esp_rom_spiflash_result_t IRAM_ATTR spi_flash_write_encrypted_chip(size_t dest_addr, const void *src, size_t size); @@ -518,7 +517,7 @@ out: #endif // CONFIG_SPI_FLASH_USE_LEGACY_IMPL #if !CONFIG_SPI_FLASH_USE_LEGACY_IMPL -#if !CONFIG_ESPTOOLPY_OCT_FLASH +#if !CONFIG_ESPTOOLPY_OCT_FLASH // Test for encryption on opi flash, IDF-3852. extern void spi_common_set_dummy_output(esp_rom_spiflash_read_mode_t mode); extern void spi_dummy_len_fix(uint8_t spi, uint8_t freqdiv); void IRAM_ATTR flash_rom_init(void) @@ -558,10 +557,6 @@ void IRAM_ATTR flash_rom_init(void) read_mode = ESP_ROM_SPIFLASH_DIO_MODE; #elif CONFIG_ESPTOOLPY_FLASHMODE_DOUT read_mode = ESP_ROM_SPIFLASH_DOUT_MODE; -#elif CONFIG_ESPTOOLPY_FLASHMODE_FASTRD - read_mode = ESP_ROM_SPIFLASH_FASTRD_MODE; -#elif CONFIG_ESPTOOLPY_FLASHMODE_SLOWRD - read_mode = ESP_ROM_SPIFLASH_SLOWRD_MODE; #endif #endif //!CONFIG_IDF_TARGET_ESP32S2 && !CONFIG_IDF_TARGET_ESP32 diff --git a/components/spi_flash/include/spi_flash/spi_flash_defs.h b/components/spi_flash/include/spi_flash/spi_flash_defs.h index cf26fda148..1ff0bfdea5 100644 --- a/components/spi_flash/include/spi_flash/spi_flash_defs.h +++ b/components/spi_flash/include/spi_flash/spi_flash_defs.h @@ -68,3 +68,7 @@ #define SPI_FLASH_FASTRD_DUMMY_BITLEN 8 #define SPI_FLASH_SLOWRD_ADDR_BITLEN 24 #define SPI_FLASH_SLOWRD_DUMMY_BITLEN 0 +#define SPI_FLASH_OPISTR_ADDR_BITLEN 32 +#define SPI_FLASH_OPISTR_DUMMY_BITLEN 20 +#define SPI_FLASH_OPIDTR_ADDR_BITLEN 32 +#define SPI_FLASH_OPIDTR_DUMMY_BITLEN 40 diff --git a/components/spi_flash/include/spi_flash_chip_driver.h b/components/spi_flash/include/spi_flash_chip_driver.h index 9d879c9e3e..d2ef25bf6d 100644 --- a/components/spi_flash/include/spi_flash_chip_driver.h +++ b/components/spi_flash/include/spi_flash_chip_driver.h @@ -30,15 +30,6 @@ typedef struct { uint32_t page_program_timeout; ///< Timeout for page program operation } flash_chip_op_timeout_t; -typedef struct flash_chip_dummy { - uint8_t dio_dummy_bitlen; - uint8_t qio_dummy_bitlen; - uint8_t qout_dummy_bitlen; - uint8_t dout_dummy_bitlen; - uint8_t fastrd_dummy_bitlen; - uint8_t slowrd_dummy_bitlen; -} flash_chip_dummy_t; - typedef enum { SPI_FLASH_REG_STATUS = 1, } spi_flash_register_t; @@ -215,6 +206,11 @@ struct spi_flash_chip_t { * Get the capabilities of the flash chip. See SPI_FLASH_CHIP_CAP_* macros as reference. */ spi_flash_caps_t (*get_chip_caps)(esp_flash_t *chip); + + /** + * Configure the host registers to use the specified read mode set in the ``chip->read_mode``. + */ + esp_err_t (*config_host_io_mode)(esp_flash_t *chip, uint32_t flags); }; /* Pointer to an array of pointers to all known drivers for flash chips. This array is used diff --git a/components/spi_flash/include/spi_flash_chip_generic.h b/components/spi_flash/include/spi_flash_chip_generic.h index 9e99f73e44..000d2c943c 100644 --- a/components/spi_flash/include/spi_flash_chip_generic.h +++ b/components/spi_flash/include/spi_flash_chip_generic.h @@ -370,14 +370,15 @@ esp_err_t spi_flash_common_set_io_mode(esp_flash_t *chip, esp_flash_wrsr_func_t * transactions. Also prepare the command to be sent in read functions. * * @param chip Pointer to SPI flash chip to use. If NULL, esp_flash_default_chip is substituted. - * @param addr_32bit Whether 32 bit commands will be used (Currently only W25Q256 and GD25Q256 are supported) + * @param flags Special rules to configure io mode, (i.e. Whether 32 bit commands will be used (Currently only W25Q256 and GD25Q256 are supported)) * * @return * - ESP_OK if success * - ESP_ERR_FLASH_NOT_INITIALISED if chip not initialized properly * - or other error passed from the ``configure_host_mode`` function of host driver */ -esp_err_t spi_flash_chip_generic_config_host_io_mode(esp_flash_t *chip, bool addr_32bit); +esp_err_t spi_flash_chip_generic_config_host_io_mode(esp_flash_t *chip, uint32_t flags); +#define SPI_FLASH_CONFIG_IO_MODE_32B_ADDR BIT(0) /** * @brief Handle explicit yield requests @@ -396,5 +397,15 @@ esp_err_t spi_flash_chip_generic_yield(esp_flash_t* chip, uint32_t wip); */ esp_err_t spi_flash_chip_generic_suspend_cmd_conf(esp_flash_t *chip); +/** + * + * @brief Read the chip unique ID unsupported function. + * + * @param chip Pointer to SPI flash chip to use. + * @param flash_unique_id Pointer to store output unique id (Although this function is an unsupported function, but the parameter should be kept for the consistence of the function pointer). + * @return Always ESP_ERR_NOT_SUPPORTED. + */ +esp_err_t spi_flash_chip_generic_read_unique_id_none(esp_flash_t *chip, uint64_t* flash_unique_id); + /// Default timeout configuration used by most chips const flash_chip_op_timeout_t spi_flash_chip_generic_timeout; diff --git a/components/spi_flash/include/spi_flash_chip_mxic.h b/components/spi_flash/include/spi_flash_chip_mxic.h index f998c1564e..201ad496e2 100644 --- a/components/spi_flash/include/spi_flash_chip_mxic.h +++ b/components/spi_flash/include/spi_flash_chip_mxic.h @@ -25,3 +25,10 @@ * is not found. */ extern const spi_flash_chip_t esp_flash_chip_mxic; + +/** + * MXIC OPI flash chip_drv, uses all the above functions for its operations. In + * default autodetection, this is used as a catchall if a more specific chip_drv + * is not found. + */ +extern const spi_flash_chip_t esp_flash_chip_mxic_opi; diff --git a/components/spi_flash/include/spi_flash_private.h b/components/spi_flash/include/spi_flash_private.h index 1a8ad6f231..9d822fb947 100644 --- a/components/spi_flash/include/spi_flash_private.h +++ b/components/spi_flash/include/spi_flash_private.h @@ -26,23 +26,13 @@ #elif CONFIG_IDF_TARGET_ESP32S3 #include "esp32s3/rom/spi_flash.h" #endif +#include "esp_flash.h" +#include "hal/spi_flash_hal.h" #ifdef __cplusplus extern "C" { #endif -/** - * This struct provide MSPI Flash necessary timing related config - */ -typedef struct { - uint8_t flash_clk_div; /*!< clock divider of Flash module. */ - uint8_t flash_extra_dummy; /*!< timing required extra dummy length for Flash */ - bool flash_setup_en; /*!< SPI0/1 Flash setup enable or not */ - uint8_t flash_setup_time; /*!< SPI0/1 Flash setup time. This value should be set to register directly */ - bool flash_hold_en; /*!< SPI0/1 Flash hold enable or not */ - uint8_t flash_hold_time; /*!< SPI0/1 Flash hold time. This value should be set to register directly */ -} spi_timing_flash_config_t; - /** * @brief Register ROM functions and init flash device registers to make use of octal flash */ @@ -82,10 +72,21 @@ void esp_mspi_pin_init(void); void spi_flash_set_rom_required_regs(void); /** - * @brief Get MSPI Flash necessary timing related config - * @param config see `spi_timing_flash_config_t` + * @brief Initialize main flash + * @param chip Pointer to main SPI flash(SPI1 CS0) chip to use.. */ -void spi_timing_get_flash_regs(spi_timing_flash_config_t *config); +esp_err_t esp_flash_init_main(esp_flash_t *chip); + +/** + * @brief Should be only used by SPI1 Flash driver to know the necessary timing registers + * @param out_timing_config Pointer to timing_tuning parameters. + */ +void spi_timing_get_flash_timing_param(spi_flash_hal_timing_config_t *out_timing_config); + +/** + * @brief Judge if the flash in tuned + */ +bool spi_timine_config_flash_is_tuned(void); #ifdef __cplusplus } diff --git a/components/spi_flash/linker.lf b/components/spi_flash/linker.lf index ee60692de1..92beb351a8 100644 --- a/components/spi_flash/linker.lf +++ b/components/spi_flash/linker.lf @@ -18,3 +18,6 @@ entries: if IDF_TARGET_ESP32S3 = y && ESPTOOLPY_OCT_FLASH = y: spi_flash_oct_flash_init (noflash) + + if IDF_TARGET_ESP32S3 = y : + spi_flash_chip_mxic_opi (noflash) diff --git a/components/spi_flash/memspi_host_driver.c b/components/spi_flash/memspi_host_driver.c index 9194201073..b242c9512b 100644 --- a/components/spi_flash/memspi_host_driver.c +++ b/components/spi_flash/memspi_host_driver.c @@ -207,19 +207,47 @@ esp_err_t memspi_host_set_write_protect(spi_flash_host_inst_t *host, bool wp) // This is the simple case where the hardware has no other requirements than the size and page boundary int memspi_host_write_data_slicer(spi_flash_host_inst_t *host, uint32_t address, uint32_t len, uint32_t *align_address, uint32_t page_size) { + uint32_t slicer_flag = ((spi_flash_hal_context_t*)host)->slicer_flags; uint32_t align_addr = address; + + if (slicer_flag & SPI_FLASH_HOST_CONTEXT_SLICER_FLAG_DTR) { + if (((align_addr % 2) != 0) && ((len % 2) != 0)) { + align_addr -= 1; + len += 1; + } else if (((align_addr % 2) != 0) && ((len % 2) == 0)) { + align_addr -= 1; + len += 2; + } else if (((align_addr % 2) == 0) && ((len % 2) != 0)) { + len += 1; + } + } + uint32_t end_bound = (align_addr/page_size + 1) * page_size; // Shouldn't program cross the page, or longer than SPI_FLASH_HAL_MAX_WRITE_BYTES uint32_t max_len = MIN(end_bound - align_addr, SPI_FLASH_HAL_MAX_WRITE_BYTES); - *align_address = address; + *align_address = align_addr; return MIN(max_len, len); } int memspi_host_read_data_slicer(spi_flash_host_inst_t *host, uint32_t address, uint32_t len, uint32_t *align_address, uint32_t page_size) { // Shouldn't read longer than SPI_FLASH_HAL_MAX_READ_BYTES + uint32_t slicer_flag = ((spi_flash_hal_context_t*)host)->slicer_flags; + uint32_t align_addr = address; + + if (slicer_flag & SPI_FLASH_HOST_CONTEXT_SLICER_FLAG_DTR) { + if (((align_addr % 2) != 0) && ((len % 2) != 0)) { + align_addr -= 1; + len += 1; + } else if (((align_addr % 2) != 0) && ((len % 2) == 0)) { + align_addr -= 1; + len += 2; + } else if (((align_addr % 2) == 0) && ((len % 2) != 0)) { + len += 1; + } + } uint32_t max_len = SPI_FLASH_HAL_MAX_READ_BYTES; - *align_address = address; + *align_address = align_addr; return MIN(max_len, len); } diff --git a/components/spi_flash/spi_flash_chip_boya.c b/components/spi_flash/spi_flash_chip_boya.c index f87c337585..c2d3bd2ac5 100644 --- a/components/spi_flash/spi_flash_chip_boya.c +++ b/components/spi_flash/spi_flash_chip_boya.c @@ -83,4 +83,5 @@ const spi_flash_chip_t esp_flash_chip_boya = { .sus_setup = spi_flash_chip_generic_suspend_cmd_conf, .read_unique_id = spi_flash_chip_generic_read_unique_id, .get_chip_caps = spi_flash_chip_boya_get_caps, + .config_host_io_mode = spi_flash_chip_generic_config_host_io_mode, }; diff --git a/components/spi_flash/spi_flash_chip_drivers.c b/components/spi_flash/spi_flash_chip_drivers.c index 1c0940760a..d54963b84b 100644 --- a/components/spi_flash/spi_flash_chip_drivers.c +++ b/components/spi_flash/spi_flash_chip_drivers.c @@ -47,6 +47,9 @@ static const spi_flash_chip_t *default_registered_chips[] = { #endif #ifdef CONFIG_SPI_FLASH_SUPPORT_BOYA_CHIP &esp_flash_chip_boya, +#endif +#ifdef CONFIG_SPI_FLASH_SUPPORT_MXIC_OPI_CHIP + &esp_flash_chip_mxic_opi, #endif // Default chip drivers that will accept all chip ID. // FM, Winbond and XMC chips are supposed to be supported by this chip driver. diff --git a/components/spi_flash/spi_flash_chip_gd.c b/components/spi_flash/spi_flash_chip_gd.c index 509e008f55..b36c25e549 100644 --- a/components/spi_flash/spi_flash_chip_gd.c +++ b/components/spi_flash/spi_flash_chip_gd.c @@ -144,4 +144,5 @@ const spi_flash_chip_t esp_flash_chip_gd = { .sus_setup = spi_flash_chip_generic_suspend_cmd_conf, .read_unique_id = spi_flash_chip_generic_read_unique_id, .get_chip_caps = spi_flash_chip_gd_get_caps, + .config_host_io_mode = spi_flash_chip_generic_config_host_io_mode, }; diff --git a/components/spi_flash/spi_flash_chip_generic.c b/components/spi_flash/spi_flash_chip_generic.c index 13b42c3e63..c1173c9b83 100644 --- a/components/spi_flash/spi_flash_chip_generic.c +++ b/components/spi_flash/spi_flash_chip_generic.c @@ -21,6 +21,15 @@ #include "esp_log.h" #include "esp_attr.h" +typedef struct flash_chip_dummy { + uint8_t dio_dummy_bitlen; + uint8_t qio_dummy_bitlen; + uint8_t qout_dummy_bitlen; + uint8_t dout_dummy_bitlen; + uint8_t fastrd_dummy_bitlen; + uint8_t slowrd_dummy_bitlen; +} flash_chip_dummy_t; + // These parameters can be placed in the ROM. For now we use the code in IDF. DRAM_ATTR const static flash_chip_dummy_t default_flash_chip_dummy = { .dio_dummy_bitlen = SPI_FLASH_DIO_DUMMY_BITLEN, @@ -191,9 +200,10 @@ esp_err_t spi_flash_chip_generic_read(esp_flash_t *chip, void *buffer, uint32_t const uint32_t page_size = chip->chip_drv->page_size; uint32_t align_address; uint8_t temp_buffer[64]; //spiflash hal max length of read no longer than 64byte + uint32_t config_io_flags = 0; // Configure the host, and return - err = spi_flash_chip_generic_config_host_io_mode(chip, false); + err = chip->chip_drv->config_host_io_mode(chip, config_io_flags); if (err == ESP_ERR_NOT_SUPPORTED) { ESP_LOGE(TAG, "configure host io mode failed - unsupported"); @@ -439,13 +449,14 @@ esp_err_t spi_flash_chip_generic_wait_idle(esp_flash_t *chip, uint32_t timeout_u return (timeout_us > 0) ? ESP_OK : ESP_ERR_TIMEOUT; } -esp_err_t spi_flash_chip_generic_config_host_io_mode(esp_flash_t *chip, bool addr_32bit) +esp_err_t spi_flash_chip_generic_config_host_io_mode(esp_flash_t *chip, uint32_t flags) { uint32_t dummy_cyclelen_base; uint32_t addr_bitlen; uint32_t read_command; bool conf_required = false; esp_flash_io_mode_t read_mode = chip->read_mode; + bool addr_32bit = (flags & SPI_FLASH_CONFIG_IO_MODE_32B_ADDR); switch (read_mode & 0xFFFF) { case SPI_FLASH_QIO: @@ -544,6 +555,12 @@ esp_err_t spi_flash_chip_generic_read_unique_id(esp_flash_t *chip, uint64_t* fla return err; } +esp_err_t spi_flash_chip_generic_read_unique_id_none(esp_flash_t *chip, uint64_t* flash_unique_id) +{ + // For flash doesn't support read unique id. + return ESP_ERR_NOT_SUPPORTED; +} + spi_flash_caps_t spi_flash_chip_generic_get_caps(esp_flash_t *chip) { // For generic part flash capability, take the XMC chip as reference. @@ -600,6 +617,7 @@ const spi_flash_chip_t esp_flash_chip_generic = { .sus_setup = spi_flash_chip_generic_suspend_cmd_conf, .read_unique_id = spi_flash_chip_generic_read_unique_id, .get_chip_caps = spi_flash_chip_generic_get_caps, + .config_host_io_mode = spi_flash_chip_generic_config_host_io_mode, }; #ifndef CONFIG_SPI_FLASH_ROM_IMPL diff --git a/components/spi_flash/spi_flash_chip_issi.c b/components/spi_flash/spi_flash_chip_issi.c index e8ae39fcff..56f29743b6 100644 --- a/components/spi_flash/spi_flash_chip_issi.c +++ b/components/spi_flash/spi_flash_chip_issi.c @@ -107,4 +107,5 @@ const spi_flash_chip_t esp_flash_chip_issi = { .sus_setup = spi_flash_chip_generic_suspend_cmd_conf, .read_unique_id = spi_flash_chip_generic_read_unique_id, .get_chip_caps = spi_flash_chip_issi_get_caps, + .config_host_io_mode = spi_flash_chip_generic_config_host_io_mode, }; diff --git a/components/spi_flash/spi_flash_chip_mxic.c b/components/spi_flash/spi_flash_chip_mxic.c index c40e4f5b7b..fef8f99f19 100644 --- a/components/spi_flash/spi_flash_chip_mxic.c +++ b/components/spi_flash/spi_flash_chip_mxic.c @@ -16,13 +16,10 @@ #include "spi_flash_chip_generic.h" #include "spi_flash_defs.h" #include "esp_log.h" -#include "string.h" -#include // For MIN/MAX +#include "hal/spi_flash_hal.h" /* Driver for MXIC flash chip */ -extern flash_chip_dummy_t *rom_flash_chip_dummy; - esp_err_t spi_flash_chip_mxic_probe(esp_flash_t *chip, uint32_t flash_id) { /* Check manufacturer and product IDs match our desired masks */ @@ -30,6 +27,10 @@ esp_err_t spi_flash_chip_mxic_probe(esp_flash_t *chip, uint32_t flash_id) if (flash_id >> 16 != MFG_ID) { return ESP_ERR_NOT_FOUND; } + if (chip->read_mode >= SPI_FLASH_OPI_FLAG) { + // The code here serve for ordinary mxic chip. If opi mode has been selected, go `spi_flash_chip_mxic_opi.c` + return ESP_ERR_NOT_FOUND; + } return ESP_OK; } @@ -44,50 +45,6 @@ esp_err_t spi_flash_chip_issi_get_io_mode(esp_flash_t *chip, esp_flash_io_mode_t static const char chip_name[] = "mxic"; -esp_err_t spi_flash_chip_mxic_read_unique_id(esp_flash_t *chip, uint64_t* flash_unique_id) -{ - //MXIC not support read unique id. - ESP_LOGE(chip_name, "chip %s doesn't support reading unique id", chip->chip_drv->name); - return ESP_ERR_NOT_SUPPORTED; -} - -esp_err_t spi_flash_chip_mxic_read(esp_flash_t *chip, void *buffer, uint32_t address, uint32_t length) -{ - esp_err_t err = ESP_OK; - const uint32_t page_size = chip->chip_drv->page_size; - uint32_t align_address; - uint8_t temp_buffer[64]; //spiflash hal max length of read no longer than 64byte - - // Configure the host, and return - uint32_t addr_bitlen = SPI_FLASH_FASTRD_ADDR_BITLEN; - uint32_t dummy_cyclelen_base = rom_flash_chip_dummy->fastrd_dummy_bitlen;; - uint32_t read_command = CMD_FASTRD; - uint32_t read_mode = SPI_FLASH_FASTRD; - - err = chip->host->driver->configure_host_io_mode(chip->host, read_command, addr_bitlen, dummy_cyclelen_base, read_mode); - - if (err == ESP_ERR_NOT_SUPPORTED) { - ESP_LOGE(chip_name, "configure host io mode failed - unsupported"); - return err; - } - - while (err == ESP_OK && length > 0) { - memset(temp_buffer, 0xFF, sizeof(temp_buffer)); - uint32_t read_len = chip->host->driver->read_data_slicer(chip->host, address, length, &align_address, page_size); - uint32_t left_off = address - align_address; - uint32_t data_len = MIN(align_address + read_len, address + length) - address; - err = chip->host->driver->read(chip->host, temp_buffer, align_address, read_len); - - memcpy(buffer, temp_buffer + left_off, data_len); - - address += data_len; - buffer = (void *)((intptr_t)buffer + data_len); - length = length - data_len; - } - - return err; -} - spi_flash_caps_t spi_flash_chip_mxic_get_caps(esp_flash_t *chip) { spi_flash_caps_t caps_flags = 0; @@ -132,6 +89,7 @@ const spi_flash_chip_t esp_flash_chip_mxic = { .read_reg = spi_flash_chip_mxic_read_reg, .yield = spi_flash_chip_generic_yield, .sus_setup = spi_flash_chip_generic_suspend_cmd_conf, - .read_unique_id = spi_flash_chip_mxic_read_unique_id, + .read_unique_id = spi_flash_chip_generic_read_unique_id_none, .get_chip_caps = spi_flash_chip_mxic_get_caps, + .config_host_io_mode = spi_flash_chip_generic_config_host_io_mode, }; diff --git a/components/spi_flash/spi_flash_chip_mxic_opi.c b/components/spi_flash/spi_flash_chip_mxic_opi.c new file mode 100644 index 0000000000..0ddd233aa1 --- /dev/null +++ b/components/spi_flash/spi_flash_chip_mxic_opi.c @@ -0,0 +1,420 @@ +// Copyright 2015-2020 Espressif Systems (Shanghai) PTE LTD +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include +#include "spi_flash_chip_generic.h" +#include "spi_flash_defs.h" +#include "esp_log.h" +#include "string.h" +#include // For MIN/MAX +#include "hal/spi_flash_hal.h" + +#define CMD_OPI_FLASH_MXIC(cmd) ((((~(cmd) & 0xff) << 8)) | ((cmd) & 0xff)) +#define CMD_OPI_FLASH_MXIC_CHIP_ERASE 0x9F60 +#define CMD_OPI_FLASH_MXIC_READ_STR 0x13EC +#define CMD_OPI_FLASH_MXIC_READ_DTR 0x11EE +#define CMD_OPI_FLASH_MXIC_RDCR2 0x8E71 +#define CMD_OPI_FLASH_MXIC_WRCR2 0x8D72 + +/* Driver for MXIC OPI flash chip */ +static const char chip_name[] = "mxic (opi)"; + +esp_err_t spi_flash_chip_mxic_opi_probe(esp_flash_t *chip, uint32_t flash_id) +{ + /* Check manufacturer and product IDs match our desired masks */ + const uint8_t MFG_ID = 0xC2; + if (flash_id >> 16 != MFG_ID) { + return ESP_ERR_NOT_FOUND; + } + + if (chip->read_mode < SPI_FLASH_OPI_FLAG) { + // The code here serve for opi flash under opi mode only, for ordinary mxic chip, go `spi_flash_chip_mxic.c` + return ESP_ERR_NOT_FOUND; + } + + return ESP_OK; +} + +spi_flash_caps_t spi_flash_chip_mxic_opi_get_caps(esp_flash_t *chip) +{ + spi_flash_caps_t caps_flags = 0; + caps_flags |= SPI_FLASH_CHIP_CAP_32MB_SUPPORT; + // flash-suspend is not supported yet. // IDF-3852 + // reading unique id is not supported. + return caps_flags; +} + +esp_err_t spi_flash_chip_mxic_opi_set_write_protect(esp_flash_t *chip, bool write_protect) +{ + esp_err_t err = ESP_OK; + + err = chip->chip_drv->wait_idle(chip, chip->chip_drv->timeout->idle_timeout); + spi_flash_trans_t t = {}; + if (err == ESP_OK || err == ESP_ERR_NOT_SUPPORTED) { + if(write_protect) { + t.command = CMD_OPI_FLASH_MXIC(CMD_WRDI); + } else { + t.command = CMD_OPI_FLASH_MXIC(CMD_WREN); + } + err = chip->host->driver->common_command(chip->host, &t); + } + + bool wp_read; + err = chip->chip_drv->get_chip_write_protect(chip, &wp_read); + if (err == ESP_OK && wp_read != write_protect) { + err = ESP_ERR_NOT_FOUND; + } + return err; +} + +static void spi_flash_chip_mxic_opi_get_data_length_zoom(esp_flash_io_mode_t io_mode, uint32_t *length_zoom) +{ + /* Under STR mode, one byte occupies one single clock. While under DTR mode, one byte occupies half clock. + For exmaple, if an operation needs 3 clock dummy, host send 3 dummy bytes under STR mode, while 6 dummy bytes under DTR mode. + Therefore, we need to adjust data zoom to fit the clock here. */ + assert((io_mode == SPI_FLASH_OPI_STR) || (io_mode == SPI_FLASH_OPI_DTR)); + *length_zoom = (io_mode == SPI_FLASH_OPI_STR) ? 1 : 2; +} + +esp_err_t spi_flash_chip_mxic_opi_read_id(esp_flash_t *chip, uint32_t* out_chip_id) +{ + uint64_t id_buf = 0; + uint32_t length_zoom; + spi_flash_chip_mxic_opi_get_data_length_zoom(chip->read_mode, &length_zoom); + spi_flash_trans_t t = { + .command = CMD_OPI_FLASH_MXIC(CMD_RDID), + .miso_len = 3 * length_zoom, + .dummy_bitlen = 4 * length_zoom, + .address_bitlen = 32, + .miso_data = ((uint8_t*) &id_buf), + }; + + chip->host->driver->common_command(chip->host, &t); + + if(chip->read_mode == SPI_FLASH_OPI_DTR) { + // Adjust the id_buf in DTR mode, because in DTR mode, the data back in STR rule. + // So it looks like [MD, MD, MT, MT, MID, MID], adjust it to [MD, MT, MID] here. + ESP_EARLY_LOGV(chip_name, "raw_chip_id: %llx\n", id_buf); + id_buf = (id_buf & 0xff) | ((id_buf & 0xff0000) >> 8) | ((id_buf & 0xff00000000) >> 16); + } else { + ESP_EARLY_LOGV(chip_name, "raw_chip_id: %X\n", id_buf); + } + + uint32_t raw_flash_id = __builtin_bswap32(id_buf); + if (raw_flash_id == 0xFFFFFF || raw_flash_id == 0) { + ESP_EARLY_LOGE(chip_name, "no response\n"); + return ESP_ERR_FLASH_NO_RESPONSE; + } + + *out_chip_id = (raw_flash_id >> 8); + ESP_EARLY_LOGV(chip_name, "chip_id: %X\n", *out_chip_id); + return ESP_OK; +} + +esp_err_t spi_flash_chip_mxic_opi_read_reg(esp_flash_t *chip, spi_flash_register_t reg_id, uint32_t* out_reg) +{ + uint32_t stat_buf = 0; + uint32_t length_zoom; + spi_flash_chip_mxic_opi_get_data_length_zoom(chip->read_mode, &length_zoom); + spi_flash_trans_t t = { + .command = CMD_OPI_FLASH_MXIC(CMD_RDSR), + .miso_data = ((uint8_t*) &stat_buf), + .miso_len = 1 * length_zoom, + .address_bitlen = 32, + .dummy_bitlen = 4 * length_zoom, + }; + esp_err_t err = chip->host->driver->common_command(chip->host, &t); + if (err != ESP_OK) { + return err; + } + + // For DTR mode, RDSR result like [SR1, SR1], just keeping one SR1. + *out_reg = (stat_buf & 0xff); + return ESP_OK; +} + +esp_err_t spi_flash_chip_mxic_opi_get_write_protect(esp_flash_t *chip, bool *out_write_protected) +{ + esp_err_t err = ESP_OK; + uint32_t status; + assert(out_write_protected!=NULL); + err = chip->chip_drv->read_reg(chip, SPI_FLASH_REG_STATUS, &status); + if (err != ESP_OK) { + return err; + } + + *out_write_protected = ((status & SR_WREN) == 0); + return err; +} + +esp_err_t spi_flash_chip_mxic_opi_erase_chip(esp_flash_t *chip) +{ + esp_err_t err; + + err = chip->chip_drv->set_chip_write_protect(chip, false); + if (err == ESP_OK) { + err = chip->chip_drv->wait_idle(chip, chip->chip_drv->timeout->idle_timeout); + } + + if (err == ESP_OK || err == ESP_ERR_NOT_SUPPORTED) { + // Do erase chip here. + spi_flash_trans_t t = { + .command = CMD_OPI_FLASH_MXIC_CHIP_ERASE, + }; + err = chip->host->driver->common_command(chip->host, &t); + chip->busy = 1; +#ifdef CONFIG_SPI_FLASH_CHECK_ERASE_TIMEOUT_DISABLED + err = chip->chip_drv->wait_idle(chip, ESP_FLASH_CHIP_GENERIC_NO_TIMEOUT); +#else + err = chip->chip_drv->wait_idle(chip, chip->chip_drv->timeout->chip_erase_timeout); +#endif + } + // Ensure WEL is 0, even if the erase failed. + if (err == ESP_ERR_NOT_SUPPORTED) { + chip->chip_drv->set_chip_write_protect(chip, true); + } + + return err; + +} + +esp_err_t spi_flash_chip_mxic_opi_erase_sector(esp_flash_t *chip, uint32_t start_address) +{ + esp_err_t err = chip->chip_drv->set_chip_write_protect(chip, false); + if (err == ESP_OK) { + err = chip->chip_drv->wait_idle(chip, chip->chip_drv->timeout->idle_timeout); + } + //The chip didn't accept the previous write command. Ignore this in preparationstage. + if (err == ESP_OK || err == ESP_ERR_NOT_SUPPORTED) { + spi_flash_trans_t t = { + .command = CMD_OPI_FLASH_MXIC(CMD_SECTOR_ERASE_4B), + .address_bitlen = 32, + .address = start_address, + }; + err = chip->host->driver->common_command(chip->host, &t); + chip->busy = 1; +#ifdef CONFIG_SPI_FLASH_CHECK_ERASE_TIMEOUT_DISABLED + err = chip->chip_drv->wait_idle(chip, ESP_FLASH_CHIP_GENERIC_NO_TIMEOUT); +#else + err = chip->chip_drv->wait_idle(chip, chip->chip_drv->timeout->sector_erase_timeout); +#endif + } + // Ensure WEL is 0, even if the erase failed. + if (err == ESP_ERR_NOT_SUPPORTED) { + err = chip->chip_drv->set_chip_write_protect(chip, true); + } + + return err; +} + +esp_err_t spi_flash_chip_mxic_opi_erase_block(esp_flash_t *chip, uint32_t start_address) +{ + esp_err_t err = chip->chip_drv->set_chip_write_protect(chip, false); + if (err == ESP_OK) { + err = chip->chip_drv->wait_idle(chip, chip->chip_drv->timeout->idle_timeout); + } + //The chip didn't accept the previous write command. Ignore this in preparationstage. + if (err == ESP_OK || err == ESP_ERR_NOT_SUPPORTED) { + spi_flash_trans_t t = { + .command = CMD_OPI_FLASH_MXIC(CMD_LARGE_BLOCK_ERASE_4B), + .address_bitlen = 32, + .address = start_address, + }; + err = chip->host->driver->common_command(chip->host, &t); + chip->busy = 1; +#ifdef CONFIG_SPI_FLASH_CHECK_ERASE_TIMEOUT_DISABLED + err = chip->chip_drv->wait_idle(chip, ESP_FLASH_CHIP_GENERIC_NO_TIMEOUT); +#else + err = chip->chip_drv->wait_idle(chip, chip->chip_drv->timeout->block_erase_timeout); +#endif + } + // Ensure WEL is 0, even if the erase failed. + if (err == ESP_ERR_NOT_SUPPORTED) { + err = chip->chip_drv->set_chip_write_protect(chip, true); + } + + return err; +} + +esp_err_t spi_flash_chip_mxic_opi_page_program(esp_flash_t *chip, const void *buffer, uint32_t address, uint32_t length) +{ + esp_err_t err; + + err = chip->chip_drv->wait_idle(chip, chip->chip_drv->timeout->idle_timeout); + //The chip didn't accept the previous write command. Ignore this in preparationstage. + if (err == ESP_OK || err == ESP_ERR_NOT_SUPPORTED) { + // Perform the actual Page Program command + spi_flash_trans_t t = { + .command = CMD_OPI_FLASH_MXIC(CMD_PROGRAM_PAGE_4B), + .address_bitlen = 32, + .address = address, + .mosi_len = length, + .mosi_data = buffer, + }; + chip->host->driver->common_command(chip->host, &t); + chip->busy = 1; + + err = chip->chip_drv->wait_idle(chip, chip->chip_drv->timeout->page_program_timeout); + } + // Ensure WEL is 0, even if the page program failed. + if (err == ESP_ERR_NOT_SUPPORTED) { + err = chip->chip_drv->set_chip_write_protect(chip, true); + } + return err; +} + +esp_err_t spi_flash_chip_mxic_opi_write(esp_flash_t *chip, const void *buffer, uint32_t address, uint32_t length) +{ + esp_err_t err = ESP_OK; + const uint32_t page_size = chip->chip_drv->page_size; + uint32_t align_address; + uint8_t temp_buffer[64]; //spiflash hal max length of write no longer than 64byte + + while (err == ESP_OK && length > 0) { + memset(temp_buffer, 0xFF, sizeof(temp_buffer)); + uint32_t page_len = chip->host->driver->write_data_slicer(chip->host, address, length, &align_address, page_size); + uint32_t left_off = address - align_address; + uint32_t write_len = MIN(align_address + page_len, address + length) - address; + memcpy(temp_buffer + left_off, buffer, write_len); + + err = chip->chip_drv->set_chip_write_protect(chip, false); + if (err == ESP_OK && length > 0) { + err = chip->chip_drv->program_page(chip, temp_buffer, align_address, page_len); + + address += write_len; + buffer = (void *)((intptr_t)buffer + write_len); + length -= write_len; + } + } + // The caller is responsible to do host->driver->flush_cache, because this function may be + // called in small pieces. Frequency call of flush cache will do harm to the performance. + return err; +} + +esp_err_t spi_flash_chip_mxic_opi_get_io_mode(esp_flash_t *chip, esp_flash_io_mode_t* out_io_mode) +{ + uint32_t stat_buf = 0; + uint32_t length_zoom; + spi_flash_chip_mxic_opi_get_data_length_zoom(chip->read_mode, &length_zoom); + + spi_flash_trans_t t = { + .command = CMD_OPI_FLASH_MXIC_RDCR2, + .dummy_bitlen = 4 * length_zoom, + .miso_data = ((uint8_t*) &stat_buf), + .miso_len = 1 * length_zoom, + .address_bitlen = 32, + }; + esp_err_t err = chip->host->driver->common_command(chip->host, &t); + if (err != ESP_OK) { + return err; + } + + // For DTR mode, RDSR result like [CR1, CR1], just keeping one CR1. + switch (stat_buf & 0xff) + { + case 0x1: + *out_io_mode = SPI_FLASH_OPI_STR; + break; + case 0x2: + *out_io_mode = SPI_FLASH_OPI_DTR; + break; + default: + // wrong mode. + *out_io_mode = 0; + break; + } + if (*out_io_mode != chip->read_mode) { + // Current chip mode is not the mode we configured. + *out_io_mode = 0; + } + + return ESP_OK; +} + +esp_err_t spi_flash_chip_xmic_opi_set_io_mode(esp_flash_t *chip) +{ + // TODO: configure opi flash chip set io mode, only useful for external flash currently. + // For main flash, we already set io mode when chip starts up. But for external flash, + // We need to set mode when flash initialized, so keeping this for future usage. + return ESP_OK; +} + +// This function should only be called after opi mode initialization. So, only configure for OPI-STR/OPI-DTR mode +// not support other mode in this file, return `ESP_ERR_FLASH_NOT_INITIALISED` directely. +esp_err_t spi_flash_chip_xmic_opi_config_host_io_mode(esp_flash_t *chip, uint32_t flags) +{ + uint32_t dummy_cyclelen_base; + uint32_t addr_bitlen; + uint32_t read_command; + esp_flash_io_mode_t read_mode = chip->read_mode; + + switch (read_mode & 0xFFFF) { + case SPI_FLASH_OPI_STR: + addr_bitlen = SPI_FLASH_OPISTR_ADDR_BITLEN; + dummy_cyclelen_base = SPI_FLASH_OPISTR_DUMMY_BITLEN; + read_command = CMD_OPI_FLASH_MXIC_READ_STR; + break; + case SPI_FLASH_OPI_DTR: + addr_bitlen = SPI_FLASH_OPIDTR_ADDR_BITLEN; + dummy_cyclelen_base = SPI_FLASH_OPIDTR_DUMMY_BITLEN; + read_command = CMD_OPI_FLASH_MXIC_READ_DTR; + break; + default: + return ESP_ERR_FLASH_NOT_INITIALISED; + } + + return chip->host->driver->configure_host_io_mode(chip->host, read_command, addr_bitlen, dummy_cyclelen_base, read_mode); +} + +// Most of mxic opi implementations are totally different from that is generic. +// Replace them to opi implementation. +const spi_flash_chip_t esp_flash_chip_mxic_opi = { + .name = chip_name, + .timeout = &spi_flash_chip_generic_timeout, + .probe = spi_flash_chip_mxic_opi_probe, + .reset = spi_flash_chip_generic_reset, + .detect_size = spi_flash_chip_generic_detect_size, + .erase_chip = spi_flash_chip_mxic_opi_erase_chip, + .erase_sector = spi_flash_chip_mxic_opi_erase_sector, + .erase_block = spi_flash_chip_mxic_opi_erase_block, + .sector_size = 4 * 1024, + .block_erase_size = 64 * 1024, + + .get_chip_write_protect = spi_flash_chip_mxic_opi_get_write_protect, + .set_chip_write_protect = spi_flash_chip_mxic_opi_set_write_protect, + + .num_protectable_regions = 0, + .protectable_regions = NULL, + .get_protected_regions = NULL, + .set_protected_regions = NULL, + + .read = spi_flash_chip_generic_read, + .write = spi_flash_chip_mxic_opi_write, + .program_page = spi_flash_chip_mxic_opi_page_program, + .page_size = 256, + .write_encrypted = spi_flash_chip_generic_write_encrypted, + + .wait_idle = spi_flash_chip_generic_wait_idle, + + .set_io_mode = spi_flash_chip_xmic_opi_set_io_mode, + .get_io_mode = spi_flash_chip_mxic_opi_get_io_mode, + + .read_id = spi_flash_chip_mxic_opi_read_id, + .read_reg = spi_flash_chip_mxic_opi_read_reg, + .yield = spi_flash_chip_generic_yield, + .sus_setup = spi_flash_chip_generic_suspend_cmd_conf, + .read_unique_id = spi_flash_chip_generic_read_unique_id_none, + .get_chip_caps = spi_flash_chip_mxic_opi_get_caps, + .config_host_io_mode = spi_flash_chip_xmic_opi_config_host_io_mode, +}; diff --git a/components/spi_flash/spi_flash_chip_winbond.c b/components/spi_flash/spi_flash_chip_winbond.c index 98e94ae01a..6bec557716 100644 --- a/components/spi_flash/spi_flash_chip_winbond.c +++ b/components/spi_flash/spi_flash_chip_winbond.c @@ -48,9 +48,13 @@ esp_err_t spi_flash_chip_winbond_read(esp_flash_t *chip, void *buffer, uint32_t const uint32_t page_size = chip->chip_drv->page_size; uint32_t align_address; uint8_t temp_buffer[64]; //spiflash hal max length of read no longer than 64byte + uint32_t config_io_flags = 0; // Configure the host, and return - err = spi_flash_chip_generic_config_host_io_mode(chip, REGION_32BIT(address, length)); + if (REGION_32BIT(address, length)) { + config_io_flags |= SPI_FLASH_CONFIG_IO_MODE_32B_ADDR; + } + err = chip->chip_drv->config_host_io_mode(chip, config_io_flags); if (err == ESP_ERR_NOT_SUPPORTED) { ESP_LOGE(TAG, "configure host io mode failed - unsupported"); @@ -192,6 +196,7 @@ const spi_flash_chip_t esp_flash_chip_winbond = { .sus_setup = spi_flash_chip_generic_suspend_cmd_conf, .read_unique_id = spi_flash_chip_generic_read_unique_id, .get_chip_caps = spi_flash_chip_winbond_get_caps, + .config_host_io_mode = spi_flash_chip_generic_config_host_io_mode, }; diff --git a/components/spi_flash/spi_flash_timing_tuning.c b/components/spi_flash/spi_flash_timing_tuning.c index 0c46788e0a..5fd5fa5c54 100644 --- a/components/spi_flash/spi_flash_timing_tuning.c +++ b/components/spi_flash/spi_flash_timing_tuning.c @@ -14,6 +14,7 @@ #include "soc/spi_mem_reg.h" #include "soc/io_mux_reg.h" #include "spi_flash_private.h" +#include "soc/soc.h" #if CONFIG_IDF_TARGET_ESP32S3 #include "esp32s3/spi_timing_config.h" #endif @@ -462,17 +463,24 @@ void spi_timing_enter_mspi_high_speed_mode(bool control_spi1) /** * Should be only used by SPI1 Flash driver to know the necessary timing registers */ -void spi_timing_get_flash_regs(spi_timing_flash_config_t *config) -{ - config->flash_clk_div = get_flash_clock_divider(); #if SPI_TIMING_FLASH_NEEDS_TUNING || SPI_TIMING_PSRAM_NEEDS_TUNING - config->flash_extra_dummy = s_flash_best_timing_tuning_config.extra_dummy_len; -#else - config->flash_extra_dummy = 0; -#endif - config->flash_setup_en = REG_GET_BIT(SPI_MEM_USER_REG(0), SPI_MEM_CS_SETUP); - config->flash_setup_time = REG_GET_FIELD(SPI_MEM_CTRL2_REG(0), SPI_MEM_CS_SETUP_TIME); +void spi_timing_get_flash_timing_param(spi_flash_hal_timing_config_t *out_timing_config) +{ + // Get clock configuration directly from system. + out_timing_config->clock_config.spimem.val = spi_timing_config_get_flash_clock_reg(); - config->flash_hold_en = REG_GET_BIT(SPI_MEM_USER_REG(0), SPI_MEM_CS_HOLD); - config->flash_hold_time = REG_GET_FIELD(SPI_MEM_CTRL2_REG(0), SPI_MEM_CS_HOLD_TIME); + // 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; + + // Get CS setup/hold value here. + spi_timing_config_get_cs_timing(&out_timing_config->cs_setup, &out_timing_config->cs_hold); } +#else +void spi_timing_get_flash_timing_param(spi_flash_hal_timing_config_t *out_timing_config) +{ + // This function shouldn't be called if timing tuning is not used. + abort(); +} +#endif // SPI_TIMING_FLASH_NEEDS_TUNING || SPI_TIMING_PSRAM_NEEDS_TUNING diff --git a/components/spi_flash/test/test_esp_flash.c b/components/spi_flash/test/test_esp_flash.c index 1c40917cad..27d7c228e6 100644 --- a/components/spi_flash/test/test_esp_flash.c +++ b/components/spi_flash/test/test_esp_flash.c @@ -727,6 +727,7 @@ static void write_large_buffer(const esp_partition_t *part, const uint8_t *sourc static void read_and_check(const esp_partition_t *part, const uint8_t *source, size_t length); // Internal functions for testing, from esp_flash_api.c +#if !CONFIG_ESPTOOLPY_OCT_FLASH esp_err_t esp_flash_set_io_mode(esp_flash_t* chip, bool qe); esp_err_t esp_flash_get_io_mode(esp_flash_t* chip, bool* qe); esp_err_t esp_flash_read_chip_id(esp_flash_t* chip, uint32_t* flash_id); @@ -801,16 +802,17 @@ IRAM_ATTR NOINLINE_ATTR static void test_toggle_qe(const esp_partition_t* part) // `spi_flash_common_set_io_mode` and then run this test. FLASH_TEST_CASE_IGNORE("Test esp_flash_write can toggle QE bit", test_toggle_qe); FLASH_TEST_CASE_3_IGNORE("Test esp_flash_write can toggle QE bit", test_toggle_qe); +#endif //CONFIG_ESPTOOLPY_OCT_FLASH void test_permutations_part(const flashtest_config_t* config, esp_partition_t* part, void* source_buf, size_t length) { if (config->host_id != -1) { esp_flash_speed_t speed = ESP_FLASH_SPEED_MIN; - while (speed != ESP_FLASH_SPEED_MAX) { + while (speed != ESP_FLASH_120MHZ) { //test io_mode in the inner loop to test QE set/clear function, since //the io mode will switch frequently. esp_flash_io_mode_t io_mode = SPI_FLASH_READ_MODE_MIN; - while (io_mode != SPI_FLASH_READ_MODE_MAX) { + while (io_mode != SPI_FLASH_QIO + 1) { if (io_mode > SPI_FLASH_FASTRD && !SOC_SPI_PERIPH_SUPPORT_MULTILINE_MODE(config->host_id)) { io_mode++; diff --git a/components/spi_flash/test/test_read_write.c b/components/spi_flash/test/test_read_write.c index 3ca559f083..9510fb7e8d 100644 --- a/components/spi_flash/test/test_read_write.c +++ b/components/spi_flash/test/test_read_write.c @@ -28,20 +28,21 @@ #include "esp_heap_caps.h" #if CONFIG_IDF_TARGET_ESP32 -#include +#include "esp32/rom/spi_flash.h" #elif CONFIG_IDF_TARGET_ESP32S2 -#include +#include "esp32s2/rom/spi_flash.h" #elif CONFIG_IDF_TARGET_ESP32S3 -#include +#include "esp32s3/rom/spi_flash.h" #elif CONFIG_IDF_TARGET_ESP32C3 -#include +#include "esp32c3/rom/spi_flash.h" +#elif CONFIG_IDF_TARGET_ESP32H2 +#include "esp32h2/rom/spi_flash.h" #endif #define MIN_BLOCK_SIZE 12 /* Base offset in flash for tests. */ static size_t start; -extern spiflash_legacy_data_t *rom_spiflash_legacy_data; static void setup_tests(void) { @@ -151,22 +152,24 @@ TEST_CASE("Test spi_flash_read", "[spi_flash][esp_flash]") #endif } -#if !CONFIG_ESPTOOLPY_OCT_FLASH extern void spi_common_set_dummy_output(esp_rom_spiflash_read_mode_t mode); extern void spi_dummy_len_fix(uint8_t spi, uint8_t freqdiv); static void IRAM_ATTR fix_rom_func(void) { uint32_t freqdiv = 0; - uint32_t vendor_id = (rom_spiflash_legacy_data->chip.device_id >> 16); -#if CONFIG_ESPTOOLPY_FLASHFREQ_80M +#if CONFIG_ESPTOOLPY_FLASHFREQ_80M && !CONFIG_ESPTOOLPY_OCT_FLASH freqdiv = 1; +#elif CONFIG_ESPTOOLPY_FLASHFREQ_80M && CONFIG_ESPTOOLPY_OCT_FLASH + freqdiv = 2; #elif CONFIG_ESPTOOLPY_FLASHFREQ_40M freqdiv = 2; #elif CONFIG_ESPTOOLPY_FLASHFREQ_26M freqdiv = 3; #elif CONFIG_ESPTOOLPY_FLASHFREQ_20M freqdiv = 4; +#elif CONFIG_ESPTOOLPY_FLASHFREQ_120M + freqdiv = 2; #endif #if CONFIG_IDF_TARGET_ESP32 @@ -194,24 +197,20 @@ static void IRAM_ATTR fix_rom_func(void) read_mode = ESP_ROM_SPIFLASH_DIO_MODE; #elif CONFIG_ESPTOOLPY_FLASHMODE_DOUT read_mode = ESP_ROM_SPIFLASH_DOUT_MODE; -#elif CONFIG_ESPTOOLPY_FLASHMODE_FASTRD - read_mode = ESP_ROM_SPIFLASH_FASTRD_MODE; -#elif CONFIG_ESPTOOLPY_FLASHMODE_SLOWRD - read_mode = ESP_ROM_SPIFLASH_SLOWRD_MODE; +#elif CONFIG_ESPTOOLPY_FLASHMODE_OPI_STR + read_mode = ESP_ROM_SPIFLASH_OPI_STR_MODE; +#elif CONFIG_ESPTOOLPY_FLASHMODE_OPI_DTR + read_mode = ESP_ROM_SPIFLASH_OPI_DTR_MODE; #endif #if !CONFIG_IDF_TARGET_ESP32S2 && !CONFIG_IDF_TARGET_ESP32 spi_common_set_dummy_output(read_mode); #endif //!CONFIG_IDF_TARGET_ESP32S2 esp_rom_spiflash_config_clk(freqdiv, 1); - if (vendor_id == 0xc2) { - // If the flash vendor is mxic, it dones't support the read mode mentioned above. - // So, do nothing - } else { - esp_rom_spiflash_config_readmode(read_mode); - } -} +#if !CONFIG_ESPTOOLPY_OCT_FLASH + esp_rom_spiflash_config_readmode(read_mode); #endif +} static void IRAM_ATTR test_write(int dst_off, int src_off, int len) { @@ -233,9 +232,8 @@ static void IRAM_ATTR test_write(int dst_off, int src_off, int len) fill(dst_gold + dst_off, src_off, len); } ESP_ERROR_CHECK(spi_flash_write(start + dst_off, src_buf + src_off, len)); -#if !CONFIG_ESPTOOLPY_OCT_FLASH + fix_rom_func(); -#endif spi_flash_disable_interrupts_caches_and_other_cpu(); esp_rom_spiflash_result_t rc = esp_rom_spiflash_read(start, dst_buf, sizeof(dst_buf));