bootloader: fix the WRSR format for ISSI flash chips

1. The 2nd bootloader always call `rom_spiflash_unlock()`, but never help to clear the WEL bit when exit. This may cause system unstability.

   This commit helps to clear WEL when flash configuration is done.

   **RISK:** When the app starts, it didn't have to clear the WEL before it actually write/erase. But now the very first write/erase operation should be done after a WEL clear. Though the risk is little (all the following write/erase also need to clear the WEL), we still have to test this carefully, especially for those functions used by the OTA.

2. The `rom_spiflash_unlock()` function in the patch of ESP32 may (1) trigger the QPI, (2) clear the QE or (3) fail to unlock the ISSI chips.

   Status register bitmap of ISSI chip and GD chip:

| SR | ISSI | GD25LQ32C |
| -- | ---- | --------- |
| 0  | WIP  | WIP       |
| 1  | WEL  | WEL       |
| 2  | BP0  | BP0       |
| 3  | BP1  | BP1       |
| 4  | BP2  | BP2       |
| 5  | BP3  | BP3       |
| 6  | QE   | BP4       |
| 7  | SRWD | SRP0      |
| 8  |      | SRP1      |
| 9  |      | QE        |
| 10 |      | SUS2      |
| 11 |      | LB1       |
| 12 |      | LB2       |
| 13 |      | LB3       |
| 14 |      | CMP       |
| 15 |      | SUS1      |

   QE bit of other chips are at the bit 9 of the status register (i.e. bit 1 of SR2), which should be read by RDSR2 command.

   However, the RDSR2 (35H, Read Status 2) command for chip of other vendors happens to be the QIOEN (Enter QPI mode) command of ISSI chips. When the `rom_spiflash_unlock()` function trys to read SR2, it may trigger the QPI of ISSI chips.

   Moreover, when `rom_spiflash_unlock()` try to clear the BP4 bit in the status register, QE (bit 6) of ISSI chip may be cleared by accident. Or if the ISSI chip doesn't accept WRSR command with argument of two bytes (since it only have status register of one byte), it may fail to clear the other protect bits (BP0~BP3) as expected.

   This commit makes the `rom_spiflash_unlock()` check whether the vendor is issi. if so, `rom_spiflash_unlock()` only send RDSR to read the status register, send WRSR with only 1 byte argument, and also avoid clearing the QE bit (bit 6).

3. `rom_spiflash_unlock()` always send WRSR command to clear protection bits even when there is no protection bit active. And the execution of clearing status registers, which takes about 700us, will also happen even when there's no bits cleared.

   This commit skips the clearing of status register if there is no protection bits active.

Also move the execute_flash_command to be a bootloader API; move
implementation of spi_flash_wrap_set to the bootloader
This commit is contained in:
Michael (XIAO Xufeng) 2020-03-12 18:20:31 +08:00
parent 740b961bb1
commit d2f9113d14
13 changed files with 215 additions and 172 deletions

View File

@ -19,11 +19,26 @@
#include <stdint.h> #include <stdint.h>
#include <esp_err.h> #include <esp_err.h>
#include <esp_spi_flash.h> /* including in bootloader for error values */ #include <esp_spi_flash.h> /* including in bootloader for error values */
#include "sdkconfig.h"
#define FLASH_SECTOR_SIZE 0x1000 #define FLASH_SECTOR_SIZE 0x1000
#define FLASH_BLOCK_SIZE 0x10000 #define FLASH_BLOCK_SIZE 0x10000
#define MMAP_ALIGNED_MASK 0x0000FFFF #define MMAP_ALIGNED_MASK 0x0000FFFF
/* SPI commands (actual on-wire commands not SPI controller bitmasks)
Suitable for use with the bootloader_execute_flash_command static function.
*/
#define CMD_RDID 0x9F
#define CMD_WRSR 0x01
#define CMD_WRSR2 0x31 /* Not all SPI flash uses this command */
#define CMD_WREN 0x06
#define CMD_WRDI 0x04
#define CMD_RDSR 0x05
#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_WRAP 0x77 /* Set burst with wrap command */
/* Provide a Flash API for bootloader_support code, /* Provide a Flash API for bootloader_support code,
that can be used from bootloader or app code. that can be used from bootloader or app code.
@ -136,4 +151,30 @@ static inline uint32_t bootloader_cache_pages_to_map(uint32_t size, uint32_t vad
return (size + (vaddr - (vaddr & MMU_FLASH_MASK)) + MMU_BLOCK_SIZE - 1) / MMU_BLOCK_SIZE; return (size + (vaddr - (vaddr & MMU_FLASH_MASK)) + MMU_BLOCK_SIZE - 1) / MMU_BLOCK_SIZE;
} }
/**
* @brief Execute a user command on the flash
*
* @param command The command value to execute.
* @param mosi_data MOSI data to send
* @param mosi_len Length of MOSI data, in bits
* @param miso_len Length of MISO data to receive, in bits
* @return Received MISO data
*/
uint32_t bootloader_execute_flash_command(uint8_t command, uint32_t mosi_data, uint8_t mosi_len, uint8_t miso_len);
/**
* @brief Enable the flash write protect (WEL bit).
*/
void bootloader_enable_wp(void);
#if CONFIG_IDF_TARGET_ESP32S2
/**
* @brief Set the burst mode setting command for specified wrap mode.
*
* @param mode The specified warp mode.
* @return always ESP_OK
*/
esp_err_t bootloader_flash_wrap_set(spi_flash_wrap_mode_t mode);
#endif
#endif #endif

