Merge branch 'feature/flash_suspend_support' into 'master'

spi_flash: One more step for supporting flash suspend. 1. Support more esp chips 2. Improve real-time performance 3. Make timing more stable

See merge request espressif/esp-idf!22755
This commit is contained in:
C.S.M
2023-05-12 14:16:11 +08:00
48 changed files with 828 additions and 108 deletions

View File

@@ -399,6 +399,16 @@ static inline void spi_flash_ll_set_cs_setup(spi_dev_t *dev, uint32_t cs_setup_t
dev->ctrl2.setup_time = cs_setup_time - 1;
}
/**
* @brief Set lock for SPI0 so that spi0 can request new cache request after a cache transfer.
*
* @param dev Beginning address of the peripheral registers.
*/
static inline void spi_flash_ll_set_pe_bit(spi_dev_t *dev)
{
// Not supported on esp32
}
/**
* Get the spi flash source clock frequency. Used for calculating
* the divider parameters.

View File

@@ -149,6 +149,16 @@ static inline void gpspi_flash_ll_user_start(spi_dev_t *dev)
dev->cmd.usr = 1;
}
/**
* In user mode, it is set to indicate that program/erase operation will be triggered.
*
* @param dev Beginning address of the peripheral registers.
*/
static inline void gpspi_flash_ll_set_pe_bit(spi_dev_t *dev)
{
// Not supported on GPSPI
}
/**
* Check whether the host is idle to perform new commands.
*

View File

@@ -64,6 +64,7 @@ typedef union {
#define spi_flash_ll_set_dummy_out(dev, en, lev) gpspi_flash_ll_set_dummy_out((spi_dev_t*)dev, en, lev)
#define spi_flash_ll_set_hold(dev, hold_n) gpspi_flash_ll_set_hold((spi_dev_t*)dev, hold_n)
#define spi_flash_ll_set_cs_setup(dev, cs_setup_time) gpspi_flash_ll_set_cs_setup((spi_dev_t*)dev, cs_setup_time)
#define spi_flash_ll_set_pe_bit(dev) gpspi_flash_ll_set_pe_bit((spi_dev_t*)dev)
#else
#define spi_flash_ll_reset(dev) spimem_flash_ll_reset((spi_mem_dev_t*)dev)
#define spi_flash_ll_cmd_is_done(dev) spimem_flash_ll_cmd_is_done((spi_mem_dev_t*)dev)
@@ -91,6 +92,7 @@ typedef union {
#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_cs_setup(dev, cs_setup_time) spimem_flash_ll_set_cs_setup((spi_mem_dev_t*)dev, cs_setup_time)
#define spi_flash_ll_set_pe_bit(dev) spimem_flash_ll_set_pe_bit((spi_mem_dev_t*)dev)
#endif

View File

@@ -31,6 +31,7 @@ extern "C" {
#define spimem_flash_ll_hw_get_id(dev) ((dev) == (void*)&SPIMEM1? SPI1_HOST: -1)
#define SPIMEM_FLASH_LL_PERIPHERAL_FREQUENCY_MHZ (60)
#define SPIMEM_FLASH_LL_SPI0_MAX_LOCK_VAL_MSPI_TICKS (0x1f)
typedef typeof(SPIMEM1.clock.val) spimem_flash_ll_clock_reg_t;
@@ -207,6 +208,30 @@ static inline void spimem_flash_ll_set_read_sus_status(spi_mem_dev_t *dev, uint3
dev->flash_sus_ctrl.pesr_end_msk = sus_conf;
}
/**
* Configure the delay after Suspend/Resume
*
* @param dev Beginning address of the peripheral registers.
* @param dly_val delay time
*/
static inline void spimem_flash_ll_set_sus_delay(spi_mem_dev_t *dev, uint32_t dly_val)
{
dev->ctrl1.cs_hold_dly_res = dly_val;
dev->sus_status.flash_pes_dly_128 = 1;
dev->sus_status.flash_per_dly_128 = 1;
}
/**
* Configure the cs hold delay time(used to set the minimum CS high time tSHSL)
*
* @param dev Beginning address of the peripheral registers.
* @param cs_hold_delay cs hold delay time
*/
static inline void spimem_flash_set_cs_hold_delay(spi_mem_dev_t *dev, uint32_t cs_hold_delay)
{
SPIMEM0.ctrl2.cs_hold_delay = cs_hold_delay;
}
/**
* Initialize auto wait idle mode
*
@@ -232,6 +257,35 @@ static inline bool spimem_flash_ll_sus_status(spi_mem_dev_t *dev)
return dev->sus_status.flash_sus;
}
/**
* @brief Set lock for SPI0 so that spi0 can request new cache request after a cache transfer.
*
* @param dev Beginning address of the peripheral registers.
* @param lock_time Lock delay time
*/
static inline void spimem_flash_ll_sus_set_spi0_lock_trans(spi_mem_dev_t *dev, uint32_t lock_time)
{
dev->sus_status.spi0_lock_en = 1;
SPIMEM0.fsm.cspi_lock_delay_time = lock_time;
}
/**
* @brief Get tsus unit values in SPI_CLK cycles
*
* @param dev Beginning address of the peripheral registers.
* @return uint32_t tsus unit values
*/
static inline uint32_t spimem_flash_ll_get_tsus_unit_in_cycles(spi_mem_dev_t *dev)
{
uint32_t tsus_unit = 0;
if (dev->sus_status.flash_pes_dly_128 == 1) {
tsus_unit = 128;
} else {
tsus_unit = 4;
}
return tsus_unit;
}
/**
* Enable/disable write protection for the flash chip.
*
@@ -320,6 +374,17 @@ static inline void spimem_flash_ll_user_start(spi_mem_dev_t *dev)
dev->cmd.usr = 1;
}
/**
* In user mode, it is set to indicate that program/erase operation will be triggered.
* This function is combined with `spimem_flash_ll_user_start`. The pe_bit will be cleared automatically once the operation done.
*
* @param dev Beginning address of the peripheral registers.
*/
static inline void spimem_flash_ll_set_pe_bit(spi_mem_dev_t *dev)
{
dev->cmd.flash_pe = 1;
}
/**
* Check whether the host is idle to perform new commands.
*

View File

@@ -140,6 +140,16 @@ static inline void gpspi_flash_ll_user_start(spi_dev_t *dev)
dev->cmd.usr = 1;
}
/**
* In user mode, it is set to indicate that program/erase operation will be triggered.
*
* @param dev Beginning address of the peripheral registers.
*/
static inline void gpspi_flash_ll_set_pe_bit(spi_dev_t *dev)
{
// Not supported on GPSPI
}
/**
* Set HD pin high when flash work at spi mode.
*

View File

@@ -64,6 +64,7 @@ typedef union {
#define spi_flash_ll_set_dummy_out(dev, en, lev) gpspi_flash_ll_set_dummy_out((spi_dev_t*)dev, en, lev)
#define spi_flash_ll_set_hold(dev, hold_n) gpspi_flash_ll_set_hold((spi_dev_t*)dev, hold_n)
#define spi_flash_ll_set_cs_setup(dev, cs_setup_time) gpspi_flash_ll_set_cs_setup((spi_dev_t*)dev, cs_setup_time)
#define spi_flash_ll_set_pe_bit(dev) gpspi_flash_ll_set_pe_bit((spi_dev_t*)dev)
#else
#define spi_flash_ll_reset(dev) spimem_flash_ll_reset((spi_mem_dev_t*)dev)
#define spi_flash_ll_cmd_is_done(dev) spimem_flash_ll_cmd_is_done((spi_mem_dev_t*)dev)
@@ -91,6 +92,7 @@ typedef union {
#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_cs_setup(dev, cs_setup_time) spimem_flash_ll_set_cs_setup((spi_mem_dev_t*)dev, cs_setup_time)
#define spi_flash_ll_set_pe_bit(dev) spimem_flash_ll_set_pe_bit((spi_mem_dev_t*)dev)
#endif

View File

@@ -32,6 +32,8 @@ extern "C" {
#define spimem_flash_ll_get_hw(host_id) (((host_id)==SPI1_HOST ? &SPIMEM1 : NULL ))
#define spimem_flash_ll_hw_get_id(dev) ((dev) == (void*)&SPIMEM1? SPI1_HOST: -1)
#define SPIMEM_FLASH_LL_SPI0_MAX_LOCK_VAL_MSPI_TICKS (0x1f)
typedef typeof(SPIMEM1.clock.val) spimem_flash_ll_clock_reg_t;
/*------------------------------------------------------------------------------
@@ -207,6 +209,30 @@ static inline void spimem_flash_ll_set_read_sus_status(spi_mem_dev_t *dev, uint3
HAL_FORCE_MODIFY_U32_REG_FIELD(dev->flash_sus_ctrl, pesr_end_msk, sus_conf);
}
/**
* Configure the delay after Suspend/Resume
*
* @param dev Beginning address of the peripheral registers.
* @param dly_val delay time
*/
static inline void spimem_flash_ll_set_sus_delay(spi_mem_dev_t *dev, uint32_t dly_val)
{
dev->ctrl1.cs_hold_dly_res = dly_val;
dev->sus_status.pes_dly_128 = 1;
dev->sus_status.per_dly_128 = 1;
}
/**
* Configure the cs hold delay time(used to set the minimum CS high time tSHSL)
*
* @param dev Beginning address of the peripheral registers.
* @param cs_hold_delay cs hold delay time
*/
static inline void spimem_flash_set_cs_hold_delay(spi_mem_dev_t *dev, uint32_t cs_hold_delay)
{
SPIMEM0.ctrl2.cs_hold_delay = cs_hold_delay;
}
/**
* Initialize auto wait idle mode
*
@@ -232,6 +258,35 @@ static inline bool spimem_flash_ll_sus_status(spi_mem_dev_t *dev)
return dev->sus_status.flash_sus;
}
/**
* @brief Set lock for SPI0 so that spi0 can request new cache request after a cache transfer.
*
* @param dev Beginning address of the peripheral registers.
* @param lock_time Lock delay time
*/
static inline void spimem_flash_ll_sus_set_spi0_lock_trans(spi_mem_dev_t *dev, uint32_t lock_time)
{
dev->sus_status.spi0_lock_en = 1;
SPIMEM0.fsm.cspi_lock_delay_time = lock_time;
}
/**
* @brief Get tsus unit values in SPI_CLK cycles
*
* @param dev Beginning address of the peripheral registers.
* @return uint32_t tsus unit values
*/
static inline uint32_t spimem_flash_ll_get_tsus_unit_in_cycles(spi_mem_dev_t *dev)
{
uint32_t tsus_unit = 0;
if (dev->sus_status.pes_dly_128 == 1) {
tsus_unit = 128;
} else {
tsus_unit = 4;
}
return tsus_unit;
}
/**
* Enable/disable write protection for the flash chip.
*
@@ -320,6 +375,17 @@ static inline void spimem_flash_ll_user_start(spi_mem_dev_t *dev)
dev->cmd.usr = 1;
}
/**
* In user mode, it is set to indicate that program/erase operation will be triggered.
* This function is combined with `spimem_flash_ll_user_start`. The pe_bit will be cleared automatically once the operation done.
*
* @param dev Beginning address of the peripheral registers.
*/
static inline void spimem_flash_ll_set_pe_bit(spi_mem_dev_t *dev)
{
dev->cmd.flash_pe = 1;
}
/**
* Check whether the host is idle to perform new commands.
*

View File

@@ -1,5 +1,5 @@
/*
* SPDX-FileCopyrightText: 2022 Espressif Systems (Shanghai) CO LTD
* SPDX-FileCopyrightText: 2022-2023 Espressif Systems (Shanghai) CO LTD
*
* SPDX-License-Identifier: Apache-2.0
*/
@@ -140,6 +140,16 @@ static inline void gpspi_flash_ll_user_start(spi_dev_t *dev)
dev->cmd.usr = 1;
}
/**
* In user mode, it is set to indicate that program/erase operation will be triggered.
*
* @param dev Beginning address of the peripheral registers.
*/
static inline void gpspi_flash_ll_set_pe_bit(spi_dev_t *dev)
{
// Not supported on GPSPI
}
/**
* Set HD pin high when flash work at spi mode.
*

View File

@@ -1,5 +1,5 @@
/*
* SPDX-FileCopyrightText: 2022 Espressif Systems (Shanghai) CO LTD
* SPDX-FileCopyrightText: 2022-2023 Espressif Systems (Shanghai) CO LTD
*
* SPDX-License-Identifier: Apache-2.0
*/
@@ -65,6 +65,7 @@ typedef union {
#define spi_flash_ll_set_hold(dev, hold_n) gpspi_flash_ll_set_hold((spi_dev_t*)dev, hold_n)
#define spi_flash_ll_set_cs_setup(dev, cs_setup_time) gpspi_flash_ll_set_cs_setup((spi_dev_t*)dev, cs_setup_time)
#define spi_flash_ll_set_extra_address(dev, extra_addr) { /* Not supported on gpspi on ESP32-C6*/ }
#define spi_flash_ll_set_pe_bit(dev) gpspi_flash_ll_set_pe_bit((spi_dev_t*)dev)
#else
#define spi_flash_ll_reset(dev) spimem_flash_ll_reset((spi_mem_dev_t*)dev)
#define spi_flash_ll_cmd_is_done(dev) spimem_flash_ll_cmd_is_done((spi_mem_dev_t*)dev)
@@ -92,6 +93,7 @@ typedef union {
#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_cs_setup(dev, cs_setup_time) spimem_flash_ll_set_cs_setup((spi_mem_dev_t*)dev, cs_setup_time)
#define spi_flash_ll_set_extra_address(dev, extra_addr) spimem_flash_ll_set_extra_address((spi_mem_dev_t*)dev, extra_addr)
#define spi_flash_ll_set_pe_bit(dev) spimem_flash_ll_set_pe_bit((spi_mem_dev_t*)dev)
#endif

View File

@@ -1,5 +1,5 @@
/*
* SPDX-FileCopyrightText: 2022 Espressif Systems (Shanghai) CO LTD
* SPDX-FileCopyrightText: 2022-2023 Espressif Systems (Shanghai) CO LTD
*
* SPDX-License-Identifier: Apache-2.0
*/
@@ -33,6 +33,8 @@ extern "C" {
#define spimem_flash_ll_get_hw(host_id) (((host_id)==SPI1_HOST ? &SPIMEM1 : NULL ))
#define spimem_flash_ll_hw_get_id(dev) ((dev) == (void*)&SPIMEM1? SPI1_HOST: -1)
#define SPIMEM_FLASH_LL_SPI0_MAX_LOCK_VAL_MSPI_TICKS (0x1f)
typedef typeof(SPIMEM1.clock.val) spimem_flash_ll_clock_reg_t;
/*------------------------------------------------------------------------------
@@ -208,6 +210,30 @@ static inline void spimem_flash_ll_set_read_sus_status(spi_mem_dev_t *dev, uint3
HAL_FORCE_MODIFY_U32_REG_FIELD(dev->flash_sus_ctrl, pesr_end_msk, sus_conf);
}
/**
* Configure the delay after Suspend/Resume
*
* @param dev Beginning address of the peripheral registers.
* @param dly_val delay time
*/
static inline void spimem_flash_ll_set_sus_delay(spi_mem_dev_t *dev, uint32_t dly_val)
{
dev->ctrl1.cs_hold_dly_res = dly_val;
dev->sus_status.flash_per_dly_128 = 1;
dev->sus_status.flash_pes_dly_128 = 1;
}
/**
* Configure the cs hold delay time(used to set the minimum CS high time tSHSL)
*
* @param dev Beginning address of the peripheral registers.
* @param cs_hold_delay cs hold delay time
*/
static inline void spimem_flash_set_cs_hold_delay(spi_mem_dev_t *dev, uint32_t cs_hold_delay)
{
SPIMEM0.ctrl2.cs_hold_delay = cs_hold_delay;
}
/**
* Initialize auto wait idle mode
*
@@ -233,6 +259,35 @@ static inline bool spimem_flash_ll_sus_status(spi_mem_dev_t *dev)
return dev->sus_status.flash_sus;
}
/**
* @brief Set lock for SPI0 so that spi0 can request new cache request after a cache transfer.
*
* @param dev Beginning address of the peripheral registers.
* @param lock_time Lock delay time
*/
static inline void spimem_flash_ll_sus_set_spi0_lock_trans(spi_mem_dev_t *dev, uint32_t lock_time)
{
dev->sus_status.spi0_lock_en = 1;
SPIMEM0.fsm.lock_delay_time = lock_time;
}
/**
* @brief Get tsus unit values in SPI_CLK cycles
*
* @param dev Beginning address of the peripheral registers.
* @return uint32_t tsus unit values
*/
static inline uint32_t spimem_flash_ll_get_tsus_unit_in_cycles(spi_mem_dev_t *dev)
{
uint32_t tsus_unit = 0;
if (dev->sus_status.flash_pes_dly_128 == 1) {
tsus_unit = 128;
} else {
tsus_unit = 4;
}
return tsus_unit;
}
/**
* Enable/disable write protection for the flash chip.
*
@@ -321,6 +376,17 @@ static inline void spimem_flash_ll_user_start(spi_mem_dev_t *dev)
dev->cmd.usr = 1;
}
/**
* In user mode, it is set to indicate that program/erase operation will be triggered.
* This function is combined with `spimem_flash_ll_user_start`. The pe_bit will be cleared automatically once the operation done.
*
* @param dev Beginning address of the peripheral registers.
*/
static inline void spimem_flash_ll_set_pe_bit(spi_mem_dev_t *dev)
{
dev->cmd.flash_pe = 1;
}
/**
* Check whether the host is idle to perform new commands.
*

View File

@@ -1,5 +1,5 @@
/*
* SPDX-FileCopyrightText: 2022 Espressif Systems (Shanghai) CO LTD
* SPDX-FileCopyrightText: 2022-2023 Espressif Systems (Shanghai) CO LTD
*
* SPDX-License-Identifier: Apache-2.0
*/
@@ -140,6 +140,16 @@ static inline void gpspi_flash_ll_user_start(spi_dev_t *dev)
dev->cmd.usr = 1;
}
/**
* In user mode, it is set to indicate that program/erase operation will be triggered.
*
* @param dev Beginning address of the peripheral registers.
*/
static inline void gpspi_flash_ll_set_pe_bit(spi_dev_t *dev)
{
// Not supported on GPSPI
}
/**
* Set HD pin high when flash work at spi mode.
*

View File

@@ -1,5 +1,5 @@
/*
* SPDX-FileCopyrightText: 2022 Espressif Systems (Shanghai) CO LTD
* SPDX-FileCopyrightText: 2022-2023 Espressif Systems (Shanghai) CO LTD
*
* SPDX-License-Identifier: Apache-2.0
*/
@@ -65,6 +65,7 @@ typedef union {
#define spi_flash_ll_set_hold(dev, hold_n) gpspi_flash_ll_set_hold((spi_dev_t*)dev, hold_n)
#define spi_flash_ll_set_cs_setup(dev, cs_setup_time) gpspi_flash_ll_set_cs_setup((spi_dev_t*)dev, cs_setup_time)
#define spi_flash_ll_set_extra_address(dev, extra_addr) { /* Not supported on gpspi on ESP32-H2*/ }
#define spi_flash_ll_set_pe_bit(dev) gpspi_flash_ll_set_pe_bit((spi_dev_t*)dev)
#else
#define spi_flash_ll_reset(dev) spimem_flash_ll_reset((spi_mem_dev_t*)dev)
#define spi_flash_ll_cmd_is_done(dev) spimem_flash_ll_cmd_is_done((spi_mem_dev_t*)dev)
@@ -92,6 +93,7 @@ typedef union {
#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_cs_setup(dev, cs_setup_time) spimem_flash_ll_set_cs_setup((spi_mem_dev_t*)dev, cs_setup_time)
#define spi_flash_ll_set_extra_address(dev, extra_addr) spimem_flash_ll_set_extra_address((spi_mem_dev_t*)dev, extra_addr)
#define spi_flash_ll_set_pe_bit(dev) spimem_flash_ll_set_pe_bit((spi_mem_dev_t*)dev)
#endif

View File

@@ -35,6 +35,8 @@ extern "C" {
#define spimem_flash_ll_get_hw(host_id) (((host_id)==SPI1_HOST ? &SPIMEM1 : NULL ))
#define spimem_flash_ll_hw_get_id(dev) ((dev) == (void*)&SPIMEM1? SPI1_HOST: -1)
#define SPIMEM_FLASH_LL_SPI0_MAX_LOCK_VAL_MSPI_TICKS (0x1f)
typedef typeof(SPIMEM1.clock.val) spimem_flash_ll_clock_reg_t;
/*------------------------------------------------------------------------------
@@ -210,6 +212,30 @@ static inline void spimem_flash_ll_set_read_sus_status(spi_mem_dev_t *dev, uint3
HAL_FORCE_MODIFY_U32_REG_FIELD(dev->flash_sus_ctrl, pesr_end_msk, sus_conf);
}
/**
* Configure the delay after Suspend/Resume
*
* @param dev Beginning address of the peripheral registers.
* @param dly_val delay time
*/
static inline void spimem_flash_ll_set_sus_delay(spi_mem_dev_t *dev, uint32_t dly_val)
{
dev->ctrl1.cs_hold_dly_res = dly_val;
dev->sus_status.flash_per_dly_128 = 1;
dev->sus_status.flash_pes_dly_128 = 1;
}
/**
* Configure the cs hold delay time(used to set the minimum CS high time tSHSL)
*
* @param dev Beginning address of the peripheral registers.
* @param cs_hold_delay cs hold delay time
*/
static inline void spimem_flash_set_cs_hold_delay(spi_mem_dev_t *dev, uint32_t cs_hold_delay)
{
SPIMEM0.ctrl2.cs_hold_delay = cs_hold_delay;
}
/**
* Initialize auto wait idle mode
*
@@ -235,6 +261,35 @@ static inline bool spimem_flash_ll_sus_status(spi_mem_dev_t *dev)
return dev->sus_status.flash_sus;
}
/**
* @brief Set lock for SPI0 so that spi0 can request new cache request after a cache transfer.
*
* @param dev Beginning address of the peripheral registers.
* @param lock_time Lock delay time
*/
static inline void spimem_flash_ll_sus_set_spi0_lock_trans(spi_mem_dev_t *dev, uint32_t lock_time)
{
dev->sus_status.spi0_lock_en = 1;
SPIMEM0.fsm.lock_delay_time = lock_time;
}
/**
* @brief Get tsus unit values in SPI_CLK cycles
*
* @param dev Beginning address of the peripheral registers.
* @return uint32_t tsus unit values
*/
static inline uint32_t spimem_flash_ll_get_tsus_unit_in_cycles(spi_mem_dev_t *dev)
{
uint32_t tsus_unit = 0;
if (dev->sus_status.flash_pes_dly_128 == 1) {
tsus_unit = 128;
} else {
tsus_unit = 4;
}
return tsus_unit;
}
/**
* Enable/disable write protection for the flash chip.
*
@@ -323,6 +378,17 @@ static inline void spimem_flash_ll_user_start(spi_mem_dev_t *dev)
dev->cmd.usr = 1;
}
/**
* In user mode, it is set to indicate that program/erase operation will be triggered.
* This function is combined with `spimem_flash_ll_user_start`. The pe_bit will be cleared automatically once the operation done.
*
* @param dev Beginning address of the peripheral registers.
*/
static inline void spimem_flash_ll_set_pe_bit(spi_mem_dev_t *dev)
{
dev->cmd.flash_pe = 1;
}
/**
* Check whether the host is idle to perform new commands.
*

View File

@@ -134,6 +134,16 @@ static inline void gpspi_flash_ll_user_start(spi_dev_t *dev)
dev->cmd.usr = 1;
}
/**
* In user mode, it is set to indicate that program/erase operation will be triggered.
*
* @param dev Beginning address of the peripheral registers.
*/
static inline void gpspi_flash_ll_set_pe_bit(spi_dev_t *dev)
{
// Not supported on GPSPI
}
/**
* Set HD pin high when flash work at spi mode.
*

View File

@@ -65,6 +65,7 @@ typedef union {
#define spi_flash_ll_set_dummy_out(dev, en, lev) gpspi_flash_ll_set_dummy_out((spi_dev_t*)dev, en, lev)
#define spi_flash_ll_set_hold(dev, hold_n) gpspi_flash_ll_set_hold((spi_dev_t*)dev, hold_n)
#define spi_flash_ll_set_cs_setup(dev, cs_setup_time) gpspi_flash_ll_set_cs_setup((spi_dev_t*)dev, cs_setup_time)
#define spi_flash_ll_set_pe_bit(dev) gpspi_flash_ll_set_pe_bit((spi_dev_t*)dev)
#else
@@ -94,6 +95,7 @@ typedef union {
#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_cs_setup(dev, cs_setup_time) spimem_flash_ll_set_cs_setup((spi_mem_dev_t*)dev, cs_setup_time)
#define spi_flash_ll_set_pe_bit(dev) spimem_flash_ll_set_pe_bit((spi_mem_dev_t*)dev)
#endif

View File

@@ -316,6 +316,17 @@ static inline void spimem_flash_ll_user_start(spi_mem_dev_t *dev)
dev->cmd.usr = 1;
}
/**
* In user mode, it is set to indicate that program/erase operation will be triggered.
* This function is combined with `spimem_flash_ll_user_start`. The pe_bit will be cleared automatically once the operation done.
*
* @param dev Beginning address of the peripheral registers.
*/
static inline void spimem_flash_ll_set_pe_bit(spi_mem_dev_t *dev)
{
dev->cmd.flash_pe = 1;
}
/**
* Check whether the host is idle to perform new commands.
*

View File

@@ -145,6 +145,16 @@ static inline void gpspi_flash_ll_user_start(spi_dev_t *dev)
dev->cmd.usr = 1;
}
/**
* In user mode, it is set to indicate that program/erase operation will be triggered.
*
* @param dev Beginning address of the peripheral registers.
*/
static inline void gpspi_flash_ll_set_pe_bit(spi_dev_t *dev)
{
// Not supported on GPSPI
}
/**
* Set HD pin high when flash work at spi mode.
*

View File

@@ -63,6 +63,7 @@ typedef union {
#define spi_flash_ll_set_dummy_out(dev, en, lev) gpspi_flash_ll_set_dummy_out((spi_dev_t*)dev, en, lev)
#define spi_flash_ll_set_hold(dev, hold_n) gpspi_flash_ll_set_hold((spi_dev_t*)dev, hold_n)
#define spi_flash_ll_set_cs_setup(dev, cs_setup_time) gpspi_flash_ll_set_cs_setup((spi_dev_t*)dev, cs_setup_time)
#define spi_flash_ll_set_pe_bit(dev) gpspi_flash_ll_set_pe_bit((spi_dev_t*)dev)
#else
#define spi_flash_ll_reset(dev) spimem_flash_ll_reset((spi_mem_dev_t*)dev)
@@ -91,6 +92,8 @@ typedef union {
#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_cs_setup(dev, cs_setup_time) spimem_flash_ll_set_cs_setup((spi_mem_dev_t*)dev, cs_setup_time)
#define spi_flash_ll_set_pe_bit(dev) spimem_flash_ll_set_pe_bit((spi_mem_dev_t*)dev)
#endif
#ifdef __cplusplus

View File

@@ -32,6 +32,8 @@ extern "C" {
#define spimem_flash_ll_get_hw(host_id) (((host_id)==SPI1_HOST ? &SPIMEM1 : NULL ))
#define spimem_flash_ll_hw_get_id(dev) ((dev) == (void*)&SPIMEM1? SPI1_HOST: -1)
#define SPIMEM_FLASH_LL_SPI0_MAX_LOCK_VAL_MSPI_TICKS (0x1f)
typedef typeof(SPIMEM1.clock.val) spimem_flash_ll_clock_reg_t;
/*------------------------------------------------------------------------------
@@ -120,6 +122,7 @@ static inline void spimem_flash_ll_resume(spi_mem_dev_t *dev)
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; // enable Flash Auto-Suspend.
dev->flash_sus_cmd.pes_per_en = auto_sus;
}
/**
@@ -167,7 +170,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)
{
abort(); //Not support on esp32s3
//Not support on esp32s3
}
/**
@@ -201,7 +204,59 @@ 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_mask)
{
abort();// Not supported on esp32s3
// Not supported on esp32s3
}
/**
* Configure the delay after Suspend/Resume
*
* @param dev Beginning address of the peripheral registers.
* @param dly_val delay time
*/
static inline void spimem_flash_ll_set_sus_delay(spi_mem_dev_t *dev, uint32_t dly_val)
{
dev->ctrl1.cs_hold_dly_res = dly_val;
dev->sus_status.flash_per_dly_256 = 1;
dev->sus_status.flash_pes_dly_256 = 1;
}
/**
* Configure the cs hold delay time(used to set the minimum CS high time tSHSL)
*
* @param dev Beginning address of the peripheral registers.
* @param cs_hold_delay cs hold delay time
*/
static inline void spimem_flash_set_cs_hold_delay(spi_mem_dev_t *dev, uint32_t cs_hold_delay)
{
SPIMEM0.ctrl2.cs_hold_delay = cs_hold_delay;
}
/**
* @brief Set lock for SPI0 so that spi0 can request new cache request after a cache transfer.
*
* @param dev Beginning address of the peripheral registers.
* @param lock_time Lock delay time
*/
static inline void spimem_flash_ll_sus_set_spi0_lock_trans(spi_mem_dev_t *dev, uint32_t lock_time)
{
// Not support on esp32s3
}
/**
* @brief Get tsus unit values in SPI_CLK cycles
*
* @param dev Beginning address of the peripheral registers.
* @return uint32_t tsus unit values
*/
static inline uint32_t spimem_flash_ll_get_tsus_unit_in_cycles(spi_mem_dev_t *dev)
{
uint32_t tsus_unit = 0;
if (dev->sus_status.flash_pes_dly_256 == 1) {
tsus_unit = 128;
} else {
tsus_unit = 4;
}
return tsus_unit;
}
/**
@@ -214,6 +269,8 @@ static inline void spimem_flash_ll_auto_wait_idle_init(spi_mem_dev_t *dev, bool
{
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;
}
/**
@@ -316,6 +373,17 @@ static inline void spimem_flash_ll_user_start(spi_mem_dev_t *dev)
dev->cmd.usr = 1;
}
/**
* In user mode, it is set to indicate that program/erase operation will be triggered.
* This function is combined with `spimem_flash_ll_user_start`. The pe_bit will be cleared automatically once the operation done.
*
* @param dev Beginning address of the peripheral registers.
*/
static inline void spimem_flash_ll_set_pe_bit(spi_mem_dev_t *dev)
{
dev->cmd.flash_pe = 1;
}
/**
* Check whether the host is idle to perform new commands.
*

View File

@@ -47,8 +47,9 @@ typedef struct {
spi_flash_sus_cmd_conf sus_cfg; ///< To store suspend command/mask information.
uint32_t slicer_flags; /// Slicer flags for configuring how to slice data correctly while reading or writing.
#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.
} spi_flash_hal_context_t;
ESP_STATIC_ASSERT(sizeof(spi_flash_hal_context_t) == 40, "size of spi_flash_hal_context_t incorrect. Please check data compatibility with the ROM");
ESP_STATIC_ASSERT(sizeof(spi_flash_hal_context_t) == 44, "size of spi_flash_hal_context_t incorrect. Please check data compatibility with the ROM");
/// This struct provide MSPI Flash necessary timing related config, should be consistent with that in union in `spi_flash_hal_config_t`.
typedef struct {

View File

@@ -27,6 +27,7 @@ typedef struct {
#define SPI_FLASH_TRANS_FLAG_CMD16 BIT(0) ///< Send command of 16 bits
#define SPI_FLASH_TRANS_FLAG_IGNORE_BASEIO BIT(1) ///< Not applying the basic io mode configuration for this transaction
#define SPI_FLASH_TRANS_FLAG_BYTE_SWAP BIT(2) ///< Used for DTR mode, to swap the bytes of a pair of rising/falling edge
#define SPI_FLASH_TRANS_FLAG_PE_CMD BIT(3) ///< Indicates that this transaction is to erase/program flash chip.
uint16_t command; ///< Command to send
uint8_t dummy_bitlen; ///< Basic dummy bits to use
uint32_t io_mode; ///< Flash working mode when `SPI_FLASH_IGNORE_BASEIO` is specified.

View File

@@ -103,6 +103,7 @@ esp_err_t spi_flash_hal_init(spi_flash_hal_context_t *data_out, const spi_flash_
.cs_hold = cfg->cs_hold,
.cs_setup = cfg->cs_setup,
.base_io_mode = cfg->default_io_mode,
.freq_mhz = cfg->freq_mhz,
};
#if SOC_SPI_MEM_SUPPORT_TIMING_TUNING
if (cfg->using_timing_tuning) {

View File

@@ -170,6 +170,9 @@ esp_err_t spi_flash_hal_common_command(spi_flash_host_inst_t *host, spi_flash_tr
spi_flash_ll_set_buffer_data(dev, trans->mosi_data, trans->mosi_len);
spi_flash_ll_set_miso_bitlen(dev, trans->miso_len * 8);
if ((trans->flags & SPI_FLASH_TRANS_FLAG_PE_CMD) != 0) {
spi_flash_ll_set_pe_bit(dev);
}
spi_flash_ll_user_start(dev);
host->driver->poll_cmd_done(host);
if (trans->miso_len > 0) {

View File

@@ -6,11 +6,14 @@
#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);
#define SPI_FLASH_TSUS_SAFE_VAL_US (30)
#define SPI_FLASH_TSHSL2_SAFE_VAL_NS (30)
#endif //SOC_SPI_MEM_SUPPORT_AUTO_SUSPEND
#ifndef CONFIG_SPI_FLASH_ROM_IMPL
@@ -128,10 +131,12 @@ esp_err_t spi_flash_hal_setup_read_suspend(spi_flash_host_inst_t *host, const sp
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;
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);
#if SOC_SPI_MEM_SUPPORT_CHECK_SUS
spimem_flash_ll_set_read_sus_status(dev, sus_conf->sus_mask);
spimem_flash_ll_rd_sus_cmd_setup(dev, sus_conf->cmd_rdsr);
#endif // SOC_SPI_MEM_SUPPORT_CHECK_SUS
#endif // SOC_SPI_MEM_SUPPORT_AUTO_SUSPEND
return ESP_OK;
}
@@ -140,8 +145,16 @@ esp_err_t spi_flash_hal_setup_read_suspend(spi_flash_host_inst_t *host, const sp
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);
spimem_flash_ll_auto_suspend_init(dev, true);
// tsus = ceil(SPI_FLASH_TSUS_SAFE_VAL_US * ctx->freq_mhz / spimem_flash_ll_get_tsus_unit_in_cycles);
uint32_t tsus = (SPI_FLASH_TSUS_SAFE_VAL_US * ctx->freq_mhz / spimem_flash_ll_get_tsus_unit_in_cycles(dev)) + ((SPI_FLASH_TSUS_SAFE_VAL_US * ctx->freq_mhz) % spimem_flash_ll_get_tsus_unit_in_cycles(dev) != 0);
spimem_flash_ll_set_sus_delay(dev, tsus);
// tshsl2 = ceil(SPI_FLASH_TSHSL2_SAFE_VAL_NS * spimem_flash_ll_get_source_freq_mhz() * 0.001);
uint32_t tshsl2 = (SPI_FLASH_TSHSL2_SAFE_VAL_NS * spimem_flash_ll_get_source_freq_mhz() / 1000) + ((SPI_FLASH_TSHSL2_SAFE_VAL_NS * spimem_flash_ll_get_source_freq_mhz()) % 1000 != 0);
spimem_flash_set_cs_hold_delay(dev, tshsl2);
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);
#endif

View File

@@ -238,7 +238,14 @@ typedef volatile struct spi_mem_dev_s {
union {
struct {
uint32_t flash_sus: 1; /*The status of flash suspend only used in SPI1.*/
uint32_t reserved1: 31; /*reserved*/
uint32_t wait_pesr_cmd_2b:1;
uint32_t hpm_dly_128: 1;
uint32_t res_dly_128: 1;
uint32_t dp_dly_128: 1;
uint32_t per_dly_128: 1;
uint32_t pes_dly_128: 1;
uint32_t spi0_lock_en: 1;
uint32_t reserved1: 24; /*reserved*/
};
uint32_t val;
} sus_status;

View File

@@ -951,10 +951,6 @@ config SOC_SPI_MEM_SUPPORT_AUTO_WAIT_IDLE
bool
default y
config SOC_SPI_MEM_SUPPORT_AUTO_SUSPEND
bool
default y
config SOC_SPI_MEM_SUPPORT_SW_SUSPEND
bool
default y

View File

@@ -414,7 +414,6 @@
/*-------------------------- 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)
#define SOC_SPI_MEM_SUPPORT_CONFIG_GPIO_BY_EFUSE (1)
#define SOC_SPI_MEM_SUPPORT_WRAP (1)

View File

@@ -11,3 +11,11 @@ components/spi_flash/test_apps/flash_encryption:
- if: IDF_TARGET in ["esp32c2", "esp32s2", "esp32c6", "esp32h2"]
temporary: true
reason: No runners # IDF-5634
components/spi_flash/test_apps/flash_suspend:
disable:
- if: SOC_SPI_MEM_SUPPORT_AUTO_SUSPEND != 1
disable_test:
- if: IDF_TARGET != "esp32c3"
temporary: true
reason: lack of runners

View File

@@ -147,17 +147,24 @@ menu "SPI Flash driver"
help
Defines how many ticks will be before returning to continue a erasing.
config SPI_FLASH_SUSPEND_QVL_SUPPORTED
# Internally usage
bool
default y if IDF_TARGET_ESP32C3 || IDF_TARGET_ESP32S3 || IDF_TARGET_ESP32C2
default n
config SPI_FLASH_AUTO_SUSPEND
bool "Auto suspend long erase/write operations (READ DOCS FIRST)"
default n
depends on IDF_TARGET_ESP32C3 && !SPI_FLASH_ROM_IMPL
depends on SPI_FLASH_SUSPEND_QVL_SUPPORTED && !SPI_FLASH_ROM_IMPL
help
This option is default n before ESP32-C3, because it needs bootloader support.
This option is default n because it can't be used in every situation. You need to
evaluate this feature through suspend part in `SPI Flash API` document.
CAUTION: If you want to OTA to an app with this feature turned on, please make
sure the bootloader has the support for it. (later than IDF v4.3)
Auto-suspend feature only supported by XMC chip.
Auto-suspend feature only supported by specific flash chips.
If you are using an official module, please contact Espressif Business support.
Also reading auto suspend part in `SPI Flash API` document before you enable this function.

View File

@@ -97,26 +97,7 @@ esp_flash_t *esp_flash_default_chip = NULL;
.input_delay_ns = 0,\
.cs_setup = 1,\
}
#elif CONFIG_IDF_TARGET_ESP32S2
#define ESP_FLASH_HOST_CONFIG_DEFAULT() (memspi_host_config_t){ \
.host_id = SPI1_HOST,\
.freq_mhz = DEFAULT_FLASH_SPEED, \
.cs_num = 0, \
.iomux = true, \
.input_delay_ns = 0,\
.cs_setup = 1,\
}
#elif CONFIG_IDF_TARGET_ESP32S3
#include "esp32s3/rom/efuse.h"
#define ESP_FLASH_HOST_CONFIG_DEFAULT() (memspi_host_config_t){ \
.host_id = SPI1_HOST,\
.freq_mhz = DEFAULT_FLASH_SPEED, \
.cs_num = 0, \
.iomux = true, \
.input_delay_ns = 0,\
.cs_setup = 1,\
}
#elif CONFIG_IDF_TARGET_ESP32C3 || CONFIG_IDF_TARGET_ESP32C2 || CONFIG_IDF_TARGET_ESP32C6 || CONFIG_IDF_TARGET_ESP32H2
#else // Other target
#if !CONFIG_SPI_FLASH_AUTO_SUSPEND
#define ESP_FLASH_HOST_CONFIG_DEFAULT() (memspi_host_config_t){ \
.host_id = SPI1_HOST,\
@@ -137,7 +118,7 @@ esp_flash_t *esp_flash_default_chip = NULL;
.cs_setup = 1,\
}
#endif //!CONFIG_SPI_FLASH_AUTO_SUSPEND
#endif
#endif // Other target
static IRAM_ATTR NOINLINE_ATTR void cs_initialize(esp_flash_t *chip, const esp_flash_spi_device_config_t *config, bool use_iomux, int cs_id)
{

View File

@@ -11,6 +11,7 @@
#include "spi_flash_chip_generic.h"
#include "spi_flash_chip_gd.h"
#include "spi_flash_defs.h"
#include "sdkconfig.h"
#define ADDR_32BIT(addr) (addr >= (1<<24))
@@ -33,6 +34,17 @@ spi_flash_caps_t spi_flash_chip_gd_get_caps(esp_flash_t *chip)
if ((chip->chip_id & 0xFF) >= 0x19) {
caps_flags |= SPI_FLASH_CHIP_CAP_32MB_SUPPORT;
}
#if CONFIG_SPI_FLASH_AUTO_SUSPEND
switch (chip->chip_id) {
/* The flash listed here can support suspend */
case 0xC84017:
case 0xC84018:
caps_flags |= SPI_FLASH_CHIP_CAP_SUSPEND;
break;
default:
break;
}
#endif
// flash-suspend is not supported
// flash read unique id.
caps_flags |= SPI_FLASH_CHIP_CAP_UNIQUE_ID;
@@ -112,6 +124,18 @@ esp_err_t spi_flash_chip_gd_get_io_mode(esp_flash_t *chip, esp_flash_io_mode_t*
}
#endif //CONFIG_SPI_FLASH_ROM_IMPL
esp_err_t spi_flash_chip_gd_suspend_cmd_conf(esp_flash_t *chip)
{
spi_flash_sus_cmd_conf sus_conf = {
.sus_mask = 0x84,
.cmd_rdsr = CMD_RDSR2,
.sus_cmd = CMD_SUSPEND,
.res_cmd = CMD_RESUME,
};
return chip->host->driver->sus_setup(chip->host, &sus_conf);
}
static const char chip_name[] = "gd";
// The issi chip can use the functions for generic chips except from set read mode and probe,
@@ -148,7 +172,7 @@ 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,
.sus_setup = spi_flash_chip_gd_suspend_cmd_conf,
.read_unique_id = spi_flash_chip_generic_read_unique_id,
.get_chip_caps = spi_flash_chip_gd_get_caps,
.config_host_io_mode = spi_flash_chip_generic_config_host_io_mode,

View File

@@ -1,16 +1,8 @@
// Copyright 2015-2020 Espressif Systems (Shanghai) PTE LTD
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
/*
* SPDX-FileCopyrightText: 2015-2023 Espressif Systems (Shanghai) CO LTD
*
* SPDX-License-Identifier: Apache-2.0
*/
#include <stdlib.h>
#include <string.h>
@@ -209,6 +201,7 @@ static esp_err_t spi_flash_command_winbond_program_4B(esp_flash_t *chip, const v
.address = address,
.mosi_len = length,
.mosi_data = buffer,
.flags = SPI_FLASH_TRANS_FLAG_PE_CMD,
};
return chip->host->driver->common_command(chip->host, &t);
}
@@ -220,6 +213,7 @@ esp_err_t spi_flash_command_winbond_erase_sector_4B(esp_flash_t *chip, uint32_t
.command = (addr_4b? CMD_SECTOR_ERASE_4B: CMD_SECTOR_ERASE),
.address_bitlen = (addr_4b? 32: 24),
.address = start_address,
.flags = SPI_FLASH_TRANS_FLAG_PE_CMD,
};
return chip->host->driver->common_command(chip->host, &t);
}
@@ -231,6 +225,7 @@ esp_err_t spi_flash_command_erase_block_4B(esp_flash_t *chip, uint32_t start_add
.command = (addr_4b? CMD_LARGE_BLOCK_ERASE_4B: CMD_LARGE_BLOCK_ERASE),
.address_bitlen = (addr_4b? 32: 24),
.address = start_address,
.flags = SPI_FLASH_TRANS_FLAG_PE_CMD,
};
return chip->host->driver->common_command(chip->host, &t);
}

