Merge branch 'feature/flash_software_resume_v5.4' into 'release/v5.4'

feat(spi_flash): Add config for adding auto check status after suspend to improve performance (backport v5.4)

See merge request espressif/esp-idf!36525
This commit is contained in:
morris 2025-02-20 10:52:17 +08:00
commit cf392937b6
19 changed files with 180 additions and 108 deletions

View File

@ -18,7 +18,7 @@
#include <sys/param.h> // For MIN/MAX
#include <stdbool.h>
#include <string.h>
#include "hal/misc.h"
#include "soc/spi_periph.h"
#include "hal/spi_types.h"
#include "hal/spi_flash_types.h"
@ -144,7 +144,7 @@ static inline void spimem_flash_ll_auto_resume_init(spi_mem_dev_t *dev, bool aut
*/
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;
HAL_FORCE_MODIFY_U32_REG_FIELD(dev->flash_sus_cmd, flash_pes_command, sus_cmd);
}
/**
@ -156,7 +156,7 @@ static inline void spimem_flash_ll_suspend_cmd_setup(spi_mem_dev_t *dev, uint32_
*/
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;
HAL_FORCE_MODIFY_U32_REG_FIELD(dev->flash_sus_cmd, flash_per_command, res_cmd);
}
/**
@ -168,7 +168,7 @@ static inline void spimem_flash_ll_resume_cmd_setup(spi_mem_dev_t *dev, uint32_t
*/
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;
HAL_FORCE_MODIFY_U32_REG_FIELD(dev->flash_sus_cmd, wait_pesr_command, pesr_cmd);
}
/**
@ -205,7 +205,7 @@ static inline void spimem_flash_ll_res_check_sus_setup(spi_mem_dev_t *dev, bool
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;
HAL_FORCE_MODIFY_U32_REG_FIELD(dev->flash_sus_ctrl, pesr_end_msk, sus_conf);
}
/**
@ -236,13 +236,14 @@ static inline void spimem_flash_set_cs_hold_delay(spi_mem_dev_t *dev, uint32_t c
* Initialize auto wait idle mode
*
* @param dev Beginning address of the peripheral registers.
* @param auto_waiti Enable/disable auto wait-idle function
* @param per_waiti Enable wait-idle with time delay function after resume.
* @param pes_waiti Enable wait-idle with time delay function after suspend.
*/
static inline void spimem_flash_ll_auto_wait_idle_init(spi_mem_dev_t *dev, bool auto_waiti)
static inline void spimem_flash_ll_auto_wait_idle_init(spi_mem_dev_t *dev, bool per_waiti, bool pes_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;
HAL_FORCE_MODIFY_U32_REG_FIELD(dev->flash_waiti_ctrl, waiti_cmd, 0x05);
dev->flash_sus_ctrl.flash_per_wait_en = per_waiti;
dev->flash_sus_ctrl.flash_pes_wait_en = pes_waiti;
}
/**
@ -518,11 +519,8 @@ static inline void spimem_flash_ll_set_mosi_bitlen(spi_mem_dev_t *dev, uint32_t
static inline void spimem_flash_ll_set_command(spi_mem_dev_t *dev, uint32_t command, uint32_t bitlen)
{
dev->user.usr_command = 1;
typeof(dev->user2) user2 = {
.usr_command_value = command,
.usr_command_bitlen = (bitlen - 1),
};
dev->user2.val = user2.val;
HAL_FORCE_MODIFY_U32_REG_FIELD(dev->user2, usr_command_value, command);
HAL_FORCE_MODIFY_U32_REG_FIELD(dev->user2, usr_command_bitlen, (bitlen - 1));
}
/**

View File

@ -238,13 +238,14 @@ static inline void spimem_flash_set_cs_hold_delay(spi_mem_dev_t *dev, uint32_t c
* Initialize auto wait idle mode
*
* @param dev Beginning address of the peripheral registers.
* @param auto_waiti Enable/disable auto wait-idle function
* @param per_waiti Enable wait-idle with time delay function after resume.
* @param pes_waiti Enable wait-idle with time delay function after suspend.
*/
static inline void spimem_flash_ll_auto_wait_idle_init(spi_mem_dev_t *dev, bool auto_waiti)
static inline void spimem_flash_ll_auto_wait_idle_init(spi_mem_dev_t *dev, bool per_waiti, bool pes_waiti)
{
HAL_FORCE_MODIFY_U32_REG_FIELD(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;
dev->flash_sus_ctrl.flash_per_wait_en = per_waiti;
dev->flash_sus_ctrl.flash_pes_wait_en = pes_waiti;
}
/**
@ -520,11 +521,8 @@ static inline void spimem_flash_ll_set_mosi_bitlen(spi_mem_dev_t *dev, uint32_t
static inline void spimem_flash_ll_set_command(spi_mem_dev_t *dev, uint32_t command, uint32_t bitlen)
{
dev->user.usr_command = 1;
typeof(dev->user2) user2 = {
.usr_command_value = command,
.usr_command_bitlen = (bitlen - 1),
};
dev->user2.val = user2.val;
HAL_FORCE_MODIFY_U32_REG_FIELD(dev->user2, usr_command_value, command);
HAL_FORCE_MODIFY_U32_REG_FIELD(dev->user2, usr_command_bitlen, (bitlen - 1));
}
/**

View File

@ -243,13 +243,14 @@ static inline void spimem_flash_set_cs_hold_delay(spi_mem_dev_t *dev, uint32_t c
* Initialize auto wait idle mode
*
* @param dev Beginning address of the peripheral registers.
* @param auto_waiti Enable/disable auto wait-idle function
* @param per_waiti Enable wait-idle with time delay function after resume.
* @param pes_waiti Enable wait-idle with time delay function after suspend.
*/
static inline void spimem_flash_ll_auto_wait_idle_init(spi_mem_dev_t *dev, bool auto_waiti)
static inline void spimem_flash_ll_auto_wait_idle_init(spi_mem_dev_t *dev, bool per_waiti, bool pes_waiti)
{
HAL_FORCE_MODIFY_U32_REG_FIELD(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;
dev->flash_sus_ctrl.flash_per_wait_en = per_waiti;
dev->flash_sus_ctrl.flash_pes_wait_en = pes_waiti;
}
/**
@ -530,11 +531,8 @@ static inline void spimem_flash_ll_set_mosi_bitlen(spi_mem_dev_t *dev, uint32_t
static inline void spimem_flash_ll_set_command(spi_mem_dev_t *dev, uint32_t command, uint32_t bitlen)
{
dev->user.usr_command = 1;
typeof(dev->user2) user2 = {
.usr_command_value = command,
.usr_command_bitlen = (bitlen - 1),
};
dev->user2.val = user2.val;
HAL_FORCE_MODIFY_U32_REG_FIELD(dev->user2, usr_command_value, command);
HAL_FORCE_MODIFY_U32_REG_FIELD(dev->user2, usr_command_bitlen, (bitlen - 1));
}
/**

View File

@ -239,13 +239,14 @@ static inline void spimem_flash_set_cs_hold_delay(spi_mem_dev_t *dev, uint32_t c
* Initialize auto wait idle mode
*
* @param dev Beginning address of the peripheral registers.
* @param auto_waiti Enable/disable auto wait-idle function
* @param per_waiti Enable wait-idle with time delay function after resume.
* @param pes_waiti Enable wait-idle with time delay function after suspend.
*/
static inline void spimem_flash_ll_auto_wait_idle_init(spi_mem_dev_t *dev, bool auto_waiti)
static inline void spimem_flash_ll_auto_wait_idle_init(spi_mem_dev_t *dev, bool per_waiti, bool pes_waiti)
{
HAL_FORCE_MODIFY_U32_REG_FIELD(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;
dev->flash_sus_ctrl.flash_per_wait_en = per_waiti;
dev->flash_sus_ctrl.flash_pes_wait_en = pes_waiti;
}
/**
@ -521,11 +522,8 @@ static inline void spimem_flash_ll_set_mosi_bitlen(spi_mem_dev_t *dev, uint32_t
static inline void spimem_flash_ll_set_command(spi_mem_dev_t *dev, uint32_t command, uint32_t bitlen)
{
dev->user.usr_command = 1;
typeof(dev->user2) user2 = {
.usr_command_value = command,
.usr_command_bitlen = (bitlen - 1),
};
dev->user2.val = user2.val;
HAL_FORCE_MODIFY_U32_REG_FIELD(dev->user2, usr_command_value, command);
HAL_FORCE_MODIFY_U32_REG_FIELD(dev->user2, usr_command_bitlen, (bitlen - 1));
}
/**
@ -561,7 +559,7 @@ static inline void spimem_flash_ll_set_addr_bitlen(spi_mem_dev_t *dev, uint32_t
static inline void spimem_flash_ll_set_extra_address(spi_mem_dev_t *dev, uint32_t extra_addr)
{
dev->cache_fctrl.usr_addr_4byte = 0;
dev->rd_status.wb_mode = extra_addr;
HAL_FORCE_MODIFY_U32_REG_FIELD(dev->rd_status, wb_mode, extra_addr);
}
/**

View File

@ -242,13 +242,14 @@ static inline void spimem_flash_set_cs_hold_delay(spi_mem_dev_t *dev, uint32_t c
* Initialize auto wait idle mode
*
* @param dev Beginning address of the peripheral registers.
* @param auto_waiti Enable/disable auto wait-idle function
* @param per_waiti Enable wait-idle with time delay function after resume.
* @param pes_waiti Enable wait-idle with time delay function after suspend.
*/
static inline void spimem_flash_ll_auto_wait_idle_init(spi_mem_dev_t *dev, bool auto_waiti)
static inline void spimem_flash_ll_auto_wait_idle_init(spi_mem_dev_t *dev, bool per_waiti, bool pes_waiti)
{
HAL_FORCE_MODIFY_U32_REG_FIELD(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;
dev->flash_sus_ctrl.flash_per_wait_en = per_waiti;
dev->flash_sus_ctrl.flash_pes_wait_en = pes_waiti;
}
/**
@ -523,11 +524,8 @@ static inline void spimem_flash_ll_set_mosi_bitlen(spi_mem_dev_t *dev, uint32_t
static inline void spimem_flash_ll_set_command(spi_mem_dev_t *dev, uint32_t command, uint32_t bitlen)
{
dev->user.usr_command = 1;
typeof(dev->user2) user2 = {
.usr_command_value = command,
.usr_command_bitlen = (bitlen - 1),
};
dev->user2.val = user2.val;
HAL_FORCE_MODIFY_U32_REG_FIELD(dev->user2, usr_command_value, command);
HAL_FORCE_MODIFY_U32_REG_FIELD(dev->user2, usr_command_bitlen, (bitlen - 1));
}
/**
@ -563,7 +561,7 @@ static inline void spimem_flash_ll_set_addr_bitlen(spi_mem_dev_t *dev, uint32_t
static inline void spimem_flash_ll_set_extra_address(spi_mem_dev_t *dev, uint32_t extra_addr)
{
dev->cache_fctrl.cache_usr_addr_4byte = 0;
dev->rd_status.wb_mode = extra_addr;
HAL_FORCE_MODIFY_U32_REG_FIELD(dev->rd_status, wb_mode, extra_addr);
}
/**

View File

@ -240,13 +240,14 @@ static inline void spimem_flash_set_cs_hold_delay(spi_mem_dev_t *dev, uint32_t c
* Initialize auto wait idle mode
*
* @param dev Beginning address of the peripheral registers.
* @param auto_waiti Enable/disable auto wait-idle function
* @param per_waiti Enable wait-idle with time delay function after resume.
* @param pes_waiti Enable wait-idle with time delay function after suspend.
*/
static inline void spimem_flash_ll_auto_wait_idle_init(spi_mem_dev_t *dev, bool auto_waiti)
static inline void spimem_flash_ll_auto_wait_idle_init(spi_mem_dev_t *dev, bool per_waiti, bool pes_waiti)
{
HAL_FORCE_MODIFY_U32_REG_FIELD(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;
dev->flash_sus_ctrl.flash_per_wait_en = per_waiti;
dev->flash_sus_ctrl.flash_pes_wait_en = pes_waiti;
}
/**
@ -542,11 +543,8 @@ static inline void spimem_flash_ll_set_mosi_bitlen(spi_mem_dev_t *dev, uint32_t
static inline void spimem_flash_ll_set_command(spi_mem_dev_t *dev, uint32_t command, uint32_t bitlen)
{
dev->user.usr_command = 1;
typeof(dev->user2) user2 = {
.usr_command_value = command,
.usr_command_bitlen = (bitlen - 1),
};
dev->user2.val = user2.val;
HAL_FORCE_MODIFY_U32_REG_FIELD(dev->user2, usr_command_value, command);
HAL_FORCE_MODIFY_U32_REG_FIELD(dev->user2, usr_command_bitlen, (bitlen - 1));
}
/**
@ -582,7 +580,7 @@ static inline void spimem_flash_ll_set_addr_bitlen(spi_mem_dev_t *dev, uint32_t
static inline void spimem_flash_ll_set_extra_address(spi_mem_dev_t *dev, uint32_t extra_addr)
{
dev->cache_fctrl.usr_addr_4byte = 0;
dev->rd_status.wb_mode = extra_addr;
HAL_FORCE_MODIFY_U32_REG_FIELD(dev->rd_status, wb_mode, extra_addr);
}
/**

View File

@ -245,13 +245,14 @@ static inline void spimem_flash_set_cs_hold_delay(spi_mem_dev_t *dev, uint32_t c
* Initialize auto wait idle mode
*
* @param dev Beginning address of the peripheral registers.
* @param auto_waiti Enable/disable auto wait-idle function
* @param per_waiti Enable wait-idle with time delay function after resume.
* @param pes_waiti Enable wait-idle with time delay function after suspend.
*/
static inline void spimem_flash_ll_auto_wait_idle_init(spi_mem_dev_t *dev, bool auto_waiti)
static inline void spimem_flash_ll_auto_wait_idle_init(spi_mem_dev_t *dev, bool per_waiti, bool pes_waiti)
{
HAL_FORCE_MODIFY_U32_REG_FIELD(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;
dev->flash_sus_ctrl.flash_per_wait_en = per_waiti;
dev->flash_sus_ctrl.flash_pes_wait_en = pes_waiti;
}
/**
@ -531,11 +532,8 @@ static inline void spimem_flash_ll_set_mosi_bitlen(spi_mem_dev_t *dev, uint32_t
static inline void spimem_flash_ll_set_command(spi_mem_dev_t *dev, uint32_t command, uint32_t bitlen)
{
dev->user.usr_command = 1;
typeof(dev->user2) user2 = {
.usr_command_value = command,
.usr_command_bitlen = (bitlen - 1),
};
dev->user2.val = user2.val;
HAL_FORCE_MODIFY_U32_REG_FIELD(dev->user2, usr_command_value, command);
HAL_FORCE_MODIFY_U32_REG_FIELD(dev->user2, usr_command_bitlen, (bitlen - 1));
}
/**

View File

@ -209,12 +209,13 @@ static inline void spimem_flash_ll_res_check_sus_setup(spi_mem_dev_t *dev, bool
* Initialize auto wait idle mode
*
* @param dev Beginning address of the peripheral registers.
* @param auto_waiti Enable/disable auto wait-idle function
* @param per_waiti Enable wait-idle with time delay function after resume.
* @param pes_waiti Enable wait-idle with time delay function after suspend.
*/
static inline void spimem_flash_ll_auto_wait_idle_init(spi_mem_dev_t *dev, bool auto_waiti)
static inline void spimem_flash_ll_auto_wait_idle_init(spi_mem_dev_t *dev, bool per_waiti, bool pes_waiti)
{
HAL_FORCE_MODIFY_U32_REG_FIELD(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.
HAL_FORCE_MODIFY_U32_REG_FIELD(dev->flash_waiti_ctrl, waiti_cmd, 0x05);
dev->flash_waiti_ctrl.waiti_en = (per_waiti | pes_waiti); // enable auto wait-idle function.
}
/**
@ -460,11 +461,8 @@ static inline void spimem_flash_ll_set_mosi_bitlen(spi_mem_dev_t *dev, uint32_t
static inline void spimem_flash_ll_set_command(spi_mem_dev_t *dev, uint32_t command, uint32_t bitlen)
{
dev->user.usr_command = 1;
typeof(dev->user2) user2 = {
.usr_command_value = command,
.usr_command_bitlen = (bitlen - 1),
};
dev->user2.val = user2.val;
HAL_FORCE_MODIFY_U32_REG_FIELD(dev->user2, usr_command_value, command);
HAL_FORCE_MODIFY_U32_REG_FIELD(dev->user2, usr_command_bitlen, (bitlen - 1));
}
/**

View File

@ -264,14 +264,15 @@ static inline uint32_t spimem_flash_ll_get_tsus_unit_in_cycles(spi_mem_dev_t *de
* Initialize auto wait idle mode
*
* @param dev Beginning address of the peripheral registers.
* @param auto_waiti Enable/disable auto wait-idle function
* @param per_waiti Enable wait-idle with time delay function after resume.
* @param pes_waiti Enable wait-idle with time delay function after suspend.
*/
static inline void spimem_flash_ll_auto_wait_idle_init(spi_mem_dev_t *dev, bool auto_waiti)
static inline void spimem_flash_ll_auto_wait_idle_init(spi_mem_dev_t *dev, bool per_waiti, bool pes_waiti)
{
HAL_FORCE_MODIFY_U32_REG_FIELD(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.
dev->flash_sus_cmd.flash_per_wait_en = 1;
dev->flash_sus_cmd.flash_pes_wait_en = 1;
HAL_FORCE_MODIFY_U32_REG_FIELD(dev->flash_waiti_ctrl, waiti_cmd, 0x05);
dev->flash_waiti_ctrl.waiti_en = (per_waiti | pes_waiti); // enable auto wait-idle function.
dev->flash_sus_cmd.flash_per_wait_en = per_waiti;
dev->flash_sus_cmd.flash_pes_wait_en = pes_waiti;
}
/**
@ -538,11 +539,8 @@ static inline void spimem_flash_ll_set_mosi_bitlen(spi_mem_dev_t *dev, uint32_t
static inline void spimem_flash_ll_set_command(spi_mem_dev_t *dev, uint32_t command, uint32_t bitlen)
{
dev->user.usr_command = 1;
typeof(dev->user2) user2 = {
.usr_command_value = command,
.usr_command_bitlen = (bitlen - 1),
};
dev->user2.val = user2.val;
HAL_FORCE_MODIFY_U32_REG_FIELD(dev->user2, usr_command_value, command);
HAL_FORCE_MODIFY_U32_REG_FIELD(dev->user2, usr_command_bitlen, (bitlen - 1));
}
/**

View File

@ -1,5 +1,5 @@
/*
* SPDX-FileCopyrightText: 2010-2022 Espressif Systems (Shanghai) CO LTD
* SPDX-FileCopyrightText: 2010-2024 Espressif Systems (Shanghai) CO LTD
*
* SPDX-License-Identifier: Apache-2.0
*/
@ -27,6 +27,10 @@ extern "C" {
#define SPI_FLASH_HAL_MAX_WRITE_BYTES 64
#define SPI_FLASH_HAL_MAX_READ_BYTES 64
/* spi flash state */
#define SPI_FLASH_HAL_STATUS_BUSY BIT0
#define SPI_FLASH_HAL_STATUS_SUSPEND BIT1
/**
* Generic driver context structure for all chips using the SPI peripheral.
* Include this into the HEAD of the driver data for other driver
@ -53,6 +57,7 @@ typedef struct {
#define SPI_FLASH_HOST_CONTEXT_SLICER_FLAG_DTR BIT(0) ///< Slice data according to DTR mode, the address and length must be even (A0=0).
int freq_mhz; /// Flash clock frequency.
uint8_t tsus_val; ///< Tsus value of suspend (us)
bool auto_waiti_pes; ///< True for auto-wait idle after suspend command. False for using time delay.
} spi_flash_hal_context_t;
ESP_STATIC_ASSERT(sizeof(spi_flash_hal_context_t) == 48, "size of spi_flash_hal_context_t incorrect. Please check data compatibility with the ROM");
@ -82,11 +87,12 @@ typedef struct {
int cs_num; ///< Which cs pin is used, 0-(SOC_SPI_PERIPH_CS_NUM-1).
bool auto_sus_en; ///< Auto suspend feature enable bit 1: enable, 0: disable.
bool octal_mode_en; ///< Octal spi flash mode enable bit 1: enable, 0: disable.
bool using_timing_tuning; ///< System exist SPI0/1 timing tuning, using value from system directely if set to 1.
bool using_timing_tuning; ///< System exist SPI0/1 timing tuning, using value from system directly if set to 1.
esp_flash_io_mode_t default_io_mode; ///< Default flash io mode.
int freq_mhz; ///< SPI flash clock speed (MHZ).
int clock_src_freq; ///< SPI flash clock source (MHZ).
uint8_t tsus_val; ///< Tsus value of suspend (us)
uint8_t tsus_val; ///< Tsus value of suspend (us).
bool auto_waiti_pes; ///< True for auto-wait idle after suspend command. False for using time delay.
} spi_flash_hal_config_t;
/**

View File

@ -130,8 +130,13 @@ esp_err_t spi_flash_hal_init(spi_flash_hal_context_t *data_out, const spi_flash_
data_out->flags |= SPI_FLASH_HOST_CONTEXT_FLAG_AUTO_SUSPEND;
data_out->flags |= SPI_FLASH_HOST_CONTEXT_FLAG_AUTO_RESUME;
data_out->tsus_val = cfg->tsus_val;
data_out->auto_waiti_pes = cfg->auto_waiti_pes;
}
#if CONFIG_SPI_FLASH_SOFTWARE_RESUME
data_out->flags &= ~SPI_FLASH_HOST_CONTEXT_FLAG_AUTO_RESUME;
#endif
#if SOC_SPI_MEM_SUPPORT_OPI_MODE
if (cfg->octal_mode_en) {
data_out->flags |= SPI_FLASH_HOST_CONTEXT_FLAG_OCTAL_MODE;

View File

@ -1,5 +1,5 @@
/*
* SPDX-FileCopyrightText: 2015-2023 Espressif Systems (Shanghai) CO LTD
* SPDX-FileCopyrightText: 2015-2024 Espressif Systems (Shanghai) CO LTD
*
* SPDX-License-Identifier: Apache-2.0
*/
@ -145,7 +145,8 @@ 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(SPI1_HOST);
spi_flash_hal_context_t* ctx = (spi_flash_hal_context_t*)host;
spimem_flash_ll_auto_wait_idle_init(dev, true);
bool pes_waiti_delay = ctx->auto_waiti_pes ? false : true;
spimem_flash_ll_auto_wait_idle_init(dev, true, pes_waiti_delay);
if (ctx->freq_mhz == 120) {
spimem_flash_ll_set_wait_idle_dummy_phase(dev, ctx->extra_dummy);
}
@ -159,6 +160,7 @@ void spi_flash_hal_setup_auto_suspend_mode(spi_flash_host_inst_t *host)
spimem_flash_ll_sus_set_spi0_lock_trans(dev, SPIMEM_FLASH_LL_SPI0_MAX_LOCK_VAL_MSPI_TICKS);
#if SOC_SPI_MEM_SUPPORT_CHECK_SUS
spimem_flash_ll_sus_check_sus_setup(dev, true);
spimem_flash_ll_res_check_sus_setup(dev, true);
#endif
}
@ -166,18 +168,16 @@ void spi_flash_hal_setup_auto_resume_mode(spi_flash_host_inst_t *host)
{
spi_mem_dev_t *dev = (spi_mem_dev_t*)spi_flash_ll_get_hw(SPI1_HOST);
spimem_flash_ll_auto_resume_init(dev, true);
#if SOC_SPI_MEM_SUPPORT_CHECK_SUS
spimem_flash_ll_res_check_sus_setup(dev, true);
#endif
}
void spi_flash_hal_disable_auto_suspend_mode(spi_flash_host_inst_t *host)
{
spi_mem_dev_t *dev = (spi_mem_dev_t *)spi_flash_ll_get_hw(SPI1_HOST);
spimem_flash_ll_auto_wait_idle_init(dev, false);
spimem_flash_ll_auto_wait_idle_init(dev, false, 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);
spimem_flash_ll_res_check_sus_setup(dev, false);
#endif
}
@ -185,9 +185,6 @@ void spi_flash_hal_disable_auto_resume_mode(spi_flash_host_inst_t *host)
{
spi_mem_dev_t *dev = (spi_mem_dev_t*)spi_flash_ll_get_hw(SPI1_HOST);
spimem_flash_ll_auto_resume_init(dev, false);
#if SOC_SPI_MEM_SUPPORT_CHECK_SUS
spimem_flash_ll_res_check_sus_setup(dev, false);
#endif
}
#endif // SOC_SPI_MEM_SUPPORT_AUTO_SUSPEND
@ -195,6 +192,7 @@ void spi_flash_hal_resume(spi_flash_host_inst_t *host)
{
#if SOC_SPI_MEM_SUPPORT_SW_SUSPEND
spimem_flash_ll_resume((spi_mem_dev_t*)(((spi_flash_hal_context_t *)host)->spi));
host->driver->poll_cmd_done(host);
#else
abort();
#endif
@ -204,6 +202,7 @@ void spi_flash_hal_suspend(spi_flash_host_inst_t *host)
{
#if SOC_SPI_MEM_SUPPORT_SW_SUSPEND
spimem_flash_ll_suspend((spi_mem_dev_t *)(((spi_flash_hal_context_t *)host)->spi));
host->driver->poll_cmd_done(host);
#else
abort();
#endif

View File

@ -120,6 +120,40 @@ menu "Main Flash configuration"
For new users, DO NOT enable this config.
config SPI_FLASH_SOFTWARE_RESUME
bool "Resume flash program/erase form suspend state by software control"
default n
depends on SPI_FLASH_AUTO_SUSPEND && FREERTOS_UNICORE && IDF_EXPERIMENTAL_FEATURES
help
Enable this config will disable auto-resume from hardware. Thus the software will resume the chip
after any higher priority task/interrupt which suspend the chip. The benefit is that the suspend-resume
will not disturb the higher priority task and interrupt.
This currently is only valid on single core chip.
config SPI_FLASH_DISABLE_SCHEDULER_IN_SUSPEND
bool "Disable task scheduler when suspend is enabled when SPI1 operation is ongoing"
default n
# Only valid on single core because no protection is supported on multi core
depends on SPI_FLASH_AUTO_SUSPEND && FREERTOS_UNICORE
help
Disable freertos task scheduler when CONFIG_SPI_FLASH_AUTO_SUSPEND is enabled.
Thus only interrupt can trigger a suspend. When SPI_FLASH_AUTO_SUSPEND is enabled,
default behavior is not disable the task scheduler, so both interrupt and high priority
task can suspend the erase/program operation. When this option is enabled, task
scheduler is disabled, only interrupt can suspend erase/program operation.
config SPI_FLASH_AUTO_CHECK_SUSPEND_STATUS
bool "Check flash status automatically after flash suspend"
default n
depends on SPI_FLASH_AUTO_SUSPEND
help
Majority flash supports to use flash register to judge if flash suspend status is
done or not. So enable this config, the behavior would use flash register WIP bit to judge
whether suspend is valid instead of waiting for a specific long time, which can save a
lot of time and benefit for performance improvement.
endmenu
endmenu

View File

@ -21,6 +21,7 @@
#include "esp_rom_spiflash.h"
#include "esp_private/esp_clk.h"
#include "esp_spi_flash_counters.h"
#include "esp_check.h"
#if CONFIG_IDF_TARGET_ESP32S2
#include "esp_crypto_lock.h" // for locking flash encryption peripheral
@ -1269,7 +1270,7 @@ esp_err_t IRAM_ATTR esp_flash_write_encrypted(esp_flash_t *chip, uint32_t addres
that share a key (as derived from flash address).
On ESP32-S2 and later, the temporary buffer need to be
seperated into 16-bytes, 32-bytes, 64-bytes(if supported).
separated into 16-bytes, 32-bytes, 64-bytes(if supported).
So, on ESP32-S2 and later, here has a totally different
data prepare implementation.

View File

@ -386,6 +386,10 @@ esp_err_t esp_flash_init_default_chip(void)
cfg.tsus_val = TSUS_VAL_SUSPEND;
#endif // CONFIG_SPI_FLASH_AUTO_SUSPEND
#if CONFIG_SPI_FLASH_AUTO_CHECK_SUSPEND_STATUS
cfg.auto_waiti_pes = true;
#endif
//the host is already initialized, only do init for the data and load it to the host
esp_err_t err = memspi_host_init_pointers(&esp_flash_default_host, &cfg);
if (err != ESP_OK) {

View File

@ -1,5 +1,5 @@
/*
* SPDX-FileCopyrightText: 2015-2023 Espressif Systems (Shanghai) CO LTD
* SPDX-FileCopyrightText: 2015-2024 Espressif Systems (Shanghai) CO LTD
*
* SPDX-License-Identifier: Apache-2.0
*/
@ -204,7 +204,7 @@ esp_err_t esp_flash_erase_chip(esp_flash_t *chip);
* @param start Address to start erasing flash. Must be sector aligned.
* @param len Length of region to erase. Must also be sector aligned.
*
* Sector size is specifyed in chip->drv->sector_size field (typically 4096 bytes.) ESP_ERR_INVALID_ARG will be
* Sector size is specified in chip->drv->sector_size field (typically 4096 bytes.) ESP_ERR_INVALID_ARG will be
* returned if the start & length are not a multiple of this size.
*
* Erase is performed using block (multi-sector) erases where possible (block size is specified in

View File

@ -376,6 +376,7 @@ esp_err_t spi_flash_chip_generic_wait_idle(esp_flash_t *chip, uint32_t timeout_u
uint8_t status = 0;
const int interval = CHIP_WAIT_IDLE_INTERVAL_US;
bool suspend_state = false;
while (timeout_us > 0) {
while (!chip->host->driver->host_status(chip->host) && timeout_us > 0) {
@ -388,6 +389,15 @@ esp_err_t spi_flash_chip_generic_wait_idle(esp_flash_t *chip, uint32_t timeout_u
#endif
}
#if CONFIG_SPI_FLASH_SOFTWARE_RESUME
suspend_state = ((chip->host->driver->host_status(chip->host) & SPI_FLASH_HAL_STATUS_SUSPEND) != 0) ? true : false;
if (suspend_state) {
// Oh! find you are in suspend state
chip->host->driver->resume(chip->host);
}
#endif
uint32_t read;
esp_err_t err = chip->chip_drv->read_reg(chip, SPI_FLASH_REG_STATUS, &read);
if (err != ESP_OK) {
@ -395,7 +405,7 @@ esp_err_t spi_flash_chip_generic_wait_idle(esp_flash_t *chip, uint32_t timeout_u
}
status = read;
if ((status & SR_WIP) == 0) { // Verify write in progress is complete
if ((status & SR_WIP) == 0 && (suspend_state == false)) { // Verify write in progress is complete
if (chip->busy == 1) {
chip->busy = 0;
if ((status & SR_WREN) != 0) { // The previous command is not accepted, leaving the WEL still set.

View File

@ -121,6 +121,20 @@ static IRAM_ATTR esp_err_t spi1_start(void *arg)
//directly disable the cache and interrupts when lock is not used
cache_disable(NULL);
#endif
#if CONFIG_SPI_FLASH_DISABLE_SCHEDULER_IN_SUSPEND
// Disable scheduler
if (xTaskGetSchedulerState() == taskSCHEDULER_RUNNING) {
#ifdef CONFIG_FREERTOS_SMP
//Note: Scheduler suspension behavior changed in FreeRTOS SMP
vTaskPreemptionDisable(NULL);
#else
// Disable scheduler on the current CPU
vTaskSuspendAll();
#endif // CONFIG_FREERTOS_SMP
}
#endif // CONFIG_SPI_FLASH_DISABLE_SCHEDULER_IN_SUSPEND
on_spi_acquired((app_func_arg_t*)arg);
return ret;
}
@ -139,6 +153,18 @@ static IRAM_ATTR esp_err_t spi1_end(void *arg)
#else
cache_enable(NULL);
#endif
#if CONFIG_SPI_FLASH_DISABLE_SCHEDULER_IN_SUSPEND
if (xTaskGetSchedulerState() == taskSCHEDULER_RUNNING) {
#ifdef CONFIG_FREERTOS_SMP
//Note: Scheduler suspension behavior changed in FreeRTOS SMP
vTaskPreemptionEnable(NULL);
#else
xTaskResumeAll();
#endif // CONFIG_FREERTOS_SMP
}
#endif // CONFIG_SPI_FLASH_DISABLE_SCHEDULER_IN_SUSPEND
on_spi_released((app_func_arg_t*)arg);
return ret;
}

View File

@ -10,3 +10,8 @@ set(COMPONENTS main esptool_py)
include($ENV{IDF_PATH}/tools/cmake/project.cmake)
project(test_esp_flash_drv)
message(STATUS "Checking memspi registers are not read-write by half-word")
include($ENV{IDF_PATH}/tools/ci/check_register_rw_half_word.cmake)
check_register_rw_half_word(SOC_MODULES "spi_mem*" "spi1_mem*"
HAL_MODULES "spimem_flash")