View File

@ -16,10 +16,26 @@
#include <bootloader_flash.h> #include <bootloader_flash.h>
#include <esp_log.h> #include <esp_log.h>
#include <esp_flash_encrypt.h> #include <esp_flash_encrypt.h>
#include "sdkconfig.h"
#include "soc/soc_caps.h"
#if CONFIG_IDF_TARGET_ESP32
# include "soc/spi_struct.h"
# include "soc/spi_reg.h"
/* SPI flash controller */
# define SPIFLASH SPI1
#else
# include "soc/spi_mem_struct.h"
# include "soc/spi_mem_reg.h"
/* SPI flash controller */
# define SPIFLASH SPIMEM1
#endif
#if CONFIG_IDF_TARGET_ESP32S2BETA #if CONFIG_IDF_TARGET_ESP32S2BETA
#include "esp32s2beta/rom/spi_flash.h" #include "esp32s2beta/rom/spi_flash.h"
#endif #endif
#ifndef BOOTLOADER_BUILD #ifndef BOOTLOADER_BUILD
/* Normal app version maps to esp_spi_flash.h operations... /* Normal app version maps to esp_spi_flash.h operations...
*/ */
@ -363,4 +379,90 @@ 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
extern uint8_t g_rom_spiflash_dummy_len_plus[];
uint32_t 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
SPIFLASH.ctrl.val = SPI_WP_REG_M; // keep WP high while idle, otherwise leave DIO mode
#elif CONFIG_IDF_TARGET_ESP32S2 || CONFIG_IDF_TARGET_ESP32S3
SPIFLASH.ctrl.val = SPI_MEM_WP_REG_M; // keep WP high while idle, otherwise leave DIO mode
#endif
SPIFLASH.user.usr_dummy = 0;
SPIFLASH.user.usr_addr = 0;
SPIFLASH.user.usr_command = 1;
SPIFLASH.user2.usr_command_bitlen = 7;
SPIFLASH.user2.usr_command_value = command;
SPIFLASH.user.usr_miso = miso_len > 0;
#if CONFIG_IDF_TARGET_ESP32
SPIFLASH.miso_dlen.usr_miso_dbitlen = miso_len ? (miso_len - 1) : 0;
#elif CONFIG_IDF_TARGET_ESP32S2 || CONFIG_IDF_TARGET_ESP32S3
SPIFLASH.miso_dlen.usr_miso_bit_len = miso_len ? (miso_len - 1) : 0;
#endif
SPIFLASH.user.usr_mosi = mosi_len > 0;
#if CONFIG_IDF_TARGET_ESP32
SPIFLASH.mosi_dlen.usr_mosi_dbitlen = mosi_len ? (mosi_len - 1) : 0;
#elif CONFIG_IDF_TARGET_ESP32S2 || CONFIG_IDF_TARGET_ESP32S3
SPIFLASH.mosi_dlen.usr_mosi_bit_len = mosi_len ? (mosi_len - 1) : 0;
#endif
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;
}
}
SPIFLASH.cmd.usr = 1;
while (SPIFLASH.cmd.usr != 0) {
}
SPIFLASH.ctrl.val = old_ctrl_reg;
return SPIFLASH.data_buf[0];
}
void bootloader_enable_wp(void)
{
bootloader_execute_flash_command(CMD_WRDI, 0, 0, 0); /* Exit OTP mode */
}
#if SOC_CACHE_SUPPORT_WRAP
esp_err_t bootloader_flash_wrap_set(spi_flash_wrap_mode_t mode)
{
uint32_t reg_bkp_ctrl = SPIFLASH.ctrl.val;
uint32_t reg_bkp_usr = SPIFLASH.user.val;
SPIFLASH.user.fwrite_dio = 0;
SPIFLASH.user.fwrite_dual = 0;
SPIFLASH.user.fwrite_qio = 1;
SPIFLASH.user.fwrite_quad = 0;
SPIFLASH.ctrl.fcmd_dual = 0;
SPIFLASH.ctrl.fcmd_quad = 0;
SPIFLASH.user.usr_dummy = 0;
SPIFLASH.user.usr_addr = 1;
SPIFLASH.user.usr_command = 1;
SPIFLASH.user2.usr_command_bitlen = 7;
SPIFLASH.user2.usr_command_value = CMD_WRAP;
SPIFLASH.user1.usr_addr_bitlen = 23;
SPIFLASH.addr = 0;
SPIFLASH.user.usr_miso = 0;
SPIFLASH.user.usr_mosi = 1;
SPIFLASH.mosi_dlen.usr_mosi_bit_len = 7;
SPIFLASH.data_buf[0] = (uint32_t) mode << 4;;
SPIFLASH.cmd.usr = 1;
while(SPIFLASH.cmd.usr != 0)
{ }
SPIFLASH.ctrl.val = reg_bkp_ctrl;
SPIFLASH.user.val = reg_bkp_usr;
return ESP_OK;
}
#endif //SOC_CACHE_SUPPORT_WRAP

