Merge branch 'feature/support_eon_flash_qaud_mode_esp_flash' into 'master'

esp_flash: support override default chip driver list

Closes IDF-2907

See merge request espressif/esp-idf!12565
This commit is contained in:
Michael (XIAO Xufeng) 2021-04-29 04:03:56 +00:00
commit 859f7e3664
21 changed files with 403 additions and 2 deletions

View File

@ -102,7 +102,7 @@ else()
REQUIRES hal
PRIV_REQUIRES "${priv_requires}"
INCLUDE_DIRS include
PRIV_INCLUDE_DIRS private_include
PRIV_INCLUDE_DIRS include/spi_flash
LDFRAGMENTS linker.lf)
# Avoid cache miss by unexpected inlineing when built by -Os

View File

@ -186,8 +186,21 @@ menu "SPI Flash driver"
help
This option is helpful if you are using a flash chip whose timeout is quite large or unpredictable.
config SPI_FLASH_OVERRIDE_CHIP_DRIVER_LIST
bool "Override default chip driver list"
depends on !SPI_FLASH_USE_LEGACY_IMPL
default n
help
This option allows the chip driver list to be customized, instead of using the default list provided by
ESP-IDF.
When this option is enabled, the default list is no longer compiled or linked. Instead, the
`default_registered_chips` structure must be provided by the user.
See example: custom_chip_driver under examples/storage for more details.
menu "Auto-detect flash chips"
visible if !SPI_FLASH_OVERRIDE_CHIP_DRIVER_LIST
config SPI_FLASH_SUPPORT_ISSI_CHIP
bool "ISSI"

View File

@ -1,7 +1,7 @@
COMPONENT_ADD_INCLUDEDIRS := include
COMPONENT_SRCDIRS := . $(IDF_TARGET)
COMPONENT_PRIV_INCLUDEDIRS := private_include
COMPONENT_PRIV_INCLUDEDIRS := include/spi_flash
COMPONENT_ADD_LDFRAGMENTS += linker.lf

View File

