Bladeren bron

spi_flash: move the unlock patch to bootloader and add support for GD

Cao Sen Miao 4 jaren geleden
bovenliggende
commit
c29b3e2e36

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

@@ -10,6 +10,10 @@
 #include "sdkconfig.h"
 #include "soc/soc_caps.h"
 
+#ifdef __cplusplus
+extern "C" {
+#endif
+
 #if SOC_CACHE_SUPPORT_WRAP
 /**
  * @brief Set the burst mode setting command for specified wrap mode.
@@ -19,3 +23,15 @@
  */
 esp_err_t bootloader_flash_wrap_set(spi_flash_wrap_mode_t mode);
 #endif
+
+/**
+  * @brief Unlock Flash write protect.
+  *        Please do not call this function in SDK.
+  *
+  * @note This can be overridden because it's attribute weak.
+  */
+esp_err_t bootloader_flash_unlock(void);
+
+#ifdef __cplusplus
+}
+#endif

+ 96 - 3
components/bootloader_support/src/bootloader_flash.c

@@ -23,7 +23,9 @@
 #   define SPIFLASH SPIMEM1
 #endif
 
-#if CONFIG_IDF_TARGET_ESP32S2
+#if CONFIG_IDF_TARGET_ESP32
+#include "esp32/rom/spi_flash.h"
+#elif CONFIG_IDF_TARGET_ESP32S2
 #include "esp32s2/rom/spi_flash.h"
 #elif CONFIG_IDF_TARGET_ESP32S3
 #include "esp32s3/rom/spi_flash.h"
@@ -39,6 +41,17 @@
 #define ENCRYPTION_IS_VIRTUAL 0
 #endif
 
