diff --git a/components/bootloader_support/include_bootloader/flash_qio_mode.h b/components/bootloader_support/include_bootloader/flash_qio_mode.h index d3ec56576f..9d9ae59783 100644 --- a/components/bootloader_support/include_bootloader/flash_qio_mode.h +++ b/components/bootloader_support/include_bootloader/flash_qio_mode.h @@ -17,6 +17,8 @@ extern "C" { #endif +#include "esp_err.h" + /** @brief Enable Quad I/O mode in bootloader (if configured) * * Queries attached SPI flash ID and sends correct SPI flash @@ -32,6 +34,14 @@ void bootloader_enable_qio_mode(void); */ uint32_t bootloader_read_flash_id(); +/** + * @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); + /** * @brief Read the SFDP of the flash * diff --git a/components/bootloader_support/src/bootloader_flash.c b/components/bootloader_support/src/bootloader_flash.c index 6b25addf22..2312a6799e 100644 --- a/components/bootloader_support/src/bootloader_flash.c +++ b/components/bootloader_support/src/bootloader_flash.c @@ -17,6 +17,7 @@ #include #include /* including in bootloader for error values */ #include +#include "flash_qio_mode.h" #ifndef BOOTLOADER_BUILD /* Normal app version maps to esp_spi_flash.h operations... @@ -249,7 +250,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; } diff --git a/components/bootloader_support/src/bootloader_init.c b/components/bootloader_support/src/bootloader_init.c index ac3fd02297..51906d3f67 100644 --- a/components/bootloader_support/src/bootloader_init.c +++ b/components/bootloader_support/src/bootloader_init.c @@ -183,7 +183,7 @@ static esp_err_t bootloader_main() } #endif - esp_rom_spiflash_unlock(); + bootloader_flash_unlock(); ESP_LOGI(TAG, "Enabling RNG early entropy source..."); bootloader_random_enable(); diff --git a/components/bootloader_support/src/flash_qio_mode.c b/components/bootloader_support/src/flash_qio_mode.c index 8af7b55d29..fc766fc1b4 100644 --- a/components/bootloader_support/src/flash_qio_mode.c +++ b/components/bootloader_support/src/flash_qio_mode.c @@ -41,7 +41,16 @@ #define CMD_RDSFDP 0x5A /* Read the SFDP of the flash */ #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) static DRAM_ATTR char TAG[] = "qio_mode"; @@ -462,3 +471,77 @@ esp_err_t IRAM_ATTR bootloader_flash_xmc_startup(void) #endif //XMC_SUPPORT +static inline IRAM_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. +static inline IRAM_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 = 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 = execute_flash_command(CMD_RDSR, 0, 0, 8); + new_status = 0; + + status_sr2 = 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 = execute_flash_command(CMD_RDSR, 0, 0, 8) | (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); + execute_flash_command(CMD_WREN, 0, 0, 0); + esp_rom_spiflash_wait_idle(&g_rom_flashchip); + 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) { + esp_rom_spiflash_wait_idle(&g_rom_flashchip); + execute_flash_command(CMD_WREN, 0, 0, 0); + esp_rom_spiflash_wait_idle(&g_rom_flashchip); + execute_flash_command(CMD_WRSR2, new_status_sr2, write_sr_bit, 0); + esp_rom_spiflash_wait_idle(&g_rom_flashchip); + } + execute_flash_command(CMD_WRDI, 0, 0, 0); + esp_rom_spiflash_wait_idle(&g_rom_flashchip); + return err; +} diff --git a/components/esp32/cpu_start.c b/components/esp32/cpu_start.c index 282ef713ea..5430a15104 100644 --- a/components/esp32/cpu_start.c +++ b/components/esp32/cpu_start.c @@ -88,6 +88,7 @@ static void do_global_ctors(void); static void main_task(void* args); extern void app_main(void); extern esp_err_t esp_pthread_init(void); +extern esp_err_t bootloader_flash_unlock(void); extern int _bss_start; extern int _bss_end; @@ -458,6 +459,7 @@ void start_cpu0_default(void) #endif bootloader_flash_update_id(); + bootloader_flash_unlock(); #if !CONFIG_SPIRAM_BOOT_INIT // If psram is uninitialized, we need to improve some flash configuration. bootloader_flash_clock_config(&fhdr);