@ -22,6 +22,7 @@
#include "spi_flash_chip_boya.h"
#include "sdkconfig.h"
#if !CONFIG_SPI_FLASH_OVERRIDE_CHIP_DRIVER_LIST
/*
* Default registered chip drivers. Note these are tested in order and first
* match is taken, so generic/catchall entries should go last. Note that the
@ -52,5 +53,9 @@ static const spi_flash_chip_t *default_registered_chips[] = {
&esp_flash_chip_generic,
NULL,
};
#else
//When the config option is enabled, user should provide this struct themselves.
extern const spi_flash_chip_t *default_registered_chips[];
#endif //!CONFIG_SPI_FLASH_OVERRIDE_CHIP_DRIVER_LIST
const spi_flash_chip_t **esp_flash_registered_chips = default_registered_chips;

View File

@ -43,6 +43,13 @@ The 32-bit address range of following chip type is supported:
1. W25Q256
Users can also customize their own flash chip driver, see :doc:`spi_flash_override_driver` for more details.
.. toctree::
:hidden:
Custom Flash Driver <spi_flash_override_driver>
Initializing a flash device
---------------------------

View File

@ -0,0 +1,42 @@
Overriding Default Chip Drivers
===============================
.. warning::
Customizing SPI Flash Chip Drivers is considered an "expert" feature. Users should only do so at their own risk. (See the notes below)
During the SPI Flash driver's initialization (i.e., :cpp:func:`esp_flash_init`), there is a chip detection step during which the driver will iterate through a Default Chip Driver List and determine which chip driver can properly support the currently connected flash chip. The Default Chip Drivers are provided by the IDF, thus are updated in together with each IDF version. However IDF also allows users to customize their own chip drivers.
Users should note the following when customizing chip drivers:
1. You may need to rely on some non-public IDF functions, which have slight possibility to change between IDF versions. On the one hand, these changes may be useful bug fixes for your driver, on the other hand, they may also be breaking changes (i.e., breaks your code).
2. Some IDF bug fixes to other chip drivers will not be automatically applied to your own custom chip drivers.
3. If the protection of flash is not handled properly, there may be some random reliability issues.
4. If you update to a newer IDF version that has support for more chips, you will have to manually add those new chip drivers into your custom chip driver list. Otherwise the driver will only search for the drivers in custom list you provided.
Steps For Creating Custom Chip Drivers and Overriding the IDF Default Driver List
---------------------------------------------------------------------------------
1. Enable the :ref:`CONFIG_SPI_FLASH_OVERRIDE_CHIP_DRIVER_LIST` config option. This will prevent compilation and linking of the Default Chip Driver List (`default_registered_chips`) provided by IDF. Instead, the linker will search 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 IDF. This may include:
- `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
Modify the files above properly.
4. Add linking dependency from `spi_flash` component to the new `custom_chip_driver` component, by adding the following lines after the `idf_component_register`, in the `CMakeLists.txt` file of the `custom_chip_driver` component:
idf_component_get_property(spi_flash_lib spi_flash COMPONENT_LIB)
set_property(TARGET ${spi_flash_lib} APPEND PROPERTY INTERFACE_LINK_LIBRARIES $<LINK_ONLY:${COMPONENT_LIB}>)
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.
6. Build your project, and you will see the new flash driver is used.
Example
-------
See also :example:`storage/custom_flash_driver`.

View File

@ -23,6 +23,32 @@ Kconfig 选项 :ref:`CONFIG_SPI_FLASH_USE_LEGACY_IMPL` 可将 ``spi_flash_*``
即便未启用 :ref:`CONFIG_SPI_FLASH_USE_LEGACY_IMPL`,加密读取和加密写入操作也均使用旧实现。因此,仅有主 flash 芯片支持加密操作,外接(经 SPI1 使用其他不同片选访问,或经其它 SPI 总线访问)的 flash 芯片则不支持加密操作。也仅有主 flash 支持从 cache 当中读取,因为这是由硬件决定的。
Flash 特性支持情况
-----------------------------------
不同厂家的 Flash 特性通过不同的方式来操作,因此需要特殊的驱动支持。当前驱动支持大多数厂家 Flash 24 位地址范围内的快速/慢速读,以及二线模式( DIO / DOUT ),因为他们不需要任何厂家自定义的命令。
当前驱动支持以下厂家/型号的 Flash 的四线模式( QIO / QOUT
1. ISSI
2. GD
3. MXIC
4. FM
5. Winbond
6. XMC
7. BOYA
当前驱动支持以下厂家/型号的 Flash 的 32 位地址范围的访问:
1. W25Q256
如果有需要,也可以自定义 Flash 芯片驱动,参见 :doc:`spi_flash_override_driver` 。但此功能仅供专业用户使用。
.. toctree::
:hidden:
自定义 Flash 芯片驱动 <spi_flash_override_driver>
初始化 Flash 设备
---------------------------

View File

@ -0,0 +1 @@
.. include:: ../../../en/api-reference/storage/spi_flash_override_driver.rst

View File

@ -0,0 +1,6 @@
# The following lines of boilerplate have to be in your project's
# CMakeLists in this exact order for cmake to work correctly
cmake_minimum_required(VERSION 3.5)
include($ENV{IDF_PATH}/tools/cmake/project.cmake)
project(custom-flash-driver)

View File

@ -0,0 +1,8 @@
#
# This is a project Makefile. It is assumed the directory this Makefile resides in is a
# project subdirectory.
#
PROJECT_NAME := custom-flash-driver
include $(IDF_PATH)/make/project.mk

View File

@ -0,0 +1,64 @@
# Custom Flash Driver Example
This example shows how to override the default chip driver list provided by IDF. Please make sure the SPI_FLASH_OVERRIDE_CHIP_DRIVER_LIST config option is enabled when you build this project (though it should already be enabled by default via `sdkconfig.defaults`). See [the programming guide](https://docs.espressif.com/projects/esp-idf/en/stable/api-reference/storage/spi_flash_override_driver.html) for more details regarding this feature.
**CAUTION: This is only an example showing how to extend your own flash chip driver. Espressif doesn't guarantee the chip driver in the example is reliable for mass production, nor the reliability of the flash models that appear in this example. Please refer to the specification of your flash chip, or contact the flash vendor if you have any problems with the flash chip.**
(See the README.md file in the upper level 'examples' directory for more information about examples.)
## How to use example
Follow detailed instructions provided specifically for this example.
Select the instructions depending on Espressif chip installed on your development board:
- [ESP32 Getting Started Guide](https://docs.espressif.com/projects/esp-idf/en/stable/get-started/index.html)
- [ESP32-S2 Getting Started Guide](https://docs.espressif.com/projects/esp-idf/en/latest/esp32s2/get-started/index.html)
## Example folder contents
The project **custom_flash_driver** contains one source file in C language [main.c](main/main.c). The file is located in folder [main](main).
The component **custom_chip_driver** placed under **components** folder, provides
ESP-IDF projects are built using CMake. The project build configuration is contained in `CMakeLists.txt` files that provide set of directives and instructions describing the project's source files and targets (executable, library, or both).
Below is short explanation of remaining files in the project folder.
```
├── CMakeLists.txt
├── sdkconfig.defaults Default options to add into sdkconfig file (mainly to enable the SPI_FLASH_OVERRIDE_CHIP_DRIVER_LIST option)
├── 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
└── 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.
## Troubleshooting
* Program upload failure
* Hardware connection is not correct: run `idf.py -p PORT monitor`, and reboot your board to see if there are any output logs.
* The baud rate for downloading is too high: lower your baud rate in the `menuconfig` menu, and try again.
## Technical support and feedback
Please use the following feedback channels:
* For technical queries, go to the [esp32.com](https://esp32.com/) forum
* For a feature request or bug report, create a [GitHub issue](https://github.com/espressif/esp-idf/issues)
We will get back to you as soon as possible.

View File

@ -0,0 +1,7 @@
idf_component_register(SRCS "chip_drivers.c" "spi_flash_chip_eon.c"
PRIV_REQUIRES spi_flash
LDFRAGMENTS linker.lf
INCLUDE_DIRS "")
idf_component_get_property(spi_flash_lib spi_flash COMPONENT_LIB)
set_property(TARGET ${spi_flash_lib} APPEND PROPERTY INTERFACE_LINK_LIBRARIES $<LINK_ONLY:${COMPONENT_LIB}>)

View File

@ -0,0 +1,32 @@
/* Custom flash 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 "spi_flash_chip_driver.h"
#include "spi_flash_chip_generic.h"
#include "spi_flash_chip_issi.h"
#include "spi_flash_chip_mxic.h"
#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;
//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[] = {
&esp_flash_chip_eon,
&esp_flash_chip_issi,
&esp_flash_chip_gd,
&esp_flash_chip_mxic,
&esp_flash_chip_winbond,
&esp_flash_chip_boya,
// Default chip drivers that will accept all chip ID.
// FM and XMC chips are supposed to be supported by this chip driver.
&esp_flash_chip_generic,
NULL,
};

View File

@ -0,0 +1,4 @@
#
# "main" pseudo-component makefile.
#
# (Uses default behaviour of compiling all source files in directory, adding 'include' to include path.)

View File

@ -0,0 +1,5 @@
[mapping:custom_chip_driver]
archive: libcustom_chip_driver.a
entries:
chip_drivers (noflash)
spi_flash_chip_eon (noflash)

View File

@ -0,0 +1,146 @@
/* 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;
}
// 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,
};

View File

@ -0,0 +1,3 @@
idf_component_register(SRCS "main.c"
PRIV_REQUIRES custom_chip_driver
INCLUDE_DIRS "")

View File

@ -0,0 +1,4 @@
#
# "main" pseudo-component makefile.
#
# (Uses default behaviour of compiling all source files in directory, adding 'include' to include path.)

View File

@ -0,0 +1,26 @@
/* Hello World 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 <stdio.h>
#include "sdkconfig.h"
#include "freertos/FreeRTOS.h"
#include "freertos/task.h"
// Extra check to ensure our custom chip driver file is correctly linked
#if !CONFIG_SPI_FLASH_OVERRIDE_CHIP_DRIVER_LIST
#error Custom flash chip driver not used!
#endif
void app_main(void)
{
printf("Hello world!\n");
while(1) {
vTaskDelay(1);
}
}

View File

@ -0,0 +1,2 @@
CONFIG_SPI_FLASH_USE_LEGACY_IMPL=n
CONFIG_SPI_FLASH_OVERRIDE_CHIP_DRIVER_LIST=y