+#define BYTESHIFT(VAR, IDX)    (((VAR) >> ((IDX) * 8)) & 0xFF)
+#define ISSI_ID                0x9D
+#define GD_Q_ID_HIGH           0xC8
+#define GD_Q_ID_MID            0x40
+#define GD_Q_ID_LOW            0x16
+
+#define ESP_BOOTLOADER_SPIFLASH_BP_MASK_ISSI    (BIT7 | BIT5 | BIT4 | BIT3 | BIT2)
+#define ESP_BOOTLOADER_SPIFLASH_QE_16B           BIT9   // QE position when you write 16 bits at one time.
+#define ESP_BOOTLOADER_SPIFLASH_QE_8B            BIT1   // QE position when you write 8 bits(for SR2) at one time.
+#define ESP_BOOTLOADER_SPIFLASH_WRITE_8B         (8)
+#define ESP_BOOTLOADER_SPIFLASH_WRITE_16B        (16)
 
 #ifndef BOOTLOADER_BUILD
 /* Normal app version maps to esp_spi_flash.h operations...
@@ -426,7 +439,7 @@ esp_err_t bootloader_flash_write(size_t dest_addr, void *src, size_t size, bool
         return ESP_FAIL;
     }
 
-    err = spi_to_esp_err(esp_rom_spiflash_unlock());
+    err = bootloader_flash_unlock();
     if (err != ESP_OK) {
         return err;
     }
@@ -470,10 +483,90 @@ esp_err_t bootloader_flash_erase_range(uint32_t start_addr, uint32_t size)
 
 #endif
 
+FORCE_INLINE_ATTR bool is_issi_chip(const esp_rom_spiflash_chip_t* chip)
+{
+    return BYTESHIFT(chip->device_id, 2) == ISSI_ID;
+}
+
+// For GD25Q32, GD25Q64, GD25Q127C, GD25Q128, which use single command to read/write different SR.
+FORCE_INLINE_ATTR bool is_gd_q_chip(const esp_rom_spiflash_chip_t* chip)
+{
+    return BYTESHIFT(chip->device_id, 2) == GD_Q_ID_HIGH && BYTESHIFT(chip->device_id, 1) == GD_Q_ID_MID && BYTESHIFT(chip->device_id, 0) >= GD_Q_ID_LOW;
+}
+
+esp_err_t IRAM_ATTR __attribute__((weak)) bootloader_flash_unlock(void)
+{
+    uint16_t status = 0;    // status for SR1 or SR1+SR2 if writing SR with 01H + 2Bytes.
+    uint16_t new_status = 0;
+    uint8_t status_sr2 = 0;    // status_sr2 for SR2.
+    uint8_t new_status_sr2 = 0;
+    uint8_t write_sr_bit = 0;
+    esp_err_t err = ESP_OK;
+
+    esp_rom_spiflash_wait_idle(&g_rom_flashchip);
+    if (is_issi_chip(&g_rom_flashchip)) {
+        write_sr_bit = ESP_BOOTLOADER_SPIFLASH_WRITE_8B;
+        // ISSI chips have different QE position
+
+        status = bootloader_execute_flash_command(CMD_RDSR, 0, 0, 8);
+
+        /* Clear all bits in the mask.
+        (This is different from ROM esp_rom_spiflash_unlock, which keeps all bits as-is.)
+        */
+        new_status = status & (~ESP_BOOTLOADER_SPIFLASH_BP_MASK_ISSI);
+        // Skip if nothing needs to be cleared. Otherwise will waste time waiting for the flash to clear nothing.
+    } else if (is_gd_q_chip(&g_rom_flashchip)) {
+        /* The GD chips behaviour is to clear all bits in SR1 and clear bits in SR2 except QE bit.
+           Use 01H to write SR1 and 31H to write SR2.
+        */
+        write_sr_bit = ESP_BOOTLOADER_SPIFLASH_WRITE_8B;
+
+        status = bootloader_execute_flash_command(CMD_RDSR, 0, 0, 8);
+        new_status = 0;
+
+        status_sr2 = bootloader_execute_flash_command(CMD_RDSR2, 0, 0, 8);
+        new_status_sr2 = status_sr2 & ESP_BOOTLOADER_SPIFLASH_QE_8B;
+    } else {
+        /* For common behaviour, like XMC chips, Use 01H+2Bytes to write both SR1 and SR2*/
+        write_sr_bit = ESP_BOOTLOADER_SPIFLASH_WRITE_16B;
+        status = bootloader_execute_flash_command(CMD_RDSR, 0, 0, 8) | (bootloader_execute_flash_command(CMD_RDSR2, 0, 0, 8) << 8);
+
+        /* Clear all bits except QE, if it is set.
+        (This is different from ROM esp_rom_spiflash_unlock, which keeps all bits as-is.)
+        */
+        new_status = status & ESP_BOOTLOADER_SPIFLASH_QE_16B;
+    }
+
+    if (status != new_status) {
+        /* if the status in SR not equal to the ideal status, the status need to be updated */
+        esp_rom_spiflash_wait_idle(&g_rom_flashchip);
+        bootloader_execute_flash_command(CMD_WREN, 0, 0, 0);
+        esp_rom_spiflash_wait_idle(&g_rom_flashchip);
+        bootloader_execute_flash_command(CMD_WRSR, new_status, write_sr_bit, 0);
+        esp_rom_spiflash_wait_idle(&g_rom_flashchip);
+    }
+
+    if (status_sr2 != new_status_sr2) {
+        /* If the status in SR2 not equal to the ideal status, the status need to be updated.
+           It doesn't need to be updated if status in SR2 is 0.
+           Note: if we need to update both SR1 and SR2, the `CMD_WREN` needs to be sent again.
+        */
+        esp_rom_spiflash_wait_idle(&g_rom_flashchip);
+        bootloader_execute_flash_command(CMD_WREN, 0, 0, 0);
+        esp_rom_spiflash_wait_idle(&g_rom_flashchip);
+        bootloader_execute_flash_command(CMD_WRSR2, new_status_sr2, write_sr_bit, 0);
+        esp_rom_spiflash_wait_idle(&g_rom_flashchip);
+    }
+
+    bootloader_execute_flash_command(CMD_WRDI, 0, 0, 0);
+    esp_rom_spiflash_wait_idle(&g_rom_flashchip);
+    return err;
+}
+
 #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[];
 #endif
-uint32_t bootloader_execute_flash_command(uint8_t command, uint32_t mosi_data, uint8_t mosi_len, uint8_t miso_len)
+uint32_t IRAM_ATTR bootloader_execute_flash_command(uint8_t command, uint32_t mosi_data, uint8_t mosi_len, uint8_t miso_len)
 {
     uint32_t old_ctrl_reg = SPIFLASH.ctrl.val;
 #if CONFIG_IDF_TARGET_ESP32

+ 1 - 1
components/bootloader_support/src/esp32/bootloader_esp32.c

@@ -252,7 +252,7 @@ static esp_err_t bootloader_init_spi_flash(void)
     }
 #endif
 
