esp-idf/components/spi_flash/esp32/spi_flash_rom_patch.c

694 wiersze
27 KiB
C
Czysty Zwykły widok Historia

// Copyright 2015-2016 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 "esp32/rom/spi_flash.h"
#include "soc/spi_periph.h"
#include "spi_flash_defs.h"
global: move the soc component out of the common list This MR removes the common dependency from every IDF components to the SOC component. Currently, in the ``idf_functions.cmake`` script, we include the header path of SOC component by default for all components. But for better code organization (or maybe also benifits to the compiling speed), we may remove the dependency to SOC components for most components except the driver and kernel related components. In CMAKE, we have two kinds of header visibilities (set by include path visibility): (Assume component A --(depends on)--> B, B is the current component) 1. public (``COMPONENT_ADD_INCLUDEDIRS``): means this path is visible to other depending components (A) (visible to A and B) 2. private (``COMPONENT_PRIV_INCLUDEDIRS``): means this path is only visible to source files inside the component (visible to B only) and we have two kinds of depending ways: (Assume component A --(depends on)--> B --(depends on)--> C, B is the current component) 1. public (```COMPONENT_REQUIRES```): means B can access to public include path of C. All other components rely on you (A) will also be available for the public headers. (visible to A, B) 2. private (``COMPONENT_PRIV_REQUIRES``): means B can access to public include path of C, but don't propagate this relation to other components (A). (visible to B) 1. remove the common requirement in ``idf_functions.cmake``, this makes the SOC components invisible to all other components by default. 2. if a component (for example, DRIVER) really needs the dependency to SOC, add a private dependency to SOC for it. 3. some other components that don't really depends on the SOC may still meet some errors saying "can't find header soc/...", this is because it's depended component (DRIVER) incorrectly include the header of SOC in its public headers. Moving all this kind of #include into source files, or private headers 4. Fix the include requirements for some file which miss sufficient #include directives. (Previously they include some headers by the long long long header include link) This is a breaking change. Previous code may depends on the long include chain. You may need to include the following headers for some files after this commit: - soc/soc.h - soc/soc_memory_layout.h - driver/gpio.h - esp_sleep.h The major broken include chain includes: 1. esp_system.h no longer includes esp_sleep.h. The latter includes driver/gpio.h and driver/touch_pad.h. 2. ets_sys.h no longer includes soc/soc.h 3. freertos/portmacro.h no longer includes soc/soc_memory_layout.h some peripheral headers no longer includes their hw related headers, e.g. rom/gpio.h no longer includes soc/gpio_pins.h and soc/gpio_reg.h BREAKING CHANGE
2019-04-03 05:17:38 +00:00
#define SPI_IDX 1
#define OTH_IDX 0
bootloader: fix the WRSR format for ISSI flash chips 1. The 2nd bootloader always call `rom_spiflash_unlock()`, but never help to clear the WEL bit when exit. This may cause system unstability. This commit helps to clear WEL when flash configuration is done. **RISK:** When the app starts, it didn't have to clear the WEL before it actually write/erase. But now the very first write/erase operation should be done after a WEL clear. Though the risk is little (all the following write/erase also need to clear the WEL), we still have to test this carefully, especially for those functions used by the OTA. 2. The `rom_spiflash_unlock()` function in the patch of ESP32 may (1) trigger the QPI, (2) clear the QE or (3) fail to unlock the ISSI chips. Status register bitmap of ISSI chip and GD chip: | SR | ISSI | GD25LQ32C | | -- | ---- | --------- | | 0 | WIP | WIP | | 1 | WEL | WEL | | 2 | BP0 | BP0 | | 3 | BP1 | BP1 | | 4 | BP2 | BP2 | | 5 | BP3 | BP3 | | 6 | QE | BP4 | | 7 | SRWD | SRP0 | | 8 | | SRP1 | | 9 | | QE | | 10 | | SUS2 | | 11 | | LB1 | | 12 | | LB2 | | 13 | | LB3 | | 14 | | CMP | | 15 | | SUS1 | QE bit of other chips are at the bit 9 of the status register (i.e. bit 1 of SR2), which should be read by RDSR2 command. However, the RDSR2 (35H, Read Status 2) command for chip of other vendors happens to be the QIOEN (Enter QPI mode) command of ISSI chips. When the `rom_spiflash_unlock()` function trys to read SR2, it may trigger the QPI of ISSI chips. Moreover, when `rom_spiflash_unlock()` try to clear the BP4 bit in the status register, QE (bit 6) of ISSI chip may be cleared by accident. Or if the ISSI chip doesn't accept WRSR command with argument of two bytes (since it only have status register of one byte), it may fail to clear the other protect bits (BP0~BP3) as expected. This commit makes the `rom_spiflash_unlock()` check whether the vendor is issi. if so, `rom_spiflash_unlock()` only send RDSR to read the status register, send WRSR with only 1 byte argument, and also avoid clearing the QE bit (bit 6). 3. `rom_spiflash_unlock()` always send WRSR command to clear protection bits even when there is no protection bit active. And the execution of clearing status registers, which takes about 700us, will also happen even when there's no bits cleared. This commit skips the clearing of status register if there is no protection bits active. Also move the execute_flash_command to be a bootloader API; move implementation of spi_flash_wrap_set to the bootloader
2020-03-12 10:20:31 +00:00
extern esp_rom_spiflash_chip_t g_rom_spiflash_chip;
bootloader: fix the WRSR format for ISSI flash chips 1. The 2nd bootloader always call `rom_spiflash_unlock()`, but never help to clear the WEL bit when exit. This may cause system unstability. This commit helps to clear WEL when flash configuration is done. **RISK:** When the app starts, it didn't have to clear the WEL before it actually write/erase. But now the very first write/erase operation should be done after a WEL clear. Though the risk is little (all the following write/erase also need to clear the WEL), we still have to test this carefully, especially for those functions used by the OTA. 2. The `rom_spiflash_unlock()` function in the patch of ESP32 may (1) trigger the QPI, (2) clear the QE or (3) fail to unlock the ISSI chips. Status register bitmap of ISSI chip and GD chip: | SR | ISSI | GD25LQ32C | | -- | ---- | --------- | | 0 | WIP | WIP | | 1 | WEL | WEL | | 2 | BP0 | BP0 | | 3 | BP1 | BP1 | | 4 | BP2 | BP2 | | 5 | BP3 | BP3 | | 6 | QE | BP4 | | 7 | SRWD | SRP0 | | 8 | | SRP1 | | 9 | | QE | | 10 | | SUS2 | | 11 | | LB1 | | 12 | | LB2 | | 13 | | LB3 | | 14 | | CMP | | 15 | | SUS1 | QE bit of other chips are at the bit 9 of the status register (i.e. bit 1 of SR2), which should be read by RDSR2 command. However, the RDSR2 (35H, Read Status 2) command for chip of other vendors happens to be the QIOEN (Enter QPI mode) command of ISSI chips. When the `rom_spiflash_unlock()` function trys to read SR2, it may trigger the QPI of ISSI chips. Moreover, when `rom_spiflash_unlock()` try to clear the BP4 bit in the status register, QE (bit 6) of ISSI chip may be cleared by accident. Or if the ISSI chip doesn't accept WRSR command with argument of two bytes (since it only have status register of one byte), it may fail to clear the other protect bits (BP0~BP3) as expected. This commit makes the `rom_spiflash_unlock()` check whether the vendor is issi. if so, `rom_spiflash_unlock()` only send RDSR to read the status register, send WRSR with only 1 byte argument, and also avoid clearing the QE bit (bit 6). 3. `rom_spiflash_unlock()` always send WRSR command to clear protection bits even when there is no protection bit active. And the execution of clearing status registers, which takes about 700us, will also happen even when there's no bits cleared. This commit skips the clearing of status register if there is no protection bits active. Also move the execute_flash_command to be a bootloader API; move implementation of spi_flash_wrap_set to the bootloader
2020-03-12 10:20:31 +00:00
static inline bool is_issi_chip(const esp_rom_spiflash_chip_t* chip)
{
return (((chip->device_id >> 16)&0xff) == 0x9D);
}
esp_rom_spiflash_result_t esp_rom_spiflash_wait_idle(esp_rom_spiflash_chip_t *spi)
{
uint32_t status;
#if CONFIG_IDF_TARGET_ESP32
//wait for spi control ready
while ((REG_READ(SPI_EXT2_REG(1)) & SPI_ST)) {
}
while ((REG_READ(SPI_EXT2_REG(0)) & SPI_ST)) {
}
2020-01-17 03:47:08 +00:00
#elif CONFIG_IDF_TARGET_ESP32S2
while ((REG_READ(SPI_MEM_FSM_REG(1)) & SPI_MEM_ST)) {
}
while ((REG_READ(SPI_MEM_FSM_REG(0)) & SPI_MEM_ST)) {
}
#endif
//wait for flash status ready
if ( ESP_ROM_SPIFLASH_RESULT_OK != esp_rom_spiflash_read_status(spi, &status)) {
return ESP_ROM_SPIFLASH_RESULT_ERR;
}
return ESP_ROM_SPIFLASH_RESULT_OK;
}
/* Modified version of esp_rom_spiflash_unlock() that replaces version in ROM.
This works around a bug where esp_rom_spiflash_unlock sometimes reads the wrong
high status byte (RDSR2 result) and then copies it back to the
flash status, which can cause the CMP bit or Status Register
Protect bit to become set.
Like other ROM SPI functions, this function is not designed to be
called directly from an RTOS environment without taking precautions
about interrupts, CPU coordination, flash mapping. However some of
the functions in esp_spi_flash.c call it.
*/
__attribute__((__unused__)) esp_rom_spiflash_result_t esp_rom_spiflash_unlock(void)
{
uint32_t status;
bootloader: fix the WRSR format for ISSI flash chips 1. The 2nd bootloader always call `rom_spiflash_unlock()`, but never help to clear the WEL bit when exit. This may cause system unstability. This commit helps to clear WEL when flash configuration is done. **RISK:** When the app starts, it didn't have to clear the WEL before it actually write/erase. But now the very first write/erase operation should be done after a WEL clear. Though the risk is little (all the following write/erase also need to clear the WEL), we still have to test this carefully, especially for those functions used by the OTA. 2. The `rom_spiflash_unlock()` function in the patch of ESP32 may (1) trigger the QPI, (2) clear the QE or (3) fail to unlock the ISSI chips. Status register bitmap of ISSI chip and GD chip: | SR | ISSI | GD25LQ32C | | -- | ---- | --------- | | 0 | WIP | WIP | | 1 | WEL | WEL | | 2 | BP0 | BP0 | | 3 | BP1 | BP1 | | 4 | BP2 | BP2 | | 5 | BP3 | BP3 | | 6 | QE | BP4 | | 7 | SRWD | SRP0 | | 8 | | SRP1 | | 9 | | QE | | 10 | | SUS2 | | 11 | | LB1 | | 12 | | LB2 | | 13 | | LB3 | | 14 | | CMP | | 15 | | SUS1 | QE bit of other chips are at the bit 9 of the status register (i.e. bit 1 of SR2), which should be read by RDSR2 command. However, the RDSR2 (35H, Read Status 2) command for chip of other vendors happens to be the QIOEN (Enter QPI mode) command of ISSI chips. When the `rom_spiflash_unlock()` function trys to read SR2, it may trigger the QPI of ISSI chips. Moreover, when `rom_spiflash_unlock()` try to clear the BP4 bit in the status register, QE (bit 6) of ISSI chip may be cleared by accident. Or if the ISSI chip doesn't accept WRSR command with argument of two bytes (since it only have status register of one byte), it may fail to clear the other protect bits (BP0~BP3) as expected. This commit makes the `rom_spiflash_unlock()` check whether the vendor is issi. if so, `rom_spiflash_unlock()` only send RDSR to read the status register, send WRSR with only 1 byte argument, and also avoid clearing the QE bit (bit 6). 3. `rom_spiflash_unlock()` always send WRSR command to clear protection bits even when there is no protection bit active. And the execution of clearing status registers, which takes about 700us, will also happen even when there's no bits cleared. This commit skips the clearing of status register if there is no protection bits active. Also move the execute_flash_command to be a bootloader API; move implementation of spi_flash_wrap_set to the bootloader
2020-03-12 10:20:31 +00:00
uint32_t new_status;
esp_rom_spiflash_wait_idle(&g_rom_spiflash_chip);
bootloader: fix the WRSR format for ISSI flash chips 1. The 2nd bootloader always call `rom_spiflash_unlock()`, but never help to clear the WEL bit when exit. This may cause system unstability. This commit helps to clear WEL when flash configuration is done. **RISK:** When the app starts, it didn't have to clear the WEL before it actually write/erase. But now the very first write/erase operation should be done after a WEL clear. Though the risk is little (all the following write/erase also need to clear the WEL), we still have to test this carefully, especially for those functions used by the OTA. 2. The `rom_spiflash_unlock()` function in the patch of ESP32 may (1) trigger the QPI, (2) clear the QE or (3) fail to unlock the ISSI chips. Status register bitmap of ISSI chip and GD chip: | SR | ISSI | GD25LQ32C | | -- | ---- | --------- | | 0 | WIP | WIP | | 1 | WEL | WEL | | 2 | BP0 | BP0 | | 3 | BP1 | BP1 | | 4 | BP2 | BP2 | | 5 | BP3 | BP3 | | 6 | QE | BP4 | | 7 | SRWD | SRP0 | | 8 | | SRP1 | | 9 | | QE | | 10 | | SUS2 | | 11 | | LB1 | | 12 | | LB2 | | 13 | | LB3 | | 14 | | CMP | | 15 | | SUS1 | QE bit of other chips are at the bit 9 of the status register (i.e. bit 1 of SR2), which should be read by RDSR2 command. However, the RDSR2 (35H, Read Status 2) command for chip of other vendors happens to be the QIOEN (Enter QPI mode) command of ISSI chips. When the `rom_spiflash_unlock()` function trys to read SR2, it may trigger the QPI of ISSI chips. Moreover, when `rom_spiflash_unlock()` try to clear the BP4 bit in the status register, QE (bit 6) of ISSI chip may be cleared by accident. Or if the ISSI chip doesn't accept WRSR command with argument of two bytes (since it only have status register of one byte), it may fail to clear the other protect bits (BP0~BP3) as expected. This commit makes the `rom_spiflash_unlock()` check whether the vendor is issi. if so, `rom_spiflash_unlock()` only send RDSR to read the status register, send WRSR with only 1 byte argument, and also avoid clearing the QE bit (bit 6). 3. `rom_spiflash_unlock()` always send WRSR command to clear protection bits even when there is no protection bit active. And the execution of clearing status registers, which takes about 700us, will also happen even when there's no bits cleared. This commit skips the clearing of status register if there is no protection bits active. Also move the execute_flash_command to be a bootloader API; move implementation of spi_flash_wrap_set to the bootloader
2020-03-12 10:20:31 +00:00
if (is_issi_chip(&g_rom_spiflash_chip)) {
// ISSI chips have different QE position
if (esp_rom_spiflash_read_status(&g_rom_spiflash_chip, &status) != ESP_ROM_SPIFLASH_RESULT_OK) {
return ESP_ROM_SPIFLASH_RESULT_ERR;
}
/* Clear all bits in the mask.
(This is different from ROM esp_rom_spiflash_unlock, which keeps all bits as-is.)
*/
new_status = status & (~ESP_ROM_SPIFLASH_BP_MASK_ISSI);
// Skip if nothing needs to be cleared. Otherwise will waste time waiting for the flash to clear nothing.
if (new_status == status) return ESP_ROM_SPIFLASH_RESULT_OK;
CLEAR_PERI_REG_MASK(SPI_CTRL_REG(SPI_IDX), SPI_WRSR_2B);
} else {
if (esp_rom_spiflash_read_statushigh(&g_rom_spiflash_chip, &status) != ESP_ROM_SPIFLASH_RESULT_OK) {
return ESP_ROM_SPIFLASH_RESULT_ERR;
}
/* Clear all bits except QE, if it is set.
(This is different from ROM esp_rom_spiflash_unlock, which keeps all bits as-is.)
*/
new_status = status & ESP_ROM_SPIFLASH_QE;
SET_PERI_REG_MASK(SPI_CTRL_REG(SPI_IDX), SPI_WRSR_2B);
}
esp_rom_spiflash_wait_idle(&g_rom_spiflash_chip);
REG_WRITE(SPI_CMD_REG(SPI_IDX), SPI_FLASH_WREN);
while (REG_READ(SPI_CMD_REG(SPI_IDX)) != 0) {
}
esp_rom_spiflash_wait_idle(&g_rom_spiflash_chip);
2020-09-21 04:49:30 +00:00
esp_rom_spiflash_result_t ret = esp_rom_spiflash_write_status(&g_rom_spiflash_chip, new_status);
// WEL bit should be cleared after operations regardless of writing succeed or not.
esp_rom_spiflash_wait_idle(&g_rom_spiflash_chip);
REG_WRITE(SPI_CMD_REG(SPI_IDX), SPI_FLASH_WRDI);
while (REG_READ(SPI_CMD_REG(SPI_IDX)) != 0) {
}
2020-09-21 04:49:30 +00:00
return ret;
}
#if CONFIG_SPI_FLASH_ROM_DRIVER_PATCH
extern uint8_t g_rom_spiflash_dummy_len_plus[];
static esp_rom_spiflash_result_t esp_rom_spiflash_enable_write(esp_rom_spiflash_chip_t *spi);
//only support spi1
static esp_rom_spiflash_result_t esp_rom_spiflash_erase_chip_internal(esp_rom_spiflash_chip_t *spi)
{
esp_rom_spiflash_wait_idle(spi);
// Chip erase.
WRITE_PERI_REG(PERIPHS_SPI_FLASH_CMD, SPI_FLASH_CE);
while (READ_PERI_REG(PERIPHS_SPI_FLASH_CMD) != 0);
// check erase is finished.
esp_rom_spiflash_wait_idle(spi);
return ESP_ROM_SPIFLASH_RESULT_OK;
}
//only support spi1
static esp_rom_spiflash_result_t esp_rom_spiflash_erase_sector_internal(esp_rom_spiflash_chip_t *spi, uint32_t addr)
{
//check if addr is 4k alignment
if (0 != (addr & 0xfff)) {
return ESP_ROM_SPIFLASH_RESULT_ERR;
}
esp_rom_spiflash_wait_idle(spi);
// sector erase 4Kbytes erase is sector erase.
WRITE_PERI_REG(PERIPHS_SPI_FLASH_ADDR, addr & 0xffffff);
WRITE_PERI_REG(PERIPHS_SPI_FLASH_CMD, SPI_FLASH_SE);
while (READ_PERI_REG(PERIPHS_SPI_FLASH_CMD) != 0);
esp_rom_spiflash_wait_idle(spi);
return ESP_ROM_SPIFLASH_RESULT_OK;
}
//only support spi1
static esp_rom_spiflash_result_t esp_rom_spiflash_erase_block_internal(esp_rom_spiflash_chip_t *spi, uint32_t addr)
{
esp_rom_spiflash_wait_idle(spi);
// sector erase 4Kbytes erase is sector erase.
WRITE_PERI_REG(PERIPHS_SPI_FLASH_ADDR, addr & 0xffffff);
WRITE_PERI_REG(PERIPHS_SPI_FLASH_CMD, SPI_FLASH_BE);
while (READ_PERI_REG(PERIPHS_SPI_FLASH_CMD) != 0);
esp_rom_spiflash_wait_idle(spi);
return ESP_ROM_SPIFLASH_RESULT_OK;
}
//only support spi1
static esp_rom_spiflash_result_t esp_rom_spiflash_program_page_internal(esp_rom_spiflash_chip_t *spi, uint32_t spi_addr,
uint32_t *addr_source, int32_t byte_length)
{
uint32_t temp_addr;
int32_t temp_bl;
uint8_t i;
uint8_t remain_word_num;
//check 4byte alignment
if (0 != (byte_length & 0x3)) {
return ESP_ROM_SPIFLASH_RESULT_ERR;
}
//check if write in one page
if ((spi->page_size) < ((spi_addr % (spi->page_size)) + byte_length)) {
return ESP_ROM_SPIFLASH_RESULT_ERR;
}
esp_rom_spiflash_wait_idle(spi);
temp_addr = spi_addr;
temp_bl = byte_length;
while (temp_bl > 0 ) {
if ( ESP_ROM_SPIFLASH_RESULT_OK != esp_rom_spiflash_enable_write(spi)) {
return ESP_ROM_SPIFLASH_RESULT_ERR;
}
if ( temp_bl >= ESP_ROM_SPIFLASH_BUFF_BYTE_WRITE_NUM ) {
WRITE_PERI_REG(PERIPHS_SPI_FLASH_ADDR, (temp_addr & 0xffffff) | ( ESP_ROM_SPIFLASH_BUFF_BYTE_WRITE_NUM << ESP_ROM_SPIFLASH_BYTES_LEN )); // 32 byte a block
for (i = 0; i < (ESP_ROM_SPIFLASH_BUFF_BYTE_WRITE_NUM >> 2); i++) {
WRITE_PERI_REG(PERIPHS_SPI_FLASH_C0 + i * 4, *addr_source++);
}
temp_bl = temp_bl - 32;
temp_addr = temp_addr + 32;
} else {
WRITE_PERI_REG(PERIPHS_SPI_FLASH_ADDR, (temp_addr & 0xffffff) | (temp_bl << ESP_ROM_SPIFLASH_BYTES_LEN ));
remain_word_num = (0 == (temp_bl & 0x3)) ? (temp_bl >> 2) : (temp_bl >> 2) + 1;
for (i = 0; i < remain_word_num; i++) {
WRITE_PERI_REG(PERIPHS_SPI_FLASH_C0 + i * 4, *addr_source++);
temp_bl = temp_bl - 4;
}
temp_bl = 0;
}
WRITE_PERI_REG(PERIPHS_SPI_FLASH_CMD, SPI_FLASH_PP);
while ( READ_PERI_REG(PERIPHS_SPI_FLASH_CMD ) != 0 );
esp_rom_spiflash_wait_idle(spi);
}
return ESP_ROM_SPIFLASH_RESULT_OK;
}
esp_rom_spiflash_result_t esp_rom_spiflash_read_status(esp_rom_spiflash_chip_t *spi, uint32_t *status)
{
uint32_t status_value = ESP_ROM_SPIFLASH_BUSY_FLAG;
if (g_rom_spiflash_dummy_len_plus[1] == 0) {
while (ESP_ROM_SPIFLASH_BUSY_FLAG == (status_value & ESP_ROM_SPIFLASH_BUSY_FLAG)) {
WRITE_PERI_REG(PERIPHS_SPI_FLASH_STATUS, 0); // clear regisrter
WRITE_PERI_REG(PERIPHS_SPI_FLASH_CMD, SPI_FLASH_RDSR);
while (READ_PERI_REG(PERIPHS_SPI_FLASH_CMD) != 0);
status_value = READ_PERI_REG(PERIPHS_SPI_FLASH_STATUS) & (spi->status_mask);
}
} else {
while (ESP_ROM_SPIFLASH_BUSY_FLAG == (status_value & ESP_ROM_SPIFLASH_BUSY_FLAG)) {
esp_rom_spiflash_read_user_cmd(&status_value, 0x05);
}
}
*status = status_value;
return ESP_ROM_SPIFLASH_RESULT_OK;
}
esp_rom_spiflash_result_t esp_rom_spiflash_read_statushigh(esp_rom_spiflash_chip_t *spi, uint32_t *status)
{
esp_rom_spiflash_result_t ret;
esp_rom_spiflash_wait_idle(&g_rom_spiflash_chip);
ret = esp_rom_spiflash_read_user_cmd(status, 0x35);
*status = *status << 8;
return ret;
}
esp_rom_spiflash_result_t esp_rom_spiflash_write_status(esp_rom_spiflash_chip_t *spi, uint32_t status_value)
{
esp_rom_spiflash_wait_idle(spi);
// update status value by status_value
WRITE_PERI_REG(PERIPHS_SPI_FLASH_STATUS, status_value); // write status regisrter
WRITE_PERI_REG(PERIPHS_SPI_FLASH_CMD, SPI_FLASH_WRSR);
while (READ_PERI_REG(PERIPHS_SPI_FLASH_CMD) != 0);
esp_rom_spiflash_wait_idle(spi);
return ESP_ROM_SPIFLASH_RESULT_OK;
}
//only support spi1
static esp_rom_spiflash_result_t esp_rom_spiflash_read_data(esp_rom_spiflash_chip_t *spi, uint32_t flash_addr,
uint32_t *addr_dest, int32_t byte_length)
{
uint32_t temp_addr;
int32_t temp_length;
uint8_t i;
uint8_t remain_word_num;
//address range check
if ((flash_addr + byte_length) > (spi->chip_size)) {
return ESP_ROM_SPIFLASH_RESULT_ERR;
}
temp_addr = flash_addr;
temp_length = byte_length;
esp_rom_spiflash_wait_idle(spi);
while (temp_length > 0) {
if (temp_length >= ESP_ROM_SPIFLASH_BUFF_BYTE_READ_NUM) {
//WRITE_PERI_REG(PERIPHS_SPI_FLASH_ADDR, temp_addr |(ESP_ROM_SPIFLASH_BUFF_BYTE_READ_NUM << ESP_ROM_SPIFLASH_BYTES_LEN));
REG_WRITE(SPI_MISO_DLEN_REG(1), ((ESP_ROM_SPIFLASH_BUFF_BYTE_READ_NUM << 3) - 1) << SPI_USR_MISO_DBITLEN_S);
WRITE_PERI_REG(PERIPHS_SPI_FLASH_ADDR, temp_addr << 8);
REG_WRITE(PERIPHS_SPI_FLASH_CMD, SPI_USR);
while (REG_READ(PERIPHS_SPI_FLASH_CMD) != 0);
for (i = 0; i < (ESP_ROM_SPIFLASH_BUFF_BYTE_READ_NUM >> 2); i++) {
*addr_dest++ = READ_PERI_REG(PERIPHS_SPI_FLASH_C0 + i * 4);
}
temp_length = temp_length - ESP_ROM_SPIFLASH_BUFF_BYTE_READ_NUM;
temp_addr = temp_addr + ESP_ROM_SPIFLASH_BUFF_BYTE_READ_NUM;
} else {
//WRITE_PERI_REG(PERIPHS_SPI_FLASH_ADDR, temp_addr |(temp_length << ESP_ROM_SPIFLASH_BYTES_LEN ));
WRITE_PERI_REG(PERIPHS_SPI_FLASH_ADDR, temp_addr << 8);
REG_WRITE(SPI_MISO_DLEN_REG(1), ((ESP_ROM_SPIFLASH_BUFF_BYTE_READ_NUM << 3) - 1) << SPI_USR_MISO_DBITLEN_S);
REG_WRITE(PERIPHS_SPI_FLASH_CMD, SPI_USR);
while (REG_READ(PERIPHS_SPI_FLASH_CMD) != 0);
remain_word_num = (0 == (temp_length & 0x3)) ? (temp_length >> 2) : (temp_length >> 2) + 1;
for (i = 0; i < remain_word_num; i++) {
*addr_dest++ = READ_PERI_REG(PERIPHS_SPI_FLASH_C0 + i * 4);
}
temp_length = 0;
}
}
return ESP_ROM_SPIFLASH_RESULT_OK;
}
static esp_rom_spiflash_result_t esp_rom_spiflash_enable_write(esp_rom_spiflash_chip_t *spi)
{
uint32_t flash_status = 0;
esp_rom_spiflash_wait_idle(spi);
//enable write
WRITE_PERI_REG(PERIPHS_SPI_FLASH_CMD, SPI_FLASH_WREN); // enable write operation
while (READ_PERI_REG(PERIPHS_SPI_FLASH_CMD) != 0);
// make sure the flash is ready for writing
while (ESP_ROM_SPIFLASH_WRENABLE_FLAG != (flash_status & ESP_ROM_SPIFLASH_WRENABLE_FLAG)) {
esp_rom_spiflash_read_status(spi, &flash_status);
}
return ESP_ROM_SPIFLASH_RESULT_OK;
}
static void spi_cache_mode_switch(uint32_t modebit)
{
if ((modebit & SPI_FREAD_QIO) && (modebit & SPI_FASTRD_MODE)) {
REG_CLR_BIT(SPI_USER_REG(0), SPI_USR_MOSI);
REG_SET_BIT(SPI_USER_REG(0), SPI_USR_MISO | SPI_USR_DUMMY | SPI_USR_ADDR);
REG_SET_FIELD(SPI_USER1_REG(0), SPI_USR_ADDR_BITLEN, SPI0_R_QIO_ADDR_BITSLEN);
REG_SET_FIELD(SPI_USER1_REG(0), SPI_USR_DUMMY_CYCLELEN, SPI0_R_QIO_DUMMY_CYCLELEN + g_rom_spiflash_dummy_len_plus[0]);
REG_SET_FIELD(SPI_USER2_REG(0), SPI_USR_COMMAND_VALUE, 0xEB);
} else if (modebit & SPI_FASTRD_MODE) {
REG_CLR_BIT(SPI_USER_REG(0), SPI_USR_MOSI);
REG_SET_BIT(SPI_USER_REG(0), SPI_USR_MISO | SPI_USR_DUMMY | SPI_USR_ADDR);
REG_SET_FIELD(SPI_USER1_REG(0), SPI_USR_ADDR_BITLEN, SPI0_R_FAST_ADDR_BITSLEN);
if ((modebit & SPI_FREAD_QUAD)) {
REG_SET_FIELD(SPI_USER2_REG(0), SPI_USR_COMMAND_VALUE, 0x6B);
REG_SET_FIELD(SPI_USER1_REG(0), SPI_USR_DUMMY_CYCLELEN, SPI0_R_FAST_DUMMY_CYCLELEN + g_rom_spiflash_dummy_len_plus[0]);
} else if ((modebit & SPI_FREAD_DIO)) {
REG_SET_FIELD(SPI_USER1_REG(0), SPI_USR_ADDR_BITLEN, SPI0_R_DIO_ADDR_BITSLEN);
REG_SET_FIELD(SPI_USER1_REG(0), SPI_USR_DUMMY_CYCLELEN, SPI0_R_DIO_DUMMY_CYCLELEN + g_rom_spiflash_dummy_len_plus[0]);
REG_SET_FIELD(SPI_USER2_REG(0), SPI_USR_COMMAND_VALUE, 0xBB);
} else if ((modebit & SPI_FREAD_DUAL)) {
REG_SET_FIELD(SPI_USER1_REG(0), SPI_USR_DUMMY_CYCLELEN, SPI0_R_FAST_DUMMY_CYCLELEN + g_rom_spiflash_dummy_len_plus[0]);
REG_SET_FIELD(SPI_USER2_REG(0), SPI_USR_COMMAND_VALUE, 0x3B);
} else {
REG_SET_FIELD(SPI_USER1_REG(0), SPI_USR_DUMMY_CYCLELEN, SPI0_R_FAST_DUMMY_CYCLELEN + g_rom_spiflash_dummy_len_plus[0]);
REG_SET_FIELD(SPI_USER2_REG(0), SPI_USR_COMMAND_VALUE, 0x0B);
}
} else {
REG_CLR_BIT(SPI_USER_REG(0), SPI_USR_MOSI);
if (g_rom_spiflash_dummy_len_plus[0] == 0) {
REG_CLR_BIT(SPI_USER_REG(0), SPI_USR_DUMMY);
} else {
REG_SET_BIT(SPI_USER_REG(0), SPI_USR_DUMMY);
REG_SET_FIELD(SPI_USER1_REG(0), SPI_USR_DUMMY_CYCLELEN, g_rom_spiflash_dummy_len_plus[0] - 1);
}
REG_SET_BIT(SPI_USER_REG(0), SPI_USR_MISO | SPI_USR_ADDR);
REG_SET_FIELD(SPI_USER1_REG(0), SPI_USR_ADDR_BITLEN, SPI0_R_SIO_ADDR_BITSLEN);
REG_SET_FIELD(SPI_USER2_REG(0), SPI_USR_COMMAND_VALUE, 0x03);
}
}
esp_rom_spiflash_result_t esp_rom_spiflash_lock(void)
{
uint32_t status;
//read QE bit, not write if not QE
if (ESP_ROM_SPIFLASH_RESULT_OK != esp_rom_spiflash_read_statushigh(&g_rom_spiflash_chip, &status)) {
return ESP_ROM_SPIFLASH_RESULT_ERR;
}
//enable 2 byte status writing
SET_PERI_REG_MASK(PERIPHS_SPI_FLASH_CTRL, ESP_ROM_SPIFLASH_TWO_BYTE_STATUS_EN);
if ( ESP_ROM_SPIFLASH_RESULT_OK != esp_rom_spiflash_enable_write(&g_rom_spiflash_chip)) {
return ESP_ROM_SPIFLASH_RESULT_ERR;
}
if ( ESP_ROM_SPIFLASH_RESULT_OK != esp_rom_spiflash_write_status(&g_rom_spiflash_chip, status | ESP_ROM_SPIFLASH_WR_PROTECT)) {
return ESP_ROM_SPIFLASH_RESULT_ERR;
}
return ESP_ROM_SPIFLASH_RESULT_OK;
}
esp_rom_spiflash_result_t esp_rom_spiflash_config_readmode(esp_rom_spiflash_read_mode_t mode)
{
uint32_t modebit;
while ((REG_READ(SPI_EXT2_REG(1)) & SPI_ST)) {
}
while ((REG_READ(SPI_EXT2_REG(0)) & SPI_ST)) {
}
//clear old mode bit
CLEAR_PERI_REG_MASK(PERIPHS_SPI_FLASH_CTRL, SPI_FREAD_QIO | SPI_FREAD_QUAD | SPI_FREAD_DIO | SPI_FREAD_DUAL | SPI_FASTRD_MODE);
CLEAR_PERI_REG_MASK(SPI_CTRL_REG(0), SPI_FREAD_QIO | SPI_FREAD_QUAD | SPI_FREAD_DIO | SPI_FREAD_DUAL | SPI_FASTRD_MODE);
//configure read mode
switch (mode) {
case ESP_ROM_SPIFLASH_QIO_MODE : modebit = SPI_FREAD_QIO | SPI_FASTRD_MODE; break;
case ESP_ROM_SPIFLASH_QOUT_MODE : modebit = SPI_FREAD_QUAD | SPI_FASTRD_MODE; break;
case ESP_ROM_SPIFLASH_DIO_MODE : modebit = SPI_FREAD_DIO | SPI_FASTRD_MODE; break;
case ESP_ROM_SPIFLASH_DOUT_MODE : modebit = SPI_FREAD_DUAL | SPI_FASTRD_MODE; break;
case ESP_ROM_SPIFLASH_FASTRD_MODE: modebit = SPI_FASTRD_MODE; break;
case ESP_ROM_SPIFLASH_SLOWRD_MODE: modebit = 0; break;
default : modebit = 0;
}
SET_PERI_REG_MASK(PERIPHS_SPI_FLASH_CTRL, modebit);
SET_PERI_REG_MASK(SPI_CTRL_REG(0), modebit);
spi_cache_mode_switch(modebit);
return ESP_ROM_SPIFLASH_RESULT_OK;
}
esp_rom_spiflash_result_t esp_rom_spiflash_erase_chip(void)
{
if ( ESP_ROM_SPIFLASH_RESULT_OK != esp_rom_spiflash_enable_write(&g_rom_spiflash_chip)) {
return ESP_ROM_SPIFLASH_RESULT_ERR;
}
if (ESP_ROM_SPIFLASH_RESULT_OK != esp_rom_spiflash_erase_chip_internal(&g_rom_spiflash_chip)) {
return ESP_ROM_SPIFLASH_RESULT_ERR;
}
return ESP_ROM_SPIFLASH_RESULT_OK;
}
esp_rom_spiflash_result_t esp_rom_spiflash_erase_block(uint32_t block_num)
{
// flash write is always 1 line currently
REG_CLR_BIT(PERIPHS_SPI_FLASH_USRREG, SPI_USR_DUMMY);
REG_SET_FIELD(PERIPHS_SPI_FLASH_USRREG1, SPI_USR_ADDR_BITLEN, ESP_ROM_SPIFLASH_W_SIO_ADDR_BITSLEN);
//check program size
if (block_num >= ((g_rom_spiflash_chip.chip_size) / (g_rom_spiflash_chip.block_size))) {
return ESP_ROM_SPIFLASH_RESULT_ERR;
}
if ( ESP_ROM_SPIFLASH_RESULT_OK != esp_rom_spiflash_enable_write(&g_rom_spiflash_chip)) {
return ESP_ROM_SPIFLASH_RESULT_ERR;
}
if (ESP_ROM_SPIFLASH_RESULT_OK != esp_rom_spiflash_erase_block_internal(&g_rom_spiflash_chip, block_num * (g_rom_spiflash_chip.block_size))) {
return ESP_ROM_SPIFLASH_RESULT_ERR;
}
return ESP_ROM_SPIFLASH_RESULT_OK;
}
esp_rom_spiflash_result_t esp_rom_spiflash_erase_sector(uint32_t sector_num)
{
// flash write is always 1 line currently
REG_CLR_BIT(PERIPHS_SPI_FLASH_USRREG, SPI_USR_DUMMY);
REG_SET_FIELD(PERIPHS_SPI_FLASH_USRREG1, SPI_USR_ADDR_BITLEN, ESP_ROM_SPIFLASH_W_SIO_ADDR_BITSLEN);
//check program size
if (sector_num >= ((g_rom_spiflash_chip.chip_size) / (g_rom_spiflash_chip.sector_size))) {
return ESP_ROM_SPIFLASH_RESULT_ERR;
}
if ( ESP_ROM_SPIFLASH_RESULT_OK != esp_rom_spiflash_enable_write(&g_rom_spiflash_chip)) {
return ESP_ROM_SPIFLASH_RESULT_ERR;
}
if (ESP_ROM_SPIFLASH_RESULT_OK != esp_rom_spiflash_erase_sector_internal(&g_rom_spiflash_chip, sector_num * (g_rom_spiflash_chip.sector_size))) {
return ESP_ROM_SPIFLASH_RESULT_ERR;
}
return ESP_ROM_SPIFLASH_RESULT_OK;
}
esp_rom_spiflash_result_t esp_rom_spiflash_write(uint32_t target, const uint32_t *src_addr, int32_t len)
{
uint32_t page_size;
uint32_t pgm_len, pgm_num;
uint8_t i;
// flash write is always 1 line currently
REG_CLR_BIT(PERIPHS_SPI_FLASH_USRREG, SPI_USR_DUMMY);
REG_SET_FIELD(PERIPHS_SPI_FLASH_USRREG1, SPI_USR_ADDR_BITLEN, ESP_ROM_SPIFLASH_W_SIO_ADDR_BITSLEN);
//check program size
if ( (target + len) > (g_rom_spiflash_chip.chip_size)) {
return ESP_ROM_SPIFLASH_RESULT_ERR;
}
page_size = g_rom_spiflash_chip.page_size;
pgm_len = page_size - (target % page_size);
if (len < pgm_len) {
if (ESP_ROM_SPIFLASH_RESULT_OK != esp_rom_spiflash_program_page_internal(&g_rom_spiflash_chip,
target, (uint32_t *)src_addr, len)) {
return ESP_ROM_SPIFLASH_RESULT_ERR;
}
} else {
if (ESP_ROM_SPIFLASH_RESULT_OK != esp_rom_spiflash_program_page_internal(&g_rom_spiflash_chip,
target, (uint32_t *)src_addr, pgm_len)) {
return ESP_ROM_SPIFLASH_RESULT_ERR;
}
//whole page program
pgm_num = (len - pgm_len) / page_size;
for (i = 0; i < pgm_num; i++) {
if (ESP_ROM_SPIFLASH_RESULT_OK != esp_rom_spiflash_program_page_internal(&g_rom_spiflash_chip,
target + pgm_len, (uint32_t *)src_addr + (pgm_len >> 2), page_size)) {
return ESP_ROM_SPIFLASH_RESULT_ERR;
}
pgm_len += page_size;
}
//remain parts to program
if (ESP_ROM_SPIFLASH_RESULT_OK != esp_rom_spiflash_program_page_internal(&g_rom_spiflash_chip,
target + pgm_len, (uint32_t *)src_addr + (pgm_len >> 2), len - pgm_len)) {
return ESP_ROM_SPIFLASH_RESULT_ERR;
}
}
return ESP_ROM_SPIFLASH_RESULT_OK;
}
esp_rom_spiflash_result_t esp_rom_spiflash_write_encrypted(uint32_t flash_addr, uint32_t *data, uint32_t len)
{
esp_rom_spiflash_result_t ret = ESP_ROM_SPIFLASH_RESULT_OK;
uint32_t i;
if ((flash_addr & 0x1f) || (len & 0x1f)) { //check 32 byte alignment
return ESP_ROM_SPIFLASH_RESULT_ERR;
}
esp_rom_spiflash_write_encrypted_enable();
for (i = 0; i < (len >> 5); i++) {
if ((ret = esp_rom_spiflash_prepare_encrypted_data(flash_addr + (i << 5), data + (i << 3))) != ESP_ROM_SPIFLASH_RESULT_OK) {
break;
}
if ((ret = esp_rom_spiflash_write(flash_addr + (i << 5), data, 32)) != ESP_ROM_SPIFLASH_RESULT_OK) {
break;
}
}
esp_rom_spiflash_write_encrypted_disable();
return ret;
}
esp_rom_spiflash_result_t esp_rom_spiflash_read(uint32_t target, uint32_t *dest_addr, int32_t len)
{
// QIO or SIO, non-QIO regard as SIO
uint32_t modebit;
modebit = READ_PERI_REG(PERIPHS_SPI_FLASH_CTRL);
if ((modebit & SPI_FREAD_QIO) && (modebit & SPI_FASTRD_MODE)) {
REG_CLR_BIT(PERIPHS_SPI_FLASH_USRREG, SPI_USR_MOSI);
REG_SET_BIT(PERIPHS_SPI_FLASH_USRREG, SPI_USR_MISO | SPI_USR_DUMMY | SPI_USR_ADDR);
REG_SET_FIELD(PERIPHS_SPI_FLASH_USRREG1, SPI_USR_ADDR_BITLEN, SPI1_R_QIO_ADDR_BITSLEN);
REG_SET_FIELD(PERIPHS_SPI_FLASH_USRREG1, SPI_USR_DUMMY_CYCLELEN, SPI1_R_QIO_DUMMY_CYCLELEN + g_rom_spiflash_dummy_len_plus[1]);
//REG_SET_FIELD(PERIPHS_SPI_FLASH_USRREG2, SPI_USR_COMMAND_VALUE, 0xEB);
REG_WRITE(PERIPHS_SPI_FLASH_USRREG2, (0x7 << SPI_USR_COMMAND_BITLEN_S) | 0xEB);
} else if (modebit & SPI_FASTRD_MODE) {
REG_CLR_BIT(PERIPHS_SPI_FLASH_USRREG, SPI_USR_MOSI);
REG_SET_BIT(PERIPHS_SPI_FLASH_USRREG, SPI_USR_MISO | SPI_USR_ADDR);
if (modebit & SPI_FREAD_DIO) {
if (g_rom_spiflash_dummy_len_plus[1] == 0) {
REG_CLR_BIT(PERIPHS_SPI_FLASH_USRREG, SPI_USR_DUMMY);
REG_SET_FIELD(PERIPHS_SPI_FLASH_USRREG1, SPI_USR_ADDR_BITLEN, SPI1_R_DIO_ADDR_BITSLEN);
REG_WRITE(PERIPHS_SPI_FLASH_USRREG2, (0x7 << SPI_USR_COMMAND_BITLEN_S) | 0xBB);
} else {
REG_SET_BIT(PERIPHS_SPI_FLASH_USRREG, SPI_USR_DUMMY);
REG_SET_FIELD(PERIPHS_SPI_FLASH_USRREG1, SPI_USR_ADDR_BITLEN, SPI1_R_DIO_ADDR_BITSLEN);
REG_SET_FIELD(PERIPHS_SPI_FLASH_USRREG1, SPI_USR_DUMMY_CYCLELEN, g_rom_spiflash_dummy_len_plus[1] - 1);
REG_SET_FIELD(PERIPHS_SPI_FLASH_USRREG2, SPI_USR_COMMAND_VALUE, 0xBB);
}
} else {
if ((modebit & SPI_FREAD_QUAD)) {
//REG_SET_FIELD(PERIPHS_SPI_FLASH_USRREG2, SPI_USR_COMMAND_VALUE, 0x6B);
REG_WRITE(PERIPHS_SPI_FLASH_USRREG2, (0x7 << SPI_USR_COMMAND_BITLEN_S) | 0x6B);
} else if ((modebit & SPI_FREAD_DUAL)) {
//REG_SET_FIELD(PERIPHS_SPI_FLASH_USRREG2, SPI_USR_COMMAND_VALUE, 0x3B);
REG_WRITE(PERIPHS_SPI_FLASH_USRREG2, (0x7 << SPI_USR_COMMAND_BITLEN_S) | 0x3B);
} else {
//REG_SET_FIELD(PERIPHS_SPI_FLASH_USRREG2, SPI_USR_COMMAND_VALUE, 0x0B);
REG_WRITE(PERIPHS_SPI_FLASH_USRREG2, (0x7 << SPI_USR_COMMAND_BITLEN_S) | 0x0B);
}
REG_SET_BIT(PERIPHS_SPI_FLASH_USRREG, SPI_USR_DUMMY);
REG_SET_FIELD(PERIPHS_SPI_FLASH_USRREG1, SPI_USR_ADDR_BITLEN, SPI1_R_FAST_ADDR_BITSLEN);
REG_SET_FIELD(PERIPHS_SPI_FLASH_USRREG1, SPI_USR_DUMMY_CYCLELEN, SPI1_R_FAST_DUMMY_CYCLELEN + g_rom_spiflash_dummy_len_plus[1]);
}
} else {
REG_CLR_BIT(PERIPHS_SPI_FLASH_USRREG, SPI_USR_MOSI);
if (g_rom_spiflash_dummy_len_plus[1] == 0) {
REG_CLR_BIT(PERIPHS_SPI_FLASH_USRREG, SPI_USR_DUMMY);
} else {
REG_SET_BIT(PERIPHS_SPI_FLASH_USRREG, SPI_USR_DUMMY);
REG_SET_FIELD(PERIPHS_SPI_FLASH_USRREG1, SPI_USR_DUMMY_CYCLELEN, g_rom_spiflash_dummy_len_plus[1] - 1);
}
REG_SET_BIT(PERIPHS_SPI_FLASH_USRREG, SPI_USR_MISO | SPI_USR_ADDR);
REG_SET_FIELD(PERIPHS_SPI_FLASH_USRREG1, SPI_USR_ADDR_BITLEN, SPI1_R_SIO_ADDR_BITSLEN);
//REG_SET_FIELD(PERIPHS_SPI_FLASH_USRREG2, SPI_USR_COMMAND_VALUE, 0x03);
REG_WRITE(PERIPHS_SPI_FLASH_USRREG2, (0x7 << SPI_USR_COMMAND_BITLEN_S) | 0x03);
}
if ( ESP_ROM_SPIFLASH_RESULT_OK != esp_rom_spiflash_read_data(&g_rom_spiflash_chip, target, dest_addr, len)) {
return ESP_ROM_SPIFLASH_RESULT_ERR;
}
return ESP_ROM_SPIFLASH_RESULT_OK;
}
esp_rom_spiflash_result_t esp_rom_spiflash_erase_area(uint32_t start_addr, uint32_t area_len)
{
int32_t total_sector_num;
int32_t head_sector_num;
uint32_t sector_no;
uint32_t sector_num_per_block;
//set read mode to Fastmode ,not QDIO mode for erase
//
// TODO: this is probably a bug as it doesn't re-enable QIO mode, not serious as this
// function is not used in IDF.
esp_rom_spiflash_config_readmode(ESP_ROM_SPIFLASH_SLOWRD_MODE);
//check if area is oversize of flash
if ((start_addr + area_len) > g_rom_spiflash_chip.chip_size) {
return ESP_ROM_SPIFLASH_RESULT_ERR;
}
//start_addr is aligned as sector boundary
if (0 != (start_addr % g_rom_spiflash_chip.sector_size)) {
return ESP_ROM_SPIFLASH_RESULT_ERR;
}
//Unlock flash to enable erase
if (ESP_ROM_SPIFLASH_RESULT_OK != esp_rom_spiflash_unlock(/*&g_rom_spiflash_chip*/)) {
return ESP_ROM_SPIFLASH_RESULT_ERR;
}
sector_no = start_addr / g_rom_spiflash_chip.sector_size;
sector_num_per_block = g_rom_spiflash_chip.block_size / g_rom_spiflash_chip.sector_size;
total_sector_num = (0 == (area_len % g_rom_spiflash_chip.sector_size)) ? area_len / g_rom_spiflash_chip.sector_size :
1 + (area_len / g_rom_spiflash_chip.sector_size);
//check if erase area reach over block boundary
head_sector_num = sector_num_per_block - (sector_no % sector_num_per_block);
head_sector_num = (head_sector_num >= total_sector_num) ? total_sector_num : head_sector_num;
//JJJ, BUG of 6.0 erase
//middle part of area is aligned by blocks
total_sector_num -= head_sector_num;
//head part of area is erased
while (0 != head_sector_num) {
if (ESP_ROM_SPIFLASH_RESULT_OK != esp_rom_spiflash_erase_sector(sector_no)) {
return ESP_ROM_SPIFLASH_RESULT_ERR;
}
sector_no++;
head_sector_num--;
}
while (total_sector_num > sector_num_per_block) {
if (ESP_ROM_SPIFLASH_RESULT_OK != esp_rom_spiflash_erase_block(sector_no / sector_num_per_block)) {
return ESP_ROM_SPIFLASH_RESULT_ERR;
}
sector_no += sector_num_per_block;
total_sector_num -= sector_num_per_block;
}
//tail part of area burn
while (0 < total_sector_num) {
if (ESP_ROM_SPIFLASH_RESULT_OK != esp_rom_spiflash_erase_sector(sector_no)) {
return ESP_ROM_SPIFLASH_RESULT_ERR;
}
sector_no++;
total_sector_num--;
}
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