diff --git a/components/bootloader_support/include_bootloader/bootloader_flash_priv.h b/components/bootloader_support/include_bootloader/bootloader_flash_priv.h index 45b7b06008..39aae454fc 100644 --- a/components/bootloader_support/include_bootloader/bootloader_flash_priv.h +++ b/components/bootloader_support/include_bootloader/bootloader_flash_priv.h @@ -38,6 +38,7 @@ #define CMD_RDSR2 0x35 /* Not all SPI flash uses this command */ #define CMD_OTPEN 0x3A /* Enable OTP mode, not all SPI flash uses this command */ #define CMD_WRAP 0x77 /* Set burst with wrap command */ +#define CMD_RESUME 0x7A /* Resume command to clear flash suspend bit */ /* Provide a Flash API for bootloader_support code, diff --git a/components/bootloader_support/src/esp32c3/bootloader_esp32c3.c b/components/bootloader_support/src/esp32c3/bootloader_esp32c3.c index c3eec1e440..13607bcf3d 100644 --- a/components/bootloader_support/src/esp32c3/bootloader_esp32c3.c +++ b/components/bootloader_support/src/esp32c3/bootloader_esp32c3.c @@ -193,6 +193,12 @@ static void IRAM_ATTR bootloader_init_flash_configure(void) bootloader_flash_cs_timing_config(); } +static void bootloader_spi_flash_resume(void) +{ + bootloader_execute_flash_command(CMD_RESUME, 0, 0, 0); + esp_rom_spiflash_wait_idle(&g_rom_flashchip); +} + static esp_err_t bootloader_init_spi_flash(void) { bootloader_init_flash_configure(); @@ -204,6 +210,7 @@ static esp_err_t bootloader_init_spi_flash(void) } #endif + bootloader_spi_flash_resume(); esp_rom_spiflash_unlock(); #if CONFIG_ESPTOOLPY_FLASHMODE_QIO || CONFIG_ESPTOOLPY_FLASHMODE_QOUT diff --git a/components/hal/esp32c3/include/hal/spimem_flash_ll.h b/components/hal/esp32c3/include/hal/spimem_flash_ll.h index 9e957d57ad..e2db080044 100644 --- a/components/hal/esp32c3/include/hal/spimem_flash_ll.h +++ b/components/hal/esp32c3/include/hal/spimem_flash_ll.h @@ -105,6 +105,147 @@ static inline void spimem_flash_ll_erase_block(spi_mem_dev_t *dev) dev->cmd.flash_be = 1; } +/** + * Suspend erase/program operation. + * + * @param dev Beginning address of the peripheral registers. + */ +static inline void spimem_flash_ll_suspend(spi_mem_dev_t *dev) +{ + dev->flash_sus_ctrl.flash_pes = 1; +} + +/** + * Resume suspended erase/program operation. + * + * @param dev Beginning address of the peripheral registers. + */ +static inline void spimem_flash_ll_resume(spi_mem_dev_t *dev) +{ + dev->flash_sus_ctrl.flash_per = 1; +} + +/** + * Initialize auto suspend mode, and esp32c3 doesn't support disable auto-suspend. + * + * @param dev Beginning address of the peripheral registers. + * @param auto_sus Enable/disable Flash Auto-Suspend. + */ +static inline void spimem_flash_ll_auto_suspend_init(spi_mem_dev_t *dev, bool auto_sus) +{ + dev->flash_sus_ctrl.flash_pes_en = auto_sus; +} + +/** + * Initialize auto resume mode + * + * @param dev Beginning address of the peripheral registers. + * @param auto_res Enable/Disable Flash Auto-Resume. + * + */ +static inline void spimem_flash_ll_auto_resume_init(spi_mem_dev_t *dev, bool auto_res) +{ + dev->flash_sus_ctrl.pes_per_en = auto_res; +} + +/** + * Setup the flash suspend command, may vary from chips to chips. + * + * @param dev Beginning address of the peripheral registers. + * @param sus_cmd Flash suspend command. + * + */ +static inline void spimem_flash_ll_suspend_cmd_setup(spi_mem_dev_t *dev, uint32_t sus_cmd) +{ + dev->flash_sus_cmd.flash_pes_command = sus_cmd; +} + +/** + * Setup the flash resume command, may vary from chips to chips. + * + * @param dev Beginning address of the peripheral registers. + * @param res_cmd Flash resume command. + * + */ +static inline void spimem_flash_ll_resume_cmd_setup(spi_mem_dev_t *dev, uint32_t res_cmd) +{ + dev->flash_sus_cmd.flash_per_command = res_cmd; +} + +/** + * Setup the flash read suspend status command, may vary from chips to chips. + * + * @param dev Beginning address of the peripheral registers. + * @param pesr_cmd Flash read suspend status command. + * + */ +static inline void spimem_flash_ll_rd_sus_cmd_setup(spi_mem_dev_t *dev, uint32_t pesr_cmd) +{ + dev->flash_sus_cmd.wait_pesr_command = pesr_cmd; +} + +/** + * Setup to check SUS/SUS1/SUS2 to ensure the suspend status of flashs. + * + * @param dev Beginning address of the peripheral registers. + * @param sus_check_sus_en 1: enable, 0: disable. + * + */ +static inline void spimem_flash_ll_sus_check_sus_setup(spi_mem_dev_t *dev, bool sus_check_sus_en) +{ + dev->flash_sus_ctrl.sus_timeout_cnt = 5; + dev->flash_sus_ctrl.pes_end_en = sus_check_sus_en; +} + +/** + * Setup to check SUS/SUS1/SUS2 to ensure the resume status of flashs. + * + * @param dev Beginning address of the peripheral registers. + * @param sus_check_sus_en 1: enable, 0: disable. + * + */ +static inline void spimem_flash_ll_res_check_sus_setup(spi_mem_dev_t *dev, bool res_check_sus_en) +{ + dev->flash_sus_ctrl.sus_timeout_cnt = 5; + dev->flash_sus_ctrl.per_end_en = res_check_sus_en; +} + +/** + * Set 8 bit command to read suspend status + * + * @param dev Beginning address of the peripheral registers. + */ +static inline void spimem_flash_ll_set_read_sus_status(spi_mem_dev_t *dev, uint32_t sus_conf) +{ + dev->flash_sus_ctrl.frd_sus_2b = 0; + dev->flash_sus_ctrl.pesr_end_msk = sus_conf; +} + +/** + * Initialize auto wait idle mode + * + * @param dev Beginning address of the peripheral registers. + * @param auto_waiti Enable/disable auto wait-idle function + */ +static inline void spimem_flash_ll_auto_wait_idle_init(spi_mem_dev_t *dev, bool auto_waiti) +{ + dev->flash_waiti_ctrl.waiti_cmd = 0x05; + dev->flash_sus_ctrl.flash_per_wait_en = auto_waiti; + dev->flash_sus_ctrl.flash_pes_wait_en = auto_waiti; +} + +/** + * Return the suspend status of erase or program operations. + * + * @param dev Beginning address of the peripheral registers. + * + * @return true if suspended, otherwise false. + */ +static inline bool spimem_flash_ll_sus_status(spi_mem_dev_t *dev) +{ + return dev->sus_status.flash_sus; +} + /** * Enable/disable write protection for the flash chip. * diff --git a/components/hal/esp32s2/include/hal/spimem_flash_ll.h b/components/hal/esp32s2/include/hal/spimem_flash_ll.h index ac15632f96..f36d514986 100644 --- a/components/hal/esp32s2/include/hal/spimem_flash_ll.h +++ b/components/hal/esp32s2/include/hal/spimem_flash_ll.h @@ -105,6 +105,143 @@ static inline void spimem_flash_ll_erase_block(spi_mem_dev_t *dev) dev->cmd.flash_be = 1; } +/** + * Suspend erase/program operation. + * + * @param dev Beginning address of the peripheral registers. + */ +static inline void spimem_flash_ll_suspend(spi_mem_dev_t *dev) +{ + dev->flash_sus_cmd.flash_pes = 1; +} + +/** + * Resume suspended erase/program operation. + * + * @param dev Beginning address of the peripheral registers. + */ +static inline void spimem_flash_ll_resume(spi_mem_dev_t *dev) +{ + dev->flash_sus_cmd.flash_per = 1; +} + +/** + * Initialize auto suspend mode + * + * @param dev Beginning address of the peripheral registers. + * @param auto_sus Enable/disable Flash Auto-Suspend. + */ +static inline void spimem_flash_ll_auto_suspend_init(spi_mem_dev_t *dev, bool auto_sus) +{ + dev->flash_sus_ctrl.flash_pes_en = auto_sus; +} + +/** + * Initialize auto resume mode + * + * @param dev Beginning address of the peripheral registers. + * @param auto_res Enable/Disable Flash Auto-Resume. + * + */ +static inline void spimem_flash_ll_auto_resume_init(spi_mem_dev_t *dev, bool auto_res) +{ + dev->misc.auto_per = 0; // Must disable Hardware Auto-Resume (should not be enabled, ESP32-S2 has bugs). +} + +/** + * Set 8 bit command to read suspend status + * + * @param dev Beginning address of the peripheral registers. + */ +static inline void spimem_flash_ll_set_read_sus_status(spi_mem_dev_t *dev, uint32_t sus_mask) +{ + abort();// Not supported on esp32s2 +} + +/** + * Setup the flash suspend command, may vary from chips to chips. + * + * @param dev Beginning address of the peripheral registers. + * @param sus_cmd Flash suspend command. + * + */ +static inline void spimem_flash_ll_suspend_cmd_setup(spi_mem_dev_t *dev, uint32_t sus_cmd) +{ + dev->flash_sus_ctrl.flash_pes_command = sus_cmd; +} + +/** + * Setup the flash resume command, may vary from chips to chips. + * + * @param dev Beginning address of the peripheral registers. + * @param res_cmd Flash resume command. + * + */ +static inline void spimem_flash_ll_resume_cmd_setup(spi_mem_dev_t *dev, uint32_t res_cmd) +{ + dev->flash_sus_ctrl.flash_per_command = res_cmd; +} + +/** + * Setup the flash read suspend status command, may vary from chips to chips. + * + * @param dev Beginning address of the peripheral registers. + * @param pesr_cmd Flash read suspend status command. + * + */ +static inline void spimem_flash_ll_rd_sus_cmd_setup(spi_mem_dev_t *dev, uint32_t pesr_cmd) +{ + abort();// Not supported on esp32s2 +} + +/** + * Setup to check SUS/SUS1/SUS2 to ensure the suspend status of flashs. + * + * @param dev Beginning address of the peripheral registers. + * @param sus_check_sus_en 1: enable, 0: disable. + * + */ +static inline void spimem_flash_ll_sus_check_sus_setup(spi_mem_dev_t *dev, bool sus_check_sus_en) +{ + abort();// Not supported on esp32s2 +} + +/** + * Setup to check SUS/SUS1/SUS2 to ensure the resume status of flashs. + * + * @param dev Beginning address of the peripheral registers. + * @param sus_check_sus_en 1: enable, 0: disable. + * + */ +static inline void spimem_flash_ll_res_check_sus_setup(spi_mem_dev_t *dev, bool res_check_sus_en) +{ + abort();// Not supported on esp32s2 +} + +/** + * Initialize auto wait idle mode + * + * @param dev Beginning address of the peripheral registers. + * @param auto_waiti Enable/disable auto wait-idle function + */ +static inline void spimem_flash_ll_auto_wait_idle_init(spi_mem_dev_t *dev, bool auto_waiti) +{ + dev->flash_waiti_ctrl.waiti_cmd = 0x05; // Set the command to send, to fetch flash status reg value. + dev->flash_waiti_ctrl.waiti_en = auto_waiti; // enable auto wait-idle function. +} + +/** + * Return the suspend status of erase or program operations. + * + * @param dev Beginning address of the peripheral registers. + * + * @return true if suspended, otherwise false. + */ +static inline bool spimem_flash_ll_sus_status(spi_mem_dev_t *dev) +{ + return dev->sus_status.flash_sus; +} + /** * Enable/disable write protection for the flash chip. * diff --git a/components/hal/esp32s3/include/hal/spimem_flash_ll.h b/components/hal/esp32s3/include/hal/spimem_flash_ll.h index 21f5dfdc58..e88ce28d59 100644 --- a/components/hal/esp32s3/include/hal/spimem_flash_ll.h +++ b/components/hal/esp32s3/include/hal/spimem_flash_ll.h @@ -105,6 +105,144 @@ static inline void spimem_flash_ll_erase_block(spi_mem_dev_t *dev) dev->cmd.flash_be = 1; } +/** + * Suspend erase/program operation. + * + * @param dev Beginning address of the peripheral registers. + */ +static inline void spimem_flash_ll_suspend(spi_mem_dev_t *dev) +{ + dev->flash_sus_cmd.flash_pes = 1; +} + +/** + * Resume suspended erase/program operation. + * + * @param dev Beginning address of the peripheral registers. + */ +static inline void spimem_flash_ll_resume(spi_mem_dev_t *dev) +{ + dev->flash_sus_cmd.flash_per = 1; +} + +/** + * Initialize auto wait idle mode + * + * @param dev Beginning address of the peripheral registers. + * @param auto_sus Enable/disable Flash Auto-Suspend. + */ +static inline void spimem_flash_ll_auto_suspend_init(spi_mem_dev_t *dev, bool auto_sus) +{ + dev->user.usr_dummy_idle = 1;// MUST SET 1, to avoid missing Resume + dev->flash_sus_ctrl.flash_pes_en = auto_sus; // enable Flash Auto-Suspend. +} + +/** + * Initialize auto resume mode + * + * @param dev Beginning address of the peripheral registers. + * @param auto_res Enable/Disable Flash Auto-Resume. + * + */ +static inline void spimem_flash_ll_auto_resume_init(spi_mem_dev_t *dev, bool auto_res) +{ + dev->misc.auto_per = auto_res; +} + +/** + * Setup the flash suspend command, may vary from chips to chips. + * + * @param dev Beginning address of the peripheral registers. + * @param sus_cmd Flash suspend command. + * + */ +static inline void spimem_flash_ll_suspend_cmd_setup(spi_mem_dev_t *dev, uint32_t sus_cmd) +{ + dev->flash_sus_ctrl.flash_pes_command = sus_cmd; +} + +/** + * Setup the flash resume command, may vary from chips to chips. + * + * @param dev Beginning address of the peripheral registers. + * @param res_cmd Flash resume command. + * + */ +static inline void spimem_flash_ll_resume_cmd_setup(spi_mem_dev_t *dev, uint32_t res_cmd) +{ + dev->flash_sus_ctrl.flash_per_command = res_cmd; +} + +/** + * Setup the flash read suspend status command, may vary from chips to chips. + * + * @param dev Beginning address of the peripheral registers. + * @param pesr_cmd Flash read suspend status command. + * + */ +static inline void spimem_flash_ll_rd_sus_cmd_setup(spi_mem_dev_t *dev, uint32_t pesr_cmd) +{ + abort(); //Not support on esp32s3 +} + +/** + * Setup to check SUS/SUS1/SUS2 to ensure the suspend status of flashs. + * + * @param dev Beginning address of the peripheral registers. + * @param sus_check_sus_en 1: enable, 0: disable. + * + */ +static inline void spimem_flash_ll_sus_check_sus_setup(spi_mem_dev_t *dev, bool sus_check_sus_en) +{ + abort(); //Not support on esp32s3 +} + +/** + * Setup to check SUS/SUS1/SUS2 to ensure the resume status of flashs. + * + * @param dev Beginning address of the peripheral registers. + * @param sus_check_sus_en 1: enable, 0: disable. + * + */ +static inline void spimem_flash_ll_res_check_sus_setup(spi_mem_dev_t *dev, bool res_check_sus_en) +{ + abort(); //Not support ont esp32s3 +} + +/** + * Set 8 bit command to read suspend status + * + * @param dev Beginning address of the peripheral registers. + */ +static inline void spimem_flash_ll_set_read_sus_status(spi_mem_dev_t *dev, uint32_t sus_mask) +{ + abort();// Not supported on esp32s3 +} + +/** + * Initialize auto wait idle mode + * + * @param dev Beginning address of the peripheral registers. + * @param auto_waiti Enable/disable auto wait-idle function + */ +static inline void spimem_flash_ll_auto_wait_idle_init(spi_mem_dev_t *dev, bool auto_waiti) +{ + dev->flash_waiti_ctrl.waiti_cmd = 0x05; // Set the command to send, to fetch flash status reg value. + dev->flash_waiti_ctrl.waiti_en = auto_waiti; // enable auto wait-idle function. +} + +/** + * Return the suspend status of erase or program operations. + * + * @param dev Beginning address of the peripheral registers. + * + * @return true if suspended, otherwise false. + */ +static inline bool spimem_flash_ll_sus_status(spi_mem_dev_t *dev) +{ + return dev->sus_status.flash_sus; +} + /** * Enable/disable write protection for the flash chip. * diff --git a/components/hal/include/hal/spi_flash_hal.h b/components/hal/include/hal/spi_flash_hal.h index c17ed9861d..5122689191 100644 --- a/components/hal/include/hal/spi_flash_hal.h +++ b/components/hal/include/hal/spi_flash_hal.h @@ -48,18 +48,22 @@ typedef struct { }; spi_flash_ll_clock_reg_t clock_conf; ///< Pre-calculated clock configuration value esp_flash_io_mode_t base_io_mode; ///< Default IO mode mask for common commands - uint32_t reserved_config[1]; ///< The ROM has reserved some memory for configurations with one set of driver code. (e.g. QPI mode, 64-bit address mode, etc.) + uint32_t flags; ///< Flags for configurations with one set of driver code. (e.g. QPI mode, auto-suspend mode, 64-bit address mode, etc.) +#define SPI_FLASH_HOST_CONTEXT_FLAG_AUTO_SUSPEND BIT(0) ///< When the auto-suspend is setup in configuration. +#define SPI_FLASH_HOST_CONTEXT_FLAG_AUTO_RESUME BIT(1) ///< Setup auto-resume feature. + spi_flash_sus_cmd_conf sus_cfg; ///< To store suspend command/mask information. } spi_flash_hal_context_t; -_Static_assert(sizeof(spi_flash_hal_context_t) == 28, "size of spi_flash_hal_context_t incorrect. Please check data compatibility with the ROM"); +_Static_assert(sizeof(spi_flash_hal_context_t) == 36, "size of spi_flash_hal_context_t incorrect. Please check data compatibility with the ROM"); /// Configuration structure for the SPI driver. typedef struct { spi_host_device_t host_id; ///< SPI peripheral ID. int cs_num; ///< Which cs pin is used, 0-(SOC_SPI_PERIPH_CS_NUM-1). bool iomux; ///< Whether the IOMUX is used, used for timing compensation. - int input_delay_ns; ///< Input delay on the MISO pin after the launch clock, used for timing compensation. + int input_delay_ns; ///< Input delay on the MISO pin after the launch clock, used for timing compensation. esp_flash_speed_t speed;///< SPI flash clock speed to work at. uint32_t cs_hold; ///< CS hold time config used by the host + bool auto_sus_en; ///< Auto suspend feature enable bit 1: enable, 0: disable. } spi_flash_hal_config_t; /** @@ -160,9 +164,9 @@ esp_err_t spi_flash_hal_set_write_protect(spi_flash_host_inst_t *host, bool wp); * * @param host The driver context. * - * @return ture if idle, otherwise false. + * @return 0:busy, 1:idle, 2:suspended. */ -bool spi_flash_hal_host_idle(spi_flash_host_inst_t *host); +uint32_t spi_flash_hal_check_status(spi_flash_host_inst_t *host); /** * @brief Configure the SPI host hardware registers for the specified io mode. @@ -226,3 +230,29 @@ bool spi_flash_hal_supports_direct_write(spi_flash_host_inst_t *host, const void * @return True if the buffer can be used to receive data, otherwise false. */ bool spi_flash_hal_supports_direct_read(spi_flash_host_inst_t *host, const void *p); + +/** + * @brief Resume flash chip status from suspend. + * + * @param host The driver context. + * + */ +void spi_flash_hal_resume(spi_flash_host_inst_t *host); + +/** + * @brief Set the flash into suspend status manually. + * + * @param host The driver context. + * + */ +void spi_flash_hal_suspend(spi_flash_host_inst_t *host); + +/** + * To setup for reading flash suspend status register + * + * @param host The driver context. + * @param sus_conf Flash chip suspend feature configuration, mainly for command config, may vary from chip to chip. + * + * @return Always ESP_OK + */ +esp_err_t spi_flash_hal_setup_read_suspend(spi_flash_host_inst_t *host, const spi_flash_sus_cmd_conf *sus_conf); diff --git a/components/hal/include/hal/spi_flash_types.h b/components/hal/include/hal/spi_flash_types.h index b763ee3d4a..8e7004548a 100644 --- a/components/hal/include/hal/spi_flash_types.h +++ b/components/hal/include/hal/spi_flash_types.h @@ -75,6 +75,17 @@ typedef enum { SPI_FLASH_READ_MODE_MAX, ///< The fastest io mode supported by the host is ``ESP_FLASH_READ_MODE_MAX-1``. } esp_flash_io_mode_t; +/// Configuration structure for the flash chip suspend feature. +typedef struct { + uint32_t sus_mask; ///< SUS/SUS1/SUS2 bit in flash register. + struct { + uint32_t cmd_rdsr :8; ///< Read flash status register(2) command. + uint32_t sus_cmd :8; ///< Flash suspend command. + uint32_t res_cmd :8; ///< Flash resume command. + uint32_t reserved :8; ///< Reserved, set to 0. + }; +} spi_flash_sus_cmd_conf; + ///Slowest io mode supported by ESP32, currently SlowRd #define SPI_FLASH_READ_MODE_MIN SPI_FLASH_SLOWRD @@ -159,9 +170,9 @@ struct spi_flash_host_driver_s { */ int (*read_data_slicer)(spi_flash_host_inst_t *host, uint32_t address, uint32_t len, uint32_t *align_addr, uint32_t page_size); /** - * Check whether the host is idle to perform new operations. + * Check the host status, 0:busy, 1:idle, 2:suspended. */ - bool (*host_idle)(spi_flash_host_inst_t *host); + uint32_t (*host_status)(spi_flash_host_inst_t *host); /** * Configure the host to work at different read mode. Responsible to compensate the timing and set IO mode. */ @@ -177,6 +188,21 @@ struct spi_flash_host_driver_s { * modified, the cache needs to be flushed. Left NULL if not supported. */ esp_err_t (*flush_cache)(spi_flash_host_inst_t* host, uint32_t addr, uint32_t size); + + /** + * Resume flash from suspend manually + */ + void (*resume)(spi_flash_host_inst_t *host); + + /** + * Set flash in suspend status manually + */ + void (*suspend)(spi_flash_host_inst_t *host); + + /** + * Suspend feature setup for setting cmd and status register mask. + */ + esp_err_t (*sus_setup)(spi_flash_host_inst_t *host, const spi_flash_sus_cmd_conf *sus_conf); }; #ifdef __cplusplus diff --git a/components/hal/spi_flash_hal.c b/components/hal/spi_flash_hal.c index 170c117e41..5f87b7d7cb 100644 --- a/components/hal/spi_flash_hal.c +++ b/components/hal/spi_flash_hal.c @@ -91,6 +91,10 @@ esp_err_t spi_flash_hal_init(spi_flash_hal_context_t *data_out, const spi_flash_ .clock_conf = clock_cfg->clock_reg_val, .cs_hold = cfg->cs_hold, }; + if (cfg->auto_sus_en) { + data_out->flags |= SPI_FLASH_HOST_CONTEXT_FLAG_AUTO_SUSPEND; + data_out->flags |= SPI_FLASH_HOST_CONTEXT_FLAG_AUTO_RESUME; + } ESP_EARLY_LOGD(TAG, "extra_dummy: %d", data_out->extra_dummy); return ESP_OK; diff --git a/components/hal/spi_flash_hal_common.inc b/components/hal/spi_flash_hal_common.inc index 6a33c67e38..47629ba7ab 100644 --- a/components/hal/spi_flash_hal_common.inc +++ b/components/hal/spi_flash_hal_common.inc @@ -50,6 +50,20 @@ esp_err_t spi_flash_hal_device_config(spi_flash_host_inst_t *host) spi_flash_ll_set_clock(dev, &ctx->clock_conf); int cs_hold = ctx->cs_hold; spi_flash_ll_set_hold(dev, cs_hold); +#ifndef GPSPI_BUILD +#if SOC_SPI_MEM_SUPPORT_AUTO_SUSPEND + if ((ctx->flags & SPI_FLASH_HOST_CONTEXT_FLAG_AUTO_SUSPEND) != 0) { + spi_flash_hal_setup_auto_suspend_mode(host); + } else { + spi_flash_hal_disable_auto_suspend_mode(host); + } + if ((ctx->flags & SPI_FLASH_HOST_CONTEXT_FLAG_AUTO_RESUME) != 0) { + spi_flash_hal_setup_auto_resume_mode(host); + } else { + spi_flash_hal_disable_auto_resume_mode(host); + } +#endif //SOC_SPI_MEM_SUPPORT_AUTO_SUSPEND +#endif //GPSPI_BUILD return ESP_OK; } @@ -78,7 +92,7 @@ esp_err_t spi_flash_hal_configure_host_io_mode( return ESP_ERR_NOT_SUPPORTED; } -#if CONFIG_SPI_FLASH_ROM_IMPL +#if CONFIG_SPI_FLASH_ROM_IMPL && CONFIG_IDF_TARGET_ESP32S3 /* * In S3 ROM, extra bits than 24-bit are used to indicate requirements of M7-M0: * - 24: normal transactions diff --git a/components/hal/spi_flash_hal_gpspi.c b/components/hal/spi_flash_hal_gpspi.c index 73aade3d9a..3393cd2196 100644 --- a/components/hal/spi_flash_hal_gpspi.c +++ b/components/hal/spi_flash_hal_gpspi.c @@ -36,7 +36,7 @@ bool spi_flash_hal_gpspi_supports_direct_read(spi_flash_host_inst_t *host, const return true; } -bool spi_flash_hal_gpspi_host_idle(spi_flash_host_inst_t *host) +uint32_t spi_flash_hal_gpspi_check_status(spi_flash_host_inst_t *host) { spi_dev_t *dev = get_spi_dev(host); return spi_flash_ll_host_idle(dev); diff --git a/components/hal/spi_flash_hal_iram.c b/components/hal/spi_flash_hal_iram.c index 0efda5a47a..8fe43a3e1a 100644 --- a/components/hal/spi_flash_hal_iram.c +++ b/components/hal/spi_flash_hal_iram.c @@ -13,30 +13,33 @@ // limitations under the License. #include "sdkconfig.h" +#include "hal/spi_flash_hal.h" +#if SOC_SPI_MEM_SUPPORT_AUTO_SUSPEND +void spi_flash_hal_setup_auto_suspend_mode(spi_flash_host_inst_t *host); +void spi_flash_hal_disable_auto_resume_mode(spi_flash_host_inst_t *host); +void spi_flash_hal_disable_auto_suspend_mode(spi_flash_host_inst_t *host); +void spi_flash_hal_setup_auto_resume_mode(spi_flash_host_inst_t *host); +#endif //SOC_SPI_MEM_SUPPORT_AUTO_SUSPEND +#include "spi_flash_hal_common.inc" + #ifndef CONFIG_SPI_FLASH_ROM_IMPL // HAL for // - MEMSPI -// - SPI1~3 on ESP32 +// - SPI1~3 on ESP32/S2/S3/C3 // The common part is in spi_flash_hal_common.inc -// HAL for -// - MEMSPI -// - SPI1~3 on ESP32 -// The common part is in spi_flash_hal_common.inc - -// HAL for -// - MEMSPI -// - SPI1~3 on ESP32 -// The common part is in spi_flash_hal_common.inc - -#include "spi_flash_hal_common.inc" - void spi_flash_hal_erase_chip(spi_flash_host_inst_t *host) { spi_dev_t *dev = get_spi_dev(host); spi_flash_ll_erase_chip(dev); +#if SOC_SPI_MEM_SUPPORT_AUTO_WAIT_IDLE + if((((spi_flash_hal_context_t*)host)->flags & SPI_FLASH_HOST_CONTEXT_FLAG_AUTO_SUSPEND) == 0) { + host->driver->poll_cmd_done(host); + } +#else host->driver->poll_cmd_done(host); +#endif } // Only support 24bit address @@ -46,7 +49,14 @@ void spi_flash_hal_erase_sector(spi_flash_host_inst_t *host, uint32_t start_addr spi_flash_ll_set_addr_bitlen(dev, 24); spi_flash_ll_set_address(dev, start_address & ADDRESS_MASK_24BIT); spi_flash_ll_erase_sector(dev); + +#if SOC_SPI_MEM_SUPPORT_AUTO_WAIT_IDLE + if((((spi_flash_hal_context_t*)host)->flags & SPI_FLASH_HOST_CONTEXT_FLAG_AUTO_SUSPEND) == 0) { + host->driver->poll_cmd_done(host); + } +#else host->driver->poll_cmd_done(host); +#endif } // Only support 24bit address @@ -56,7 +66,13 @@ void spi_flash_hal_erase_block(spi_flash_host_inst_t *host, uint32_t start_addre spi_flash_ll_set_addr_bitlen(dev, 24); spi_flash_ll_set_address(dev, start_address & ADDRESS_MASK_24BIT); spi_flash_ll_erase_block(dev); +#if SOC_SPI_MEM_SUPPORT_AUTO_WAIT_IDLE + if((((spi_flash_hal_context_t*)host)->flags & SPI_FLASH_HOST_CONTEXT_FLAG_AUTO_SUSPEND) == 0) { + host->driver->poll_cmd_done(host); + } +#else host->driver->poll_cmd_done(host); +#endif } // Only support 24bit address @@ -77,22 +93,97 @@ esp_err_t spi_flash_hal_set_write_protect(spi_flash_host_inst_t *host, bool wp) return ESP_OK; } -bool spi_flash_hal_host_idle(spi_flash_host_inst_t *host) +#endif // !CONFIG_SPI_FLASH_ROM_IMPL + +uint32_t spi_flash_hal_check_status(spi_flash_host_inst_t *host) { spi_dev_t *dev = get_spi_dev(host); - bool idle = spi_flash_ll_host_idle(dev); - + uint32_t status = spi_flash_ll_host_idle(dev); +#if SOC_SPI_MEM_SUPPORT_AUTO_WAIT_IDLE + uint32_t sus_status = spimem_flash_ll_sus_status((spi_mem_dev_t*)dev) << 1; +#else + uint32_t sus_status = 0; +#endif // Not clear if this is necessary, or only necessary if // chip->spi == SPI1. But probably doesn't hurt... if ((void*) dev == spi_flash_ll_get_hw(SPI_HOST)) { #if CONFIG_IDF_TARGET_ESP32 - idle &= spi_flash_ll_host_idle(&SPI0); -#else - idle &= spi_flash_ll_host_idle(&SPIMEM0); + status &= spi_flash_ll_host_idle(&SPI0); #endif } - return idle; + //status and sus_status should be mutual exclusion + return (status | sus_status); } -#endif // !CONFIG_SPI_FLASH_ROM_IMPL +esp_err_t spi_flash_hal_setup_read_suspend(spi_flash_host_inst_t *host, const spi_flash_sus_cmd_conf *sus_conf) +{ +#if SOC_SPI_MEM_SUPPORT_AUTO_SUSPEND + spi_mem_dev_t *dev = (spi_mem_dev_t *)spi_flash_ll_get_hw(SPI_HOST); + spi_flash_hal_context_t* ctx = (spi_flash_hal_context_t*)host; + memcpy(&(ctx->sus_cfg), sus_conf, sizeof(spi_flash_sus_cmd_conf)); + spimem_flash_ll_set_read_sus_status(dev, sus_conf->sus_mask); + spimem_flash_ll_suspend_cmd_setup(dev, sus_conf->sus_cmd); + spimem_flash_ll_resume_cmd_setup(dev, sus_conf->res_cmd); + spimem_flash_ll_rd_sus_cmd_setup(dev, sus_conf->cmd_rdsr); +#endif // SOC_SPI_MEM_SUPPORT_AUTO_SUSPEND + return ESP_OK; +} + +#if SOC_SPI_MEM_SUPPORT_AUTO_SUSPEND +void spi_flash_hal_setup_auto_suspend_mode(spi_flash_host_inst_t *host) +{ + spi_mem_dev_t *dev = (spi_mem_dev_t*)spi_flash_ll_get_hw(SPI_HOST); + spimem_flash_ll_auto_wait_idle_init(dev, true); + spimem_flash_ll_auto_suspend_init(dev, true); +#if SOC_SPI_MEM_SUPPORT_CHECK_SUS + spimem_flash_ll_sus_check_sus_setup(dev, true); +#endif +} + +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(SPI_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) +{ + spi_mem_dev_t *dev = (spi_mem_dev_t *)spi_flash_ll_get_hw(SPI_HOST); + spimem_flash_ll_auto_wait_idle_init(dev, false); + spimem_flash_ll_auto_suspend_init(dev, false); +#if SOC_SPI_MEM_SUPPORT_CHECK_SUS + spimem_flash_ll_sus_check_sus_setup(dev, false); +#endif +} + +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(SPI_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 + +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)); +#else + abort(); +#endif +} + +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)); +#else + abort(); +#endif +} diff --git a/components/soc/esp32c3/include/soc/soc_caps.h b/components/soc/esp32c3/include/soc/soc_caps.h index 0f256f5843..63ff028fb2 100644 --- a/components/soc/esp32c3/include/soc/soc_caps.h +++ b/components/soc/esp32c3/include/soc/soc_caps.h @@ -123,6 +123,13 @@ /*-------------------------- COEXISTENCE HARDWARE PTI CAPS -------------------------------*/ #define SOC_COEX_HW_PTI (1) +/*-------------------------- SPI MEM CAPS ---------------------------------------*/ +#define SOC_SPI_MEM_SUPPORT_AUTO_WAIT_IDLE (1) +#define SOC_SPI_MEM_SUPPORT_AUTO_SUSPEND (1) +#define SOC_SPI_MEM_SUPPORT_AUTO_RESUME (1) +#define SOC_SPI_MEM_SUPPORT_IDLE_INTR (1) +#define SOC_SPI_MEM_SUPPORT_SW_SUSPEND (1) +#define SOC_SPI_MEM_SUPPORT_CHECK_SUS (1) /*-------------------------- Power Management CAPS ----------------------------*/ #define SOC_PM_SUPPORT_WIFI_WAKEUP (1) diff --git a/components/soc/esp32s2/include/soc/soc_caps.h b/components/soc/esp32s2/include/soc/soc_caps.h index 6368926b85..026ad37f06 100644 --- a/components/soc/esp32s2/include/soc/soc_caps.h +++ b/components/soc/esp32s2/include/soc/soc_caps.h @@ -297,6 +297,10 @@ /*-------------------------- WI-FI HARDWARE TSF CAPS -------------------------------*/ #define SOC_WIFI_HW_TSF (1) +/*-------------------------- SPI MEM CAPS ---------------------------------------*/ +#define SOC_SPI_MEM_SUPPORT_AUTO_WAIT_IDLE (1) +#define SOC_SPI_MEM_SUPPORT_AUTO_SUSPEND (1) +#define SOC_SPI_MEM_SUPPORT_SW_SUSPEND (1) /*-------------------------- Power Management CAPS ---------------------------*/ #define SOC_PM_SUPPORT_EXT_WAKEUP (1) diff --git a/components/soc/esp32s2/include/soc/spi_mem_struct.h b/components/soc/esp32s2/include/soc/spi_mem_struct.h index ee318c20f1..085a226f4e 100644 --- a/components/soc/esp32s2/include/soc/spi_mem_struct.h +++ b/components/soc/esp32s2/include/soc/spi_mem_struct.h @@ -266,7 +266,7 @@ typedef volatile struct spi_mem_dev_s { }; uint32_t val; } fsm; - uint32_t data_buf[18]; /*data buffer*/ + uint32_t data_buf[16]; /*data buffer*/ union { struct { uint32_t waiti_en: 1; /*auto-waiting flash idle operation when program flash or erase flash. 1: enable 0: disable.*/ @@ -699,6 +699,7 @@ typedef volatile struct spi_mem_dev_s { uint32_t val; } date; } spi_mem_dev_t; +_Static_assert(sizeof(spi_mem_dev_t) == 0x400, "invalid spi_mem_dev_t size"); extern spi_mem_dev_t SPIMEM0; extern spi_mem_dev_t SPIMEM1; #ifdef __cplusplus diff --git a/components/soc/esp32s3/include/soc/soc_caps.h b/components/soc/esp32s3/include/soc/soc_caps.h index 8ca652c6b3..fe949f659f 100644 --- a/components/soc/esp32s3/include/soc/soc_caps.h +++ b/components/soc/esp32s3/include/soc/soc_caps.h @@ -167,5 +167,11 @@ /*-------------------------- WI-FI HARDWARE TSF CAPS -------------------------------*/ #define SOC_WIFI_HW_TSF (1) + +/*-------------------------- SPI MEM CAPS ---------------------------------------*/ +#define SOC_SPI_MEM_SUPPORT_AUTO_WAIT_IDLE (1) +#define SOC_SPI_MEM_SUPPORT_AUTO_SUSPEND (1) +#define SOC_SPI_MEM_SUPPORT_AUTO_RESUME (1) +#define SOC_SPI_MEM_SUPPORT_SW_SUSPEND (1) /*-------------------------- COEXISTENCE HARDWARE PTI CAPS -------------------------------*/ #define SOC_COEX_HW_PTI (1) diff --git a/components/spi_flash/CMakeLists.txt b/components/spi_flash/CMakeLists.txt index 0c2d20a81c..27d900b20c 100644 --- a/components/spi_flash/CMakeLists.txt +++ b/components/spi_flash/CMakeLists.txt @@ -85,6 +85,7 @@ else() "spi_flash_chip_mxic.c" "spi_flash_chip_gd.c" "spi_flash_chip_winbond.c" + "spi_flash_chip_boya.c" "memspi_host_driver.c") list(APPEND cache_srcs diff --git a/components/spi_flash/Kconfig b/components/spi_flash/Kconfig index 738e8e542f..1317e34b2c 100644 --- a/components/spi_flash/Kconfig +++ b/components/spi_flash/Kconfig @@ -137,12 +137,23 @@ menu "SPI Flash driver" then it will yield CPUs after finishing a current command. config SPI_FLASH_ERASE_YIELD_TICKS - int "CPU release time (tick)" + int "CPU release time (tick) for an erase operation" depends on SPI_FLASH_YIELD_DURING_ERASE default 1 help Defines how many ticks will be before returning to continue a erasing. + config SPI_FLASH_AUTO_SUSPEND + bool "Auto suspend long erase/write operations" + default n + depends on IDF_TARGET_ESP32C3 && !SPI_FLASH_USE_LEGACY_IMPL && !SPI_FLASH_ROM_IMPL + help + This is made default n, because this needs bootloader support. + This feature needs special bootloader support. + If you want to OTA to a image with this feature + (e.g. turn on this config option for OTA image), please make + sure the bootloader has the support for it. (above IDF v4.3) + config SPI_FLASH_WRITE_CHUNK_SIZE int "Flash write chunk size" default 8192 @@ -208,6 +219,15 @@ menu "SPI Flash driver" given by ``chip_drv`` member of the chip struct. This adds support for variant chips, however will extend detecting time. + config SPI_FLASH_SUPPORT_BOYA_CHIP + bool "BOYA" + depends on !IDF_TARGET_ESP32 + default y + help + Enable this to support auto detection of BOYA chips if chip vendor not directly + given by ``chip_drv`` member of the chip struct. This adds support for variant + chips, however will extend detecting time. + endmenu #auto detect flash chips endmenu diff --git a/components/spi_flash/esp_flash_api.c b/components/spi_flash/esp_flash_api.c index bc9bede10b..b47b0afadd 100644 --- a/components/spi_flash/esp_flash_api.c +++ b/components/spi_flash/esp_flash_api.c @@ -22,6 +22,7 @@ #include "esp_log.h" #include "sdkconfig.h" #include "esp_flash_internal.h" +#include "spi_flash_defs.h" static const char TAG[] = "spi_flash"; @@ -832,6 +833,12 @@ IRAM_ATTR esp_err_t esp_flash_set_io_mode(esp_flash_t* chip, bool qe) } #endif //CONFIG_SPI_FLASH_ROM_IMPL +//init suspend mode cmd, uses internal. +esp_err_t esp_flash_suspend_cmd_init(esp_flash_t* chip) +{ + return chip->chip_drv->sus_setup(chip); +} + #ifndef CONFIG_SPI_FLASH_USE_LEGACY_IMPL esp_err_t esp_flash_app_disable_protect(bool disable) { diff --git a/components/spi_flash/esp_flash_spi_init.c b/components/spi_flash/esp_flash_spi_init.c index 73eaf6dad4..69330fd794 100644 --- a/components/spi_flash/esp_flash_spi_init.c +++ b/components/spi_flash/esp_flash_spi_init.c @@ -23,6 +23,7 @@ #include "esp_heap_caps.h" #include "hal/spi_types.h" #include "driver/spi_common_internal.h" +#include "hal/spi_flash_hal.h" #include "esp_flash_internal.h" #include "esp_rom_gpio.h" #if CONFIG_IDF_TARGET_ESP32 @@ -89,6 +90,7 @@ __attribute__((unused)) static const char TAG[] = "spi_flash"; } #elif CONFIG_IDF_TARGET_ESP32C3 #include "esp32c3/rom/efuse.h" +#if !CONFIG_SPI_FLASH_AUTO_SUSPEND #define ESP_FLASH_HOST_CONFIG_DEFAULT() (memspi_host_config_t){ \ .host_id = SPI_HOST,\ .speed = DEFAULT_FLASH_SPEED, \ @@ -96,6 +98,16 @@ __attribute__((unused)) static const char TAG[] = "spi_flash"; .iomux = true, \ .input_delay_ns = 0,\ } +#else +#define ESP_FLASH_HOST_CONFIG_DEFAULT() (memspi_host_config_t){ \ + .host_id = SPI_HOST,\ + .speed = DEFAULT_FLASH_SPEED, \ + .cs_num = 0, \ + .iomux = true, \ + .input_delay_ns = 0,\ + .auto_sus_en = true,\ +} +#endif //!CONFIG_SPI_FLASH_AUTO_SUSPEND #endif @@ -239,6 +251,7 @@ static DRAM_ATTR esp_flash_t default_chip = { .os_func = &esp_flash_noos_functions, }; +extern esp_err_t esp_flash_suspend_cmd_init(esp_flash_t* chip); esp_err_t esp_flash_init_default_chip(void) { const esp_rom_spiflash_chip_t *legacy_chip = &g_rom_flashchip; @@ -272,6 +285,12 @@ esp_err_t esp_flash_init_default_chip(void) default_chip.size = legacy_chip->chip_size; esp_flash_default_chip = &default_chip; +#ifdef CONFIG_SPI_FLASH_AUTO_SUSPEND + err = esp_flash_suspend_cmd_init(&default_chip); + if (err != ESP_OK) { + return err; + } +#endif return ESP_OK; } diff --git a/components/spi_flash/include/memspi_host_driver.h b/components/spi_flash/include/memspi_host_driver.h index edc6c705eb..269bd991d0 100644 --- a/components/spi_flash/include/memspi_host_driver.h +++ b/components/spi_flash/include/memspi_host_driver.h @@ -31,10 +31,13 @@ .write_data_slicer = memspi_host_write_data_slicer, \ .read = spi_flash_hal_read, \ .read_data_slicer = memspi_host_read_data_slicer, \ - .host_idle = spi_flash_hal_host_idle, \ + .host_status = spi_flash_hal_check_status, \ .configure_host_io_mode = spi_flash_hal_configure_host_io_mode, \ .poll_cmd_done = spi_flash_hal_poll_cmd_done, \ .flush_cache = memspi_host_flush_cache, \ + .resume = spi_flash_hal_resume, \ + .suspend = spi_flash_hal_suspend,\ + .sus_setup = spi_flash_hal_setup_read_suspend,\ } /// configuration for the memspi host diff --git a/components/spi_flash/include/spi_flash_chip_boya.h b/components/spi_flash/include/spi_flash_chip_boya.h new file mode 100644 index 0000000000..58139b3c94 --- /dev/null +++ b/components/spi_flash/include/spi_flash_chip_boya.h @@ -0,0 +1,21 @@ +// Copyright 2015-2021 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. + +#pragma once + +#include +#include "esp_flash.h" +#include "spi_flash_chip_driver.h" + +extern const spi_flash_chip_t esp_flash_chip_boya; diff --git a/components/spi_flash/include/spi_flash_chip_driver.h b/components/spi_flash/include/spi_flash_chip_driver.h index 83668b0df9..43abfcc6c4 100644 --- a/components/spi_flash/include/spi_flash_chip_driver.h +++ b/components/spi_flash/include/spi_flash_chip_driver.h @@ -186,6 +186,8 @@ struct spi_flash_chip_t { /** Yield to other tasks. Called during erase operations. */ esp_err_t (*yield)(esp_flash_t *chip, uint32_t wip); + /** Setup flash suspend configuration. */ + esp_err_t (*sus_setup)(esp_flash_t *chip); }; /* Pointer to an array of pointers to all known drivers for flash chips. This array is used diff --git a/components/spi_flash/include/spi_flash_chip_generic.h b/components/spi_flash/include/spi_flash_chip_generic.h index 0469a438a6..de99c02d5e 100644 --- a/components/spi_flash/include/spi_flash_chip_generic.h +++ b/components/spi_flash/include/spi_flash_chip_generic.h @@ -255,24 +255,6 @@ extern const spi_flash_chip_t esp_flash_chip_generic; * Utilities *******************************************************************************/ -/** - * @brief Wait for the SPI host hardware state machine to be idle. - * - * This isn't a flash chip_drv operation, but it's called by - * spi_flash_chip_generic_wait_idle() and may be useful when implementing - * alternative drivers. - * - * timeout_us will be decremented if the function needs to wait until the host hardware is idle. - * - * @param chip Pointer to SPI flash chip to use. If NULL, esp_flash_default_chip is substituted. - * - * @return - * - ESP_OK if success - * - ESP_ERR_TIMEOUT if not idle before timeout - * - or other error passed from the ``set_write_protect`` or ``common_command`` function of host driver - */ -esp_err_t spi_flash_generic_wait_host_idle(esp_flash_t *chip, uint32_t *timeout_us); - /// Function pointer type for reading status register with QE bit. typedef esp_err_t (*esp_flash_rdsr_func_t)(esp_flash_t* chip, uint32_t* out_sr); @@ -394,5 +376,13 @@ esp_err_t spi_flash_chip_generic_config_host_io_mode(esp_flash_t *chip, bool add */ esp_err_t spi_flash_chip_generic_yield(esp_flash_t* chip, uint32_t wip); +/** + * @brief Setup for flash suspend command configuration. + * + * @param chip Pointer to SPI flash chip to use. If NULL, esp_flash_default_chip is substituted. + * @return ESP_OK + */ +esp_err_t spi_flash_chip_generic_suspend_cmd_conf(esp_flash_t *chip); + /// Default timeout configuration used by most chips const flash_chip_op_timeout_t spi_flash_chip_generic_timeout; diff --git a/components/spi_flash/linker.lf b/components/spi_flash/linker.lf index 6691173e54..800d2e1683 100644 --- a/components/spi_flash/linker.lf +++ b/components/spi_flash/linker.lf @@ -8,3 +8,6 @@ entries: spi_flash_chip_gd (noflash) spi_flash_chip_winbond (noflash) memspi_host_driver (noflash) + + if IDF_TARGET_ESP32 = n: + spi_flash_chip_boya (noflash) diff --git a/components/spi_flash/memspi_host_driver.c b/components/spi_flash/memspi_host_driver.c index edbfc00325..f713df829f 100644 --- a/components/spi_flash/memspi_host_driver.c +++ b/components/spi_flash/memspi_host_driver.c @@ -37,7 +37,7 @@ esp_err_t spi_flash_hal_gpspi_configure_host_io_mode( esp_flash_io_mode_t io_mode); extern esp_err_t spi_flash_hal_gpspi_common_command(spi_flash_host_inst_t *host, spi_flash_trans_t *trans); extern esp_err_t spi_flash_hal_gpspi_read(spi_flash_host_inst_t *host, void *buffer, uint32_t address, uint32_t read_len); -extern bool spi_flash_hal_gpspi_host_idle(spi_flash_host_inst_t *host); +extern uint32_t spi_flash_hal_gpspi_check_status(spi_flash_host_inst_t *host); extern bool spi_flash_hal_gpspi_supports_direct_write(spi_flash_host_inst_t *host, const void *p); extern bool spi_flash_hal_gpspi_supports_direct_read(spi_flash_host_inst_t *host, const void *p); @@ -57,10 +57,12 @@ static const spi_flash_host_driver_t esp_flash_gpspi_host = { .write_data_slicer = memspi_host_write_data_slicer, .read = spi_flash_hal_gpspi_read, .read_data_slicer = memspi_host_read_data_slicer, - .host_idle = spi_flash_hal_gpspi_host_idle, + .host_status = spi_flash_hal_gpspi_check_status, .configure_host_io_mode = spi_flash_hal_gpspi_configure_host_io_mode, .poll_cmd_done = spi_flash_hal_gpspi_poll_cmd_done, .flush_cache = NULL, + .resume = spi_flash_hal_resume, + .suspend = spi_flash_hal_suspend, }; #endif diff --git a/components/spi_flash/private_include/spi_flash_defs.h b/components/spi_flash/private_include/spi_flash_defs.h index 3d07eb1353..79e06669f3 100644 --- a/components/spi_flash/private_include/spi_flash_defs.h +++ b/components/spi_flash/private_include/spi_flash_defs.h @@ -26,6 +26,8 @@ #define CMD_WRDI 0x04 #define CMD_RDSR 0x05 #define CMD_RDSR2 0x35 /* Not all SPI flash uses this command */ +#define CMD_RDSCUR 0x2B /* on specific(MXIC) board, read security register */ +#define CMD_RDFR 0x48 /* on specific(ISSI) board, read function register */ #define CMD_FASTRD_QIO 0xEB #define CMD_FASTRD_QIO_4B 0xEC @@ -47,6 +49,8 @@ #define CMD_LARGE_BLOCK_ERASE_4B 0xDC /* 64KB block erase command */ #define CMD_PROGRAM_PAGE 0x02 #define CMD_PROGRAM_PAGE_4B 0x12 +#define CMD_SUSPEND 0x75 +#define CMD_RESUME 0x7A #define CMD_RST_EN 0x66 #define CMD_RST_DEV 0x99 diff --git a/components/spi_flash/sim/stubs/soc/include/hal/spi_flash_types.h b/components/spi_flash/sim/stubs/soc/include/hal/spi_flash_types.h index 71cdf73d67..9e0525e309 100644 --- a/components/spi_flash/sim/stubs/soc/include/hal/spi_flash_types.h +++ b/components/spi_flash/sim/stubs/soc/include/hal/spi_flash_types.h @@ -121,9 +121,9 @@ struct spi_flash_host_driver_s { */ int (*read_data_slicer)(spi_flash_host_inst_t *host, uint32_t address, uint32_t len, uint32_t *align_addr, uint32_t page_size); /** - * Check whether the host is idle to perform new operations. + * Check the host status, 0:busy, 1:idle, 2:suspended. */ - bool (*host_idle)(spi_flash_host_inst_t *host); + uint32_t (*host_status)(spi_flash_host_inst_t *host); /** * Configure the host to work at different read mode. Responsible to compensate the timing and set IO mode. */ @@ -139,6 +139,21 @@ struct spi_flash_host_driver_s { * modified, the cache needs to be flushed. Left NULL if not supported. */ esp_err_t (*flush_cache)(spi_flash_host_inst_t* host, uint32_t addr, uint32_t size); + + /** + * Resume flash from suspend manually + */ + void (*resume)(spi_flash_host_inst_t *host); + + /** + * Set flash in suspend status manually + */ + void (*suspend)(spi_flash_host_inst_t *host); + + /** + * Suspend feature setup for setting cmd and status register mask. + */ + esp_err_t (*sus_setup)(spi_flash_host_inst_t *host, const spi_flash_sus_cmd_conf *sus_conf); }; ///Slowest io mode supported by ESP32, currently SlowRd #define SPI_FLASH_READ_MODE_MIN SPI_FLASH_SLOWRD diff --git a/components/spi_flash/spi_flash_chip_boya.c b/components/spi_flash/spi_flash_chip_boya.c new file mode 100644 index 0000000000..57e20e7b83 --- /dev/null +++ b/components/spi_flash/spi_flash_chip_boya.c @@ -0,0 +1,80 @@ +// Copyright 2015-2021 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 +#include "spi_flash_chip_generic.h" +#include "spi_flash_chip_gd.h" +#include "spi_flash_defs.h" + +/* Driver for BOYA flash chip */ +esp_err_t spi_flash_chip_gd_suspend_cmd_conf(esp_flash_t *chip); + +// Use the same implementation as GD chips +#define spi_flash_chip_boya_suspend_cmd_conf spi_flash_chip_gd_suspend_cmd_conf + +esp_err_t spi_flash_chip_boya_probe(esp_flash_t *chip, uint32_t flash_id) +{ + /* Check manufacturer and product IDs match our desired masks */ + const uint8_t MFG_ID = 0x68; + if (flash_id >> 16 != MFG_ID) { + return ESP_ERR_NOT_FOUND; + } + + const uint16_t FLASH_ID_MASK = 0xFF00; + const uint16_t FLASH_ID_VALUE = 0x4000; + if ((flash_id & FLASH_ID_MASK) != FLASH_ID_VALUE) { + return ESP_ERR_NOT_FOUND; + } + + return ESP_OK; +} + +static const char chip_name[] = "boya"; + +// The BOYA chip can use the functions for generic chips except from set read mode and probe, +// So we only replace these two functions. +const spi_flash_chip_t esp_flash_chip_boya = { + .name = chip_name, + .timeout = &spi_flash_chip_generic_timeout, + .probe = spi_flash_chip_boya_probe, + .reset = spi_flash_chip_generic_reset, + .detect_size = spi_flash_chip_generic_detect_size, + .erase_chip = spi_flash_chip_generic_erase_chip, + .erase_sector = spi_flash_chip_generic_erase_sector, + .erase_block = spi_flash_chip_generic_erase_block, + .sector_size = 4 * 1024, + .block_erase_size = 64 * 1024, + + .get_chip_write_protect = spi_flash_chip_generic_get_write_protect, + .set_chip_write_protect = spi_flash_chip_generic_set_write_protect, + + .num_protectable_regions = 0, + .protectable_regions = NULL, + .get_protected_regions = NULL, + .set_protected_regions = NULL, + + .read = spi_flash_chip_generic_read, + .write = spi_flash_chip_generic_write, + .program_page = spi_flash_chip_generic_page_program, + .page_size = 256, + .write_encrypted = spi_flash_chip_generic_write_encrypted, + + .wait_idle = spi_flash_chip_generic_wait_idle, + .set_io_mode = spi_flash_chip_generic_set_io_mode, + .get_io_mode = spi_flash_chip_generic_get_io_mode, + + .read_reg = spi_flash_chip_generic_read_reg, + .yield = spi_flash_chip_generic_yield, + .sus_setup = spi_flash_chip_boya_suspend_cmd_conf, +}; diff --git a/components/spi_flash/spi_flash_chip_drivers.c b/components/spi_flash/spi_flash_chip_drivers.c index ab5ad1b653..a7f68e895f 100644 --- a/components/spi_flash/spi_flash_chip_drivers.c +++ b/components/spi_flash/spi_flash_chip_drivers.c @@ -19,6 +19,7 @@ #include "spi_flash_chip_mxic.h" #include "spi_flash_chip_gd.h" #include "spi_flash_chip_winbond.h" +#include "spi_flash_chip_boya.h" #include "sdkconfig.h" /* @@ -42,6 +43,9 @@ static const spi_flash_chip_t *default_registered_chips[] = { #endif #ifdef CONFIG_SPI_FLASH_SUPPORT_WINBOND_CHIP &esp_flash_chip_winbond, +#endif +#ifdef CONFIG_SPI_FLASH_SUPPORT_BOYA_CHIP + &esp_flash_chip_boya, #endif // Default chip drivers that will accept all chip ID. // FM, Winbond and XMC chips are supposed to be supported by this chip driver. diff --git a/components/spi_flash/spi_flash_chip_gd.c b/components/spi_flash/spi_flash_chip_gd.c index c7c1005cf9..54a2255566 100644 --- a/components/spi_flash/spi_flash_chip_gd.c +++ b/components/spi_flash/spi_flash_chip_gd.c @@ -75,6 +75,18 @@ esp_err_t spi_flash_chip_gd_get_io_mode(esp_flash_t *chip, esp_flash_io_mode_t* } #endif //CONFIG_SPI_FLASH_ROM_IMPL +esp_err_t spi_flash_chip_gd_suspend_cmd_conf(esp_flash_t *chip) +{ + spi_flash_sus_cmd_conf sus_conf = { + .sus_mask = 0x84, + .cmd_rdsr = CMD_RDSR2, + .sus_cmd = CMD_SUSPEND, + .res_cmd = CMD_RESUME, + }; + + return chip->host->driver->sus_setup(chip->host, &sus_conf); +} + static const char chip_name[] = "gd"; // The issi chip can use the functions for generic chips except from set read mode and probe, @@ -111,4 +123,5 @@ const spi_flash_chip_t esp_flash_chip_gd = { .read_reg = spi_flash_chip_generic_read_reg, .yield = spi_flash_chip_generic_yield, + .sus_setup = spi_flash_chip_gd_suspend_cmd_conf, }; diff --git a/components/spi_flash/spi_flash_chip_generic.c b/components/spi_flash/spi_flash_chip_generic.c index 917da64f81..e4011ff67b 100644 --- a/components/spi_flash/spi_flash_chip_generic.c +++ b/components/spi_flash/spi_flash_chip_generic.c @@ -301,22 +301,6 @@ esp_err_t spi_flash_chip_generic_get_write_protect(esp_flash_t *chip, bool *out_ return err; } -esp_err_t spi_flash_generic_wait_host_idle(esp_flash_t *chip, uint32_t *timeout_us) -{ - while (!chip->host->driver->host_idle(chip->host) && *timeout_us > 0) { -#if HOST_DELAY_INTERVAL_US > 0 - if (*timeout_us > 1) { - int delay = MIN(HOST_DELAY_INTERVAL_US, *timeout_us); - chip->os_func->delay_us(chip->os_func_data, delay); - *timeout_us -= delay; - } else { - return ESP_ERR_TIMEOUT; - } -#endif - } - return ESP_OK; -} - esp_err_t spi_flash_chip_generic_read_reg(esp_flash_t* chip, spi_flash_register_t reg_id, uint32_t* out_reg) { return chip->host->driver->read_status(chip->host, (uint8_t*)out_reg); @@ -357,14 +341,19 @@ 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; while (timeout_us > 0) { + while (!chip->host->driver->host_status(chip->host) && timeout_us > 0) { - esp_err_t err = spi_flash_generic_wait_host_idle(chip, & timeout_us); - if (err != ESP_OK) { - return err; +#if HOST_DELAY_INTERVAL_US > 0 + if (timeout_us > 1) { + int delay = MIN(HOST_DELAY_INTERVAL_US, timeout_us); + chip->os_func->delay_us(chip->os_func_data, delay); + timeout_us -= delay; + } +#endif } uint32_t read; - err = chip->chip_drv->read_reg(chip, SPI_FLASH_REG_STATUS, &read); + esp_err_t err = chip->chip_drv->read_reg(chip, SPI_FLASH_REG_STATUS, &read); if (err != ESP_OK) { return err; } @@ -511,6 +500,7 @@ const spi_flash_chip_t esp_flash_chip_generic = { .read_reg = spi_flash_chip_generic_read_reg, .yield = spi_flash_chip_generic_yield, + .sus_setup = spi_flash_chip_generic_suspend_cmd_conf, }; #ifndef CONFIG_SPI_FLASH_ROM_IMPL @@ -652,3 +642,15 @@ esp_err_t spi_flash_common_set_io_mode(esp_flash_t *chip, esp_flash_wrsr_func_t } #endif // !CONFIG_SPI_FLASH_ROM_IMPL + +esp_err_t spi_flash_chip_generic_suspend_cmd_conf(esp_flash_t *chip) +{ + spi_flash_sus_cmd_conf sus_conf = { + .sus_mask = 0x80, + .cmd_rdsr = CMD_RDSR2, + .sus_cmd = CMD_SUSPEND, + .res_cmd = CMD_RESUME, + }; + + return chip->host->driver->sus_setup(chip->host, &sus_conf); +} diff --git a/components/spi_flash/spi_flash_chip_issi.c b/components/spi_flash/spi_flash_chip_issi.c index 6bb13f9e44..b5cdbcb326 100644 --- a/components/spi_flash/spi_flash_chip_issi.c +++ b/components/spi_flash/spi_flash_chip_issi.c @@ -58,6 +58,17 @@ esp_err_t spi_flash_chip_issi_get_io_mode(esp_flash_t *chip, esp_flash_io_mode_t return ret; } +esp_err_t spi_flash_chip_issi_suspend_cmd_conf(esp_flash_t *chip) +{ + spi_flash_sus_cmd_conf sus_conf = { + .sus_mask = 0x06, + .cmd_rdsr = CMD_RDFR, + .sus_cmd = CMD_SUSPEND, + .res_cmd = CMD_RESUME, + }; + + return chip->host->driver->sus_setup(chip->host, &sus_conf); +} static const char chip_name[] = "issi"; @@ -95,4 +106,5 @@ const spi_flash_chip_t esp_flash_chip_issi = { .read_reg = spi_flash_chip_generic_read_reg, .yield = spi_flash_chip_generic_yield, + .sus_setup = spi_flash_chip_issi_suspend_cmd_conf, }; diff --git a/components/spi_flash/spi_flash_chip_mxic.c b/components/spi_flash/spi_flash_chip_mxic.c index 750e765221..ed19db7611 100644 --- a/components/spi_flash/spi_flash_chip_mxic.c +++ b/components/spi_flash/spi_flash_chip_mxic.c @@ -39,6 +39,18 @@ esp_err_t spi_flash_chip_issi_get_io_mode(esp_flash_t *chip, esp_flash_io_mode_t static const char chip_name[] = "mxic"; +esp_err_t spi_flash_chip_mxic_suspend_cmd_conf(esp_flash_t *chip) +{ + spi_flash_sus_cmd_conf sus_conf = { + .sus_mask = 0x06, + .cmd_rdsr = CMD_RDSCUR, + .sus_cmd = CMD_SUSPEND, + .res_cmd = CMD_RESUME, + }; + + return chip->host->driver->sus_setup(chip->host, &sus_conf); +} + // The mxic chip can use the functions for generic chips except from set read mode and probe, // So we only replace these two functions. const spi_flash_chip_t esp_flash_chip_mxic = { @@ -73,4 +85,5 @@ const spi_flash_chip_t esp_flash_chip_mxic = { .read_reg = spi_flash_chip_mxic_read_reg, .yield = spi_flash_chip_generic_yield, + .sus_setup = spi_flash_chip_mxic_suspend_cmd_conf, }; diff --git a/components/spi_flash/spi_flash_chip_winbond.c b/components/spi_flash/spi_flash_chip_winbond.c index 73ab2f09b4..74ca36735e 100644 --- a/components/spi_flash/spi_flash_chip_winbond.c +++ b/components/spi_flash/spi_flash_chip_winbond.c @@ -176,6 +176,7 @@ const spi_flash_chip_t esp_flash_chip_winbond = { .read_reg = spi_flash_chip_generic_read_reg, .yield = spi_flash_chip_generic_yield, + .sus_setup = spi_flash_chip_generic_suspend_cmd_conf, }; diff --git a/components/spi_flash/spi_flash_os_func_app.c b/components/spi_flash/spi_flash_os_func_app.c index f9c334ac3f..3df88bd9a1 100644 --- a/components/spi_flash/spi_flash_os_func_app.c +++ b/components/spi_flash/spi_flash_os_func_app.c @@ -67,12 +67,16 @@ static inline IRAM_ATTR bool on_spi1_check_yield(spi1_app_func_arg_t* ctx); IRAM_ATTR static void cache_enable(void* arg) { +#ifndef CONFIG_SPI_FLASH_AUTO_SUSPEND g_flash_guard_default_ops.end(); +#endif } IRAM_ATTR static void cache_disable(void* arg) { +#ifndef CONFIG_SPI_FLASH_AUTO_SUSPEND g_flash_guard_default_ops.start(); +#endif } static IRAM_ATTR esp_err_t spi_start(void *arg) diff --git a/components/spi_flash/test/test_esp_flash.c b/components/spi_flash/test/test_esp_flash.c index f95cc53546..94810e3963 100644 --- a/components/spi_flash/test/test_esp_flash.c +++ b/components/spi_flash/test/test_esp_flash.c @@ -24,6 +24,14 @@ #include "esp_rom_sys.h" #include "esp_timer.h" +#if CONFIG_IDF_TARGET_ESP32S2 +#include "esp32s2/rom/cache.h" +#elif CONFIG_IDF_TARGET_ESP32S3 +#include "esp32s3/rom/cache.h" +#elif CONFIG_IDF_TARGET_ESP32C3 +#include "esp32c3/rom/cache.h" +#endif + #define FUNC_SPI 1 static uint8_t sector_buf[4096]; @@ -606,6 +614,51 @@ void test_erase_large_region(const esp_partition_t *part) FLASH_TEST_CASE("SPI flash erase large region", test_erase_large_region); FLASH_TEST_CASE_3("SPI flash erase large region", test_erase_large_region); +#if CONFIG_SPI_FLASH_AUTO_SUSPEND +void esp_test_for_suspend(void) +{ + /*clear content in cache*/ +#if !CONFIG_IDF_TARGET_ESP32C3 + Cache_Invalidate_DCache_All(); +#endif + Cache_Invalidate_ICache_All(); + ESP_LOGI(TAG, "suspend test begins:"); + printf("run into test suspend function\n"); + printf("print something when flash is erasing:\n"); + printf("aaaaa bbbbb zzzzz fffff qqqqq ccccc\n"); +} + +void task_erase_large_region(void *arg) +{ + esp_partition_t *part = (esp_partition_t *)arg; + test_erase_large_region(part); + vTaskDelete(NULL); +} + +void task_request_suspend(void *arg) +{ + vTaskDelay(2); + ESP_LOGI(TAG, "flash go into suspend"); + esp_test_for_suspend(); + vTaskDelete(NULL); +} + +void task_delay(void *arg) +{ + esp_rom_delay_us(2000000); + vTaskDelete(NULL); +} + +static void test_flash_suspend_resume(const esp_partition_t* part) +{ + xTaskCreatePinnedToCore(task_request_suspend, "suspend", 2048, (void *)"test_for_suspend", UNITY_FREERTOS_PRIORITY + 3, NULL, 0); + xTaskCreatePinnedToCore(task_erase_large_region, "test", 2048, (void *)part, UNITY_FREERTOS_PRIORITY + 2, NULL, 0); + xTaskCreatePinnedToCore(task_delay, "task_delay", 1024, (void *)"task_delay", UNITY_FREERTOS_PRIORITY + 1, NULL, 0); +} + +FLASH_TEST_CASE("SPI flash suspend and resume test", test_flash_suspend_resume); +#endif //CONFIG_SPI_FLASH_AUTO_SUSPEND + static void test_write_protection(const esp_partition_t* part) { esp_flash_t* chip = part->flash_chip; diff --git a/tools/ci/check_public_headers_exceptions.txt b/tools/ci/check_public_headers_exceptions.txt index 68a736e87b..10285dee3b 100644 --- a/tools/ci/check_public_headers_exceptions.txt +++ b/tools/ci/check_public_headers_exceptions.txt @@ -42,6 +42,7 @@ components/spi_flash/include/spi_flash_chip_issi.h components/spi_flash/include/spi_flash_chip_mxic.h components/spi_flash/include/spi_flash_chip_gd.h components/spi_flash/include/spi_flash_chip_winbond.h +components/spi_flash/include/spi_flash_chip_boya.h components/spi_flash/include/memspi_host_driver.h components/spi_flash/include/spi_flash_chip_driver.h components/spi_flash/include/spi_flash_chip_generic.h