-    esp_rom_spiflash_unlock();
+    bootloader_flash_unlock();
 
 #if CONFIG_ESPTOOLPY_FLASHMODE_QIO || CONFIG_ESPTOOLPY_FLASHMODE_QOUT
     bootloader_enable_qio_mode();

+ 1 - 1
components/bootloader_support/src/esp32c3/bootloader_esp32c3.c

@@ -203,7 +203,7 @@ static esp_err_t bootloader_init_spi_flash(void)
 #endif
 
     bootloader_spi_flash_resume();
-    esp_rom_spiflash_unlock();
+    bootloader_flash_unlock();
 
 #if CONFIG_ESPTOOLPY_FLASHMODE_QIO || CONFIG_ESPTOOLPY_FLASHMODE_QOUT
     bootloader_enable_qio_mode();

+ 1 - 1
components/bootloader_support/src/esp32h2/bootloader_esp32h2.c

@@ -202,7 +202,7 @@ static esp_err_t bootloader_init_spi_flash(void)
 #endif
 
     bootloader_spi_flash_resume();
-    esp_rom_spiflash_unlock();
+    bootloader_flash_unlock();
 
 #if CONFIG_ESPTOOLPY_FLASHMODE_QIO || CONFIG_ESPTOOLPY_FLASHMODE_QOUT
     bootloader_enable_qio_mode();

+ 1 - 1
components/bootloader_support/src/esp32s2/bootloader_esp32s2.c

@@ -198,7 +198,7 @@ static esp_err_t bootloader_init_spi_flash(void)
     }
 #endif
 
-    esp_rom_spiflash_unlock();
+    bootloader_flash_unlock();
 
 #if CONFIG_ESPTOOLPY_FLASHMODE_QIO || CONFIG_ESPTOOLPY_FLASHMODE_QOUT
     bootloader_enable_qio_mode();

+ 1 - 1
components/bootloader_support/src/esp32s3/bootloader_esp32s3.c

@@ -199,7 +199,7 @@ static esp_err_t bootloader_init_spi_flash(void)
     }
 #endif
 
-    esp_rom_spiflash_unlock();
+    bootloader_flash_unlock();
 
 #if CONFIG_ESPTOOLPY_FLASHMODE_QIO || CONFIG_ESPTOOLPY_FLASHMODE_QOUT
     bootloader_enable_qio_mode();

+ 1 - 1
components/esp_rom/include/esp32/rom/spi_flash.h

