Przeglądaj źródła

Merge branch 'bugfix/xmc_overerase' into 'master'

bootloader: add xmc spi_flash startup flow to improve reliability

See merge request espressif/esp-idf!13895
Michael (XIAO Xufeng) 4 lat temu
rodzic
commit
a0d2efe1be

+ 9 - 0
components/bootloader/Kconfig.projbuild

@@ -386,6 +386,15 @@ menu "Bootloader config"
             in this area of memory, you can increase it. It must be a multiple of 4 bytes.
             in this area of memory, you can increase it. It must be a multiple of 4 bytes.
             This area (rtc_retain_mem_t) is reserved and has access from the bootloader and an application.
             This area (rtc_retain_mem_t) is reserved and has access from the bootloader and an application.
 
 
+    config BOOTLOADER_FLASH_XMC_SUPPORT
+        bool "Enable the support for flash chips of XMC (READ HELP FIRST)"
+        default y
+        help
+            Perform the startup flow recommended by XMC. Please consult XMC for the details of this flow.
+            XMC chips will be forbidden to be used, when this option is disabled.
+
+            DON'T DISABLE THIS UNLESS YOU KNOW WHAT YOU ARE DOING.
+
 endmenu  # Bootloader
 endmenu  # Bootloader
 
 
 
 

+ 15 - 0
components/bootloader_support/include/bootloader_flash.h

@@ -14,6 +14,14 @@
 extern "C" {
 extern "C" {
 #endif
 #endif
 
 
+/**
+ * @brief Read flash ID by sending RDID command (0x9F)
+ * @return flash raw ID
+ *     mfg_id = (ID >> 16) & 0xFF;
+       flash_id = ID & 0xffff;
+ */
+uint32_t bootloader_read_flash_id(void);
+
 #if SOC_CACHE_SUPPORT_WRAP
 #if SOC_CACHE_SUPPORT_WRAP
 /**
 /**
  * @brief Set the burst mode setting command for specified wrap mode.
  * @brief Set the burst mode setting command for specified wrap mode.
@@ -32,6 +40,13 @@ esp_err_t bootloader_flash_wrap_set(spi_flash_wrap_mode_t mode);
   */
   */
 esp_err_t bootloader_flash_unlock(void);
 esp_err_t bootloader_flash_unlock(void);
 
 
+/**
+ * @brief Startup flow recommended by XMC. Call at startup before any erase/write operation.
+ *
+ * @return ESP_OK When startup successfully, otherwise ESP_FAIL (indiciating you should reboot before erase/write).
+ */
+esp_err_t bootloader_flash_xmc_startup(void);
+
 #ifdef __cplusplus
 #ifdef __cplusplus
 }
 }
 #endif
 #endif

+ 10 - 0
components/bootloader_support/include_bootloader/bootloader_flash_priv.h

@@ -29,6 +29,7 @@
 #define CMD_RDSR       0x05
 #define CMD_RDSR       0x05
 #define CMD_RDSR2      0x35 /* Not all SPI flash uses this command */
 #define CMD_RDSR2      0x35 /* Not all SPI flash uses this command */
 #define CMD_OTPEN      0x3A /* Enable OTP mode, not all SPI flash uses this command */
 #define CMD_OTPEN      0x3A /* Enable OTP mode, not all SPI flash uses this command */
+#define CMD_RDSFDP     0x5A /* Read the SFDP of the flash */
 #define CMD_WRAP       0x77 /* Set burst with wrap command */
 #define CMD_WRAP       0x77 /* Set burst with wrap command */
 #define CMD_RESUME     0x7A /* Resume command to clear flash suspend bit */
 #define CMD_RESUME     0x7A /* Resume command to clear flash suspend bit */
 
 
