spi_flash: Add auto suspend mode on esp32c3

This commit is contained in:
Cao Sen Miao
2020-12-18 12:57:55 +08:00
parent 1f37a5f162
commit 9905da46e0
30 changed files with 759 additions and 170 deletions

View File

@@ -38,6 +38,7 @@
#define CMD_RDSR2 0x35 /* Not all SPI flash uses this command */ #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_OTPEN 0x3A /* Enable OTP mode, not all SPI flash uses this command */
#define CMD_WRAP 0x77 /* Set burst with wrap 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, /* Provide a Flash API for bootloader_support code,

View File

@@ -193,6 +193,12 @@ static void IRAM_ATTR bootloader_init_flash_configure(void)
bootloader_flash_cs_timing_config(); 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) static esp_err_t bootloader_init_spi_flash(void)
{ {
bootloader_init_flash_configure(); bootloader_init_flash_configure();
@@ -204,6 +210,7 @@ static esp_err_t bootloader_init_spi_flash(void)
} }
#endif #endif
bootloader_spi_flash_resume();
esp_rom_spiflash_unlock(); esp_rom_spiflash_unlock();
#if CONFIG_ESPTOOLPY_FLASHMODE_QIO || CONFIG_ESPTOOLPY_FLASHMODE_QOUT #if CONFIG_ESPTOOLPY_FLASHMODE_QIO || CONFIG_ESPTOOLPY_FLASHMODE_QOUT

View File

@@ -117,51 +117,6 @@ static inline void spi_flash_ll_erase_block(spi_dev_t *dev)
dev->cmd.flash_be = 1; 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. * Enable/disable write protection for the flash chip.
* *

View File

@@ -105,6 +105,147 @@ static inline void spimem_flash_ll_erase_block(spi_mem_dev_t *dev)
dev->cmd.flash_be = 1; 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. * Enable/disable write protection for the flash chip.
* *

View File

@@ -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_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_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_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_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_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) #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(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_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_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 #endif

View File

@@ -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) 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; 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 dev Beginning address of the peripheral registers.
* @param auto_sus Enable/disable Flash Auto-Suspend. * @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) 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_pes_en = auto_sus;
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. * 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. * @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; return dev->sus_status.flash_sus;
} }

View File

@@ -105,6 +105,144 @@ static inline void spimem_flash_ll_erase_block(spi_mem_dev_t *dev)
dev->cmd.flash_be = 1; 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. * Enable/disable write protection for the flash chip.
* *

View File

@@ -48,18 +48,22 @@ typedef struct {
}; };
spi_flash_ll_clock_reg_t clock_conf; ///< Pre-calculated clock configuration value 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 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; } 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. /// Configuration structure for the SPI driver.
typedef struct { typedef struct {
spi_host_device_t host_id; ///< SPI peripheral ID. spi_host_device_t host_id; ///< SPI peripheral ID.
int cs_num; ///< Which cs pin is used, 0-(SOC_SPI_PERIPH_CS_NUM-1). 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. 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. esp_flash_speed_t speed;///< SPI flash clock speed to work at.
uint32_t cs_hold; ///< CS hold time config used by the host 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; } 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. * @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. * @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); 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 * 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. * * 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. * @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); 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);

View File

@@ -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``. 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; } 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 ///Slowest io mode supported by ESP32, currently SlowRd
#define SPI_FLASH_READ_MODE_MIN SPI_FLASH_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); 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. * 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. * 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); 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 #ifdef __cplusplus

View File

@@ -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; 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) 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) { 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, .clock_conf = clock_cfg->clock_reg_val,
.cs_hold = cfg->cs_hold, .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); ESP_EARLY_LOGD(TAG, "extra_dummy: %d", data_out->extra_dummy);
return ESP_OK; return ESP_OK;

View File

