spi_flash: refactor spi_flash.h to esp_rom_spiflash.h but keep the content in spi_flash.h

This commit is contained in:
Cao Sen Miao
2021-11-26 16:04:49 +08:00
parent 8e220f7bbe
commit d397464fc4
39 changed files with 2171 additions and 152 deletions
@@ -0,0 +1,749 @@
/*
* SPDX-FileCopyrightText: 2015-2021 Espressif Systems (Shanghai) CO LTD
*
* SPDX-License-Identifier: Apache-2.0
*/
#include <stddef.h>
#include <bootloader_flash_priv.h>
#include <esp_log.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
#include "esp_rom_spiflash.h"
#ifdef CONFIG_EFUSE_VIRTUAL_KEEP_IN_FLASH
#define ENCRYPTION_IS_VIRTUAL 1
#else
#define ENCRYPTION_IS_VIRTUAL 0
#endif
#define BYTESHIFT(VAR, IDX) (((VAR) >> ((IDX) * 8)) & 0xFF)
#define ISSI_ID 0x9D
#define MXIC_ID 0xC2
#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_GD_SR2 BIT1 // QE position when you write 8 bits(for SR2) at one time.
#define ESP_BOOTLOADER_SPIFLASH_QE_SR1_2BYTE BIT9 // QE position when you write 16 bits at one time.
#ifndef BOOTLOADER_BUILD
/* Normal app version maps to esp_spi_flash.h operations...
*/
static const char *TAG = "bootloader_mmap";
static spi_flash_mmap_handle_t map;
uint32_t bootloader_mmap_get_free_pages(void)
{
return spi_flash_mmap_get_free_pages(SPI_FLASH_MMAP_DATA);
}
const void *bootloader_mmap(uint32_t src_addr, uint32_t size)
{
if (map) {
ESP_LOGE(TAG, "tried to bootloader_mmap twice");
return NULL; /* existing mapping in use... */
}
const void *result = NULL;
uint32_t src_page = src_addr & ~(SPI_FLASH_MMU_PAGE_SIZE - 1);
size += (src_addr - src_page);
esp_err_t err = spi_flash_mmap(src_page, size, SPI_FLASH_MMAP_DATA, &result, &map);
if (err != ESP_OK) {
ESP_LOGE(TAG, "spi_flash_mmap failed: 0x%x", err);
return NULL;
}
return (void *)((intptr_t)result + (src_addr - src_page));
}
void bootloader_munmap(const void *mapping)
{
if (mapping && map) {
spi_flash_munmap(map);
}
map = 0;
}
esp_err_t bootloader_flash_read(size_t src, void *dest, size_t size, bool allow_decrypt)
{
if (allow_decrypt && esp_flash_encryption_enabled()) {
return spi_flash_read_encrypted(src, dest, size);
} else {
return spi_flash_read(src, dest, size);
}
}
esp_err_t bootloader_flash_write(size_t dest_addr, void *src, size_t size, bool write_encrypted)
{
if (write_encrypted && !ENCRYPTION_IS_VIRTUAL) {
#if CONFIG_IDF_TARGET_ESP32
return spi_flash_write_encrypted(dest_addr, src, size);
#else
return esp_rom_spiflash_write_encrypted(dest_addr, src, size);
#endif
} else {
return spi_flash_write(dest_addr, src, size);
}
}
esp_err_t bootloader_flash_erase_sector(size_t sector)
{
return spi_flash_erase_sector(sector);
}
esp_err_t bootloader_flash_erase_range(uint32_t start_addr, uint32_t size)
{
return spi_flash_erase_range(start_addr, size);
}
#else //BOOTLOADER_BUILD
/* Bootloader version, uses ROM functions only */
#if CONFIG_IDF_TARGET_ESP32
#include "esp32/rom/cache.h"
#elif CONFIG_IDF_TARGET_ESP32S2
#include "esp32s2/rom/cache.h"
#include "soc/cache_memory.h"
#elif CONFIG_IDF_TARGET_ESP32S3
#include "esp32s3/rom/cache.h"
#include "soc/cache_memory.h"
#elif CONFIG_IDF_TARGET_ESP32C3
#include "esp32c3/rom/cache.h"
#include "soc/cache_memory.h"
#elif CONFIG_IDF_TARGET_ESP32H2
#include "esp32h2/rom/cache.h"
#include "soc/cache_memory.h"
#elif CONFIG_IDF_TARGET_ESP8684
#include "esp8684/rom/cache.h"
#include "soc/cache_memory.h"
#endif
#include "esp_rom_spiflash.h"
static const char *TAG = "bootloader_flash";
#if CONFIG_IDF_TARGET_ESP32
/* Use first 50 blocks in MMU for bootloader_mmap,
50th block for bootloader_flash_read
*/
#define MMU_BLOCK0_VADDR SOC_DROM_LOW
#define MMU_SIZE (0x320000)
#define MMU_BLOCK50_VADDR (MMU_BLOCK0_VADDR + MMU_SIZE)
#define FLASH_READ_VADDR MMU_BLOCK50_VADDR
#else // !CONFIG_IDF_TARGET_ESP32
/* Use first 63 blocks in MMU for bootloader_mmap,
63th block for bootloader_flash_read
*/
#define MMU_BLOCK0_VADDR SOC_DROM_LOW
#define MMU_SIZE (0x3f0000)
#define MMU_BLOCK63_VADDR (MMU_BLOCK0_VADDR + MMU_SIZE)
#define FLASH_READ_VADDR MMU_BLOCK63_VADDR
#endif
#define MMU_FREE_PAGES (MMU_SIZE / FLASH_BLOCK_SIZE)
static bool mapped;
// Current bootloader mapping (ab)used for bootloader_read()
static uint32_t current_read_mapping = UINT32_MAX;
uint32_t bootloader_mmap_get_free_pages(void)
{
/**
* Allow mapping up to 50 of the 51 available MMU blocks (last one used for reads)
* Since, bootloader_mmap function below assumes it to be 0x320000 (50 pages), we can safely do this.
*/
return MMU_FREE_PAGES;
}
const void *bootloader_mmap(uint32_t src_addr, uint32_t size)
{
if (mapped) {
ESP_LOGE(TAG, "tried to bootloader_mmap twice");
return NULL; /* can't map twice */
}
if (size > MMU_SIZE) {
ESP_LOGE(TAG, "bootloader_mmap excess size %x", size);
return NULL;
}
uint32_t src_addr_aligned = src_addr & MMU_FLASH_MASK;
uint32_t count = bootloader_cache_pages_to_map(size, src_addr);
#if CONFIG_IDF_TARGET_ESP32
Cache_Read_Disable(0);
Cache_Flush(0);
#elif SOC_ICACHE_ACCESS_RODATA_SUPPORTED
uint32_t autoload = Cache_Suspend_ICache();
Cache_Invalidate_ICache_All();
#else // access rodata with DCache
uint32_t autoload = Cache_Suspend_DCache();
Cache_Invalidate_DCache_All();
#endif
ESP_LOGD(TAG, "mmu set paddr=%08x count=%d size=%x src_addr=%x src_addr_aligned=%x",
src_addr & MMU_FLASH_MASK, count, size, src_addr, src_addr_aligned );
#if CONFIG_IDF_TARGET_ESP32
int e = cache_flash_mmu_set(0, 0, MMU_BLOCK0_VADDR, src_addr_aligned, 64, count);
#elif CONFIG_IDF_TARGET_ESP32S2
int e = Cache_Ibus_MMU_Set(MMU_ACCESS_FLASH, MMU_BLOCK0_VADDR, src_addr_aligned, 64, count, 0);
#else
int e = Cache_Dbus_MMU_Set(MMU_ACCESS_FLASH, MMU_BLOCK0_VADDR, src_addr_aligned, 64, count, 0);
#endif
if (e != 0) {
ESP_LOGE(TAG, "cache_flash_mmu_set failed: %d\n", e);
#if CONFIG_IDF_TARGET_ESP32
Cache_Read_Enable(0);
#elif SOC_ICACHE_ACCESS_RODATA_SUPPORTED
Cache_Resume_ICache(autoload);
#else // access rodata with DCache
Cache_Resume_DCache(autoload);
#endif
return NULL;
}
#if CONFIG_IDF_TARGET_ESP32
Cache_Read_Enable(0);
#elif SOC_ICACHE_ACCESS_RODATA_SUPPORTED
Cache_Resume_ICache(autoload);
#else // access rodata with DCache
Cache_Resume_DCache(autoload);
#endif
mapped = true;
return (void *)(MMU_BLOCK0_VADDR + (src_addr - src_addr_aligned));
}
void bootloader_munmap(const void *mapping)
{
if (mapped) {
#if CONFIG_IDF_TARGET_ESP32
/* Full MMU reset */
Cache_Read_Disable(0);
Cache_Flush(0);
mmu_init(0);
#elif SOC_ICACHE_ACCESS_RODATA_SUPPORTED
//TODO, save the autoload value.
Cache_Suspend_ICache();
Cache_Invalidate_ICache_All();
Cache_MMU_Init();
#else // access rodata with DCache
Cache_Suspend_DCache();
Cache_Invalidate_DCache_All();
Cache_MMU_Init();
#endif
mapped = false;
current_read_mapping = UINT32_MAX;
}
}
static esp_err_t spi_to_esp_err(esp_rom_spiflash_result_t r)
{
switch (r) {
case ESP_ROM_SPIFLASH_RESULT_OK:
return ESP_OK;
case ESP_ROM_SPIFLASH_RESULT_ERR:
return ESP_ERR_FLASH_OP_FAIL;
case ESP_ROM_SPIFLASH_RESULT_TIMEOUT:
return ESP_ERR_FLASH_OP_TIMEOUT;
default:
return ESP_FAIL;
}
}
static esp_err_t bootloader_flash_read_no_decrypt(size_t src_addr, void *dest, size_t size)
{
#if CONFIG_IDF_TARGET_ESP32
Cache_Read_Disable(0);
Cache_Flush(0);
#elif SOC_ICACHE_ACCESS_RODATA_SUPPORTED
uint32_t autoload = Cache_Suspend_ICache();
#else // access rodata with DCache
uint32_t autoload = Cache_Suspend_DCache();
#endif
esp_rom_spiflash_result_t r = esp_rom_spiflash_read(src_addr, dest, size);
#if CONFIG_IDF_TARGET_ESP32
Cache_Read_Enable(0);
#elif SOC_ICACHE_ACCESS_RODATA_SUPPORTED
Cache_Resume_ICache(autoload);
#else // access rodata with DCache
Cache_Resume_DCache(autoload);
#endif
return spi_to_esp_err(r);
}
static esp_err_t bootloader_flash_read_allow_decrypt(size_t src_addr, void *dest, size_t size)
{
uint32_t *dest_words = (uint32_t *)dest;
for (size_t word = 0; word < size / 4; word++) {
uint32_t word_src = src_addr + word * 4; /* Read this offset from flash */
uint32_t map_at = word_src & MMU_FLASH_MASK; /* Map this 64KB block from flash */
uint32_t *map_ptr;
if (map_at != current_read_mapping) {
/* Move the 64KB mmu mapping window to fit map_at */
#if CONFIG_IDF_TARGET_ESP32
Cache_Read_Disable(0);
Cache_Flush(0);
#elif SOC_ICACHE_ACCESS_RODATA_SUPPORTED
uint32_t autoload = Cache_Suspend_ICache();
Cache_Invalidate_ICache_All();
#else // access rodata with DCache
uint32_t autoload = Cache_Suspend_DCache();
Cache_Invalidate_DCache_All();
#endif
ESP_LOGD(TAG, "mmu set block paddr=0x%08x (was 0x%08x)", map_at, current_read_mapping);
#if CONFIG_IDF_TARGET_ESP32
int e = cache_flash_mmu_set(0, 0, FLASH_READ_VADDR, map_at, 64, 1);
#elif CONFIG_IDF_TARGET_ESP32S2
int e = Cache_Ibus_MMU_Set(MMU_ACCESS_FLASH, MMU_BLOCK63_VADDR, map_at, 64, 1, 0);
#else // map rodata with DBus
int e = Cache_Dbus_MMU_Set(MMU_ACCESS_FLASH, MMU_BLOCK63_VADDR, map_at, 64, 1, 0);
#endif
if (e != 0) {
ESP_LOGE(TAG, "cache_flash_mmu_set failed: %d\n", e);
#if CONFIG_IDF_TARGET_ESP32
Cache_Read_Enable(0);
#elif SOC_ICACHE_ACCESS_RODATA_SUPPORTED
Cache_Resume_ICache(autoload);
#else // access rodata with DCache
Cache_Resume_DCache(autoload);
#endif
return ESP_FAIL;
}
current_read_mapping = map_at;
#if CONFIG_IDF_TARGET_ESP32
Cache_Read_Enable(0);
#elif SOC_ICACHE_ACCESS_RODATA_SUPPORTED
Cache_Resume_ICache(autoload);
#else // access rodata with DCache
Cache_Resume_DCache(autoload);
#endif
}
map_ptr = (uint32_t *)(FLASH_READ_VADDR + (word_src - map_at));
dest_words[word] = *map_ptr;
}
return ESP_OK;
}
esp_err_t bootloader_flash_read(size_t src_addr, void *dest, size_t size, bool allow_decrypt)
{
if (src_addr & 3) {
ESP_LOGE(TAG, "bootloader_flash_read src_addr 0x%x not 4-byte aligned", src_addr);
return ESP_FAIL;
}
if (size & 3) {
ESP_LOGE(TAG, "bootloader_flash_read size 0x%x not 4-byte aligned", size);
return ESP_FAIL;
}
if ((intptr_t)dest & 3) {
ESP_LOGE(TAG, "bootloader_flash_read dest 0x%x not 4-byte aligned", (intptr_t)dest);
return ESP_FAIL;
}
if (allow_decrypt) {
return bootloader_flash_read_allow_decrypt(src_addr, dest, size);
} else {
return bootloader_flash_read_no_decrypt(src_addr, dest, size);
}
}
esp_err_t bootloader_flash_write(size_t dest_addr, void *src, size_t size, bool write_encrypted)
{
esp_err_t err;
size_t alignment = write_encrypted ? 32 : 4;
if ((dest_addr % alignment) != 0) {
ESP_LOGE(TAG, "bootloader_flash_write dest_addr 0x%x not %d-byte aligned", dest_addr, alignment);
return ESP_FAIL;
}
if ((size % alignment) != 0) {
ESP_LOGE(TAG, "bootloader_flash_write size 0x%x not %d-byte aligned", size, alignment);
return ESP_FAIL;
}
if (((intptr_t)src % 4) != 0) {
ESP_LOGE(TAG, "bootloader_flash_write src 0x%x not 4 byte aligned", (intptr_t)src);
return ESP_FAIL;
}
err = bootloader_flash_unlock();
if (err != ESP_OK) {
return err;
}
if (write_encrypted && !ENCRYPTION_IS_VIRTUAL) {
return spi_to_esp_err(esp_rom_spiflash_write_encrypted(dest_addr, src, size));
} else {
return spi_to_esp_err(esp_rom_spiflash_write(dest_addr, src, size));
}
}
esp_err_t bootloader_flash_erase_sector(size_t sector)
{
return spi_to_esp_err(esp_rom_spiflash_erase_sector(sector));
}
esp_err_t bootloader_flash_erase_range(uint32_t start_addr, uint32_t size)
{
if (start_addr % FLASH_SECTOR_SIZE != 0) {
return ESP_ERR_INVALID_ARG;
}
if (size % FLASH_SECTOR_SIZE != 0) {
return ESP_ERR_INVALID_SIZE;
}
size_t start = start_addr / FLASH_SECTOR_SIZE;
size_t end = start + size / FLASH_SECTOR_SIZE;
const size_t sectors_per_block = FLASH_BLOCK_SIZE / FLASH_SECTOR_SIZE;
esp_rom_spiflash_result_t rc = ESP_ROM_SPIFLASH_RESULT_OK;
for (size_t sector = start; sector != end && rc == ESP_ROM_SPIFLASH_RESULT_OK; ) {
if (sector % sectors_per_block == 0 && end - sector >= sectors_per_block) {
rc = esp_rom_spiflash_erase_block(sector / sectors_per_block);
sector += sectors_per_block;
} else {
rc = esp_rom_spiflash_erase_sector(sector);
++sector;
}
}
return spi_to_esp_err(rc);
}
#endif // BOOTLOADER_BUILD
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;
}
FORCE_INLINE_ATTR bool is_mxic_chip(const esp_rom_spiflash_chip_t* chip)
{
return BYTESHIFT(chip->device_id, 2) == MXIC_ID;
}
esp_err_t IRAM_ATTR __attribute__((weak)) bootloader_flash_unlock(void)
{
// At the beginning status == new_status == status_sr2 == new_status_sr2 == 0.
// If the register doesn't need to be updated, keep them the same (0), so that no command will be actually sent.
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 sr1_bit_num = 0;
esp_err_t err = ESP_OK;
esp_rom_spiflash_wait_idle(&g_rom_flashchip);
if (is_issi_chip(&g_rom_flashchip) || is_mxic_chip(&g_rom_flashchip)) {
// Currently ISSI & MXIC share the same command and register layout, which is different from the default model.
// If any code here needs to be modified, check both chips.
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.)
*/
sr1_bit_num = 8;
new_status = status & (~ESP_BOOTLOADER_SPIFLASH_BP_MASK_ISSI);
} 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.
*/
status = bootloader_execute_flash_command(CMD_RDSR, 0, 0, 8);
sr1_bit_num = 8;
new_status = 0;
status_sr2 = bootloader_execute_flash_command(CMD_RDSR2, 0, 0, 8);
new_status_sr2 = status_sr2 & ESP_BOOTLOADER_SPIFLASH_QE_GD_SR2;
} else {
/* For common behaviour, like XMC chips, Use 01H+2Bytes to write both SR1 and SR2*/
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.)
*/
sr1_bit_num = 16;
new_status = status & ESP_BOOTLOADER_SPIFLASH_QE_SR1_2BYTE;
}
// When SR is written, set to true to indicate that WRDI need to be sent to ensure the protection is ON before return.
bool status_written = false;
// Skip if nothing needs to be changed. Meaningless writing to SR increases the risk during write and wastes time.
if (status != new_status) {
esp_rom_spiflash_wait_idle(&g_rom_flashchip);
bootloader_execute_flash_command(CMD_WREN, 0, 0, 0);
bootloader_execute_flash_command(CMD_WRSR, new_status, sr1_bit_num, 0);
status_written = true;
}
if (status_sr2 != new_status_sr2) {
esp_rom_spiflash_wait_idle(&g_rom_flashchip);
bootloader_execute_flash_command(CMD_WREN, 0, 0, 0);
bootloader_execute_flash_command(CMD_WRSR2, new_status_sr2, 8, 0);
status_written = true;
}
if (status_written) {
//Call esp_rom_spiflash_wait_idle to make sure previous WRSR is completed.
esp_rom_spiflash_wait_idle(&g_rom_flashchip);
bootloader_execute_flash_command(CMD_WRDI, 0, 0, 0);
}
return err;
}
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;
#if CONFIG_IDF_TARGET_ESP32
SPIFLASH.ctrl.val = SPI_WP_REG_M; // keep WP high while idle, otherwise leave DIO mode
#else
SPIFLASH.ctrl.val = SPI_MEM_WP_REG_M; // keep WP high while idle, otherwise leave DIO mode
#endif
//command phase
SPIFLASH.user.usr_command = 1;
SPIFLASH.user2.usr_command_bitlen = 7;
SPIFLASH.user2.usr_command_value = command;
//addr phase
SPIFLASH.user.usr_addr = addr_len > 0;
SPIFLASH.user1.usr_addr_bitlen = addr_len - 1;
#if CONFIG_IDF_TARGET_ESP32
SPIFLASH.addr = (addr_len > 0)? (address << (32-addr_len)) : 0;
#else
SPIFLASH.addr = address;
#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;
#if CONFIG_IDF_TARGET_ESP32
SPIFLASH.mosi_dlen.usr_mosi_dbitlen = mosi_len ? (mosi_len - 1) : 0;
#else
SPIFLASH.mosi_dlen.usr_mosi_bit_len = mosi_len ? (mosi_len - 1) : 0;
#endif
SPIFLASH.data_buf[0] = mosi_data;
//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;
while (SPIFLASH.cmd.usr != 0) {
}
SPIFLASH.ctrl.val = old_ctrl_reg;
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;
}
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)
{
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
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
/*******************************************************************************
* 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
@@ -0,0 +1,186 @@
/*
* SPDX-FileCopyrightText: 2018-2021 Espressif Systems (Shanghai) CO LTD
*
* SPDX-License-Identifier: Apache-2.0
*/
#include <stdbool.h>
#include <assert.h>
#include "string.h"
#include "sdkconfig.h"
#include "esp_err.h"
#include "esp_log.h"
#include "esp_rom_gpio.h"
#include "esp_rom_efuse.h"
#include "esp32/rom/spi_flash.h"
#include "soc/gpio_periph.h"
#include "soc/efuse_reg.h"
#include "soc/spi_reg.h"
#include "soc/soc_caps.h"
#include "soc/soc_pins.h"
#include "hal/gpio_hal.h"
#include "flash_qio_mode.h"
#include "bootloader_common.h"
#include "bootloader_flash_config.h"
void bootloader_flash_update_id(void)
{
g_rom_flashchip.device_id = bootloader_read_flash_id();
}
void bootloader_flash_update_size(uint32_t size)
{
g_rom_flashchip.chip_size = size;
}
void IRAM_ATTR bootloader_flash_cs_timing_config(void)
{
SET_PERI_REG_MASK(SPI_USER_REG(0), SPI_CS_HOLD_M | SPI_CS_SETUP_M);
SET_PERI_REG_BITS(SPI_CTRL2_REG(0), SPI_HOLD_TIME_V, 1, SPI_HOLD_TIME_S);
SET_PERI_REG_BITS(SPI_CTRL2_REG(0), SPI_SETUP_TIME_V, 0, SPI_SETUP_TIME_S);
SET_PERI_REG_MASK(SPI_USER_REG(1), SPI_CS_HOLD_M | SPI_CS_SETUP_M);
SET_PERI_REG_BITS(SPI_CTRL2_REG(1), SPI_HOLD_TIME_V, 1, SPI_HOLD_TIME_S);
SET_PERI_REG_BITS(SPI_CTRL2_REG(1), SPI_SETUP_TIME_V, 0, SPI_SETUP_TIME_S);
}
void IRAM_ATTR bootloader_flash_clock_config(const esp_image_header_t* pfhdr)
{
uint32_t spi_clk_div = 0;
switch (pfhdr->spi_speed) {
case ESP_IMAGE_SPI_SPEED_80M:
spi_clk_div = 1;
break;
case ESP_IMAGE_SPI_SPEED_40M:
spi_clk_div = 2;
break;
case ESP_IMAGE_SPI_SPEED_26M:
spi_clk_div = 3;
break;
case ESP_IMAGE_SPI_SPEED_20M:
spi_clk_div = 4;
break;
default:
break;
}
esp_rom_spiflash_config_clk(spi_clk_div, 0);
esp_rom_spiflash_config_clk(spi_clk_div, 1);
}
void IRAM_ATTR bootloader_flash_gpio_config(const esp_image_header_t* pfhdr)
{
uint32_t drv = 2;
if (pfhdr->spi_speed == ESP_IMAGE_SPI_SPEED_80M) {
drv = 3;
}
uint32_t pkg_ver = bootloader_common_get_chip_ver_pkg();
if (pkg_ver == EFUSE_RD_CHIP_VER_PKG_ESP32D2WDQ5 ||
pkg_ver == EFUSE_RD_CHIP_VER_PKG_ESP32PICOD2 ||
pkg_ver == EFUSE_RD_CHIP_VER_PKG_ESP32PICOD4 ||
pkg_ver == EFUSE_RD_CHIP_VER_PKG_ESP32PICOV302) {
// For ESP32D2WD or ESP32-PICO series,the SPI pins are already configured
// flash clock signal should come from IO MUX.
gpio_hal_iomux_func_sel(PERIPHS_IO_MUX_SD_CLK_U, FUNC_SD_CLK_SPICLK);
SET_PERI_REG_BITS(PERIPHS_IO_MUX_SD_CLK_U, FUN_DRV, drv, FUN_DRV_S);
} else {
const uint32_t spiconfig = esp_rom_efuse_get_flash_gpio_info();
if (spiconfig == ESP_ROM_EFUSE_FLASH_DEFAULT_SPI) {
esp_rom_gpio_connect_out_signal(SPI_IOMUX_PIN_NUM_CS, SPICS0_OUT_IDX, 0, 0);
esp_rom_gpio_connect_out_signal(SPI_IOMUX_PIN_NUM_MISO, SPIQ_OUT_IDX, 0, 0);
esp_rom_gpio_connect_in_signal(SPI_IOMUX_PIN_NUM_MISO, SPIQ_IN_IDX, 0);
esp_rom_gpio_connect_out_signal(SPI_IOMUX_PIN_NUM_MOSI, SPID_OUT_IDX, 0, 0);
esp_rom_gpio_connect_in_signal(SPI_IOMUX_PIN_NUM_MOSI, SPID_IN_IDX, 0);
esp_rom_gpio_connect_out_signal(SPI_IOMUX_PIN_NUM_WP, SPIWP_OUT_IDX, 0, 0);
esp_rom_gpio_connect_in_signal(SPI_IOMUX_PIN_NUM_WP, SPIWP_IN_IDX, 0);
esp_rom_gpio_connect_out_signal(SPI_IOMUX_PIN_NUM_HD, SPIHD_OUT_IDX, 0, 0);
esp_rom_gpio_connect_in_signal(SPI_IOMUX_PIN_NUM_HD, SPIHD_IN_IDX, 0);
//select pin function gpio
gpio_hal_iomux_func_sel(PERIPHS_IO_MUX_SD_DATA0_U, PIN_FUNC_GPIO);
gpio_hal_iomux_func_sel(PERIPHS_IO_MUX_SD_DATA1_U, PIN_FUNC_GPIO);
gpio_hal_iomux_func_sel(PERIPHS_IO_MUX_SD_DATA2_U, PIN_FUNC_GPIO);
gpio_hal_iomux_func_sel(PERIPHS_IO_MUX_SD_DATA3_U, PIN_FUNC_GPIO);
gpio_hal_iomux_func_sel(PERIPHS_IO_MUX_SD_CMD_U, PIN_FUNC_GPIO);
// flash clock signal should come from IO MUX.
// set drive ability for clock
gpio_hal_iomux_func_sel(PERIPHS_IO_MUX_SD_CLK_U, FUNC_SD_CLK_SPICLK);
SET_PERI_REG_BITS(PERIPHS_IO_MUX_SD_CLK_U, FUN_DRV, drv, FUN_DRV_S);
uint32_t flash_id = g_rom_flashchip.device_id;
if (flash_id == FLASH_ID_GD25LQ32C) {
// Set drive ability for 1.8v flash in 80Mhz.
SET_PERI_REG_BITS(PERIPHS_IO_MUX_SD_DATA0_U, FUN_DRV, 3, FUN_DRV_S);
SET_PERI_REG_BITS(PERIPHS_IO_MUX_SD_DATA1_U, FUN_DRV, 3, FUN_DRV_S);
SET_PERI_REG_BITS(PERIPHS_IO_MUX_SD_DATA2_U, FUN_DRV, 3, FUN_DRV_S);
SET_PERI_REG_BITS(PERIPHS_IO_MUX_SD_DATA3_U, FUN_DRV, 3, FUN_DRV_S);
SET_PERI_REG_BITS(PERIPHS_IO_MUX_SD_CMD_U, FUN_DRV, 3, FUN_DRV_S);
SET_PERI_REG_BITS(PERIPHS_IO_MUX_SD_CLK_U, FUN_DRV, 3, FUN_DRV_S);
}
}
}
}
void IRAM_ATTR bootloader_flash_dummy_config(const esp_image_header_t* pfhdr)
{
int spi_cache_dummy = 0;
uint32_t modebit = READ_PERI_REG(SPI_CTRL_REG(0));
if (modebit & SPI_FASTRD_MODE) {
if (modebit & SPI_FREAD_QIO) { //SPI mode is QIO
spi_cache_dummy = SPI0_R_QIO_DUMMY_CYCLELEN;
} else if (modebit & SPI_FREAD_DIO) { //SPI mode is DIO
spi_cache_dummy = SPI0_R_DIO_DUMMY_CYCLELEN;
SET_PERI_REG_BITS(SPI_USER1_REG(0), SPI_USR_ADDR_BITLEN_V, SPI0_R_DIO_ADDR_BITSLEN, SPI_USR_ADDR_BITLEN_S);
} else if(modebit & (SPI_FREAD_QUAD | SPI_FREAD_DUAL)) { //SPI mode is QOUT or DIO
spi_cache_dummy = SPI0_R_FAST_DUMMY_CYCLELEN;
}
}
switch (pfhdr->spi_speed) {
case ESP_IMAGE_SPI_SPEED_80M:
g_rom_spiflash_dummy_len_plus[0] = ESP_ROM_SPIFLASH_DUMMY_LEN_PLUS_80M;
g_rom_spiflash_dummy_len_plus[1] = ESP_ROM_SPIFLASH_DUMMY_LEN_PLUS_80M;
break;
case ESP_IMAGE_SPI_SPEED_40M:
g_rom_spiflash_dummy_len_plus[0] = ESP_ROM_SPIFLASH_DUMMY_LEN_PLUS_40M;
g_rom_spiflash_dummy_len_plus[1] = ESP_ROM_SPIFLASH_DUMMY_LEN_PLUS_40M;
break;
case ESP_IMAGE_SPI_SPEED_26M:
case ESP_IMAGE_SPI_SPEED_20M:
g_rom_spiflash_dummy_len_plus[0] = ESP_ROM_SPIFLASH_DUMMY_LEN_PLUS_20M;
g_rom_spiflash_dummy_len_plus[1] = ESP_ROM_SPIFLASH_DUMMY_LEN_PLUS_20M;
break;
default:
break;
}
SET_PERI_REG_BITS(SPI_USER1_REG(0), SPI_USR_DUMMY_CYCLELEN_V, spi_cache_dummy + g_rom_spiflash_dummy_len_plus[0],
SPI_USR_DUMMY_CYCLELEN_S);
}
#define ESP32_D2WD_WP_GPIO 7 /* ESP32-D2WD & ESP32-PICO-D4 has this GPIO wired to WP pin of flash */
#define ESP32_PICO_V3_GPIO 18 /* ESP32-PICO-V3* use this GPIO for WP pin of flash */
int bootloader_flash_get_wp_pin(void)
{
#if CONFIG_BOOTLOADER_SPI_CUSTOM_WP_PIN
return CONFIG_BOOTLOADER_SPI_WP_PIN; // can be set for bootloader when QIO or QOUT config in use
#elif CONFIG_SPIRAM_CUSTOM_SPIWP_SD3_PIN
return CONFIG_SPIRAM_SPIWP_SD3_PIN; // can be set for app when DIO or DOUT config used for PSRAM only
#else
// no custom value, find it based on the package eFuse value
uint8_t chip_ver;
uint32_t pkg_ver = bootloader_common_get_chip_ver_pkg();
switch(pkg_ver) {
case EFUSE_RD_CHIP_VER_PKG_ESP32U4WDH:
case EFUSE_RD_CHIP_VER_PKG_ESP32D2WDQ5:
return ESP32_D2WD_WP_GPIO;
case EFUSE_RD_CHIP_VER_PKG_ESP32PICOD4:
/* Same package IDs are used for ESP32-PICO-V3 and ESP32-PICO-D4, silicon version differentiates */
chip_ver = bootloader_common_get_chip_revision();
return (chip_ver < 3) ? ESP32_D2WD_WP_GPIO : ESP32_PICO_V3_GPIO;
case EFUSE_RD_CHIP_VER_PKG_ESP32PICOV302:
return ESP32_PICO_V3_GPIO;
default:
return SPI_WP_GPIO_NUM;
}
#endif
}
@@ -0,0 +1,75 @@
/*
* SPDX-FileCopyrightText: 2020-2021 Espressif Systems (Shanghai) CO LTD
*
* SPDX-License-Identifier: Apache-2.0
*/
#include <stdbool.h>
#include <assert.h>
#include "string.h"
#include "sdkconfig.h"
#include "esp_err.h"
#include "esp_log.h"
#include "esp32c3/rom/gpio.h"
#include "esp32c3/rom/spi_flash.h"
#include "esp32c3/rom/efuse.h"
#include "soc/gpio_periph.h"
#include "soc/efuse_reg.h"
#include "soc/spi_reg.h"
#include "soc/spi_mem_reg.h"
#include "soc/soc_caps.h"
#include "flash_qio_mode.h"
#include "bootloader_flash_config.h"
#include "bootloader_common.h"
#define FLASH_IO_MATRIX_DUMMY_40M 0
#define FLASH_IO_MATRIX_DUMMY_80M 0
void bootloader_flash_update_id()
{
esp_rom_spiflash_chip_t *chip = &rom_spiflash_legacy_data->chip;
chip->device_id = bootloader_read_flash_id();
}
void IRAM_ATTR bootloader_flash_cs_timing_config()
{
SET_PERI_REG_MASK(SPI_MEM_USER_REG(0), SPI_MEM_CS_HOLD_M | SPI_MEM_CS_SETUP_M);
SET_PERI_REG_BITS(SPI_MEM_CTRL2_REG(0), SPI_MEM_CS_HOLD_TIME_V, 0, SPI_MEM_CS_HOLD_TIME_S);
SET_PERI_REG_BITS(SPI_MEM_CTRL2_REG(0), SPI_MEM_CS_SETUP_TIME_V, 0, SPI_MEM_CS_SETUP_TIME_S);
SET_PERI_REG_MASK(SPI_MEM_USER_REG(1), SPI_MEM_CS_HOLD_M | SPI_MEM_CS_SETUP_M);
SET_PERI_REG_BITS(SPI_MEM_CTRL2_REG(1), SPI_MEM_CS_HOLD_TIME_V, 1, SPI_MEM_CS_HOLD_TIME_S);
SET_PERI_REG_BITS(SPI_MEM_CTRL2_REG(1), SPI_MEM_CS_SETUP_TIME_V, 0, SPI_MEM_CS_SETUP_TIME_S);
}
void IRAM_ATTR bootloader_flash_clock_config(const esp_image_header_t *pfhdr)
{
uint32_t spi_clk_div = 0;
switch (pfhdr->spi_speed) {
case ESP_IMAGE_SPI_SPEED_80M:
spi_clk_div = 1;
break;
case ESP_IMAGE_SPI_SPEED_40M:
spi_clk_div = 2;
break;
case ESP_IMAGE_SPI_SPEED_26M:
spi_clk_div = 3;
break;
case ESP_IMAGE_SPI_SPEED_20M:
spi_clk_div = 4;
break;
default:
break;
}
esp_rom_spiflash_config_clk(spi_clk_div, 0);
}
void IRAM_ATTR bootloader_flash_set_dummy_out(void)
{
REG_SET_BIT(SPI_MEM_CTRL_REG(0), SPI_MEM_FDUMMY_OUT | SPI_MEM_D_POL | SPI_MEM_Q_POL);
REG_SET_BIT(SPI_MEM_CTRL_REG(1), SPI_MEM_FDUMMY_OUT | SPI_MEM_D_POL | SPI_MEM_Q_POL);
}
void IRAM_ATTR bootloader_flash_dummy_config(const esp_image_header_t *pfhdr)
{
bootloader_configure_spi_pins(1);
bootloader_flash_set_dummy_out();
}
@@ -0,0 +1,75 @@
/*
* SPDX-FileCopyrightText: 2020-2021 Espressif Systems (Shanghai) CO LTD
*
* SPDX-License-Identifier: Apache-2.0
*/
#include <stdbool.h>
#include <assert.h>
#include "string.h"
#include "sdkconfig.h"
#include "esp_err.h"
#include "esp_log.h"
#include "esp32h2/rom/gpio.h"
#include "esp32h2/rom/spi_flash.h"
#include "esp32h2/rom/efuse.h"
#include "soc/gpio_periph.h"
#include "soc/efuse_reg.h"
#include "soc/spi_reg.h"
#include "soc/spi_mem_reg.h"
#include "soc/soc_caps.h"
#include "flash_qio_mode.h"
#include "bootloader_flash_config.h"
#include "bootloader_common.h"
#define FLASH_IO_MATRIX_DUMMY_40M 0
#define FLASH_IO_MATRIX_DUMMY_80M 0
void bootloader_flash_update_id()
{
esp_rom_spiflash_chip_t *chip = &rom_spiflash_legacy_data->chip;
chip->device_id = bootloader_read_flash_id();
}
void IRAM_ATTR bootloader_flash_cs_timing_config()
{
SET_PERI_REG_MASK(SPI_MEM_USER_REG(0), SPI_MEM_CS_HOLD_M | SPI_MEM_CS_SETUP_M);
SET_PERI_REG_BITS(SPI_MEM_CTRL2_REG(0), SPI_MEM_CS_HOLD_TIME_V, 0, SPI_MEM_CS_HOLD_TIME_S);
SET_PERI_REG_BITS(SPI_MEM_CTRL2_REG(0), SPI_MEM_CS_SETUP_TIME_V, 0, SPI_MEM_CS_SETUP_TIME_S);
SET_PERI_REG_MASK(SPI_MEM_USER_REG(1), SPI_MEM_CS_HOLD_M | SPI_MEM_CS_SETUP_M);
SET_PERI_REG_BITS(SPI_MEM_CTRL2_REG(1), SPI_MEM_CS_HOLD_TIME_V, 1, SPI_MEM_CS_HOLD_TIME_S);
SET_PERI_REG_BITS(SPI_MEM_CTRL2_REG(1), SPI_MEM_CS_SETUP_TIME_V, 0, SPI_MEM_CS_SETUP_TIME_S);
}
void IRAM_ATTR bootloader_flash_clock_config(const esp_image_header_t *pfhdr)
{
uint32_t spi_clk_div = 0;
switch (pfhdr->spi_speed) {
case ESP_IMAGE_SPI_SPEED_80M:
spi_clk_div = 1;
break;
case ESP_IMAGE_SPI_SPEED_40M:
spi_clk_div = 2;
break;
case ESP_IMAGE_SPI_SPEED_26M:
spi_clk_div = 3;
break;
case ESP_IMAGE_SPI_SPEED_20M:
spi_clk_div = 4;
break;
default:
break;
}
esp_rom_spiflash_config_clk(spi_clk_div, 0);
}
void IRAM_ATTR bootloader_flash_set_dummy_out(void)
{
REG_SET_BIT(SPI_MEM_CTRL_REG(0), SPI_MEM_FDUMMY_OUT | SPI_MEM_D_POL | SPI_MEM_Q_POL);
REG_SET_BIT(SPI_MEM_CTRL_REG(1), SPI_MEM_FDUMMY_OUT | SPI_MEM_D_POL | SPI_MEM_Q_POL);
}
void IRAM_ATTR bootloader_flash_dummy_config(const esp_image_header_t *pfhdr)
{
bootloader_configure_spi_pins(1);
bootloader_flash_set_dummy_out();
}
@@ -0,0 +1,79 @@
/*
* SPDX-FileCopyrightText: 2019-2021 Espressif Systems (Shanghai) CO LTD
*
* SPDX-License-Identifier: Apache-2.0
*/
#include <stdbool.h>
#include <assert.h>
#include "string.h"
#include "sdkconfig.h"
#include "esp_err.h"
#include "esp_log.h"
#include "esp32s2/rom/spi_flash.h"
#include "soc/efuse_reg.h"
#include "soc/spi_reg.h"
#include "soc/spi_mem_reg.h"
#include "soc/soc_caps.h"
#include "flash_qio_mode.h"
#include "bootloader_flash_config.h"
#include "bootloader_common.h"
#define FLASH_IO_MATRIX_DUMMY_40M 0
#define FLASH_IO_MATRIX_DUMMY_80M 0
#define FLASH_IO_DRIVE_GD_WITH_1V8PSRAM 3
void bootloader_flash_update_id()
{
g_rom_flashchip.device_id = bootloader_read_flash_id();
}
void bootloader_flash_update_size(uint32_t size)
{
g_rom_flashchip.chip_size = size;
}
void IRAM_ATTR bootloader_flash_cs_timing_config()
{
SET_PERI_REG_MASK(SPI_MEM_USER_REG(0), SPI_MEM_CS_HOLD_M | SPI_MEM_CS_SETUP_M);
SET_PERI_REG_BITS(SPI_MEM_CTRL2_REG(0), SPI_MEM_CS_HOLD_TIME_V, 0, SPI_MEM_CS_HOLD_TIME_S);
SET_PERI_REG_BITS(SPI_MEM_CTRL2_REG(0), SPI_MEM_CS_SETUP_TIME_V, 0, SPI_MEM_CS_SETUP_TIME_S);
SET_PERI_REG_MASK(SPI_MEM_USER_REG(1), SPI_MEM_CS_HOLD_M | SPI_MEM_CS_SETUP_M);
SET_PERI_REG_BITS(SPI_MEM_CTRL2_REG(1), SPI_MEM_CS_HOLD_TIME_V, 1, SPI_MEM_CS_HOLD_TIME_S);
SET_PERI_REG_BITS(SPI_MEM_CTRL2_REG(1), SPI_MEM_CS_SETUP_TIME_V, 0, SPI_MEM_CS_SETUP_TIME_S);
}
void IRAM_ATTR bootloader_flash_clock_config(const esp_image_header_t* pfhdr)
{
uint32_t spi_clk_div = 0;
switch (pfhdr->spi_speed) {
case ESP_IMAGE_SPI_SPEED_80M:
spi_clk_div = 1;
break;
case ESP_IMAGE_SPI_SPEED_40M:
spi_clk_div = 2;
break;
case ESP_IMAGE_SPI_SPEED_26M:
spi_clk_div = 3;
break;
case ESP_IMAGE_SPI_SPEED_20M:
spi_clk_div = 4;
break;
default:
break;
}
esp_rom_spiflash_config_clk(spi_clk_div, 0);
esp_rom_spiflash_config_clk(spi_clk_div, 1);
}
void IRAM_ATTR bootloader_flash_set_dummy_out(void)
{
REG_SET_BIT(SPI_MEM_CTRL_REG(0), SPI_MEM_FDUMMY_OUT | SPI_MEM_D_POL | SPI_MEM_Q_POL);
REG_SET_BIT(SPI_MEM_CTRL_REG(1), SPI_MEM_FDUMMY_OUT | SPI_MEM_D_POL | SPI_MEM_Q_POL);
}
void IRAM_ATTR bootloader_flash_dummy_config(const esp_image_header_t* pfhdr)
{
bootloader_configure_spi_pins(1);
bootloader_flash_set_dummy_out();
}
@@ -0,0 +1,83 @@
/*
* SPDX-FileCopyrightText: 2020-2021 Espressif Systems (Shanghai) CO LTD
*
* SPDX-License-Identifier: Apache-2.0
*/
#include <stdbool.h>
#include <assert.h>
#include "string.h"
#include "sdkconfig.h"
#include "esp_err.h"
#include "esp_log.h"
#include "esp32s3/rom/spi_flash.h"
#include "soc/efuse_reg.h"
#include "soc/spi_reg.h"
#include "soc/spi_mem_reg.h"
#include "soc/soc_caps.h"
#include "flash_qio_mode.h"
#include "bootloader_flash_config.h"
#include "bootloader_common.h"
#define FLASH_IO_MATRIX_DUMMY_40M 0
#define FLASH_IO_MATRIX_DUMMY_80M 0
#define FLASH_IO_DRIVE_GD_WITH_1V8PSRAM 3
#define FLASH_CS_SETUP_TIME 3
#define FLASH_CS_HOLD_TIME 3
#define FLASH_CS_HOLD_DELAY 2
void bootloader_flash_update_id()
{
esp_rom_spiflash_chip_t *chip = &rom_spiflash_legacy_data->chip;
chip->device_id = bootloader_read_flash_id();
}
void IRAM_ATTR bootloader_flash_cs_timing_config()
{
//SPI0/1 share the cs_hold / cs_setup, cd_hold_time / cd_setup_time, cs_hold_delay registers for FLASH, so we only need to set SPI0 related registers here
#if CONFIG_ESPTOOLPY_OCT_FLASH
SET_PERI_REG_MASK(SPI_MEM_USER_REG(0), SPI_MEM_CS_HOLD_M | SPI_MEM_CS_SETUP_M);
SET_PERI_REG_BITS(SPI_MEM_CTRL2_REG(0), SPI_MEM_CS_HOLD_TIME_V, FLASH_CS_HOLD_TIME, SPI_MEM_CS_HOLD_TIME_S);
SET_PERI_REG_BITS(SPI_MEM_CTRL2_REG(0), SPI_MEM_CS_SETUP_TIME_V, FLASH_CS_SETUP_TIME, SPI_MEM_CS_SETUP_TIME_S);
//CS high time
SET_PERI_REG_BITS(SPI_MEM_CTRL2_REG(0), SPI_MEM_CS_HOLD_DELAY_V, FLASH_CS_HOLD_DELAY, SPI_MEM_CS_HOLD_DELAY_S);
#else
SET_PERI_REG_BITS(SPI_MEM_CTRL2_REG(0), SPI_MEM_CS_HOLD_TIME_V, 0, SPI_MEM_CS_HOLD_TIME_S);
SET_PERI_REG_BITS(SPI_MEM_CTRL2_REG(0), SPI_MEM_CS_SETUP_TIME_V, 0, SPI_MEM_CS_SETUP_TIME_S);
SET_PERI_REG_MASK(SPI_MEM_USER_REG(0), SPI_MEM_CS_HOLD_M | SPI_MEM_CS_SETUP_M);
#endif
}
void IRAM_ATTR bootloader_flash_clock_config(const esp_image_header_t *pfhdr)
{
uint32_t spi_clk_div = 0;
switch (pfhdr->spi_speed) {
case ESP_IMAGE_SPI_SPEED_80M:
spi_clk_div = 1;
break;
case ESP_IMAGE_SPI_SPEED_40M:
spi_clk_div = 2;
break;
case ESP_IMAGE_SPI_SPEED_26M:
spi_clk_div = 3;
break;
case ESP_IMAGE_SPI_SPEED_20M:
spi_clk_div = 4;
break;
default:
break;
}
esp_rom_spiflash_config_clk(spi_clk_div, 0);
esp_rom_spiflash_config_clk(spi_clk_div, 1);
}
void IRAM_ATTR bootloader_flash_set_dummy_out(void)
{
REG_SET_BIT(SPI_MEM_CTRL_REG(0), SPI_MEM_FDUMMY_OUT | SPI_MEM_D_POL | SPI_MEM_Q_POL);
REG_SET_BIT(SPI_MEM_CTRL_REG(1), SPI_MEM_FDUMMY_OUT | SPI_MEM_D_POL | SPI_MEM_Q_POL);
}
void IRAM_ATTR bootloader_flash_dummy_config(const esp_image_header_t *pfhdr)
{
bootloader_configure_spi_pins(1);
bootloader_flash_set_dummy_out();
}
@@ -0,0 +1,73 @@
/*
* SPDX-FileCopyrightText: 2020-2021 Espressif Systems (Shanghai) CO LTD
*
* SPDX-License-Identifier: Apache-2.0
*/
#include <stdbool.h>
#include <assert.h>
#include "string.h"
#include "sdkconfig.h"
#include "esp_err.h"
#include "esp_log.h"
#include "esp8684/rom/gpio.h"
#include "esp8684/rom/spi_flash.h"
#include "esp8684/rom/efuse.h"
#include "soc/gpio_periph.h"
#include "soc/efuse_reg.h"
#include "soc/spi_reg.h"
#include "soc/spi_mem_reg.h"
#include "soc/soc_caps.h"
#include "flash_qio_mode.h"
#include "bootloader_flash_config.h"
#include "bootloader_common.h"
#define FLASH_IO_MATRIX_DUMMY_40M 0
#define FLASH_IO_MATRIX_DUMMY_80M 0
void bootloader_flash_update_id()
{
esp_rom_spiflash_chip_t *chip = &rom_spiflash_legacy_data->chip;
chip->device_id = bootloader_read_flash_id();
}
void IRAM_ATTR bootloader_flash_cs_timing_config()
{
SET_PERI_REG_MASK(SPI_MEM_USER_REG(0), SPI_MEM_CS_HOLD_M | SPI_MEM_CS_SETUP_M);
SET_PERI_REG_BITS(SPI_MEM_CTRL2_REG(0), SPI_MEM_CS_HOLD_TIME_V, 0, SPI_MEM_CS_HOLD_TIME_S);
SET_PERI_REG_BITS(SPI_MEM_CTRL2_REG(0), SPI_MEM_CS_SETUP_TIME_V, 0, SPI_MEM_CS_SETUP_TIME_S);
}
void IRAM_ATTR bootloader_flash_clock_config(const esp_image_header_t *pfhdr)
{
uint32_t spi_clk_div = 0;
switch (pfhdr->spi_speed) {
// TODO: change MSPI freq, IDF-3831
case ESP_IMAGE_SPI_SPEED_80M:
spi_clk_div = 1;
break;
case ESP_IMAGE_SPI_SPEED_40M:
spi_clk_div = 2;
break;
case ESP_IMAGE_SPI_SPEED_26M:
spi_clk_div = 3;
break;
case ESP_IMAGE_SPI_SPEED_20M:
spi_clk_div = 4;
break;
default:
break;
}
esp_rom_spiflash_config_clk(spi_clk_div, 0);
}
void IRAM_ATTR bootloader_flash_set_dummy_out(void)
{
REG_SET_BIT(SPI_MEM_CTRL_REG(0), SPI_MEM_FDUMMY_OUT | SPI_MEM_D_POL | SPI_MEM_Q_POL);
REG_SET_BIT(SPI_MEM_CTRL_REG(1), SPI_MEM_FDUMMY_OUT | SPI_MEM_D_POL | SPI_MEM_Q_POL);
}
void IRAM_ATTR bootloader_flash_dummy_config(const esp_image_header_t *pfhdr)
{
bootloader_configure_spi_pins(1);
bootloader_flash_set_dummy_out();
}
@@ -0,0 +1,204 @@
/*
* SPDX-FileCopyrightText: 2015-2021 Espressif Systems (Shanghai) CO LTD
*
* SPDX-License-Identifier: Apache-2.0
*/
#include <stddef.h>
#include <stdint.h>
#include "bootloader_flash_config.h"
#include "flash_qio_mode.h"
#include "sdkconfig.h"
#include "bootloader_flash_priv.h"
#include "esp_log.h"
#include "esp_err.h"
#include "esp_rom_efuse.h"
#include "esp_rom_spiflash.h"
#include "flash_qio_mode.h"
#include "soc/efuse_periph.h"
#include "soc/io_mux_reg.h"
static const char *TAG = "qio_mode";
/* Array of known flash chips and data to enable Quad I/O mode
Manufacturer & flash ID can be tested by running "esptool.py
flash_id"
If manufacturer ID matches, and flash ID ORed with flash ID mask
matches, enable_qio_mode() will execute "Read Cmd", test if bit
number "QIE Bit" is set, and if not set it will call "Write Cmd"
with this bit set.
Searching of this table stops when the first match is found.
*/
const bootloader_qio_info_t __attribute__((weak)) bootloader_flash_qe_support_list[] = {
/* Manufacturer, mfg_id, flash_id, id mask, Read Status, Write Status, QIE Bit */
{ "MXIC", 0xC2, 0x2000, 0xFF00, bootloader_read_status_8b_rdsr, bootloader_write_status_8b_wrsr, 6 },
{ "ISSI", 0x9D, 0x4000, 0xCF00, bootloader_read_status_8b_rdsr, bootloader_write_status_8b_wrsr, 6 }, /* IDs 0x40xx, 0x70xx */
{ "WinBond", 0xEF, 0x4000, 0xFF00, bootloader_read_status_16b_rdsr_rdsr2, bootloader_write_status_16b_wrsr, 9 },
{ "GD", 0xC8, 0x6000, 0xFF00, bootloader_read_status_16b_rdsr_rdsr2, bootloader_write_status_16b_wrsr, 9 },
{ "XM25QU64A", 0x20, 0x3817, 0xFFFF, bootloader_read_status_8b_xmc25qu64a, bootloader_write_status_8b_xmc25qu64a, 6 },
{ "TH", 0xcd, 0x6000, 0xFF00, bootloader_read_status_16b_rdsr_rdsr2, bootloader_write_status_16b_wrsr, 9 },
/* Final entry is default entry, if no other IDs have matched.
This approach works for chips including:
GigaDevice (mfg ID 0xC8, flash IDs including 4016),
FM25Q32 (QOUT mode only, mfg ID 0xA1, flash IDs including 4016)
BY25Q32 (mfg ID 0x68, flash IDs including 4016)
*/
{ NULL, 0xFF, 0xFFFF, 0xFFFF, bootloader_read_status_8b_rdsr2, bootloader_write_status_8b_wrsr2, 1 },
};
#define NUM_CHIPS (sizeof(bootloader_flash_qe_support_list) / sizeof(bootloader_qio_info_t))
static esp_err_t enable_qio_mode(bootloader_flash_read_status_fn_t read_status_fn,
bootloader_flash_write_status_fn_t write_status_fn,
uint8_t status_qio_bit);
/* Generic function to use the "user command" SPI controller functionality
to send commands to the SPI flash and read the respopnse.
The command passed here is always the on-the-wire command given to the SPI flash unit.
*/
void bootloader_enable_qio_mode(void)
{
uint32_t raw_flash_id;
uint8_t mfg_id;
uint16_t flash_id;
size_t i;
ESP_LOGD(TAG, "Probing for QIO mode enable...");
esp_rom_spiflash_wait_idle(&g_rom_flashchip);
raw_flash_id = g_rom_flashchip.device_id;
ESP_LOGD(TAG, "Raw SPI flash chip id 0x%x", raw_flash_id);
mfg_id = (raw_flash_id >> 16) & 0xFF;
flash_id = raw_flash_id & 0xFFFF;
ESP_LOGD(TAG, "Manufacturer ID 0x%02x chip ID 0x%04x", mfg_id, flash_id);
for (i = 0; i < NUM_CHIPS - 1; i++) {
const bootloader_qio_info_t *chip = &bootloader_flash_qe_support_list[i];
if (mfg_id == chip->mfg_id && (flash_id & chip->id_mask) == (chip->flash_id & chip->id_mask)) {
ESP_LOGI(TAG, "Enabling QIO for flash chip %s", bootloader_flash_qe_support_list[i].manufacturer);
break;
}
}
if (i == NUM_CHIPS - 1) {
ESP_LOGI(TAG, "Enabling default flash chip QIO");
}
enable_qio_mode(bootloader_flash_qe_support_list[i].read_status_fn,
bootloader_flash_qe_support_list[i].write_status_fn,
bootloader_flash_qe_support_list[i].status_qio_bit);
#if SOC_CACHE_SUPPORT_WRAP
bootloader_flash_wrap_set(FLASH_WRAP_MODE_DISABLE);
#endif
}
static esp_err_t enable_qio_mode(bootloader_flash_read_status_fn_t read_status_fn,
bootloader_flash_write_status_fn_t write_status_fn,
uint8_t status_qio_bit)
{
uint32_t status;
esp_rom_spiflash_wait_idle(&g_rom_flashchip);
status = read_status_fn();
ESP_LOGD(TAG, "Initial flash chip status 0x%x", status);
if ((status & (1 << status_qio_bit)) == 0) {
bootloader_execute_flash_command(CMD_WREN, 0, 0, 0);
write_status_fn(status | (1 << status_qio_bit));
esp_rom_spiflash_wait_idle(&g_rom_flashchip);
status = read_status_fn();
ESP_LOGD(TAG, "Updated flash chip status 0x%x", status);
if ((status & (1 << status_qio_bit)) == 0) {
ESP_LOGE(TAG, "Failed to set QIE bit, not enabling QIO mode");
return ESP_FAIL;
}
} else {
ESP_LOGD(TAG, "QIO mode already enabled in flash");
}
ESP_LOGD(TAG, "Enabling QIO mode...");
esp_rom_spiflash_read_mode_t mode;
#if CONFIG_ESPTOOLPY_FLASHMODE_QOUT
mode = ESP_ROM_SPIFLASH_QOUT_MODE;
#else
mode = ESP_ROM_SPIFLASH_QIO_MODE;
#endif
esp_rom_spiflash_config_readmode(mode);
#if !CONFIG_IDF_TARGET_ESP8684
//IDF-3914
const uint32_t spiconfig = esp_rom_efuse_get_flash_gpio_info();
#endif
#if CONFIG_IDF_TARGET_ESP32
int wp_pin = bootloader_flash_get_wp_pin();
esp_rom_spiflash_select_qio_pins(wp_pin, spiconfig);
#elif CONFIG_IDF_TARGET_ESP8684
//IDF-3914
esp_rom_spiflash_select_qio_pins(0, 0);
#else
esp_rom_spiflash_select_qio_pins(esp_rom_efuse_get_flash_wp_gpio(), spiconfig);
#endif
return ESP_OK;
}
unsigned bootloader_read_status_8b_rdsr(void)
{
return bootloader_execute_flash_command(CMD_RDSR, 0, 0, 8);
}
unsigned bootloader_read_status_8b_rdsr2(void)
{
return bootloader_execute_flash_command(CMD_RDSR2, 0, 0, 8);
}
unsigned bootloader_read_status_16b_rdsr_rdsr2(void)
{
return bootloader_execute_flash_command(CMD_RDSR, 0, 0, 8) | (bootloader_execute_flash_command(CMD_RDSR2, 0, 0, 8) << 8);
}
void bootloader_write_status_8b_wrsr(unsigned new_status)
{
bootloader_execute_flash_command(CMD_WRSR, new_status, 8, 0);
}
void bootloader_write_status_8b_wrsr2(unsigned new_status)
{
bootloader_execute_flash_command(CMD_WRSR2, new_status, 8, 0);
}
void bootloader_write_status_16b_wrsr(unsigned new_status)
{
bootloader_execute_flash_command(CMD_WRSR, new_status, 16, 0);
}
unsigned bootloader_read_status_8b_xmc25qu64a(void)
{
bootloader_execute_flash_command(CMD_OTPEN, 0, 0, 0); /* Enter OTP mode */
esp_rom_spiflash_wait_idle(&g_rom_flashchip);
uint32_t read_status = bootloader_execute_flash_command(CMD_RDSR, 0, 0, 8);
bootloader_execute_flash_command(CMD_WRDI, 0, 0, 0); /* Exit OTP mode */
return read_status;
}
void bootloader_write_status_8b_xmc25qu64a(unsigned new_status)
{
bootloader_execute_flash_command(CMD_OTPEN, 0, 0, 0); /* Enter OTP mode */
esp_rom_spiflash_wait_idle(&g_rom_flashchip);
bootloader_execute_flash_command(CMD_WRSR, new_status, 8, 0);
esp_rom_spiflash_wait_idle(&g_rom_flashchip);
bootloader_execute_flash_command(CMD_WRDI, 0, 0, 0); /* Exit OTP mode */
}