feat(spi_flash): support software resume after suspend in unicore

This commit is contained in:
C.S.M
2025-01-21 15:02:22 +08:00
parent aedf0408a6
commit d772cb3055
8 changed files with 79 additions and 13 deletions
+23
View File
@@ -127,6 +127,29 @@ menu "Main Flash configuration"
For new users, DO NOT enable this config.
config SPI_FLASH_SOFTWARE_RESUME
bool "Resume flash program/erase form suspend state by software control"
default n
depends on SPI_FLASH_AUTO_SUSPEND && FREERTOS_UNICORE && IDF_EXPERIMENTAL_FEATURES
help
Enable this config will disable auto-resume from hardware. Thus the software will resume the chip
after any higher priority task/interrupt which suspend the chip. The benefit is that the suspend-resume
will not disturb the higher priority task and interrupt.
This currently is only valid on single core chip.
config SPI_FLASH_DISABLE_SCHEDULER_IN_SUSPEND
bool "Disable task scheduler when suspend is enabled when SPI1 operation is ongoing"
default n
# Only valid on single core because no protection is supported on multi core
depends on SPI_FLASH_AUTO_SUSPEND && FREERTOS_UNICORE
help
Disable freertos task scheduler when CONFIG_SPI_FLASH_AUTO_SUSPEND is enabled.
Thus only interrupt can trigger a suspend. When SPI_FLASH_AUTO_SUSPEND is enabled,
default behavior is not disable the task scheduler, so both interrupt and high priority
task can suspend the erase/program operation. When this option is enabled, task
scheduler is disabled, only interrupt can suspend erase/program operation.
endmenu
endmenu
+2 -1
View File
@@ -21,6 +21,7 @@
#include "esp_rom_spiflash.h"
#include "esp_private/esp_clk.h"
#include "esp_spi_flash_counters.h"
#include "esp_check.h"
#if CONFIG_IDF_TARGET_ESP32S2
#include "esp_crypto_lock.h" // for locking flash encryption peripheral
@@ -1269,7 +1270,7 @@ esp_err_t IRAM_ATTR esp_flash_write_encrypted(esp_flash_t *chip, uint32_t addres
that share a key (as derived from flash address).
On ESP32-S2 and later, the temporary buffer need to be
seperated into 16-bytes, 32-bytes, 64-bytes(if supported).
separated into 16-bytes, 32-bytes, 64-bytes(if supported).
So, on ESP32-S2 and later, here has a totally different
data prepare implementation.
+2 -2
View File
@@ -1,5 +1,5 @@
/*
* SPDX-FileCopyrightText: 2015-2023 Espressif Systems (Shanghai) CO LTD
* SPDX-FileCopyrightText: 2015-2024 Espressif Systems (Shanghai) CO LTD
*
* SPDX-License-Identifier: Apache-2.0
*/
@@ -204,7 +204,7 @@ esp_err_t esp_flash_erase_chip(esp_flash_t *chip);
* @param start Address to start erasing flash. Must be sector aligned.
* @param len Length of region to erase. Must also be sector aligned.
*
* Sector size is specifyed in chip->drv->sector_size field (typically 4096 bytes.) ESP_ERR_INVALID_ARG will be
* Sector size is specified in chip->drv->sector_size field (typically 4096 bytes.) ESP_ERR_INVALID_ARG will be
* returned if the start & length are not a multiple of this size.
*
* Erase is performed using block (multi-sector) erases where possible (block size is specified in
+11 -1
View File
@@ -376,6 +376,7 @@ esp_err_t spi_flash_chip_generic_wait_idle(esp_flash_t *chip, uint32_t timeout_u
uint8_t status = 0;
const int interval = CHIP_WAIT_IDLE_INTERVAL_US;
bool suspend_state = false;
while (timeout_us > 0) {
while (!chip->host->driver->host_status(chip->host) && timeout_us > 0) {
@@ -388,6 +389,15 @@ esp_err_t spi_flash_chip_generic_wait_idle(esp_flash_t *chip, uint32_t timeout_u
#endif
}
#if CONFIG_SPI_FLASH_SOFTWARE_RESUME
suspend_state = ((chip->host->driver->host_status(chip->host) & SPI_FLASH_HAL_STATUS_SUSPEND) != 0) ? true : false;
if (suspend_state) {
// Oh! find you are in suspend state
chip->host->driver->resume(chip->host);
}
#endif
uint32_t read;
esp_err_t err = chip->chip_drv->read_reg(chip, SPI_FLASH_REG_STATUS, &read);
if (err != ESP_OK) {
@@ -395,7 +405,7 @@ esp_err_t spi_flash_chip_generic_wait_idle(esp_flash_t *chip, uint32_t timeout_u
}
status = read;
if ((status & SR_WIP) == 0) { // Verify write in progress is complete
if ((status & SR_WIP) == 0 && (suspend_state == false)) { // 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.
@@ -121,6 +121,20 @@ static IRAM_ATTR esp_err_t spi1_start(void *arg)
//directly disable the cache and interrupts when lock is not used
cache_disable(NULL);
#endif
#if CONFIG_SPI_FLASH_DISABLE_SCHEDULER_IN_SUSPEND
// Disable scheduler
if (xTaskGetSchedulerState() == taskSCHEDULER_RUNNING) {
#ifdef CONFIG_FREERTOS_SMP
//Note: Scheduler suspension behavior changed in FreeRTOS SMP
vTaskPreemptionDisable(NULL);
#else
// Disable scheduler on the current CPU
vTaskSuspendAll();
#endif // CONFIG_FREERTOS_SMP
}
#endif // CONFIG_SPI_FLASH_DISABLE_SCHEDULER_IN_SUSPEND
on_spi_acquired((app_func_arg_t*)arg);
return ret;
}
@@ -139,6 +153,18 @@ static IRAM_ATTR esp_err_t spi1_end(void *arg)
#else
cache_enable(NULL);
#endif
#if CONFIG_SPI_FLASH_DISABLE_SCHEDULER_IN_SUSPEND
if (xTaskGetSchedulerState() == taskSCHEDULER_RUNNING) {
#ifdef CONFIG_FREERTOS_SMP
//Note: Scheduler suspension behavior changed in FreeRTOS SMP
vTaskPreemptionEnable(NULL);
#else
xTaskResumeAll();
#endif // CONFIG_FREERTOS_SMP
}
#endif // CONFIG_SPI_FLASH_DISABLE_SCHEDULER_IN_SUSPEND
on_spi_released((app_func_arg_t*)arg);
return ret;
}