@@ -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); spi_flash_ll_set_clock(dev, &ctx->clock_conf);
int cs_hold = ctx->cs_hold; int cs_hold = ctx->cs_hold;
spi_flash_ll_set_hold(dev, 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; return ESP_OK;
} }
@@ -78,7 +92,7 @@ esp_err_t spi_flash_hal_configure_host_io_mode(
return ESP_ERR_NOT_SUPPORTED; 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: * In S3 ROM, extra bits than 24-bit are used to indicate requirements of M7-M0:
* - 24: normal transactions * - 24: normal transactions

View File

@@ -36,7 +36,7 @@ bool spi_flash_hal_gpspi_supports_direct_read(spi_flash_host_inst_t *host, const
return true; 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); spi_dev_t *dev = get_spi_dev(host);
return spi_flash_ll_host_idle(dev); return spi_flash_ll_host_idle(dev);

View File

@@ -13,30 +13,33 @@
// limitations under the License. // limitations under the License.
#include "sdkconfig.h" #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 #ifndef CONFIG_SPI_FLASH_ROM_IMPL
// HAL for // HAL for
// - MEMSPI // - MEMSPI
// - SPI1~3 on ESP32 // - SPI1~3 on ESP32/S2/S3/C3
// The common part is in spi_flash_hal_common.inc // 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) void spi_flash_hal_erase_chip(spi_flash_host_inst_t *host)
{ {
spi_dev_t *dev = get_spi_dev(host); spi_dev_t *dev = get_spi_dev(host);
spi_flash_ll_erase_chip(dev); 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); host->driver->poll_cmd_done(host);
#endif
} }
// Only support 24bit address // 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_addr_bitlen(dev, 24);
spi_flash_ll_set_address(dev, start_address & ADDRESS_MASK_24BIT); spi_flash_ll_set_address(dev, start_address & ADDRESS_MASK_24BIT);
spi_flash_ll_erase_sector(dev); 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); host->driver->poll_cmd_done(host);
#endif
} }
// Only support 24bit address // 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_addr_bitlen(dev, 24);
spi_flash_ll_set_address(dev, start_address & ADDRESS_MASK_24BIT); spi_flash_ll_set_address(dev, start_address & ADDRESS_MASK_24BIT);
spi_flash_ll_erase_block(dev); 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); host->driver->poll_cmd_done(host);
#endif
} }
// Only support 24bit address // 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; 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); 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 // Not clear if this is necessary, or only necessary if
// chip->spi == SPI1. But probably doesn't hurt... // chip->spi == SPI1. But probably doesn't hurt...
if ((void*) dev == spi_flash_ll_get_hw(SPI_HOST)) { if ((void*) dev == spi_flash_ll_get_hw(SPI_HOST)) {
#if CONFIG_IDF_TARGET_ESP32 #if CONFIG_IDF_TARGET_ESP32
idle &= spi_flash_ll_host_idle(&SPI0); status &= spi_flash_ll_host_idle(&SPI0);
#else #elif CONFIG_IDF_TARGET_ESP32S2
idle &= spi_flash_ll_host_idle(&SPIMEM0); status &= spi_flash_ll_host_idle(&SPIMEM0);
#endif #endif
} }
return idle; //status and sus_status should be mutual exclusion
return (status | sus_status);
} }
#endif // !CONFIG_SPI_FLASH_ROM_IMPL #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
}

View File

@@ -125,6 +125,13 @@
/*-------------------------- COEXISTENCE HARDWARE PTI CAPS -------------------------------*/ /*-------------------------- COEXISTENCE HARDWARE PTI CAPS -------------------------------*/
#define SOC_COEX_HW_PTI (1) #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 ----------------------------*/ /*-------------------------- Power Management CAPS ----------------------------*/
#define SOC_PM_SUPPORT_WIFI_WAKEUP (1) #define SOC_PM_SUPPORT_WIFI_WAKEUP (1)

View File

@@ -296,6 +296,10 @@
/*-------------------------- WI-FI HARDWARE TSF CAPS -------------------------------*/ /*-------------------------- WI-FI HARDWARE TSF CAPS -------------------------------*/
#define SOC_WIFI_HW_TSF (1) #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 ---------------------------*/ /*-------------------------- Power Management CAPS ---------------------------*/
#define SOC_PM_SUPPORT_EXT_WAKEUP (1) #define SOC_PM_SUPPORT_EXT_WAKEUP (1)

View File

@@ -699,6 +699,7 @@ typedef volatile struct spi_mem_dev_s {
uint32_t val; uint32_t val;
} date; } date;
} spi_mem_dev_t; } 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 SPIMEM0;
extern spi_mem_dev_t SPIMEM1; extern spi_mem_dev_t SPIMEM1;
#ifdef __cplusplus #ifdef __cplusplus

View File

@@ -167,5 +167,11 @@
/*-------------------------- WI-FI HARDWARE TSF CAPS -------------------------------*/ /*-------------------------- WI-FI HARDWARE TSF CAPS -------------------------------*/
#define SOC_WIFI_HW_TSF (1) #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 -------------------------------*/ /*-------------------------- COEXISTENCE HARDWARE PTI CAPS -------------------------------*/
#define SOC_COEX_HW_PTI (1) #define SOC_COEX_HW_PTI (1)

View File

