mirror of
https://github.com/espressif/esp-idf.git
synced 2024-10-05 20:47:46 -04:00
esp_flash: add opi flash support in esp_flash chip driver, for MXIC
This commit is contained in:
parent
559c1ac3f9
commit
6c0aebe279
@ -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
|
||||
|
@ -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"
|
||||
|
@ -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
|
||||
|
@ -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;
|
||||
|
||||
/**
|
||||
|
@ -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;
|
||||
|
||||
|
@ -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;
|
||||
}
|
||||
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -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)
|
||||
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
@ -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));
|
||||
}
|
||||
|
@ -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
|
||||
|
@ -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);
|
||||
|
@ -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;
|
||||
}
|
||||
|
@ -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
|
||||
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
@ -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;
|
||||
|
@ -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;
|
||||
|
@ -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
|
||||
}
|
||||
|
@ -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)
|
||||
|
@ -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);
|
||||
}
|
||||
|
||||
|
@ -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,
|
||||
};
|
||||
|
@ -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.
|
||||
|
@ -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,
|
||||
};
|
||||
|
@ -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
|
||||
|
@ -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,
|
||||
};
|
||||
|
@ -16,13 +16,10 @@
|
||||
#include "spi_flash_chip_generic.h"
|
||||
#include "spi_flash_defs.h"
|
||||
#include "esp_log.h"
|
||||
#include "string.h"
|
||||
#include <sys/param.h> // 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,
|
||||
};
|
||||
|
420
components/spi_flash/spi_flash_chip_mxic_opi.c
Normal file
420
components/spi_flash/spi_flash_chip_mxic_opi.c
Normal file
@ -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 <stdlib.h>
|
||||
#include "spi_flash_chip_generic.h"
|
||||
#include "spi_flash_defs.h"
|
||||
#include "esp_log.h"
|
||||
#include "string.h"
|
||||
#include <sys/param.h> // 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,
|
||||
};
|
@ -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,
|
||||
};
|
||||
|
||||
|
||||
|
@ -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
|
||||
|
@ -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++;
|
||||
|
@ -28,20 +28,21 @@
|
||||
#include "esp_heap_caps.h"
|
||||
|
||||
#if CONFIG_IDF_TARGET_ESP32
|
||||
#include <esp32/rom/spi_flash.h>
|
||||
#include "esp32/rom/spi_flash.h"
|
||||
#elif CONFIG_IDF_TARGET_ESP32S2
|
||||
#include <esp32s2/rom/spi_flash.h>
|
||||
#include "esp32s2/rom/spi_flash.h"
|
||||
#elif CONFIG_IDF_TARGET_ESP32S3
|
||||
#include <esp32s3/rom/spi_flash.h>
|
||||
#include "esp32s3/rom/spi_flash.h"
|
||||
#elif CONFIG_IDF_TARGET_ESP32C3
|
||||
#include <esp32c3/rom/spi_flash.h>
|
||||
#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));
|
||||
|
Loading…
Reference in New Issue
Block a user