@@ -156,6 +157,15 @@ static inline uint32_t bootloader_cache_pages_to_map(uint32_t size, uint32_t vad
  */
  */
 uint32_t bootloader_execute_flash_command(uint8_t command, uint32_t mosi_data, uint8_t mosi_len, uint8_t miso_len);
 uint32_t bootloader_execute_flash_command(uint8_t command, uint32_t mosi_data, uint8_t mosi_len, uint8_t miso_len);
 
 
+/**
+ * @brief Read the SFDP of the flash
+ *
+ * @param sfdp_addr Address of the parameter to read
+ * @param miso_byte_num Bytes to read
+ * @return The read SFDP, little endian, 4 bytes at most
+ */
+uint32_t bootloader_flash_read_sfdp(uint32_t sfdp_addr, unsigned int miso_byte_num);
+
 /**
 /**
  * @brief Enable the flash write protect (WEL bit).
  * @brief Enable the flash write protect (WEL bit).
  */
  */

+ 175 - 22
components/bootloader_support/src/bootloader_flash.c

@@ -122,7 +122,7 @@ esp_err_t bootloader_flash_erase_range(uint32_t start_addr, uint32_t size)
     return spi_flash_erase_range(start_addr, size);
     return spi_flash_erase_range(start_addr, size);
 }
 }
 
 
-#else
+#else //BOOTLOADER_BUILD
 /* Bootloader version, uses ROM functions only */
 /* Bootloader version, uses ROM functions only */
 #if CONFIG_IDF_TARGET_ESP32
 #if CONFIG_IDF_TARGET_ESP32
 #include "esp32/rom/spi_flash.h"
 #include "esp32/rom/spi_flash.h"
@@ -481,7 +481,8 @@ esp_err_t bootloader_flash_erase_range(uint32_t start_addr, uint32_t size)
     return spi_to_esp_err(rc);
     return spi_to_esp_err(rc);
 }
 }
 
 
-#endif
+#endif // BOOTLOADER_BUILD
+
 
 
 FORCE_INLINE_ATTR bool is_issi_chip(const esp_rom_spiflash_chip_t* chip)
 FORCE_INLINE_ATTR bool is_issi_chip(const esp_rom_spiflash_chip_t* chip)
 {
 {
@@ -563,29 +564,47 @@ esp_err_t IRAM_ATTR __attribute__((weak)) bootloader_flash_unlock(void)
     return err;
     return err;
 }
 }
 
 
+/* dummy_len_plus values defined in ROM for SPI flash configuration */
 #ifndef g_rom_spiflash_dummy_len_plus // ESP32-C3 uses a macro to access ROM data here
 #ifndef g_rom_spiflash_dummy_len_plus // ESP32-C3 uses a macro to access ROM data here
 extern uint8_t g_rom_spiflash_dummy_len_plus[];
 extern uint8_t g_rom_spiflash_dummy_len_plus[];
 #endif
 #endif
