diff --git a/components/hal/include/hal/spi_flash_hal.h b/components/hal/include/hal/spi_flash_hal.h index 593bb5b1a1..e4e2cd7e2e 100644 --- a/components/hal/include/hal/spi_flash_hal.h +++ b/components/hal/include/hal/spi_flash_hal.h @@ -1,5 +1,5 @@ /* - * SPDX-FileCopyrightText: 2010-2022 Espressif Systems (Shanghai) CO LTD + * SPDX-FileCopyrightText: 2010-2024 Espressif Systems (Shanghai) CO LTD * * SPDX-License-Identifier: Apache-2.0 */ @@ -27,6 +27,10 @@ extern "C" { #define SPI_FLASH_HAL_MAX_WRITE_BYTES 64 #define SPI_FLASH_HAL_MAX_READ_BYTES 64 +/* spi flash state */ +#define SPI_FLASH_HAL_STATUS_BUSY BIT0 +#define SPI_FLASH_HAL_STATUS_SUSPEND BIT1 + /** * Generic driver context structure for all chips using the SPI peripheral. * Include this into the HEAD of the driver data for other driver @@ -82,7 +86,7 @@ typedef struct { int cs_num; ///< Which cs pin is used, 0-(SOC_SPI_PERIPH_CS_NUM-1). bool auto_sus_en; ///< Auto suspend feature enable bit 1: enable, 0: disable. bool octal_mode_en; ///< Octal spi flash mode enable bit 1: enable, 0: disable. - bool using_timing_tuning; ///< System exist SPI0/1 timing tuning, using value from system directely if set to 1. + bool using_timing_tuning; ///< System exist SPI0/1 timing tuning, using value from system directly if set to 1. esp_flash_io_mode_t default_io_mode; ///< Default flash io mode. int freq_mhz; ///< SPI flash clock speed (MHZ). int clock_src_freq; ///< SPI flash clock source (MHZ). diff --git a/components/hal/spi_flash_hal.c b/components/hal/spi_flash_hal.c index d758608c8b..66c225f770 100644 --- a/components/hal/spi_flash_hal.c +++ b/components/hal/spi_flash_hal.c @@ -132,6 +132,10 @@ esp_err_t spi_flash_hal_init(spi_flash_hal_context_t *data_out, const spi_flash_ data_out->tsus_val = cfg->tsus_val; } +#if CONFIG_SPI_FLASH_SOFTWARE_RESUME + data_out->flags &= ~SPI_FLASH_HOST_CONTEXT_FLAG_AUTO_RESUME; +#endif + #if SOC_SPI_MEM_SUPPORT_OPI_MODE if (cfg->octal_mode_en) { data_out->flags |= SPI_FLASH_HOST_CONTEXT_FLAG_OCTAL_MODE; diff --git a/components/hal/spi_flash_hal_iram.c b/components/hal/spi_flash_hal_iram.c index e73ead6ad9..eae0f54031 100644 --- a/components/hal/spi_flash_hal_iram.c +++ b/components/hal/spi_flash_hal_iram.c @@ -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 */ @@ -159,6 +159,7 @@ void spi_flash_hal_setup_auto_suspend_mode(spi_flash_host_inst_t *host) spimem_flash_ll_sus_set_spi0_lock_trans(dev, SPIMEM_FLASH_LL_SPI0_MAX_LOCK_VAL_MSPI_TICKS); #if SOC_SPI_MEM_SUPPORT_CHECK_SUS spimem_flash_ll_sus_check_sus_setup(dev, true); + spimem_flash_ll_res_check_sus_setup(dev, true); #endif } @@ -166,9 +167,6 @@ void spi_flash_hal_setup_auto_resume_mode(spi_flash_host_inst_t *host) { spi_mem_dev_t *dev = (spi_mem_dev_t*)spi_flash_ll_get_hw(SPI1_HOST); spimem_flash_ll_auto_resume_init(dev, true); -#if SOC_SPI_MEM_SUPPORT_CHECK_SUS - spimem_flash_ll_res_check_sus_setup(dev, true); -#endif } void spi_flash_hal_disable_auto_suspend_mode(spi_flash_host_inst_t *host) @@ -178,6 +176,7 @@ void spi_flash_hal_disable_auto_suspend_mode(spi_flash_host_inst_t *host) spimem_flash_ll_auto_suspend_init(dev, false); #if SOC_SPI_MEM_SUPPORT_CHECK_SUS spimem_flash_ll_sus_check_sus_setup(dev, false); + spimem_flash_ll_res_check_sus_setup(dev, false); #endif } @@ -185,9 +184,6 @@ void spi_flash_hal_disable_auto_resume_mode(spi_flash_host_inst_t *host) { spi_mem_dev_t *dev = (spi_mem_dev_t*)spi_flash_ll_get_hw(SPI1_HOST); spimem_flash_ll_auto_resume_init(dev, false); -#if SOC_SPI_MEM_SUPPORT_CHECK_SUS - spimem_flash_ll_res_check_sus_setup(dev, false); -#endif } #endif // SOC_SPI_MEM_SUPPORT_AUTO_SUSPEND @@ -195,6 +191,7 @@ void spi_flash_hal_resume(spi_flash_host_inst_t *host) { #if SOC_SPI_MEM_SUPPORT_SW_SUSPEND spimem_flash_ll_resume((spi_mem_dev_t*)(((spi_flash_hal_context_t *)host)->spi)); + host->driver->poll_cmd_done(host); #else abort(); #endif @@ -204,6 +201,7 @@ void spi_flash_hal_suspend(spi_flash_host_inst_t *host) { #if SOC_SPI_MEM_SUPPORT_SW_SUSPEND spimem_flash_ll_suspend((spi_mem_dev_t *)(((spi_flash_hal_context_t *)host)->spi)); + host->driver->poll_cmd_done(host); #else abort(); #endif diff --git a/components/spi_flash/Kconfig b/components/spi_flash/Kconfig index 80a48a1b7e..fbabf81bd9 100644 --- a/components/spi_flash/Kconfig +++ b/components/spi_flash/Kconfig @@ -120,6 +120,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 diff --git a/components/spi_flash/esp_flash_api.c b/components/spi_flash/esp_flash_api.c index aafaa32233..a4cb822718 100644 --- a/components/spi_flash/esp_flash_api.c +++ b/components/spi_flash/esp_flash_api.c @@ -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. diff --git a/components/spi_flash/include/esp_flash.h b/components/spi_flash/include/esp_flash.h index f9b6d11f3a..062e1c598f 100644 --- a/components/spi_flash/include/esp_flash.h +++ b/components/spi_flash/include/esp_flash.h @@ -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 diff --git a/components/spi_flash/spi_flash_chip_generic.c b/components/spi_flash/spi_flash_chip_generic.c index 8233eaf174..9edcbe6c51 100644 --- a/components/spi_flash/spi_flash_chip_generic.c +++ b/components/spi_flash/spi_flash_chip_generic.c @@ -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. diff --git a/components/spi_flash/spi_flash_os_func_app.c b/components/spi_flash/spi_flash_os_func_app.c index 51f0671f09..4bbfbb9e36 100644 --- a/components/spi_flash/spi_flash_os_func_app.c +++ b/components/spi_flash/spi_flash_os_func_app.c @@ -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; }