diff --git a/components/esp_rom/include/esp32/rom/spi_flash.h b/components/esp_rom/include/esp32/rom/spi_flash.h index c71b810017..9fcbffc8af 100644 --- a/components/esp_rom/include/esp32/rom/spi_flash.h +++ b/components/esp_rom/include/esp32/rom/spi_flash.h @@ -543,6 +543,13 @@ esp_rom_spiflash_result_t esp_rom_spiflash_wait_idle(esp_rom_spiflash_chip_t *sp */ void esp_rom_spiflash_select_qio_pins(uint8_t wp_gpio_num, uint32_t spiconfig); +/** + * @brief Clear WEL bit unconditionally. + * + * @return always ESP_ROM_SPIFLASH_RESULT_OK + */ +esp_rom_spiflash_result_t esp_rom_spiflash_write_disable(void); + /** @brief Global esp_rom_spiflash_chip_t structure used by ROM functions * */ diff --git a/components/esp_rom/include/esp32s2/rom/spi_flash.h b/components/esp_rom/include/esp32s2/rom/spi_flash.h index 2cf631666c..b056d317ea 100644 --- a/components/esp_rom/include/esp32s2/rom/spi_flash.h +++ b/components/esp_rom/include/esp32s2/rom/spi_flash.h @@ -554,6 +554,13 @@ esp_rom_spiflash_result_t esp_rom_spiflash_wait_idle(esp_rom_spiflash_chip_t *sp */ void esp_rom_spiflash_select_qio_pins(uint8_t wp_gpio_num, uint32_t spiconfig); +/** + * @brief Clear WEL bit unconditionally. + * + * @return always ESP_ROM_SPIFLASH_RESULT_OK + */ +esp_rom_spiflash_result_t esp_rom_spiflash_write_disable(void); + /** @brief Global esp_rom_spiflash_chip_t structure used by ROM functions * */ diff --git a/components/esp_rom/include/esp32s3/rom/spi_flash.h b/components/esp_rom/include/esp32s3/rom/spi_flash.h index 44d447e933..63768168a2 100644 --- a/components/esp_rom/include/esp32s3/rom/spi_flash.h +++ b/components/esp_rom/include/esp32s3/rom/spi_flash.h @@ -541,6 +541,13 @@ esp_rom_spiflash_result_t esp_rom_spiflash_wait_idle(esp_rom_spiflash_chip_t *sp */ void esp_rom_spiflash_select_qio_pins(uint8_t wp_gpio_num, uint32_t spiconfig); +/** + * @brief Clear WEL bit unconditionally. + * + * @return always ESP_ROM_SPIFLASH_RESULT_OK + */ +esp_rom_spiflash_result_t esp_rom_spiflash_write_disable(void); + /** @brief Global esp_rom_spiflash_chip_t structure used by ROM functions * */ diff --git a/components/spi_flash/CMakeLists.txt b/components/spi_flash/CMakeLists.txt index e49bdbd2cf..78b536774b 100644 --- a/components/spi_flash/CMakeLists.txt +++ b/components/spi_flash/CMakeLists.txt @@ -3,9 +3,10 @@ if(BOOTLOADER_BUILD) # ESP32 Bootloader needs SPIUnlock from this file, but doesn't # need other parts of this component set(srcs "esp32/spi_flash_rom_patch.c") - else() - # but on other platforms no source files are needed for bootloader - set(srcs) + elseif(CONFIG_IDF_TARGET_ESP32S2) + set(srcs "esp32s2/spi_flash_rom_patch.c") + elseif(CONFIG_IDF_TARGET_ESP32S3) + set(srcs "esp32s3/spi_flash_rom_patch.c") endif() set(cache_srcs "") set(priv_requires bootloader_support soc) @@ -22,6 +23,12 @@ else() if(CONFIG_IDF_TARGET_ESP32) list(APPEND srcs "esp32/spi_flash_rom_patch.c") + elseif(CONFIG_IDF_TARGET_ESP32S2) + list(APPEND srcs + "esp32s2/spi_flash_rom_patch.c") + elseif(CONFIG_IDF_TARGET_ESP32S3) + list(APPEND srcs + "esp32s3/spi_flash_rom_patch.c") endif() # New implementation after IDF v4.0 diff --git a/components/spi_flash/esp32/spi_flash_rom_patch.c b/components/spi_flash/esp32/spi_flash_rom_patch.c index 4712d37a64..9ec05c37e7 100644 --- a/components/spi_flash/esp32/spi_flash_rom_patch.c +++ b/components/spi_flash/esp32/spi_flash_rom_patch.c @@ -14,6 +14,7 @@ #include "sdkconfig.h" #include "esp32/rom/spi_flash.h" #include "soc/spi_periph.h" +#include "spi_flash_defs.h" #define SPI_IDX 1 @@ -682,4 +683,11 @@ esp_rom_spiflash_result_t esp_rom_spiflash_erase_area(uint32_t start_addr, uint3 return ESP_ROM_SPIFLASH_RESULT_OK; } +esp_rom_spiflash_result_t esp_rom_spiflash_write_disable(void) +{ + REG_WRITE(SPI_CMD_REG(SPI_IDX), SPI_FLASH_WRDI); + while (READ_PERI_REG(PERIPHS_SPI_FLASH_CMD) != 0); + return ESP_ROM_SPIFLASH_RESULT_OK; +} + #endif diff --git a/components/spi_flash/esp32s2/spi_flash_rom_patch.c b/components/spi_flash/esp32s2/spi_flash_rom_patch.c new file mode 100644 index 0000000000..139a56bd2b --- /dev/null +++ b/components/spi_flash/esp32s2/spi_flash_rom_patch.c @@ -0,0 +1,28 @@ +// 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 "sdkconfig.h" +#include "esp32s2/rom/spi_flash.h" +#include "soc/spi_periph.h" +#include "spi_flash_defs.h" + + +#define SPI_IDX 1 +extern esp_rom_spiflash_chip_t g_rom_spiflash_chip; + +esp_rom_spiflash_result_t esp_rom_spiflash_write_disable(void) +{ + REG_WRITE(SPI_MEM_CMD_REG(SPI_IDX), SPI_MEM_FLASH_WRDI); + while (READ_PERI_REG(PERIPHS_SPI_FLASH_CMD) != 0); + return ESP_ROM_SPIFLASH_RESULT_OK; +} diff --git a/components/spi_flash/esp32s3/spi_flash_rom_patch.c b/components/spi_flash/esp32s3/spi_flash_rom_patch.c new file mode 100644 index 0000000000..2cdec50427 --- /dev/null +++ b/components/spi_flash/esp32s3/spi_flash_rom_patch.c @@ -0,0 +1,28 @@ +// 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 "sdkconfig.h" +#include "esp32s3/rom/spi_flash.h" +#include "soc/spi_periph.h" +#include "spi_flash_defs.h" + + +#define SPI_IDX 1 +extern esp_rom_spiflash_chip_t g_rom_spiflash_chip; + +esp_rom_spiflash_result_t esp_rom_spiflash_write_disable(void) +{ + REG_WRITE(SPI_MEM_CMD_REG(SPI_IDX), SPI_MEM_FLASH_WRDI); + while (READ_PERI_REG(PERIPHS_SPI_FLASH_CMD) != 0); + return ESP_ROM_SPIFLASH_RESULT_OK; +} diff --git a/components/spi_flash/flash_ops.c b/components/spi_flash/flash_ops.c index cd1f2ea115..4cbdab4bdf 100644 --- a/components/spi_flash/flash_ops.c +++ b/components/spi_flash/flash_ops.c @@ -289,6 +289,8 @@ esp_err_t IRAM_ATTR spi_flash_erase_range(size_t start_addr, size_t size) COUNTER_STOP(erase); spi_flash_guard_start(); + // Ensure WEL is 0 after the operation, even if the erase failed. + esp_rom_spiflash_write_disable(); spi_flash_check_and_flush_cache(start_addr, size); spi_flash_guard_end(); @@ -461,6 +463,8 @@ out: COUNTER_STOP(write); spi_flash_guard_start(); + // Ensure WEL is 0 after the operation, even if the write failed. + esp_rom_spiflash_write_disable(); spi_flash_check_and_flush_cache(dst, size); spi_flash_guard_end(); @@ -491,6 +495,7 @@ esp_err_t IRAM_ATTR spi_flash_write_encrypted(size_t dest_addr, const void *src, err = spi_flash_write_encrypted_chip(dest_addr, src, size); COUNTER_ADD_BYTES(write, size); spi_flash_guard_start(); + esp_rom_spiflash_write_disable(); spi_flash_check_and_flush_cache(dest_addr, size); spi_flash_guard_end(); #else @@ -525,6 +530,7 @@ esp_err_t IRAM_ATTR spi_flash_write_encrypted(size_t dest_addr, const void *src, COUNTER_ADD_BYTES(write, size); spi_flash_guard_start(); + esp_rom_spiflash_write_disable(); spi_flash_check_and_flush_cache(dest_addr, size); spi_flash_guard_end(); diff --git a/components/spi_flash/include/esp_flash.h b/components/spi_flash/include/esp_flash.h index 2df1d53c57..845d82f17c 100644 --- a/components/spi_flash/include/esp_flash.h +++ b/components/spi_flash/include/esp_flash.h @@ -102,6 +102,8 @@ struct esp_flash_t { 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. uint32_t chip_id; ///< Detected chip id. + uint32_t busy :1; ///< This flag is used to verify chip's status. + uint32_t reserved_flags :31; ///< reserved. }; @@ -159,7 +161,10 @@ esp_err_t esp_flash_get_size(esp_flash_t *chip, uint32_t *out_size); * @param chip Pointer to identify flash chip. Must have been successfully initialised via esp_flash_init() * * - * @return ESP_OK on success, or a flash error code if operation failed. + * @return + * - ESP_OK on success, + * - ESP_ERR_NOT_SUPPORTED if the chip is not able to perform the operation. This is indicated by WREN = 1 after the command is sent. + * - Other flash error code if operation failed. */ esp_err_t esp_flash_erase_chip(esp_flash_t *chip); @@ -176,7 +181,10 @@ esp_err_t esp_flash_erase_chip(esp_flash_t *chip); * chip->drv->block_erase_size field, typically 65536 bytes). Remaining sectors are erased using individual sector erase * commands. * - * @return ESP_OK on success, or a flash error code if operation failed. + * @return + * - ESP_OK on success, + * - ESP_ERR_NOT_SUPPORTED if the chip is not able to perform the operation. This is indicated by WREN = 1 after the command is sent. + * - Other flash error code if operation failed. */ esp_err_t esp_flash_erase_region(esp_flash_t *chip, uint32_t start, uint32_t len); @@ -280,7 +288,10 @@ esp_err_t esp_flash_read(esp_flash_t *chip, void *buffer, uint32_t address, uint * * There are no alignment constraints on buffer, address or length. * - * @return ESP_OK on success, or a flash error code if operation failed. + * @return + * - ESP_OK on success, + * - ESP_ERR_NOT_SUPPORTED if the chip is not able to perform the operation. This is indicated by WREN = 1 after the command is sent. + * - Other flash error code if operation failed. */ esp_err_t esp_flash_write(esp_flash_t *chip, const void *buffer, uint32_t address, uint32_t length); diff --git a/components/spi_flash/include/spi_flash_chip_generic.h b/components/spi_flash/include/spi_flash_chip_generic.h index 43475c3fc6..1b54e0740e 100644 --- a/components/spi_flash/include/spi_flash_chip_generic.h +++ b/components/spi_flash/include/spi_flash_chip_generic.h @@ -72,6 +72,7 @@ esp_err_t spi_flash_chip_generic_detect_size(esp_flash_t *chip, uint32_t *size); * * @return * - ESP_OK if success + * - ESP_ERR_NOT_SUPPORTED if the chip is not able to perform the operation. This is indicated by WREN = 1 after the command is sent. * - or other error passed from the ``set_write_protect``, ``wait_idle`` or ``erase_chip`` function of host driver */ esp_err_t spi_flash_chip_generic_erase_chip(esp_flash_t *chip); @@ -84,6 +85,7 @@ esp_err_t spi_flash_chip_generic_erase_chip(esp_flash_t *chip); * * @return * - ESP_OK if success + * - ESP_ERR_NOT_SUPPORTED if the chip is not able to perform the operation. This is indicated by WREN = 1 after the command is sent. * - or other error passed from the ``set_write_protect``, ``wait_idle`` or ``erase_sector`` function of host driver */ esp_err_t spi_flash_chip_generic_erase_sector(esp_flash_t *chip, uint32_t start_address); @@ -96,6 +98,7 @@ esp_err_t spi_flash_chip_generic_erase_sector(esp_flash_t *chip, uint32_t start_ * * @return * - ESP_OK if success + * - ESP_ERR_NOT_SUPPORTED if the chip is not able to perform the operation. This is indicated by WREN = 1 after the command is sent. * - or other error passed from the ``set_write_protect``, ``wait_idle`` or ``erase_block`` function of host driver */ esp_err_t spi_flash_chip_generic_erase_block(esp_flash_t *chip, uint32_t start_address); @@ -129,6 +132,7 @@ esp_err_t spi_flash_chip_generic_read(esp_flash_t *chip, void *buffer, uint32_t * * @return * - ESP_OK if success + * - ESP_ERR_NOT_SUPPORTED if the chip is not able to perform the operation. This is indicated by WREN = 1 after the command is sent. * - or other error passed from the ``wait_idle`` or ``program_page`` function of host driver */ esp_err_t diff --git a/components/spi_flash/sim/flash_mock.cpp b/components/spi_flash/sim/flash_mock.cpp index 81529b5d81..a8e2ed7361 100644 --- a/components/spi_flash/sim/flash_mock.cpp +++ b/components/spi_flash/sim/flash_mock.cpp @@ -110,4 +110,9 @@ void *heap_caps_malloc( size_t size, uint32_t caps ) return NULL; } +esp_rom_spiflash_result_t esp_rom_spiflash_write_disable(void) +{ + return ESP_ROM_SPIFLASH_RESULT_OK; +} + esp_flash_t* esp_flash_default_chip = NULL; diff --git a/components/spi_flash/spi_flash_chip_generic.c b/components/spi_flash/spi_flash_chip_generic.c index 2277826541..e36d443e6b 100644 --- a/components/spi_flash/spi_flash_chip_generic.c +++ b/components/spi_flash/spi_flash_chip_generic.c @@ -117,14 +117,21 @@ esp_err_t spi_flash_chip_generic_erase_chip(esp_flash_t *chip) if (err == ESP_OK) { err = chip->chip_drv->wait_idle(chip, chip->chip_drv->timeout->idle_timeout); } - if (err == ESP_OK) { + //The chip didn't accept the previous write command. Ignore this in preparation stage. + if (err == ESP_OK || err == ESP_ERR_NOT_SUPPORTED) { chip->host->driver->erase_chip(chip->host); + 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) { + err = chip->chip_drv->set_chip_write_protect(chip, true); + } + return err; } @@ -134,14 +141,21 @@ esp_err_t spi_flash_chip_generic_erase_sector(esp_flash_t *chip, uint32_t start_ if (err == ESP_OK) { err = chip->chip_drv->wait_idle(chip, chip->chip_drv->timeout->idle_timeout); } - if (err == ESP_OK) { + //The chip didn't accept the previous write command. Ignore this in preparationstage. + if (err == ESP_OK || err == ESP_ERR_NOT_SUPPORTED) { chip->host->driver->erase_sector(chip->host, start_address); + 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; } @@ -151,14 +165,21 @@ esp_err_t spi_flash_chip_generic_erase_block(esp_flash_t *chip, uint32_t start_a if (err == ESP_OK) { err = chip->chip_drv->wait_idle(chip, chip->chip_drv->timeout->idle_timeout); } - if (err == ESP_OK) { + //The chip didn't accept the previous write command. Ignore this in preparationstage. + if (err == ESP_OK || err == ESP_ERR_NOT_SUPPORTED) { chip->host->driver->erase_block(chip->host, start_address); + 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; } @@ -199,13 +220,18 @@ esp_err_t spi_flash_chip_generic_page_program(esp_flash_t *chip, const void *buf esp_err_t err; err = chip->chip_drv->wait_idle(chip, chip->chip_drv->timeout->idle_timeout); - - if (err == ESP_OK) { + //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 chip->host->driver->program_page(chip->host, buffer, address, length); + 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; } @@ -247,8 +273,8 @@ esp_err_t spi_flash_chip_generic_set_write_protect(esp_flash_t *chip, bool write esp_err_t err = ESP_OK; err = chip->chip_drv->wait_idle(chip, chip->chip_drv->timeout->idle_timeout); - - if (err == ESP_OK) { + //The chip didn't accept the previous write command. Ignore this in preparationstage. + if (err == ESP_OK || err == ESP_ERR_NOT_SUPPORTED) { chip->host->driver->set_write_protect(chip->host, write_protect); } @@ -264,9 +290,9 @@ esp_err_t spi_flash_chip_generic_set_write_protect(esp_flash_t *chip, bool write esp_err_t spi_flash_chip_generic_get_write_protect(esp_flash_t *chip, bool *out_write_protect) { esp_err_t err = ESP_OK; - uint8_t status; + uint32_t status; assert(out_write_protect!=NULL); - err = chip->host->driver->read_status(chip->host, &status); + err = chip->chip_drv->read_reg(chip, SPI_FLASH_REG_STATUS, &status); if (err != ESP_OK) { return err; } @@ -344,8 +370,14 @@ esp_err_t spi_flash_chip_generic_wait_idle(esp_flash_t *chip, uint32_t timeout_u } status = read; - if ((status & SR_WIP) == 0) { - break; // Write in progress is complete + if ((status & SR_WIP) == 0) { // Verify write in progress is complete + if (chip->busy == 1) { + chip->busy = 0; + if ((status & SR_WREN) != 0) { // The previous command is not accepted, leaving the WEL still set. + return ESP_ERR_NOT_SUPPORTED; + } + } + break; } if (timeout_us > 0 && interval > 0) { int delay = MIN(interval, timeout_us); @@ -355,7 +387,6 @@ 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; } @@ -587,11 +618,19 @@ esp_err_t spi_flash_common_set_io_mode(esp_flash_t *chip, esp_flash_wrsr_func_t ret = (*wrsr_func)(chip, sr_update); if (ret != ESP_OK) { + chip->chip_drv->set_chip_write_protect(chip, true); return ret; } ret = chip->chip_drv->wait_idle(chip, chip->chip_drv->timeout->idle_timeout); - if (ret != ESP_OK) { + if (ret == ESP_ERR_NOT_SUPPORTED) { + chip->chip_drv->set_chip_write_protect(chip, true); + } + /* This function is the fallback approach, so we give it higher tolerance. + * When the previous WRSR is rejected by the flash, + * the result of this function is determined by the result -whether the value of RDSR meets the expectation. + */ + if (ret != ESP_OK && ret != ESP_ERR_NOT_SUPPORTED) { return ret; } @@ -605,8 +644,6 @@ esp_err_t spi_flash_common_set_io_mode(esp_flash_t *chip, esp_flash_wrsr_func_t if (sr != sr_update) { ret = ESP_ERR_FLASH_NO_RESPONSE; } - - chip->chip_drv->set_chip_write_protect(chip, true); } return ret; }