From ed96dadd063cf1d2de2ab54e3f410b8a34cd14f5 Mon Sep 17 00:00:00 2001 From: Cao Sen Miao Date: Thu, 11 May 2023 20:10:30 +0800 Subject: [PATCH 1/3] spi_flash: 2nd stage for supporting flash suspend. (1). Support more esp chips (2). Improve real-time performance (3). Making timing more stable (4) Add documents --- .../hal/esp32/include/hal/spi_flash_ll.h | 10 +++ .../hal/esp32c2/include/hal/gpspi_flash_ll.h | 10 +++ .../hal/esp32c2/include/hal/spi_flash_ll.h | 2 + .../hal/esp32c2/include/hal/spimem_flash_ll.h | 65 +++++++++++++++++ .../hal/esp32c3/include/hal/gpspi_flash_ll.h | 10 +++ .../hal/esp32c3/include/hal/spi_flash_ll.h | 2 + .../hal/esp32c3/include/hal/spimem_flash_ll.h | 66 +++++++++++++++++ .../hal/esp32c6/include/hal/gpspi_flash_ll.h | 12 +++- .../hal/esp32c6/include/hal/spi_flash_ll.h | 4 +- .../hal/esp32c6/include/hal/spimem_flash_ll.h | 68 +++++++++++++++++- .../hal/esp32h2/include/hal/gpspi_flash_ll.h | 12 +++- .../hal/esp32h2/include/hal/spi_flash_ll.h | 4 +- .../hal/esp32h2/include/hal/spimem_flash_ll.h | 66 +++++++++++++++++ .../hal/esp32s2/include/hal/gpspi_flash_ll.h | 10 +++ .../hal/esp32s2/include/hal/spi_flash_ll.h | 2 + .../hal/esp32s2/include/hal/spimem_flash_ll.h | 11 +++ .../hal/esp32s3/include/hal/gpspi_flash_ll.h | 10 +++ .../hal/esp32s3/include/hal/spi_flash_ll.h | 3 + .../hal/esp32s3/include/hal/spimem_flash_ll.h | 72 ++++++++++++++++++- components/hal/include/hal/spi_flash_hal.h | 3 +- components/hal/include/hal/spi_flash_types.h | 1 + components/hal/spi_flash_hal.c | 1 + components/hal/spi_flash_hal_common.inc | 3 + components/hal/spi_flash_hal_iram.c | 15 +++- .../soc/esp32c3/include/soc/spi_mem_struct.h | 9 ++- .../esp32s2/include/soc/Kconfig.soc_caps.in | 4 -- components/soc/esp32s2/include/soc/soc_caps.h | 1 - components/spi_flash/.build-test-rules.yml | 8 +++ components/spi_flash/Kconfig | 13 +++- components/spi_flash/esp_flash_spi_init.c | 23 +----- components/spi_flash/spi_flash_chip_gd.c | 26 ++++++- components/spi_flash/spi_flash_chip_winbond.c | 21 +++--- .../esp_flash/main/test_esp_flash_drv.c | 49 ------------- tools/ci/check_copyright_ignore.txt | 1 - 34 files changed, 514 insertions(+), 103 deletions(-) diff --git a/components/hal/esp32/include/hal/spi_flash_ll.h b/components/hal/esp32/include/hal/spi_flash_ll.h index d4a2b4bcfc14..0a9a956ad2fe 100644 --- a/components/hal/esp32/include/hal/spi_flash_ll.h +++ b/components/hal/esp32/include/hal/spi_flash_ll.h @@ -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. diff --git a/components/hal/esp32c2/include/hal/gpspi_flash_ll.h b/components/hal/esp32c2/include/hal/gpspi_flash_ll.h index 3887e7acdf43..2777adf9cbeb 100644 --- a/components/hal/esp32c2/include/hal/gpspi_flash_ll.h +++ b/components/hal/esp32c2/include/hal/gpspi_flash_ll.h @@ -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. * diff --git a/components/hal/esp32c2/include/hal/spi_flash_ll.h b/components/hal/esp32c2/include/hal/spi_flash_ll.h index fea625a6fb93..81bdc6f16aef 100644 --- a/components/hal/esp32c2/include/hal/spi_flash_ll.h +++ b/components/hal/esp32c2/include/hal/spi_flash_ll.h @@ -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 diff --git a/components/hal/esp32c2/include/hal/spimem_flash_ll.h b/components/hal/esp32c2/include/hal/spimem_flash_ll.h index d97c41258dd3..25901a0eb238 100644 --- a/components/hal/esp32c2/include/hal/spimem_flash_ll.h +++ b/components/hal/esp32c2/include/hal/spimem_flash_ll.h @@ -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. * diff --git a/components/hal/esp32c3/include/hal/gpspi_flash_ll.h b/components/hal/esp32c3/include/hal/gpspi_flash_ll.h index ae81a85e3345..7a40c4a9d6a7 100644 --- a/components/hal/esp32c3/include/hal/gpspi_flash_ll.h +++ b/components/hal/esp32c3/include/hal/gpspi_flash_ll.h @@ -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. * diff --git a/components/hal/esp32c3/include/hal/spi_flash_ll.h b/components/hal/esp32c3/include/hal/spi_flash_ll.h index fea625a6fb93..81bdc6f16aef 100644 --- a/components/hal/esp32c3/include/hal/spi_flash_ll.h +++ b/components/hal/esp32c3/include/hal/spi_flash_ll.h @@ -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 diff --git a/components/hal/esp32c3/include/hal/spimem_flash_ll.h b/components/hal/esp32c3/include/hal/spimem_flash_ll.h index 76e6369b4fb0..cf75253affb8 100644 --- a/components/hal/esp32c3/include/hal/spimem_flash_ll.h +++ b/components/hal/esp32c3/include/hal/spimem_flash_ll.h @@ -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. * diff --git a/components/hal/esp32c6/include/hal/gpspi_flash_ll.h b/components/hal/esp32c6/include/hal/gpspi_flash_ll.h index 1cd9da5ea45a..d097df3ce984 100644 --- a/components/hal/esp32c6/include/hal/gpspi_flash_ll.h +++ b/components/hal/esp32c6/include/hal/gpspi_flash_ll.h @@ -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. * diff --git a/components/hal/esp32c6/include/hal/spi_flash_ll.h b/components/hal/esp32c6/include/hal/spi_flash_ll.h index 522b862cb08d..64efbfdfaa2c 100644 --- a/components/hal/esp32c6/include/hal/spi_flash_ll.h +++ b/components/hal/esp32c6/include/hal/spi_flash_ll.h @@ -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 diff --git a/components/hal/esp32c6/include/hal/spimem_flash_ll.h b/components/hal/esp32c6/include/hal/spimem_flash_ll.h index 27106133fd48..71b133520227 100644 --- a/components/hal/esp32c6/include/hal/spimem_flash_ll.h +++ b/components/hal/esp32c6/include/hal/spimem_flash_ll.h @@ -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. * diff --git a/components/hal/esp32h2/include/hal/gpspi_flash_ll.h b/components/hal/esp32h2/include/hal/gpspi_flash_ll.h index 91850dfd79af..56bd7e77b8d7 100644 --- a/components/hal/esp32h2/include/hal/gpspi_flash_ll.h +++ b/components/hal/esp32h2/include/hal/gpspi_flash_ll.h @@ -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. * diff --git a/components/hal/esp32h2/include/hal/spi_flash_ll.h b/components/hal/esp32h2/include/hal/spi_flash_ll.h index 425695498dbb..b14aff7eeef7 100644 --- a/components/hal/esp32h2/include/hal/spi_flash_ll.h +++ b/components/hal/esp32h2/include/hal/spi_flash_ll.h @@ -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 diff --git a/components/hal/esp32h2/include/hal/spimem_flash_ll.h b/components/hal/esp32h2/include/hal/spimem_flash_ll.h index e8922a131801..9135ff882aa1 100644 --- a/components/hal/esp32h2/include/hal/spimem_flash_ll.h +++ b/components/hal/esp32h2/include/hal/spimem_flash_ll.h @@ -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. * diff --git a/components/hal/esp32s2/include/hal/gpspi_flash_ll.h b/components/hal/esp32s2/include/hal/gpspi_flash_ll.h index 4a0d1c086b94..c839a98bc5c4 100644 --- a/components/hal/esp32s2/include/hal/gpspi_flash_ll.h +++ b/components/hal/esp32s2/include/hal/gpspi_flash_ll.h @@ -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. * diff --git a/components/hal/esp32s2/include/hal/spi_flash_ll.h b/components/hal/esp32s2/include/hal/spi_flash_ll.h index 1aa9d800b5fa..b5da668b164e 100644 --- a/components/hal/esp32s2/include/hal/spi_flash_ll.h +++ b/components/hal/esp32s2/include/hal/spi_flash_ll.h @@ -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 diff --git a/components/hal/esp32s2/include/hal/spimem_flash_ll.h b/components/hal/esp32s2/include/hal/spimem_flash_ll.h index f4bf54f3f09d..ed2dc569e4cc 100644 --- a/components/hal/esp32s2/include/hal/spimem_flash_ll.h +++ b/components/hal/esp32s2/include/hal/spimem_flash_ll.h @@ -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. * diff --git a/components/hal/esp32s3/include/hal/gpspi_flash_ll.h b/components/hal/esp32s3/include/hal/gpspi_flash_ll.h index aab2e4d4ecfa..c9deccb6b6b2 100644 --- a/components/hal/esp32s3/include/hal/gpspi_flash_ll.h +++ b/components/hal/esp32s3/include/hal/gpspi_flash_ll.h @@ -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. * diff --git a/components/hal/esp32s3/include/hal/spi_flash_ll.h b/components/hal/esp32s3/include/hal/spi_flash_ll.h index f9d2ecadf661..7c046ea7df1b 100644 --- a/components/hal/esp32s3/include/hal/spi_flash_ll.h +++ b/components/hal/esp32s3/include/hal/spi_flash_ll.h @@ -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 diff --git a/components/hal/esp32s3/include/hal/spimem_flash_ll.h b/components/hal/esp32s3/include/hal/spimem_flash_ll.h index 4e8d5744f65f..ddc49a9f4ec8 100644 --- a/components/hal/esp32s3/include/hal/spimem_flash_ll.h +++ b/components/hal/esp32s3/include/hal/spimem_flash_ll.h @@ -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. * diff --git a/components/hal/include/hal/spi_flash_hal.h b/components/hal/include/hal/spi_flash_hal.h index a17e1acd11c9..2abf7820f1dc 100644 --- a/components/hal/include/hal/spi_flash_hal.h +++ b/components/hal/include/hal/spi_flash_hal.h @@ -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 { diff --git a/components/hal/include/hal/spi_flash_types.h b/components/hal/include/hal/spi_flash_types.h index 53d22d9e26bc..d36332e2d8a4 100644 --- a/components/hal/include/hal/spi_flash_types.h +++ b/components/hal/include/hal/spi_flash_types.h @@ -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. diff --git a/components/hal/spi_flash_hal.c b/components/hal/spi_flash_hal.c index 75992468b8f8..30c088bec42b 100644 --- a/components/hal/spi_flash_hal.c +++ b/components/hal/spi_flash_hal.c @@ -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) { diff --git a/components/hal/spi_flash_hal_common.inc b/components/hal/spi_flash_hal_common.inc index 11ba34d2f9f4..f563d61d47f8 100644 --- a/components/hal/spi_flash_hal_common.inc +++ b/components/hal/spi_flash_hal_common.inc @@ -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) { diff --git a/components/hal/spi_flash_hal_iram.c b/components/hal/spi_flash_hal_iram.c index 28f02c202352..3adb6404bd62 100644 --- a/components/hal/spi_flash_hal_iram.c +++ b/components/hal/spi_flash_hal_iram.c @@ -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 diff --git a/components/soc/esp32c3/include/soc/spi_mem_struct.h b/components/soc/esp32c3/include/soc/spi_mem_struct.h index 4b78f184d006..d816a9a290ad 100644 --- a/components/soc/esp32c3/include/soc/spi_mem_struct.h +++ b/components/soc/esp32c3/include/soc/spi_mem_struct.h @@ -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; diff --git a/components/soc/esp32s2/include/soc/Kconfig.soc_caps.in b/components/soc/esp32s2/include/soc/Kconfig.soc_caps.in index 0304ed5edfbc..55d870e48ed4 100644 --- a/components/soc/esp32s2/include/soc/Kconfig.soc_caps.in +++ b/components/soc/esp32s2/include/soc/Kconfig.soc_caps.in @@ -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 diff --git a/components/soc/esp32s2/include/soc/soc_caps.h b/components/soc/esp32s2/include/soc/soc_caps.h index 5bcdb6626530..287683578cc5 100644 --- a/components/soc/esp32s2/include/soc/soc_caps.h +++ b/components/soc/esp32s2/include/soc/soc_caps.h @@ -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) diff --git a/components/spi_flash/.build-test-rules.yml b/components/spi_flash/.build-test-rules.yml index 6f208cb0ab60..aaa7c7412357 100644 --- a/components/spi_flash/.build-test-rules.yml +++ b/components/spi_flash/.build-test-rules.yml @@ -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 diff --git a/components/spi_flash/Kconfig b/components/spi_flash/Kconfig index a0c5e0e0bf4c..3a9f1240f338 100644 --- a/components/spi_flash/Kconfig +++ b/components/spi_flash/Kconfig @@ -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. diff --git a/components/spi_flash/esp_flash_spi_init.c b/components/spi_flash/esp_flash_spi_init.c index cda8449b6819..03037c472d23 100644 --- a/components/spi_flash/esp_flash_spi_init.c +++ b/components/spi_flash/esp_flash_spi_init.c @@ -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) { diff --git a/components/spi_flash/spi_flash_chip_gd.c b/components/spi_flash/spi_flash_chip_gd.c index d8696177dd67..9aabf52972d5 100644 --- a/components/spi_flash/spi_flash_chip_gd.c +++ b/components/spi_flash/spi_flash_chip_gd.c @@ -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, diff --git a/components/spi_flash/spi_flash_chip_winbond.c b/components/spi_flash/spi_flash_chip_winbond.c index 6bec557716f0..8da9714e32b5 100644 --- a/components/spi_flash/spi_flash_chip_winbond.c +++ b/components/spi_flash/spi_flash_chip_winbond.c @@ -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 #include @@ -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); } diff --git a/components/spi_flash/test_apps/esp_flash/main/test_esp_flash_drv.c b/components/spi_flash/test_apps/esp_flash/main/test_esp_flash_drv.c index 814c9a5d729a..cf72f2985964 100644 --- a/components/spi_flash/test_apps/esp_flash/main/test_esp_flash_drv.c +++ b/components/spi_flash/test_apps/esp_flash/main/test_esp_flash_drv.c @@ -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; diff --git a/tools/ci/check_copyright_ignore.txt b/tools/ci/check_copyright_ignore.txt index 9d838d8aaee8..7fab49500404 100644 --- a/tools/ci/check_copyright_ignore.txt +++ b/tools/ci/check_copyright_ignore.txt @@ -999,7 +999,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 From 15153b5598b4bd898e0213040cd5a77423866660 Mon Sep 17 00:00:00 2001 From: Cao Sen Miao Date: Thu, 11 May 2023 20:12:22 +0800 Subject: [PATCH 2/3] spi_flash: Update documents for flash-suspend --- .../spi_flash/flash_auto_suspend_timing.json | 23 +++++++++++++++++++ .../peripherals/spi_flash/auto_suspend.inc | 13 +++++++++++ .../spi_flash/spi_flash_concurrency.rst | 2 +- .../spi_flash/spi_flash_optional_feature.rst | 13 ++++++++--- .../spi_flash/spi_flash_concurrency.rst | 2 +- 5 files changed, 48 insertions(+), 5 deletions(-) create mode 100644 docs/_static/diagrams/spi_flash/flash_auto_suspend_timing.json diff --git a/docs/_static/diagrams/spi_flash/flash_auto_suspend_timing.json b/docs/_static/diagrams/spi_flash/flash_auto_suspend_timing.json new file mode 100644 index 000000000000..b9abb1b3cbdc --- /dev/null +++ b/docs/_static/diagrams/spi_flash/flash_auto_suspend_timing.json @@ -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" + ] +} diff --git a/docs/en/api-reference/peripherals/spi_flash/auto_suspend.inc b/docs/en/api-reference/peripherals/spi_flash/auto_suspend.inc index 74460ecff65a..59f48c688690 100644 --- a/docs/en/api-reference/peripherals/spi_flash/auto_suspend.inc +++ b/docs/en/api-reference/peripherals/spi_flash/auto_suspend.inc @@ -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. diff --git a/docs/en/api-reference/peripherals/spi_flash/spi_flash_concurrency.rst b/docs/en/api-reference/peripherals/spi_flash/spi_flash_concurrency.rst index adb95766ad8e..3885e712857b 100644 --- a/docs/en/api-reference/peripherals/spi_flash/spi_flash_concurrency.rst +++ b/docs/en/api-reference/peripherals/spi_flash/spi_flash_concurrency.rst @@ -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 diff --git a/docs/en/api-reference/peripherals/spi_flash/spi_flash_optional_feature.rst b/docs/en/api-reference/peripherals/spi_flash/spi_flash_optional_feature.rst index 7ead7b2bf8e7..2f1fe589179f 100644 --- a/docs/en/api-reference/peripherals/spi_flash/spi_flash_optional_feature.rst +++ b/docs/en/api-reference/peripherals/spi_flash/spi_flash_optional_feature.rst @@ -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 --------------- diff --git a/docs/zh_CN/api-reference/peripherals/spi_flash/spi_flash_concurrency.rst b/docs/zh_CN/api-reference/peripherals/spi_flash/spi_flash_concurrency.rst index 160cf9f49a9d..ffb387f4e596 100644 --- a/docs/zh_CN/api-reference/peripherals/spi_flash/spi_flash_concurrency.rst +++ b/docs/zh_CN/api-reference/peripherals/spi_flash/spi_flash_concurrency.rst @@ -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 From 5bd535e9e04e90ca9919d898661b26ae97b24e0b Mon Sep 17 00:00:00 2001 From: Cao Sen Miao Date: Thu, 11 May 2023 20:12:41 +0800 Subject: [PATCH 3/3] spi_flash: Add strict test for flash suspend --- .../test_apps/flash_suspend/CMakeLists.txt | 7 + .../test_apps/flash_suspend/README.md | 2 + .../flash_suspend/main/CMakeLists.txt | 7 + .../flash_suspend/main/test_app_main.c | 54 ++++++ .../flash_suspend/main/test_flash_suspend.c | 163 ++++++++++++++++++ .../test_apps/flash_suspend/partitions.csv | 6 + .../pytest_flash_auto_suspend.py | 18 ++ .../flash_suspend/sdkconfig.ci.release | 5 + .../flash_suspend/sdkconfig.defaults | 4 + 9 files changed, 266 insertions(+) create mode 100644 components/spi_flash/test_apps/flash_suspend/CMakeLists.txt create mode 100644 components/spi_flash/test_apps/flash_suspend/README.md create mode 100644 components/spi_flash/test_apps/flash_suspend/main/CMakeLists.txt create mode 100644 components/spi_flash/test_apps/flash_suspend/main/test_app_main.c create mode 100644 components/spi_flash/test_apps/flash_suspend/main/test_flash_suspend.c create mode 100644 components/spi_flash/test_apps/flash_suspend/partitions.csv create mode 100644 components/spi_flash/test_apps/flash_suspend/pytest_flash_auto_suspend.py create mode 100644 components/spi_flash/test_apps/flash_suspend/sdkconfig.ci.release create mode 100644 components/spi_flash/test_apps/flash_suspend/sdkconfig.defaults diff --git a/components/spi_flash/test_apps/flash_suspend/CMakeLists.txt b/components/spi_flash/test_apps/flash_suspend/CMakeLists.txt new file mode 100644 index 000000000000..46da0ba372b4 --- /dev/null +++ b/components/spi_flash/test_apps/flash_suspend/CMakeLists.txt @@ -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) diff --git a/components/spi_flash/test_apps/flash_suspend/README.md b/components/spi_flash/test_apps/flash_suspend/README.md new file mode 100644 index 000000000000..f39c17bef3ce --- /dev/null +++ b/components/spi_flash/test_apps/flash_suspend/README.md @@ -0,0 +1,2 @@ +| Supported Targets | ESP32-C2 | ESP32-C3 | ESP32-C6 | ESP32-H2 | ESP32-S3 | +| ----------------- | -------- | -------- | -------- | -------- | -------- | diff --git a/components/spi_flash/test_apps/flash_suspend/main/CMakeLists.txt b/components/spi_flash/test_apps/flash_suspend/main/CMakeLists.txt new file mode 100644 index 000000000000..e017e8371ec0 --- /dev/null +++ b/components/spi_flash/test_apps/flash_suspend/main/CMakeLists.txt @@ -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) diff --git a/components/spi_flash/test_apps/flash_suspend/main/test_app_main.c b/components/spi_flash/test_apps/flash_suspend/main/test_app_main.c new file mode 100644 index 000000000000..a626f5e8cd69 --- /dev/null +++ b/components/spi_flash/test_apps/flash_suspend/main/test_app_main.c @@ -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(); +} diff --git a/components/spi_flash/test_apps/flash_suspend/main/test_flash_suspend.c b/components/spi_flash/test_apps/flash_suspend/main/test_flash_suspend.c new file mode 100644 index 000000000000..cde14fe00a47 --- /dev/null +++ b/components/spi_flash/test_apps/flash_suspend/main/test_flash_suspend.c @@ -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 +#include +#include +#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)); +} diff --git a/components/spi_flash/test_apps/flash_suspend/partitions.csv b/components/spi_flash/test_apps/flash_suspend/partitions.csv new file mode 100644 index 000000000000..6b057cd35444 --- /dev/null +++ b/components/spi_flash/test_apps/flash_suspend/partitions.csv @@ -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, diff --git a/components/spi_flash/test_apps/flash_suspend/pytest_flash_auto_suspend.py b/components/spi_flash/test_apps/flash_suspend/pytest_flash_auto_suspend.py new file mode 100644 index 000000000000..f24ef1b8bcb6 --- /dev/null +++ b/components/spi_flash/test_apps/flash_suspend/pytest_flash_auto_suspend.py @@ -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) diff --git a/components/spi_flash/test_apps/flash_suspend/sdkconfig.ci.release b/components/spi_flash/test_apps/flash_suspend/sdkconfig.ci.release new file mode 100644 index 000000000000..b45157852056 --- /dev/null +++ b/components/spi_flash/test_apps/flash_suspend/sdkconfig.ci.release @@ -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 diff --git a/components/spi_flash/test_apps/flash_suspend/sdkconfig.defaults b/components/spi_flash/test_apps/flash_suspend/sdkconfig.defaults new file mode 100644 index 000000000000..cdb818d6f9bb --- /dev/null +++ b/components/spi_flash/test_apps/flash_suspend/sdkconfig.defaults @@ -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"