Skip to content

Commit

Permalink
feat(spi_flash): Add new customize flash driver framework
Browse files Browse the repository at this point in the history
  • Loading branch information
mythbuster5 committed Aug 27, 2024
1 parent 8b6657b commit 1a1fda1
Show file tree
Hide file tree
Showing 9 changed files with 298 additions and 7 deletions.
32 changes: 27 additions & 5 deletions .github/workflows/upload_component.yml
Original file line number Diff line number Diff line change
@@ -1,6 +1,12 @@
name: Push components to IDF Component Registry
name: Push components to Espressif Component Service

# Copy from https://github.com/espressif/idf-extra-components/blob/master/.github/workflows/upload_component.yml

on:
# For pull requests: perform upload with "--dry-run" argument,
# i.e. validate that the component passes all checks for being uploaded.
pull_request:

push:
branches:
- main
Expand All @@ -9,11 +15,27 @@ jobs:
upload_components:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v2

- name: Upload components to IDF Component Registry
- uses: actions/checkout@v3
with:
submodules: 'recursive'
- run: |
echo "${{ ( github.ref_name != 'main' || github.repository_owner != 'espressif' ) && 'Checking' || 'Uploading' }} components"
- name: Upload components to component service
uses: espressif/upload-components-ci-action@v1
with:
directories: esp_flash_nor;
# Please try to keep the directories list sorted.
#
# Do note, however, that if you are updating two components in the same PR
# and one depends on the other, the new version of the 2nd component won't
# be found in the registry when the 1st component is uploaded.
#
# This is only a problem if you are adding two components for the first time,
# or if the 2nd component depends on the exact (new) version of the first one.
#
directories: >
esp_flash_nor;
namespace: "espressif"
# API token will only be available in the main branch in the main repository.
# However, dry-run doesn't require a valid token.
api_token: ${{ secrets.IDF_COMPONENT_API_TOKEN }}
dry_run: ${{ github.ref_name != 'main' || github.repository_owner != 'espressif' }}
16 changes: 16 additions & 0 deletions esp_flash_nor/CMakeLists.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
if(BOOTLOADER_BUILD)
idf_component_register(SRCS "bootloader_flash_driver/bootloader_flash_custom.c"
PRIV_REQUIRES bootloader_support spi_flash
INCLUDE_DIRS bootloader_flash_driver/include
INCLUDE_DIRS ""
LDFRAGMENTS linker.lf)
else()
idf_component_register(SRCS
"app_flash_driver/esp_flash_eon/spi_flash_chip_eon.c"
INCLUDE_DIRS app_flash_driver/include
PRIV_REQUIRES spi_flash bootloader_support esp_common
INCLUDE_DIRS ""
LDFRAGMENTS linker.lf)

idf_component_add_link_dependency(FROM spi_flash)
endif()
16 changes: 15 additions & 1 deletion esp_flash_nor/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,10 +6,24 @@ Please be aware of:

- Espressif will do basic reviewing (format, simple logic check, etc.) for the PRs
- Contributors should do functionality tests on actual flash chips
- Users should do all neessary tests, including integrated tests, stress tests, stability tests themselves. Users should be responsible for:
- Users should do all necessary tests, including integrated tests, stress tests, stability tests themselves. Users should be responsible for:
- Verify 3rd party flash drivers.
- Verify related flash models (including their stability, continuous supply) before selecting them into the supply chain. Note supply voltage and temperature may affect a lot to the performance of the flash chips.

# Usage example

There is an [simple example](https://github.com/espressif/esp-idf/tree/master/examples/storage/custom_flash_driver) about how to use this component. You can also follow [this instruction](https://github.com/espressif/esp-idf/blob/master/examples/storage/custom_flash_driver/README.md) for how to implement a customized driver for your flash with this component.

Contributions for more customized flash driver, or bug fixes are appreciated.

# Note

1. All flash related code/data should be put into RAM, so please add `IRAM_ATTR` for function and `DRAM_ATTR` for data.

# Error handling

1. **Cache disabled but cached memory region accessed** : Please check whether all function and data have been put into RAM memory.

# Contributions

See https://github.com/espressif/esp-flash-drivers/blob/main/README.md on how to make contributions!
Expand Down
159 changes: 159 additions & 0 deletions esp_flash_nor/app_flash_driver/esp_flash_eon/spi_flash_chip_eon.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,159 @@
/* 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"
#include "esp_attr.h"


// Not for all the vendors
#define CMD_ENTER_OTP 0x3A

static const char chip_name[] = "eon";

/* Driver for EON flash chip */

IRAM_ATTR 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,
you can search in datasheet with 9FH to get manufacturer and
device identification*/
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 IRAM_ATTR 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 IRAM_ATTR 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);
}

IRAM_ATTR 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;
}

IRAM_ATTR 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;
}

IRAM_ATTR esp_err_t spi_flash_chip_eon_suspend_cmd_conf(esp_flash_t *chip)
{
return ESP_ERR_NOT_SUPPORTED;
}

IRAM_ATTR 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 DRAM_ATTR 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,
};
20 changes: 20 additions & 0 deletions esp_flash_nor/app_flash_driver/include/spi_flash_chip_custom.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
#pragma once

#include <stdint.h>
#include "esp_flash.h"
#include "spi_flash_chip_driver.h"

#ifdef __cplusplus
extern "C" {
#endif

/**
* EON SPI flash chip_drv, uses all the above functions for its operations. In
* default auto detection, this is used as a catchall if a more specific chip_drv
* is not found.
*/
extern const spi_flash_chip_t esp_flash_chip_eon;

#ifdef __cplusplus
}
#endif
35 changes: 35 additions & 0 deletions esp_flash_nor/bootloader_flash_driver/bootloader_flash_custom.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
#include "bootloader_flash_override.h"
#include "bootloader_flash_priv.h"
#include "esp_rom_spiflash.h"
#include "esp_attr.h"

/* 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.
*/

IRAM_ATTR unsigned bootloader_read_status_otp_mode_8b(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;
}

IRAM_ATTR void bootloader_write_status_otp_mode_8b(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 */
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
#include <stdint.h>

#ifdef __cplusplus
extern "C" {
#endif

/**
* @brief read bootloader status register in eon rule
*/
unsigned bootloader_read_status_otp_mode_8b(void);

/**
* @brief write bootloader status register in eon rule
*
* @param new_status
*/
void bootloader_write_status_otp_mode_8b(unsigned new_status);

#ifdef __cplusplus
}
#endif
2 changes: 1 addition & 1 deletion esp_flash_nor/idf_component.yml
Original file line number Diff line number Diff line change
Expand Up @@ -3,4 +3,4 @@ description: "Component of 3rd party NOR flash drivers"
url: https://github.com/espressif/esp-flash-drivers/esp_flash_nor
dependencies:
idf:
version: ">=4.3"
version: ">=5.0"
4 changes: 4 additions & 0 deletions esp_flash_nor/linker.lf
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
[mapping:custom_flash_driver]
archive: libcustom_flash_driver.a
entries:
spi_flash_chip_eon (noflash)

0 comments on commit 1a1fda1

Please sign in to comment.