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/esp32/include/hal/spi_flash_ll.h b/components/hal/esp32/include/hal/spi_flash_ll.h index 1114ac4d84..d7314f8710 100644 --- a/components/hal/esp32/include/hal/spi_flash_ll.h +++ b/components/hal/esp32/include/hal/spi_flash_ll.h @@ -117,51 +117,6 @@ static inline void spi_flash_ll_erase_block(spi_dev_t *dev) dev->cmd.flash_be = 1; } -/** - * Suspend erase/program operation. - * - * @param dev Beginning address of the peripheral registers. - */ -static inline void spi_flash_ll_suspend(spi_dev_t *dev) -{ - dev->cmd.flash_pes = 1; -} - -/** - * Resume suspended erase/program operation. - * - * @param dev Beginning address of the peripheral registers. - */ -static inline void spi_flash_ll_resume(spi_dev_t *dev) -{ - dev->cmd.flash_per = 1; -} - -/** - * Initialize auto wait idle mode. (work only for ESP32-S2) - * - * @param dev Beginning address of the peripheral registers. - * @param auto_sus Enable/disable Flash Auto-Suspend. - */ -#define spi_flash_ll_auto_wait_idle_init(...) () - -/** - * Initialize auto wait idle mode (work only for ESP32-S2) - * - * @param dev Beginning address of the peripheral registers. - * @param auto_waiti Enable/disable auto wait-idle function - */ -#define spi_flash_ll_auto_suspend_init(...) () - -/** - * Return the suspend status of erase or program operations. (work only for ESP32-S2) - * - * @param dev Beginning address of the peripheral registers. - * - * @return true if suspended, otherwise false. - */ -#define spi_flash_ll_sus_status(...) ({false;}) - /** * Enable/disable write protection for the flash chip. * 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/spi_flash_ll.h b/components/hal/esp32s2/include/hal/spi_flash_ll.h index ecfb28b465..cf20f55bf6 100644 --- a/components/hal/esp32s2/include/hal/spi_flash_ll.h +++ b/components/hal/esp32s2/include/hal/spi_flash_ll.h @@ -83,8 +83,6 @@ typedef union { #define spi_flash_ll_erase_chip(dev) spimem_flash_ll_erase_chip((spi_mem_dev_t*)dev) #define spi_flash_ll_erase_sector(dev) spimem_flash_ll_erase_sector((spi_mem_dev_t*)dev) #define spi_flash_ll_erase_block(dev) spimem_flash_ll_erase_block((spi_mem_dev_t*)dev) -#define spi_flash_ll_suspend(dev) spimem_flash_ll_suspend((spi_mem_dev_t*)dev) -#define spi_flash_ll_resume(dev) spimem_flash_ll_resume((spi_mem_dev_t*)dev) #define spi_flash_ll_set_write_protect(dev, wp) spimem_flash_ll_set_write_protect((spi_mem_dev_t*)dev, wp) #define spi_flash_ll_get_buffer_data(dev, buffer, read_len) spimem_flash_ll_get_buffer_data((spi_mem_dev_t*)dev, buffer, read_len) #define spi_flash_ll_set_buffer_data(dev, buffer, len) spimem_flash_ll_set_buffer_data((spi_mem_dev_t*)dev, buffer, len) @@ -105,9 +103,6 @@ typedef union { #define spi_flash_ll_set_dummy(dev, dummy) spimem_flash_ll_set_dummy((spi_mem_dev_t*)dev, dummy) #define spi_flash_ll_set_dummy_out(dev, en, lev) spimem_flash_ll_set_dummy_out((spi_mem_dev_t*)dev, en, lev) #define spi_flash_ll_set_hold(dev, hold_n) spimem_flash_ll_set_hold((spi_mem_dev_t*)dev, hold_n) -#define spi_flash_ll_auto_wait_idle_init(dev, auto_waiti) spimem_flash_ll_auto_wait_idle_init((spi_mem_dev_t*)dev, auto_waiti) -#define spi_flash_ll_auto_suspend_init(dev, auto_sus) spimem_flash_ll_auto_suspend_init((spi_mem_dev_t*)dev, auto_sus) -#define spi_flash_ll_sus_status(dev) spimem_flash_ll_sus_status((spi_mem_dev_t*)dev) #endif diff --git a/components/hal/esp32s2/include/hal/spimem_flash_ll.h b/components/hal/esp32s2/include/hal/spimem_flash_ll.h index 9a283ad31d..f36d514986 100644 --- a/components/hal/esp32s2/include/hal/spimem_flash_ll.h +++ b/components/hal/esp32s2/include/hal/spimem_flash_ll.h @@ -122,24 +122,100 @@ static inline void spimem_flash_ll_suspend(spi_mem_dev_t *dev) */ static inline void spimem_flash_ll_resume(spi_mem_dev_t *dev) { - dev->misc.auto_per = 0; // Must disable Hardware Auto-Resume (should not be enabled, ESP32-S2 has bugs). dev->flash_sus_cmd.flash_per = 1; - while (dev->flash_sus_cmd.flash_per) { }; } /** - * Initialize auto wait idle mode + * 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_command = 0x75; // Set auto suspend command, usually 0x75 - dev->flash_sus_ctrl.flash_per_command = 0x7A; // Set auto resume command, usually 0x7A - // SET_PERI_REG_MASK(SPI_MEM_FLASH_SUS_CMD_REG(1), SPI_MEM_PES_PER_EN_M); // Only on S3 chip - // SET_PERI_REG_MASK(SPI_MEM_FLASH_SUS_CMD_REG(1), SPI_MEM_PESR_IDLE_EN_M); // MUST SET 1, to avoid missing Resume (Only on S3 chip) - dev->flash_sus_ctrl.flash_pes_en = auto_sus; // enable Flash Auto-Suspend. + 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 } /** @@ -161,7 +237,7 @@ static inline void spimem_flash_ll_auto_wait_idle_init(spi_mem_dev_t *dev, bool * * @return true if suspended, otherwise false. */ -static inline bool spimem_flash_ll_sus_status(const spi_mem_dev_t *dev) +static inline bool spimem_flash_ll_sus_status(spi_mem_dev_t *dev) { return dev->sus_status.flash_sus; } 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 dfd73508a4..aacd686897 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_host_idle(spi_flash_host_inst_t *host); /** * @brief Configure the SPI host hardware registers for the specified io mode. @@ -205,13 +209,6 @@ esp_err_t spi_flash_hal_configure_host_io_mode(spi_flash_host_inst_t *host, uint */ void spi_flash_hal_poll_cmd_done(spi_flash_host_inst_t *host); -/** - * Setup a auto-suspend mode. - * - * @param host The driver context. - */ -void spi_flash_hal_setup_auto_suspend_mode(spi_flash_host_inst_t *host); - /** * Check whether the given buffer can be used as the write buffer directly. If 'chip' is connected to the main SPI bus, we can only write directly from * regions that are accessible ith cache disabled. * @@ -233,3 +230,37 @@ 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 Check the suspend status and resume a suspended operation. + * + * @param host The driver context. + * + */ +bool spi_flash_hal_check_suspend(spi_flash_host_inst_t *host); + +/** + * @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 c361cfc4d2..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,10 +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); + /** - * Check the necessity of suspending erase/program operations. + * Resume flash from suspend manually */ - void (*check_suspend)(spi_flash_host_inst_t *host); + 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 5835462dfc..5f87b7d7cb 100644 --- a/components/hal/spi_flash_hal.c +++ b/components/hal/spi_flash_hal.c @@ -71,17 +71,6 @@ static inline int get_dummy_n(bool gpio_is_used, int input_delay_ns, int eff_clk return apb_period_n / apbclk_n; } -#ifdef CONFIG_SPI_FLASH_AUTO_SUSPEND - -void spi_flash_hal_setup_auto_suspend_mode(spi_flash_host_inst_t *host) -{ - mspi_auto_suspend_stub_install(); - spi_flash_ll_auto_wait_idle_init(spi_flash_ll_get_hw(SPI_HOST), true); - spi_flash_ll_auto_suspend_init(spi_flash_ll_get_hw(SPI_HOST), true); -} - -#endif // CONFIG_SPI_FLASH_AUTO_SUSPEND - esp_err_t spi_flash_hal_init(spi_flash_hal_context_t *data_out, const spi_flash_hal_config_t *cfg) { if (!esp_ptr_internal(data_out) && cfg->host_id == SPI1_HOST) { @@ -102,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..89d1697f18 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_host_idle(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..b2451f4413 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,104 @@ 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) +uint32_t spi_flash_hal_host_idle(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); + uint32_t sus_status = spi_flash_hal_check_suspend(host) << 1; // 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); +#elif CONFIG_IDF_TARGET_ESP32S2 + status &= spi_flash_ll_host_idle(&SPIMEM0); #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 + +bool spi_flash_hal_check_suspend(spi_flash_host_inst_t *host) +{ +#if SOC_SPI_MEM_SUPPORT_AUTO_WAIT_IDLE + if (spimem_flash_ll_sus_status((spi_mem_dev_t*)(((spi_flash_hal_context_t *)host)->spi))) { + return true; + } +#endif + return false; +} +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 a236e2e6fc..dd9909cbc1 100644 --- a/components/soc/esp32c3/include/soc/soc_caps.h +++ b/components/soc/esp32c3/include/soc/soc_caps.h @@ -125,6 +125,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 11454d5b2a..91d768d6b6 100644 --- a/components/soc/esp32s2/include/soc/soc_caps.h +++ b/components/soc/esp32s2/include/soc/soc_caps.h @@ -296,6 +296,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 4ca11b2e95..085a226f4e 100644 --- a/components/soc/esp32s2/include/soc/spi_mem_struct.h +++ b/components/soc/esp32s2/include/soc/spi_mem_struct.h @@ -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/Kconfig b/components/spi_flash/Kconfig index 916dd8c49c..040b6e2dd8 100644 --- a/components/spi_flash/Kconfig +++ b/components/spi_flash/Kconfig @@ -124,7 +124,6 @@ menu "SPI Flash driver" config SPI_FLASH_YIELD_DURING_ERASE bool "Enables yield operation during flash erase" default y - depends on SPI_FLASH_USE_LEGACY_IMPL help This allows to yield the CPUs between erase commands. Prevents starvation of other tasks. @@ -144,6 +143,17 @@ menu "SPI Flash driver" 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 diff --git a/components/spi_flash/esp_flash_api.c b/components/spi_flash/esp_flash_api.c index 80f663467b..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"; @@ -173,9 +174,6 @@ bool esp_flash_chip_driver_initialized(const esp_flash_t *chip) return true; } -#ifdef CONFIG_SPI_FLASH_AUTO_SUSPEND - spi_flash_ll_auto_suspend_init(spi_flash_ll_get_hw(SPI_HOST), val); -#endif esp_err_t IRAM_ATTR esp_flash_init(esp_flash_t *chip) { // Chip init flow @@ -835,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 65d9d2af92..69330fd794 100644 --- a/components/spi_flash/esp_flash_spi_init.c +++ b/components/spi_flash/esp_flash_spi_init.c @@ -90,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, \ @@ -97,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 @@ -240,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; @@ -256,10 +268,6 @@ esp_err_t esp_flash_init_default_chip(void) return err; } -#ifdef CONFIG_SPI_FLASH_AUTO_SUSPEND - spi_flash_hal_setup_auto_suspend_mode(default_chip.host); -#endif - // ROM TODO: account for non-standard default pins in efuse // ROM TODO: to account for chips which are slow to power on, maybe keep probing in a loop here err = esp_flash_init(&default_chip); @@ -277,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 e3ad68f4e3..3424b43cb9 100644 --- a/components/spi_flash/include/memspi_host_driver.h +++ b/components/spi_flash/include/memspi_host_driver.h @@ -31,11 +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_host_idle, \ .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, \ - .check_suspend = memspi_host_cb_check_suspend, \ + .resume = spi_flash_hal_resume, \ + .suspend = spi_flash_hal_suspend,\ + .sus_setup = spi_flash_hal_setup_read_suspend,\ } /// configuration for the memspi host @@ -178,10 +180,3 @@ int memspi_host_read_data_slicer(spi_flash_host_inst_t *host, uint32_t address, */ int memspi_host_write_data_slicer(spi_flash_host_inst_t *host, uint32_t address, uint32_t len, uint32_t *align_address, uint32_t page_size); -/** - * @brief Check the suspend status and resume a suspended operation. - * - * @param host The driver context. - * - */ -void memspi_host_cb_check_suspend(spi_flash_host_inst_t *host); 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/memspi_host_driver.c b/components/spi_flash/memspi_host_driver.c index 1cedc303c4..47b6634861 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_host_idle(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,11 +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_host_idle, .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, - .check_suspend = memspi_host_cb_check_suspend, + .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 372a4cd4e2..7bd2358c44 100644 --- a/components/spi_flash/private_include/spi_flash_defs.h +++ b/components/spi_flash/private_include/spi_flash_defs.h @@ -47,8 +47,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_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_gd.c b/components/spi_flash/spi_flash_chip_gd.c index c7c1005cf9..9e1e580b75 100644 --- a/components/spi_flash/spi_flash_chip_gd.c +++ b/components/spi_flash/spi_flash_chip_gd.c @@ -111,4 +111,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_generic_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..c7047794a6 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 = 0x84, + .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_winbond.c b/components/spi_flash/spi_flash_chip_winbond.c index 73ab2f09b4..c2f52e0ad1 100644 --- a/components/spi_flash/spi_flash_chip_winbond.c +++ b/components/spi_flash/spi_flash_chip_winbond.c @@ -140,6 +140,18 @@ esp_err_t spi_flash_chip_winbond_erase_block(esp_flash_t *chip, uint32_t start_a return err; } +esp_err_t spi_flash_chip_winbond_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); +} + static const char chip_name[] = "winbond"; // The issi chip can use the functions for generic chips except from set read mode and probe, @@ -176,6 +188,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/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;