View File

@ -22,6 +22,7 @@
#include "bootloader_clock.h" #include "bootloader_clock.h"
#include "bootloader_common.h" #include "bootloader_common.h"
#include "bootloader_flash_config.h" #include "bootloader_flash_config.h"
#include "bootloader_flash.h"
#include "soc/cpu.h" #include "soc/cpu.h"
#include "soc/dport_reg.h" #include "soc/dport_reg.h"
@ -265,6 +266,8 @@ static esp_err_t bootloader_init_spi_flash(void)
print_flash_info(&bootloader_image_hdr); print_flash_info(&bootloader_image_hdr);
update_flash_config(&bootloader_image_hdr); update_flash_config(&bootloader_image_hdr);
//ensure the flash is write-protected
bootloader_enable_wp();
return ESP_OK; return ESP_OK;
} }

View File

@ -24,6 +24,7 @@
#include "bootloader_init.h" #include "bootloader_init.h"
#include "bootloader_clock.h" #include "bootloader_clock.h"
#include "bootloader_flash_config.h" #include "bootloader_flash_config.h"
#include "bootloader_flash.h"
#include "esp32s2beta/rom/cache.h" #include "esp32s2beta/rom/cache.h"
#include "esp32s2beta/rom/ets_sys.h" #include "esp32s2beta/rom/ets_sys.h"
@ -225,6 +226,8 @@ static esp_err_t bootloader_init_spi_flash(void)
print_flash_info(&bootloader_image_hdr); print_flash_info(&bootloader_image_hdr);
update_flash_config(&bootloader_image_hdr); update_flash_config(&bootloader_image_hdr);
//ensure the flash is write-protected
bootloader_enable_wp();
return ESP_OK; return ESP_OK;
} }

View File