-uint32_t IRAM_ATTR bootloader_execute_flash_command(uint8_t command, uint32_t mosi_data, uint8_t mosi_len, uint8_t miso_len)
+IRAM_ATTR static uint32_t bootloader_flash_execute_command_common(
+    uint8_t command,
+    uint32_t addr_len, uint32_t address,
+    uint8_t dummy_len,
+    uint8_t mosi_len, uint32_t mosi_data,
+    uint8_t miso_len)
 {
 {
+    assert(mosi_len <= 32);
+    assert(miso_len <= 32);
     uint32_t old_ctrl_reg = SPIFLASH.ctrl.val;
     uint32_t old_ctrl_reg = SPIFLASH.ctrl.val;
 #if CONFIG_IDF_TARGET_ESP32
 #if CONFIG_IDF_TARGET_ESP32
     SPIFLASH.ctrl.val = SPI_WP_REG_M; // keep WP high while idle, otherwise leave DIO mode
     SPIFLASH.ctrl.val = SPI_WP_REG_M; // keep WP high while idle, otherwise leave DIO mode
 #else
 #else
     SPIFLASH.ctrl.val = SPI_MEM_WP_REG_M; // keep WP high while idle, otherwise leave DIO mode
     SPIFLASH.ctrl.val = SPI_MEM_WP_REG_M; // keep WP high while idle, otherwise leave DIO mode
 #endif
 #endif
-    SPIFLASH.user.usr_dummy = 0;
-    SPIFLASH.user.usr_addr = 0;
+    //command phase
     SPIFLASH.user.usr_command = 1;
     SPIFLASH.user.usr_command = 1;
     SPIFLASH.user2.usr_command_bitlen = 7;
     SPIFLASH.user2.usr_command_bitlen = 7;
-
     SPIFLASH.user2.usr_command_value = command;
     SPIFLASH.user2.usr_command_value = command;
-    SPIFLASH.user.usr_miso = miso_len > 0;
+    //addr phase
+    SPIFLASH.user.usr_addr = addr_len > 0;
+    SPIFLASH.user1.usr_addr_bitlen = addr_len - 1;
 #if CONFIG_IDF_TARGET_ESP32
 #if CONFIG_IDF_TARGET_ESP32
-    SPIFLASH.miso_dlen.usr_miso_dbitlen = miso_len ? (miso_len - 1) : 0;
+    SPIFLASH.addr = (addr_len > 0)? (address << (32-addr_len)) : 0;
 #else
 #else
-    SPIFLASH.miso_dlen.usr_miso_bit_len = miso_len ? (miso_len - 1) : 0;
+    SPIFLASH.addr = address;
 #endif
 #endif
+    //dummy phase
+    if (miso_len > 0) {
+        uint32_t total_dummy = dummy_len + g_rom_spiflash_dummy_len_plus[1];
+        SPIFLASH.user.usr_dummy = total_dummy > 0;
+        SPIFLASH.user1.usr_dummy_cyclelen = total_dummy - 1;
+    } else {
+        SPIFLASH.user.usr_dummy = 0;
+        SPIFLASH.user1.usr_dummy_cyclelen = 0;
+    }
+    //output data
     SPIFLASH.user.usr_mosi = mosi_len > 0;
     SPIFLASH.user.usr_mosi = mosi_len > 0;
 #if CONFIG_IDF_TARGET_ESP32
 #if CONFIG_IDF_TARGET_ESP32
     SPIFLASH.mosi_dlen.usr_mosi_dbitlen = mosi_len ? (mosi_len - 1) : 0;
     SPIFLASH.mosi_dlen.usr_mosi_dbitlen = mosi_len ? (mosi_len - 1) : 0;
@@ -593,24 +612,50 @@ uint32_t IRAM_ATTR bootloader_execute_flash_command(uint8_t command, uint32_t mo
     SPIFLASH.mosi_dlen.usr_mosi_bit_len = mosi_len ? (mosi_len - 1) : 0;
     SPIFLASH.mosi_dlen.usr_mosi_bit_len = mosi_len ? (mosi_len - 1) : 0;
 #endif
 #endif
     SPIFLASH.data_buf[0] = mosi_data;
     SPIFLASH.data_buf[0] = mosi_data;
-
-    if (g_rom_spiflash_dummy_len_plus[1]) {
-        /* When flash pins are mapped via GPIO matrix, need a dummy cycle before reading via MISO */
-        if (miso_len > 0) {
-            SPIFLASH.user.usr_dummy = 1;
-            SPIFLASH.user1.usr_dummy_cyclelen = g_rom_spiflash_dummy_len_plus[1] - 1;
-        } else {
-            SPIFLASH.user.usr_dummy = 0;
-            SPIFLASH.user1.usr_dummy_cyclelen = 0;
-        }
-    }
+    //input data
+    SPIFLASH.user.usr_miso = miso_len > 0;
+#if CONFIG_IDF_TARGET_ESP32
+    SPIFLASH.miso_dlen.usr_miso_dbitlen = miso_len ? (miso_len - 1) : 0;
+#else
+    SPIFLASH.miso_dlen.usr_miso_bit_len = miso_len ? (miso_len - 1) : 0;
+#endif
 
 
     SPIFLASH.cmd.usr = 1;
     SPIFLASH.cmd.usr = 1;
     while (SPIFLASH.cmd.usr != 0) {
     while (SPIFLASH.cmd.usr != 0) {
     }
     }
-
     SPIFLASH.ctrl.val = old_ctrl_reg;
     SPIFLASH.ctrl.val = old_ctrl_reg;
-    return SPIFLASH.data_buf[0];
+
+    uint32_t ret = SPIFLASH.data_buf[0];
+    if (miso_len < 32) {
+        //set unused bits to 0
+        ret &= ~(UINT32_MAX << miso_len);
+    }
+    return ret;
+}
+
+uint32_t IRAM_ATTR bootloader_execute_flash_command(uint8_t command, uint32_t mosi_data, uint8_t mosi_len, uint8_t miso_len)
+{
+    const uint8_t addr_len = 0;
+    const uint8_t address = 0;
+    const uint8_t dummy_len = 0;
+
+    return bootloader_flash_execute_command_common(command, addr_len, address,
+            dummy_len, mosi_len, mosi_data, miso_len);
+}
+
+// cmd(0x5A) + 24bit address + 8 cycles dummy
+uint32_t IRAM_ATTR bootloader_flash_read_sfdp(uint32_t sfdp_addr, unsigned int miso_byte_num)
+{
+    assert(miso_byte_num <= 4);
+    const uint8_t command = CMD_RDSFDP;
+    const uint8_t addr_len = 24;
+    const uint8_t dummy_len = 8;
+    const uint8_t mosi_len = 0;
+    const uint32_t mosi_data = 0;
+    const uint8_t miso_len = miso_byte_num * 8;
+
+    return bootloader_flash_execute_command_common(command, addr_len, sfdp_addr,
+            dummy_len, mosi_len, mosi_data, miso_len);
 }
 }
 
 
 void bootloader_enable_wp(void)
 void bootloader_enable_wp(void)
@@ -618,6 +663,13 @@ void bootloader_enable_wp(void)
     bootloader_execute_flash_command(CMD_WRDI, 0, 0, 0);   /* Exit OTP mode */
     bootloader_execute_flash_command(CMD_WRDI, 0, 0, 0);   /* Exit OTP mode */
 }
 }
 
 