@@ -124,7 +124,6 @@ menu "SPI Flash driver"
config SPI_FLASH_YIELD_DURING_ERASE config SPI_FLASH_YIELD_DURING_ERASE
bool "Enables yield operation during flash erase" bool "Enables yield operation during flash erase"
default y default y
depends on SPI_FLASH_USE_LEGACY_IMPL
help help
This allows to yield the CPUs between erase commands. This allows to yield the CPUs between erase commands.
Prevents starvation of other tasks. Prevents starvation of other tasks.
@@ -144,6 +143,17 @@ menu "SPI Flash driver"
help help
Defines how many ticks will be before returning to continue a erasing. 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 config SPI_FLASH_WRITE_CHUNK_SIZE
int "Flash write chunk size" int "Flash write chunk size"
default 8192 default 8192

View File

@@ -22,6 +22,7 @@
#include "esp_log.h" #include "esp_log.h"
#include "sdkconfig.h" #include "sdkconfig.h"
#include "esp_flash_internal.h" #include "esp_flash_internal.h"
#include "spi_flash_defs.h"
static const char TAG[] = "spi_flash"; static const char TAG[] = "spi_flash";
@@ -173,9 +174,6 @@ bool esp_flash_chip_driver_initialized(const esp_flash_t *chip)
return true; 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) esp_err_t IRAM_ATTR esp_flash_init(esp_flash_t *chip)
{ {
// Chip init flow // 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 #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 #ifndef CONFIG_SPI_FLASH_USE_LEGACY_IMPL
esp_err_t esp_flash_app_disable_protect(bool disable) esp_err_t esp_flash_app_disable_protect(bool disable)
{ {

View File

@@ -90,6 +90,7 @@ __attribute__((unused)) static const char TAG[] = "spi_flash";
} }
#elif CONFIG_IDF_TARGET_ESP32C3 #elif CONFIG_IDF_TARGET_ESP32C3
#include "esp32c3/rom/efuse.h" #include "esp32c3/rom/efuse.h"
#if !CONFIG_SPI_FLASH_AUTO_SUSPEND
#define ESP_FLASH_HOST_CONFIG_DEFAULT() (memspi_host_config_t){ \ #define ESP_FLASH_HOST_CONFIG_DEFAULT() (memspi_host_config_t){ \
.host_id = SPI_HOST,\ .host_id = SPI_HOST,\
.speed = DEFAULT_FLASH_SPEED, \ .speed = DEFAULT_FLASH_SPEED, \
@@ -97,6 +98,16 @@ __attribute__((unused)) static const char TAG[] = "spi_flash";
.iomux = true, \ .iomux = true, \
.input_delay_ns = 0,\ .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 #endif
@@ -240,6 +251,7 @@ static DRAM_ATTR esp_flash_t default_chip = {
.os_func = &esp_flash_noos_functions, .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) esp_err_t esp_flash_init_default_chip(void)
{ {
const esp_rom_spiflash_chip_t *legacy_chip = &g_rom_flashchip; 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; 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: 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 // 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); 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; default_chip.size = legacy_chip->chip_size;
esp_flash_default_chip = &default_chip; 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; return ESP_OK;
} }

View File

@@ -31,11 +31,13 @@
.write_data_slicer = memspi_host_write_data_slicer, \ .write_data_slicer = memspi_host_write_data_slicer, \
.read = spi_flash_hal_read, \ .read = spi_flash_hal_read, \
.read_data_slicer = memspi_host_read_data_slicer, \ .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, \ .configure_host_io_mode = spi_flash_hal_configure_host_io_mode, \
.poll_cmd_done = spi_flash_hal_poll_cmd_done, \ .poll_cmd_done = spi_flash_hal_poll_cmd_done, \
.flush_cache = memspi_host_flush_cache, \ .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 /// 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); 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);

View File

@@ -186,6 +186,8 @@ struct spi_flash_chip_t {
/** Yield to other tasks. Called during erase operations. */ /** Yield to other tasks. Called during erase operations. */
esp_err_t (*yield)(esp_flash_t *chip, uint32_t wip); 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 /* Pointer to an array of pointers to all known drivers for flash chips. This array is used

View File

@@ -255,24 +255,6 @@ extern const spi_flash_chip_t esp_flash_chip_generic;
* Utilities * 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. /// 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); 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); 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 /// Default timeout configuration used by most chips
const flash_chip_op_timeout_t spi_flash_chip_generic_timeout; const flash_chip_op_timeout_t spi_flash_chip_generic_timeout;

View File

@@ -37,7 +37,7 @@ esp_err_t spi_flash_hal_gpspi_configure_host_io_mode(
esp_flash_io_mode_t 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_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 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_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); 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, .write_data_slicer = memspi_host_write_data_slicer,
.read = spi_flash_hal_gpspi_read, .read = spi_flash_hal_gpspi_read,
.read_data_slicer = memspi_host_read_data_slicer, .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, .configure_host_io_mode = spi_flash_hal_gpspi_configure_host_io_mode,
.poll_cmd_done = spi_flash_hal_gpspi_poll_cmd_done, .poll_cmd_done = spi_flash_hal_gpspi_poll_cmd_done,
.flush_cache = NULL, .flush_cache = NULL,
.check_suspend = memspi_host_cb_check_suspend, .resume = spi_flash_hal_resume,
.suspend = spi_flash_hal_suspend,
}; };
#endif #endif

View File

@@ -47,8 +47,8 @@
#define CMD_LARGE_BLOCK_ERASE_4B 0xDC /* 64KB block erase command */ #define CMD_LARGE_BLOCK_ERASE_4B 0xDC /* 64KB block erase command */
#define CMD_PROGRAM_PAGE 0x02 #define CMD_PROGRAM_PAGE 0x02
#define CMD_PROGRAM_PAGE_4B 0x12 #define CMD_PROGRAM_PAGE_4B 0x12
#define CMD_SUSPEND 0x75 #define CMD_SUSPEND 0x75
#define CMD_RESUME 0x7A #define CMD_RESUME 0x7A
#define CMD_RST_EN 0x66 #define CMD_RST_EN 0x66
#define CMD_RST_DEV 0x99 #define CMD_RST_DEV 0x99

View File

@@ -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); 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. * 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. * 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); 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 ///Slowest io mode supported by ESP32, currently SlowRd
#define SPI_FLASH_READ_MODE_MIN SPI_FLASH_SLOWRD #define SPI_FLASH_READ_MODE_MIN SPI_FLASH_SLOWRD

View File

@@ -111,4 +111,5 @@ const spi_flash_chip_t esp_flash_chip_gd = {
.read_reg = spi_flash_chip_generic_read_reg, .read_reg = spi_flash_chip_generic_read_reg,
.yield = spi_flash_chip_generic_yield, .yield = spi_flash_chip_generic_yield,
.sus_setup = spi_flash_chip_generic_suspend_cmd_conf,
}; };

