kopia lustrzana https://github.com/espressif/esp-idf
Merge branch 'feat/custom_flash_component' into 'master'
feature(spi_flash): New customized flash drivers framework, including bootloader📡 Closes IDFGH-8624 See merge request espressif/esp-idf!32774pull/14997/head
commit
2b1c27feb4
|
@ -1,5 +1,5 @@
|
|||
/*
|
||||
* SPDX-FileCopyrightText: 2020-2022 Espressif Systems (Shanghai) CO LTD
|
||||
* SPDX-FileCopyrightText: 2020-2024 Espressif Systems (Shanghai) CO LTD
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
@ -28,7 +28,7 @@ uint32_t bootloader_read_flash_id(void);
|
|||
/**
|
||||
* @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).
|
||||
* @return ESP_OK When startup successfully, otherwise ESP_FAIL (indicating you should reboot before erase/write).
|
||||
*/
|
||||
esp_err_t bootloader_flash_xmc_startup(void);
|
||||
|
||||
|
@ -36,9 +36,16 @@ esp_err_t bootloader_flash_xmc_startup(void);
|
|||
* @brief Unlock Flash write protect.
|
||||
* Please do not call this function in SDK.
|
||||
*
|
||||
* @note This can be overridden because it's attribute weak.
|
||||
* @note This can be overridden because it's attribute weak, when there is a same name symbol.
|
||||
*/
|
||||
esp_err_t __attribute__((weak)) bootloader_flash_unlock(void);
|
||||
esp_err_t bootloader_flash_unlock(void);
|
||||
|
||||
/**
|
||||
* @brief Unlock Flash write protect.
|
||||
* This is alias to `bootloader_flash_unlock`.
|
||||
* Please do not call this function in SDK.
|
||||
*/
|
||||
esp_err_t bootloader_flash_unlock_default(void);
|
||||
|
||||
/**
|
||||
* @brief Reset the flash chip (66H + 99H).
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
/*
|
||||
* SPDX-FileCopyrightText: 2020-2022 Espressif Systems (Shanghai) CO LTD
|
||||
* SPDX-FileCopyrightText: 2020-2024 Espressif Systems (Shanghai) CO LTD
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
@ -99,15 +99,20 @@ void bootloader_write_status_8b_xmc25qu64a(unsigned new_status);
|
|||
|
||||
Searching of this table stops when the first match is found.
|
||||
*/
|
||||
extern const bootloader_qio_info_t __attribute__((weak)) bootloader_flash_qe_support_list[];
|
||||
extern const bootloader_qio_info_t* bootloader_flash_qe_support_list;
|
||||
|
||||
/**
|
||||
* @brief The bootloader flash qe list count number.
|
||||
*/
|
||||
extern uint8_t bootloader_flash_qe_list_count;
|
||||
|
||||
/**
|
||||
* @brief Unlock Flash write protect.
|
||||
* Please do not call this function in SDK.
|
||||
*
|
||||
* @note This can be overridden because it's attribute weak.
|
||||
* @note This can be overridden because it's attribute weak, when there is a same name symbol.
|
||||
*/
|
||||
esp_err_t __attribute__((weak)) bootloader_flash_unlock(void);
|
||||
esp_err_t bootloader_flash_unlock(void);
|
||||
|
||||
#if CONFIG_BOOTLOADER_CACHE_32BIT_ADDR_QUAD_FLASH || CONFIG_BOOTLOADER_CACHE_32BIT_ADDR_OCTAL_FLASH
|
||||
/**
|
||||
|
@ -115,7 +120,7 @@ esp_err_t __attribute__((weak)) bootloader_flash_unlock(void);
|
|||
*
|
||||
* @param flash_mode SPI flash working mode.
|
||||
*
|
||||
* @note This can be overridden because it's attribute weak.
|
||||
* @note This can be overridden because it's attribute weak, when there is a same name symbol.
|
||||
*/
|
||||
void __attribute__((weak)) bootloader_flash_32bits_address_map_enable(esp_rom_spiflash_read_mode_t flash_mode);
|
||||
#endif
|
||||
|
|
|
@ -655,7 +655,7 @@ 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)
|
||||
esp_err_t IRAM_ATTR bootloader_flash_unlock_default(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.
|
||||
|
@ -724,6 +724,8 @@ esp_err_t IRAM_ATTR __attribute__((weak)) bootloader_flash_unlock(void)
|
|||
return err;
|
||||
}
|
||||
|
||||
esp_err_t __attribute__((weak, alias("bootloader_flash_unlock_default"))) bootloader_flash_unlock(void);
|
||||
|
||||
IRAM_ATTR uint32_t bootloader_flash_execute_command_common(
|
||||
uint8_t command,
|
||||
uint32_t addr_len, uint32_t address,
|
||||
|
|
|
@ -371,6 +371,10 @@ esp_err_t bootloader_init_spi_flash(void)
|
|||
}
|
||||
#endif
|
||||
|
||||
if ((void*)bootloader_flash_unlock != (void*)bootloader_flash_unlock_default) {
|
||||
ESP_EARLY_LOGD(TAG, "Using overridden bootloader_flash_unlock");
|
||||
}
|
||||
|
||||
bootloader_flash_unlock();
|
||||
|
||||
#if CONFIG_ESPTOOLPY_FLASHMODE_QIO || CONFIG_ESPTOOLPY_FLASHMODE_QOUT
|
||||
|
|
|
@ -238,6 +238,9 @@ esp_err_t bootloader_init_spi_flash(void)
|
|||
bootloader_init_flash_configure();
|
||||
|
||||
bootloader_spi_flash_resume();
|
||||
if ((void*)bootloader_flash_unlock != (void*)bootloader_flash_unlock_default) {
|
||||
ESP_EARLY_LOGD(TAG, "Using overridden bootloader_flash_unlock");
|
||||
}
|
||||
bootloader_flash_unlock();
|
||||
|
||||
#if CONFIG_ESPTOOLPY_FLASHMODE_QIO || CONFIG_ESPTOOLPY_FLASHMODE_QOUT
|
||||
|
|
|
@ -247,6 +247,9 @@ esp_err_t bootloader_init_spi_flash(void)
|
|||
#endif
|
||||
|
||||
bootloader_spi_flash_resume();
|
||||
if ((void*)bootloader_flash_unlock != (void*)bootloader_flash_unlock_default) {
|
||||
ESP_EARLY_LOGD(TAG, "Using overridden bootloader_flash_unlock");
|
||||
}
|
||||
bootloader_flash_unlock();
|
||||
|
||||
#if CONFIG_ESPTOOLPY_FLASHMODE_QIO || CONFIG_ESPTOOLPY_FLASHMODE_QOUT
|
||||
|
|
|
@ -218,6 +218,9 @@ esp_err_t bootloader_init_spi_flash(void)
|
|||
|
||||
bootloader_init_flash_configure();
|
||||
bootloader_spi_flash_resume();
|
||||
if ((void*)bootloader_flash_unlock != (void*)bootloader_flash_unlock_default) {
|
||||
ESP_EARLY_LOGD(TAG, "Using overridden bootloader_flash_unlock");
|
||||
}
|
||||
bootloader_flash_unlock();
|
||||
|
||||
#if CONFIG_ESPTOOLPY_FLASHMODE_QIO || CONFIG_ESPTOOLPY_FLASHMODE_QOUT
|
||||
|
|
|
@ -201,6 +201,9 @@ esp_err_t bootloader_init_spi_flash(void)
|
|||
{
|
||||
bootloader_init_flash_configure();
|
||||
bootloader_spi_flash_resume();
|
||||
if ((void*)bootloader_flash_unlock != (void*)bootloader_flash_unlock_default) {
|
||||
ESP_EARLY_LOGD(TAG, "Using overridden bootloader_flash_unlock");
|
||||
}
|
||||
bootloader_flash_unlock();
|
||||
|
||||
#if CONFIG_ESPTOOLPY_FLASHMODE_QIO || CONFIG_ESPTOOLPY_FLASHMODE_QOUT
|
||||
|
|
|
@ -212,6 +212,9 @@ esp_err_t bootloader_init_spi_flash(void)
|
|||
bootloader_init_mspi_clock();
|
||||
bootloader_init_flash_configure();
|
||||
bootloader_spi_flash_resume();
|
||||
if ((void*)bootloader_flash_unlock != (void*)bootloader_flash_unlock_default) {
|
||||
ESP_EARLY_LOGD(TAG, "Using overridden bootloader_flash_unlock");
|
||||
}
|
||||
bootloader_flash_unlock();
|
||||
|
||||
#if CONFIG_ESPTOOLPY_FLASHMODE_QIO || CONFIG_ESPTOOLPY_FLASHMODE_QOUT
|
||||
|
|
|
@ -209,6 +209,9 @@ esp_err_t bootloader_init_spi_flash(void)
|
|||
{
|
||||
bootloader_init_flash_configure();
|
||||
bootloader_spi_flash_resume();
|
||||
if ((void*)bootloader_flash_unlock != (void*)bootloader_flash_unlock_default) {
|
||||
ESP_EARLY_LOGD(TAG, "Using overridden bootloader_flash_unlock");
|
||||
}
|
||||
bootloader_flash_unlock();
|
||||
|
||||
#if CONFIG_ESPTOOLPY_FLASHMODE_QIO || CONFIG_ESPTOOLPY_FLASHMODE_QOUT
|
||||
|
|
|
@ -232,6 +232,9 @@ esp_err_t bootloader_init_spi_flash(void)
|
|||
#endif
|
||||
|
||||
bootloader_spi_flash_resume();
|
||||
if ((void*)bootloader_flash_unlock != (void*)bootloader_flash_unlock_default) {
|
||||
ESP_EARLY_LOGD(TAG, "Using overridden bootloader_flash_unlock");
|
||||
}
|
||||
bootloader_flash_unlock();
|
||||
|
||||
#if CONFIG_ESPTOOLPY_FLASHMODE_QIO || CONFIG_ESPTOOLPY_FLASHMODE_QOUT
|
||||
|
|
|
@ -262,6 +262,10 @@ esp_err_t bootloader_init_spi_flash(void)
|
|||
}
|
||||
#endif
|
||||
|
||||
if ((void*)bootloader_flash_unlock != (void*)bootloader_flash_unlock_default) {
|
||||
ESP_EARLY_LOGD(TAG, "Using overridden bootloader_flash_unlock");
|
||||
}
|
||||
|
||||
bootloader_flash_unlock();
|
||||
|
||||
#if CONFIG_ESPTOOLPY_FLASHMODE_QIO || CONFIG_ESPTOOLPY_FLASHMODE_QOUT
|
||||
|
|
|
@ -286,6 +286,9 @@ esp_err_t bootloader_init_spi_flash(void)
|
|||
#endif
|
||||
|
||||
bootloader_spi_flash_resume();
|
||||
if ((void*)bootloader_flash_unlock != (void*)bootloader_flash_unlock_default) {
|
||||
ESP_EARLY_LOGD(TAG, "Using overridden bootloader_flash_unlock");
|
||||
}
|
||||
bootloader_flash_unlock();
|
||||
|
||||
#if CONFIG_ESPTOOLPY_FLASHMODE_QIO || CONFIG_ESPTOOLPY_FLASHMODE_QOUT
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
/*
|
||||
* SPDX-FileCopyrightText: 2015-2023 Espressif Systems (Shanghai) CO LTD
|
||||
* SPDX-FileCopyrightText: 2015-2024 Espressif Systems (Shanghai) CO LTD
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
@ -18,6 +18,7 @@
|
|||
#include "soc/efuse_periph.h"
|
||||
#include "soc/io_mux_reg.h"
|
||||
#include "esp_private/spi_flash_os.h"
|
||||
#include "bootloader_flash_override.h"
|
||||
|
||||
|
||||
static const char *TAG = "qio_mode";
|
||||
|
@ -34,7 +35,7 @@ static const char *TAG = "qio_mode";
|
|||
|
||||
Searching of this table stops when the first match is found.
|
||||
*/
|
||||
const bootloader_qio_info_t __attribute__((weak)) bootloader_flash_qe_support_list[] = {
|
||||
const DRAM_ATTR bootloader_qio_info_t __attribute__((weak)) bootloader_flash_qe_support_list_default[] = {
|
||||
/* 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 */
|
||||
|
@ -53,7 +54,9 @@ const bootloader_qio_info_t __attribute__((weak)) bootloader_flash_qe_support_li
|
|||
{ 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))
|
||||
const DRAM_ATTR bootloader_qio_info_t* bootloader_flash_qe_support_list __attribute__((weak)) = bootloader_flash_qe_support_list_default;
|
||||
|
||||
uint8_t DRAM_ATTR __attribute__((weak)) bootloader_flash_qe_list_count = (sizeof(bootloader_flash_qe_support_list_default) / 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,
|
||||
|
@ -82,7 +85,11 @@ void bootloader_enable_qio_mode(void)
|
|||
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++) {
|
||||
if ((intptr_t)bootloader_flash_qe_support_list != (intptr_t)bootloader_flash_qe_support_list_default) {
|
||||
ESP_EARLY_LOGD(TAG, "Using overridden bootloader_flash_qio, the list number is %d", bootloader_flash_qe_list_count);
|
||||
}
|
||||
|
||||
for (i = 0; i < bootloader_flash_qe_list_count - 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);
|
||||
|
@ -90,7 +97,7 @@ void bootloader_enable_qio_mode(void)
|
|||
}
|
||||
}
|
||||
|
||||
if (i == NUM_CHIPS - 1) {
|
||||
if (i == bootloader_flash_qe_list_count - 1) {
|
||||
ESP_LOGI(TAG, "Enabling default flash chip QIO");
|
||||
}
|
||||
enable_qio_mode(bootloader_flash_qe_support_list[i].read_status_fn,
|
||||
|
|
|
@ -20,21 +20,179 @@ Users should note the following when customizing chip drivers:
|
|||
Steps For Creating Custom Chip Drivers and Overriding the ESP-IDF Default Driver List
|
||||
-------------------------------------------------------------------------------------
|
||||
|
||||
.. highlight: cmake
|
||||
Bootloader Flash Driver
|
||||
^^^^^^^^^^^^^^^^^^^^^^^
|
||||
|
||||
1. Enable the :ref:`CONFIG_SPI_FLASH_OVERRIDE_CHIP_DRIVER_LIST` config option. This prevents compilation and linking of the Default Chip Driver List (``default_registered_chips``) provided by ESP-IDF. Instead, the linker searches for the structure of the same name (``default_registered_chips``) that must be provided by the user.
|
||||
2. Add a new component in your project, e.g., ``custom_chip_driver``.
|
||||
3. Copy the necessary chip driver files from the ``spi_flash`` component in ESP-IDF. This may include:
|
||||
To implement bootloader driver in order that the ESP chip can boot successfully, please read this part carefully. Wrong code will make ESP chip fail to boot or introduce random issues. And issues in bootloader is not fixable via OTA. Please fully comprehend below parts before making any changes.
|
||||
|
||||
- ``spi_flash_chip_drivers.c`` (to provide the ``default_registered_chips`` structure)
|
||||
- Any of the ``spi_flash_chip_*.c`` files that matches your own flash model best
|
||||
- ``CMakeLists.txt`` and ``linker.lf`` files
|
||||
Currently there are two parts to override in bootloader: `bootloader_flash_unlock` and `bootloader_flash_qe_support_list` (and its size). See below sections.
|
||||
|
||||
Modify the files above properly. Including:
|
||||
Ensure that all the commands should be sent out and recognized successfully. Otherwise if the flash receives an unknown command, it may ignore silently and continue working while the protection is not re-enabled. This might lead to mis-writing/erasing, mis-locked issues.
|
||||
|
||||
- Change the ``default_registered_chips`` variable to non-static and remove the #ifdef logic around it.
|
||||
- Update ``linker.lf`` file to rename the fragment header and the library name to match the new component.
|
||||
- If reusing other drivers, some header names need prefixing with ``spi_flash/`` when included from outside spi_flash component.
|
||||
All bootloader flash driver functions reside in a component for the bootloader, which should be placed under `bootloader_components`. For example, :example:`storage/custom_flash_driver/bootloader_components/`. Only the components under `bootloader_components` will be added into the component list of bootloader automatically.
|
||||
|
||||
There is slight difference in the `CMakeLists.txt` of the component. The customized flash functions take effect by overriding existing weak ones. In order to link them into the bootloader, linker argument `-u` should be used against the overriding functions in the `CMakeLists.txt`. See :example_file:`storage/custom_flash_driver/bootloader_components/bootloader_flash/CMakeLists.txt`.
|
||||
|
||||
Bootloader flash unlock
|
||||
~~~~~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
`bootloader_flash_unlock` function is used to unlock flash write protection, which means once the flash is locked by accident, IDF is able to unlock it. By default, the unlock function will clear all bits in Status Register and Configuration Register except QE bit.
|
||||
|
||||
Please don't touch QE bits, otherwise, your chip will not be able to run under QUAD modes.
|
||||
|
||||
Please check the default case below. If your flash have different behavior from that, some modifications are required.
|
||||
|
||||
You can start from copying `bootloader_flash_unlock_default` from `components/bootloader_support/bootloader_flash/src/bootloader_flash.c` into a file in your customized component folder before modifying it. The function should be renamed to `bootloader_flash_unlock` to override the IDF bootloader function. For example, :example_file:`storage/custom_flash_driver/bootloader_components/bootloader_flash/bootloader_flash_unlock_custom.c`.
|
||||
|
||||
After that, your implementation of `bootloader_flash_unlock` will be linked instead of IDF default one, which has been declared as a weak symbol. So when IDF 2nd bootloader boots, your implementation of `bootloader_flash_unlock` will be executed. If you want to make sure your function is truly linked, you can enable `CONFIG_BOOTLOADER_LOG_LEVEL_DEBUG` and you will see a ``Using overridden bootloader_flash_unlock`` log.
|
||||
|
||||
In the default function, we have provided 3 common behaviors, please check first:
|
||||
|
||||
- case 1 (**Default**): Please check whether QE bit in your flash chip is placed at bit 1 in status register-2 written by the second byte after command 01H (WRSR). If so, this matches the default behavior and you don't need to anything else.
|
||||
- case 2: Please check whether QE bit in your flash chip is placed at bit 1 in status register-2 to be written by command 31H. (And your flash doesn't support case 1, using 01H+2bytes to write it). If so , you need to add your chip ID in function :cpp:func:`is_qe_bit_in_bit1_at_reg2`, for example:
|
||||
|
||||
.. code-block:: c
|
||||
|
||||
IRAM_ATTR bool is_qe_bit_in_bit1_at_reg2(const esp_rom_spiflash_chip_t* chip)
|
||||
{
|
||||
bool ret = true;
|
||||
switch (chip->device_id) {
|
||||
/****GD series***/
|
||||
case 0xC84016:
|
||||
case 0xC84017:
|
||||
case 0xC84018:
|
||||
break;
|
||||
/**** your flash series ****/
|
||||
case /*your flash ID*/:
|
||||
break;
|
||||
default:
|
||||
ret = false;
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
- case 3: Please check whether QE bit in your flash chip is placed at bit 6 in status register-1 to be written by command 01H. If so, you need to add your chip ID in function :cpp:func:`is_qe_bit_in_bit6_at_reg1`, for example:
|
||||
|
||||
.. code-block:: c
|
||||
|
||||
IRAM_ATTR bool is_qe_bit_in_bit6_at_reg1(const esp_rom_spiflash_chip_t* chip)
|
||||
{
|
||||
bool ret = true;
|
||||
switch (chip->device_id) {
|
||||
/***ISSI series***/
|
||||
case 0x9D4016:
|
||||
case 0x9D4017:
|
||||
break;
|
||||
|
||||
/***MXIC series***/
|
||||
case 0xC22016:
|
||||
case 0xC22017:
|
||||
break;
|
||||
|
||||
/****your clash series***/
|
||||
case /*your flash ID*/:
|
||||
break;
|
||||
default:
|
||||
ret = false;
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
- case 4: If 3 cases mentioned above cannot cover your usage. Please add another `if` block and corresponding behavior function in function `bootloader_flash_unlock`. The determination function in the `if` block is suggested to be named after `is_qe_bit_in_bit_x_at_reg_x_` (x stands for behavior). Refer to example :example_file:`storage/custom_flash_driver/bootloader_components/bootloader_flash/bootloader_flash_unlock_custom.c`.
|
||||
|
||||
Bootloader flash quad mode support
|
||||
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
Pointer `bootloader_flash_qe_support_list` is used for iteration in bootloader for selecting the correct behavior to enable flash chip work under QUAD mode. In order to make flash works under QUAD mode, enable QE bit in flash status register is necessary. If you want to use your flash chip under QUAD mode, please read this part and make implementation for yourself.
|
||||
|
||||
* case 1: If QE bit is placed at bit1 in status register-2 to be written by command 31H, nothing needs to be done because this is the default behavior.
|
||||
* case 2: If QE bit on your chip is placed at different places, or need to use different command. Please add your own support.
|
||||
|
||||
To add your own support, you can start from copying the `bootloader_flash_qe_support_list_user` function from `flash_qio_mode.c <https://github.com/espressif/esp-idf/blob/master/components/bootloader_support/bootloader_flash/src/flash_qio_mode.c>`_ , into your file, renaming to `bootloader_flash_qe_support_list`. Please also define a corresponding `bootloader_flash_qe_list_count`.
|
||||
|
||||
Add your flash into it, with the chip's name, ID, and the functions to write registers etc. into `bootloader_flash_qio_support_list`. You can also reuse the existing functions like `bootloader_read_status_8b_rdsr`.
|
||||
|
||||
If functions there cannot fully cover your usage, you can define your own function with `bootloader_execute_flash_command`, like `bootloader_read_status_otp_mode_8b`, and `bootloader_write_status_otp_mode_8b`. For example, `bootloader_flash_custom.c <https://github.com/espressif/esp-flash-drivers/tree/main/esp_flash_nor/bootloader_flash_driver/bootloader_flash_custom.c>`_ .
|
||||
|
||||
Put everything together:
|
||||
|
||||
.. code-block:: c
|
||||
|
||||
const DRAM_ATTR bootloader_qio_info_t bootloader_flash_qe_support_list_user[] = {
|
||||
/* 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 },
|
||||
{ "WinBond", 0xEF, 0x4000, 0xFF00, bootloader_read_status_16b_rdsr_rdsr2, bootloader_write_status_16b_wrsr, 9 },
|
||||
{ "GD", 0xC8, 0x4000, 0xFFFF, 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 },
|
||||
{ "EON", 0x1C, 0x7000, 0xFF00, bootloader_read_status_otp_mode_8b, bootloader_write_status_otp_mode_8b, 6 },
|
||||
|
||||
/* 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 },
|
||||
};
|
||||
const DRAM_ATTR bootloader_qio_info_t* bootloader_flash_qe_support_list = bootloader_flash_qe_support_list_user;
|
||||
uint8_t DRAM_ATTR bootloader_flash_qe_list_count = (sizeof(bootloader_flash_qe_support_list_user) / sizeof(bootloader_qio_info_t));
|
||||
|
||||
App flash driver
|
||||
^^^^^^^^^^^^^^^^
|
||||
|
||||
Generic Flash driver
|
||||
~~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
The flash driver in the application is used to read, write, erase, etc. to save data. And some advanced features like the OTA. Below is the guide on how to customize the driver for your own flash model.
|
||||
|
||||
- STEP 1: The last item of `default_registered_chips` should be the `generic chip driver <https://github.com/espressif/esp-idf/blob/master/components/spi_flash/spi_flash_chip_generic.c>`_ . When your flash chip cannot match any one chip driver above, it will use the generic driver. Please check what behavior of your flash is different from the generic driver, including but not limited to different commands, different dummys, different data bytes, different status registers.
|
||||
- STEP 2: If you have found something different from the generic driver, you need to implement your own chip driver. Create a new file naming `spi_flash_chip_<vendor>.c` implementing specific behavior there, and copy the `esp_flash_chip_generic` structure into it as a start. Don't forget to include "spi_flash_chip_generic.h". Here is an example `esp_flash_nor <https://github.com/espressif/esp-flash-drivers/tree/main/esp_flash_nor/>`_ .
|
||||
- STEP 3: Implement the functions with difference and point to them from the `spi_flash_chip_t`. Note: if some behavior of your flash is same as the generic one, just keep the function of generic driver and no need to customize them. Only the different part should be implemented. Here is an example:
|
||||
|
||||
.. important::
|
||||
|
||||
Flash work for suspend (means you want to enable :ref:`CONFIG_SPI_FLASH_AUTO_SUSPEND`) should be tested carefully and systematically due to different flash hardware design. If you want to use suspend feature for massive production, please contact espressif business support team.
|
||||
|
||||
.. code-block:: c
|
||||
|
||||
const DRAM_ATTR spi_flash_chip_t esp_flash_chip_eon = {
|
||||
.name = chip_name,
|
||||
.timeout = &spi_flash_chip_generic_timeout, /*<! default behavior*/
|
||||
.probe = spi_flash_chip_eon_probe, /*<! EON specific */
|
||||
.reset = spi_flash_chip_generic_reset,
|
||||
.detect_size = spi_flash_chip_generic_detect_size,
|
||||
.erase_chip = spi_flash_chip_generic_erase_chip,
|
||||
.erase_sector = spi_flash_chip_generic_erase_sector,
|
||||
.erase_block = spi_flash_chip_generic_erase_block,
|
||||
.sector_size = 4 * 1024,
|
||||
.block_erase_size = 64 * 1024,
|
||||
|
||||
.get_chip_write_protect = spi_flash_chip_generic_get_write_protect,
|
||||
.set_chip_write_protect = spi_flash_chip_generic_set_write_protect,
|
||||
|
||||
.num_protectable_regions = 0,
|
||||
.protectable_regions = NULL,
|
||||
.get_protected_regions = NULL,
|
||||
.set_protected_regions = NULL,
|
||||
|
||||
.read = spi_flash_chip_generic_read,
|
||||
.write = spi_flash_chip_generic_write,
|
||||
.program_page = spi_flash_chip_generic_page_program,
|
||||
.page_size = 256,
|
||||
.write_encrypted = spi_flash_chip_generic_write_encrypted,
|
||||
|
||||
.wait_idle = spi_flash_chip_generic_wait_idle,
|
||||
.set_io_mode = spi_flash_chip_eon_set_io_mode,
|
||||
.get_io_mode = spi_flash_chip_eon_get_io_mode,
|
||||
|
||||
.read_reg = spi_flash_chip_generic_read_reg,
|
||||
.yield = spi_flash_chip_generic_yield,
|
||||
.sus_setup = spi_flash_chip_eon_suspend_cmd_conf,
|
||||
.get_chip_caps = spi_flash_chip_eon_get_caps,
|
||||
};
|
||||
|
||||
.. note::
|
||||
|
||||
|
@ -50,7 +208,6 @@ Steps For Creating Custom Chip Drivers and Overriding the ESP-IDF Default Driver
|
|||
{
|
||||
spi_flash_caps_t caps_flags = 0;
|
||||
// 32-bit-address flash is not supported
|
||||
flash-suspend is supported
|
||||
caps_flags |= SPI_FLAHS_CHIP_CAP_SUSPEND;
|
||||
// flash read unique id.
|
||||
caps_flags |= SPI_FLASH_CHIP_CAP_UNIQUE_ID;
|
||||
|
@ -66,17 +223,26 @@ Steps For Creating Custom Chip Drivers and Overriding the ESP-IDF Default Driver
|
|||
|
||||
- You also can see how to implement this in the example :example:`storage/custom_flash_driver`. This example demonstrates how to override the default chip driver list.
|
||||
|
||||
4. Write a new ``CMakeLists.txt`` file for the ``custom_chip_driver`` component, including an additional line to add a linker dependency from ``spi_flash`` to ``custom_chip_driver``::
|
||||
- STEP 4: Add a header (e.g. `spi_flash_chip_<vendor>.h`) and `extern` declare this structure, so that other component or source code can reuse this structure. Wrap all your chip drivers (source files as well as their headers) with a chip-driver component. Add the include path and the source file to component CMakeLists.txt.
|
||||
|
||||
idf_component_register(SRCS "spi_flash_chip_drivers.c"
|
||||
"spi_flash_chip_mychip.c" # modify as needed
|
||||
REQUIRES hal
|
||||
PRIV_REQUIRES spi_flash
|
||||
LDFRAGMENTS linker.lf)
|
||||
idf_component_add_link_dependency(FROM spi_flash)
|
||||
- STEP 5: The ``linker.lf`` is used to put every chip driver that you are going to use whilst cache is disabled into internal RAM. See :doc:`/api-guides/linker-script-generation` for more details. Make sure this file covers all the source files that you add.
|
||||
|
||||
- An example of this component CMakeLists.txt can be found in :example_file:`storage/custom_flash_driver/components/custom_chip_driver/CMakeLists.txt`
|
||||
- STEP 6: Add a new component in your project, e.g., ``custom_chip_driver``. List your chip object under `default_registered_chips` in `custom_chip_driver/chip_drivers.c`. Then enable the :ref:`CONFIG_SPI_FLASH_OVERRIDE_CHIP_DRIVER_LIST` config option. This prevents compilation and linking of the Default Chip Driver List (``default_registered_chips``) provided by ESP-IDF. Instead, the linker searches for the structure of the same name (``default_registered_chips``) that must be provided by the user. You can refer to :example_file:`storage/custom_flash_driver/components/custom_chip_driver/chip_drivers.c`.
|
||||
|
||||
5. The ``linker.lf`` is used to put every chip driver that you are going to use whilst cache is disabled into internal RAM. See :doc:`/api-guides/linker-script-generation` for more details. Make sure this file covers all the source files that you add.
|
||||
- STEP 7: Build your project, and you will see the new flash driver is used.
|
||||
|
||||
6. Build your project, and you will see the new flash driver is used.
|
||||
.. only:: SOC_MEMSPI_SRC_FREQ_120M
|
||||
|
||||
High Performance Flash implementation
|
||||
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
The high performance mode means frequency at frequencies higher than 80 MHz. Please check the datasheet according to your flash and find which approach can reach to 120MHz, which would be listed in DC Characteristics section. Now, we already have some behavior in `high performance file <https://github.com/espressif/components/spi_flash/spi_flash_hpm_enable.c>`_ . If your flash meets the behavior there, just extend the list as what has been introduced in `bootloader_flash_unlock` sector. If your flash has different behavior, please add the new behavior and override the behavior table `spi_flash_hpm_enable_list`.
|
||||
|
||||
.. important::
|
||||
|
||||
Flash with frequencies set to 120 MHz or higher should be tested carefully due to it's strict timing. If you want to use high performance mode feature for massive production, please contact espressif business support team.
|
||||
|
||||
Example
|
||||
-------
|
||||
|
||||
See :example:`storage/custom_flash_driver`, which make use of an `external component <https://github.com/espressif/esp-flash-drivers/tree/main/esp_flash_nor>`_ to add support for some customized flash. This example mainly shows how to override IDF driver, and the external component shows how to implement customized drivers.
|
||||
|
|
|
@ -4,9 +4,6 @@ examples/storage/custom_flash_driver:
|
|||
depends_components:
|
||||
- spi_flash
|
||||
- driver
|
||||
disable_test:
|
||||
- if: IDF_TARGET not in ["esp32", "esp32c3"]
|
||||
reason: only one target per arch needed
|
||||
|
||||
examples/storage/emmc:
|
||||
depends_components:
|
||||
|
|
|
@ -5,4 +5,8 @@ cmake_minimum_required(VERSION 3.16)
|
|||
include($ENV{IDF_PATH}/tools/cmake/project.cmake)
|
||||
# "Trim" the build. Include the minimal set of components, main, and anything it depends on.
|
||||
idf_build_set_property(MINIMAL_BUILD ON)
|
||||
|
||||
idf_build_set_property(BOOTLOADER_EXTRA_COMPONENT_DIRS "${CMAKE_CURRENT_LIST_DIR}/managed_components/
|
||||
espressif__esp_flash_nor/" APPEND)
|
||||
|
||||
project(custom_flash_driver)
|
||||
|
|
|
@ -35,21 +35,39 @@ Below is short explanation of remaining files in the project folder.
|
|||
├── example_test.py Python script used for automated example testing
|
||||
├── main
|
||||
│ ├── CMakeLists.txt
|
||||
│ ├── component.mk Component make file
|
||||
│ └── main.c
|
||||
├── components/custom_chip_driver
|
||||
│ ├── CMakeLists.txt
|
||||
│ ├── component.mk Component make file
|
||||
│ ├── linker.lf Linker script to put the customized chip driver into internal RAM
|
||||
│ ├── project_include.cmake Global cmake file to add dependency to spi_flash
|
||||
│ ├── chip_drivers.c
|
||||
│ └── spi_flash_chip_eon.c
|
||||
├── Makefile Makefile used by legacy GNU Make
|
||||
│ ├── idf_component.yml Component manager for flash driver component
|
||||
├── bootloader_components/bootloader_flash
|
||||
│ ├── bootloader_flash_qio_custom.c
|
||||
| ├── bootloader_flash_unlock_custom.c
|
||||
│ ├── CMakeLists.txt
|
||||
│ ├── idf_component.yml Component manager for flash driver component
|
||||
└── README.md This is the file you are currently reading
|
||||
```
|
||||
|
||||
For more information on structure and contents of ESP-IDF projects, please refer to Section [Build System](https://docs.espressif.com/projects/esp-idf/en/latest/esp32/api-guides/build-system.html) of the ESP-IDF Programming Guide.
|
||||
|
||||
## How to implement your driver
|
||||
|
||||
Please read [instructions](https://docs.espressif.com/projects/esp-idf/en/latest/esp32/api-reference/peripherals/spi_flash/spi_flash_override_driver.html)
|
||||
|
||||
## Example output
|
||||
|
||||
The example output will be valid when you switch on `CONFIG_BOOTLOADER_LOG_LEVEL_DEBUG`, and see output in bootloader stage
|
||||
|
||||
```
|
||||
I (30) boot: ESP-IDF v5.4-dev-2463-g0d6cf47e5e-dirty 2nd stage bootloader
|
||||
...
|
||||
I (45) boot: chip revision: v0.3
|
||||
D (49) boot.esp32c3: Using overridden bootloader_flash_unlock
|
||||
...
|
||||
D (69) qio_mode: Using overridden bootloader_flash_qio
|
||||
```
|
||||
|
||||
## Troubleshooting
|
||||
|
||||
* Program upload failure
|
||||
|
|
|
@ -0,0 +1,10 @@
|
|||
idf_component_register(SRCS "bootloader_flash_qio_custom.c"
|
||||
"bootloader_flash_unlock_custom.c"
|
||||
REQUIRES bootloader_support spi_flash
|
||||
INCLUDE_DIRS ""
|
||||
WHOLE_ARCHIVE
|
||||
)
|
||||
|
||||
# The symbol should be linked to cover weak symbol. Please don't call the function directly.
|
||||
target_link_libraries(${COMPONENT_LIB} INTERFACE "-u bootloader_flash_unlock")
|
||||
target_link_libraries(${COMPONENT_LIB} INTERFACE "-u bootloader_flash_qe_support_list")
|
|
@ -0,0 +1,32 @@
|
|||
/*
|
||||
* SPDX-FileCopyrightText: 2024 Espressif Systems (Shanghai) CO LTD
|
||||
*
|
||||
* SPDX-License-Identifier: Unlicense OR CC0-1.0
|
||||
*/
|
||||
#include "bootloader_flash_override.h"
|
||||
#include "bootloader_flash_priv.h"
|
||||
#include "bootloader_flash_custom.h"
|
||||
#include "esp_rom_spiflash.h"
|
||||
#include "esp_attr.h"
|
||||
#include "esp_rom_sys.h"
|
||||
|
||||
|
||||
// All used flash chips that need special QIO operations need to be added here. The simplest way is to copy all entries from bootloader_..c from ESP-IDF and add your own at the beginning.
|
||||
const DRAM_ATTR bootloader_qio_info_t bootloader_flash_qe_support_list_user[] = {
|
||||
/* Manufacturer, mfg_id, flash_id, id mask, Read Status, Write Status, QIE Bit */
|
||||
{ "GD", 0xC8, 0x4000, 0xFFFF, bootloader_read_status_16b_rdsr_rdsr2, bootloader_write_status_16b_wrsr, 9 },
|
||||
{ "EON", 0x1C, 0x7000, 0xFF00, bootloader_read_status_otp_mode_8b, bootloader_write_status_otp_mode_8b, 6 },
|
||||
|
||||
/* 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 },
|
||||
};
|
||||
|
||||
const DRAM_ATTR bootloader_qio_info_t* bootloader_flash_qe_support_list = bootloader_flash_qe_support_list_user;
|
||||
|
||||
uint8_t DRAM_ATTR bootloader_flash_qe_list_count = (sizeof(bootloader_flash_qe_support_list_user) / sizeof(bootloader_qio_info_t));
|
|
@ -0,0 +1,154 @@
|
|||
/*
|
||||
* SPDX-FileCopyrightText: 2024 Espressif Systems (Shanghai) CO LTD
|
||||
*
|
||||
* SPDX-License-Identifier: Unlicense OR CC0-1.0
|
||||
*/
|
||||
#include "bootloader_flash_priv.h"
|
||||
#include "esp_rom_spiflash.h"
|
||||
#include "esp_rom_sys.h"
|
||||
#include "esp_attr.h"
|
||||
#include "esp_log.h"
|
||||
|
||||
/**
|
||||
Overview
|
||||
|
||||
This implementation is for adding unlock support to a specific SPI flash chip in the ESP32 series chip bootloader. Some flash chips might lock the sector in bootloader when its restart in a error state, thus will cause cannot write flash sector after esp chip boot up. So, an unlock function in bootloader is necessary. The goal of this function is to clear all bits except QE bit(which usually indicated the flash can work in quad mode). The main function of interest is bootloader_flash_unlock, which is responsible for ensuring that the QE bit is set correctly based on the flash chip type.
|
||||
|
||||
Step-by-Step Implementation
|
||||
|
||||
1. Include Necessary Headers:
|
||||
• bootloader_flash_priv.h: For private bootloader functions.
|
||||
• esp_rom_spiflash.h: For ROM SPI flash functions.
|
||||
• esp_rom_sys.h: For basic ROM system functions.
|
||||
• esp_attr.h and esp_log.h: For attributes and logging.
|
||||
... Add necessary header file according to your requirement.
|
||||
2. Define Macros for Flash IDs and QE Bits:
|
||||
• Use macros to define the manufacturer IDs and bit positions for the QE bit. This is important as different flash chips have different ways to set the QE bit.
|
||||
3. Identify QE Bit Position:
|
||||
• Write functions to identify the QE bit position based on the chip’s device ID. For example, is_qe_bit_in_bit6_at_reg1 checks if the QE bit is in bit 6 of status register 1, a common configuration for ISSI and MXIC chips. Same, `is_qe_bit_in_bit1_at_reg2` is for GD chips in following example.
|
||||
4. Implement bootloader_flash_unlock Function:
|
||||
• Step 1: Initialize registers and prepare to read the current status.
|
||||
• Step 2: Determine the correct QE bit position using the chip ID. Depending on the chip type, different commands and bit positions are used.
|
||||
• Step 3: Read the status registers and determine if the QE bit is already set. If not, it sets the bit.
|
||||
• Step 4: If changes are required, write the new status back to the flash chip.
|
||||
• Step 5: Ensure that write operations are completed successfully by waiting for the chip to be idle and disabling write operations.
|
||||
5. Test and Validate:
|
||||
• After implementing the custom support, you must test with your specific flash chip to ensure that the QE bit is set correctly and that the chip works as expected in Quad SPI mode.
|
||||
|
||||
Example Code Snippets:
|
||||
|
||||
*/
|
||||
|
||||
|
||||
#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_QE_SR1_BIT6 (BIT6)
|
||||
#define ESP_BOOTLOADER_SPIFLASH_QE_SR2_BIT1 (BIT1) // QE position when you write 8 bits(for SR2) at one time.
|
||||
#define ESP_BOOTLOADER_SPIFLASH_QE_SR1_2BYTE_BIT9 (BIT9) // QE position when you write 16 bits at one time.
|
||||
|
||||
FORCE_INLINE_ATTR bool is_qe_bit_in_bit6_at_reg1(const esp_rom_spiflash_chip_t* chip)
|
||||
{
|
||||
bool ret = true;
|
||||
switch (chip->device_id) {
|
||||
/***ISSI series***/
|
||||
case 0x9D4016:
|
||||
case 0x9D4017:
|
||||
break;
|
||||
|
||||
/***MXIC series***/
|
||||
case 0xC22016:
|
||||
case 0xC22017:
|
||||
break;
|
||||
default:
|
||||
ret = false;
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
FORCE_INLINE_ATTR bool is_qe_bit_in_bit1_at_reg2(const esp_rom_spiflash_chip_t* chip)
|
||||
{
|
||||
bool ret = true;
|
||||
switch (chip->device_id) {
|
||||
/****GD series***/
|
||||
case 0xC84016:
|
||||
case 0xC84017:
|
||||
case 0xC84018:
|
||||
break;
|
||||
default:
|
||||
ret = false;
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
esp_err_t IRAM_ATTR 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_qe_bit_in_bit6_at_reg1(&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_QE_SR1_BIT6;
|
||||
} else if (is_qe_bit_in_bit1_at_reg2(&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_SR2_BIT1;
|
||||
} 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_BIT9;
|
||||
}
|
||||
|
||||
// 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;
|
||||
}
|
|
@ -0,0 +1,3 @@
|
|||
## IDF Component Manager Manifest File
|
||||
dependencies:
|
||||
esp_flash_nor: ">=0.0.3"
|
|
@ -1,4 +1,4 @@
|
|||
idf_component_register(SRCS "chip_drivers.c" "spi_flash_chip_eon.c"
|
||||
idf_component_register(SRCS "chip_drivers.c"
|
||||
PRIV_REQUIRES spi_flash
|
||||
LDFRAGMENTS linker.lf
|
||||
INCLUDE_DIRS "")
|
||||
|
|
|
@ -1,3 +1,8 @@
|
|||
/*
|
||||
* SPDX-FileCopyrightText: 2024 Espressif Systems (Shanghai) CO LTD
|
||||
*
|
||||
* SPDX-License-Identifier: Unlicense OR CC0-1.0
|
||||
*/
|
||||
/* Custom flash driver example
|
||||
|
||||
This example code is in the Public Domain (or CC0 licensed, at your option.)
|
||||
|
@ -14,8 +19,9 @@
|
|||
#include "spi_flash_chip_gd.h"
|
||||
#include "spi_flash_chip_winbond.h"
|
||||
#include "spi_flash_chip_boya.h"
|
||||
|
||||
extern const spi_flash_chip_t esp_flash_chip_eon;
|
||||
// `spi_flash_chip_custom.h` is the header for the structure of customize flash driver,
|
||||
// in this example is `esp_flash_chip_eon`.
|
||||
#include "spi_flash_chip_custom.h"
|
||||
|
||||
//Override the default chip driver provided by the IDF, CONFIG_SPI_FLASH_OVERRIDE_CHIP_DRIVER_LIST should be set
|
||||
const spi_flash_chip_t *default_registered_chips[] = {
|
||||
|
|
|
@ -0,0 +1,3 @@
|
|||
## IDF Component Manager Manifest File
|
||||
dependencies:
|
||||
esp_flash_nor: ">=0.0.3"
|
|
@ -2,4 +2,3 @@
|
|||
archive: libcustom_chip_driver.a
|
||||
entries:
|
||||
chip_drivers (noflash)
|
||||
spi_flash_chip_eon (noflash)
|
||||
|
|
|
@ -1,156 +0,0 @@
|
|||
/* Custom chip driver example
|
||||
|
||||
This example code is in the Public Domain (or CC0 licensed, at your option.)
|
||||
|
||||
Unless required by applicable law or agreed to in writing, this
|
||||
software is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR
|
||||
CONDITIONS OF ANY KIND, either express or implied.
|
||||
*/
|
||||
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <sys/param.h> // For MIN/MAX
|
||||
#include "esp_log.h"
|
||||
#include "spi_flash_chip_generic.h"
|
||||
#include "spi_flash/spi_flash_defs.h"
|
||||
|
||||
|
||||
// Not for all the vendors
|
||||
#define CMD_ENTER_OTP 0x3A
|
||||
|
||||
static const char chip_name[] = "eon";
|
||||
|
||||
/* Driver for EON flash chip */
|
||||
|
||||
esp_err_t spi_flash_chip_eon_probe(esp_flash_t *chip, uint32_t flash_id)
|
||||
{
|
||||
/* Check manufacturer and product IDs match our desired masks */
|
||||
const uint8_t MFG_ID = 0x1C;
|
||||
const uint16_t DEV_ID = 0x7000;
|
||||
if (flash_id >> 16 != MFG_ID || (flash_id & 0xFF00) != DEV_ID) {
|
||||
return ESP_ERR_NOT_FOUND;
|
||||
}
|
||||
return ESP_OK;
|
||||
}
|
||||
|
||||
static esp_err_t spi_flash_chip_eon_enter_otp_mode(esp_flash_t* chip)
|
||||
{
|
||||
spi_flash_trans_t trans = {
|
||||
.command = CMD_ENTER_OTP,
|
||||
};
|
||||
return chip->host->driver->common_command(chip->host, &trans);
|
||||
}
|
||||
|
||||
static esp_err_t spi_flash_chip_eon_exit_otp_mode(esp_flash_t* chip)
|
||||
{
|
||||
spi_flash_trans_t trans = {
|
||||
.command = CMD_WRDI,
|
||||
};
|
||||
return chip->host->driver->common_command(chip->host, &trans);
|
||||
}
|
||||
|
||||
esp_err_t spi_flash_chip_eon_get_io_mode(esp_flash_t *chip, esp_flash_io_mode_t* out_io_mode)
|
||||
{
|
||||
esp_err_t ret;
|
||||
ret = spi_flash_chip_eon_enter_otp_mode(chip);
|
||||
if (ret != ESP_OK) {
|
||||
return ret;
|
||||
}
|
||||
|
||||
// On "eon" chips, this involves checking
|
||||
// bit 1 (QE) of RDSR2 (35h) result
|
||||
// (it works this way on GigaDevice & Fudan Micro chips, probably others...)
|
||||
const uint8_t BIT_QE = 1 << 6;
|
||||
uint32_t sr;
|
||||
ret = spi_flash_common_read_status_8b_rdsr(chip, &sr);
|
||||
if (ret == ESP_OK) {
|
||||
*out_io_mode = ((sr & BIT_QE)? SPI_FLASH_QOUT: 0);
|
||||
}
|
||||
|
||||
//unconditionally exit OTP mode
|
||||
esp_err_t ret_exit = spi_flash_chip_eon_exit_otp_mode(chip);
|
||||
if (ret != ESP_OK) {
|
||||
return ret;
|
||||
}
|
||||
return ret_exit;
|
||||
}
|
||||
|
||||
esp_err_t spi_flash_chip_eon_set_io_mode(esp_flash_t *chip)
|
||||
{
|
||||
if (!esp_flash_is_quad_mode(chip)) {
|
||||
return ESP_OK;
|
||||
}
|
||||
|
||||
esp_err_t ret;
|
||||
ret = spi_flash_chip_eon_enter_otp_mode(chip);
|
||||
if (ret != ESP_OK) {
|
||||
return ret;
|
||||
}
|
||||
|
||||
// On "eon" chips, this involves checking
|
||||
// bit 6 (QE) of RDSR (05h) result
|
||||
const uint32_t BIT_QE = 1 << 6;
|
||||
ret = spi_flash_common_set_io_mode(chip,
|
||||
spi_flash_common_write_status_8b_wrsr,
|
||||
spi_flash_common_read_status_8b_rdsr,
|
||||
BIT_QE);
|
||||
|
||||
//unconditionally exit OTP mode
|
||||
esp_err_t ret_exit = spi_flash_chip_eon_exit_otp_mode(chip);
|
||||
if (ret != ESP_OK) {
|
||||
return ret;
|
||||
}
|
||||
return ret_exit;
|
||||
}
|
||||
|
||||
esp_err_t spi_flash_chip_eon_suspend_cmd_conf(esp_flash_t *chip)
|
||||
{
|
||||
return ESP_ERR_NOT_SUPPORTED;
|
||||
}
|
||||
|
||||
spi_flash_caps_t spi_flash_chip_eon_get_caps(esp_flash_t *chip)
|
||||
{
|
||||
spi_flash_caps_t caps_flags = 0;
|
||||
// 32-bit-address flash is not supported
|
||||
// flash-suspend is not supported
|
||||
// flash read unique id is not supported.
|
||||
return caps_flags;
|
||||
}
|
||||
|
||||
// The issi chip can use the functions for generic chips except from set read mode and probe,
|
||||
// So we only replace these two functions.
|
||||
const spi_flash_chip_t esp_flash_chip_eon = {
|
||||
.name = chip_name,
|
||||
.timeout = &spi_flash_chip_generic_timeout,
|
||||
.probe = spi_flash_chip_eon_probe,
|
||||
.reset = spi_flash_chip_generic_reset,
|
||||
.detect_size = spi_flash_chip_generic_detect_size,
|
||||
.erase_chip = spi_flash_chip_generic_erase_chip,
|
||||
.erase_sector = spi_flash_chip_generic_erase_sector,
|
||||
.erase_block = spi_flash_chip_generic_erase_block,
|
||||
.sector_size = 4 * 1024,
|
||||
.block_erase_size = 64 * 1024,
|
||||
|
||||
.get_chip_write_protect = spi_flash_chip_generic_get_write_protect,
|
||||
.set_chip_write_protect = spi_flash_chip_generic_set_write_protect,
|
||||
|
||||
.num_protectable_regions = 0,
|
||||
.protectable_regions = NULL,
|
||||
.get_protected_regions = NULL,
|
||||
.set_protected_regions = NULL,
|
||||
|
||||
.read = spi_flash_chip_generic_read,
|
||||
.write = spi_flash_chip_generic_write,
|
||||
.program_page = spi_flash_chip_generic_page_program,
|
||||
.page_size = 256,
|
||||
.write_encrypted = spi_flash_chip_generic_write_encrypted,
|
||||
|
||||
.wait_idle = spi_flash_chip_generic_wait_idle,
|
||||
.set_io_mode = spi_flash_chip_eon_set_io_mode,
|
||||
.get_io_mode = spi_flash_chip_eon_get_io_mode,
|
||||
|
||||
.read_reg = spi_flash_chip_generic_read_reg,
|
||||
.yield = spi_flash_chip_generic_yield,
|
||||
.sus_setup = spi_flash_chip_eon_suspend_cmd_conf,
|
||||
.get_chip_caps = spi_flash_chip_eon_get_caps,
|
||||
};
|
|
@ -1,3 +1,3 @@
|
|||
idf_component_register(SRCS "main.c"
|
||||
PRIV_REQUIRES custom_chip_driver
|
||||
PRIV_REQUIRES espressif__esp_flash_nor custom_chip_driver
|
||||
INCLUDE_DIRS "")
|
||||
|
|
|
@ -0,0 +1,18 @@
|
|||
# SPDX-FileCopyrightText: 2024 Espressif Systems (Shanghai) CO LTD
|
||||
# SPDX-License-Identifier: Unlicense OR CC0-1.0
|
||||
import pytest
|
||||
from pytest_embedded import Dut
|
||||
|
||||
|
||||
@pytest.mark.supported_targets
|
||||
@pytest.mark.generic
|
||||
@pytest.mark.parametrize(
|
||||
'config',
|
||||
[
|
||||
'default',
|
||||
],
|
||||
indirect=True,
|
||||
)
|
||||
def test_examples_custom_flash_driver(dut: Dut) -> None:
|
||||
dut.expect(r'Using overridden bootloader_flash_unlock', timeout=20)
|
||||
dut.expect(r'Using overridden bootloader_flash_qio, the list number is \d+', timeout=20)
|
|
@ -0,0 +1,5 @@
|
|||
CONFIG_BOOTLOADER_LOG_LEVEL_DEBUG=y
|
||||
CONFIG_BOOTLOADER_LOG_LEVEL=4
|
||||
CONFIG_ESPTOOLPY_FLASHMODE_QIO=y
|
||||
# Bootloader overlapped with partition table after we enabled LOGD for bootloader. Move the partition table a bit to fix the issue on CI.
|
||||
CONFIG_PARTITION_TABLE_OFFSET=0x9000
|
|
@ -910,8 +910,6 @@ examples/protocols/sockets/udp_server/main/udp_server.c
|
|||
examples/protocols/static_ip/main/static_ip_example_main.c
|
||||
examples/provisioning/wifi_prov_mgr/main/app_main.c
|
||||
examples/security/flash_encryption/main/flash_encrypt_main.c
|
||||
examples/storage/custom_flash_driver/components/custom_chip_driver/chip_drivers.c
|
||||
examples/storage/custom_flash_driver/components/custom_chip_driver/spi_flash_chip_eon.c
|
||||
examples/storage/custom_flash_driver/main/main.c
|
||||
examples/storage/nvs_rw_blob/main/nvs_blob_example_main.c
|
||||
examples/storage/nvs_rw_value/main/nvs_value_example_main.c
|
||||
|
|
Ładowanie…
Reference in New Issue