@@ -260,7 +260,7 @@ esp_rom_spiflash_result_t esp_rom_spiflash_read_status(esp_rom_spiflash_chip_t *
 esp_rom_spiflash_result_t esp_rom_spiflash_read_statushigh(esp_rom_spiflash_chip_t *spi, uint32_t *status);
 
 /**
-  * @brief Write status to Falsh status register.
+  * @brief Write status to Flash status register.
   *        Please do not call this function in SDK.
   *
   * @param  esp_rom_spiflash_chip_t *spi : The information for Flash, which is exported from ld file.

+ 0 - 1
components/esp_rom/include/esp32s2/rom/opi_flash.h

@@ -40,7 +40,6 @@ typedef struct {
 #define ESP_ROM_SPIFLASH_BP2           BIT4
 #define ESP_ROM_SPIFLASH_WR_PROTECT    (ESP_ROM_SPIFLASH_BP0|ESP_ROM_SPIFLASH_BP1|ESP_ROM_SPIFLASH_BP2)
 #define ESP_ROM_SPIFLASH_QE            BIT9
-#define ESP_ROM_SPIFLASH_BP_MASK_ISSI        (BIT7 | BIT5 | BIT4 | BIT3 | BIT2)
 
 #define FLASH_OP_MODE_RDCMD_DOUT       0x3B
 #define ESP_ROM_FLASH_SECTOR_SIZE      0x1000

+ 0 - 1
components/esp_rom/include/esp32s2/rom/spi_flash.h

@@ -119,7 +119,6 @@ extern "C" {
 #define  ESP_ROM_SPIFLASH_BP2                 BIT4
 #define  ESP_ROM_SPIFLASH_WR_PROTECT          (ESP_ROM_SPIFLASH_BP0|ESP_ROM_SPIFLASH_BP1|ESP_ROM_SPIFLASH_BP2)
 #define  ESP_ROM_SPIFLASH_QE                  BIT9
-#define  ESP_ROM_SPIFLASH_BP_MASK_ISSI        (BIT7 | BIT5 | BIT4 | BIT3 | BIT2)
 
 #define FLASH_ID_GD25LQ32C  0xC86016
 

+ 0 - 1
components/esp_rom/include/esp32s3/rom/spi_flash.h

@@ -111,7 +111,6 @@ extern "C" {
 #define  ESP_ROM_SPIFLASH_BP2                 BIT4
 #define  ESP_ROM_SPIFLASH_WR_PROTECT          (ESP_ROM_SPIFLASH_BP0|ESP_ROM_SPIFLASH_BP1|ESP_ROM_SPIFLASH_BP2)
 #define  ESP_ROM_SPIFLASH_QE                  BIT9
-#define  ESP_ROM_SPIFLASH_BP_MASK_ISSI        (BIT7 | BIT5 | BIT4 | BIT3 | BIT2)
 
 #define FLASH_ID_GD25LQ32C  0xC86016
 

+ 2 - 1
components/esp_system/port/cpu_start.c

@@ -66,6 +66,7 @@
 
 #include "spi_flash_private.h"
 #include "bootloader_flash_config.h"
+#include "bootloader_flash.h"
 #include "esp_private/crosscore_int.h"
 #include "esp_flash_encrypt.h"
 
@@ -527,7 +528,7 @@ void IRAM_ATTR call_start_cpu0(void)
 
     extern void esp_rom_spiflash_attach(uint32_t, bool);
     esp_rom_spiflash_attach(esp_rom_efuse_get_flash_gpio_info(), false);
-    esp_rom_spiflash_unlock();
+    bootloader_flash_unlock();
 #else
     // This assumes that DROM is the first segment in the application binary, i.e. that we can read
     // the binary header through cache by accessing SOC_DROM_LOW address.

+ 1 - 1
components/spi_flash/esp32/spi_flash_rom_patch.c

@@ -63,7 +63,7 @@ esp_rom_spiflash_result_t esp_rom_spiflash_wait_idle(esp_rom_spiflash_chip_t *sp
    about interrupts, CPU coordination, flash mapping. However some of
    the functions in esp_spi_flash.c call it.
  */
-esp_rom_spiflash_result_t esp_rom_spiflash_unlock(void)
+__attribute__((__unused__)) esp_rom_spiflash_result_t esp_rom_spiflash_unlock(void)
 {
     uint32_t status;
     uint32_t new_status;

+ 2 - 4
components/spi_flash/flash_ops.c

@@ -56,6 +56,7 @@
 #include "esp_flash.h"
 #include "esp_attr.h"
 #include "spi_flash_private.h"
+#include "bootloader_flash.h"
 
 esp_rom_spiflash_result_t IRAM_ATTR spi_flash_write_encrypted_chip(size_t dest_addr, const void *src, size_t size);
 
@@ -248,11 +249,8 @@ static esp_rom_spiflash_result_t IRAM_ATTR spi_flash_unlock(void)
     static bool unlocked = false;
     if (!unlocked) {
         spi_flash_guard_start();
-        esp_rom_spiflash_result_t rc = esp_rom_spiflash_unlock();
+        bootloader_flash_unlock();
         spi_flash_guard_end();
-        if (rc != ESP_ROM_SPIFLASH_RESULT_OK) {
-            return rc;
-        }
         unlocked = true;
     }
     return ESP_ROM_SPIFLASH_RESULT_OK;

+ 5 - 0
components/spi_flash/sim/flash_mock.cpp

@@ -75,6 +75,11 @@ extern "C" int spi_flash_get_erase_cycles(size_t sector)
     return spiflash.get_erase_cycles(sector);
 }
 
+extern "C" esp_err_t bootloader_flash_unlock(void)
+{
+    return ESP_OK;
+}
+
 esp_rom_spiflash_result_t esp_rom_spiflash_read(uint32_t target, uint32_t *dest, int32_t len)
 {
     return spiflash.read(target, dest, len);