View File

@@ -301,22 +301,6 @@ esp_err_t spi_flash_chip_generic_get_write_protect(esp_flash_t *chip, bool *out_
return err; 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) 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); 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; uint8_t status = 0;
const int interval = CHIP_WAIT_IDLE_INTERVAL_US; const int interval = CHIP_WAIT_IDLE_INTERVAL_US;
while (timeout_us > 0) { 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 HOST_DELAY_INTERVAL_US > 0
if (err != ESP_OK) { if (timeout_us > 1) {
return err; 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; 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) { if (err != ESP_OK) {
return err; return err;
} }
@@ -511,6 +500,7 @@ const spi_flash_chip_t esp_flash_chip_generic = {
.read_reg = spi_flash_chip_generic_read_reg, .read_reg = spi_flash_chip_generic_read_reg,
.yield = spi_flash_chip_generic_yield, .yield = spi_flash_chip_generic_yield,
.sus_setup = spi_flash_chip_generic_suspend_cmd_conf,
}; };
#ifndef CONFIG_SPI_FLASH_ROM_IMPL #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 #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);
}

View File

@@ -140,6 +140,18 @@ esp_err_t spi_flash_chip_winbond_erase_block(esp_flash_t *chip, uint32_t start_a
return err; 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"; static const char chip_name[] = "winbond";
// The issi chip can use the functions for generic chips except from set read mode and probe, // 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, .read_reg = spi_flash_chip_generic_read_reg,
.yield = spi_flash_chip_generic_yield, .yield = spi_flash_chip_generic_yield,
.sus_setup = spi_flash_chip_generic_suspend_cmd_conf,
}; };

View File

@@ -24,6 +24,14 @@
#include "esp_rom_sys.h" #include "esp_rom_sys.h"
#include "esp_timer.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 #define FUNC_SPI 1
static uint8_t sector_buf[4096]; 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("SPI flash erase large region", test_erase_large_region);
FLASH_TEST_CASE_3("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) static void test_write_protection(const esp_partition_t* part)
{ {
esp_flash_t* chip = part->flash_chip; esp_flash_t* chip = part->flash_chip;