diff --git a/components/soc/esp32/include/hal/spi_flash_ll.h b/components/soc/esp32/include/hal/spi_flash_ll.h index 5522a17219..a30284fa65 100644 --- a/components/soc/esp32/include/hal/spi_flash_ll.h +++ b/components/soc/esp32/include/hal/spi_flash_ll.h @@ -202,21 +202,6 @@ static inline bool spi_flash_ll_host_idle(const spi_dev_t *dev) return dev->ext2.st != 0; } -/** - * Set phases for user-defined transaction to read - * - * @param dev Beginning address of the peripheral registers. - */ -static inline void spi_flash_ll_read_phase(spi_dev_t *dev) -{ - typeof (dev->user) user = { - .usr_command = 1, - .usr_mosi = 0, - .usr_miso = 1, - .usr_addr = 1, - }; - dev->user = user; -} /*------------------------------------------------------------------------------ * Configs *----------------------------------------------------------------------------*/ @@ -239,7 +224,7 @@ static inline void spi_flash_ll_set_cs_pin(spi_dev_t *dev, int pin) * @param dev Beginning address of the peripheral registers. * @param read_mode I/O mode to use in the following transactions. */ -static inline void spi_flash_ll_set_read_mode(spi_dev_t *dev, esp_flash_read_mode_t read_mode) +static inline void spi_flash_ll_set_read_mode(spi_dev_t *dev, esp_flash_io_mode_t read_mode) { typeof (dev->ctrl) ctrl = dev->ctrl; ctrl.val &= ~(SPI_FREAD_QIO_M | SPI_FREAD_QUAD_M | SPI_FREAD_DIO_M | SPI_FREAD_DUAL_M); diff --git a/components/soc/include/hal/spi_flash_hal.h b/components/soc/include/hal/spi_flash_hal.h index bf89c41488..ec1b361514 100644 --- a/components/soc/include/hal/spi_flash_hal.h +++ b/components/soc/include/hal/spi_flash_hal.h @@ -155,23 +155,38 @@ esp_err_t spi_flash_hal_set_write_protect(spi_flash_host_driver_t *chip_drv, boo bool spi_flash_hal_host_idle(spi_flash_host_driver_t *driver); /** - * Configure the SPI host hardware registers for the specified read mode. + * @brief Configure the SPI host hardware registers for the specified io mode. * * Note that calling this configures SPI host registers, so if running any - * other commands as part of set_read_mode() then these must be run before + * other commands as part of set_io_mode() then these must be run before * calling this function. * + * The command value, address length and dummy cycles are configured according + * to the format of read commands: + * + * - command: 8 bits, value set. + * - address: 24 bits + * - dummy: cycles to compensate the input delay + * - out & in data: 0 bits. + * + * The following commands still need to: + * + * - Read data: set address value and data (length and contents), no need + * to touch command and dummy phases. + * - Common read: set command value, address value (or length to 0 if not used) + * - Common write: set command value, address value (or length to 0 if not + * used), disable dummy phase, and set output data. + * * @param driver The driver context - * @param read_mode The HW read mode to use + * @param io_mode The HW read mode to use * @param addr_bitlen Length of the address phase, in bits * @param dummy_cyclelen_base Base cycles of the dummy phase, some extra dummy cycles may be appended to compensate the timing. - * @param read_command Actual reading command to send to flash chip on the bus. + * @param command Actual reading command to send to flash chip on the bus. * * @return always return ESP_OK. */ -esp_err_t spi_flash_hal_configure_host_read_mode(spi_flash_host_driver_t *driver, esp_flash_read_mode_t read_mode, - uint32_t addr_bitlen, uint32_t dummy_cyclelen_base, - uint32_t read_command); +esp_err_t spi_flash_hal_configure_host_io_mode(spi_flash_host_driver_t *driver, uint32_t command, uint32_t addr_bitlen, + int dummy_cyclelen_base, esp_flash_io_mode_t io_mode); /** * Poll until the last operation is done. diff --git a/components/soc/include/hal/spi_flash_types.h b/components/soc/include/hal/spi_flash_types.h index c19eee1ed0..3e825c2aa6 100644 --- a/components/soc/include/hal/spi_flash_types.h +++ b/components/soc/include/hal/spi_flash_types.h @@ -61,7 +61,7 @@ typedef enum { SPI_FLASH_QIO, ///< Both address & data transferred using quad I/O SPI_FLASH_READ_MODE_MAX, ///< The fastest io mode supported by the host is ``ESP_FLASH_READ_MODE_MAX-1``. -} esp_flash_read_mode_t; +} esp_flash_io_mode_t; ///Slowest io mode supported by ESP32, currently SlowRd #define SPI_FLASH_READ_MODE_MIN SPI_FLASH_SLOWRD @@ -130,9 +130,11 @@ struct spi_flash_host_driver_t { */ bool (*host_idle)(spi_flash_host_driver_t *driver); /** - * Configure the host to work at different read mode. + * Configure the host to work at different read mode. Responsible to compensate the timing and set IO mode. */ - esp_err_t (*configure_host_read_mode)(spi_flash_host_driver_t *driver, esp_flash_read_mode_t read_mode, uint32_t addr_bitlen, uint32_t dummy_bitlen_base, uint32_t read_command); + esp_err_t (*configure_host_io_mode)(spi_flash_host_driver_t *driver, uint32_t command, + uint32_t addr_bitlen, int dummy_bitlen_base, + esp_flash_io_mode_t io_mode); /** * Internal use, poll the HW until the last operation is done. */ diff --git a/components/soc/src/hal/spi_flash_hal.c b/components/soc/src/hal/spi_flash_hal.c index 6a88d35a17..ee787fd914 100644 --- a/components/soc/src/hal/spi_flash_hal.c +++ b/components/soc/src/hal/spi_flash_hal.c @@ -67,8 +67,3 @@ esp_err_t spi_flash_hal_init(spi_flash_memspi_data_t *data_out, const spi_flash_ ESP_EARLY_LOGD(TAG, "extra_dummy: %d", data_out->extra_dummy); return ESP_OK; } - -static inline spi_dev_t *get_spi_dev(spi_flash_host_driver_t *chip_drv) -{ - return ((spi_flash_memspi_data_t *)chip_drv->driver_data)->spi; -} diff --git a/components/soc/src/hal/spi_flash_hal_iram.c b/components/soc/src/hal/spi_flash_hal_iram.c index 56a525baac..ea8e2bfbc7 100644 --- a/components/soc/src/hal/spi_flash_hal_iram.c +++ b/components/soc/src/hal/spi_flash_hal_iram.c @@ -52,28 +52,37 @@ esp_err_t spi_flash_hal_device_config(spi_flash_host_driver_t *driver) return ESP_OK; } -esp_err_t spi_flash_hal_configure_host_read_mode(spi_flash_host_driver_t *driver, esp_flash_read_mode_t read_mode, - uint32_t addr_bitlen, uint32_t dummy_cyclelen_base, - uint32_t read_command) +esp_err_t spi_flash_hal_configure_host_io_mode( + spi_flash_host_driver_t *host, + uint32_t command, + uint32_t addr_bitlen, + int dummy_cyclelen_base, + esp_flash_io_mode_t io_mode) { // Add dummy cycles to compensate for latency of GPIO matrix and external delay, if necessary... int dummy_cyclelen = dummy_cyclelen_base + ((spi_flash_memspi_data_t *)driver->driver_data)->extra_dummy; spi_dev_t *dev = get_spi_dev(driver); + spi_flash_ll_set_command8(dev, command); spi_flash_ll_set_addr_bitlen(dev, addr_bitlen); - spi_flash_ll_set_command8(dev, read_command); - spi_flash_ll_read_phase(dev); spi_flash_ll_set_dummy(dev, dummy_cyclelen); - spi_flash_ll_set_read_mode(dev, read_mode); + //disable all data phases, enable them later if needed + spi_flash_ll_set_miso_bitlen(dev, 0); + spi_flash_ll_set_mosi_bitlen(dev, 0); + spi_flash_ll_set_read_mode(dev, io_mode); return ESP_OK; } esp_err_t spi_flash_hal_common_command(spi_flash_host_driver_t *chip_drv, spi_flash_trans_t *trans) { - chip_drv->configure_host_read_mode(chip_drv, SPI_FLASH_FASTRD, 0, 0, 0); + host->configure_host_io_mode(host, trans->command, 0, 0, SPI_FLASH_FASTRD); + spi_dev_t *dev = get_spi_dev(chip_drv); - spi_flash_ll_set_command8(dev, trans->command); - spi_flash_ll_set_addr_bitlen(dev, 0); + //disable dummy if no input phase + if (trans->miso_len == 0) { + spi_flash_ll_set_dummy(dev, 0); + } + spi_flash_ll_set_miso_bitlen(dev, trans->miso_len); spi_flash_ll_set_mosi_bitlen(dev, trans->mosi_len); @@ -122,7 +131,7 @@ void spi_flash_hal_program_page(spi_flash_host_driver_t *chip_drv, const void *b esp_err_t spi_flash_hal_read(spi_flash_host_driver_t *chip_drv, void *buffer, uint32_t address, uint32_t read_len) { spi_dev_t *dev = get_spi_dev(chip_drv); - //the command is already set by ``spi_flash_hal_configure_host_read_mode`` before. + //the command is already set by ``spi_flash_hal_configure_host_io_mode`` before. spi_flash_ll_set_address(dev, address << 8); spi_flash_ll_set_miso_bitlen(dev, read_len * 8); spi_flash_ll_user_start(dev); diff --git a/components/spi_flash/CMakeLists.txt b/components/spi_flash/CMakeLists.txt index 878b3c8cf9..497125e47b 100644 --- a/components/spi_flash/CMakeLists.txt +++ b/components/spi_flash/CMakeLists.txt @@ -3,25 +3,31 @@ if(BOOTLOADER_BUILD) # Bootloader needs SPIUnlock from this file, but doesn't # need other parts of this component set(srcs "spi_flash_rom_patch.c") + set(cache_srcs "") else() - set(srcs + set(cache_srcs "cache_utils.c" "flash_mmap.c" "flash_ops.c" + ) + set(srcs "partition.c" "spi_flash_rom_patch.c" ) # New implementation + list(APPEND cache_srcs + "esp_flash_api.c" + "esp_flash_spi_init.c" + "spi_flash_os_func_app.c" + "spi_flash_os_func_noos.c" + ) list(APPEND srcs "spi_flash_chip_drivers.c" "spi_flash_chip_generic.c" "spi_flash_chip_issi.c" - "spi_flash_os_func_app.c" - "spi_flash_os_func_noos.c" "memspi_host_driver.c" - "esp_flash_api.c" - "esp_flash_spi_init.c" ) + list(APPEND srcs ${cache_srcs}) set(priv_requires bootloader_support app_update soc) endif() @@ -30,3 +36,7 @@ idf_component_register(SRCS "${srcs}" INCLUDE_DIRS include PRIV_INCLUDE_DIRS private_include LDFRAGMENTS linker.lf) + +# Avoid cache miss by unexpected inlineing when built by -Os +set_source_files_properties(${cache_srcs} PROPERTIES COMPILE_FLAGS + "-fno-inline-functions -fno-inline-small-functions -fno-inline-functions-called-once") diff --git a/components/spi_flash/esp_flash_api.c b/components/spi_flash/esp_flash_api.c index bb5be1bb84..7cb0e0d139 100644 --- a/components/spi_flash/esp_flash_api.c +++ b/components/spi_flash/esp_flash_api.c @@ -60,7 +60,7 @@ static const char io_mode_str[][IO_STR_LEN] = { "qio", }; -_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_read_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_ll.h"); /* Static function to notify OS of a new SPI flash operation. @@ -139,38 +139,51 @@ esp_err_t IRAM_ATTR esp_flash_init(esp_flash_t *chip) if (err == ESP_OK) { // Try to set the flash mode to whatever default mode was chosen - err = chip->chip_drv->set_read_mode(chip); + 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 spiflash_end(chip, err); } +//this is not public, but useful in unit tests +esp_err_t IRAM_ATTR esp_flash_read_chip_id(esp_flash_t* chip, uint32_t* flash_id) +{ + esp_err_t err = spiflash_start(chip); + if (err != ESP_OK) { + return err; + } + + // Send generic RDID command twice, check for a matching result and retry in case we just powered on (inner + // function fails if it sees all-ones or all-zeroes.) + err = chip->host->read_id(chip->host, flash_id); + + if (err == ESP_OK) { // check we see the same ID twice, in case of transient power-on errors + uint32_t new_id; + err = chip->host->read_id(chip->host, &new_id); + if (err == ESP_OK && (new_id != *flash_id)) { + err = ESP_ERR_FLASH_NOT_INITIALISED; + } + } + + return spiflash_end(chip, err); +} + static esp_err_t IRAM_ATTR detect_spi_flash_chip(esp_flash_t *chip) { esp_err_t err; uint32_t flash_id; int retries = 10; do { - err = spiflash_start(chip); - if (err != ESP_OK) { - return err; - } - - // Send generic RDID command twice, check for a matching result and retry in case we just powered on (inner - // function fails if it sees all-ones or all-zeroes.) - err = chip->host->read_id(chip->host, &flash_id); - - if (err == ESP_OK) { // check we see the same ID twice, in case of transient power-on errors - uint32_t new_id; - err = chip->host->read_id(chip->host, &new_id); - if (err == ESP_OK && (new_id != flash_id)) { - err = ESP_ERR_FLASH_NOT_INITIALISED; - } - } - - err = spiflash_end(chip, err); - } while (err != ESP_OK && retries-- > 0); + err = esp_flash_read_chip_id(chip, &flash_id); + } while (err == ESP_ERR_FLASH_NOT_INITIALISED && retries-- > 0); + if (err != ESP_OK) { + return err; + } // Detect the chip and set the chip_drv structure for it const spi_flash_chip_t **drivers = esp_flash_registered_chips; @@ -617,6 +630,36 @@ esp_err_t IRAM_ATTR esp_flash_read_encrypted(esp_flash_t *chip, uint32_t address return spi_flash_read_encrypted(address, out_buffer, length); } +// test only, non-public +IRAM_ATTR esp_err_t esp_flash_get_io_mode(esp_flash_t* chip, bool* qe) +{ + VERIFY_OP(get_io_mode); + esp_flash_io_mode_t io_mode; + + esp_err_t err = spiflash_start(chip); + if (err != ESP_OK) { + return err; + } + err = chip->chip_drv->get_io_mode(chip, &io_mode); + err = spiflash_end(chip, err); + if (err == ESP_OK) { + *qe = (io_mode == SPI_FLASH_QOUT); + } + return err; +} + +IRAM_ATTR esp_err_t esp_flash_set_io_mode(esp_flash_t* chip, bool qe) +{ + VERIFY_OP(set_io_mode); + chip->read_mode = (qe? SPI_FLASH_QOUT: SPI_FLASH_SLOWRD); + esp_err_t err = spiflash_start(chip); + if (err != ESP_OK) { + return err; + } + err = chip->chip_drv->set_io_mode(chip); + return spiflash_end(chip, err); +} + #ifndef CONFIG_SPI_FLASH_USE_LEGACY_IMPL esp_err_t esp_flash_app_disable_protect(bool disable) { diff --git a/components/spi_flash/esp_flash_spi_init.c b/components/spi_flash/esp_flash_spi_init.c index 0839d90181..c93afc0a76 100644 --- a/components/spi_flash/esp_flash_spi_init.c +++ b/components/spi_flash/esp_flash_spi_init.c @@ -62,7 +62,7 @@ __attribute__((unused)) static const char TAG[] = "spi_flash"; esp_flash_t *esp_flash_default_chip = NULL; -static IRAM_ATTR void cs_initialize(esp_flash_t *chip, const esp_flash_spi_device_config_t *config, bool use_iomux) +static IRAM_ATTR NOINLINE_ATTR void cs_initialize(esp_flash_t *chip, const esp_flash_spi_device_config_t *config, bool use_iomux) { //Not using spicommon_cs_initialize since we don't want to put the whole //spi_periph_signal into the DRAM. Copy these data from flash before the diff --git a/components/spi_flash/include/esp_flash.h b/components/spi_flash/include/esp_flash.h index caca075960..b2b38806d9 100644 --- a/components/spi_flash/include/esp_flash.h +++ b/components/spi_flash/include/esp_flash.h @@ -63,7 +63,7 @@ struct esp_flash_t { const esp_flash_os_functions_t *os_func; ///< Pointer to os-specific hook structure. Call ``esp_flash_init_os_functions()`` to setup this field, after the host is properly initialized. void *os_func_data; ///< Pointer to argument for os-specific hooks. Left NULL and will be initialized with ``os_func``. - esp_flash_read_mode_t read_mode; ///< Configured SPI flash read mode. Set before ``esp_flash_init`` is called. + esp_flash_io_mode_t read_mode; ///< Configured SPI flash read mode. Set before ``esp_flash_init`` is called. uint32_t size; ///< Size of SPI flash in bytes. If 0, size will be detected during initialisation. }; @@ -286,6 +286,22 @@ esp_err_t esp_flash_read_encrypted(esp_flash_t *chip, uint32_t address, void *ou extern esp_flash_t *esp_flash_default_chip; +/******************************************************************************* + * Utility Functions + ******************************************************************************/ + +/** + * @brief Returns true if chip is configured for Quad I/O or Quad Fast Read. + * + * @param chip Pointer to SPI flash chip to use. If NULL, esp_flash_default_chip is substituted. + * + * @return true if flash works in quad mode, otherwise false + */ +static inline bool esp_flash_is_quad_mode(const esp_flash_t *chip) +{ + return (chip->read_mode == SPI_FLASH_QIO) || (chip->read_mode == SPI_FLASH_QOUT); +} + #ifdef __cplusplus } #endif diff --git a/components/spi_flash/include/esp_flash_spi_init.h b/components/spi_flash/include/esp_flash_spi_init.h index 4a26a4fd1b..f7e9816183 100644 --- a/components/spi_flash/include/esp_flash_spi_init.h +++ b/components/spi_flash/include/esp_flash_spi_init.h @@ -22,7 +22,7 @@ typedef struct { spi_host_device_t host_id; ///< Bus to use int cs_id; ///< CS pin (signal) to use int cs_io_num; ///< GPIO pin to output the CS signal - esp_flash_read_mode_t io_mode; ///< IO mode to read from the Flash + esp_flash_io_mode_t io_mode; ///< IO mode to read from the Flash esp_flash_speed_t speed; ///< Speed of the Flash clock int input_delay_ns; ///< Input delay of the data pins, in ns. Set to 0 if unknown. } esp_flash_spi_device_config_t; diff --git a/components/spi_flash/include/memspi_host_driver.h b/components/spi_flash/include/memspi_host_driver.h index cb0f3e9d9e..a200f20edd 100644 --- a/components/spi_flash/include/memspi_host_driver.h +++ b/components/spi_flash/include/memspi_host_driver.h @@ -32,7 +32,7 @@ .read = spi_flash_hal_read, \ .max_read_bytes = SPI_FLASH_HAL_MAX_READ_BYTES, \ .host_idle = spi_flash_hal_host_idle, \ - .configure_host_read_mode = spi_flash_hal_configure_host_read_mode, \ + .configure_host_io_mode = spi_flash_hal_configure_host_io_mode, \ .poll_cmd_done = spi_flash_hal_poll_cmd_done, \ .flush_cache = memspi_host_flush_cache, \ } diff --git a/components/spi_flash/include/spi_flash_chip_driver.h b/components/spi_flash/include/spi_flash_chip_driver.h index 9dd8ffa971..46667350de 100644 --- a/components/spi_flash/include/spi_flash_chip_driver.h +++ b/components/spi_flash/include/spi_flash_chip_driver.h @@ -149,7 +149,13 @@ struct spi_flash_chip_t { * * Can return ESP_ERR_FLASH_UNSUPPORTED_HOST or ESP_ERR_FLASH_UNSUPPORTED_CHIP if the specified mode is unsupported. */ - esp_err_t (*set_read_mode)(esp_flash_t *chip); + esp_err_t (*set_io_mode)(esp_flash_t *chip); + + /* + * Get whether the Quad Enable (QE) is set. (*out_io_mode)=SPI_FLASH_QOUT if + * enabled, otherwise disabled + */ + esp_err_t (*get_io_mode)(esp_flash_t *chip, esp_flash_io_mode_t* out_io_mode); }; /* 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 672ca6550b..36dd3cb89e 100644 --- a/components/spi_flash/include/spi_flash_chip_generic.h +++ b/components/spi_flash/include/spi_flash_chip_generic.h @@ -213,7 +213,21 @@ esp_err_t spi_flash_chip_generic_wait_idle(esp_flash_t *chip, uint32_t timeout_m * - ESP_ERR_TIMEOUT if not idle before timeout * - or other error passed from the ``set_write_protect`` or ``common_command`` function of host driver */ -esp_err_t spi_flash_chip_generic_set_read_mode(esp_flash_t *chip); +esp_err_t spi_flash_chip_generic_set_io_mode(esp_flash_t *chip); + +/** + * Get whether the Quad Enable (QE) is set. + * + * @param chip Pointer to SPI flash chip to use. If NULL, esp_flash_default_chip is substituted. + * @param out_quad_mode Pointer to store the output mode. + * - SPI_FLASH_QOUT: QE is enabled + * - otherwise: QE is disabled + * + * @return + * - ESP_OK if success + * - or other error passed from the ``common_command`` function of host driver + */ +esp_err_t spi_flash_chip_generic_get_io_mode(esp_flash_t *chip, esp_flash_io_mode_t* out_quad_mode); /** * Generic SPI flash chip_drv, uses all the above functions for its operations. @@ -244,6 +258,78 @@ extern const spi_flash_chip_t esp_flash_chip_generic; */ esp_err_t spi_flash_generic_wait_host_idle(esp_flash_t *chip, uint32_t *timeout_ms); +/// Function pointer type for reading status register with QE bit. +typedef esp_err_t (*esp_flash_rdsr_func_t)(esp_flash_t* chip, uint32_t* out_sr); + +/** + * Use RDSR2 (35H) to read bit 15-8 of the SR, and RDSR (05H) to read bit 7-0. + * + * @param chip Pointer to SPI flash chip to use. + * @param out_sr Pointer to buffer to hold the status register, 16 bits. + * + * @return ESP_OK if success, otherwise error code passed from the + * `common_command` function of the host driver. + */ +esp_err_t spi_flash_common_read_status_16b_rdsr_rdsr2(esp_flash_t* chip, uint32_t* out_sr); + +/** + * Use RDSR2 (35H) to read bit 15-8 of the SR. + * + * @param chip Pointer to SPI flash chip to use. + * @param out_sr Pointer to buffer to hold the status register, 8 bits. + * + * @return ESP_OK if success, otherwise error code passed from the + * `common_command` function of the host driver. + */ +esp_err_t spi_flash_common_read_status_8b_rdsr2(esp_flash_t* chip, uint32_t* out_sr); + +/** + * Use RDSR (05H) to read bit 7-0 of the SR. + * + * @param chip Pointer to SPI flash chip to use. + * @param out_sr Pointer to buffer to hold the status register, 8 bits. + * + * @return ESP_OK if success, otherwise error code passed from the + * `common_command` function of the host driver. + */ +esp_err_t spi_flash_common_read_status_8b_rdsr(esp_flash_t* chip, uint32_t* out_sr); + +/// Function pointer type for writing status register with QE bit. +typedef esp_err_t (*esp_flash_wrsr_func_t)(esp_flash_t* chip, uint32_t sr); + +/** + * Use WRSR (01H) to write bit 7-0 of the SR. + * + * @param chip Pointer to SPI flash chip to use. + * @param sr Value of the status register to write, 8 bits. + * + * @return ESP_OK if success, otherwise error code passed from the + * `common_command` function of the host driver. + */ +esp_err_t spi_flash_common_write_status_8b_wrsr(esp_flash_t* chip, uint32_t sr); + +/** + * Use WRSR (01H) to write bit 15-0 of the SR. + * + * @param chip Pointer to SPI flash chip to use. + * @param sr Value of the status register to write, 16 bits. + * + * @return ESP_OK if success, otherwise error code passed from the + * `common_command` function of the host driver. + */ +esp_err_t spi_flash_common_write_status_16b_wrsr(esp_flash_t* chip, uint32_t sr); + +/** + * Use WRSR2 (31H) to write bit 15-8 of the SR. + * + * @param chip Pointer to SPI flash chip to use. + * @param sr Value of the status register to write, 8 bits. + * + * @return ESP_OK if success, otherwise error code passed from the + * `common_command` function of the host driver. + */ +esp_err_t spi_flash_common_write_status_8b_wrsr2(esp_flash_t* chip, uint32_t sr); + /** * @brief Utility function for set_read_mode chip_drv function. If required, * set and check the QE bit in the flash chip to enable the QIO/QOUT mode. @@ -253,16 +339,19 @@ esp_err_t spi_flash_generic_wait_host_idle(esp_flash_t *chip, uint32_t *timeout_ * * Registers to actually do Quad transtions and command to be sent in reading * should also be configured via - * spi_flash_chip_generic_config_host_read_mode(). + * spi_flash_chip_generic_config_host_io_mode(). * - * @param qe_rdsr_command SPI flash command to read status register - * @param qe_wrsr_command SPI flash command to write status register - * @param qe_sr_bitwidth Width of the status register these commands operate on, in bits. - * @param qe_sr_bit Bit mask for enabling Quad Enable functions on this chip. + * Note that the bit length and qe position of wrsr_func, rdsr_func and + * qe_sr_bit should be consistent. + * + * @param chip Pointer to SPI flash chip to use. + * @param wrsr_func Function pointer for writing the status register + * @param rdsr_func Function pointer for reading the status register + * @param qe_sr_bit status with the qe bit only. * * @return always ESP_OK (currently). */ -esp_err_t spi_flash_common_set_read_mode(esp_flash_t *chip, uint8_t qe_rdsr_command, uint8_t qe_wrsr_command, uint8_t qe_sr_bitwidth, unsigned qe_sr_bit); +esp_err_t spi_flash_common_set_io_mode(esp_flash_t *chip, esp_flash_wrsr_func_t wrsr_func, esp_flash_rdsr_func_t rdsr_func, uint32_t qe_sr_bit); /** * @brief Configure the host registers to use the specified read mode set in @@ -278,17 +367,4 @@ esp_err_t spi_flash_common_set_read_mode(esp_flash_t *chip, uint8_t qe_rdsr_comm * - 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_read_mode(esp_flash_t *chip); - -/** - * @brief Returns true if chip is configured for Quad I/O or Quad Fast Read. - * - * @param chip Pointer to SPI flash chip to use. If NULL, esp_flash_default_chip is substituted. - * - * @return true if flash works in quad mode, otherwise false - */ -static inline bool spi_flash_is_quad_mode(const esp_flash_t *chip) -{ - return (chip->read_mode == SPI_FLASH_QIO) || (chip->read_mode == SPI_FLASH_QOUT); -} - +esp_err_t spi_flash_chip_generic_config_host_io_mode(esp_flash_t *chip); diff --git a/components/spi_flash/spi_flash_chip_generic.c b/components/spi_flash/spi_flash_chip_generic.c index 8bbb9d1374..16d6b85e16 100644 --- a/components/spi_flash/spi_flash_chip_generic.c +++ b/components/spi_flash/spi_flash_chip_generic.c @@ -144,7 +144,7 @@ esp_err_t spi_flash_chip_generic_read(esp_flash_t *chip, void *buffer, uint32_t { esp_err_t err = ESP_OK; // Configure the host, and return - spi_flash_chip_generic_config_host_read_mode(chip); + spi_flash_chip_generic_config_host_io_mode(chip); while (err == ESP_OK && length > 0) { uint32_t read_len = MIN(length, chip->host->max_read_bytes); @@ -277,7 +277,7 @@ esp_err_t spi_flash_chip_generic_wait_idle(esp_flash_t *chip, uint32_t timeout_m return (timeout_ms > 0) ? ESP_OK : ESP_ERR_TIMEOUT; } -esp_err_t spi_flash_chip_generic_config_host_read_mode(esp_flash_t *chip) +esp_err_t spi_flash_chip_generic_config_host_io_mode(esp_flash_t *chip) { uint32_t dummy_cyclelen_base; uint32_t addr_bitlen; @@ -320,62 +320,33 @@ esp_err_t spi_flash_chip_generic_config_host_read_mode(esp_flash_t *chip) return ESP_ERR_FLASH_NOT_INITIALISED; } - return chip->host->configure_host_read_mode(chip->host, chip->read_mode, addr_bitlen, dummy_cyclelen_base, read_command); + return chip->host->configure_host_io_mode(chip->host, read_command, addr_bitlen, dummy_cyclelen_base, + chip->read_mode); } -esp_err_t spi_flash_common_set_read_mode(esp_flash_t *chip, uint8_t qe_rdsr_command, uint8_t qe_wrsr_command, uint8_t qe_sr_bitwidth, unsigned qe_sr_bit) -{ - if (spi_flash_is_quad_mode(chip)) { - // Ensure quad modes are enabled, using the Quad Enable parameters supplied. - spi_flash_trans_t t = { - .command = qe_rdsr_command, - .mosi_data = 0, - .mosi_len = 0, - .miso_len = qe_sr_bitwidth, - }; - chip->host->common_command(chip->host, &t); - unsigned sr = t.miso_data[0]; - ESP_EARLY_LOGV(TAG, "set_read_mode: status before 0x%x", sr); - if ((sr & qe_sr_bit) == 0) { - //some chips needs the write protect to be disabled before writing to Status Register - chip->chip_drv->set_chip_write_protect(chip, false); - - sr |= qe_sr_bit; - spi_flash_trans_t t = { - .command = qe_wrsr_command, - .mosi_data = sr, - .mosi_len = qe_sr_bitwidth, - .miso_len = 0, - }; - chip->host->common_command(chip->host, &t); - - /* Check the new QE bit has stayed set */ - spi_flash_trans_t t_rdsr = { - .command = qe_rdsr_command, - .mosi_data = 0, - .mosi_len = 0, - .miso_len = qe_sr_bitwidth - }; - chip->host->common_command(chip->host, &t_rdsr); - sr = t_rdsr.miso_data[0]; - ESP_EARLY_LOGV(TAG, "set_read_mode: status after 0x%x", sr); - if ((sr & qe_sr_bit) == 0) { - return ESP_ERR_FLASH_NO_RESPONSE; - } - - chip->chip_drv->set_chip_write_protect(chip, true); - } - } - return ESP_OK; -} - -esp_err_t spi_flash_chip_generic_set_read_mode(esp_flash_t *chip) +esp_err_t spi_flash_chip_generic_get_io_mode(esp_flash_t *chip, esp_flash_io_mode_t* out_io_mode) { // On "generic" chips, this involves checking // bit 1 (QE) of RDSR2 (35h) result // (it works this way on GigaDevice & Fudan Micro chips, probably others...) const uint8_t BIT_QE = 1 << 1; - return spi_flash_common_set_read_mode(chip, CMD_RDSR2, CMD_WRSR2, 8, BIT_QE); + uint32_t sr; + esp_err_t ret = spi_flash_common_read_status_8b_rdsr2(chip, &sr); + if (ret == ESP_OK) { + *out_io_mode = ((sr & BIT_QE)? SPI_FLASH_QOUT: 0); + } + return ret; +} + +esp_err_t spi_flash_chip_generic_set_io_mode(esp_flash_t *chip) +{ + // On "generic" chips, this involves checking + // bit 9 (QE) of RDSR (05h) result + const uint32_t BIT_QE = 1 << 9; + return spi_flash_common_set_io_mode(chip, + spi_flash_common_write_status_16b_wrsr, + spi_flash_common_read_status_16b_rdsr_rdsr2, + BIT_QE); } static const char chip_name[] = "generic"; @@ -409,5 +380,134 @@ const spi_flash_chip_t esp_flash_chip_generic = { .write_encrypted = spi_flash_chip_generic_write_encrypted, .wait_idle = spi_flash_chip_generic_wait_idle, - .set_read_mode = spi_flash_chip_generic_set_read_mode, + .set_io_mode = spi_flash_chip_generic_set_io_mode, + .get_io_mode = spi_flash_chip_generic_get_io_mode, }; + +/******************************************************************************* + * Utility functions + ******************************************************************************/ + +static esp_err_t spi_flash_common_read_qe_sr(esp_flash_t *chip, uint8_t qe_rdsr_command, uint8_t qe_sr_bitwidth, uint32_t *sr) +{ + spi_flash_trans_t t = { + .command = qe_rdsr_command, + .mosi_data = 0, + .mosi_len = 0, + .miso_len = qe_sr_bitwidth, + }; + esp_err_t ret = chip->host->common_command(chip->host, &t); + *sr = t.miso_data[0]; + return ret; +} + +static esp_err_t spi_flash_common_write_qe_sr(esp_flash_t *chip, uint8_t qe_wrsr_command, uint8_t qe_sr_bitwidth, uint32_t qe) +{ + spi_flash_trans_t t = { + .command = qe_wrsr_command, + .mosi_data = qe, + .mosi_len = qe_sr_bitwidth, + .miso_len = 0, + }; + return chip->host->common_command(chip->host, &t); +} + +esp_err_t spi_flash_common_read_status_16b_rdsr_rdsr2(esp_flash_t* chip, uint32_t* out_sr) +{ + uint32_t sr, sr2; + esp_err_t ret = spi_flash_common_read_qe_sr(chip, CMD_RDSR2, 8, &sr2); + if (ret == ESP_OK) { + ret = spi_flash_common_read_qe_sr(chip, CMD_RDSR, 8, &sr); + } + if (ret == ESP_OK) { + *out_sr = (sr & 0xff) | ((sr2 & 0xff) << 8); + } + return ret; +} + +esp_err_t spi_flash_common_read_status_8b_rdsr2(esp_flash_t* chip, uint32_t* out_sr) +{ + return spi_flash_common_read_qe_sr(chip, CMD_RDSR2, 8, out_sr); +} + +esp_err_t spi_flash_common_read_status_8b_rdsr(esp_flash_t* chip, uint32_t* out_sr) +{ + return spi_flash_common_read_qe_sr(chip, CMD_RDSR, 8, out_sr); +} + +esp_err_t spi_flash_common_write_status_16b_wrsr(esp_flash_t* chip, uint32_t sr) +{ + return spi_flash_common_write_qe_sr(chip, CMD_WRSR, 16, sr); +} + +esp_err_t spi_flash_common_write_status_8b_wrsr(esp_flash_t* chip, uint32_t sr) +{ + return spi_flash_common_write_qe_sr(chip, CMD_WRSR, 8, sr); +} + +esp_err_t spi_flash_common_write_status_8b_wrsr2(esp_flash_t* chip, uint32_t sr) +{ + return spi_flash_common_write_qe_sr(chip, CMD_WRSR2, 8, sr); +} + +esp_err_t spi_flash_common_set_io_mode(esp_flash_t *chip, esp_flash_wrsr_func_t wrsr_func, esp_flash_rdsr_func_t rdsr_func, uint32_t qe_sr_bit) +{ + esp_err_t ret = ESP_OK; + const bool is_quad_mode = esp_flash_is_quad_mode(chip); + bool update_config = false; + const bool force_check = true; //in case some chips doesn't support erase QE + + bool need_check = is_quad_mode; + if (force_check) { + need_check = true; + } + + uint32_t sr_update; + if (need_check) { + // Ensure quad modes are enabled, using the Quad Enable parameters supplied. + uint32_t sr; + ret = (*rdsr_func)(chip, &sr); + if (ret != ESP_OK) { + return ret; + } + ESP_EARLY_LOGD(TAG, "set_io_mode: status before 0x%x", sr); + if (is_quad_mode) { + sr_update = sr | qe_sr_bit; + } else { + sr_update = sr & (~qe_sr_bit); + } + ESP_EARLY_LOGV(TAG, "set_io_mode: status update 0x%x", sr_update); + if (sr != sr_update) { + update_config = true; + } + } + + if (update_config) { + //some chips needs the write protect to be disabled before writing to Status Register + chip->chip_drv->set_chip_write_protect(chip, false); + + ret = (*wrsr_func)(chip, sr_update); + if (ret != ESP_OK) { + return ret; + } + + ret = chip->chip_drv->wait_idle(chip, DEFAULT_IDLE_TIMEOUT); + if (ret != ESP_OK) { + return ret; + } + + /* Check the new QE bit has stayed set */ + uint32_t sr; + ret = (*rdsr_func)(chip, &sr); + if (ret != ESP_OK) { + return ret; + } + ESP_EARLY_LOGD(TAG, "set_io_mode: status after 0x%x", sr); + if (sr != sr_update) { + ret = ESP_ERR_FLASH_NO_RESPONSE; + } + + chip->chip_drv->set_chip_write_protect(chip, true); + } + return ret; +} diff --git a/components/spi_flash/spi_flash_chip_issi.c b/components/spi_flash/spi_flash_chip_issi.c index 71684ec195..d5ecd4c040 100644 --- a/components/spi_flash/spi_flash_chip_issi.c +++ b/components/spi_flash/spi_flash_chip_issi.c @@ -35,13 +35,29 @@ esp_err_t spi_flash_chip_issi_probe(esp_flash_t *chip, uint32_t flash_id) return ESP_OK; } -esp_err_t spi_flash_chip_issi_set_read_mode(esp_flash_t *chip) +esp_err_t spi_flash_chip_issi_set_io_mode(esp_flash_t *chip) { /* ISSI uses bit 6 of "basic" SR as Quad Enable */ const uint8_t BIT_QE = 1 << 6; - return spi_flash_common_set_read_mode(chip, CMD_RDSR, CMD_WRSR, 8, BIT_QE); + return spi_flash_common_set_io_mode(chip, + spi_flash_common_write_status_8b_wrsr, + spi_flash_common_read_status_8b_rdsr, + BIT_QE); } +esp_err_t spi_flash_chip_issi_get_io_mode(esp_flash_t *chip, esp_flash_io_mode_t* out_io_mode) +{ + /* ISSI uses bit 6 of "basic" SR as Quad Enable */ + const uint8_t BIT_QE = 1 << 6; + uint32_t sr; + esp_err_t ret = spi_flash_common_read_status_8b_rdsr(chip, &sr); + if (ret == ESP_OK) { + *out_io_mode = ((sr & BIT_QE)? SPI_FLASH_QOUT: 0); + } + return ret; +} + + static const char chip_name[] = "issi"; // The issi chip can use the functions for generic chips except from set read mode and probe, @@ -73,5 +89,6 @@ const spi_flash_chip_t esp_flash_chip_issi = { .write_encrypted = spi_flash_chip_generic_write_encrypted, .wait_idle = spi_flash_chip_generic_wait_idle, - .set_read_mode = spi_flash_chip_issi_set_read_mode, + .set_io_mode = spi_flash_chip_issi_set_io_mode, + .get_io_mode = spi_flash_chip_issi_get_io_mode, }; diff --git a/components/spi_flash/test/test_esp_flash.c b/components/spi_flash/test/test_esp_flash.c index 6d229404d4..ded1756232 100644 --- a/components/spi_flash/test/test_esp_flash.c +++ b/components/spi_flash/test/test_esp_flash.c @@ -128,7 +128,7 @@ static void release_bus(int host_id) } } -static void setup_new_chip(esp_flash_read_mode_t io_mode, esp_flash_speed_t speed) +static void setup_new_chip(esp_flash_io_mode_t io_mode, esp_flash_speed_t speed) { //the bus should be initialized before the flash is attached to the bus setup_bus(TEST_HOST);