From 1f37a5f1626bf27a6efb0273e21756b4730250f4 Mon Sep 17 00:00:00 2001 From: KonstantinKondrashov Date: Fri, 12 Jun 2020 18:52:00 +0800 Subject: [PATCH] spi_flash(esp32-s2): Add the workaround of a reboot issue when SPI HW suspend is enabled --- .../hal/esp32/include/hal/spi_flash_ll.h | 45 ++++++++++++++ .../hal/esp32s2/include/hal/spi_flash_ll.h | 5 ++ .../hal/esp32s2/include/hal/spimem_flash_ll.h | 61 +++++++++++++++++++ components/hal/include/hal/spi_flash_hal.h | 7 +++ components/hal/include/hal/spi_flash_types.h | 4 ++ components/hal/spi_flash_hal.c | 11 ++++ components/spi_flash/esp_flash_spi_init.c | 1 + 7 files changed, 134 insertions(+) diff --git a/components/hal/esp32/include/hal/spi_flash_ll.h b/components/hal/esp32/include/hal/spi_flash_ll.h index d7314f8710..1114ac4d84 100644 --- a/components/hal/esp32/include/hal/spi_flash_ll.h +++ b/components/hal/esp32/include/hal/spi_flash_ll.h @@ -117,6 +117,51 @@ static inline void spi_flash_ll_erase_block(spi_dev_t *dev) dev->cmd.flash_be = 1; } +/** + * Suspend erase/program operation. + * + * @param dev Beginning address of the peripheral registers. + */ +static inline void spi_flash_ll_suspend(spi_dev_t *dev) +{ + dev->cmd.flash_pes = 1; +} + +/** + * Resume suspended erase/program operation. + * + * @param dev Beginning address of the peripheral registers. + */ +static inline void spi_flash_ll_resume(spi_dev_t *dev) +{ + dev->cmd.flash_per = 1; +} + +/** + * Initialize auto wait idle mode. (work only for ESP32-S2) + * + * @param dev Beginning address of the peripheral registers. + * @param auto_sus Enable/disable Flash Auto-Suspend. + */ +#define spi_flash_ll_auto_wait_idle_init(...) () + +/** + * Initialize auto wait idle mode (work only for ESP32-S2) + * + * @param dev Beginning address of the peripheral registers. + * @param auto_waiti Enable/disable auto wait-idle function + */ +#define spi_flash_ll_auto_suspend_init(...) () + +/** + * Return the suspend status of erase or program operations. (work only for ESP32-S2) + * + * @param dev Beginning address of the peripheral registers. + * + * @return true if suspended, otherwise false. + */ +#define spi_flash_ll_sus_status(...) ({false;}) + /** * Enable/disable write protection for the flash chip. * diff --git a/components/hal/esp32s2/include/hal/spi_flash_ll.h b/components/hal/esp32s2/include/hal/spi_flash_ll.h index cf20f55bf6..ecfb28b465 100644 --- a/components/hal/esp32s2/include/hal/spi_flash_ll.h +++ b/components/hal/esp32s2/include/hal/spi_flash_ll.h @@ -83,6 +83,8 @@ typedef union { #define spi_flash_ll_erase_chip(dev) spimem_flash_ll_erase_chip((spi_mem_dev_t*)dev) #define spi_flash_ll_erase_sector(dev) spimem_flash_ll_erase_sector((spi_mem_dev_t*)dev) #define spi_flash_ll_erase_block(dev) spimem_flash_ll_erase_block((spi_mem_dev_t*)dev) +#define spi_flash_ll_suspend(dev) spimem_flash_ll_suspend((spi_mem_dev_t*)dev) +#define spi_flash_ll_resume(dev) spimem_flash_ll_resume((spi_mem_dev_t*)dev) #define spi_flash_ll_set_write_protect(dev, wp) spimem_flash_ll_set_write_protect((spi_mem_dev_t*)dev, wp) #define spi_flash_ll_get_buffer_data(dev, buffer, read_len) spimem_flash_ll_get_buffer_data((spi_mem_dev_t*)dev, buffer, read_len) #define spi_flash_ll_set_buffer_data(dev, buffer, len) spimem_flash_ll_set_buffer_data((spi_mem_dev_t*)dev, buffer, len) @@ -103,6 +105,9 @@ typedef union { #define spi_flash_ll_set_dummy(dev, dummy) spimem_flash_ll_set_dummy((spi_mem_dev_t*)dev, dummy) #define spi_flash_ll_set_dummy_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_auto_wait_idle_init(dev, auto_waiti) spimem_flash_ll_auto_wait_idle_init((spi_mem_dev_t*)dev, auto_waiti) +#define spi_flash_ll_auto_suspend_init(dev, auto_sus) spimem_flash_ll_auto_suspend_init((spi_mem_dev_t*)dev, auto_sus) +#define spi_flash_ll_sus_status(dev) spimem_flash_ll_sus_status((spi_mem_dev_t*)dev) #endif diff --git a/components/hal/esp32s2/include/hal/spimem_flash_ll.h b/components/hal/esp32s2/include/hal/spimem_flash_ll.h index ac15632f96..9a283ad31d 100644 --- a/components/hal/esp32s2/include/hal/spimem_flash_ll.h +++ b/components/hal/esp32s2/include/hal/spimem_flash_ll.h @@ -105,6 +105,67 @@ static inline void spimem_flash_ll_erase_block(spi_mem_dev_t *dev) dev->cmd.flash_be = 1; } +/** + * Suspend erase/program operation. + * + * @param dev Beginning address of the peripheral registers. + */ +static inline void spimem_flash_ll_suspend(spi_mem_dev_t *dev) +{ + dev->flash_sus_cmd.flash_pes = 1; +} + +/** + * Resume suspended erase/program operation. + * + * @param dev Beginning address of the peripheral registers. + */ +static inline void spimem_flash_ll_resume(spi_mem_dev_t *dev) +{ + dev->misc.auto_per = 0; // Must disable Hardware Auto-Resume (should not be enabled, ESP32-S2 has bugs). + dev->flash_sus_cmd.flash_per = 1; + while (dev->flash_sus_cmd.flash_per) { }; +} + +/** + * Initialize auto wait idle mode + * + * @param dev Beginning address of the peripheral registers. + * @param auto_sus Enable/disable Flash Auto-Suspend. + */ +static inline void spimem_flash_ll_auto_suspend_init(spi_mem_dev_t *dev, bool auto_sus) +{ + dev->flash_sus_ctrl.flash_pes_command = 0x75; // Set auto suspend command, usually 0x75 + dev->flash_sus_ctrl.flash_per_command = 0x7A; // Set auto resume command, usually 0x7A + // SET_PERI_REG_MASK(SPI_MEM_FLASH_SUS_CMD_REG(1), SPI_MEM_PES_PER_EN_M); // Only on S3 chip + // SET_PERI_REG_MASK(SPI_MEM_FLASH_SUS_CMD_REG(1), SPI_MEM_PESR_IDLE_EN_M); // MUST SET 1, to avoid missing Resume (Only on S3 chip) + dev->flash_sus_ctrl.flash_pes_en = auto_sus; // enable Flash Auto-Suspend. +} + +/** + * Initialize auto wait idle mode + * + * @param dev Beginning address of the peripheral registers. + * @param auto_waiti Enable/disable auto wait-idle function + */ +static inline void spimem_flash_ll_auto_wait_idle_init(spi_mem_dev_t *dev, bool auto_waiti) +{ + dev->flash_waiti_ctrl.waiti_cmd = 0x05; // Set the command to send, to fetch flash status reg value. + dev->flash_waiti_ctrl.waiti_en = auto_waiti; // enable auto wait-idle function. +} + +/** + * Return the suspend status of erase or program operations. + * + * @param dev Beginning address of the peripheral registers. + * + * @return true if suspended, otherwise false. + */ +static inline bool spimem_flash_ll_sus_status(const spi_mem_dev_t *dev) +{ + return dev->sus_status.flash_sus; +} + /** * Enable/disable write protection for the flash chip. * diff --git a/components/hal/include/hal/spi_flash_hal.h b/components/hal/include/hal/spi_flash_hal.h index c17ed9861d..dfd73508a4 100644 --- a/components/hal/include/hal/spi_flash_hal.h +++ b/components/hal/include/hal/spi_flash_hal.h @@ -205,6 +205,13 @@ esp_err_t spi_flash_hal_configure_host_io_mode(spi_flash_host_inst_t *host, uint */ void spi_flash_hal_poll_cmd_done(spi_flash_host_inst_t *host); +/** + * Setup a auto-suspend mode. + * + * @param host The driver context. + */ +void spi_flash_hal_setup_auto_suspend_mode(spi_flash_host_inst_t *host); + /** * Check whether the given buffer can be used as the write buffer directly. If 'chip' is connected to the main SPI bus, we can only write directly from * regions that are accessible ith cache disabled. * diff --git a/components/hal/include/hal/spi_flash_types.h b/components/hal/include/hal/spi_flash_types.h index b763ee3d4a..c361cfc4d2 100644 --- a/components/hal/include/hal/spi_flash_types.h +++ b/components/hal/include/hal/spi_flash_types.h @@ -177,6 +177,10 @@ struct spi_flash_host_driver_s { * modified, the cache needs to be flushed. Left NULL if not supported. */ esp_err_t (*flush_cache)(spi_flash_host_inst_t* host, uint32_t addr, uint32_t size); + /** + * Check the necessity of suspending erase/program operations. + */ + void (*check_suspend)(spi_flash_host_inst_t *host); }; #ifdef __cplusplus diff --git a/components/hal/spi_flash_hal.c b/components/hal/spi_flash_hal.c index 170c117e41..5835462dfc 100644 --- a/components/hal/spi_flash_hal.c +++ b/components/hal/spi_flash_hal.c @@ -71,6 +71,17 @@ static inline int get_dummy_n(bool gpio_is_used, int input_delay_ns, int eff_clk return apb_period_n / apbclk_n; } +#ifdef CONFIG_SPI_FLASH_AUTO_SUSPEND + +void spi_flash_hal_setup_auto_suspend_mode(spi_flash_host_inst_t *host) +{ + mspi_auto_suspend_stub_install(); + spi_flash_ll_auto_wait_idle_init(spi_flash_ll_get_hw(SPI_HOST), true); + spi_flash_ll_auto_suspend_init(spi_flash_ll_get_hw(SPI_HOST), true); +} + +#endif // CONFIG_SPI_FLASH_AUTO_SUSPEND + esp_err_t spi_flash_hal_init(spi_flash_hal_context_t *data_out, const spi_flash_hal_config_t *cfg) { if (!esp_ptr_internal(data_out) && cfg->host_id == SPI1_HOST) { diff --git a/components/spi_flash/esp_flash_spi_init.c b/components/spi_flash/esp_flash_spi_init.c index 5095d2f6a7..65d9d2af92 100644 --- a/components/spi_flash/esp_flash_spi_init.c +++ b/components/spi_flash/esp_flash_spi_init.c @@ -23,6 +23,7 @@ #include "esp_heap_caps.h" #include "hal/spi_types.h" #include "driver/spi_common_internal.h" +#include "hal/spi_flash_hal.h" #include "esp_flash_internal.h" #include "esp_rom_gpio.h" #if CONFIG_IDF_TARGET_ESP32