mirror of
https://github.com/espressif/esp-idf.git
synced 2024-10-05 20:47:46 -04:00
Merge branch 'bugfix/xmc_overerase_v3.3' into 'release/v3.3'
bootloader: add xmc spi_flash startup flow to improve reliability (v3.3) See merge request espressif/esp-idf!14780
This commit is contained in:
commit
9fc4057e9e
@ -233,6 +233,15 @@ menu "Bootloader config"
|
||||
It allow to test anti-rollback implemention without permanent write eFuse bits.
|
||||
In partition table should be exist this partition `emul_efuse, data, 5, , 0x2000`.
|
||||
|
||||
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
|
||||
|
||||
|
||||
|
@ -32,6 +32,22 @@ void bootloader_enable_qio_mode(void);
|
||||
*/
|
||||
uint32_t bootloader_read_flash_id();
|
||||
|
||||
/**
|
||||
* @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 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
|
||||
}
|
||||
#endif
|
||||
|
@ -145,6 +145,14 @@ static esp_err_t bootloader_main()
|
||||
#endif
|
||||
bootloader_clock_configure();
|
||||
uart_console_configure();
|
||||
|
||||
/* Check and run XMC startup flow. The function depends on the clock and console, so it's run right after these stuff
|
||||
* are initialized. */
|
||||
if (bootloader_flash_xmc_startup() != ESP_OK) {
|
||||
ESP_LOGE(TAG, "failed when running XMC startup flow, reboot!");
|
||||
return ESP_FAIL;
|
||||
}
|
||||
|
||||
wdt_reset_check();
|
||||
ESP_LOGI(TAG, "ESP-IDF %s 2nd stage bootloader", IDF_VER);
|
||||
|
||||
|
@ -14,9 +14,11 @@
|
||||
#include <stddef.h>
|
||||
#include <stdint.h>
|
||||
#include "bootloader_flash_config.h"
|
||||
#include "esp_attr.h"
|
||||
#include "flash_qio_mode.h"
|
||||
#include "esp_log.h"
|
||||
#include "esp_err.h"
|
||||
#include "rom/ets_sys.h"
|
||||
#include "rom/spi_flash.h"
|
||||
#include "rom/efuse.h"
|
||||
#include "soc/spi_struct.h"
|
||||
@ -37,8 +39,12 @@
|
||||
#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_RDSFDP 0x5A /* Read the SFDP of the flash */
|
||||
|
||||
static const char *TAG = "qio_mode";
|
||||
#define BYTESHIFT(VAR, IDX) (((VAR) >> ((IDX) * 8)) & 0xFF)
|
||||
|
||||
|
||||
static DRAM_ATTR char TAG[] = "qio_mode";
|
||||
|
||||
typedef unsigned (*read_status_fn_t)();
|
||||
typedef void (*write_status_fn_t)(unsigned);
|
||||
@ -116,7 +122,7 @@ static uint32_t execute_flash_command(uint8_t command, uint32_t mosi_data, uint8
|
||||
|
||||
/* 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()
|
||||
uint32_t IRAM_ATTR bootloader_read_flash_id(void)
|
||||
{
|
||||
uint32_t id = execute_flash_command(CMD_RDID, 0, 0, 24);
|
||||
id = ((id & 0xff) << 16) | ((id >> 16) & 0xff) | (id & 0xff00);
|
||||
@ -251,37 +257,193 @@ static void write_status_8b_xmc25qu64a(unsigned new_status)
|
||||
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)
|
||||
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_user_reg = SPIFLASH.user.val;
|
||||
uint32_t old_user1_reg = SPIFLASH.user1.val;
|
||||
SPIFLASH.ctrl.val = SPI_WP_REG_M; // keep WP high while idle, otherwise leave DIO mode
|
||||
SPIFLASH.user.usr_dummy = 0;
|
||||
SPIFLASH.user.usr_addr = 0;
|
||||
//command phase
|
||||
SPIFLASH.user.usr_command = 1;
|
||||
SPIFLASH.user2.usr_command_bitlen = 7;
|
||||
|
||||
SPIFLASH.user2.usr_command_value = command;
|
||||
SPIFLASH.user.usr_miso = miso_len > 0;
|
||||
SPIFLASH.miso_dlen.usr_miso_dbitlen = miso_len ? (miso_len - 1) : 0;
|
||||
//addr phase
|
||||
SPIFLASH.user.usr_addr = addr_len > 0;
|
||||
SPIFLASH.user1.usr_addr_bitlen = addr_len - 1;
|
||||
SPIFLASH.addr = (addr_len > 0)? (address << (32-addr_len)) : 0;
|
||||
//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.mosi_dlen.usr_mosi_dbitlen = mosi_len ? (mosi_len - 1) : 0;
|
||||
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;
|
||||
SPIFLASH.miso_dlen.usr_miso_dbitlen = miso_len ? (miso_len - 1) : 0;
|
||||
|
||||
SPIFLASH.cmd.usr = 1;
|
||||
while(SPIFLASH.cmd.usr != 0)
|
||||
{ }
|
||||
|
||||
SPIFLASH.ctrl.val = old_ctrl_reg;
|
||||
return SPIFLASH.data_buf[0];
|
||||
SPIFLASH.user.val = old_user_reg;
|
||||
SPIFLASH.user1.val = old_user1_reg;
|
||||
|
||||
uint32_t ret = SPIFLASH.data_buf[0];
|
||||
if (miso_len < 32) {
|
||||
//set unused bits to 0
|
||||
ret &= ~(UINT32_MAX << miso_len);
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
static uint32_t IRAM_ATTR 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);
|
||||
}
|
||||
|
||||
/*******************************************************************************
|
||||
* 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
|
||||
#define BOOTLOADER_FLASH_LOG(level, ...) ESP_DRAM_LOG##level(TAG, ##__VA_ARGS__)
|
||||
|
||||
#define ESP_DRAM_LOGE( tag, format, ... ) ESP_DRAM_LOG_IMPL(tag, format, ESP_LOG_ERROR, E, ##__VA_ARGS__)
|
||||
#define ESP_DRAM_LOGW( tag, format, ... ) ESP_DRAM_LOG_IMPL(tag, format, ESP_LOG_WARN, E, ##__VA_ARGS__)
|
||||
#define ESP_DRAM_LOGI( tag, format, ... ) ESP_DRAM_LOG_IMPL(tag, format, ESP_LOG_INFO, E, ##__VA_ARGS__)
|
||||
#define ESP_DRAM_LOGD( tag, format, ... ) ESP_DRAM_LOG_IMPL(tag, format, ESP_LOG_DEBUG, E, ##__VA_ARGS__)
|
||||
#define ESP_DRAM_LOGV( tag, format, ... ) ESP_DRAM_LOG_IMPL(tag, format, ESP_LOG_VERBOSE, E, ##__VA_ARGS__)
|
||||
|
||||
#define ESP_DRAM_LOG_IMPL(tag, format, log_level, log_tag_letter, ...) do { \
|
||||
if (LOG_LOCAL_LEVEL >= (log_level)) { \
|
||||
ets_printf(DRAM_STR(#log_tag_letter " %s: " format "\n"), tag, ##__VA_ARGS__); \
|
||||
}} while(0)
|
||||
|
||||
#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
|
||||
execute_flash_command(0xB9, 0, 0, 0);
|
||||
// Enter UDPD
|
||||
execute_flash_command(0x79, 0, 0, 0);
|
||||
// Exit UDPD
|
||||
execute_flash_command(0xFF, 0, 0, 0);
|
||||
// Delay tXUDPD
|
||||
ets_delay_us(2000);
|
||||
// Release Power-down
|
||||
execute_flash_command(0xAB, 0, 0, 0);
|
||||
ets_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
|
||||
|
||||
|
@ -9,6 +9,8 @@
|
||||
#include "driver/timer.h"
|
||||
#include "esp_intr_alloc.h"
|
||||
#include "test_utils.h"
|
||||
#include "../include_bootloader/flash_qio_mode.h" //for bootloader_flash_xmc_startup
|
||||
|
||||
|
||||
struct flash_test_ctx {
|
||||
uint32_t offset;
|
||||
@ -226,3 +228,22 @@ TEST_CASE("spi_flash deadlock with high priority busy-waiting task", "[spi_flash
|
||||
TEST_ASSERT_EQUAL_INT(uxTaskPriorityGet(NULL), UNITY_FREERTOS_PRIORITY);
|
||||
}
|
||||
#endif // portNUM_PROCESSORS > 1
|
||||
|
||||
|
||||
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();
|
||||
}
|
Loading…
Reference in New Issue
Block a user