View File

@@ -459,55 +459,6 @@ static void test_flash_erase_not_trigger_wdt(const esp_partition_t *part)
TEST_CASE_MULTI_FLASH_LONG("Test erasing flash chip not triggering WDT", test_flash_erase_not_trigger_wdt);
#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");
}
static volatile bool task_erase_end, task_suspend_end = false;
void task_erase_large_region(void *arg)
{
esp_partition_t *part = (esp_partition_t *)arg;
test_erase_large_region(part);
task_erase_end = true;
vTaskDelete(NULL);
}
void task_request_suspend(void *arg)
{
vTaskDelay(2);
ESP_LOGI(TAG, "flash go into suspend");
esp_test_for_suspend();
task_suspend_end = true;
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);
while (!task_erase_end || !task_suspend_end) {
}
vTaskDelay(200);
}
TEST_CASE("SPI flash suspend and resume test", "[esp_flash][test_env=UT_T1_Flash_Suspend]")
{
flash_test_func(test_flash_suspend_resume, 1 /* first index reserved for main flash */ );
}
#endif //CONFIG_SPI_FLASH_AUTO_SUSPEND
static void test_write_protection(const esp_partition_t* part)
{
esp_flash_t* chip = part->flash_chip;

View File

@@ -0,0 +1,7 @@
# This is the project CMakeLists.txt file for the test subproject
cmake_minimum_required(VERSION 3.16)
set(EXTRA_COMPONENT_DIRS "$ENV{IDF_PATH}/tools/unit-test-app/components")
include($ENV{IDF_PATH}/tools/cmake/project.cmake)
project(test_flash_suspend)

View File

@@ -0,0 +1,2 @@
| Supported Targets | ESP32-C2 | ESP32-C3 | ESP32-C6 | ESP32-H2 | ESP32-S3 |
| ----------------- | -------- | -------- | -------- | -------- | -------- |

View File

@@ -0,0 +1,7 @@
set(srcs "test_app_main.c"
"test_flash_suspend.c")
# In order for the cases defined by `TEST_CASE` to be linked into the final elf,
# the component can be registered as WHOLE_ARCHIVE
idf_component_register(SRCS ${srcs}
WHOLE_ARCHIVE)

View File

@@ -0,0 +1,54 @@
/*
* SPDX-FileCopyrightText: 2023 Espressif Systems (Shanghai) CO LTD
*
* SPDX-License-Identifier: Unlicense OR CC0-1.0
*/
#include "unity.h"
#include "unity_test_utils.h"
#include "esp_heap_caps.h"
// Some resources are lazy allocated, the threshold is left for that case
#define TEST_MEMORY_LEAK_THRESHOLD (1000)
static size_t before_free_8bit;
static size_t before_free_32bit;
void setUp(void)
{
before_free_8bit = heap_caps_get_free_size(MALLOC_CAP_8BIT);
before_free_32bit = heap_caps_get_free_size(MALLOC_CAP_32BIT);
}
void tearDown(void)
{
size_t after_free_8bit = heap_caps_get_free_size(MALLOC_CAP_8BIT);
size_t after_free_32bit = heap_caps_get_free_size(MALLOC_CAP_32BIT);
unity_utils_check_leak(before_free_8bit, after_free_8bit, "8BIT", TEST_MEMORY_LEAK_THRESHOLD);
unity_utils_check_leak(before_free_32bit, after_free_32bit, "32BIT", TEST_MEMORY_LEAK_THRESHOLD);
}
void app_main(void)
{
/*
______ _ _____ _ _ _____ _ _ _____ _____ ______ _ _ _____
| ____| | /\ / ____| | | | / ____| | | |/ ____| __ \| ____| \ | | __ \
| |__ | | / \ | (___ | |__| | | (___ | | | | (___ | |__) | |__ | \| | | | |
| __| | | / /\ \ \___ \| __ | \___ \| | | |\___ \| ___/| __| | . ` | | | |
| | | |____ / ____ \ ____) | | | | ____) | |__| |____) | | | |____| |\ | |__| |
|_| |______/_/ \_\_____/|_| |_| |_____/ \____/|_____/|_| |______|_| \_|_____/
*/
printf(" ______ _ _____ _ _ _____ _ _ _____ _____ ______ _ _ _____ \n");
printf("| ____| | /\\ / ____| | | | / ____| | | |/ ____| __ \\| ____| \\ | | __ \\ \n");
printf("| |__ | | / \\ | (___ | |__| | | (___ | | | | (___ | |__) | |__ | \\| | | | |\n");
printf("| __| | | / /\\ \\ \\___ \\| __ | \\___ \\| | | |\\___ \\| ___/| __| | . ` | | | |\n");
printf("| | | |____ / ____ \\ ____) | | | | ____) | |__| |____) | | | |____| |\\ | |__| |\n");
printf("|_| |______/_/ \\_\\_____/|_| |_| |_____/ \\____/|_____/|_| |______|_| \\_|_____/ \n");
unity_run_menu();
}

View File

@@ -0,0 +1,163 @@
/*
* SPDX-FileCopyrightText: 2023 Espressif Systems (Shanghai) CO LTD
*
* SPDX-License-Identifier: Apache-2.0
*/
/**
* In this test, we show you a way to make an ISR-based callback work during Flash operations, when the ISR-based
* callback is put in Flash.
*
* Please read the README.md to know more details about this feature!
*/
#include <stdbool.h>
#include <stdio.h>
#include <inttypes.h>
#include "sdkconfig.h"
#include "esp_log.h"
#include "esp_attr.h"
#include "esp_cpu.h"
#include "esp_partition.h"
#include "driver/gptimer.h"
#include "esp_flash.h"
#include "hal/gpio_hal.h"
#include "rom/cache.h"
#include "test_utils.h"
#define TIMER_RESOLUTION_HZ (1 * 1000 * 1000) // 1MHz resolution
#define TIMER_ALARM_PERIOD_S 1 // Alarm period 1s
#define RECORD_TIME_PREPARE() uint32_t __t1, __t2
#define RECORD_TIME_START() do {__t1 = esp_cpu_get_cycle_count();} while(0)
#define RECORD_TIME_END(p_time) do{__t2 = esp_cpu_get_cycle_count(); p_time = (__t2 - __t1);} while(0)
#define GET_US_BY_CCOUNT(t) ((double)(t)/CONFIG_ESP_DEFAULT_CPU_FREQ_MHZ)
const static char *TAG = "flash_suspend test";
DRAM_ATTR static uint32_t s_flash_func_t1;
DRAM_ATTR static uint32_t s_flash_func_t2;
DRAM_ATTR static uint32_t s_flash_func_time;
DRAM_ATTR static uint32_t s_isr_t1;
DRAM_ATTR static uint32_t s_isr_t2;
DRAM_ATTR static uint32_t s_isr_time;
DRAM_ATTR static uint32_t s_isr_interval_t1;
DRAM_ATTR static uint32_t s_isr_interval_t2;
DRAM_ATTR static uint32_t s_isr_interval_time;
DRAM_ATTR static uint32_t times = 0;
static NOINLINE_ATTR void func_in_flash(void)
{
/**
* - Here we will have few instructions in .flash.text
* - Cache will read from Flash to get the instructions synchronized.
* - CPU will execute around 90000 times.
*/
for (int i = 0; i < 50000; i++) {
asm volatile("nop");
}
s_flash_func_t2 = esp_cpu_get_cycle_count();
}
static bool IRAM_ATTR gptimer_alarm_suspend_cb(gptimer_handle_t timer, const gptimer_alarm_event_data_t *edata, void *user_ctx)
{
s_isr_t1 = esp_cpu_get_cycle_count();
if (s_isr_interval_t1 != 0 ) {
s_isr_interval_t2 = esp_cpu_get_cycle_count();
s_isr_interval_time += (s_isr_interval_t2 - s_isr_interval_t1);
}
s_isr_interval_t1 = esp_cpu_get_cycle_count();
/*clear content in cache*/
#if CONFIG_IDF_TARGET_ESP32S3
Cache_Invalidate_DCache_All();
#endif
Cache_Invalidate_ICache_All();
s_flash_func_t1 = esp_cpu_get_cycle_count();
func_in_flash();
s_flash_func_time += (s_flash_func_t2 - s_flash_func_t1);
times++;
s_isr_t2 = esp_cpu_get_cycle_count();
s_isr_time += (s_isr_t2 - s_isr_t1);
return false;
}
static const esp_partition_t *s_get_partition(void)
{
//Find the "storage1" partition defined in `partitions.csv`
const esp_partition_t *result = esp_partition_find_first(ESP_PARTITION_TYPE_DATA, ESP_PARTITION_SUBTYPE_ANY, "storage1");
if (!result) {
ESP_LOGE(TAG, "Can't find the partition, please define it correctly in `partitions.csv`");
abort();
}
return result;
}
static const uint8_t large_const_buffer[16400] = {
203, // first byte
1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20,
21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37,
[50 ... 99] = 2,
[1600 ... 2000] = 3,
[8000 ... 9000] = 77,
[15000 ... 16398] = 8,
43 // last byte
};
TEST_CASE("flash suspend test", "[spi_flash][suspend]")
{
//Get the partition used for SPI1 erase operation
const esp_partition_t *part = s_get_partition();
ESP_LOGI(TAG, "found partition '%s' at offset 0x%"PRIx32" with size 0x%"PRIx32, part->label, part->address, part->size);
gptimer_handle_t gptimer = NULL;
gptimer_config_t timer_config = {
.clk_src = GPTIMER_CLK_SRC_DEFAULT,
.direction = GPTIMER_COUNT_UP,
.resolution_hz = TIMER_RESOLUTION_HZ,
};
TEST_ESP_OK(gptimer_new_timer(&timer_config, &gptimer));
/**
set alarm_count is 2500.
So, in this case, ISR duration time is around 2240 and ISR interval time is around 2470
so ISR_interval - ISR_duration is around 230us. Just a little bit larger than `tsus` value.
*/
gptimer_alarm_config_t alarm_config = {
.reload_count = 0,
.alarm_count = 2500,
.flags.auto_reload_on_alarm = true,
};
TEST_ESP_OK(gptimer_set_alarm_action(gptimer, &alarm_config));
gptimer_event_callbacks_t cbs = {
.on_alarm = gptimer_alarm_suspend_cb,
};
bool is_flash = true;
TEST_ESP_OK(gptimer_register_event_callbacks(gptimer, &cbs, &is_flash));
TEST_ESP_OK(gptimer_enable(gptimer));
TEST_ESP_OK(gptimer_start(gptimer));
uint32_t erase_time = 0;
RECORD_TIME_PREPARE();
RECORD_TIME_START();
TEST_ESP_OK(esp_flash_erase_region(part->flash_chip, part->address, part->size));
TEST_ESP_OK(esp_flash_write(part->flash_chip, large_const_buffer, part->address, sizeof(large_const_buffer)));
RECORD_TIME_END(erase_time);
TEST_ESP_OK(gptimer_stop(gptimer));
ESP_LOGI(TAG, "Flash Driver Erase Operation finishes, duration:\n\t\t%0.2f us", GET_US_BY_CCOUNT(erase_time));
ESP_LOGI(TAG, "During Erase, ISR callback function(in flash) response time:\n\t\t%0.2f us", GET_US_BY_CCOUNT(s_flash_func_time / times));
ESP_LOGI(TAG, "During Erase, ISR duration time:\n\t\t%0.2f us", GET_US_BY_CCOUNT(s_isr_time / times));
ESP_LOGI(TAG, "During Erase, ISR interval time:\n\t\t%0.2f us", GET_US_BY_CCOUNT(s_isr_interval_time / (times - 1)));
ESP_LOGI(TAG, "Finish");
TEST_ESP_OK(gptimer_disable(gptimer));
TEST_ESP_OK(gptimer_del_timer(gptimer));
}

View File

@@ -0,0 +1,6 @@
# Name, Type, SubType, Offset, Size, Flags
# Note: if you have increased the bootloader size, make sure to update the offsets to avoid overlap
nvs, data, nvs, 0x9000, 0x6000,
phy_init, data, phy, 0xf000, 0x1000,
factory, app, factory, 0x10000, 1M,
storage1, data, fat, , 512K,
1 # Name, Type, SubType, Offset, Size, Flags
2 # Note: if you have increased the bootloader size, make sure to update the offsets to avoid overlap
3 nvs, data, nvs, 0x9000, 0x6000,
4 phy_init, data, phy, 0xf000, 0x1000,
5 factory, app, factory, 0x10000, 1M,
6 storage1, data, fat, , 512K,

View File

@@ -0,0 +1,18 @@
# SPDX-FileCopyrightText: 2023 Espressif Systems (Shanghai) CO LTD
# SPDX-License-Identifier: Apache-2.0
import pytest
from pytest_embedded import Dut
@pytest.mark.esp32c3
@pytest.mark.flash_suspend
@pytest.mark.parametrize(
'config',
[
'release',
],
indirect=True,
)
def test_flash_auto_suspend(dut: Dut) -> None:
dut.run_all_single_board_cases(timeout=30)

View File

@@ -0,0 +1,5 @@
CONFIG_ESP_TASK_WDT=n
CONFIG_SPI_FLASH_AUTO_SUSPEND=y
CONFIG_COMPILER_OPTIMIZATION_SIZE=y
CONFIG_BOOTLOADER_COMPILER_OPTIMIZATION_SIZE=y
CONFIG_COMPILER_OPTIMIZATION_ASSERTIONS_SILENT=y

View File

@@ -0,0 +1,4 @@
CONFIG_ESP_TASK_WDT=n
CONFIG_SPI_FLASH_AUTO_SUSPEND=y
CONFIG_PARTITION_TABLE_CUSTOM=y
CONFIG_PARTITION_TABLE_CUSTOM_FILENAME="partitions.csv"

View File

@@ -0,0 +1,23 @@
{
"signal": [
{
"name": "SPI1(flash)",
"wave": "x73...|.8x........8x3..",
"data": ["PE", "Programming...", "SUS", "RES", "CONTINUE.."],
"node":".........E.........FG" },
{
"name": "SPI0(cache)",
"wave": "x.........3...|.x......",
"data":["READ"], "node": "..........D" },
{
"name": "ISR", "wave": "0......1.........0...1.",
"data":["1"], "node": ".......a.........b...c" },
{
"node": ".......A.............C"}
],
"edge": [
"A+C ISR interval",
"E~D tsus", "F~G tsus",
"a~b ISR time"
]
}

View File

@@ -35,3 +35,16 @@ In other words, there are three kinds of code:
3. Low priority code: inside flash/psram and disabled during erasing. This kind of code should be forbidden from executed to avoid affecting the flash erasing, by setting a lower task priority than the erasing task.
Regarding the flash suspend feature usage, and corresponding response time delay, please also see this example :example:`system/flash_suspend` .
.. note::
The flash auto suspend feature relies heavily on strict timing. You can see it as a kind of optimisation plan, which means that you cannot use it in every situation, like high requirement of real-time system or triggering interrupt very frequently (e.g. lcd flush, bluetooth, wifi, etc.). You should take following steps to evaluate what kind of ISR can be used together with flash suspend.
.. wavedrom:: /../_static/diagrams/spi_flash/flash_auto_suspend_timing.json
As you can see from the diagram. Two key values should be noted.
1. ISR time: The ISR time cannot be very long, at least not larger than the value you set in `IWDT`. But it will significantly lengthen the erase/write completion times.
2. ISR interval: The ISR cannot be triggered very often. The most important time is the `ISR interval minus ISR time`(from point b to point c in the diagram). During this time, SPI1 will send resume to restart the operation, but it needs a time `tsus` for preparation, and the typical value of `tsus` is about **40us**. If SPI1 cannot resume the operation but another suspend comes, it will cause CPU starve and `TWDT` may be triggered.
Furthermore, the flash suspend might be delayed. If CPU and the cache operation accesses to flash via SPI0 very frequently and SPI1 sending suspend command is also very frequently, it will influence the efficiency of MSPI data transfer. So, we have a "lock" inside to prevent this. When SPI1 send suspend command, then SPI0 will take over memory SPI bus and take the "lock". After SPI0 finishes sending data, SPI0 will still take the memory SPI bus until the "lock" delay period time finishes. During this "lock" delay period, if there is any other SPI0 transaction, then start SPI0 transaction and "lock" delay period start again. Otherwise, SPI0 will release the memory bus and start SPI0/1 arbitration.

View File

@@ -76,7 +76,7 @@ Non-IRAM-Safe Interrupt Handlers
If the ``ESP_INTR_FLAG_IRAM`` flag is not set when registering, the interrupt handler will not get executed when the caches are disabled. Once the caches are restored, the non-IRAM-safe interrupts will be re-enabled. After this moment, the interrupt handler will run normally again. This means that as long as caches are disabled, users won't see the corresponding hardware event happening.
.. only:: esp32c3
.. only:: esp32c3 or esp32c2 or esp32s3
.. include:: auto_suspend.inc

View File

@@ -28,17 +28,24 @@ Some features are not supported on all ESP chips and Flash chips. You can check
Auto Suspend & Resume
---------------------
.. only:: esp32c3
You can refer to :ref:`auto-suspend` for more information about this feature. The support list is as follows.
The support list is as follows.
ESP Chips List:
1. ESP32C3
2. ESP32C2
3. ESP32S3
Flash Chips List:
1. XM25QxxC series.
2. GD25QxxE series.
.. only:: esp32c3 or esp32c2 or esp32s3
.. attention::
There are multiple limitations about the auto-suspend feature, please do read :ref:`auto-suspend` for more information before you enable this feature.
Flash unique ID
---------------

View File

@@ -76,7 +76,7 @@ IRAM 安全中断处理程序
如果在注册时没有设置 `ESP_INTR_FLAG_IRAM` 标志,当 cache 被禁用时,将不会执行中断处理程序。一旦 cache 恢复,非 IRAM 安全的中断将重新启用,中断处理程序随即再次正常运行。这意味着,只要 cache 被禁用,将不会发生相应的硬件事件。
.. only:: esp32c3
.. only:: esp32c3 or esp32c2 or esp32s3
.. include:: auto_suspend.inc

View File

@@ -996,7 +996,6 @@ components/spi_flash/include/spi_flash_chip_winbond.h
components/spi_flash/spi_flash_chip_boya.c
components/spi_flash/spi_flash_chip_issi.c
components/spi_flash/spi_flash_chip_mxic.c
components/spi_flash/spi_flash_chip_winbond.c
components/spi_flash/test/test_esp_flash.c
components/spi_flash/test/test_flash_encryption.c
components/spi_flash/test/test_mmap.c