esp_flash: support override default chip driver list

This commit is contained in:
Michael (XIAO Xufeng) 2021-03-01 12:56:34 +08:00
parent 1d0e1409c7
commit f2a86c4205
22 changed files with 390 additions and 3 deletions

View File

@ -35,7 +35,7 @@ endif()
idf_component_register(SRCS "${srcs}" idf_component_register(SRCS "${srcs}"
PRIV_REQUIRES "${priv_requires}" PRIV_REQUIRES "${priv_requires}"
INCLUDE_DIRS include INCLUDE_DIRS include
PRIV_INCLUDE_DIRS private_include PRIV_INCLUDE_DIRS include/spi_flash
LDFRAGMENTS linker.lf) LDFRAGMENTS linker.lf)
# Avoid cache miss by unexpected inlineing when built by -Os # Avoid cache miss by unexpected inlineing when built by -Os

View File

@ -114,7 +114,18 @@ menu "SPI Flash driver"
help help
Defines how many ticks will be before returning to continue a erasing. Defines how many ticks will be before returning to continue a erasing.
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 customize the chip driver list, instead of using the default one provided by IDF.
When this option is enabled, the default list is no longer compiled or linked. `default_registered_chips` structure should be provided by the user.
See example: custom_chip_driver under examples/storage for more details.
menu "Auto-detect flash chips" menu "Auto-detect flash chips"
visible if !SPI_FLASH_OVERRIDE_CHIP_DRIVER_LIST
config SPI_FLASH_SUPPORT_ISSI_CHIP config SPI_FLASH_SUPPORT_ISSI_CHIP
bool "ISSI" bool "ISSI"

View File

@ -1,5 +1,5 @@
COMPONENT_ADD_INCLUDEDIRS := include COMPONENT_ADD_INCLUDEDIRS := include
COMPONENT_PRIV_INCLUDEDIRS := private_include COMPONENT_PRIV_INCLUDEDIRS := include/spi_flash
COMPONENT_ADD_LDFRAGMENTS += linker.lf COMPONENT_ADD_LDFRAGMENTS += linker.lf

View File

@ -19,6 +19,7 @@
#include "spi_flash_chip_gd.h" #include "spi_flash_chip_gd.h"
#include "sdkconfig.h" #include "sdkconfig.h"
#if !CONFIG_SPI_FLASH_OVERRIDE_CHIP_DRIVER_LIST
/* /*
* Default registered chip drivers. Note these are tested in order and first * 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 * match is taken, so generic/catchall entries should go last. Note that the
@ -40,5 +41,9 @@ static const spi_flash_chip_t *default_registered_chips[] = {
&esp_flash_chip_generic, &esp_flash_chip_generic,
NULL, 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; const spi_flash_chip_t **esp_flash_registered_chips = default_registered_chips;

View File

@ -1,4 +1,11 @@
.. include:: ../../../../components/spi_flash/README.rst .. include:: ../../../../components/spi_flash/README.rst
As an expert feature, you can also try to customize your own flash chip driver, see :doc:`spi_flash_override_driver`.
.. toctree::
:hidden:
Custom Flash Driver <spi_flash_override_driver>
See also See also
-------- --------

View File

@ -0,0 +1,37 @@
Override Default Chip Drivers
=============================
The flash driver has a chip detection step, during which the driver go through the default chip driver list and see which driver can properly fit the current flash chip. The default chip driver is provided by the IDF, and will update together with IDF version. However IDF also allows user customizing their own chip drivers.
.. note::
Customize the flash driver is an expert feature, please use it on your own risk:
1. You may have to rely on some non-public IDF functions, which have slightly possibility to change among versions. On one hand, these changes may be useful bug fixes for your driver, on the other hand, they may also break 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 issue.
Steps of Making Custom Chip Driver and Override the IDF Default Driver
----------------------------------------------------------------------
1. Enables the SPI_FLASH_OVERRIDE_CHIP_DRIVER_LIST config option. The default chip driver list (`default_registered_chips`) provided by IDF will no longer be compiled or linked. Instead, the linker will search for the structure, which is supposed to be provided by the user.
2. Add a new component in your project, e.g. 'custom_chip_driver'.
3. Add dependency from `spi_flash` component to the new `custom_chip_driver` component, by a CMake file `project_include.cmake` under the component folder, with the following code:
```
idf_build_set_property(___COMPONENT_REQUIRES_COMMON idf::custom_chip_driver APPEND)
```
4. Copy the necessary chip driver files from IDF spi_flash component. It may includes:
- 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 file
Modify the files above properly.
5. The linker.lf is used to put every you are going to use, when the cache is disabled, into internal RAM. Make sure this file covers all the source files 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

@ -1 +1,28 @@
.. include:: ../../../en/api-reference/storage/spi_flash.rst .. include:: ../../../en/api-reference/storage/spi_flash.rst
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>

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 the project (though it is supposed be default with the sdkconfig.defaults). See [programming guide](https://docs.espressif.com/projects/esp-idf/en/stable/api-reference/storage/spi_flash_override_driver.html) for more details of this feature.
CAUTION: this is only an example on 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 reliabilities of the flash models appear in this example. Please refer to the specification of the flash chips, or contact the flash vendors if you have any problems on the flash.
(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,4 @@
idf_component_register(SRCS "chip_drivers.c" "spi_flash_chip_eon.c"
PRIV_REQUIRES spi_flash
LDFRAGMENTS linker.lf
INCLUDE_DIRS "")

View File

@ -0,0 +1,27 @@
/* 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_gd.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,
// Default chip drivers that will accept all chip ID.
// FM, Winbond 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,3 @@
# Add custom dependency to the spi_flash component.
# This is a workaround, which will also add the dependency to all other components.
idf_build_set_property(___COMPONENT_REQUIRES_COMMON idf::custom_chip_driver APPEND)

View File

@ -0,0 +1,143 @@
/* 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"
#define REGION_32BIT(start, len) ((start) + (len) > (1<<24))
#define ADDR_32BIT(addr) (addr >= (1<<24))
// Not for all the vendors
#define CMD_ENTER_OTP 0x3A
static const char chip_name[] = "eon";
/* Driver for Winbond 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->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->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 RDSR (05h) 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 1 (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;
}
const spi_flash_chip_t esp_flash_chip_eon = {
.name = chip_name,
.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,
};

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