From 3e79641afae0fbe6b69c2c14640145f21e55e4e0 Mon Sep 17 00:00:00 2001 From: "C.S.M" Date: Mon, 10 Feb 2025 13:51:11 +0800 Subject: [PATCH] test(spi_flash): Add framework to test driver can work under flash auto-suspend --- .../hal/esp32c2/include/hal/spimem_flash_ll.h | 76 +++++++++- .../hal/esp32c3/include/hal/spimem_flash_ll.h | 76 +++++++++- .../hal/esp32c5/include/hal/spimem_flash_ll.h | 76 +++++++++- .../hal/esp32c6/include/hal/spimem_flash_ll.h | 76 +++++++++- .../esp32c61/include/hal/spimem_flash_ll.h | 76 +++++++++- .../hal/esp32h2/include/hal/spimem_flash_ll.h | 76 +++++++++- .../hal/esp32p4/include/hal/spimem_flash_ll.h | 76 +++++++++- .../hal/esp32s3/include/hal/spimem_flash_ll.h | 76 +++++++++- .../test_flash_utils/CMakeLists.txt | 2 +- .../include/test_flash_utils.h | 76 +++++++++- .../test_flash_utils/test_flash_utils.c | 128 +++++++++++++++- .../flash_suspend/main/CMakeLists.txt | 8 +- .../flash_suspend/main/idf_component.yml | 3 + .../flash_suspend/main/test_i2c_flash_text.c | 142 ++++++++++++++++++ .../pytest_flash_auto_suspend.py | 2 +- .../flash_suspend/sdkconfig.ci.i2c_isr_flash | 1 + 16 files changed, 956 insertions(+), 14 deletions(-) create mode 100644 components/spi_flash/test_apps/flash_suspend/main/idf_component.yml create mode 100644 components/spi_flash/test_apps/flash_suspend/main/test_i2c_flash_text.c create mode 100644 components/spi_flash/test_apps/flash_suspend/sdkconfig.ci.i2c_isr_flash diff --git a/components/hal/esp32c2/include/hal/spimem_flash_ll.h b/components/hal/esp32c2/include/hal/spimem_flash_ll.h index ba7342eede..a01a4f8465 100644 --- a/components/hal/esp32c2/include/hal/spimem_flash_ll.h +++ b/components/hal/esp32c2/include/hal/spimem_flash_ll.h @@ -1,5 +1,5 @@ /* - * SPDX-FileCopyrightText: 2020-2024 Espressif Systems (Shanghai) CO LTD + * SPDX-FileCopyrightText: 2020-2025 Espressif Systems (Shanghai) CO LTD * * SPDX-License-Identifier: Apache-2.0 */ @@ -717,6 +717,80 @@ static inline void spimem_flash_ll_set_common_command_register_info(spi_mem_dev_ dev->user2.val = user2_reg; } +// For flash test utils + +#define SPIMEM_FLASH_LL_SUSPEND_END_INTR SPI_MEM_PES_END_INT_ENA_M +#define SPIMEM_FLASH_LL_INTERRUPT_SOURCE ETS_SPI1_INTR_SOURCE + +/** + * @brief Get the address of the interrupt status register. + * + * This function returns a pointer to the interrupt status register of the SPI memory device. + * + * @param[in] dev Pointer to the SPI memory device structure. + * @return volatile void* Pointer to the interrupt status register. + */ +static inline volatile void *spimem_flash_ll_get_interrupt_status_reg(spi_mem_dev_t *dev) +{ + return &dev->int_st; +} + +/** + * @brief Clear specific interrupt status bits. + * + * This function clears the specified interrupt bits in the interrupt clear register of the SPI memory device. + * + * @param[in] dev Pointer to the SPI memory device structure. + * @param[in] mask Bitmask specifying which interrupt bits to clear. + */ +__attribute__((always_inline)) +static inline void spimem_flash_ll_clear_intr_mask(spi_mem_dev_t *dev, uint32_t mask) +{ + dev->int_clr.val = mask; +} + +/** + * @brief Enable specific interrupt bits. + * + * This function enables the specified interrupts in the interrupt enable register of the SPI memory device. + * + * @param[in] dev Pointer to the SPI memory device structure. + * @param[in] mask Bitmask specifying which interrupt bits to enable. + */ +__attribute__((always_inline)) +static inline void spimem_flash_ll_enable_intr_mask(spi_mem_dev_t *dev, uint32_t mask) +{ + dev->int_ena.val |= mask; +} + +/** + * @brief Disable specific interrupt bits. + * + * This function disables the specified interrupts in the interrupt enable register of the SPI memory device. + * + * @param[in] dev Pointer to the SPI memory device structure. + * @param[in] mask Bitmask specifying which interrupt bits to disable. + */ +__attribute__((always_inline)) +static inline void spimem_flash_ll_disable_intr_mask(spi_mem_dev_t *dev, uint32_t mask) +{ + dev->int_ena.val &= (~mask); +} + +/** + * @brief Get the current interrupt status. + * + * This function retrieves the current interrupt status from the interrupt status register of the SPI memory device. + * + * @param[in] dev Pointer to the SPI memory device structure. + * @param[out] intr_status Pointer to a variable where the interrupt status will be stored. + */ +__attribute__((always_inline)) +static inline void spimem_flash_ll_get_intr_mask(spi_mem_dev_t *dev, uint32_t *intr_status) +{ + *intr_status = dev->int_st.val; +} + #ifdef __cplusplus } #endif diff --git a/components/hal/esp32c3/include/hal/spimem_flash_ll.h b/components/hal/esp32c3/include/hal/spimem_flash_ll.h index e2b53f60b0..3f03e7e86a 100644 --- a/components/hal/esp32c3/include/hal/spimem_flash_ll.h +++ b/components/hal/esp32c3/include/hal/spimem_flash_ll.h @@ -1,5 +1,5 @@ /* - * SPDX-FileCopyrightText: 2020-2024 Espressif Systems (Shanghai) CO LTD + * SPDX-FileCopyrightText: 2020-2025 Espressif Systems (Shanghai) CO LTD * * SPDX-License-Identifier: Apache-2.0 */ @@ -732,6 +732,80 @@ static inline void spimem_flash_ll_set_common_command_register_info(spi_mem_dev_ dev->user2.val = user2_reg; } +// For flash test utils + +#define SPIMEM_FLASH_LL_SUSPEND_END_INTR SPI_MEM_PES_END_INT_ENA_M +#define SPIMEM_FLASH_LL_INTERRUPT_SOURCE ETS_SPI1_INTR_SOURCE + +/** + * @brief Get the address of the interrupt status register. + * + * This function returns a pointer to the interrupt status register of the SPI memory device. + * + * @param[in] dev Pointer to the SPI memory device structure. + * @return volatile void* Pointer to the interrupt status register. + */ +static inline volatile void *spimem_flash_ll_get_interrupt_status_reg(spi_mem_dev_t *dev) +{ + return &dev->int_st; +} + +/** + * @brief Clear specific interrupt status bits. + * + * This function clears the specified interrupt bits in the interrupt clear register of the SPI memory device. + * + * @param[in] dev Pointer to the SPI memory device structure. + * @param[in] mask Bitmask specifying which interrupt bits to clear. + */ +__attribute__((always_inline)) +static inline void spimem_flash_ll_clear_intr_mask(spi_mem_dev_t *dev, uint32_t mask) +{ + dev->int_clr.val = mask; +} + +/** + * @brief Enable specific interrupt bits. + * + * This function enables the specified interrupts in the interrupt enable register of the SPI memory device. + * + * @param[in] dev Pointer to the SPI memory device structure. + * @param[in] mask Bitmask specifying which interrupt bits to enable. + */ +__attribute__((always_inline)) +static inline void spimem_flash_ll_enable_intr_mask(spi_mem_dev_t *dev, uint32_t mask) +{ + dev->int_ena.val |= mask; +} + +/** + * @brief Disable specific interrupt bits. + * + * This function disables the specified interrupts in the interrupt enable register of the SPI memory device. + * + * @param[in] dev Pointer to the SPI memory device structure. + * @param[in] mask Bitmask specifying which interrupt bits to disable. + */ +__attribute__((always_inline)) +static inline void spimem_flash_ll_disable_intr_mask(spi_mem_dev_t *dev, uint32_t mask) +{ + dev->int_ena.val &= (~mask); +} + +/** + * @brief Get the current interrupt status. + * + * This function retrieves the current interrupt status from the interrupt status register of the SPI memory device. + * + * @param[in] dev Pointer to the SPI memory device structure. + * @param[out] intr_status Pointer to a variable where the interrupt status will be stored. + */ +__attribute__((always_inline)) +static inline void spimem_flash_ll_get_intr_mask(spi_mem_dev_t *dev, uint32_t *intr_status) +{ + *intr_status = dev->int_st.val; +} + #ifdef __cplusplus } #endif diff --git a/components/hal/esp32c5/include/hal/spimem_flash_ll.h b/components/hal/esp32c5/include/hal/spimem_flash_ll.h index 9b33156f5f..c23dd350c4 100644 --- a/components/hal/esp32c5/include/hal/spimem_flash_ll.h +++ b/components/hal/esp32c5/include/hal/spimem_flash_ll.h @@ -1,5 +1,5 @@ /* - * SPDX-FileCopyrightText: 2022-2024 Espressif Systems (Shanghai) CO LTD + * SPDX-FileCopyrightText: 2022-2025 Espressif Systems (Shanghai) CO LTD * * SPDX-License-Identifier: Apache-2.0 */ @@ -763,6 +763,80 @@ static inline void spimem_flash_ll_set_common_command_register_info(spi_mem_dev_ dev->user2.val = user2_reg; } +// For flash test utils + +#define SPIMEM_FLASH_LL_SUSPEND_END_INTR SPI_MEM_PES_END_INT_ENA_M +#define SPIMEM_FLASH_LL_INTERRUPT_SOURCE ETS_MSPI_INTR_SOURCE + +/** + * @brief Get the address of the interrupt status register. + * + * This function returns a pointer to the interrupt status register of the SPI memory device. + * + * @param[in] dev Pointer to the SPI memory device structure. + * @return volatile void* Pointer to the interrupt status register. + */ +static inline volatile void *spimem_flash_ll_get_interrupt_status_reg(spi_mem_dev_t *dev) +{ + return &dev->int_st; +} + +/** + * @brief Clear specific interrupt status bits. + * + * This function clears the specified interrupt bits in the interrupt clear register of the SPI memory device. + * + * @param[in] dev Pointer to the SPI memory device structure. + * @param[in] mask Bitmask specifying which interrupt bits to clear. + */ +__attribute__((always_inline)) +static inline void spimem_flash_ll_clear_intr_mask(spi_mem_dev_t *dev, uint32_t mask) +{ + dev->int_clr.val = mask; +} + +/** + * @brief Enable specific interrupt bits. + * + * This function enables the specified interrupts in the interrupt enable register of the SPI memory device. + * + * @param[in] dev Pointer to the SPI memory device structure. + * @param[in] mask Bitmask specifying which interrupt bits to enable. + */ +__attribute__((always_inline)) +static inline void spimem_flash_ll_enable_intr_mask(spi_mem_dev_t *dev, uint32_t mask) +{ + dev->int_ena.val |= mask; +} + +/** + * @brief Disable specific interrupt bits. + * + * This function disables the specified interrupts in the interrupt enable register of the SPI memory device. + * + * @param[in] dev Pointer to the SPI memory device structure. + * @param[in] mask Bitmask specifying which interrupt bits to disable. + */ +__attribute__((always_inline)) +static inline void spimem_flash_ll_disable_intr_mask(spi_mem_dev_t *dev, uint32_t mask) +{ + dev->int_ena.val &= (~mask); +} + +/** + * @brief Get the current interrupt status. + * + * This function retrieves the current interrupt status from the interrupt status register of the SPI memory device. + * + * @param[in] dev Pointer to the SPI memory device structure. + * @param[out] intr_status Pointer to a variable where the interrupt status will be stored. + */ +__attribute__((always_inline)) +static inline void spimem_flash_ll_get_intr_mask(spi_mem_dev_t *dev, uint32_t *intr_status) +{ + *intr_status = dev->int_st.val; +} + #ifdef __cplusplus } #endif diff --git a/components/hal/esp32c6/include/hal/spimem_flash_ll.h b/components/hal/esp32c6/include/hal/spimem_flash_ll.h index 9664c87858..7ec09923cf 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-2024 Espressif Systems (Shanghai) CO LTD + * SPDX-FileCopyrightText: 2022-2025 Espressif Systems (Shanghai) CO LTD * * SPDX-License-Identifier: Apache-2.0 */ @@ -751,6 +751,80 @@ static inline void spimem_flash_ll_set_common_command_register_info(spi_mem_dev_ dev->user2.val = user2_reg; } +// For flash test utils + +#define SPIMEM_FLASH_LL_SUSPEND_END_INTR SPI_MEM_PES_END_INT_ENA_M +#define SPIMEM_FLASH_LL_INTERRUPT_SOURCE ETS_MSPI_INTR_SOURCE + +/** + * @brief Get the address of the interrupt status register. + * + * This function returns a pointer to the interrupt status register of the SPI memory device. + * + * @param[in] dev Pointer to the SPI memory device structure. + * @return volatile void* Pointer to the interrupt status register. + */ +static inline volatile void *spimem_flash_ll_get_interrupt_status_reg(spi_mem_dev_t *dev) +{ + return &dev->int_st; +} + +/** + * @brief Clear specific interrupt status bits. + * + * This function clears the specified interrupt bits in the interrupt clear register of the SPI memory device. + * + * @param[in] dev Pointer to the SPI memory device structure. + * @param[in] mask Bitmask specifying which interrupt bits to clear. + */ +__attribute__((always_inline)) +static inline void spimem_flash_ll_clear_intr_mask(spi_mem_dev_t *dev, uint32_t mask) +{ + dev->int_clr.val = mask; +} + +/** + * @brief Enable specific interrupt bits. + * + * This function enables the specified interrupts in the interrupt enable register of the SPI memory device. + * + * @param[in] dev Pointer to the SPI memory device structure. + * @param[in] mask Bitmask specifying which interrupt bits to enable. + */ +__attribute__((always_inline)) +static inline void spimem_flash_ll_enable_intr_mask(spi_mem_dev_t *dev, uint32_t mask) +{ + dev->int_ena.val |= mask; +} + +/** + * @brief Disable specific interrupt bits. + * + * This function disables the specified interrupts in the interrupt enable register of the SPI memory device. + * + * @param[in] dev Pointer to the SPI memory device structure. + * @param[in] mask Bitmask specifying which interrupt bits to disable. + */ +__attribute__((always_inline)) +static inline void spimem_flash_ll_disable_intr_mask(spi_mem_dev_t *dev, uint32_t mask) +{ + dev->int_ena.val &= (~mask); +} + +/** + * @brief Get the current interrupt status. + * + * This function retrieves the current interrupt status from the interrupt status register of the SPI memory device. + * + * @param[in] dev Pointer to the SPI memory device structure. + * @param[out] intr_status Pointer to a variable where the interrupt status will be stored. + */ +__attribute__((always_inline)) +static inline void spimem_flash_ll_get_intr_mask(spi_mem_dev_t *dev, uint32_t *intr_status) +{ + *intr_status = dev->int_st.val; +} + #ifdef __cplusplus } #endif diff --git a/components/hal/esp32c61/include/hal/spimem_flash_ll.h b/components/hal/esp32c61/include/hal/spimem_flash_ll.h index fe8ddf42d6..6ad873b781 100644 --- a/components/hal/esp32c61/include/hal/spimem_flash_ll.h +++ b/components/hal/esp32c61/include/hal/spimem_flash_ll.h @@ -1,5 +1,5 @@ /* - * SPDX-FileCopyrightText: 2024 Espressif Systems (Shanghai) CO LTD + * SPDX-FileCopyrightText: 2024-2025 Espressif Systems (Shanghai) CO LTD * * SPDX-License-Identifier: Apache-2.0 */ @@ -732,6 +732,80 @@ static inline void spimem_flash_ll_set_common_command_register_info(spi_mem_dev_ dev->user2.val = user2_reg; } +// For flash test utils + +#define SPIMEM_FLASH_LL_SUSPEND_END_INTR SPI_MEM_PES_END_INT_ENA_M +#define SPIMEM_FLASH_LL_INTERRUPT_SOURCE ETS_MSPI_INTR_SOURCE + +/** + * @brief Get the address of the interrupt status register. + * + * This function returns a pointer to the interrupt status register of the SPI memory device. + * + * @param[in] dev Pointer to the SPI memory device structure. + * @return volatile void* Pointer to the interrupt status register. + */ +static inline volatile void *spimem_flash_ll_get_interrupt_status_reg(spi_mem_dev_t *dev) +{ + return &dev->int_st; +} + +/** + * @brief Clear specific interrupt status bits. + * + * This function clears the specified interrupt bits in the interrupt clear register of the SPI memory device. + * + * @param[in] dev Pointer to the SPI memory device structure. + * @param[in] mask Bitmask specifying which interrupt bits to clear. + */ +__attribute__((always_inline)) +static inline void spimem_flash_ll_clear_intr_mask(spi_mem_dev_t *dev, uint32_t mask) +{ + dev->int_clr.val = mask; +} + +/** + * @brief Enable specific interrupt bits. + * + * This function enables the specified interrupts in the interrupt enable register of the SPI memory device. + * + * @param[in] dev Pointer to the SPI memory device structure. + * @param[in] mask Bitmask specifying which interrupt bits to enable. + */ +__attribute__((always_inline)) +static inline void spimem_flash_ll_enable_intr_mask(spi_mem_dev_t *dev, uint32_t mask) +{ + dev->int_ena.val |= mask; +} + +/** + * @brief Disable specific interrupt bits. + * + * This function disables the specified interrupts in the interrupt enable register of the SPI memory device. + * + * @param[in] dev Pointer to the SPI memory device structure. + * @param[in] mask Bitmask specifying which interrupt bits to disable. + */ +__attribute__((always_inline)) +static inline void spimem_flash_ll_disable_intr_mask(spi_mem_dev_t *dev, uint32_t mask) +{ + dev->int_ena.val &= (~mask); +} + +/** + * @brief Get the current interrupt status. + * + * This function retrieves the current interrupt status from the interrupt status register of the SPI memory device. + * + * @param[in] dev Pointer to the SPI memory device structure. + * @param[out] intr_status Pointer to a variable where the interrupt status will be stored. + */ +__attribute__((always_inline)) +static inline void spimem_flash_ll_get_intr_mask(spi_mem_dev_t *dev, uint32_t *intr_status) +{ + *intr_status = dev->int_st.val; +} + #ifdef __cplusplus } #endif diff --git a/components/hal/esp32h2/include/hal/spimem_flash_ll.h b/components/hal/esp32h2/include/hal/spimem_flash_ll.h index a97b254d96..1295765679 100644 --- a/components/hal/esp32h2/include/hal/spimem_flash_ll.h +++ b/components/hal/esp32h2/include/hal/spimem_flash_ll.h @@ -1,5 +1,5 @@ /* - * SPDX-FileCopyrightText: 2022-2024 Espressif Systems (Shanghai) CO LTD + * SPDX-FileCopyrightText: 2022-2025 Espressif Systems (Shanghai) CO LTD * * SPDX-License-Identifier: Apache-2.0 */ @@ -732,6 +732,80 @@ static inline void spimem_flash_ll_set_common_command_register_info(spi_mem_dev_ dev->user2.val = user2_reg; } +// For flash test utils + +#define SPIMEM_FLASH_LL_SUSPEND_END_INTR SPI_MEM_PES_END_INT_ENA_M +#define SPIMEM_FLASH_LL_INTERRUPT_SOURCE ETS_MSPI_INTR_SOURCE + +/** + * @brief Get the address of the interrupt status register. + * + * This function returns a pointer to the interrupt status register of the SPI memory device. + * + * @param[in] dev Pointer to the SPI memory device structure. + * @return volatile void* Pointer to the interrupt status register. + */ +static inline volatile void *spimem_flash_ll_get_interrupt_status_reg(spi_mem_dev_t *dev) +{ + return &dev->int_st; +} + +/** + * @brief Clear specific interrupt status bits. + * + * This function clears the specified interrupt bits in the interrupt clear register of the SPI memory device. + * + * @param[in] dev Pointer to the SPI memory device structure. + * @param[in] mask Bitmask specifying which interrupt bits to clear. + */ +__attribute__((always_inline)) +static inline void spimem_flash_ll_clear_intr_mask(spi_mem_dev_t *dev, uint32_t mask) +{ + dev->int_clr.val = mask; +} + +/** + * @brief Enable specific interrupt bits. + * + * This function enables the specified interrupts in the interrupt enable register of the SPI memory device. + * + * @param[in] dev Pointer to the SPI memory device structure. + * @param[in] mask Bitmask specifying which interrupt bits to enable. + */ +__attribute__((always_inline)) +static inline void spimem_flash_ll_enable_intr_mask(spi_mem_dev_t *dev, uint32_t mask) +{ + dev->int_ena.val |= mask; +} + +/** + * @brief Disable specific interrupt bits. + * + * This function disables the specified interrupts in the interrupt enable register of the SPI memory device. + * + * @param[in] dev Pointer to the SPI memory device structure. + * @param[in] mask Bitmask specifying which interrupt bits to disable. + */ +__attribute__((always_inline)) +static inline void spimem_flash_ll_disable_intr_mask(spi_mem_dev_t *dev, uint32_t mask) +{ + dev->int_ena.val &= (~mask); +} + +/** + * @brief Get the current interrupt status. + * + * This function retrieves the current interrupt status from the interrupt status register of the SPI memory device. + * + * @param[in] dev Pointer to the SPI memory device structure. + * @param[out] intr_status Pointer to a variable where the interrupt status will be stored. + */ +__attribute__((always_inline)) +static inline void spimem_flash_ll_get_intr_mask(spi_mem_dev_t *dev, uint32_t *intr_status) +{ + *intr_status = dev->int_st.val; +} + #ifdef __cplusplus } #endif diff --git a/components/hal/esp32p4/include/hal/spimem_flash_ll.h b/components/hal/esp32p4/include/hal/spimem_flash_ll.h index 55ea532bee..c0fdf699b8 100644 --- a/components/hal/esp32p4/include/hal/spimem_flash_ll.h +++ b/components/hal/esp32p4/include/hal/spimem_flash_ll.h @@ -1,5 +1,5 @@ /* - * SPDX-FileCopyrightText: 2022-2024 Espressif Systems (Shanghai) CO LTD + * SPDX-FileCopyrightText: 2022-2025 Espressif Systems (Shanghai) CO LTD * * SPDX-License-Identifier: Apache-2.0 */ @@ -794,6 +794,80 @@ static inline void spimem_flash_ll_set_common_command_register_info(spi_mem_dev_ dev->user2.val = user2_reg; } +// For flash test utils + +#define SPIMEM_FLASH_LL_SUSPEND_END_INTR SPI1_MEM_C_PES_END_INT_ENA_M +#define SPIMEM_FLASH_LL_INTERRUPT_SOURCE ETS_MSPI_INTR_SOURCE + +/** + * @brief Get the address of the interrupt status register. + * + * This function returns a pointer to the interrupt status register of the SPI memory device. + * + * @param[in] dev Pointer to the SPI memory device structure. + * @return volatile void* Pointer to the interrupt status register. + */ +static inline volatile void *spimem_flash_ll_get_interrupt_status_reg(spi_mem_dev_t *dev) +{ + return &dev->int_st; +} + +/** + * @brief Clear specific interrupt status bits. + * + * This function clears the specified interrupt bits in the interrupt clear register of the SPI memory device. + * + * @param[in] dev Pointer to the SPI memory device structure. + * @param[in] mask Bitmask specifying which interrupt bits to clear. + */ +__attribute__((always_inline)) +static inline void spimem_flash_ll_clear_intr_mask(spi_mem_dev_t *dev, uint32_t mask) +{ + dev->int_clr.val = mask; +} + +/** + * @brief Enable specific interrupt bits. + * + * This function enables the specified interrupts in the interrupt enable register of the SPI memory device. + * + * @param[in] dev Pointer to the SPI memory device structure. + * @param[in] mask Bitmask specifying which interrupt bits to enable. + */ +__attribute__((always_inline)) +static inline void spimem_flash_ll_enable_intr_mask(spi_mem_dev_t *dev, uint32_t mask) +{ + dev->int_ena.val |= mask; +} + +/** + * @brief Disable specific interrupt bits. + * + * This function disables the specified interrupts in the interrupt enable register of the SPI memory device. + * + * @param[in] dev Pointer to the SPI memory device structure. + * @param[in] mask Bitmask specifying which interrupt bits to disable. + */ +__attribute__((always_inline)) +static inline void spimem_flash_ll_disable_intr_mask(spi_mem_dev_t *dev, uint32_t mask) +{ + dev->int_ena.val &= (~mask); +} + +/** + * @brief Get the current interrupt status. + * + * This function retrieves the current interrupt status from the interrupt status register of the SPI memory device. + * + * @param[in] dev Pointer to the SPI memory device structure. + * @param[out] intr_status Pointer to a variable where the interrupt status will be stored. + */ +__attribute__((always_inline)) +static inline void spimem_flash_ll_get_intr_mask(spi_mem_dev_t *dev, uint32_t *intr_status) +{ + *intr_status = dev->int_st.val; +} + #ifdef __cplusplus } #endif diff --git a/components/hal/esp32s3/include/hal/spimem_flash_ll.h b/components/hal/esp32s3/include/hal/spimem_flash_ll.h index c0e588b207..26b92fb155 100644 --- a/components/hal/esp32s3/include/hal/spimem_flash_ll.h +++ b/components/hal/esp32s3/include/hal/spimem_flash_ll.h @@ -1,5 +1,5 @@ /* - * SPDX-FileCopyrightText: 2015-2024 Espressif Systems (Shanghai) CO LTD + * SPDX-FileCopyrightText: 2015-2025 Espressif Systems (Shanghai) CO LTD * * SPDX-License-Identifier: Apache-2.0 */ @@ -759,6 +759,80 @@ static inline void spimem_flash_ll_set_common_command_register_info(spi_mem_dev_ dev->user2.val = user2_reg; } +// For flash test utils + +#define SPIMEM_FLASH_LL_SUSPEND_END_INTR SPI_MEM_PES_END_INT_ENA_M +#define SPIMEM_FLASH_LL_INTERRUPT_SOURCE ETS_SPI1_INTR_SOURCE + +/** + * @brief Get the address of the interrupt status register. + * + * This function returns a pointer to the interrupt status register of the SPI memory device. + * + * @param[in] dev Pointer to the SPI memory device structure. + * @return volatile void* Pointer to the interrupt status register. + */ +static inline volatile void *spimem_flash_ll_get_interrupt_status_reg(spi_mem_dev_t *dev) +{ + return &dev->int_st; +} + +/** + * @brief Clear specific interrupt status bits. + * + * This function clears the specified interrupt bits in the interrupt clear register of the SPI memory device. + * + * @param[in] dev Pointer to the SPI memory device structure. + * @param[in] mask Bitmask specifying which interrupt bits to clear. + */ +__attribute__((always_inline)) +static inline void spimem_flash_ll_clear_intr_mask(spi_mem_dev_t *dev, uint32_t mask) +{ + dev->int_clr.val = mask; +} + +/** + * @brief Enable specific interrupt bits. + * + * This function enables the specified interrupts in the interrupt enable register of the SPI memory device. + * + * @param[in] dev Pointer to the SPI memory device structure. + * @param[in] mask Bitmask specifying which interrupt bits to enable. + */ +__attribute__((always_inline)) +static inline void spimem_flash_ll_enable_intr_mask(spi_mem_dev_t *dev, uint32_t mask) +{ + dev->int_ena.val |= mask; +} + +/** + * @brief Disable specific interrupt bits. + * + * This function disables the specified interrupts in the interrupt enable register of the SPI memory device. + * + * @param[in] dev Pointer to the SPI memory device structure. + * @param[in] mask Bitmask specifying which interrupt bits to disable. + */ +__attribute__((always_inline)) +static inline void spimem_flash_ll_disable_intr_mask(spi_mem_dev_t *dev, uint32_t mask) +{ + dev->int_ena.val &= (~mask); +} + +/** + * @brief Get the current interrupt status. + * + * This function retrieves the current interrupt status from the interrupt status register of the SPI memory device. + * + * @param[in] dev Pointer to the SPI memory device structure. + * @param[out] intr_status Pointer to a variable where the interrupt status will be stored. + */ +__attribute__((always_inline)) +static inline void spimem_flash_ll_get_intr_mask(spi_mem_dev_t *dev, uint32_t *intr_status) +{ + *intr_status = dev->int_st.val; +} + #ifdef __cplusplus } #endif diff --git a/components/spi_flash/test_apps/components/test_flash_utils/CMakeLists.txt b/components/spi_flash/test_apps/components/test_flash_utils/CMakeLists.txt index b1e95cf41e..c26beb83fc 100644 --- a/components/spi_flash/test_apps/components/test_flash_utils/CMakeLists.txt +++ b/components/spi_flash/test_apps/components/test_flash_utils/CMakeLists.txt @@ -2,4 +2,4 @@ set(srcs "test_flash_utils.c") idf_component_register(SRCS ${srcs} INCLUDE_DIRS include - REQUIRES esp_partition) + REQUIRES esp_partition spi_flash) diff --git a/components/spi_flash/test_apps/components/test_flash_utils/include/test_flash_utils.h b/components/spi_flash/test_apps/components/test_flash_utils/include/test_flash_utils.h index 19162a77a2..4330e5cb46 100644 --- a/components/spi_flash/test_apps/components/test_flash_utils/include/test_flash_utils.h +++ b/components/spi_flash/test_apps/components/test_flash_utils/include/test_flash_utils.h @@ -1,5 +1,5 @@ /* - * SPDX-FileCopyrightText: 2023 Espressif Systems (Shanghai) CO LTD + * SPDX-FileCopyrightText: 2023-2025 Espressif Systems (Shanghai) CO LTD * * SPDX-License-Identifier: Apache-2.0 */ @@ -8,12 +8,22 @@ #include #include "esp_partition.h" +#include +#include +#include +#include +#include "esp_intr_alloc.h" +#include "esp_rom_sys.h" +#include "esp_partition.h" +#include "soc/soc_caps.h" +#if SOC_SPI_MEM_SUPPORT_AUTO_SUSPEND +#include "hal/spimem_flash_ll.h" +#endif #ifdef __cplusplus extern "C" { #endif - /** * Return the 'flash_test' custom data partition * defined in the custom partition table. @@ -22,6 +32,68 @@ extern "C" { */ const esp_partition_t *get_test_flash_partition(void); +#if SOC_SPI_MEM_SUPPORT_AUTO_SUSPEND +// Forward declaration of the flash test handle structure +typedef struct flash_test_handle_s flash_test_handle_t; + +// Event data structure for flash test (currently empty, can be expanded as needed) +typedef struct { +} flash_test_event_data_t; + +// Callback type definition for handling flash test events +typedef bool (*flash_test_callback_t)(flash_test_handle_t *handle, const flash_test_event_data_t *event_data, void *arg); + +// Structure to hold event callbacks for flash test +typedef struct { + flash_test_callback_t auto_suspend_done; // Callback triggered when auto suspend is complete +} flash_test_event_callbacks_t; + +// Definition of the flash test handle structure +struct flash_test_handle_s { + spi_mem_dev_t *dev; + flash_test_callback_t auto_suspend_done; // Callback for auto suspend event + uint32_t suspend_times; // Counter for the number of suspend operations + intr_handle_t intr_handle; // Handle for the interrupt associated with flash test + uint32_t operation_address; // flash operation address. + uint32_t operation_size; // flash operation size. +}; + +/** + * @brief Initialize the flash test system. + * + * This function sets up the flash test handle and necessary resources. + * + * @param[out] test_handle Pointer to the pointer that will hold the initialized handle. + * @return esp_err_t ESP_OK on success, or an appropriate error code on failure. + */ +esp_err_t spi_flash_suspend_test_init(flash_test_handle_t **test_handle); + +/** + * @brief Deinitialize the flash test system. + * + * This function releases the resources associated with the flash test handle. + * + * @param[in] handle Pointer to the flash test handle to deinitialize. + * @return esp_err_t ESP_OK on success, or an appropriate error code on failure. + */ +esp_err_t spi_flash_suspend_test_deinit(flash_test_handle_t *handle); + +/** + * @brief Finds the last partition in the flash memory. + * + * This function retrieves the last partition available for testing purposes. + * + * @return const esp_partition_t* Pointer to the last partition, or NULL if none found. + */ +const esp_partition_t *spi_flash_suspend_test_find_last_partition(void); + +/** + * @brief Invalidates the flash cache. + */ +void spi_flash_suspend_test_invalidate_cache(void); + +#endif + #ifdef __cplusplus } #endif diff --git a/components/spi_flash/test_apps/components/test_flash_utils/test_flash_utils.c b/components/spi_flash/test_apps/components/test_flash_utils/test_flash_utils.c index 290f632a35..284a93a4f0 100644 --- a/components/spi_flash/test_apps/components/test_flash_utils/test_flash_utils.c +++ b/components/spi_flash/test_apps/components/test_flash_utils/test_flash_utils.c @@ -1,11 +1,21 @@ /* - * SPDX-FileCopyrightText: 2023 Espressif Systems (Shanghai) CO LTD + * SPDX-FileCopyrightText: 2023-2025 Espressif Systems (Shanghai) CO LTD * * SPDX-License-Identifier: Apache-2.0 */ #include +#include #include "test_flash_utils.h" +#include "hal/cache_ll.h" +#include "esp_intr_alloc.h" +#include "esp_flash.h" +#include "esp_rom_sys.h" +#include "esp_partition.h" +#include "esp_log.h" +#include "esp_check.h" + +#define ALIGN_UP_TO_64KB(x) (((x) + 0xFFFF) & ~0xFFFF) const esp_partition_t *get_test_flash_partition(void) { @@ -15,3 +25,119 @@ const esp_partition_t *get_test_flash_partition(void) assert(result != NULL); /* means partition table set wrong */ return result; } + +#if SOC_SPI_MEM_SUPPORT_AUTO_SUSPEND +#include "hal/spimem_flash_ll.h" + +static const char TAG[] = "test-flash"; + +IRAM_ATTR static bool flash_auto_suspend_done(flash_test_handle_t *handle, const flash_test_event_data_t *event_data, void *arg) +{ + handle->suspend_times++; + return false; +} + +IRAM_ATTR static void flash_suspend_isr_handler(void *arg) +{ + flash_test_handle_t *flash_test = (flash_test_handle_t *)arg; + uint32_t val; + spimem_flash_ll_get_intr_mask(flash_test->dev, &val); + spimem_flash_ll_clear_intr_mask(flash_test->dev, val); + + if (val & SPIMEM_FLASH_LL_SUSPEND_END_INTR) { + if (flash_test->auto_suspend_done) { + flash_test->auto_suspend_done(flash_test, NULL, flash_test); + } + } +} + +esp_err_t spi_flash_suspend_test_init(flash_test_handle_t **test_handle) +{ + esp_err_t ret = ESP_OK; + flash_test_handle_t *handle = heap_caps_calloc(1, sizeof(flash_test_handle_t), MALLOC_CAP_DEFAULT); + ESP_RETURN_ON_FALSE(handle, ESP_ERR_NO_MEM, TAG, "no memory for flash test handle"); + + handle->dev = spimem_flash_ll_get_hw(SPI1_HOST); + + handle->auto_suspend_done = flash_auto_suspend_done; + + spimem_flash_ll_enable_intr_mask(handle->dev, SPIMEM_FLASH_LL_SUSPEND_END_INTR); + + ret = esp_intr_alloc_intrstatus(SPIMEM_FLASH_LL_INTERRUPT_SOURCE, ESP_INTR_FLAG_IRAM | ESP_INTR_FLAG_LOWMED, (uint32_t)spimem_flash_ll_get_interrupt_status_reg(handle->dev), SPIMEM_FLASH_LL_SUSPEND_END_INTR, flash_suspend_isr_handler, handle, &handle->intr_handle); + ESP_GOTO_ON_ERROR(ret, err, TAG, "interrupt allocate for spi flash failed"); + + const esp_partition_t *last_partition = NULL; + esp_partition_iterator_t it = esp_partition_find(ESP_PARTITION_TYPE_ANY, ESP_PARTITION_SUBTYPE_ANY, NULL); + + // find the last valid partition + while (it != NULL) { + const esp_partition_t *current_partition = esp_partition_get(it); + if (last_partition == NULL || current_partition->address > last_partition->address) { + last_partition = current_partition; + } + it = esp_partition_next(it); + } + + esp_partition_iterator_release(it); + + uint32_t flash_size; + esp_flash_get_size(last_partition->flash_chip, &flash_size); + + handle->operation_address = ALIGN_UP_TO_64KB(last_partition->address + last_partition->size); + handle->operation_size = flash_size - (last_partition->address + last_partition->size); + + if (handle->operation_size < 64 * 1024) { + ESP_GOTO_ON_ERROR(ret, err, TAG, "reset memory is so small"); + } + + *test_handle = handle; + + return ESP_OK; +err: + if (handle) { + free(handle); + } + return ret; +} + +const esp_partition_t * spi_flash_suspend_test_find_last_partition(void) { + + const esp_partition_t *last_partition = NULL; + esp_partition_iterator_t it = esp_partition_find(ESP_PARTITION_TYPE_ANY, ESP_PARTITION_SUBTYPE_ANY, NULL); + + if (it == NULL) { + ESP_LOGE(TAG, "No partitions found!"); + return NULL; + } + + // find the last valid partition + while (it != NULL) { + const esp_partition_t *current_partition = esp_partition_get(it); + if (last_partition == NULL || current_partition->address > last_partition->address) { + last_partition = current_partition; + } + it = esp_partition_next(it); + } + + esp_partition_iterator_release(it); + + return last_partition; +} + +esp_err_t spi_flash_suspend_test_deinit(flash_test_handle_t *handle) +{ + ESP_RETURN_ON_FALSE(handle, ESP_ERR_INVALID_ARG, TAG, "no memory for flash test handle"); + if (handle->intr_handle) { + ESP_RETURN_ON_ERROR(esp_intr_free(handle->intr_handle), TAG, "delete interrupt service failed"); + } + free(handle); + + return ESP_OK; +} + +void spi_flash_suspend_test_invalidate_cache(void) +{ + cache_ll_invalidate_all(CACHE_LL_LEVEL_ALL, CACHE_TYPE_ALL, CACHE_LL_ID_ALL); +} + +#endif // SOC_SPI_MEM_SUPPORT_AUTO_SUSPEND diff --git a/components/spi_flash/test_apps/flash_suspend/main/CMakeLists.txt b/components/spi_flash/test_apps/flash_suspend/main/CMakeLists.txt index 3423bf3e02..432ac51904 100644 --- a/components/spi_flash/test_apps/flash_suspend/main/CMakeLists.txt +++ b/components/spi_flash/test_apps/flash_suspend/main/CMakeLists.txt @@ -1,8 +1,14 @@ set(srcs "test_app_main.c" "test_flash_suspend.c") + +if(NOT CONFIG_I2C_MASTER_ISR_HANDLER_IN_IRAM) + list(APPEND srcs "test_i2c_flash_text.c") +endif() + + # 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} - PRIV_REQUIRES unity test_utils spi_flash driver esp_partition + PRIV_REQUIRES unity test_utils spi_flash driver esp_partition esp_driver_i2c WHOLE_ARCHIVE) diff --git a/components/spi_flash/test_apps/flash_suspend/main/idf_component.yml b/components/spi_flash/test_apps/flash_suspend/main/idf_component.yml new file mode 100644 index 0000000000..d03fac9acd --- /dev/null +++ b/components/spi_flash/test_apps/flash_suspend/main/idf_component.yml @@ -0,0 +1,3 @@ +dependencies: + test_flash_utils: + path: ${IDF_PATH}/components/spi_flash/test_apps/components/test_flash_utils diff --git a/components/spi_flash/test_apps/flash_suspend/main/test_i2c_flash_text.c b/components/spi_flash/test_apps/flash_suspend/main/test_i2c_flash_text.c new file mode 100644 index 0000000000..e5d68175e7 --- /dev/null +++ b/components/spi_flash/test_apps/flash_suspend/main/test_i2c_flash_text.c @@ -0,0 +1,142 @@ +/* + * SPDX-FileCopyrightText: 2025 Espressif Systems (Shanghai) CO LTD + * + * SPDX-License-Identifier: Unlicense OR CC0-1.0 + */ + +#include +#include +#include +#include "freertos/FreeRTOS.h" +#include "freertos/task.h" +#include "esp_flash.h" +#include "test_flash_utils.h" +#include "esp_err.h" +#include "soc/soc_caps.h" +#include "esp_private/periph_ctrl.h" +#include "driver/i2c_master.h" +#include "esp_log.h" +#include "test_utils.h" +#include "driver/gptimer.h" +#if SOC_SPI_MEM_SUPPORT_AUTO_SUSPEND +#include "hal/spimem_flash_ll.h" + +#define TIMER_RESOLUTION_HZ (1 * 1000 * 1000) // 1MHz resolution + +typedef struct { + flash_test_handle_t *flash_handle; + SemaphoreHandle_t sem; +} spi_flash_test_context_t; + +void spi_flash_suspend_test_task(void *arg) +{ + spi_flash_test_context_t *context = (spi_flash_test_context_t *)arg; + + uint32_t cnt = 200; + while (cnt--) { + if (context->flash_handle->suspend_times != 0) { + break; + } + esp_err_t ret = esp_flash_erase_region(NULL, context->flash_handle->operation_address, context->flash_handle->operation_size); + if (ret != ESP_OK) { + abort(); + } + } + + // Give Semaphore and delete task. + xSemaphoreGive(context->sem); + vTaskDelete(NULL); +} + +static bool IRAM_ATTR gptimer_alarm_suspend_cb(gptimer_handle_t timer, const gptimer_alarm_event_data_t *edata, void *user_ctx) +{ + i2c_master_dev_handle_t dev_handle = (i2c_master_dev_handle_t) user_ctx; + spi_flash_suspend_test_invalidate_cache(); + uint8_t buf[6] = {0x11, 0x22, 0x33, 0x44, 0x55, 0x66}; + i2c_master_transmit(dev_handle, buf, 6, 200); + + return false; +} + +TEST_CASE("Flash suspend support on i2c", "[i2c]") +{ + // Init context. + spi_flash_test_context_t *context = heap_caps_calloc(1, sizeof(spi_flash_test_context_t), MALLOC_CAP_DEFAULT); + assert(context != NULL); + + context->sem = xSemaphoreCreateBinary(); + assert(context->sem != NULL); + + spi_flash_suspend_test_init(&context->flash_handle); + + i2c_master_bus_config_t i2c_bus_config = { + .clk_source = I2C_CLK_SRC_DEFAULT, + .i2c_port = 0, + .scl_io_num = 6, + .sda_io_num = 7, + .glitch_ignore_cnt = 7, + .trans_queue_depth = 37, + .flags.enable_internal_pullup = true, + }; + + i2c_master_bus_handle_t bus_handle; + + TEST_ESP_OK(i2c_new_master_bus(&i2c_bus_config, &bus_handle)); + + i2c_device_config_t dev_cfg = { + .dev_addr_length = I2C_ADDR_BIT_LEN_7, + .device_address = 0x58, + .scl_speed_hz = 400000, + .flags.disable_ack_check = true, + }; + + i2c_master_dev_handle_t dev_handle; + TEST_ESP_OK(i2c_master_bus_add_device(bus_handle, &dev_cfg, &dev_handle)); + + 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)); + + gptimer_alarm_config_t alarm_config = { + .reload_count = 0, + .alarm_count = 1000, + .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, + }; + + TEST_ESP_OK(gptimer_register_event_callbacks(gptimer, &cbs, dev_handle)); + TEST_ESP_OK(gptimer_enable(gptimer)); + TEST_ESP_OK(gptimer_start(gptimer)); + + // Then use gptimer for trigger testing function every time. + // Quit when suspend is triggered. + xTaskCreatePinnedToCore(spi_flash_suspend_test_task, "flash_task", 4096, context, 2, NULL, 0); + + xSemaphoreTake(context->sem, pdMS_TO_TICKS(5000)); // We don't always wait the semaphore + + printf("test finishes, suspend for %ld times\n", context->flash_handle->suspend_times); + TEST_ASSERT_NOT_EQUAL(0, context->flash_handle->suspend_times); + + TEST_ESP_OK(gptimer_stop(gptimer)); + TEST_ESP_OK(gptimer_disable(gptimer)); + TEST_ESP_OK(gptimer_del_timer(gptimer)); + + TEST_ESP_OK(i2c_master_bus_rm_device(dev_handle)); + TEST_ESP_OK(i2c_del_master_bus(bus_handle)); + + // Clear sources + vTaskDelay(10); + vSemaphoreDelete(context->sem); + spi_flash_suspend_test_deinit(context->flash_handle); + free(context); +} + +#endif //SOC_SPI_MEM_SUPPORT_AUTO_SUSPEND 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 index f24ef1b8bc..a1cbb4c0a2 100644 --- 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 @@ -1,6 +1,5 @@ # SPDX-FileCopyrightText: 2023 Espressif Systems (Shanghai) CO LTD # SPDX-License-Identifier: Apache-2.0 - import pytest from pytest_embedded import Dut @@ -11,6 +10,7 @@ from pytest_embedded import Dut 'config', [ 'release', + 'i2c_isr_flash', ], indirect=True, ) diff --git a/components/spi_flash/test_apps/flash_suspend/sdkconfig.ci.i2c_isr_flash b/components/spi_flash/test_apps/flash_suspend/sdkconfig.ci.i2c_isr_flash new file mode 100644 index 0000000000..b80d56a297 --- /dev/null +++ b/components/spi_flash/test_apps/flash_suspend/sdkconfig.ci.i2c_isr_flash @@ -0,0 +1 @@ +CONFIG_I2C_MASTER_ISR_HANDLER_IN_IRAM=n