+uint32_t IRAM_ATTR bootloader_read_flash_id(void)
+{
+    uint32_t id = bootloader_execute_flash_command(CMD_RDID, 0, 0, 24);
+    id = ((id & 0xff) << 16) | ((id >> 16) & 0xff) | (id & 0xff00);
+    return id;
+}
+
 #if SOC_CACHE_SUPPORT_WRAP
 #if SOC_CACHE_SUPPORT_WRAP
 esp_err_t bootloader_flash_wrap_set(spi_flash_wrap_mode_t mode)
 esp_err_t bootloader_flash_wrap_set(spi_flash_wrap_mode_t mode)
 {
 {
@@ -649,3 +701,104 @@ esp_err_t bootloader_flash_wrap_set(spi_flash_wrap_mode_t mode)
     return ESP_OK;
     return ESP_OK;
 }
 }
 #endif //SOC_CACHE_SUPPORT_WRAP
 #endif //SOC_CACHE_SUPPORT_WRAP
+
+/*******************************************************************************
+ * XMC startup flow
+ ******************************************************************************/
+
+#define XMC_SUPPORT CONFIG_BOOTLOADER_FLASH_XMC_SUPPORT
+#define XMC_VENDOR_ID 0x20
+
+#if BOOTLOADER_BUILD
+#define BOOTLOADER_FLASH_LOG(level, ...)    ESP_LOG##level(TAG, ##__VA_ARGS__)
+#else
+static DRAM_ATTR char bootloader_flash_tag[] = "bootloader_flash";
+#define BOOTLOADER_FLASH_LOG(level, ...)    ESP_DRAM_LOG##level(bootloader_flash_tag, ##__VA_ARGS__)
+#endif
+
+#if XMC_SUPPORT
+//strictly check the model
+static IRAM_ATTR bool is_xmc_chip_strict(uint32_t rdid)
+{
+    uint32_t vendor_id = BYTESHIFT(rdid, 2);
+    uint32_t mfid = BYTESHIFT(rdid, 1);
+    uint32_t cpid = BYTESHIFT(rdid, 0);
+
+    if (vendor_id != XMC_VENDOR_ID) {
+        return false;
+    }
+
+    bool matched = false;
+    if (mfid == 0x40) {
+        if (cpid >= 0x13 && cpid <= 0x20) {
+            matched = true;
+        }
+    } else if (mfid == 0x41) {
+        if (cpid >= 0x17 && cpid <= 0x20) {
+            matched = true;
+        }
+    } else if (mfid == 0x50) {
+        if (cpid >= 0x15 && cpid <= 0x16) {
+            matched =  true;
+        }
+    }
+    return matched;
+}
+
+esp_err_t IRAM_ATTR bootloader_flash_xmc_startup(void)
+{
+    // If the RDID value is a valid XMC one, may skip the flow
+    const bool fast_check = true;
+    if (fast_check && is_xmc_chip_strict(g_rom_flashchip.device_id)) {
+        BOOTLOADER_FLASH_LOG(D, "XMC chip detected by RDID (%08X), skip.", g_rom_flashchip.device_id);
+        return ESP_OK;
+    }
+
+    // Check the Manufacturer ID in SFDP registers (JEDEC standard). If not XMC chip, no need to run the flow
+    const int sfdp_mfid_addr = 0x10;
+    uint8_t mf_id = (bootloader_flash_read_sfdp(sfdp_mfid_addr, 1) & 0xff);
+    if (mf_id != XMC_VENDOR_ID) {
+        BOOTLOADER_FLASH_LOG(D, "non-XMC chip detected by SFDP Read (%02X), skip.", mf_id);
+        return ESP_OK;
+    }
+
+    BOOTLOADER_FLASH_LOG(I, "XM25QHxxC startup flow");
+    // Enter DPD
+    bootloader_execute_flash_command(0xB9, 0, 0, 0);
+    // Enter UDPD
+    bootloader_execute_flash_command(0x79, 0, 0, 0);
+    // Exit UDPD
+    bootloader_execute_flash_command(0xFF, 0, 0, 0);
+    // Delay tXUDPD
+    esp_rom_delay_us(2000);
+    // Release Power-down
+    bootloader_execute_flash_command(0xAB, 0, 0, 0);
+    esp_rom_delay_us(20);
+    // Read flash ID and check again
+    g_rom_flashchip.device_id = bootloader_read_flash_id();
+    if (!is_xmc_chip_strict(g_rom_flashchip.device_id)) {
+        BOOTLOADER_FLASH_LOG(E, "XMC flash startup fail");
+        return ESP_FAIL;
+    }
+
+    return ESP_OK;
+}
+
+#else
+//only compare the vendor id
+static IRAM_ATTR bool is_xmc_chip(uint32_t rdid)
+{
+    uint32_t vendor_id = (rdid >> 16) & 0xFF;
+    return (vendor_id == XMC_VENDOR_ID);
+}
+
+esp_err_t IRAM_ATTR bootloader_flash_xmc_startup(void)
+{
+    if (is_xmc_chip(g_rom_flashchip.device_id)) {
+        BOOTLOADER_FLASH_LOG(E, "XMC chip detected (%08X) while support disabled.", g_rom_flashchip.device_id);
+        return ESP_FAIL;
+    }
+    return ESP_OK;
+}
+
+#endif //XMC_SUPPORT

