diff --git a/components/spi_flash/Kconfig b/components/spi_flash/Kconfig index 046146e00..e884726c5 100644 --- a/components/spi_flash/Kconfig +++ b/components/spi_flash/Kconfig @@ -84,6 +84,36 @@ menu "SPI Flash driver" The implementation of SPI flash has been greatly changed in IDF v4.0. Enable this option to use the legacy implementation. + config SPI_FLASH_BYPASS_BLOCK_ERASE + bool "Bypass a block erase and always do sector erase" + default n + help + Some flash chips can have very high "max" erase times, especially for block erase (32KB or 64KB). + This option allows to bypass "block erase" and always do sector erase commands. + This will be much slower overall in most cases, but improves latency for other code to run. + + config SPI_FLASH_YIELD_DURING_ERASE + bool "Enables yield operation during flash erase" + default y + help + This allows to yield the CPUs between erase commands. + Prevents starvation of other tasks. + + config SPI_FLASH_ERASE_YIELD_DURATION_MS + int "Duration of erasing to yield CPUs (ms)" + depends on SPI_FLASH_YIELD_DURING_ERASE + default 20 + help + If a duration of one erase command is large + then it will yield CPUs after finishing a current command. + + config SPI_FLASH_ERASE_YIELD_TICKS + int "CPU release time (tick)" + depends on SPI_FLASH_YIELD_DURING_ERASE + default 1 + help + Defines how many ticks will be before returning to continue a erasing. + menu "Auto-detect flash chips" config SPI_FLASH_SUPPORT_ISSI_CHIP diff --git a/components/spi_flash/esp_flash_api.c b/components/spi_flash/esp_flash_api.c index 7073ab62e..340fba002 100644 --- a/components/spi_flash/esp_flash_api.c +++ b/components/spi_flash/esp_flash_api.c @@ -23,6 +23,9 @@ #include "sdkconfig.h" #include "esp_heap_caps.h" #include "esp_flash_internal.h" +#include +#include +#include "esp_timer.h" static const char TAG[] = "spi_flash"; @@ -337,20 +340,27 @@ esp_err_t IRAM_ATTR esp_flash_erase_region(esp_flash_t *chip, uint32_t start, ui err = spiflash_end(chip, err); } - +#ifdef CONFIG_SPI_FLASH_YIELD_DURING_ERASE + int64_t no_yield_time_us = 0; +#endif while (err == ESP_OK && len >= sector_size) { +#ifdef CONFIG_SPI_FLASH_YIELD_DURING_ERASE + int64_t start_time_us = esp_timer_get_time(); +#endif err = spiflash_start(chip); if (err != ESP_OK) { return err; } +#ifndef CONFIG_SPI_FLASH_BYPASS_BLOCK_ERASE // If possible erase an entire multi-sector block if (block_erase_size > 0 && len >= block_erase_size && (start % block_erase_size) == 0) { err = chip->chip_drv->erase_block(chip, start); start += block_erase_size; len -= block_erase_size; - } - else { + } else +#endif + { // Otherwise erase individual sector only err = chip->chip_drv->erase_sector(chip, start); start += sector_size; @@ -358,6 +368,14 @@ esp_err_t IRAM_ATTR esp_flash_erase_region(esp_flash_t *chip, uint32_t start, ui } err = spiflash_end(chip, err); + +#ifdef CONFIG_SPI_FLASH_YIELD_DURING_ERASE + no_yield_time_us += (esp_timer_get_time() - start_time_us); + if (no_yield_time_us / 1000 >= CONFIG_SPI_FLASH_ERASE_YIELD_DURATION_MS) { + no_yield_time_us = 0; + vTaskDelay(CONFIG_SPI_FLASH_ERASE_YIELD_TICKS); + } +#endif } return err; } diff --git a/components/spi_flash/flash_ops.c b/components/spi_flash/flash_ops.c index a8ffc7810..187c27c9b 100644 --- a/components/spi_flash/flash_ops.c +++ b/components/spi_flash/flash_ops.c @@ -42,6 +42,7 @@ #include "cache_utils.h" #include "esp_flash.h" #include "esp_attr.h" +#include "esp_timer.h" /* bytes erased by SPIEraseBlock() ROM function */ @@ -235,18 +236,34 @@ esp_err_t IRAM_ATTR spi_flash_erase_range(size_t start_addr, size_t size) esp_rom_spiflash_result_t rc; rc = spi_flash_unlock(); if (rc == ESP_ROM_SPIFLASH_RESULT_OK) { +#ifdef CONFIG_SPI_FLASH_YIELD_DURING_ERASE + int64_t no_yield_time_us = 0; +#endif for (size_t sector = start; sector != end && rc == ESP_ROM_SPIFLASH_RESULT_OK; ) { +#ifdef CONFIG_SPI_FLASH_YIELD_DURING_ERASE + int64_t start_time_us = esp_timer_get_time(); +#endif spi_flash_guard_start(); +#ifndef CONFIG_SPI_FLASH_BYPASS_BLOCK_ERASE if (sector % sectors_per_block == 0 && end - sector >= sectors_per_block) { rc = esp_rom_spiflash_erase_block(sector / sectors_per_block); sector += sectors_per_block; COUNTER_ADD_BYTES(erase, sectors_per_block * SPI_FLASH_SEC_SIZE); - } else { + } else +#endif + { rc = esp_rom_spiflash_erase_sector(sector); ++sector; COUNTER_ADD_BYTES(erase, SPI_FLASH_SEC_SIZE); } spi_flash_guard_end(); +#ifdef CONFIG_SPI_FLASH_YIELD_DURING_ERASE + no_yield_time_us += (esp_timer_get_time() - start_time_us); + if (no_yield_time_us / 1000 >= CONFIG_SPI_FLASH_ERASE_YIELD_DURATION_MS) { + no_yield_time_us = 0; + vTaskDelay(CONFIG_SPI_FLASH_ERASE_YIELD_TICKS); + } +#endif } } COUNTER_STOP(erase); diff --git a/components/spi_flash/sim/Makefile.files b/components/spi_flash/sim/Makefile.files index f66a58c67..bc9db882c 100644 --- a/components/spi_flash/sim/Makefile.files +++ b/components/spi_flash/sim/Makefile.files @@ -17,6 +17,7 @@ INCLUDE_DIRS := \ app_update/include \ driver/include \ esp32/include \ + esp_timer/include \ freertos/include \ log/include \ newlib/include \ @@ -31,6 +32,7 @@ INCLUDE_DIRS := \ soc/esp32/include \ soc/include \ esp32/include \ + esp_timer/include \ bootloader_support/include \ app_update/include \ spi_flash/include \ diff --git a/components/spi_flash/sim/stubs/Makefile.files b/components/spi_flash/sim/stubs/Makefile.files index 91c034768..b8627aff4 100644 --- a/components/spi_flash/sim/stubs/Makefile.files +++ b/components/spi_flash/sim/stubs/Makefile.files @@ -4,6 +4,7 @@ SOURCE_FILES := \ newlib/lock.c \ esp32/crc.cpp \ esp32/esp_random.c \ + esp_timer/src/esp_timer.c \ bootloader_support/src/bootloader_common.c INCLUDE_DIRS := \ @@ -12,6 +13,7 @@ INCLUDE_DIRS := \ app_update/include \ driver/include \ esp32/include \ + esp_timer/include \ freertos/include \ log/include \ newlib/include \ @@ -24,6 +26,7 @@ INCLUDE_DIRS := \ xtensa/include \ xtensa/esp32/include \ esp32/include \ + esp_timer/include \ bootloader_support/include \ app_update/include \ spi_flash/include \ diff --git a/components/spi_flash/sim/stubs/esp_timer/include/esp_timer.h b/components/spi_flash/sim/stubs/esp_timer/include/esp_timer.h new file mode 100644 index 000000000..954448b0d --- /dev/null +++ b/components/spi_flash/sim/stubs/esp_timer/include/esp_timer.h @@ -0,0 +1,15 @@ +#pragma once + +#include +#include + +#ifdef __cplusplus +extern "C" { +#endif + +int64_t esp_timer_get_time(void); + +#ifdef __cplusplus +} +#endif + diff --git a/components/spi_flash/sim/stubs/esp_timer/src/esp_timer.c b/components/spi_flash/sim/stubs/esp_timer/src/esp_timer.c new file mode 100644 index 000000000..657873cad --- /dev/null +++ b/components/spi_flash/sim/stubs/esp_timer/src/esp_timer.c @@ -0,0 +1,20 @@ +// Copyright 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 "esp_timer.h" + +int64_t esp_timer_get_time(void) +{ + return 0; +}