@ -15,6 +15,7 @@
#include <stdint.h> #include <stdint.h>
#include "bootloader_flash_config.h" #include "bootloader_flash_config.h"
#include "flash_qio_mode.h" #include "flash_qio_mode.h"
#include "bootloader_flash.h"
#include "esp_log.h" #include "esp_log.h"
#include "esp_err.h" #include "esp_err.h"
#if CONFIG_IDF_TARGET_ESP32 #if CONFIG_IDF_TARGET_ESP32
@ -25,30 +26,10 @@
#include "esp32s2beta/rom/efuse.h" #include "esp32s2beta/rom/efuse.h"
#include "soc/spi_mem_struct.h" #include "soc/spi_mem_struct.h"
#endif #endif
#include "soc/spi_struct.h"
#include "soc/spi_reg.h"
#include "soc/efuse_periph.h" #include "soc/efuse_periph.h"
#include "soc/io_mux_reg.h" #include "soc/io_mux_reg.h"
#include "sdkconfig.h" #include "sdkconfig.h"
/* SPI flash controller */
#if CONFIG_IDF_TARGET_ESP32
#define SPIFLASH SPI1
#elif CONFIG_IDF_TARGET_ESP32S2BETA
#define SPIFLASH SPIMEM1
#endif
/* SPI commands (actual on-wire commands not SPI controller bitmasks)
Suitable for use with the execute_flash_command static function.
*/
#define CMD_RDID 0x9F
#define CMD_WRSR 0x01
#define CMD_WRSR2 0x31 /* Not all SPI flash uses this command */
#define CMD_WREN 0x06
#define CMD_WRDI 0x04
#define CMD_RDSR 0x05
#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 */
static const char *TAG = "qio_mode"; static const char *TAG = "qio_mode";
@ -126,57 +107,15 @@ 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.
*/ */
static uint32_t execute_flash_command(uint8_t command, uint32_t mosi_data, uint8_t mosi_len, uint8_t miso_len);
/* dummy_len_plus values defined in ROM for SPI flash configuration */ /* dummy_len_plus values defined in ROM for SPI flash configuration */
extern uint8_t g_rom_spiflash_dummy_len_plus[];
uint32_t bootloader_read_flash_id(void) uint32_t bootloader_read_flash_id(void)
{ {
uint32_t id = execute_flash_command(CMD_RDID, 0, 0, 24); uint32_t id = bootloader_execute_flash_command(CMD_RDID, 0, 0, 24);
id = ((id & 0xff) << 16) | ((id >> 16) & 0xff) | (id & 0xff00); id = ((id & 0xff) << 16) | ((id >> 16) & 0xff) | (id & 0xff00);
return id; return id;
} }
#if CONFIG_IDF_TARGET_ESP32S2BETA
#define FLASH_WRAP_CMD 0x77
typedef enum {
FLASH_WRAP_MODE_8B = 0,
FLASH_WRAP_MODE_16B = 2,
FLASH_WRAP_MODE_32B = 4,
FLASH_WRAP_MODE_64B = 6,
FLASH_WRAP_MODE_DISABLE = 1
} spi_flash_wrap_mode_t;
static esp_err_t spi_flash_wrap_set(spi_flash_wrap_mode_t mode)
{
uint32_t reg_bkp_ctrl = SPIFLASH.ctrl.val;
uint32_t reg_bkp_usr = SPIFLASH.user.val;
SPIFLASH.user.fwrite_dio = 0;
SPIFLASH.user.fwrite_dual = 0;
SPIFLASH.user.fwrite_qio = 1;
SPIFLASH.user.fwrite_quad = 0;
SPIFLASH.ctrl.fcmd_dual = 0;
SPIFLASH.ctrl.fcmd_quad = 0;
SPIFLASH.user.usr_dummy = 0;
SPIFLASH.user.usr_addr = 1;
SPIFLASH.user.usr_command = 1;
SPIFLASH.user2.usr_command_bitlen = 7;
SPIFLASH.user2.usr_command_value = FLASH_WRAP_CMD;
SPIFLASH.user1.usr_addr_bitlen = 23;
SPIFLASH.addr = 0;
SPIFLASH.user.usr_miso = 0;
SPIFLASH.user.usr_mosi = 1;
SPIFLASH.mosi_dlen.usr_mosi_bit_len = 7;
SPIFLASH.data_buf[0] = (uint32_t) mode << 4;;
SPIFLASH.cmd.usr = 1;
while (SPIFLASH.cmd.usr != 0) {
}
SPIFLASH.ctrl.val = reg_bkp_ctrl;
SPIFLASH.user.val = reg_bkp_usr;
return ESP_OK;
}
#endif
void bootloader_enable_qio_mode(void) void bootloader_enable_qio_mode(void)
{ {
uint32_t raw_flash_id; uint32_t raw_flash_id;
@ -208,8 +147,8 @@ void bootloader_enable_qio_mode(void)
enable_qio_mode(chip_data[i].read_status_fn, enable_qio_mode(chip_data[i].read_status_fn,
chip_data[i].write_status_fn, chip_data[i].write_status_fn,
chip_data[i].status_qio_bit); chip_data[i].status_qio_bit);
#if CONFIG_IDF_TARGET_ESP32S2BETA #if SOC_CACHE_SUPPORT_WRAP
spi_flash_wrap_set(FLASH_WRAP_MODE_DISABLE); bootloader_flash_wrap_set(FLASH_WRAP_MODE_DISABLE);
#endif #endif
} }
@ -226,7 +165,7 @@ static esp_err_t enable_qio_mode(read_status_fn_t read_status_fn,
ESP_LOGD(TAG, "Initial flash chip status 0x%x", status); ESP_LOGD(TAG, "Initial flash chip status 0x%x", status);
if ((status & (1 << status_qio_bit)) == 0) { if ((status & (1 << status_qio_bit)) == 0) {
execute_flash_command(CMD_WREN, 0, 0, 0); bootloader_execute_flash_command(CMD_WREN, 0, 0, 0);
write_status_fn(status | (1 << status_qio_bit)); write_status_fn(status | (1 << status_qio_bit));
esp_rom_spiflash_wait_idle(&g_rom_flashchip); esp_rom_spiflash_wait_idle(&g_rom_flashchip);
@ -264,95 +203,48 @@ static esp_err_t enable_qio_mode(read_status_fn_t read_status_fn,
static unsigned read_status_8b_rdsr(void) static unsigned read_status_8b_rdsr(void)
{ {
return execute_flash_command(CMD_RDSR, 0, 0, 8); return bootloader_execute_flash_command(CMD_RDSR, 0, 0, 8);
} }
static unsigned read_status_8b_rdsr2(void) static unsigned read_status_8b_rdsr2(void)
{ {
return execute_flash_command(CMD_RDSR2, 0, 0, 8); return bootloader_execute_flash_command(CMD_RDSR2, 0, 0, 8);
} }
static unsigned read_status_16b_rdsr_rdsr2(void) static unsigned read_status_16b_rdsr_rdsr2(void)
{ {
return execute_flash_command(CMD_RDSR, 0, 0, 8) | (execute_flash_command(CMD_RDSR2, 0, 0, 8) << 8); return bootloader_execute_flash_command(CMD_RDSR, 0, 0, 8) | (bootloader_execute_flash_command(CMD_RDSR2, 0, 0, 8) << 8);
} }
static void write_status_8b_wrsr(unsigned new_status) static void write_status_8b_wrsr(unsigned new_status)
{ {
execute_flash_command(CMD_WRSR, new_status, 8, 0); bootloader_execute_flash_command(CMD_WRSR, new_status, 8, 0);
} }
static void write_status_8b_wrsr2(unsigned new_status) static void write_status_8b_wrsr2(unsigned new_status)
{ {
execute_flash_command(CMD_WRSR2, new_status, 8, 0); bootloader_execute_flash_command(CMD_WRSR2, new_status, 8, 0);
} }
static void write_status_16b_wrsr(unsigned new_status) static void write_status_16b_wrsr(unsigned new_status)
{ {
execute_flash_command(CMD_WRSR, new_status, 16, 0); bootloader_execute_flash_command(CMD_WRSR, new_status, 16, 0);
} }
static unsigned read_status_8b_xmc25qu64a(void) static unsigned read_status_8b_xmc25qu64a(void)
{ {
execute_flash_command(CMD_OTPEN, 0, 0, 0); /* Enter OTP mode */ bootloader_execute_flash_command(CMD_OTPEN, 0, 0, 0); /* Enter OTP mode */
esp_rom_spiflash_wait_idle(&g_rom_flashchip); esp_rom_spiflash_wait_idle(&g_rom_flashchip);
uint32_t read_status = execute_flash_command(CMD_RDSR, 0, 0, 8); uint32_t read_status = bootloader_execute_flash_command(CMD_RDSR, 0, 0, 8);
execute_flash_command(CMD_WRDI, 0, 0, 0); /* Exit OTP mode */ bootloader_execute_flash_command(CMD_WRDI, 0, 0, 0); /* Exit OTP mode */
return read_status; return read_status;
} }
static void write_status_8b_xmc25qu64a(unsigned new_status) static void write_status_8b_xmc25qu64a(unsigned new_status)
{ {
execute_flash_command(CMD_OTPEN, 0, 0, 0); /* Enter OTP mode */ bootloader_execute_flash_command(CMD_OTPEN, 0, 0, 0); /* Enter OTP mode */
esp_rom_spiflash_wait_idle(&g_rom_flashchip); esp_rom_spiflash_wait_idle(&g_rom_flashchip);
execute_flash_command(CMD_WRSR, new_status, 8, 0); bootloader_execute_flash_command(CMD_WRSR, new_status, 8, 0);
esp_rom_spiflash_wait_idle(&g_rom_flashchip); esp_rom_spiflash_wait_idle(&g_rom_flashchip);
execute_flash_command(CMD_WRDI, 0, 0, 0); /* Exit OTP mode */ bootloader_execute_flash_command(CMD_WRDI, 0, 0, 0); /* Exit OTP mode */
}
static uint32_t 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
SPIFLASH.ctrl.val = SPI_WP_REG_M; // keep WP high while idle, otherwise leave DIO mode
#elif CONFIG_IDF_TARGET_ESP32S2BETA
SPIFLASH.ctrl.val = SPI_MEM_WP_REG_M; // keep WP high while idle, otherwise leave DIO mode
#endif
SPIFLASH.user.usr_dummy = 0;
SPIFLASH.user.usr_addr = 0;
SPIFLASH.user.usr_command = 1;
SPIFLASH.user2.usr_command_bitlen = 7;
SPIFLASH.user2.usr_command_value = command;
SPIFLASH.user.usr_miso = miso_len > 0;
#if CONFIG_IDF_TARGET_ESP32
SPIFLASH.miso_dlen.usr_miso_dbitlen = miso_len ? (miso_len - 1) : 0;
#elif CONFIG_IDF_TARGET_ESP32S2BETA
SPIFLASH.miso_dlen.usr_miso_bit_len = miso_len ? (miso_len - 1) : 0;
#endif
SPIFLASH.user.usr_mosi = mosi_len > 0;
#if CONFIG_IDF_TARGET_ESP32
SPIFLASH.mosi_dlen.usr_mosi_dbitlen = mosi_len ? (mosi_len - 1) : 0;
#elif CONFIG_IDF_TARGET_ESP32S2BETA
SPIFLASH.mosi_dlen.usr_mosi_bit_len = mosi_len ? (mosi_len - 1) : 0;
#endif
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;
}
}
SPIFLASH.cmd.usr = 1;
while (SPIFLASH.cmd.usr != 0) {
}
SPIFLASH.ctrl.val = old_ctrl_reg;
return SPIFLASH.data_buf[0];
} }

View File

@ -121,6 +121,7 @@ extern "C" {
#define ESP_ROM_SPIFLASH_BP2 BIT4 #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_WR_PROTECT (ESP_ROM_SPIFLASH_BP0|ESP_ROM_SPIFLASH_BP1|ESP_ROM_SPIFLASH_BP2)
#define ESP_ROM_SPIFLASH_QE BIT9 #define ESP_ROM_SPIFLASH_QE BIT9
#define ESP_ROM_SPIFLASH_BP_MASK_ISSI (BIT7 | BIT5 | BIT4 | BIT3 | BIT2)
//Extra dummy for flash read //Extra dummy for flash read
#define ESP_ROM_SPIFLASH_DUMMY_LEN_PLUS_20M 0 #define ESP_ROM_SPIFLASH_DUMMY_LEN_PLUS_20M 0

View File

@ -116,6 +116,7 @@ extern "C" {
#define ESP_ROM_SPIFLASH_BP2 BIT4 #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_WR_PROTECT (ESP_ROM_SPIFLASH_BP0|ESP_ROM_SPIFLASH_BP1|ESP_ROM_SPIFLASH_BP2)
#define ESP_ROM_SPIFLASH_QE BIT9 #define ESP_ROM_SPIFLASH_QE BIT9
#define ESP_ROM_SPIFLASH_BP_MASK_ISSI (BIT7 | BIT5 | BIT4 | BIT3 | BIT2)
#define FLASH_ID_GD25LQ32C 0xC86016 #define FLASH_ID_GD25LQ32C 0xC86016

View File

@ -4,3 +4,5 @@
// include them here. // include them here.
#pragma once #pragma once
#define SOC_CACHE_SUPPORT_WRAP 1

View File

@ -1,5 +1,3 @@
set(priv_requires bootloader_support soc)
if(BOOTLOADER_BUILD) if(BOOTLOADER_BUILD)
if (CONFIG_IDF_TARGET_ESP32) if (CONFIG_IDF_TARGET_ESP32)
# ESP32 Bootloader needs SPIUnlock from this file, but doesn't # ESP32 Bootloader needs SPIUnlock from this file, but doesn't
@ -12,6 +10,7 @@ if(BOOTLOADER_BUILD)
set(srcs) set(srcs)
endif() endif()
set(cache_srcs "") set(cache_srcs "")
set(priv_requires bootloader_support soc)
else() else()
set(cache_srcs set(cache_srcs
"cache_utils.c" "cache_utils.c"

View File

@ -49,8 +49,9 @@ menu "SPI Flash driver"
default y default y
help help
Enable this flag to use patched versions of SPI flash ROM driver functions. Enable this flag to use patched versions of SPI flash ROM driver functions.
This option is needed to write to flash on ESP32-D2WD, and any configuration This option should be enabled, if any one of the following is true: (1) need to write
where external SPI flash is connected to non-default pins. to flash on ESP32-D2WD; (2) main SPI flash is connected to non-default pins; (3) main
SPI flash chip is manufactured by ISSI.
choice SPI_FLASH_DANGEROUS_WRITE choice SPI_FLASH_DANGEROUS_WRITE
bool "Writing to dangerous flash regions" bool "Writing to dangerous flash regions"

View File

@ -21,8 +21,14 @@
#define SPI_IDX 1 #define SPI_IDX 1
#define OTH_IDX 0 #define OTH_IDX 0
extern esp_rom_spiflash_chip_t g_rom_spiflash_chip; extern esp_rom_spiflash_chip_t g_rom_spiflash_chip;
static inline bool is_issi_chip(const esp_rom_spiflash_chip_t* chip)
{
return (((chip->device_id >> 16)&0xff) == 0x9D);
}
esp_rom_spiflash_result_t esp_rom_spiflash_wait_idle(esp_rom_spiflash_chip_t *spi) esp_rom_spiflash_result_t esp_rom_spiflash_wait_idle(esp_rom_spiflash_chip_t *spi)
{ {
uint32_t status; uint32_t status;
@ -61,25 +67,43 @@ esp_rom_spiflash_result_t esp_rom_spiflash_wait_idle(esp_rom_spiflash_chip_t *sp
esp_rom_spiflash_result_t esp_rom_spiflash_unlock(void) esp_rom_spiflash_result_t esp_rom_spiflash_unlock(void)
{ {
uint32_t status; uint32_t status;
uint32_t new_status;
esp_rom_spiflash_wait_idle(&g_rom_spiflash_chip); esp_rom_spiflash_wait_idle(&g_rom_spiflash_chip);
if (esp_rom_spiflash_read_statushigh(&g_rom_spiflash_chip, &status) != ESP_ROM_SPIFLASH_RESULT_OK) { if (is_issi_chip(&g_rom_spiflash_chip)) {
return ESP_ROM_SPIFLASH_RESULT_ERR; // ISSI chips have different QE position
}
/* Clear all bits except QIE, if it is set. if (esp_rom_spiflash_read_status(&g_rom_spiflash_chip, &status) != ESP_ROM_SPIFLASH_RESULT_OK) {
(This is different from ROM esp_rom_spiflash_unlock, which keeps all bits as-is.) return ESP_ROM_SPIFLASH_RESULT_ERR;
*/ }
status &= ESP_ROM_SPIFLASH_QE;
/* 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_ROM_SPIFLASH_BP_MASK_ISSI);
// Skip if nothing needs to be cleared. Otherwise will waste time waiting for the flash to clear nothing.
if (new_status == status) return ESP_ROM_SPIFLASH_RESULT_OK;
CLEAR_PERI_REG_MASK(SPI_CTRL_REG(SPI_IDX), SPI_WRSR_2B);
} else {
if (esp_rom_spiflash_read_statushigh(&g_rom_spiflash_chip, &status) != ESP_ROM_SPIFLASH_RESULT_OK) {
return ESP_ROM_SPIFLASH_RESULT_ERR;
}
/* 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_ROM_SPIFLASH_QE;
SET_PERI_REG_MASK(SPI_CTRL_REG(SPI_IDX), SPI_WRSR_2B);
}
esp_rom_spiflash_wait_idle(&g_rom_spiflash_chip); esp_rom_spiflash_wait_idle(&g_rom_spiflash_chip);
REG_WRITE(SPI_CMD_REG(SPI_IDX), SPI_FLASH_WREN); REG_WRITE(SPI_CMD_REG(SPI_IDX), SPI_FLASH_WREN);
while (REG_READ(SPI_CMD_REG(SPI_IDX)) != 0) { while (REG_READ(SPI_CMD_REG(SPI_IDX)) != 0) {
} }
esp_rom_spiflash_wait_idle(&g_rom_spiflash_chip); esp_rom_spiflash_wait_idle(&g_rom_spiflash_chip);
SET_PERI_REG_MASK(SPI_CTRL_REG(SPI_IDX), SPI_WRSR_2B); if (esp_rom_spiflash_write_status(&g_rom_spiflash_chip, new_status) != ESP_ROM_SPIFLASH_RESULT_OK) {
if (esp_rom_spiflash_write_status(&g_rom_spiflash_chip, status) != ESP_ROM_SPIFLASH_RESULT_OK) {
return ESP_ROM_SPIFLASH_RESULT_ERR; return ESP_ROM_SPIFLASH_RESULT_ERR;
} }

View File

@ -20,6 +20,7 @@
#include "soc/soc_memory_layout.h" #include "soc/soc_memory_layout.h"
#include "esp32s2beta/rom/spi_flash.h" #include "esp32s2beta/rom/spi_flash.h"
#include "esp32s2beta/rom/cache.h" #include "esp32s2beta/rom/cache.h"
#include "bootloader_flash.h"
#include "hal/spi_flash_hal.h" #include "hal/spi_flash_hal.h"
#include "esp_flash.h" #include "esp_flash.h"
@ -71,48 +72,22 @@ esp_rom_spiflash_result_t IRAM_ATTR spi_flash_write_encrypted_chip(size_t dest_a
#define SPICACHE SPIMEM0 #define SPICACHE SPIMEM0
#define SPIFLASH SPIMEM1 #define SPIFLASH SPIMEM1
#define FLASH_WRAP_CMD 0x77
esp_err_t spi_flash_wrap_set(spi_flash_wrap_mode_t mode) esp_err_t spi_flash_wrap_set(spi_flash_wrap_mode_t mode)
{ {
uint32_t reg_bkp_ctrl = SPIFLASH.ctrl.val; return bootloader_flash_wrap_set(mode);
uint32_t reg_bkp_usr = SPIFLASH.user.val;
SPIFLASH.user.fwrite_dio = 0;
SPIFLASH.user.fwrite_dual = 0;
SPIFLASH.user.fwrite_qio = 1;
SPIFLASH.user.fwrite_quad = 0;
SPIFLASH.ctrl.fcmd_dual = 0;
SPIFLASH.ctrl.fcmd_quad = 0;
SPIFLASH.user.usr_dummy = 0;
SPIFLASH.user.usr_addr = 1;
SPIFLASH.user.usr_command = 1;
SPIFLASH.user2.usr_command_bitlen = 7;
SPIFLASH.user2.usr_command_value = FLASH_WRAP_CMD;
SPIFLASH.user1.usr_addr_bitlen = 23;
SPIFLASH.addr = 0;
SPIFLASH.user.usr_miso = 0;
SPIFLASH.user.usr_mosi = 1;
SPIFLASH.mosi_dlen.usr_mosi_bit_len = 7;
SPIFLASH.data_buf[0] = (uint32_t) mode << 4;;
SPIFLASH.cmd.usr = 1;
while(SPIFLASH.cmd.usr != 0)
{ }
SPIFLASH.ctrl.val = reg_bkp_ctrl;
SPIFLASH.user.val = reg_bkp_usr;
return ESP_OK;
} }
esp_err_t spi_flash_enable_wrap(uint32_t wrap_size) esp_err_t spi_flash_enable_wrap(uint32_t wrap_size)
{ {
switch(wrap_size) { switch(wrap_size) {
case 8: case 8:
return spi_flash_wrap_set(FLASH_WRAP_MODE_8B); return bootloader_flash_wrap_set(FLASH_WRAP_MODE_8B);
case 16: case 16:
return spi_flash_wrap_set(FLASH_WRAP_MODE_16B); return bootloader_flash_wrap_set(FLASH_WRAP_MODE_16B);
case 32: case 32:
return spi_flash_wrap_set(FLASH_WRAP_MODE_32B); return bootloader_flash_wrap_set(FLASH_WRAP_MODE_32B);
case 64: case 64:
return spi_flash_wrap_set(FLASH_WRAP_MODE_64B); return bootloader_flash_wrap_set(FLASH_WRAP_MODE_64B);
default: default:
return ESP_FAIL; return ESP_FAIL;
} }
@ -120,7 +95,7 @@ esp_err_t spi_flash_enable_wrap(uint32_t wrap_size)
void spi_flash_disable_wrap(void) void spi_flash_disable_wrap(void)
{ {
spi_flash_wrap_set(FLASH_WRAP_MODE_DISABLE); bootloader_flash_wrap_set(FLASH_WRAP_MODE_DISABLE);
} }
bool spi_flash_support_wrap_size(uint32_t wrap_size) bool spi_flash_support_wrap_size(uint32_t wrap_size)

View File

@ -35,8 +35,6 @@
#include "esp32/clk.h" #include "esp32/clk.h"
#elif CONFIG_IDF_TARGET_ESP32S2BETA #elif CONFIG_IDF_TARGET_ESP32S2BETA
#include "esp32s2beta/clk.h" #include "esp32s2beta/clk.h"
#include "soc/spi_mem_reg.h"
#include "soc/spi_mem_struct.h"
#endif #endif
#include "esp_flash_partitions.h" #include "esp_flash_partitions.h"
#include "cache_utils.h" #include "cache_utils.h"
@ -832,6 +830,7 @@ void spi_flash_dump_counters(void)
#endif //CONFIG_SPI_FLASH_ENABLE_COUNTERS #endif //CONFIG_SPI_FLASH_ENABLE_COUNTERS
#if defined(CONFIG_SPI_FLASH_USE_LEGACY_IMPL) && defined(CONFIG_IDF_TARGET_ESP32S2BETA) #if defined(CONFIG_SPI_FLASH_USE_LEGACY_IMPL) && defined(CONFIG_IDF_TARGET_ESP32S2BETA)
// TODO esp32s2beta: Remove once ESP32S2Beta has new SPI Flash API support // TODO esp32s2beta: Remove once ESP32S2Beta has new SPI Flash API support
esp_flash_t *esp_flash_default_chip = NULL; esp_flash_t *esp_flash_default_chip = NULL;