+ 5 - 0
components/bootloader_support/src/esp32/bootloader_esp32.c

@@ -387,6 +387,11 @@ esp_err_t bootloader_init(void)
     bootloader_print_banner();
     bootloader_print_banner();
     // update flash ID
     // update flash ID
     bootloader_flash_update_id();
     bootloader_flash_update_id();
+    // Check and run XMC startup flow
+    if ((ret = bootloader_flash_xmc_startup()) != ESP_OK) {
+        ESP_LOGE(TAG, "failed when running XMC startup flow, reboot!");
+        goto err;
+    }
     // read bootloader header
     // read bootloader header
     if ((ret = bootloader_read_bootloader_header()) != ESP_OK) {
     if ((ret = bootloader_read_bootloader_header()) != ESP_OK) {
         goto err;
         goto err;

+ 5 - 0
components/bootloader_support/src/esp32c3/bootloader_esp32c3.c

@@ -309,6 +309,11 @@ esp_err_t bootloader_init(void)
     bootloader_print_banner();
     bootloader_print_banner();
     // update flash ID
     // update flash ID
     bootloader_flash_update_id();
     bootloader_flash_update_id();
+    // Check and run XMC startup flow
+    if ((ret = bootloader_flash_xmc_startup()) != ESP_OK) {
+        ESP_LOGE(TAG, "failed when running XMC startup flow, reboot!");
+        goto err;
+    }
     // read bootloader header
     // read bootloader header
     if ((ret = bootloader_read_bootloader_header()) != ESP_OK) {
     if ((ret = bootloader_read_bootloader_header()) != ESP_OK) {
         goto err;
         goto err;

+ 5 - 0
components/bootloader_support/src/esp32h2/bootloader_esp32h2.c

@@ -301,6 +301,11 @@ esp_err_t bootloader_init(void)
     bootloader_print_banner();
     bootloader_print_banner();
     // update flash ID
     // update flash ID
     bootloader_flash_update_id();
     bootloader_flash_update_id();
+    // Check and run XMC startup flow
+    if ((ret = bootloader_flash_xmc_startup()) != ESP_OK) {
+        ESP_LOGE(TAG, "failed when running XMC startup flow, reboot!");
+        goto err;
+    }
     // read bootloader header
     // read bootloader header
     if ((ret = bootloader_read_bootloader_header()) != ESP_OK) {
     if ((ret = bootloader_read_bootloader_header()) != ESP_OK) {
         goto err;
         goto err;

+ 5 - 0
components/bootloader_support/src/esp32s2/bootloader_esp32s2.c

@@ -307,6 +307,11 @@ esp_err_t bootloader_init(void)
     bootloader_print_banner();
     bootloader_print_banner();
     // update flash ID
     // update flash ID
     bootloader_flash_update_id();
     bootloader_flash_update_id();
+    // Check and run XMC startup flow
+    if ((ret = bootloader_flash_xmc_startup()) != ESP_OK) {
+        ESP_LOGE(TAG, "failed when running XMC startup flow, reboot!");
+        goto err;
+    }
     // read bootloader header
     // read bootloader header
     if ((ret = bootloader_read_bootloader_header()) != ESP_OK) {
     if ((ret = bootloader_read_bootloader_header()) != ESP_OK) {
         goto err;
         goto err;

+ 5 - 0
components/bootloader_support/src/esp32s3/bootloader_esp32s3.c

@@ -328,6 +328,11 @@ esp_err_t bootloader_init(void)
     bootloader_print_banner();
     bootloader_print_banner();
     // update flash ID
     // update flash ID
     bootloader_flash_update_id();
     bootloader_flash_update_id();
+    // Check and run XMC startup flow
+    if ((ret = bootloader_flash_xmc_startup()) != ESP_OK) {
+        ESP_LOGE(TAG, "failed when running XMC startup flow, reboot!");
+        goto err;
+    }
     // read bootloader header
     // read bootloader header
     if ((ret = bootloader_read_bootloader_header()) != ESP_OK) {
     if ((ret = bootloader_read_bootloader_header()) != ESP_OK) {
         goto err;
         goto err;

+ 0 - 8
components/bootloader_support/src/flash_qio_mode.c

@@ -105,14 +105,6 @@ static esp_err_t enable_qio_mode(read_status_fn_t read_status_fn,
    The command passed here is always the on-the-wire command given to the SPI flash unit.
    The command passed here is always the on-the-wire command given to the SPI flash unit.
 */
 */
 
 
-/* dummy_len_plus values defined in ROM for SPI flash configuration */
-uint32_t bootloader_read_flash_id(void)
-{
-    uint32_t id = bootloader_execute_flash_command(CMD_RDID, 0, 0, 24);
-    id = ((id & 0xff) << 16) | ((id >> 16) & 0xff) | (id & 0xff00);
-    return id;
-}
-
 void bootloader_enable_qio_mode(void)
 void bootloader_enable_qio_mode(void)
 {
 {
     uint32_t raw_flash_id;
     uint32_t raw_flash_id;

+ 20 - 0
components/spi_flash/test/test_spi_flash.c

@@ -15,6 +15,8 @@
 #include "esp_rom_sys.h"
 #include "esp_rom_sys.h"
 #include "esp_timer.h"
 #include "esp_timer.h"
 
 
+#include "bootloader_flash.h"   //for bootloader_flash_xmc_startup
+
 #include "sdkconfig.h"
 #include "sdkconfig.h"
 #if CONFIG_IDF_TARGET_ESP32
 #if CONFIG_IDF_TARGET_ESP32
 #include "esp32/rom/spi_flash.h"
 #include "esp32/rom/spi_flash.h"
@@ -427,3 +429,21 @@ TEST_CASE("rom unlock will not erase QE bit", "[spi_flash]")
     TEST_ASSERT(status & 0x40);
     TEST_ASSERT(status & 0x40);
 }
 }
 #endif
 #endif
+
+static IRAM_ATTR NOINLINE_ATTR void test_xmc_startup(void)
+{
+    extern void spi_flash_disable_interrupts_caches_and_other_cpu(void);
+    extern void spi_flash_enable_interrupts_caches_and_other_cpu(void);
+    esp_err_t ret = ESP_OK;
+
+    spi_flash_disable_interrupts_caches_and_other_cpu();
+    ret = bootloader_flash_xmc_startup();
+    spi_flash_enable_interrupts_caches_and_other_cpu();
+
+    TEST_ASSERT_EQUAL(ESP_OK, ret);
+}
+
+TEST_CASE("bootloader_flash_xmc_startup can be called when cache disabled", "[spi_flash]")
+{
+    test_xmc_startup();
+}