spi_flash: support working on differnt buses and frequency

This commit is contained in:
Michael (XIAO Xufeng) 2019-01-08 18:29:25 +08:00 committed by bot
parent ce4de867d6
commit 1036a091fe
52 changed files with 3974 additions and 1727 deletions

View file

@ -1584,6 +1584,12 @@ UT_001_43:
- ESP32_IDF
- UT_T1_1
UT_001_44:
<<: *unit_test_template
tags:
- ESP32_IDF
- UT_T1_1
UT_002_01:
<<: *unit_test_template
tags:

View file

@ -103,6 +103,17 @@ inline static __attribute__((deprecated)) esp_err_t esp_partition_table_basic_ve
{
return esp_partition_table_verify(partition_table, log_errors, num_partitions);
}
/**
* Check whether the region on the main flash is safe to write.
*
* @param addr Start address of the region
* @param size Size of the region
*
* @return true if the region is safe to write, otherwise false.
*/
bool esp_partition_main_flash_region_safe(size_t addr, size_t size);
#ifdef __cplusplus
}
#endif

View file

@ -43,7 +43,7 @@
#include "sdkconfig.h"
#include "esp_system.h"
#include "esp_spi_flash.h"
#include "spi_flash_lowlevel.h"
#include "esp_flash.h"
#include "nvs_flash.h"
#include "esp_event.h"
#include "esp_spi_flash.h"
@ -389,9 +389,10 @@ void start_cpu0_default(void)
spi_flash_init();
/* init default OS-aware flash access critical section */
spi_flash_guard_set(&g_flash_guard_default_ops);
/* Todo the following needs to be properly integrated */
esp_flash_low_level_app_init();
esp_flash_init_default_chip();
esp_flash_app_init();
esp_err_t flash_ret = esp_flash_init_default_chip();
assert(flash_ret == ESP_OK);
#ifdef CONFIG_PM_ENABLE
esp_pm_impl_init();
#ifdef CONFIG_PM_DFS_INIT_AUTO
@ -403,6 +404,7 @@ void start_cpu0_default(void)
esp_pm_configure(&cfg);
#endif //CONFIG_PM_DFS_INIT_AUTO
#endif //CONFIG_PM_ENABLE
#if CONFIG_ESP32_ENABLE_COREDUMP
esp_core_dump_init();
size_t core_data_sz = 0;

View file

@ -41,6 +41,7 @@ typedef int32_t esp_err_t;
#define ESP_ERR_WIFI_BASE 0x3000 /*!< Starting number of WiFi error codes */
#define ESP_ERR_MESH_BASE 0x4000 /*!< Starting number of MESH error codes */
#define ESP_ERR_FLASH_BASE 0x6000 /*!< Starting number of flash error codes */
/**
* @brief Returns string for esp_err_t error codes

View file

@ -4,3 +4,5 @@
#define CONFIG_LOG_DEFAULT_LEVEL 3
#define CONFIG_PARTITION_TABLE_OFFSET 0x8000
#define CONFIG_ESPTOOLPY_FLASHSIZE "8MB"
//currently use the legacy implementation, since the stubs for new HAL are not done yet
#define CONFIG_SPI_FLASH_USE_LEGACY_IMPL

View file

@ -16,6 +16,8 @@ list(APPEND COMPONENT_SRCS "src/memory_layout_utils.c"
"src/hal/spi_slave_hal.c"
"src/hal/spi_slave_hal_iram.c"
"src/soc_include_legacy_warn.c"
"src/hal/spi_flash_hal.c"
"src/hal/spi_flash_hal_iram.c"
)
set(COMPONENT_ADD_LDFRAGMENTS linker.lf)

View file

@ -0,0 +1,370 @@
// Copyright 2015-2019 Espressif Systems (Shanghai) PTE LTD
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
/*******************************************************************************
* NOTICE
* The ll is not public api, don't use in application code.
* See readme.md in soc/include/hal/readme.md
******************************************************************************/
// The Lowlevel layer for SPI Flash
#pragma once
#include <stdlib.h>
#include "soc/spi_periph.h"
#include <sys/param.h> // For MIN/MAX
#include <stdbool.h>
#include <string.h>
//Supported clock register values
#define SPI_FLASH_LL_CLKREG_VAL_5MHZ ((spi_flash_ll_clock_reg_t){.val=0x0000F1CF}) ///< Clock set to 5 MHz
#define SPI_FLASH_LL_CLKREG_VAL_10MHZ ((spi_flash_ll_clock_reg_t){.val=0x000070C7}) ///< Clock set to 10 MHz
#define SPI_FLASH_LL_CLKREG_VAL_20MHZ ((spi_flash_ll_clock_reg_t){.val=0x00003043}) ///< Clock set to 20 MHz
#define SPI_FLASH_LL_CLKREG_VAL_26MHZ ((spi_flash_ll_clock_reg_t){.val=0x00002002}) ///< Clock set to 26 MHz
#define SPI_FLASH_LL_CLKREG_VAL_40MHZ ((spi_flash_ll_clock_reg_t){.val=0x00001001}) ///< Clock set to 40 MHz
#define SPI_FLASH_LL_CLKREG_VAL_80MHZ ((spi_flash_ll_clock_reg_t){.val=0x80000000}) ///< Clock set to 80 MHz
/// Get the start address of SPI peripheral registers by the host ID
#define spi_flash_ll_get_hw(n) ((n)==0||(n)==1? &SPI1:((n)==2?&SPI2:((n)==3?&SPI3:({abort();(spi_dev_t*)0;}))))
///Slowest io mode supported by ESP32, currently SlowRd
#define SPI_FLASH_READ_MODE_MIN SPI_FLASH_SLOWRD
/** @brief Mode used for reading from SPI flash */
typedef enum {
SPI_FLASH_SLOWRD = 0, ///< Data read using single I/O, some limits on speed
SPI_FLASH_FASTRD, ///< Data read using single I/O, no limit on speed
SPI_FLASH_DOUT, ///< Data read using dual I/O
SPI_FLASH_DIO, ///< Both address & data transferred using dual I/O
SPI_FLASH_QOUT, ///< Data read using quad I/O
SPI_FLASH_QIO, ///< Both address & data transferred using quad I/O
SPI_FLASH_READ_MODE_MAX, ///< The fastest io mode supported by the host is ``ESP_FLASH_READ_MODE_MAX-1``.
} esp_flash_read_mode_t;
/// type to store pre-calculated register value in above layers
typedef typeof(SPI1.clock) spi_flash_ll_clock_reg_t;
/*------------------------------------------------------------------------------
* Control
*----------------------------------------------------------------------------*/
/**
* Reset peripheral registers before configuration and starting control
*
* @param dev Beginning address of the peripheral registers.
*/
static inline void spi_flash_ll_reset(spi_dev_t *dev)
{
dev->user.val = 0;
dev->ctrl.val = 0;
}
/**
* Check whether the previous operation is done.
*
* @param dev Beginning address of the peripheral registers.
*
* @return true if last command is done, otherwise false.
*/
static inline bool spi_flash_ll_cmd_is_done(const spi_dev_t *dev)
{
return (dev->cmd.val == 0);
}
/**
* Erase the flash chip.
*
* @param dev Beginning address of the peripheral registers.
*/
static inline void spi_flash_ll_erase_chip(spi_dev_t *dev)
{
dev->cmd.flash_ce = 1;
}
/**
* Erase the sector, the address should be set by spi_flash_ll_set_address.
*
* @param dev Beginning address of the peripheral registers.
*/
static inline void spi_flash_ll_erase_sector(spi_dev_t *dev)
{
dev->ctrl.val = 0;
dev->cmd.flash_se = 1;
}
/**
* Erase the block, the address should be set by spi_flash_ll_set_address.
*
* @param dev Beginning address of the peripheral registers.
*/
static inline void spi_flash_ll_erase_block(spi_dev_t *dev)
{
dev->cmd.flash_be = 1;
}
/**
* Enable/disable write protection for the flash chip.
*
* @param dev Beginning address of the peripheral registers.
* @param wp true to enable the protection, false to disable (write enable).
*/
static inline void spi_flash_ll_set_write_protect(spi_dev_t *dev, bool wp)
{
if (wp) {
dev->cmd.flash_wrdi = 1;
} else {
dev->cmd.flash_wren = 1;
}
}
/**
* Get the read data from the buffer after ``spi_flash_ll_read`` is done.
*
* @param dev Beginning address of the peripheral registers.
* @param buffer Buffer to hold the output data
* @param read_len Length to get out of the buffer
*/
static inline void spi_flash_ll_get_buffer_data(spi_dev_t *dev, void *buffer, uint32_t read_len)
{
if (((intptr_t)buffer % 4 == 0) && (read_len % 4 == 0)) {
// If everything is word-aligned, do a faster memcpy
memcpy(buffer, (void *)dev->data_buf, read_len);
} else {
// Otherwise, slow(er) path copies word by word
int copy_len = read_len;
for (int i = 0; i < (read_len + 3) / 4; i++) {
int word_len = MIN(sizeof(uint32_t), copy_len);
uint32_t word = dev->data_buf[i];
memcpy(buffer, &word, word_len);
buffer = (void *)((intptr_t)buffer + word_len);
copy_len -= word_len;
}
}
}
/**
* Write a word to the data buffer.
*
* @param dev Beginning address of the peripheral registers.
* @param word Data to write at address 0.
*/
static inline void spi_flash_ll_write_word(spi_dev_t *dev, uint32_t word)
{
dev->data_buf[0] = word;
}
/**
* Program a page of the flash chip. Call ``spi_flash_ll_set_address`` before
* this to set the address to program.
*
* @param dev Beginning address of the peripheral registers.
* @param buffer Buffer holding the data to program
* @param length Length to program.
*/
static inline void spi_flash_ll_program_page(spi_dev_t *dev, const void *buffer, uint32_t length)
{
dev->user.usr_dummy = 0;
// Load data registers, word at a time
int num_words = (length + 3) / 4;
for (int i = 0; i < num_words; i++) {
uint32_t word = 0;
uint32_t word_len = MIN(length, sizeof(word));
memcpy(&word, buffer, word_len);
dev->data_buf[i] = word;
length -= word_len;
buffer = (void *)((intptr_t)buffer + word_len);
}
dev->cmd.flash_pp = 1;
}
/**
* Trigger a user defined transaction. All phases, including command, address, dummy, and the data phases,
* should be configured before this is called.
*
* @param dev Beginning address of the peripheral registers.
*/
static inline void spi_flash_ll_user_start(spi_dev_t *dev)
{
dev->cmd.usr = 1;
}
/**
* Check whether the host is idle to perform new commands.
*
* @param dev Beginning address of the peripheral registers.
*
* @return true if the host is idle, otherwise false
*/
static inline bool spi_flash_ll_host_idle(const spi_dev_t *dev)
{
return dev->ext2.st != 0;
}
/**
* Set phases for user-defined transaction to read
*
* @param dev Beginning address of the peripheral registers.
*/
static inline void spi_flash_ll_read_phase(spi_dev_t *dev)
{
typeof (dev->user) user = {
.usr_command = 1,
.usr_mosi = 0,
.usr_miso = 1,
.usr_addr = 1,
};
dev->user = user;
}
/*------------------------------------------------------------------------------
* Configs
*----------------------------------------------------------------------------*/
/**
* Select which pin to use for the flash
*
* @param dev Beginning address of the peripheral registers.
* @param pin Pin ID to use, 0-2. Set to other values to disable all the CS pins.
*/
static inline void spi_flash_ll_set_cs_pin(spi_dev_t *dev, int pin)
{
dev->pin.cs0_dis = (pin == 0) ? 0 : 1;
dev->pin.cs1_dis = (pin == 1) ? 0 : 1;
dev->pin.cs2_dis = (pin == 2) ? 0 : 1;
}
/**
* Set the read io mode.
*
* @param dev Beginning address of the peripheral registers.
* @param read_mode I/O mode to use in the following transactions.
*/
static inline void spi_flash_ll_set_read_mode(spi_dev_t *dev, esp_flash_read_mode_t read_mode)
{
typeof (dev->ctrl) ctrl = dev->ctrl;
ctrl.val &= ~(SPI_FREAD_QIO_M | SPI_FREAD_QUAD_M | SPI_FREAD_DIO_M | SPI_FREAD_DUAL_M);
ctrl.val |= SPI_FASTRD_MODE_M;
switch (read_mode) {
case SPI_FLASH_FASTRD:
//the default option
break;
case SPI_FLASH_QIO:
ctrl.fread_qio = 1;
break;
case SPI_FLASH_QOUT:
ctrl.fread_quad = 1;
break;
case SPI_FLASH_DIO:
ctrl.fread_dio = 1;
break;
case SPI_FLASH_DOUT:
ctrl.fread_dual = 1;
break;
case SPI_FLASH_SLOWRD:
ctrl.fastrd_mode = 0;
break;
default:
abort();
}
dev->ctrl = ctrl;
}
/**
* Set clock frequency to work at.
*
* @param dev Beginning address of the peripheral registers.
* @param clock_val pointer to the clock value to set
*/
static inline void spi_flash_ll_set_clock(spi_dev_t *dev, spi_flash_ll_clock_reg_t *clock_val)
{
dev->clock = *clock_val;
}
/**
* Set the input length, in bits.
*
* @param dev Beginning address of the peripheral registers.
* @param bitlen Length of input, in bits.
*/
static inline void spi_flash_ll_set_miso_bitlen(spi_dev_t *dev, uint32_t bitlen)
{
dev->user.usr_miso = bitlen > 0;
dev->miso_dlen.usr_miso_dbitlen = bitlen ? (bitlen - 1) : 0;
}
/**
* Set the output length, in bits (not including command, address and dummy
* phases)
*
* @param dev Beginning address of the peripheral registers.
* @param bitlen Length of output, in bits.
*/
static inline void spi_flash_ll_set_mosi_bitlen(spi_dev_t *dev, uint32_t bitlen)
{
dev->user.usr_mosi = bitlen > 0;
dev->mosi_dlen.usr_mosi_dbitlen = bitlen ? (bitlen - 1) : 0;
}
/**
* Set the command with fixed length (8 bits).
*
* @param dev Beginning address of the peripheral registers.
* @param command Command to send
*/
static inline void spi_flash_ll_set_command8(spi_dev_t *dev, uint8_t command)
{
dev->user.usr_command = 1;
typeof(dev->user2) user2 = {
.usr_command_value = command,
.usr_command_bitlen = (8 - 1),
};
dev->user2 = user2;
}
/**
* Set the address length to send, in bits. Should be called before commands that requires the address e.g. erase sector, read, write...
*
* @param dev Beginning address of the peripheral registers.
* @param bitlen Length of the address, in bits
*/
static inline void spi_flash_ll_set_addr_bitlen(spi_dev_t *dev, uint32_t bitlen)
{
dev->user1.usr_addr_bitlen = (bitlen - 1);
dev->user.usr_addr = bitlen ? 1 : 0;
}
/**
* Set the address to send. Should be called before commands that requires the address e.g. erase sector, read, write...
*
* @param dev Beginning address of the peripheral registers.
* @param addr Address to send
*/
static inline void spi_flash_ll_set_address(spi_dev_t *dev, uint32_t addr)
{
dev->addr = addr;
}
/**
* Set the length of dummy cycles.
*
* @param dev Beginning address of the peripheral registers.
* @param dummy_n Cycles of dummy phases
*/
static inline void spi_flash_ll_set_dummy(spi_dev_t *dev, uint32_t dummy_n)
{
dev->user.usr_dummy = dummy_n ? 1 : 0;
dev->user1.usr_dummy_cyclelen = dummy_n - 1;
}

View file

@ -3,7 +3,7 @@
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
@ -22,6 +22,14 @@
#define SPI_IOMUX_PIN_NUM_WP 10
#define SPI_IOMUX_PIN_NUM_HD 9
//For D2WD and PICO-D4 chip
#define SPI_D2WD_PIN_NUM_MISO 17
#define SPI_D2WD_PIN_NUM_MOSI 8
#define SPI_D2WD_PIN_NUM_CLK 6
#define SPI_D2WD_PIN_NUM_CS 16
#define SPI_D2WD_PIN_NUM_WP 7
#define SPI_D2WD_PIN_NUM_HD 11
#define HSPI_IOMUX_PIN_NUM_MISO 12
#define HSPI_IOMUX_PIN_NUM_MOSI 13
#define HSPI_IOMUX_PIN_NUM_CLK 14

View file

@ -0,0 +1,39 @@
// Copyright 2015-2019 Espressif Systems (Shanghai) PTE LTD
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
#pragma once
#include <stdint.h>
#include "esp_err.h"
/*
* Possible errors returned from esp flash internal functions, these error codes
* should be consistent with esp_err_t codes. But in order to make the source
* files less dependent to esp_err_t, they use the error codes defined in this
* replacable header. This header should ensure the consistency to esp_err_t.
*/
/* These should be consistent with esp_err_t errors */
#define ESP_ERR_FLASH_SIZE_NOT_MATCH ESP_ERR_INVALID_SIZE ///< The chip doesn't have enough space for the current partition table
#define ESP_ERR_FLASH_NO_RESPONSE ESP_ERR_INVALID_RESPONSE ///< Chip did not respond to the command, or timed out.
#define ESP_ERR_FLASH_ERR_BASE 0x6000 ///< Starting number of Flash error codes */
//The ROM code has already taken 1 and 2, to avoid possible conflicts, start from 3.
#define ESP_ERR_FLASH_NOT_INITIALISED (ESP_ERR_FLASH_ERR_BASE+3) ///< esp_flash_chip_t structure not correctly initialised by esp_flash_init().
#define ESP_ERR_FLASH_UNSUPPORTED_HOST (ESP_ERR_FLASH_ERR_BASE+4) ///< Requested operation isn't supported via this host SPI bus (chip->spi field).
#define ESP_ERR_FLASH_UNSUPPORTED_CHIP (ESP_ERR_FLASH_ERR_BASE+5) ///< Requested operation isn't supported by this model of SPI flash chip.
#define ESP_ERR_FLASH_PROTECTED (ESP_ERR_FLASH_ERR_BASE+6) ///< Write operation failed due to chip's write protection being enabled.

View file

@ -0,0 +1,243 @@
// Copyright 2010-2019 Espressif Systems (Shanghai) PTE LTD
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
/*******************************************************************************
* NOTICE
* The HAL is not public api, don't use in application code.
* See readme.md in soc/include/hal/readme.md
******************************************************************************/
// The HAL layer for SPI Flash (common part)
#pragma once
#include "hal/spi_flash_ll.h"
#include "hal/spi_flash_host_drv.h"
#include "soc/soc_memory_layout.h"
#define ESP_FLASH_DEFAULT_FREQ ESP_FLASH_20MHZ
/* Hardware host-specific constants */
#define SPI_FLASH_HAL_MAX_WRITE_BYTES 64
#define SPI_FLASH_HAL_MAX_READ_BYTES 64
///Lowest speed supported by the driver, currently 5 MHz
#define ESP_FLASH_SPEED_MIN ESP_FLASH_5MHZ
/**
* @brief SPI flash clock speed values, always refer to them by the enum rather
* than the actual value (more speed may be appended into the list).
*
* A strategy to select the maximum allowed speed is to enumerate from the
* ``ESP_FLSH_SPEED_MAX-1`` or highest frequency supported by your flash, and
* decrease the speed until the probing success.
*/
typedef enum {
ESP_FLASH_5MHZ = 0, ///< The flash runs under 5MHz
ESP_FLASH_10MHZ, ///< The flash runs under 10MHz
ESP_FLASH_20MHZ, ///< The flash runs under 20MHz
ESP_FLASH_26MHZ, ///< The flash runs under 26MHz
ESP_FLASH_40MHZ, ///< The flash runs under 40MHz
ESP_FLASH_80MHZ, ///< The flash runs under 80MHz
ESP_FLASH_SPEED_MAX, ///< The maximum frequency supported by the host is ``ESP_FLASH_SPEED_MAX-1``.
} esp_flash_speed_t;
/**
* Generic driver context structure for all chips using the SPI peripheral.
* Include this into the HEAD of the driver data for other driver
* implementations that also use the SPI peripheral.
*/
typedef struct {
spi_dev_t *spi; ///< Pointer to SPI peripheral registers (SP1, SPI2 or SPI3). Set before initialisation.
int cs_num; ///< Which cs pin is used, 0-2.
int extra_dummy;
spi_flash_ll_clock_reg_t clock_conf;
} spi_flash_memspi_data_t;
/// Configuration structure for the SPI driver.
typedef struct {
int host_id; ///< SPI peripheral ID, 1 for SPI1, 2 for SPI2 (HSPI), 3 for SPI3 (VSPI)
int cs_num; ///< Which cs pin is used, 0-2.
bool iomux; ///< Whether the IOMUX is used, used for timing compensation.
int input_delay_ns; ///< Input delay on the MISO pin after the launch clock used for timing compensation.
esp_flash_speed_t speed;///< SPI flash clock speed to work at.
} spi_flash_memspi_config_t;
/**
* Configure SPI flash hal settings.
*
* @param data Buffer to hold configured data, the buffer should be in DRAM to be available when cache disabled
* @param cfg Configurations to set
*
* @return
* - ESP_OK: success
* - ESP_ERR_INVALID_ARG: the data buffer is not in the DRAM.
*/
esp_err_t spi_flash_hal_init(spi_flash_memspi_data_t *data_out, const spi_flash_memspi_config_t *cfg);
/**
* Configure the device-related register before transactions.
*
* @param driver The driver context.
*
* @return always return ESP_OK.
*/
esp_err_t spi_flash_hal_device_config(spi_flash_host_driver_t *driver);
/**
* Send an user-defined spi transaction to the device.
*
* @note This is usually used when the memspi interface doesn't support some
* particular commands. Since this function supports timing compensation, it is
* also used to receive some data when the frequency is high.
*
* @param driver The driver context.
* @param trans The transaction to send, also holds the received data.
*
* @return always return ESP_OK.
*/
esp_err_t spi_flash_hal_common_command(spi_flash_host_driver_t *driver, spi_flash_trans_t *trans);
/**
* Erase whole flash chip.
*
* @param driver The driver context.
*/
void spi_flash_hal_erase_chip(spi_flash_host_driver_t *driver);
/**
* Erase a specific sector by its start address.
*
* @param driver The driver context.
* @param start_address Start address of the sector to erase.
*/
void spi_flash_hal_erase_sector(spi_flash_host_driver_t *driver, uint32_t start_address);
/**
* Erase a specific block by its start address.
*
* @param driver The driver context.
* @param start_address Start address of the block to erase.
*/
void spi_flash_hal_erase_block(spi_flash_host_driver_t *driver, uint32_t start_address);
/**
* Program a page of the flash.
*
* @param driver The driver context.
* @param address Address of the page to program
* @param buffer Data to program
* @param length Size of the buffer in bytes, no larger than ``SPI_FLASH_HAL_MAX_WRITE_BYTES`` (64) bytes.
*/
void spi_flash_hal_program_page(spi_flash_host_driver_t *driver, const void *buffer, uint32_t address, uint32_t length);
/**
* Read from the flash. The read command should be set by ``spi_flash_hal_configure_host_read_mode`` before.
*
* @param driver The driver context.
* @param buffer Buffer to store the read data
* @param address Address to read
* @param length Length to read, no larger than ``SPI_FLASH_HAL_MAX_READ_BYTES`` (64) bytes.
*
* @return always return ESP_OK.
*/
esp_err_t spi_flash_hal_read(spi_flash_host_driver_t *driver, void *buffer, uint32_t address, uint32_t read_len);
/**
* Enable or disable the write protection of the flash chip.
*
* @param driver The driver context.
* @param wp true to enable the write protection, otherwise false.
*
* @return always return ESP_OK.
*/
esp_err_t spi_flash_hal_set_write_protect(spi_flash_host_driver_t *chip_drv, bool wp);
/**
* Check whether the SPI host is idle and can perform other operations.
*
* @param driver The driver context.
*
* @return ture if idle, otherwise false.
*/
bool spi_flash_hal_host_idle(spi_flash_host_driver_t *driver);
/**
* Configure the SPI host hardware registers for the specified read mode.
*
* Note that calling this configures SPI host registers, so if running any
* other commands as part of set_read_mode() then these must be run before
* calling this function.
*
* @param driver The driver context
* @param read_mode The HW read mode to use
* @param addr_bitlen Length of the address phase, in bits
* @param dummy_cyclelen_base Base cycles of the dummy phase, some extra dummy cycles may be appended to compensate the timing.
* @param read_command Actual reading command to send to flash chip on the bus.
*
* @return always return ESP_OK.
*/
esp_err_t spi_flash_hal_configure_host_read_mode(spi_flash_host_driver_t *driver, esp_flash_read_mode_t read_mode,
uint32_t addr_bitlen, uint32_t dummy_cyclelen_base,
uint32_t read_command);
/**
* Poll until the last operation is done.
*
* @param driver The driver context.
*/
void spi_flash_hal_poll_cmd_done(spi_flash_host_driver_t *driver);
/**
* Check whether the given buffer can be used as the write buffer directly. If 'chip' is connected to the main SPI bus, we can only write directly from
* regions that are accessible ith cache disabled. *
*
* @param driver The driver context
* @param p The buffer holding data to send.
*
* @return True if the buffer can be used to send data, otherwise false.
*/
static inline bool spi_flash_hal_supports_direct_write(spi_flash_host_driver_t *driver, const void *p)
{
#ifdef ESP_PLATFORM
bool direct_write = ( ((spi_flash_memspi_data_t *)driver->driver_data)->spi != &SPI1
|| esp_ptr_in_dram(p) );
#else
//If it is not on real chips, there is no limitation that the data has to be in DRAM.
bool direct_write = true;
#endif
return direct_write;
}
/**
* Check whether the given buffer can be used as the read buffer directly. If 'chip' is connected to the main SPI bus, we can only read directly from
* regions that are accessible ith cache disabled. *
*
* @param driver The driver context
* @param p The buffer to hold the received data.
*
* @return True if the buffer can be used to receive data, otherwise false.
*/
static inline bool spi_flash_hal_supports_direct_read(spi_flash_host_driver_t *driver, const void *p)
{
#ifdef ESP_PLATFORM
//currently the driver doesn't support to read through DMA, no word-aligned requirements
bool direct_read = ( ((spi_flash_memspi_data_t *)driver->driver_data)->spi != &SPI1
|| esp_ptr_in_dram(p) );
#else
//If it is not on real chips, there is no limitation that the data has to be in DRAM.
bool direct_read = true;
#endif
return direct_read;
}

View file

@ -0,0 +1,112 @@
// Copyright 2010-2019 Espressif Systems (Shanghai) PTE LTD
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
#pragma once
#include "hal/spi_flash_ll.h"
#include "hal/esp_flash_err.h"
/** Definition of a common transaction. Also holds the return value. */
typedef struct {
uint8_t command; ///< Command to send, always 8bits
uint8_t mosi_len; ///< Output data length, in bits
uint8_t miso_len; ///< Input data length, in bits
uint32_t mosi_data; ///< Output data to slave
uint32_t miso_data[2]; ///< [out] Input data from slave, little endian
} spi_flash_trans_t;
struct spi_flash_host_driver_t;
typedef struct spi_flash_host_driver_t spi_flash_host_driver_t;
/** Host driver configuration and context structure. */
struct spi_flash_host_driver_t {
/**
* Configuration and static data used by the specific host driver. The type
* is determined by the host driver.
*/
void *driver_data;
/**
* Configure the device-related register before transactions. This saves
* some time to re-configure those registers when we send continuously
*/
esp_err_t (*dev_config)(spi_flash_host_driver_t *driver);
/**
* Send an user-defined spi transaction to the device.
*/
esp_err_t (*common_command)(spi_flash_host_driver_t *driver, spi_flash_trans_t *t);
/**
* Read flash ID.
*/
esp_err_t (*read_id)(spi_flash_host_driver_t *driver, uint32_t *id);
/**
* Erase whole flash chip.
*/
void (*erase_chip)(spi_flash_host_driver_t *driver);
/**
* Erase a specific sector by its start address.
*/
void (*erase_sector)(spi_flash_host_driver_t *driver, uint32_t start_address);
/**
* Erase a specific block by its start address.
*/
void (*erase_block)(spi_flash_host_driver_t *driver, uint32_t start_address);
/**
* Read the status of the flash chip.
*/
esp_err_t (*read_status)(spi_flash_host_driver_t *driver, uint8_t *out_sr);
/**
* Disable write protection.
*/
esp_err_t (*set_write_protect)(spi_flash_host_driver_t *driver, bool wp);
/**
* Program a page of the flash. Check ``max_write_bytes`` for the maximum allowed writing length.
*/
void (*program_page)(spi_flash_host_driver_t *driver, const void *buffer, uint32_t address, uint32_t length);
/** Check whether need to allocate new buffer to write */
bool (*supports_direct_write)(spi_flash_host_driver_t *driver, const void *p);
/** Check whether need to allocate new buffer to read */
bool (*supports_direct_read)(spi_flash_host_driver_t *driver, const void *p);
/** maximum length of program_page */
int max_write_bytes;
/**
* Read data from the flash. Check ``max_read_bytes`` for the maximum allowed reading length.
*/
esp_err_t (*read)(spi_flash_host_driver_t *driver, void *buffer, uint32_t address, uint32_t read_len);
/** maximum length of read */
int max_read_bytes;
/**
* Check whether the host is idle to perform new operations.
*/
bool (*host_idle)(spi_flash_host_driver_t *driver);
/**
* Configure the host to work at different read mode.
*/
esp_err_t (*configure_host_read_mode)(spi_flash_host_driver_t *driver, esp_flash_read_mode_t read_mode, uint32_t addr_bitlen, uint32_t dummy_bitlen_base, uint32_t read_command);
/**
* Internal use, poll the HW until the last operation is done.
*/
void (*poll_cmd_done)(spi_flash_host_driver_t *driver);
/**
* For some host (SPI1), they are shared with a cache. When the data is
* modified, the cache needs to be flushed. Left NULL if not supported.
*/
esp_err_t (*flush_cache)(spi_flash_host_driver_t* driver, uint32_t addr, uint32_t size);
/**
* Check if the given region is protected (e.g. is the bootloader). Left
* NULL if current host doesn't need protection.
*/
bool (*region_protected)(spi_flash_host_driver_t* driver, uint32_t addr, uint32_t size);
};

View file

@ -12,4 +12,6 @@ entries:
rtc_wdt (noflash_text)
spi_hal_iram (noflash_text)
spi_slave_hal_iram (noflash_text)
lldesc (noflash_text)
spi_flash_hal_iram (noflash)
lldesc (noflash_text)

View file

@ -0,0 +1,74 @@
// Copyright 2015-2018 Espressif Systems (Shanghai) PTE LTD
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
#include <stdlib.h>
#include "hal/spi_flash_hal.h"
#include "string.h"
#include "hal/hal_defs.h"
#define APB_CYCLE_NS (1000*1000*1000LL/APB_CLK_FREQ)
static const char TAG[] = "FLASH_HAL";
typedef struct {
int freq;
spi_flash_ll_clock_reg_t clock_reg_val;
} spi_flash_hal_clock_config_t;
static const spi_flash_hal_clock_config_t spi_flash_clk_cfg_reg[ESP_FLASH_SPEED_MAX] = {
{5e6, SPI_FLASH_LL_CLKREG_VAL_5MHZ},
{10e6, SPI_FLASH_LL_CLKREG_VAL_10MHZ},
{20e6, SPI_FLASH_LL_CLKREG_VAL_20MHZ},
{26e6, SPI_FLASH_LL_CLKREG_VAL_26MHZ},
{40e6, SPI_FLASH_LL_CLKREG_VAL_40MHZ},
{80e6, SPI_FLASH_LL_CLKREG_VAL_80MHZ},
};
static inline int get_dummy_n(bool gpio_is_used, int input_delay_ns, int eff_clk)
{
const int apbclk_kHz = APB_CLK_FREQ / 1000;
//calculate how many apb clocks a period has
const int apbclk_n = APB_CLK_FREQ / eff_clk;
const int gpio_delay_ns = gpio_is_used ? (APB_CYCLE_NS * 2) : 0;
//calculate how many apb clocks the delay is, the 1 is to compensate in case ``input_delay_ns`` is rounded off.
int apb_period_n = (1 + input_delay_ns + gpio_delay_ns) * apbclk_kHz / 1000 / 1000;
if (apb_period_n < 0) {
apb_period_n = 0;
}
return apb_period_n / apbclk_n;
}
esp_err_t spi_flash_hal_init(spi_flash_memspi_data_t *data_out, const spi_flash_memspi_config_t *cfg)
{
if (!esp_ptr_internal(data_out)) {
return ESP_ERR_INVALID_ARG;
}
*data_out = (spi_flash_memspi_data_t) {
.spi = spi_flash_ll_get_hw(cfg->host_id),
.cs_num = cfg->cs_num,
.extra_dummy = get_dummy_n(!cfg->iomux, cfg->input_delay_ns, spi_flash_clk_cfg_reg[cfg->speed].freq),
.clock_conf = spi_flash_clk_cfg_reg[cfg->speed].clock_reg_val,
};
ESP_EARLY_LOGD(TAG, "extra_dummy: %d", data_out->extra_dummy);
return ESP_OK;
}
static inline spi_dev_t *get_spi_dev(spi_flash_host_driver_t *chip_drv)
{
return ((spi_flash_memspi_data_t *)chip_drv->driver_data)->spi;
}

View file

@ -0,0 +1,144 @@
// Copyright 2015-2019 Espressif Systems (Shanghai) PTE LTD
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
#include <stdlib.h>
#include "hal/spi_flash_hal.h"
#include "string.h"
#include "hal/hal_defs.h"
#define ADDRESS_MASK_24BIT 0xFFFFFF
static inline spi_dev_t *get_spi_dev(spi_flash_host_driver_t *chip_drv)
{
return ((spi_flash_memspi_data_t *)chip_drv->driver_data)->spi;
}
void spi_flash_hal_poll_cmd_done(spi_flash_host_driver_t *driver)
{
while (!spi_flash_ll_cmd_is_done(get_spi_dev(driver))) {
//nop
}
}
esp_err_t spi_flash_hal_device_config(spi_flash_host_driver_t *driver)
{
spi_flash_memspi_data_t *drv_data = (spi_flash_memspi_data_t *)driver->driver_data;
spi_dev_t *dev = get_spi_dev(driver);
spi_flash_ll_reset(dev);
spi_flash_ll_set_cs_pin(dev, drv_data->cs_num);
spi_flash_ll_set_clock(dev, &drv_data->clock_conf);
return ESP_OK;
}
esp_err_t spi_flash_hal_configure_host_read_mode(spi_flash_host_driver_t *driver, esp_flash_read_mode_t read_mode,
uint32_t addr_bitlen, uint32_t dummy_cyclelen_base,
uint32_t read_command)
{
// Add dummy cycles to compensate for latency of GPIO matrix and external delay, if necessary...
int dummy_cyclelen = dummy_cyclelen_base + ((spi_flash_memspi_data_t *)driver->driver_data)->extra_dummy;
spi_dev_t *dev = get_spi_dev(driver);
spi_flash_ll_set_addr_bitlen(dev, addr_bitlen);
spi_flash_ll_set_command8(dev, read_command);
spi_flash_ll_read_phase(dev);
spi_flash_ll_set_dummy(dev, dummy_cyclelen);
spi_flash_ll_set_read_mode(dev, read_mode);
return ESP_OK;
}
esp_err_t spi_flash_hal_common_command(spi_flash_host_driver_t *chip_drv, spi_flash_trans_t *trans)
{
chip_drv->configure_host_read_mode(chip_drv, SPI_FLASH_FASTRD, 0, 0, 0);
spi_dev_t *dev = get_spi_dev(chip_drv);
spi_flash_ll_set_command8(dev, trans->command);
spi_flash_ll_set_addr_bitlen(dev, 0);
spi_flash_ll_set_miso_bitlen(dev, trans->miso_len);
spi_flash_ll_set_mosi_bitlen(dev, trans->mosi_len);
spi_flash_ll_write_word(dev, trans->mosi_data);
spi_flash_ll_user_start(dev);
chip_drv->poll_cmd_done(chip_drv);
spi_flash_ll_get_buffer_data(dev, trans->miso_data, 8);
return ESP_OK;
}
void spi_flash_hal_erase_chip(spi_flash_host_driver_t *chip_drv)
{
spi_dev_t *dev = get_spi_dev(chip_drv);
spi_flash_ll_erase_chip(dev);
chip_drv->poll_cmd_done(chip_drv);
}
void spi_flash_hal_erase_sector(spi_flash_host_driver_t *chip_drv, uint32_t start_address)
{
spi_dev_t *dev = get_spi_dev(chip_drv);
spi_flash_ll_set_addr_bitlen(dev, 24);
spi_flash_ll_set_address(dev, start_address & ADDRESS_MASK_24BIT);
spi_flash_ll_erase_sector(dev);
chip_drv->poll_cmd_done(chip_drv);
}
void spi_flash_hal_erase_block(spi_flash_host_driver_t *chip_drv, uint32_t start_address)
{
spi_dev_t *dev = get_spi_dev(chip_drv);
spi_flash_ll_set_addr_bitlen(dev, 24);
spi_flash_ll_set_address(dev, start_address & ADDRESS_MASK_24BIT);
spi_flash_ll_erase_block(dev);
chip_drv->poll_cmd_done(chip_drv);
}
void spi_flash_hal_program_page(spi_flash_host_driver_t *chip_drv, const void *buffer, uint32_t address, uint32_t length)
{
spi_dev_t *dev = get_spi_dev(chip_drv);
spi_flash_ll_set_addr_bitlen(dev, 24);
spi_flash_ll_set_address(dev, (address & ADDRESS_MASK_24BIT) | (length << 24));
spi_flash_ll_program_page(dev, buffer, length);
chip_drv->poll_cmd_done(chip_drv);
}
esp_err_t spi_flash_hal_read(spi_flash_host_driver_t *chip_drv, void *buffer, uint32_t address, uint32_t read_len)
{
spi_dev_t *dev = get_spi_dev(chip_drv);
//the command is already set by ``spi_flash_hal_configure_host_read_mode`` before.
spi_flash_ll_set_address(dev, address << 8);
spi_flash_ll_set_miso_bitlen(dev, read_len * 8);
spi_flash_ll_user_start(dev);
chip_drv->poll_cmd_done(chip_drv);
spi_flash_ll_get_buffer_data(dev, buffer, read_len);
return ESP_OK;
}
bool spi_flash_hal_host_idle(spi_flash_host_driver_t *chip_drv)
{
spi_dev_t *dev = get_spi_dev(chip_drv);
bool idle = spi_flash_ll_host_idle(dev);
// Not clear if this is necessary, or only necessary if
// chip->spi == SPI1. But probably doesn't hurt...
if (dev == &SPI1) {
idle &= spi_flash_ll_host_idle(&SPI0);
}
return idle;
}
esp_err_t spi_flash_hal_set_write_protect(spi_flash_host_driver_t *chip_drv, bool wp)
{
spi_dev_t *dev = get_spi_dev(chip_drv);
spi_flash_ll_set_write_protect(dev, wp);
chip_drv->poll_cmd_done(chip_drv);
return ESP_OK;
}

View file

@ -8,11 +8,22 @@ else()
"flash_mmap.c"
"flash_ops.c"
"partition.c"
"spi_flash_rom_patch.c")
"spi_flash_rom_patch.c"
"spi_flash_chip_drivers.c"
"spi_flash_chip_generic.c"
"spi_flash_chip_issi.c"
"spi_flash_os_func_app.c"
"spi_flash_os_func_noos.c"
"memspi_host_driver.c"
)
if(NOT CONFIG_SPI_FLASH_USE_LEGACY_IMPL)
list(APPEND COMPONENT_SRCS "esp_flash_api.c")
endif()
set(COMPONENT_PRIV_REQUIRES bootloader_support app_update soc)
endif()
set(COMPONENT_ADD_INCLUDEDIRS include)
set(COMPONENT_PRIV_INCLUDEDIRS private_include)
set(COMPONENT_ADD_LDFRAGMENTS linker.lf)
register_component()

View file

@ -77,4 +77,21 @@ menu "SPI Flash driver"
bool "Allowed"
endchoice
config SPI_FLASH_USE_LEGACY_IMPL
bool "Use the legacy implementation before IDF v4.0"
default n
help
The implementation of SPI flash has been greatly changed in IDF v4.0.
Enable this option to use the legacy implementation.
menu "Auto-detect flash chips"
config SPI_FLASH_SUPPORT_ISSI_CHIP
bool "ISSI"
default y
help
Enable this to support auto detection of ISSI chips if chip vendor not specified.
This adds support for variant chips, however will extend detecting time.
endmenu #auto detect flash chips
endmenu

View file

@ -3,22 +3,40 @@ SPI Flash API
Overview
--------
The spi_flash component contains API functions related to reading, writing, erasing, memory mapping for data in the external SPI flash. The spi_flash component also has higher-level API functions which work with partitions defined in the :doc:`partition table </api-guides/partition-tables>`.
The spi_flash component contains API functions related to reading, writing,
erasing, memory mapping for data in the external flash. The spi_flash
component also has higher-level API functions which work with partitions
defined in the :doc:`partition table </api-guides/partition-tables>`.
Note that all the functionality is limited to the "main" SPI flash chip, the same SPI flash chip from which programs are runs. For ``spi_flash_*`` functions, this is a software limitation. The underlying ROM functions which work with SPI flash do not have provisions for working with flash chips attached to SPI peripherals other than SPI0.
Different from the API before IDF v4.0, the functionality is not limited to
the "main" SPI flash chip (the same SPI flash chip from which program runs).
With different chip pointers, you can access to external flashes chips on not
only SPI0/1 but also HSPI/VSPI buses.
Kconfig option :ref:``CONFIG_SPI_FLASH_USE_LEGACY_IMPL`` can be used to switch
``spi_flash_*`` functions back to the implementation before IDF v4.0.
However, the code size may get bigger if you use the new API and the old API
the same time.
Encrypted reads and writes use the old implementation, even if
:ref:``CONFIG_SPI_FLASH_USE_LEGACY_IMPL`` is not enabled. As such, encrypted
flash operations are only supported with the main flash chip (and not with
other flash chips on SPI1 with different CS).
SPI flash access API
--------------------
This is the set of API functions for working with data in flash:
- :cpp:func:`spi_flash_read` reads data from flash to RAM
- :cpp:func:`spi_flash_write` writes data from RAM to flash
- :cpp:func:`spi_flash_erase_sector` erases individual sectors of flash
- :cpp:func:`spi_flash_erase_range` erases ranges of addresses in flash
- :cpp:func:`spi_flash_get_chip_size` returns flash chip size, in bytes, as configured in menuconfig
- :cpp:func:`esp_flash_read` reads data from flash to RAM
- :cpp:func:`esp_flash_write` writes data from RAM to flash
- :cpp:func:`esp_flash_erase_region` erases specific region of flash
- :cpp:func:`esp_flash_erase_chip` erases the whole flash
- :cpp:func:`esp_flash_get_chip_size` returns flash chip size, in bytes, as configured in menuconfig
Generally, try to avoid using the raw SPI flash functions in favor of :ref:`partition-specific functions <flash-partition-apis>`.
Generally, try to avoid using the raw SPI flash functions to the "main" SPI
flash chip in favour of :ref:`partition-specific functions
<flash-partition-apis>`.
SPI Flash Size
--------------
@ -27,7 +45,7 @@ The SPI flash size is configured by writing a field in the software bootloader i
By default, the SPI flash size is detected by esptool.py when this bootloader is written to flash, and the header is updated with the correct size. Alternatively, it is possible to generate a fixed flash size by setting :envvar:`CONFIG_ESPTOOLPY_FLASHSIZE` in ``make menuconfig``.
If it is necessary to override the configured flash size at runtime, it is possible to set the ``chip_size`` member of the ``g_rom_flashchip`` structure. This size is used by ``spi_flash_*`` functions (in both software & ROM) to check the bounds.
If it is necessary to override the configured flash size at runtime, it is possible to set the ``chip_size`` member of the ``g_rom_flashchip`` structure. This size is used by ``esp_flash_*`` functions (in both software & ROM) to check the bounds.
Concurrency Constraints
-----------------------
@ -121,3 +139,67 @@ Differences between :cpp:func:`spi_flash_mmap` and :cpp:func:`esp_partition_mmap
- :cpp:func:`esp_partition_mmap` may be given any arbitrary offset within the partition, it will adjust the returned pointer to mapped memory as necessary
Note that since memory mapping happens in 64KB blocks, it may be possible to read data outside of the partition provided to ``esp_partition_mmap``.
Implementation
--------------
The ``esp_flash_t`` structure holds chip data as well as three important parts of this API:
1. The host driver, which provides the hardware support to access the chip;
2. The chip driver, which provides compability service to different chips;
3. The OS functions, provides support of some OS functions (e.g. lock, delay)
in different stages (1st/2st boot, or the app).
Host driver
^^^^^^^^^^^
The host driver relies on an interface (``spi_flash_host_driver_t``) defined
in the ``spi_flash_host_drv.h`` (in the ``soc/include/hal`` folder). This
interface provides some common functions to communicate with the chip.
In other files of the SPI HAL, some of these functions are implemented with
existing ESP32 memory-spi functionalities. However due to the speed
limitations of ESP32, the HAL layer can't provide high-speed implementations
to some reading commands (So we didn't do it at all). The files
(``memspi_host_driver.h`` and ``.c``) implement the high-speed version of
these commands with the ``common_command`` function provided in the HAL, and
wrap these functions as ``spi_flash_host_driver_t`` for upper layer to use.
You can also implement your own host driver, even with the GPIO. As long as
all the functions in the ``spi_flash_host_driver_t`` are implemented, the
esp_flash API can access to the flash regardless of the lowlevel hardware.
Chip driver
^^^^^^^^^^^
The chip driver, defined in ``spi_flash_chip_driver.h``, wraps basic
functions provided by the host driver for the API layer to use.
Some operations need some commands to be sent first, or read some status
after. Some chips need different command or value, or need special
communication ways.
There is a type of chip called ``generic chip`` which stands for common
chips. Other special chip drivers can be developed on the base of the generic
chip.
The chip driver relies on the host driver.
OS functions
^^^^^^^^^^^^
Currently the OS function layer provides a lock and a delay entrance.
The lock is used to resolve the conflicts between the SPI chip access and
other functions. E.g. the cache (used for the code and psram data fetch)
should be disabled when the flash chip on the SPI0/1 is being accessed. Also,
some devices which don't have CS wire, or the wire is controlled by the
software (e.g. SD card via SPI interface), requires the bus to be monopolized
during a period.
The delay is used by some long operations which requires the master to wait
or polling periodly.
The top API wraps these the chip driver and OS functions into an entire
component, and also provides some argument checking.

View file

@ -0,0 +1,127 @@
SPI Flash API (Legacy)
========================
Overview
--------
This is the readme for the APIs before IDF v4.0. Enable the kconfig option ``SPI_FLASH_USE_LEGACY_IMPL`` to use the
legacy implementation.
The spi_flash component contains API functions related to reading, writing, erasing, memory mapping for data in the external SPI flash. The spi_flash component also has higher-level API functions which work with partitions defined in the :doc:`partition table </api-guides/partition-tables>`.
Note that all the functionality is limited to the "main" SPI flash chip, the same SPI flash chip from which programs are runs. For ``spi_flash_*`` functions, this is a software limitation. The underlying ROM functions which work with SPI flash do not have provisions for working with flash chips attached to SPI peripherals other than SPI0.
SPI flash access API
--------------------
This is the set of API functions for working with data in flash:
- :cpp:func:`spi_flash_read` reads data from flash to RAM
- :cpp:func:`spi_flash_write` writes data from RAM to flash
- :cpp:func:`spi_flash_erase_sector` erases individual sectors of flash
- :cpp:func:`spi_flash_erase_range` erases ranges of addresses in flash
- :cpp:func:`spi_flash_get_chip_size` returns flash chip size, in bytes, as configured in menuconfig
Generally, try to avoid using the raw SPI flash functions in favor of :ref:`partition-specific functions <flash-partition-apis>`.
SPI Flash Size
--------------
The SPI flash size is configured by writing a field in the software bootloader image header, flashed at offset 0x1000.
By default, the SPI flash size is detected by esptool.py when this bootloader is written to flash, and the header is updated with the correct size. Alternatively, it is possible to generate a fixed flash size by setting :envvar:`CONFIG_ESPTOOLPY_FLASHSIZE` in ``make menuconfig``.
If it is necessary to override the configured flash size at runtime, it is possible to set the ``chip_size`` member of the ``g_rom_flashchip`` structure. This size is used by ``spi_flash_*`` functions (in both software & ROM) to check the bounds.
Concurrency Constraints
-----------------------
Because the SPI flash is also used for firmware execution via the instruction & data caches, these caches must be disabled while reading/writing/erasing. This means that both CPUs must be running code from IRAM and must only be reading data from DRAM while flash write operations occur.
If you use the API functions documented here, then these constraints are applied automatically and transparently. However, note that it will have some performance impact on other tasks in the system.
For differences between IRAM, DRAM, and flash cache, please refer to the :ref:`application memory layout <memory-layout>` documentation.
To avoid reading flash cache accidentally, when one CPU initiates a flash write or erase operation, the other CPU is put into a blocked state, and all non-IRAM-safe interrupts are disabled on both CPUs until the flash operation completes.
If one CPU initiates a flash write or erase operation, the other CPU is put into a blocked state to avoid reading flash cache accidentally. All interrupts not safe for IRAM are disabled on both CPUs until the flash operation completes.
.. _iram-safe-interrupt-handlers:
IRAM-Safe Interrupt Handlers
^^^^^^^^^^^^^^^^^^^^^^^^^^^^
If you have an interrupt handler that you want to execute while a flash operation is in progress (for example, for low latency operations), set the ``ESP_INTR_FLAG_IRAM`` flag when the :doc:`interrupt handler is registered </api-reference/system/intr_alloc>`.
You must ensure that all data and functions accessed by these interrupt handlers, including the ones that handlers call, are located in IRAM or DRAM.
Use the ``IRAM_ATTR`` attribute for functions::
#include "esp_attr.h"
void IRAM_ATTR gpio_isr_handler(void* arg)
{
// ...
}
Use the ``DRAM_ATTR`` and ``DRAM_STR`` attributes for constant data::
void IRAM_ATTR gpio_isr_handler(void* arg)
{
const static DRAM_ATTR uint8_t INDEX_DATA[] = { 45, 33, 12, 0 };
const static char *MSG = DRAM_STR("I am a string stored in RAM");
}
Note that knowing which data should be marked with ``DRAM_ATTR`` can be hard, the compiler will sometimes recognize that a variable or expression is constant (even if it is not marked ``const``) and optimize it into flash, unless it is marked with ``DRAM_ATTR``.
If a function or symbol is not correctly put into IRAM/DRAM, and the interrupt handler reads from the flash cache during a flash operation, it will cause a crash due to Illegal Instruction exception (for code which should be in IRAM) or garbage data to be read (for constant data which should be in DRAM).
.. _flash-partition-apis:
Partition table API
-------------------
ESP-IDF projects use a partition table to maintain information about various regions of SPI flash memory (bootloader, various application binaries, data, filesystems). More information on partition tables can be found :doc:`here </api-guides/partition-tables>`.
This component provides API functions to enumerate partitions found in the partition table and perform operations on them. These functions are declared in ``esp_partition.h``:
- :cpp:func:`esp_partition_find` checks a partition table for entries with specific type, returns an opaque iterator.
- :cpp:func:`esp_partition_get` returns a structure describing the partition for a given iterator.
- :cpp:func:`esp_partition_next` shifts the iterator to the next found partition.
- :cpp:func:`esp_partition_iterator_release` releases iterator returned by ``esp_partition_find``.
- :cpp:func:`esp_partition_find_first` - a convenience function which returns the structure describing the first partition found by ``esp_partition_find``.
- :cpp:func:`esp_partition_read`, :cpp:func:`esp_partition_write`, :cpp:func:`esp_partition_erase_range` are equivalent to :cpp:func:`spi_flash_read`, :cpp:func:`spi_flash_write`, :cpp:func:`spi_flash_erase_range`, but operate within partition boundaries.
.. note::
Application code should mostly use these ``esp_partition_*`` API functions instead of lower level ``spi_flash_*`` API functions. Partition table API functions do bounds checking and calculate correct offsets in flash, based on data stored in a partition table.
SPI Flash Encryption
--------------------
It is possible to encrypt the contents of SPI flash and have it transparently decrypted by hardware.
Refer to the :doc:`Flash Encryption documentation </security/flash-encryption>` for more details.
Memory mapping API
------------------
ESP32 features memory hardware which allows regions of flash memory to be mapped into instruction and data address spaces. This mapping works only for read operations. It is not possible to modify contents of flash memory by writing to a mapped memory region.
Mapping happens in 64KB pages. Memory mapping hardware can map up to four megabytes of flash into data address space and up to 16 megabytes of flash into instruction address space. See the technical reference manual for more details about memory mapping hardware.
Note that some 64KB pages are used to map the application itself into memory, so the actual number of available 64KB pages may be less.
Reading data from flash using a memory mapped region is the only way to decrypt contents of flash when :doc:`flash encryption </security/flash-encryption>` is enabled. Decryption is performed at the hardware level.
Memory mapping API are declared in ``esp_spi_flash.h`` and ``esp_partition.h``:
- :cpp:func:`spi_flash_mmap` maps a region of physical flash addresses into instruction space or data space of the CPU.
- :cpp:func:`spi_flash_munmap` unmaps previously mapped region.
- :cpp:func:`esp_partition_mmap` maps part of a partition into the instruction space or data space of the CPU.
Differences between :cpp:func:`spi_flash_mmap` and :cpp:func:`esp_partition_mmap` are as follows:
- :cpp:func:`spi_flash_mmap` must be given a 64KB aligned physical address.
- :cpp:func:`esp_partition_mmap` may be given any arbitrary offset within the partition, it will adjust the returned pointer to mapped memory as necessary
Note that since memory mapping happens in 64KB blocks, it may be possible to read data outside of the partition provided to ``esp_partition_mmap``.

View file

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

View file

@ -0,0 +1,747 @@
// Copyright 2015-2019 Espressif Systems (Shanghai) PTE LTD
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
#include <stdlib.h>
#include <stdio.h>
#include <sys/param.h>
#include <string.h>
#include "spi_flash_chip_driver.h"
#include "memspi_host_driver.h"
#include "esp32/rom/spi_flash.h"
#include "esp_log.h"
#include "sdkconfig.h"
#include "esp_heap_caps.h"
static const char TAG[] = "spi_flash";
#define MAX_WRITE_CHUNK 8192 /* write in chunks */
#define MAX_READ_CHUNK 16384
#ifdef CONFIG_ESPTOOLPY_FLASHFREQ_80M
#define DEFAULT_FLASH_SPEED ESP_FLASH_80MHZ
#elif defined CONFIG_ESPTOOLPY_FLASHFREQ_40M
#define DEFAULT_FLASH_SPEED ESP_FLASH_40MHZ
#elif defined CONFIG_ESPTOOLPY_FLASHFREQ_26M
#define DEFAULT_FLASH_SPEED ESP_FLASH_26MHZ
#elif defined CONFIG_ESPTOOLPY_FLASHFREQ_20M
#define DEFAULT_FLASH_SPEED ESP_FLASH_20MHZ
#else
#error flash frequency not defined! check sdkconfig.h
#endif
#if defined(CONFIG_ESPTOOLPY_FLASHMODE_QIO)
#define DEFAULT_FLASH_MODE SPI_FLASH_QIO
#elif defined(CONFIG_ESPTOOLPY_FLASHMODE_QOUT)
#define DEFAULT_FLASH_MODE SPI_FLASH_QOUT
#elif defined(CONFIG_ESPTOOLPY_FLASHMODE_DIO)
#define DEFAULT_FLASH_MODE SPI_FLASH_DIO
#elif defined(CONFIG_ESPTOOLPY_FLASHMODE_DOUT)
#define DEFAULT_FLASH_MODE SPI_FLASH_DOUT
#else
#define DEFAULT_FLASH_MODE SPI_FLASH_FASTRD
#endif
#ifdef CONFIG_SPI_FLASH_DANGEROUS_WRITE_ABORTS
#define UNSAFE_WRITE_ADDRESS abort()
#else
#define UNSAFE_WRITE_ADDRESS return ESP_ERR_INVALID_ARG
#endif
/* CHECK_WRITE_ADDRESS macro to fail writes which land in the
bootloader, partition table, or running application region.
*/
#if CONFIG_SPI_FLASH_DANGEROUS_WRITE_ALLOWED
#define CHECK_WRITE_ADDRESS(CHIP, ADDR, SIZE)
#else /* FAILS or ABORTS */
#define CHECK_WRITE_ADDRESS(CHIP, ADDR, SIZE) do { \
if (CHIP && CHIP->host->region_protected && CHIP->host->region_protected(CHIP->host, ADDR, SIZE)) { \
UNSAFE_WRITE_ADDRESS; \
} \
} while(0)
#endif // CONFIG_SPI_FLASH_DANGEROUS_WRITE_ALLOWED
#define IO_STR_LEN 7
static const char io_mode_str[][IO_STR_LEN] = {
"slowrd",
"fastrd",
"dout",
"dio",
"qout",
"qio",
};
_Static_assert(sizeof(io_mode_str)/IO_STR_LEN == SPI_FLASH_READ_MODE_MAX, "the io_mode_str should be consistent with the esp_flash_read_mode_t defined in spi_flash_ll.h");
/* Static function to notify OS of a new SPI flash operation.
If returns an error result, caller must abort. If returns ESP_OK, caller must
call spiflash_end() before returning.
*/
static esp_err_t IRAM_ATTR spiflash_start(esp_flash_t *chip)
{
if (chip->os_func != NULL && chip->os_func->start != NULL) {
esp_err_t err = chip->os_func->start(chip->os_func_data);
if (err != ESP_OK) {
return err;
}
}
chip->host->dev_config(chip->host);
return ESP_OK;
}
/* Static function to notify OS that SPI flash operation is complete.
*/
static esp_err_t IRAM_ATTR spiflash_end(const esp_flash_t *chip, esp_err_t err)
{
if (chip->os_func != NULL
&& chip->os_func->end != NULL) {
esp_err_t end_err = chip->os_func->end(chip->os_func_data);
if (err == ESP_OK) {
err = end_err; // Only return the 'end' error if we haven't already failed
}
}
return err;
}
/* Return true if regions 'a' and 'b' overlap at all, based on their start offsets and lengths. */
inline static bool regions_overlap(uint32_t a_start, uint32_t a_len,uint32_t b_start, uint32_t b_len);
/* Top-level API functions, calling into chip_drv functions via chip->drv */
static esp_err_t detect_spi_flash_chip(esp_flash_t *chip);
bool esp_flash_chip_driver_initialized(const esp_flash_t *chip)
{
if (!chip->chip_drv) return false;
return true;
}
esp_err_t IRAM_ATTR esp_flash_init(esp_flash_t *chip)
{
esp_err_t err = ESP_OK;
if (chip == NULL || chip->host == NULL || chip->host->driver_data == NULL ||
((memspi_host_data_t*)chip->host->driver_data)->spi == NULL) {
return ESP_ERR_INVALID_ARG;
}
if (!esp_flash_chip_driver_initialized(chip)) {
// Detect chip_drv
err = detect_spi_flash_chip(chip);
if (err != ESP_OK) {
return err;
}
}
// Detect flash size
uint32_t size;
err = esp_flash_get_size(chip, &size);
if (err != ESP_OK) {
ESP_LOGE(TAG, "failed to get chip size");
return err;
}
ESP_LOGI(TAG, "flash io: %s", io_mode_str[chip->read_mode]);
err = spiflash_start(chip);
if (err != ESP_OK) {
return err;
}
if (err == ESP_OK) {
// Try to set the flash mode to whatever default mode was chosen
err = chip->chip_drv->set_read_mode(chip);
}
// Done: all fields on 'chip' are initialised
return spiflash_end(chip, err);
}
static esp_err_t IRAM_ATTR detect_spi_flash_chip(esp_flash_t *chip)
{
esp_err_t err;
uint32_t flash_id;
int retries = 10;
do {
err = spiflash_start(chip);
if (err != ESP_OK) {
return err;
}
// Send generic RDID command twice, check for a matching result and retry in case we just powered on (inner
// function fails if it sees all-ones or all-zeroes.)
err = chip->host->read_id(chip->host, &flash_id);
if (err == ESP_OK) { // check we see the same ID twice, in case of transient power-on errors
uint32_t new_id;
err = chip->host->read_id(chip->host, &new_id);
if (err == ESP_OK && (new_id != flash_id)) {
err = ESP_ERR_FLASH_NOT_INITIALISED;
}
}
err = spiflash_end(chip, err);
} while (err != ESP_OK && retries-- > 0);
// Detect the chip and set the chip_drv structure for it
const spi_flash_chip_t **drivers = esp_flash_registered_chips;
while (*drivers != NULL && !esp_flash_chip_driver_initialized(chip)) {
chip->chip_drv = *drivers;
// start/end SPI operation each time, for multitasking
// and also so esp_flash_registered_flash_drivers can live in flash
ESP_LOGD(TAG, "trying chip: %s", chip->chip_drv->name);
err = spiflash_start(chip);
if (err != ESP_OK) {
return err;
}
if (chip->chip_drv->probe(chip, flash_id) != ESP_OK) {
chip->chip_drv = NULL;
}
// if probe succeeded, chip->drv stays set
drivers++;
err = spiflash_end(chip, err);
if (err != ESP_OK) {
return err;
}
}
if (!esp_flash_chip_driver_initialized(chip)) {
return ESP_ERR_NOT_FOUND;
}
ESP_LOGI(TAG, "detected chip: %s", chip->chip_drv->name);
return ESP_OK;
}
// Convenience macro for beginning of all API functions,
// check that the 'chip' parameter is properly initialised
// and supports the operation in question
#define VERIFY_OP(OP) do { \
if (chip == NULL) { \
chip = esp_flash_default_chip; \
} \
if (chip == NULL || !esp_flash_chip_driver_initialized(chip)) { \
return ESP_ERR_FLASH_NOT_INITIALISED; \
} \
if (chip->chip_drv->OP == NULL) { \
return ESP_ERR_FLASH_UNSUPPORTED_CHIP; \
} \
} while (0)
esp_err_t IRAM_ATTR esp_flash_read_id(esp_flash_t *chip, uint32_t *id)
{
if (chip == NULL) {
chip = esp_flash_default_chip;
}
if (chip == NULL || !esp_flash_chip_driver_initialized(chip)) {
return ESP_ERR_FLASH_NOT_INITIALISED;
}
if (id == NULL) {
return ESP_ERR_INVALID_ARG;
}
esp_err_t err = spiflash_start(chip);
if (err != ESP_OK) {
return err;
}
err = chip->host->read_id(chip->host, id);
return spiflash_end(chip, err);
}
esp_err_t IRAM_ATTR esp_flash_get_size(esp_flash_t *chip, uint32_t *size)
{
VERIFY_OP(detect_size);
if (size == NULL) {
return ESP_ERR_INVALID_ARG;
}
if (chip->size != 0) {
*size = chip->size;
return ESP_OK;
}
esp_err_t err = spiflash_start(chip);
if (err != ESP_OK) {
return err;
}
uint32_t detect_size;
err = chip->chip_drv->detect_size(chip, &detect_size);
if (err == ESP_OK) {
chip->size = detect_size;
}
return spiflash_end(chip, err);
}
esp_err_t IRAM_ATTR esp_flash_erase_chip(esp_flash_t *chip)
{
VERIFY_OP(erase_chip);
CHECK_WRITE_ADDRESS(chip, 0, chip->size);
bool write_protect = false;
esp_err_t err = spiflash_start(chip);
if (err != ESP_OK) {
return err;
}
err = esp_flash_get_chip_write_protect(chip, &write_protect);
if (err == ESP_OK && write_protect) {
err = ESP_ERR_FLASH_PROTECTED;
}
if (err == ESP_OK) {
err = chip->chip_drv->erase_chip(chip);
}
return spiflash_end(chip, err);
}
esp_err_t IRAM_ATTR esp_flash_erase_region(esp_flash_t *chip, uint32_t start, uint32_t len)
{
VERIFY_OP(erase_sector);
VERIFY_OP(erase_block);
CHECK_WRITE_ADDRESS(chip, start, len);
uint32_t block_erase_size = chip->chip_drv->erase_block == NULL ? 0 : chip->chip_drv->block_erase_size;
uint32_t sector_size = chip->chip_drv->sector_size;
bool write_protect = false;
if (sector_size == 0 || (block_erase_size % sector_size) != 0) {
return ESP_ERR_FLASH_NOT_INITIALISED;
}
if (start > chip->size || start + len > chip->size) {
return ESP_ERR_INVALID_ARG;
}
if ((start % chip->chip_drv->sector_size) != 0 || (len % chip->chip_drv->sector_size) != 0) {
// Can only erase multiples of the sector size, starting at sector boundary
return ESP_ERR_INVALID_ARG;
}
esp_err_t err = spiflash_start(chip);
if (err != ESP_OK) {
return err;
}
// Check for write protection on whole chip
if (chip->chip_drv->get_chip_write_protect != NULL) {
err = chip->chip_drv->get_chip_write_protect(chip, &write_protect);
if (err == ESP_OK && write_protect) {
err = ESP_ERR_FLASH_PROTECTED;
}
}
// Check for write protected regions overlapping the erase region
if (err == ESP_OK && chip->chip_drv->get_protected_regions != NULL && chip->chip_drv->num_protectable_regions > 0) {
uint64_t protected = 0;
err = chip->chip_drv->get_protected_regions(chip, &protected);
if (err == ESP_OK && protected != 0) {
for (int i = 0; i < chip->chip_drv->num_protectable_regions && err == ESP_OK; i++) {
const esp_flash_region_t *region = &chip->chip_drv->protectable_regions[i];
if ((protected & BIT64(i))
&& regions_overlap(start, len, region->offset, region->size)) {
err = ESP_ERR_FLASH_PROTECTED;
}
}
}
}
// Don't lock the SPI flash for the entire erase, as this may be very long
err = spiflash_end(chip, err);
while (err == ESP_OK && len >= sector_size) {
err = spiflash_start(chip);
if (err != ESP_OK) {
return err;
}
// If possible erase an entire multi-sector block
if (block_erase_size > 0 && len >= block_erase_size && (start % block_erase_size) == 0) {
err = chip->chip_drv->erase_block(chip, start);
start += block_erase_size;
len -= block_erase_size;
}
else {
// Otherwise erase individual sector only
err = chip->chip_drv->erase_sector(chip, start);
start += sector_size;
len -= sector_size;
}
err = spiflash_end(chip, err);
}
return err;
}
esp_err_t IRAM_ATTR esp_flash_get_chip_write_protect(esp_flash_t *chip, bool *write_protected)
{
VERIFY_OP(get_chip_write_protect);
if (write_protected == NULL) {
return ESP_ERR_INVALID_ARG;
}
esp_err_t err = spiflash_start(chip);
if (err != ESP_OK) {
return err;
}
err = chip->chip_drv->get_chip_write_protect(chip, write_protected);
return spiflash_end(chip, err);
}
esp_err_t IRAM_ATTR esp_flash_set_chip_write_protect(esp_flash_t *chip, bool write_protect_chip)
{
VERIFY_OP(set_chip_write_protect);
//TODO: skip writing if already locked or unlocked
esp_err_t err = spiflash_start(chip);
if (err != ESP_OK) {
return err;
}
err = chip->chip_drv->set_chip_write_protect(chip, write_protect_chip);
return spiflash_end(chip, err);
}
esp_err_t esp_flash_get_protectable_regions(const esp_flash_t *chip, const esp_flash_region_t **regions, uint32_t *num_regions)
{
if(num_regions != NULL) {
*num_regions = 0; // In case caller doesn't check result
}
VERIFY_OP(get_protected_regions);
if(regions == NULL || num_regions == NULL) {
return ESP_ERR_INVALID_ARG;
}
*num_regions = chip->chip_drv->num_protectable_regions;
*regions = chip->chip_drv->protectable_regions;
return ESP_OK;
}
static esp_err_t find_region(const esp_flash_t *chip, const esp_flash_region_t *region, uint8_t *index)
{
if (region == NULL) {
return ESP_ERR_INVALID_ARG;
}
for(*index = 0; *index < chip->chip_drv->num_protectable_regions; (*index)++) {
if (memcmp(&chip->chip_drv->protectable_regions[*index],
region, sizeof(esp_flash_region_t)) == 0) {
return ESP_OK;
}
}
return ESP_ERR_NOT_FOUND;
}
esp_err_t IRAM_ATTR esp_flash_get_protected_region(esp_flash_t *chip, const esp_flash_region_t *region, bool *protected)
{
VERIFY_OP(get_protected_regions);
if (protected == NULL) {
return ESP_ERR_INVALID_ARG;
}
uint8_t index;
esp_err_t err = find_region(chip, region, &index);
if (err != ESP_OK) {
return err;
}
uint64_t protection_mask = 0;
err = spiflash_start(chip);
if (err != ESP_OK) {
return err;
}
err = chip->chip_drv->get_protected_regions(chip, &protection_mask);
if (err == ESP_OK) {
*protected = protection_mask & (1LL << index);
}
return spiflash_end(chip, err);
}
esp_err_t IRAM_ATTR esp_flash_set_protected_region(esp_flash_t *chip, const esp_flash_region_t *region, bool protected)
{
VERIFY_OP(set_protected_regions);
uint8_t index;
esp_err_t err = find_region(chip, region, &index);
if (err != ESP_OK) {
return err;
}
uint64_t protection_mask = 0;
err = spiflash_start(chip);
if (err != ESP_OK) {
return err;
}
err = chip->chip_drv->get_protected_regions(chip, &protection_mask);
if (err == ESP_OK) {
if (protected) {
protection_mask |= (1LL << index);
} else {
protection_mask &= ~(1LL << index);
}
err = chip->chip_drv->set_protected_regions(chip, protection_mask);
}
return spiflash_end(chip, err);
}
esp_err_t IRAM_ATTR esp_flash_read(esp_flash_t *chip, void *buffer, uint32_t address, uint32_t length)
{
if (length == 0) {
return ESP_OK;
}
VERIFY_OP(read);
if (buffer == NULL || address > chip->size || address+length > chip->size) {
return ESP_ERR_INVALID_ARG;
}
//when the cache is disabled, only the DRAM can be read, check whether we need to receive in another buffer in DRAM.
bool direct_read = chip->host->supports_direct_read(chip->host, buffer);
uint8_t* temp_buffer = NULL;
if (!direct_read) {
uint32_t length_to_allocate = MAX(MAX_READ_CHUNK, length);
length_to_allocate = (length_to_allocate+3)&(~3);
temp_buffer = heap_caps_malloc(length_to_allocate, MALLOC_CAP_INTERNAL | MALLOC_CAP_8BIT);
ESP_LOGV(TAG, "allocate temp buffer: %p", temp_buffer);
if (temp_buffer == NULL) return ESP_ERR_NO_MEM;
}
esp_err_t err = ESP_OK;
do {
err = spiflash_start(chip);
if (err != ESP_OK) {
break;
}
//if required (dma buffer allocated), read to the buffer instead of the original buffer
uint8_t* buffer_to_read = (temp_buffer)? temp_buffer : buffer;
//each time, we at most read this length
//after that, we release the lock to allow some other operations
uint32_t length_to_read = MIN(MAX_READ_CHUNK, length);
if (err == ESP_OK) {
err = chip->chip_drv->read(chip, buffer_to_read, address, length_to_read);
}
if (err != ESP_OK) {
spiflash_end(chip, err);
break;
}
//even if this is failed, the data is still valid, copy before quit
err = spiflash_end(chip, err);
//copy back to the original buffer
if (temp_buffer) {
memcpy(buffer, temp_buffer, length_to_read);
}
address += length_to_read;
length -= length_to_read;
buffer += length_to_read;
} while (err == ESP_OK && length > 0);
free(temp_buffer);
return err;
}
esp_err_t IRAM_ATTR esp_flash_write(esp_flash_t *chip, const void *buffer, uint32_t address, uint32_t length)
{
if (length == 0) {
return ESP_OK;
}
VERIFY_OP(write);
CHECK_WRITE_ADDRESS(chip, address, length);
if (buffer == NULL || address > chip->size || address+length > chip->size) {
return ESP_ERR_INVALID_ARG;
}
//when the cache is disabled, only the DRAM can be read, check whether we need to copy the data first
bool direct_write = chip->host->supports_direct_write(chip->host, buffer);
esp_err_t err = ESP_OK;
/* Write output in chunks, either by buffering on stack or
by artificially cutting into MAX_WRITE_CHUNK parts (in an OS
environment, this prevents writing from causing interrupt or higher priority task
starvation.) */
do {
uint32_t write_len;
const void *write_buf;
if (direct_write) {
write_len = MIN(length, MAX_WRITE_CHUNK);
write_buf = buffer;
} else {
uint32_t buf[8];
write_len = MIN(length, sizeof(buf));
memcpy(buf, buffer, write_len);
write_buf = buf;
}
err = spiflash_start(chip);
if (err != ESP_OK) {
return err;
}
err = chip->chip_drv->write(chip, write_buf, address, write_len);
address += write_len;
buffer = (void *)((intptr_t)buffer + write_len);
length -= write_len;
err = spiflash_end(chip, err);
} while (err == ESP_OK && length > 0);
return err;
}
esp_err_t IRAM_ATTR esp_flash_write_encrypted(esp_flash_t *chip, uint32_t address, const void *buffer, uint32_t length)
{
VERIFY_OP(write_encrypted);
if (((memspi_host_data_t*)chip->host->driver_data)->spi != 0) {
// Encrypted operations have to use SPI0
return ESP_ERR_FLASH_UNSUPPORTED_HOST;
}
if (buffer == NULL || address > chip->size || address+length > chip->size) {
return ESP_ERR_INVALID_ARG;
}
esp_err_t err = spiflash_start(chip);
if (err != ESP_OK) {
return err;
}
err = chip->chip_drv->write_encrypted(chip, buffer, address, length);
return spiflash_end(chip, err);
}
inline static IRAM_ATTR bool regions_overlap(uint32_t a_start, uint32_t a_len,uint32_t b_start, uint32_t b_len)
{
uint32_t a_end = a_start + a_len;
uint32_t b_end = b_start + b_len;
return (a_end > b_start && b_end > a_start);
}
#define ESP_FLASH_HOST_CONFIG_DEFAULT() (memspi_host_config_t){ \
.host_id = 1,\
.speed = DEFAULT_FLASH_SPEED, \
.cs_num = 0, \
.iomux = true, \
.input_delay_ns = 25,\
}
static DRAM_ATTR spi_flash_host_driver_t esp_flash_default_host_drv = ESP_FLASH_DEFAULT_HOST_DRIVER();
static DRAM_ATTR memspi_host_data_t default_driver_data;
/* The default (ie initial boot) no-OS ROM esp_flash_os_functions_t */
extern const esp_flash_os_functions_t esp_flash_noos_functions;
static DRAM_ATTR esp_flash_t default_chip = {
.read_mode = DEFAULT_FLASH_MODE,
.host = &esp_flash_default_host_drv,
.os_func = &esp_flash_noos_functions,
};
esp_flash_t *esp_flash_default_chip = &default_chip;
esp_err_t esp_flash_init_default_chip()
{
memspi_host_config_t cfg = ESP_FLASH_HOST_CONFIG_DEFAULT();
//the host is already initialized, only do init for the data and load it to the host
spi_flash_hal_init(&default_driver_data, &cfg);
default_chip.host->driver_data = &default_driver_data;
// ROM TODO: account for non-standard default pins in efuse
// ROM TODO: to account for chips which are slow to power on, maybe keep probing in a loop here
esp_err_t err = esp_flash_init(&default_chip);
if (err != ESP_OK) {
return err;
}
if (default_chip.size < g_rom_flashchip.chip_size) {
ESP_EARLY_LOGE(TAG, "detected size(%dk) smaller than the size in the binary image header(%dk). probe failed.", default_chip.size/1024, g_rom_flashchip.chip_size/1024);
return ESP_ERR_FLASH_SIZE_NOT_MATCH;
} else if (default_chip.size > g_rom_flashchip.chip_size) {
ESP_EARLY_LOGW(TAG, "detected size larger than the size in the binary image header. use the size in the binary image header.");
default_chip.size = g_rom_flashchip.chip_size;
}
default_chip.size = g_rom_flashchip.chip_size;
esp_flash_default_chip = &default_chip;
return ESP_OK;
}
/*------------------------------------------------------------------------------
Adapter layer to original api before IDF v4.0
------------------------------------------------------------------------------*/
static esp_err_t spi_flash_translate_rc(esp_err_t err)
{
switch (err) {
case ESP_OK:
return ESP_OK;
case ESP_ERR_INVALID_ARG:
return ESP_ERR_INVALID_ARG;
case ESP_ERR_FLASH_NOT_INITIALISED:
case ESP_ERR_FLASH_PROTECTED:
return ESP_ERR_INVALID_STATE;
case ESP_ERR_NOT_FOUND:
case ESP_ERR_FLASH_UNSUPPORTED_HOST:
case ESP_ERR_FLASH_UNSUPPORTED_CHIP:
return ESP_ERR_NOT_SUPPORTED;
case ESP_ERR_FLASH_NO_RESPONSE:
return ESP_ERR_INVALID_RESPONSE;
default:
ESP_EARLY_LOGE(TAG, "unexpected spi flash error code: %x", err);
abort();
}
return ESP_OK;
}
#ifndef CONFIG_SPI_FLASH_USE_LEGACY_IMPL
esp_err_t spi_flash_erase_range(uint32_t start_addr, uint32_t size)
{
esp_err_t err = esp_flash_erase_region(NULL, start_addr, size);
return spi_flash_translate_rc(err);
}
esp_err_t spi_flash_write(size_t dst, const void *srcv, size_t size)
{
esp_err_t err = esp_flash_write(NULL, srcv, dst, size);
return spi_flash_translate_rc(err);
//CHECK_WRITE_ADDRESS(dst, size);
}
esp_err_t spi_flash_read(size_t src, void *dstv, size_t size)
{
esp_err_t err = esp_flash_read(NULL, dstv, src, size);
return spi_flash_translate_rc(err);
}
esp_err_t spi_flash_unlock()
{
esp_err_t err = esp_flash_set_chip_write_protect(NULL, false);
return spi_flash_translate_rc(err);
}
#endif

View file

@ -33,8 +33,8 @@
#include "esp_log.h"
#include "esp32/clk.h"
#include "esp_flash_partitions.h"
#include "esp_ota_ops.h"
#include "cache_utils.h"
#include "esp_flash.h"
/* bytes erased by SPIEraseBlock() ROM function */
#define BLOCK_ERASE_SIZE 65536
@ -116,20 +116,10 @@ static const spi_flash_guard_funcs_t *s_flash_guard_ops;
static __attribute__((unused)) bool is_safe_write_address(size_t addr, size_t size)
{
bool result = true;
if (addr <= ESP_PARTITION_TABLE_OFFSET + ESP_PARTITION_TABLE_MAX_LEN) {
if (!esp_partition_main_flash_region_safe(addr, size)) {
UNSAFE_WRITE_ADDRESS;
}
const esp_partition_t *p = esp_ota_get_running_partition();
if (addr >= p->address && addr < p->address + p->size) {
UNSAFE_WRITE_ADDRESS;
}
if (addr < p->address && addr + size > p->address) {
UNSAFE_WRITE_ADDRESS;
}
return result;
return true;
}
@ -184,6 +174,7 @@ static inline void IRAM_ATTR spi_flash_guard_op_unlock()
}
}
#ifdef CONFIG_SPI_FLASH_USE_LEGACY_IMPL
static esp_rom_spiflash_result_t IRAM_ATTR spi_flash_unlock()
{
static bool unlocked = false;
@ -198,6 +189,16 @@ static esp_rom_spiflash_result_t IRAM_ATTR spi_flash_unlock()
}
return ESP_ROM_SPIFLASH_RESULT_OK;
}
#else
static esp_rom_spiflash_result_t IRAM_ATTR spi_flash_unlock()
{
esp_err_t err = esp_flash_set_chip_write_protect(NULL, false);
if (err != ESP_OK) {
return ESP_ROM_SPIFLASH_RESULT_ERR;
}
return ESP_ROM_SPIFLASH_RESULT_OK;
}
#endif
esp_err_t IRAM_ATTR spi_flash_erase_sector(size_t sec)
{
@ -205,6 +206,8 @@ esp_err_t IRAM_ATTR spi_flash_erase_sector(size_t sec)
return spi_flash_erase_range(sec * SPI_FLASH_SEC_SIZE, SPI_FLASH_SEC_SIZE);
}
#ifdef CONFIG_SPI_FLASH_USE_LEGACY_IMPL
//deprecated, only used in compatible mode
esp_err_t IRAM_ATTR spi_flash_erase_range(uint32_t start_addr, uint32_t size)
{
CHECK_WRITE_ADDRESS(start_addr, size);
@ -416,6 +419,7 @@ out:
return spi_flash_translate_rc(rc);
}
#endif
esp_err_t IRAM_ATTR spi_flash_write_encrypted(size_t dest_addr, const void *src, size_t size)
{
@ -483,6 +487,7 @@ esp_err_t IRAM_ATTR spi_flash_write_encrypted(size_t dest_addr, const void *src,
return spi_flash_translate_rc(rc);
}
#ifdef CONFIG_SPI_FLASH_USE_LEGACY_IMPL
esp_err_t IRAM_ATTR spi_flash_read(size_t src, void *dstv, size_t size)
{
// Out of bound reads are checked in ROM code, but we can give better
@ -624,6 +629,7 @@ out:
COUNTER_STOP(read);
return spi_flash_translate_rc(rc);
}
#endif
esp_err_t IRAM_ATTR spi_flash_read_encrypted(size_t src, void *dstv, size_t size)
{

View file

@ -1,31 +1,28 @@
// Copyright 2015-2019 Espressif Systems (Shanghai) PTE LTD
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
#pragma once
#include "esp_err.h"
#include <stdint.h>
#include <stdbool.h>
#include "soc/spi_struct.h"
#include "hal/spi_flash_host_drv.h"
struct esp_flash_driver;
struct spi_flash_chip_t;
typedef struct spi_flash_chip_t spi_flash_chip_t;
/** @brief Mode used for reading from SPI flash */
typedef enum {
ESP_FLASH_QIO, ///< Both address & data transferred using quad I/O
ESP_FLASH_QOUT, ///< Data read using quad I/O
ESP_FLASH_DIO, ///< Both address & data transferred using dual I/O
ESP_FLASH_DOUT, ///< Data read using dual I/O
ESP_FLASH_FASTRD, ///< Data read using single I/O, no limit on speed
ESP_FLASH_SLOWRD, ///< Data read using single I/O, some limits on speed
ESP_FLASH_READ_MODE_MAX,
} esp_flash_read_mode_t;
/** @brief Configured SPI flash clock speed */
typedef enum {
ESP_FLASH_80MHZ,
ESP_FLASH_40MHZ,
ESP_FLASH_26MHZ,
ESP_FLASH_20MHZ,
ESP_FLASH_SPEED_MAX,
} esp_flash_speed_t;
typedef struct esp_flash_t esp_flash_t;
/** @brief Structure for describing a region of flash */
typedef struct {
@ -33,56 +30,60 @@ typedef struct {
uint32_t size;
} esp_flash_region_t;
// TODO this is copied from SPI driver, should be unified somehow
/* OS-level integration hooks for accessing flash chips inside a running OS */
typedef struct {
int mosi_io_num; ///< GPIO pin for Master Out Slave In (=spi_d) signal
int miso_io_num; ///< GPIO pin for Master In Slave Out (=spi_q) signal
int sclk_io_num; ///< GPIO pin for Spi CLocK signal
int quadwp_io_num; ///< GPIO pin for WP (Write Protect) signal which is used as D2 in 4-bit communication modes, or -1 if not used.
int quadhd_io_num; ///< GPIO pin for HD (HolD) signal which is used as D3 in 4-bit communication modes, or -1 if not used.
} esp_flash_pin_cfg_t;
/**
* Called before commencing any flash operation. Does not need to be
* recursive (ie is called at most once for each call to 'end').
*/
esp_err_t (*start)(void *arg);
/** Called after completing any flash operation. */
esp_err_t (*end)(void *arg);
/** Delay for at least 'ms' milliseconds. Called in between 'start' and 'end'. */
esp_err_t (*delay_ms)(void *arg, unsigned ms);
} esp_flash_os_functions_t;
/** @brief Structure to describe a SPI flash chip connected to the system.
Structure must be passed to esp_flash_init() before use.
*/
typedef struct {
spi_dev_t *spi; ///< Pointer to hardware SPI bus registers used for connection (SP1, SPI2 or SPI3). Set before initialisation.
esp_flash_speed_t speed; ///< Configured SPI flash clock speed. Set before initialisation.
struct esp_flash_t {
const spi_flash_chip_t *chip_drv; ///< Pointer to chip-model-specific "adpater" structure. If NULL, will be detected during initialisatiopn.
spi_flash_host_driver_t *host; ///< Pointer to hardware-specific "host_driver" structure.
const esp_flash_os_functions_t *os_func; ///< Pointer to os-specific hooker strcuture.
void *os_func_data; ///< Pointer to argument for os-specific hooker.
esp_flash_read_mode_t read_mode; ///< Configured SPI flash read mode. Set before initialisation.
uint32_t size; ///< Size of SPI flash in bytes. If 0, size will be detected during initialisation.
const struct esp_flash_driver *drv; ///< Pointer to chip-model-specific "driver" structure. If NULL, will be detected during initialisatiopn.
const esp_flash_pin_cfg_t *pins; ///< Pin configuration for the chip
};
void *driver_data; ///< Currently unused, allows drivers to store driver-implementation-specific data on initialisation
} esp_flash_chip_t;
/** @brief Possible errors returned from SPI flash low-level API */
typedef enum {
FLASH_OK = 0, ///< Success
FLASH_ERR_NOT_INITIALISED, ///< esp_flash_chip_t structure not correctly initialised by esp_flash_init().
FLASH_ERR_INVALID_ARG, ///< A supplied argument was invalid.
FLASH_ERR_NOT_FOUND, ///< A requested value is not found.
FLASH_ERR_NO_RESPONSE, ///< Chip did not respond to the command, or timed out.
FLASH_ERR_UNSUPPORTED_HOST, ///< Requested operation isn't supported via this host SPI bus (chip->spi field).
FLASH_ERR_UNSUPPORTED_CHIP, ///< Requested operation isn't supported by this model of SPI flash chip.
FLASH_ERR_PROTECTED, ///< Write operation failed due to chip's write protection being enabled.
} esp_flash_err_t;
/** @brief Initialise SPI flash chip interface.
*
*
* This function must be called before any other API functions are called for this chip.
*
* @note Only the spi, speed & read_mode fields of the chip structure need to be initialised. Other fields will be auto-detected
* if left set to zero or NULL.
*
* @note If the chip->drv pointer is NULL, chip driver will be autodetected based on its manufacturer & product IDs. See
* @note If the chip->drv pointer is NULL, chip chip_drv will be autodetected based on its manufacturer & product IDs. See
* esp_flash_registered_flash_drivers pointer for details of this process.
*
* @param chip Pointer to SPI flash chip to use. If NULL, esp_flash_default_chip is substituted.
* @return FLASH_OK on success, or a flash error code if initialisation fails.
* @return ESP_OK on success, or a flash error code if initialisation fails.
*/
esp_flash_err_t esp_flash_init(esp_flash_chip_t *chip);
esp_err_t esp_flash_init(esp_flash_t *chip);
/**
* Check if appropriate chip driver is set.
*
* @param chip Pointer to SPI flash chip to use. If NULL, esp_flash_default_chip is substituted.
*
* @return true if set, otherwise false.
*/
bool esp_flash_chip_driver_initialized(const esp_flash_t *chip);
/** @brief Read flash ID via the common "RDID" SPI flash command.
*
@ -91,9 +92,9 @@ esp_flash_err_t esp_flash_init(esp_flash_chip_t *chip);
*
* ID is a 24-bit value. Lower 16 bits of 'id' are the chip ID, upper 8 bits are the manufacturer ID.
*
* @return FLASH_OK on success, or a flash error code if operation failed.
* @return ESP_OK on success, or a flash error code if operation failed.
*/
esp_flash_err_t esp_flash_read_id(const esp_flash_chip_t *chip, uint32_t *id);
esp_err_t esp_flash_read_id(esp_flash_t *chip, uint32_t *id);
/** @brief Detect flash size based on flash ID.
*
@ -103,18 +104,18 @@ esp_flash_err_t esp_flash_read_id(const esp_flash_chip_t *chip, uint32_t *id);
* @note Most flash chips use a common format for flash ID, where the lower 4 bits specify the size as a power of 2. If
* the manufacturer doesn't follow this convention, the size may be incorrectly detected.
*
* @return FLASH_OK on success, or a flash error code if operation failed.
* @return ESP_OK on success, or a flash error code if operation failed.
*/
esp_flash_err_t esp_flash_detect_size(const esp_flash_chip_t *chip, uint32_t *size);
esp_err_t esp_flash_get_size(esp_flash_t *chip, uint32_t *size);
/** @brief Erase flash chip contents
*
* @param chip Pointer to identify flash chip. Must have been successfully initialised via esp_flash_init()
*
*
* @return FLASH_OK on success, or a flash error code if operation failed.
* @return ESP_OK on success, or a flash error code if operation failed.
*/
esp_flash_err_t esp_flash_erase_chip(const esp_flash_chip_t *chip);
esp_err_t esp_flash_erase_chip(esp_flash_t *chip);
/** @brief Erase a region of the flash chip
*
@ -122,45 +123,45 @@ esp_flash_err_t esp_flash_erase_chip(const esp_flash_chip_t *chip);
* @param start Address to start erasing flash. Must be sector aligned.
* @param len Length of region to erase. Must also be sector aligned.
*
* Sector size is specifyed in chip->drv->sector_size field (typically 4096 bytes.) FLASH_ERR_INVALID_ARG will be
* Sector size is specifyed in chip->drv->sector_size field (typically 4096 bytes.) ESP_ERR_INVALID_ARG will be
* returned if the start & length are not a multiple of this size.
*
* Erase is performed using block (multi-sector) erases where possible (block size is specified in
* chip->drv->block_erase_size field, typically 65536 bytes). Remaining sectors are erased using individual sector erase
* commands.
*
* @return FLASH_OK on success, or a flash error code if operation failed.
* @return ESP_OK on success, or a flash error code if operation failed.
*/
esp_flash_err_t esp_flash_erase_region(const esp_flash_chip_t *chip, uint32_t start, uint32_t len);
esp_err_t esp_flash_erase_region(esp_flash_t *chip, uint32_t start, uint32_t len);
/** @brief Read if the entire chip is write protected
*
* @param chip Pointer to identify flash chip. Must have been successfully initialised via esp_flash_init()
* @param[out] write_protected Pointer to boolean, set to the value of the write protect flag.
*
* @note A correct result for this flag depends on the SPI flash chip model and driver in use (via the 'chip->drv'
* @note A correct result for this flag depends on the SPI flash chip model and chip_drv in use (via the 'chip->drv'
* field).
*
* @return FLASH_OK on success, or a flash error code if operation failed.
* @return ESP_OK on success, or a flash error code if operation failed.
*/
esp_flash_err_t esp_flash_get_chip_write_protect(const esp_flash_chip_t *chip, bool *write_protected);
esp_err_t esp_flash_get_chip_write_protect(esp_flash_t *chip, bool *write_protected);
/** @brief Set write protection for the SPI flash chip
*
* @param chip Pointer to identify flash chip. Must have been successfully initialised via esp_flash_init()
* @param write_protected Boolean value for the write protect flag
*
* @note Correct behaviour of this function depends on the SPI flash chip model and driver in use (via the 'chip->drv'
* @note Correct behaviour of this function depends on the SPI flash chip model and chip_drv in use (via the 'chip->drv'
* field).
*
* If write protection is enabled, destructive operations will fail with FLASH_ERR_PROTECTED.
* If write protection is enabled, destructive operations will fail with ESP_ERR_FLASH_PROTECTED.
*
* Some SPI flash chips may require a power cycle before write protect status can be cleared. Otherwise,
* write protection can be removed via a follow-up call to this function.
*
* @return FLASH_OK on success, or a flash error code if operation failed.
* @return ESP_OK on success, or a flash error code if operation failed.
*/
esp_flash_err_t esp_flash_set_chip_write_protect(const esp_flash_chip_t *chip, bool write_protect_chip);
esp_err_t esp_flash_set_chip_write_protect(esp_flash_t *chip, bool write_protect_chip);
/** @brief Read the list of individually protectable regions of this SPI flash chip.
@ -169,12 +170,13 @@ esp_flash_err_t esp_flash_set_chip_write_protect(const esp_flash_chip_t *chip, b
* @param regions[out] Pointer to receive a pointer to the array of protectable regions of the chip.
* @param[out] Pointer to an integer receiving the count of protectable regions in the array returned in 'regions'.
*
* @note Correct behaviour of this function depends on the SPI flash chip model and driver in use (via the 'chip->drv'
* @note Correct behaviour of this function depends on the SPI flash chip model and chip_drv in use (via the 'chip->drv'
* field).
*
* @return FLASH_OK on success, or a flash error code if operation failed.
* @return ESP_OK on success, or a flash error code if operation failed.
*/
esp_flash_err_t esp_flash_get_protectable_regions(const esp_flash_chip_t *chip, const esp_flash_region_t **regions, uint32_t *num_regions);
esp_err_t
esp_flash_get_protectable_regions(const esp_flash_t *chip, const esp_flash_region_t **regions, uint32_t *num_regions);
/** @brief Detect if a region of the SPI flash chip is protected
@ -185,12 +187,12 @@ esp_flash_err_t esp_flash_get_protectable_regions(const esp_flash_chip_t *chip,
*
* @note It is possible for this result to be false and write operations to still fail, if protection is enabled for the entire chip.
*
* @note Correct behaviour of this function depends on the SPI flash chip model and driver in use (via the 'chip->drv'
* @note Correct behaviour of this function depends on the SPI flash chip model and chip_drv in use (via the 'chip->drv'
* field).
*
* @return FLASH_OK on success, or a flash error code if operation failed.
* @return ESP_OK on success, or a flash error code if operation failed.
*/
esp_flash_err_t esp_flash_get_protected_region(const esp_flash_chip_t *chip, const esp_flash_region_t *region, bool *protected);
esp_err_t esp_flash_get_protected_region(esp_flash_t *chip, const esp_flash_region_t *region, bool *protected);
/** @brief Update the protected status for a region of the SPI flash chip
*
@ -200,17 +202,17 @@ esp_flash_err_t esp_flash_get_protected_region(const esp_flash_chip_t *chip, con
*
* @note It is possible for the region protection flag to be cleared and write operations to still fail, if protection is enabled for the entire chip.
*
* @note Correct behaviour of this function depends on the SPI flash chip model and driver in use (via the 'chip->drv'
* @note Correct behaviour of this function depends on the SPI flash chip model and chip_drv in use (via the 'chip->drv'
* field).
*
* @return FLASH_OK on success, or a flash error code if operation failed.
* @return ESP_OK on success, or a flash error code if operation failed.
*/
esp_flash_err_t esp_flash_set_protected_region(const esp_flash_chip_t *chip, const esp_flash_region_t *region, bool protected);
esp_err_t esp_flash_set_protected_region(esp_flash_t *chip, const esp_flash_region_t *region, bool protected);
/** @brief Read data from the SPI flash chip
*
* @param chip Pointer to identify flash chip. Must have been successfully initialised via esp_flash_init()
* @param buffer Pointer to a buffer where the data will be read.
* @param buffer Pointer to a buffer where the data will be read. To get better performance, this should be in the DRAM and word aligned.
* @param address Address on flash to read from. Must be less than chip->size field.
* @param length Length (in bytes) of data to read.
*
@ -219,22 +221,26 @@ esp_flash_err_t esp_flash_set_protected_region(const esp_flash_chip_t *chip, con
* @note If on-chip flash encryption is used, this function returns raw (ie encrypted) data. Use the flash cache
* to transparently decrypt data.
*
* @return FLASH_OK on success, or a flash error code if operation failed.
* @return
* - ESP_OK: success
* - ESP_ERR_NO_MEM: the buffer is not valid, however failed to malloc on
* the heap.
* - or a flash error code if operation failed.
*/
esp_flash_err_t esp_flash_read(const esp_flash_chip_t *chip, void *buffer, uint32_t address, uint32_t length);
esp_err_t esp_flash_read(esp_flash_t *chip, void *buffer, uint32_t address, uint32_t length);
/** @brief Write data to the SPI flash chip
*
* @param chip Pointer to identify flash chip. Must have been successfully initialised via esp_flash_init()
* @param address Address on flash to write to. Must be previously erased (SPI NOR flash can only write bits 1->0).
* @param buffer Pointer to a buffer with the data to write.
* @param buffer Pointer to a buffer with the data to write. To get better performance, this should be in the DRAM and word aligned.
* @param length Length (in bytes) of data to write.
*
* There are no alignment constraints on buffer, address or length.
*
* @return FLASH_OK on success, or a flash error code if operation failed.
* @return ESP_OK on success, or a flash error code if operation failed.
*/
esp_flash_err_t esp_flash_write(const esp_flash_chip_t *chip, uint32_t address, const void *buffer, uint32_t length);
esp_err_t esp_flash_write(esp_flash_t *chip, const void *buffer, uint32_t address, uint32_t length);
/** @brief Encrypted and write data to the SPI flash chip using on-chip hardware flash encryption
*
@ -245,21 +251,44 @@ esp_flash_err_t esp_flash_write(const esp_flash_chip_t *chip, uint32_t address,
*
* @note Both address & length must be 16 byte aligned, as this is the encryption block size
*
* @return FLASH_OK on success, or a flash error code if operation failed.
* @return ESP_OK on success, or a flash error code if operation failed.
*/
esp_flash_err_t esp_flash_write_encrypted(const esp_flash_chip_t *chip, uint32_t address, const void *buffer, uint32_t length);
esp_err_t esp_flash_write_encrypted(esp_flash_t *chip, uint32_t address, const void *buffer, uint32_t length);
/** @brief Pointer to the "default" SPI flash chip, ie the main chip attached to the MCU.
This chip is used if the 'chip' argument pass to esp_flash_xxx API functions is ever NULL.
*/
extern const esp_flash_chip_t *esp_flash_default_chip;
extern esp_flash_t *esp_flash_default_chip;
/** @brief Initialise the default SPI flash chip
*
* Called by OS startup code. You do not need to call this in your own applications.
*/
esp_flash_err_t esp_flash_init_default_chip();
esp_err_t esp_flash_init_default_chip();
/** Enable OS-level SPI flash protections in IDF */
void esp_flash_low_level_app_init(); /* ROM TODO move this to IDF */
/**
* Enable OS-level SPI flash protections in IDF
*
* @return ESP_OK if success, otherwise failed. See return value of ``esp_flash_init_os_functions``.
*/
esp_err_t esp_flash_app_init(); /* ROM TODO move this to IDF */
/**
* Enable OS-level SPI flash for a specific chip.
*
* @param chip The chip to init os functions.
* @param host_id Which SPI host to use, 0 for SPI1, 1 for HSPI2 and 2 for VSPI.
*
* @return ESP_OK if success, otherwise failed. See return value of ``esp_flash_init_os_functions``.
*/
esp_err_t esp_flash_init_os_functions(esp_flash_t *chip, int host_id);
/* The default (ie initial boot) no-OS ROM esp_flash_os_functions_t */
extern const esp_flash_os_functions_t spi1_default_os_functions; //todo: put into non-ROM headers
/* Pointer to the current esp_flash_os_functions_t structure in use.
Can be changed at runtime to reflect different running conditions.
*/
//extern const esp_flash_os_functions_t *os_func;

View file

@ -25,7 +25,6 @@
extern "C" {
#endif
#define ESP_ERR_FLASH_BASE 0x10010
#define ESP_ERR_FLASH_OP_FAIL (ESP_ERR_FLASH_BASE + 1)
#define ESP_ERR_FLASH_OP_TIMEOUT (ESP_ERR_FLASH_BASE + 2)
@ -36,7 +35,7 @@ extern "C" {
/**
* @brief Initialize SPI flash access driver
*
* This function must be called exactly once, before any other
* This function must be called exactly once, before any other
* spi_flash_* functions are called.
* Currently this function is called from startup code. There is
* no need to call it from application code.

View file

@ -0,0 +1,111 @@
// Copyright 2015-2019 Espressif Systems (Shanghai) PTE LTD
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
#pragma once
#include "hal/spi_flash_hal.h"
/** Default configuration for the memspi (high speed version) */
#define ESP_FLASH_DEFAULT_HOST_DRIVER() (spi_flash_host_driver_t) { \
.dev_config = spi_flash_hal_device_config, \
.common_command = spi_flash_hal_common_command, \
.read_id = memspi_host_read_id_hs, \
.erase_chip = spi_flash_hal_erase_chip, \
.erase_sector = spi_flash_hal_erase_sector, \
.erase_block = spi_flash_hal_erase_block, \
.read_status = memspi_host_read_status_hs, \
.set_write_protect = spi_flash_hal_set_write_protect, \
.supports_direct_write = spi_flash_hal_supports_direct_write, \
.supports_direct_read = spi_flash_hal_supports_direct_read, \
.program_page = spi_flash_hal_program_page, \
.max_write_bytes = SPI_FLASH_HAL_MAX_WRITE_BYTES, \
.read = spi_flash_hal_read, \
.max_read_bytes = SPI_FLASH_HAL_MAX_READ_BYTES, \
.host_idle = spi_flash_hal_host_idle, \
.configure_host_read_mode = spi_flash_hal_configure_host_read_mode, \
.poll_cmd_done = spi_flash_hal_poll_cmd_done, \
.flush_cache = memspi_host_flush_cache, \
.region_protected = memspi_region_protected, \
}
/// configuration for the memspi host
typedef spi_flash_memspi_config_t memspi_host_config_t;
/// context for the memspi host
typedef spi_flash_memspi_data_t memspi_host_data_t;
/**
* Initialize the memory SPI host.
*
* @param host Pointer to the host structure.
* @param data Pointer to allocated space to hold the context of host driver.
* @param cfg Pointer to configuration structure
*
* @return always return ESP_OK
*/
esp_err_t memspi_host_init_pointers(spi_flash_host_driver_t *host, memspi_host_data_t *data, const memspi_host_config_t *cfg);
/*******************************************************************************
* NOTICE
* Rest part of this file are part of the HAL layer
* The HAL is not public api, don't use in application code.
* See readme.md in soc/include/hal/readme.md
******************************************************************************/
/**
* High speed implementation of RDID through memspi interface relying on the
* ``common_command``.
*
* @param driver The driver context.
* @param id Output of the read ID from the slave.
*
* @return
* - ESP_OK: if success
* - ESP_ERR_FLASH_NO_RESPONSE: if no response from chip
* - or other cases from ``spi_hal_common_command``
*/
esp_err_t memspi_host_read_id_hs(spi_flash_host_driver_t *driver, uint32_t *id);
/**
* High speed implementation of RDSR through memspi interface relying on the
* ``common_command``.
*
* @param driver The driver context.
* @param id Output of the read ID from the slave.
*
* @return
* - ESP_OK: if success
* - or other cases from ``spi_hal_common_command``
*/
esp_err_t memspi_host_read_status_hs(spi_flash_host_driver_t *driver, uint8_t *out_sr);
/**
* Flush the cache (if needed) after the contents are modified.
*
* @param driver The driver context.
* @param addr Start address of the modified region
* @param size Size of the region modified.
*
* @return always ESP_OK.
*/
esp_err_t memspi_host_flush_cache(spi_flash_host_driver_t* driver, uint32_t addr, uint32_t size);
/**
* Check if the given region is protected.
*
* @param driver The driver context.
* @param addr Start address of the region.
* @param size Size of the region to check.
*
* @return true if protected, otherwise false.
*/
bool memspi_region_protected(spi_flash_host_driver_t* driver, uint32_t addr, uint32_t size);

View file

@ -1,23 +1,43 @@
#pragma once
#include "spi_flash_lowlevel.h"
// Copyright 2015-2019 Espressif Systems (Shanghai) PTE LTD
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
/** @brief SPI flash driver definition structure.
#pragma once
#include "esp_flash.h"
struct esp_flash_t;
typedef struct esp_flash_t esp_flash_t;
typedef struct spi_flash_chip_t spi_flash_chip_t;
/** @brief SPI flash chip driver definition structure.
*
* The driver structure contains chip-specific pointers to functions to perform SPI flash operations, and some
* The chip driver structure contains chip-specific pointers to functions to perform SPI flash operations, and some
* chip-specific numeric values.
*
* @note This is not a public API. Driver-specific functions are called from the public API (declared in
* spi_flash_lowlevel.h). They assume the caller has already validated arguments and enabled relevant protections
* @note This is not a public API. These functions are called from the public API (declared in
* esp_flash.h). They assume the caller has already validated arguments and enabled relevant protections
* (disabling flash cache, prevent concurrent SPI access, etc.)
*
* Do not call driver functions directly in other contexts.
* Do not call chip driver functions directly in other contexts.
*
* A generic driver and it's related operations are defined in spi_flash_lowlevel_generic.h which can be used as
* building blocks for written new/specific SPI flash chip drivers.
* A generic driver for generic chips and its related operations are defined in
* spi_flash_chip_generic.h which can be used as building blocks for written
* new/specific SPI flash chip drivers.
*
* @note All of these functions may be called with SPI flash cache disabled, so must only ever access IRAM/DRAM/ROM.
*/
typedef struct esp_flash_driver {
struct spi_flash_chip_t {
const char *name; ///< Name of the chip driver
/* Probe to detect if a supported SPI flash chip is found.
*
* Attempts to configure 'chip' with these operations and probes for a matching SPI flash chip.
@ -31,28 +51,25 @@ typedef struct esp_flash_driver {
* It is permissible for the driver to modify the 'chip' structure if probing succeeds (specifically, to assign something to the
* driver_data pointer if that is useful for the driver.)
*
* @return FLASH_OK if probing was successful, an error otherwise. Driver may
* assume that returning FLASH_OK means it has claimed this chip.
* @return ESP_OK if probing was successful, an error otherwise. Driver may
* assume that returning ESP_OK means it has claimed this chip.
*/
esp_flash_err_t (*probe)(esp_flash_chip_t *chip, uint32_t flash_id);
esp_err_t (*probe)(esp_flash_t *chip, uint32_t flash_id);
esp_err_t (*reset)(esp_flash_t *chip);
/* Read SPI flash ID
*
* Sends RDID (or an equivalent command) to the device.
*/
esp_flash_err_t (*read_id)(const esp_flash_chip_t *chip, uint32_t *id);
/* Detect SPI flash size
*
* Interrogate the chip to detect it's size.
* Interrogate the chip to detect its size.
*/
esp_flash_err_t (*detect_size)(const esp_flash_chip_t *chip, uint32_t *size);
esp_err_t (*detect_size)(esp_flash_t *chip, uint32_t *size);
/* Erase the entire chip
Caller has verified the chip is not write protected.
*/
esp_flash_err_t (*erase_chip)(const esp_flash_chip_t *chip);
esp_err_t (*erase_chip)(esp_flash_t *chip);
/* Erase a sector of the chip. Sector size is specified in the 'sector_size' field.
@ -60,23 +77,23 @@ typedef struct esp_flash_driver {
Caller has verified that this sector should be non-write-protected.
*/
esp_flash_err_t (*erase_sector)(const esp_flash_chip_t *chip, uint32_t sector_address);
esp_err_t (*erase_sector)(esp_flash_t *chip, uint32_t sector_address);
/* Erase a multi-sector block of the chip. Block size is specified in the 'block_erase_size' field.
sector_address is an offset in bytes.
Caller has verified that this block should be non-write-protected.
*/
esp_flash_err_t (*erase_block)(const esp_flash_chip_t *chip, uint32_t block_address);
esp_err_t (*erase_block)(esp_flash_t *chip, uint32_t block_address);
uint32_t sector_size; /* Sector is minimum erase size */
uint32_t block_erase_size; /* Optimal (fastest) block size for multi-sector erases on this chip */
/* Read the write protect status of the entire chip. */
esp_flash_err_t (*get_chip_write_protect)(const esp_flash_chip_t *chip, bool *write_protected);
esp_err_t (*get_chip_write_protect)(esp_flash_t *chip, bool *write_protected);
/* Set the write protect status of the entire chip. */
esp_flash_err_t (*set_chip_write_protect)(const esp_flash_chip_t *chip, bool write_protect_chip);
esp_err_t (*set_chip_write_protect)(esp_flash_t *chip, bool write_protect_chip);
/* Number of individually write protectable regions on this chip. Range 0-63. */
uint8_t num_protectable_regions;
@ -84,20 +101,20 @@ typedef struct esp_flash_driver {
const esp_flash_region_t *protectable_regions;
/* Get a bitmask describing all protectable regions on the chip. Each bit represents one entry in the
protectable_regions array, ie bit (1<<N) is set then the region at array entry N is write protected. */
esp_flash_err_t (*get_protected_regions)(const esp_flash_chip_t *chip, uint64_t *regions);
esp_err_t (*get_protected_regions)(esp_flash_t *chip, uint64_t *regions);
/* Set protectable regions on the chip. Each bit represents on entry in the protectable regions array. */
esp_flash_err_t (*set_protected_regions)(const esp_flash_chip_t *chip, uint64_t regions);
esp_err_t (*set_protected_regions)(esp_flash_t *chip, uint64_t regions);
/* Read data from the chip.
*
* Before calling this function, the caller will have called chip->drv->set_read_mode(chip) in order to configure the chip's read mode correctly.
*/
esp_flash_err_t (*read)(const esp_flash_chip_t *chip, void *buffer, uint32_t address, uint32_t length);
esp_err_t (*read)(esp_flash_t *chip, void *buffer, uint32_t address, uint32_t length);
/* Write any amount of data to the chip.
*/
esp_flash_err_t (*write)(const esp_flash_chip_t *chip, uint32_t address, const void *buffer, uint32_t length);
esp_err_t (*write)(esp_flash_t *chip, const void *buffer, uint32_t address, uint32_t length);
/* Use the page program command to write data to the chip.
@ -110,33 +127,33 @@ typedef struct esp_flash_driver {
* - The region between 'address' and 'address + length' will not cross a page_size aligned boundary (the write
* implementation is expected to split such a write into two before calling page_program.)
*/
esp_flash_err_t (*page_program)(const esp_flash_chip_t *chip, uint32_t address, const void *buffer, uint32_t length);
esp_err_t (*program_page)(esp_flash_t *chip, const void *buffer, uint32_t address, uint32_t length);
/* Page size as written by the page_program function. Usually 256 bytes. */
uint32_t page_size;
/* Perform an encrypted write to the chip, using internal flash encryption hardware. */
esp_flash_err_t (*write_encrypted)(const esp_flash_chip_t *chip, uint32_t address, const void *buffer, uint32_t length);
esp_err_t (*write_encrypted)(esp_flash_t *chip, const void *buffer, uint32_t address, uint32_t length);
/* Set the write enable flag. This function is called internally by other functions in this structure, before a destructive
operation takes place. */
esp_flash_err_t (*write_enable)(const esp_flash_chip_t *chip);
esp_err_t (*set_write_protect)(esp_flash_t *chip, bool write_protect);
/* Wait for the SPI flash chip to be idle (any write operation to be complete.) This function is both called from the higher-level API functions, and from other functions in this structure.
timeout_ms should be a timeout (in milliseconds) before the function returns FLASH_ERR_NO_RESPONSE. This is useful to avoid hanging
timeout_ms should be a timeout (in milliseconds) before the function returns ESP_ERR_TIMEOUT. This is useful to avoid hanging
if the chip is otherwise unresponsive (ie returns all 0xFF or similar.)
*/
esp_flash_err_t (*wait_idle)(const esp_flash_chip_t *chip, unsigned timeout_ms);
esp_err_t (*wait_idle)(esp_flash_t *chip, unsigned timeout_ms);
/* Configure both the SPI host and the chip for the read mode specified in chip->read_mode.
*
* This function is called by the higher-level API before the 'read' function is called.
*
* Can return FLASH_ERR_UNSUPPORTED_HOST or FLASH_ERR_UNSUPPORTED_CHIP if the specified mode is unsupported.
* Can return ESP_ERR_FLASH_UNSUPPORTED_HOST or ESP_ERR_FLASH_UNSUPPORTED_CHIP if the specified mode is unsupported.
*/
esp_flash_err_t (*set_read_mode)(const esp_flash_chip_t *chip);
} esp_flash_driver_t;
esp_err_t (*set_read_mode)(esp_flash_t *chip);
};
/* Pointer to an array of pointers to all known drivers for flash chips. This array is used
by esp_flash_init() to detect the flash chip driver, if none is supplied by the caller.
@ -145,32 +162,4 @@ typedef struct esp_flash_driver {
This pointer can be overwritten with a pointer to a new array, to update the list of known flash chips.
*/
extern const esp_flash_driver_t **esp_flash_registered_flash_drivers;
/* Provide OS-level integration hooks for accessing flash chips
inside a running OS */
typedef struct
{
/* Call this function before commencing any flash operation.
Does not need to be recursive (ie is called at most once for each call to 'end'.
*/
esp_flash_err_t (*start)(const esp_flash_chip_t *chip);
/* Call this function after completing any flash operation. */
esp_flash_err_t (*end)(const esp_flash_chip_t *chip);
/* Delay for at least 'ms' milliseconds.
This function will be called in between 'start' and 'end'.
*/
esp_flash_err_t (*delay_ms)(unsigned ms);
} esp_flash_os_functions_t;
/* The default (ie initial boot) no-OS ROM esp_flash_os_functions_t */
const esp_flash_os_functions_t esp_flash_noos_functions;
/* Pointer to the current esp_flash_os_functions_t structure in use.
Can be changed at runtime to reflect different running conditions.
*/
extern const esp_flash_os_functions_t *esp_flash_os_functions;
extern const spi_flash_chip_t **esp_flash_registered_chips;

View file

@ -0,0 +1,275 @@
// Copyright 2015-2019 Espressif Systems (Shanghai) PTE LTD
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
#pragma once
#include <stdint.h>
#include "esp_flash.h"
#include "spi_flash_chip_driver.h"
/*
* The 'chip_generic' SPI flash operations are a lowest common subset of SPI
* flash commands, that work across most chips.
*
* These can be used as-is via the esp_flash_common_chip_driver chip_drv, or
* they can be used as "base chip_drv" functions when creating a new
* spi_flash_host_driver_t chip_drv structure.
*
* All of the functions in this header are internal functions, not part of a
* public API. See esp_flash.h for the public API.
*/
/**
* @brief Generic probe function
*
* @param chip Pointer to SPI flash chip to use. If NULL, esp_flash_default_chip is substituted.
* @param flash_id expected manufacture id.
*
* @return ESP_OK if the id read from chip->drv_read_id matches (always).
*/
esp_err_t spi_flash_chip_generic_probe(esp_flash_t *chip, uint32_t flash_id);
/**
* @brief Generic reset function
*
* @param chip Pointer to SPI flash chip to use. If NULL, esp_flash_default_chip is substituted.
*
* @return ESP_OK if sending success, or error code passed from ``common_command`` or ``wait_idle`` functions of host driver.
*/
esp_err_t spi_flash_chip_generic_reset(esp_flash_t *chip);
/**
* @brief Generic size detection function
*
* Tries to detect the size of chip by using the lower 4 bits of the chip->drv->read_id result = N, and assuming size is 2 ^ N.
*
* @param chip Pointer to SPI flash chip to use. If NULL, esp_flash_default_chip is substituted.
* @param size Output of the detected size
*
* @return
* - ESP_OK if success
* - ESP_ERR_FLASH_UNSUPPORTED_CHIP if the manufacturer id is not correct, which may means an error in the reading
* - or other error passed from the ``read_id`` function of host driver
*/
esp_err_t spi_flash_chip_generic_detect_size(esp_flash_t *chip, uint32_t *size);
/**
* @brief Erase chip by using the generic erase chip (C7h) command.
*
* @param chip Pointer to SPI flash chip to use. If NULL, esp_flash_default_chip is substituted.
*
* @return
* - ESP_OK if success
* - or other error passed from the ``set_write_protect``, ``wait_idle`` or ``erase_chip`` function of host driver
*/
esp_err_t spi_flash_chip_generic_erase_chip(esp_flash_t *chip);
/**
* @brief Erase sector by using the generic sector erase (20h) command.
*
* @param chip Pointer to SPI flash chip to use. If NULL, esp_flash_default_chip is substituted.
* @param start_address Start address of the sector to erase
*
* @return
* - ESP_OK if success
* - or other error passed from the ``set_write_protect``, ``wait_idle`` or ``erase_sector`` function of host driver
*/
esp_err_t spi_flash_chip_generic_erase_sector(esp_flash_t *chip, uint32_t start_address);
/**
* @brief Erase block by using the generic 64KB block erase (D8h) command
*
* @param chip Pointer to SPI flash chip to use. If NULL, esp_flash_default_chip is substituted.
* @param start_address Start address of the block to erase
*
* @return
* - ESP_OK if success
* - or other error passed from the ``set_write_protect``, ``wait_idle`` or ``erase_block`` function of host driver
*/
esp_err_t spi_flash_chip_generic_erase_block(esp_flash_t *chip, uint32_t start_address);
/**
* @brief Read from flash by using a read command that matches the programmed
* read mode.
*
* @param chip Pointer to SPI flash chip to use. If NULL, esp_flash_default_chip is substituted.
* @param buffer Buffer to hold the data read from flash
* @param address Start address of the data on the flash
* @param length Length to read
*
* @return always ESP_OK currently
*/
esp_err_t spi_flash_chip_generic_read(esp_flash_t *chip, void *buffer, uint32_t address, uint32_t length);
/**
* @brief Perform a page program using the page program (02h) command.
*
* @note Length of each call should not excced the limitation in
* ``chip->host->max_write_bytes``. This function is called in
* ``spi_flash_chip_generic_write`` recursively until the whole page is
* programmed. Strongly suggest to call ``spi_flash_chip_generic_write``
* instead.
*
* @param chip Pointer to SPI flash chip to use. If NULL, esp_flash_default_chip is substituted.
* @param buffer Buffer holding the data to program
* @param address Start address to write to flash
* @param length Length to write, no longer than ``chip->host->max_write_bytes``.
*
* @return
* - ESP_OK if success
* - or other error passed from the ``wait_idle`` or ``program_page`` function of host driver
*/
esp_err_t
spi_flash_chip_generic_page_program(esp_flash_t *chip, const void *buffer, uint32_t address, uint32_t length);
/**
* @brief Perform a generic write. Split the write buffer into page program
* operations, and call chip->chip_drv->page-program() for each.
*
* @param chip Pointer to SPI flash chip to use. If NULL, esp_flash_default_chip is substituted.
* @param buffer Buffer holding the data to program
* @param address Start address to write to flash
* @param length Length to write
*
* @return
* - ESP_OK if success
* - or other error passed from the ``wait_idle`` or ``program_page`` function of host driver
*/
esp_err_t spi_flash_chip_generic_write(esp_flash_t *chip, const void *buffer, uint32_t address, uint32_t length);
/**
* @brief Perform a write using on-chip flash encryption. Not implemented yet.
*
* @param chip Pointer to SPI flash chip to use. If NULL, esp_flash_default_chip is substituted.
* @param buffer Buffer holding the data to program
* @param address Start address to write to flash
* @param length Length to write
*
* @return always ESP_ERR_FLASH_UNSUPPORTED_HOST.
*/
esp_err_t
spi_flash_chip_generic_write_encrypted(esp_flash_t *chip, const void *buffer, uint32_t address, uint32_t length);
/**
* @brief Send the write enable (06h) command and verify the expected bit (1) in
* the status register is set.
*
* @param chip Pointer to SPI flash chip to use. If NULL, esp_flash_default_chip is substituted.
* @param write_protect true to enable write protection, false to send write enable.
*
* @return
* - ESP_OK if success
* - or other error passed from the ``wait_idle``, ``read_status`` or ``set_write_protect`` function of host driver
*/
esp_err_t spi_flash_chip_generic_write_enable(esp_flash_t *chip, bool write_protect);
/**
* @brief Read flash status via the RDSR command (05h) and wait for bit 0 (write
* in progress bit) to be cleared.
*
* @param chip Pointer to SPI flash chip to use. If NULL, esp_flash_default_chip is substituted.
* @param timeout_ms Time to wait before timeout, in ms.
*
* @return
* - ESP_OK if success
* - ESP_ERR_TIMEOUT if not idle before timeout
* - or other error passed from the ``wait_idle`` or ``read_status`` function of host driver
*/
esp_err_t spi_flash_chip_generic_wait_idle(esp_flash_t *chip, uint32_t timeout_ms);
/**
* @brief Set the specified SPI read mode according to the data in the chip
* context. Set quad enable status register bit if needed.
*
* @param chip Pointer to SPI flash chip to use. If NULL, esp_flash_default_chip is substituted.
*
* @return
* - ESP_OK if success
* - ESP_ERR_TIMEOUT if not idle before timeout
* - or other error passed from the ``set_write_protect`` or ``common_command`` function of host driver
*/
esp_err_t spi_flash_chip_generic_set_read_mode(esp_flash_t *chip);
/**
* Generic SPI flash chip_drv, uses all the above functions for its operations.
* In default autodetection, this is used as a catchall if a more specific
* chip_drv is not found.
*/
extern const spi_flash_chip_t esp_flash_chip_generic;
/*******************************************************************************
* Utilities
*******************************************************************************/
/**
* @brief Wait for the SPI host hardware state machine to be idle.
*
* This isn't a flash chip_drv operation, but it's called by
* spi_flash_chip_generic_wait_idle() and may be useful when implementing
* alternative drivers.
*
* timeout_ms will be decremented if the function needs to wait until the host hardware is idle.
*
* @param chip Pointer to SPI flash chip to use. If NULL, esp_flash_default_chip is substituted.
*
* @return
* - ESP_OK if success
* - ESP_ERR_TIMEOUT if not idle before timeout
* - or other error passed from the ``set_write_protect`` or ``common_command`` function of host driver
*/
esp_err_t spi_flash_generic_wait_host_idle(esp_flash_t *chip, uint32_t *timeout_ms);
/**
* @brief Utility function for set_read_mode chip_drv function
*
* Most setting of read mode follows a common pattern, except for how to enable Quad I/O modes (QIO/QOUT).
* These use different commands to read/write the status register, and a different bit is set/cleared.
*
* This is a generic utility function to implement set_read_mode() for this pattern. Also configures host
* registers via spi_flash_common_configure_host_read_mode().
*
* @param qe_rdsr_command SPI flash command to read status register
* @param qe_wrsr_command SPI flash command to write status register
* @param qe_sr_bitwidth Width of the status register these commands operate on, in bits.
* @param qe_sr_bit Bit mask for enabling Quad Enable functions on this chip.
*
* @return always ESP_OK (currently).
*/
esp_err_t spi_flash_common_set_read_mode(esp_flash_t *chip, uint8_t qe_rdsr_command, uint8_t qe_wrsr_command, uint8_t qe_sr_bitwidth, unsigned qe_sr_bit);
/**
* @brief Configure the host to use the specified read mode set in the ``chip->read_mode``.
*
* @param chip Pointer to SPI flash chip to use. If NULL, esp_flash_default_chip is substituted.
*
* @return
* - ESP_OK if success
* - ESP_ERR_FLASH_NOT_INITIALISED if chip not initialized properly
* - or other error passed from the ``configure_host_mode`` function of host driver
*/
esp_err_t spi_flash_chip_generic_config_host_read_mode(esp_flash_t *chip);
/**
* @brief Returns true if chip is configured for Quad I/O or Quad Fast Read.
*
* @param chip Pointer to SPI flash chip to use. If NULL, esp_flash_default_chip is substituted.
*
* @return true if flash works in quad mode, otherwise false
*/
static inline bool spi_flash_is_quad_mode(const esp_flash_t *chip)
{
return (chip->read_mode == SPI_FLASH_QIO) || (chip->read_mode == SPI_FLASH_QOUT);
}

View file

@ -0,0 +1,27 @@
// Copyright 2015-2019 Espressif Systems (Shanghai) PTE LTD
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
#pragma once
#include <stdint.h>
#include "esp_flash.h"
#include "spi_flash_chip_driver.h"
/**
* ISSI SPI flash chip_drv, uses all the above functions for its operations. In
* default autodetection, this is used as a catchall if a more specific chip_drv
* is not found.
*/
extern const spi_flash_chip_t esp_flash_chip_issi;

View file

@ -1,151 +0,0 @@
#pragma once
#include "spi_flash_lowlevel_driver.h"
/* The 'generic' SPI flash operations are a lowest common subset of SPI flash commands, that work across most chips.
*
* These can be used as-is vai the esp_flash_common_chip_driver driver, or they can be used as "base driver" functions when
* creating a new esp_flash_driver_t driver structure.
*
*
* All of the functions in this header are internal functions, not part of a public API. See spi_flash_lowlevel.h for
* the public API.
*/
/* SPI commands (actual on-wire commands not SPI controller bitmasks)
Suitable for use with spi_flash_common_command static function.
*/
#define CMD_RDID 0x9F
#define CMD_WRSR 0x01
#define CMD_WRSR2 0x31 /* Not all SPI flash uses this command */
#define CMD_WREN 0x06
#define CMD_WRDI 0x04
#define CMD_RDSR 0x05
#define CMD_RDSR2 0x35 /* Not all SPI flash uses this command */
#define CMD_FASTRD_QIO 0xEB
#define CMD_FASTRD_QUAD 0x6B
#define CMD_FASTRD_DIO 0xBB
#define CMD_FASTRD_DUAL 0x3B
#define CMD_FASTRD 0x0B
#define CMD_READ 0x03 /* Speed limited */
#define CMD_CHIP_ERASE 0xC7
#define CMD_SECTOR_ERASE 0x20
#define CMD_LARGE_BLOCK_ERASE 0xD8 /* 64KB block erase command */
#define SR_WIP (1<<0) /* Status register write-in-progress bit */
#define SR_WREN (1<<1) /* Status register write enable bit */
/** @brief Execute a simple SPI flash command against the chip.
*
* @param chip Pointer to the chip to use.
* @param command Command to execute (an on-wire hex command.)
* @param mosi_data Up to 32 bits of MOSI data to write after the command.
* @param mosi_len Length of MOSI data (in bits.)
* @param miso_len Length of MISO data (in bits.)
* @return MISO value read back, if any (depending on miso_len value.)
*/
uint32_t spi_flash_common_command(const esp_flash_chip_t *chip, uint8_t command, uint32_t mosi_data, uint8_t mosi_len, uint8_t miso_len);
/** @brief Returns true if the pin configuration for this chip uses the GPIO matrix for any signals. */
bool spi_flash_uses_gpio_matrix(const esp_flash_chip_t *chip);
/** @brief Generic probe function
*
* If chip->drv_read_id succeeds, the probe succeeds.
*/
esp_flash_err_t spi_flash_generic_probe(esp_flash_chip_t *chip, uint32_t flash_id);
/** @brief Generic implementation of esp_flash_driver_t->read_id
*
* Uses the RDID command (9Fh) supported by most SPI flash chips.
*
* Results of all-zeroes or all-ones are considered failures (probably no chip attached.)
*/
esp_flash_err_t spi_flash_generic_read_id(const esp_flash_chip_t *chip, uint32_t *id);
/** @brief Generic size detection function
*
* Tries to detect the size of chip by using the lower 4 bits of the chip->drv->read_id result = N, and assuming size is 2 ^ N.
*/
esp_flash_err_t spi_flash_generic_detect_size(const esp_flash_chip_t *chip, uint32_t *size);
/** @brief Erase chip by using the generic erase chip (C7h) command. */
esp_flash_err_t spi_flash_generic_erase_chip(const esp_flash_chip_t *chip);
/** @brief Erase sector by using the generic sector erase (20h) command. */
esp_flash_err_t spi_flash_generic_erase_sector(const esp_flash_chip_t *chip, uint32_t start_address);
/** @brief Erase block by using the generic 64KB block erase (D8h) command */
esp_flash_err_t spi_flash_generic_erase_block(const esp_flash_chip_t *chip, uint32_t start_address);
/** @brief Read from flash by using a read command that matches the programmed read mode. */
esp_flash_err_t spi_flash_generic_read(const esp_flash_chip_t *chip, void *buffer, uint32_t address, uint32_t length);
/** @brief Perform a page program using the page program (02h) command. */
esp_flash_err_t spi_flash_generic_page_program(const esp_flash_chip_t *chip, uint32_t address, const void *buffer, uint32_t length);
/** @brief Perform a generic write. Split the write buffer into
one page operations, and call chip->drv->page-program() for each.
*/
esp_flash_err_t spi_flash_generic_write(const esp_flash_chip_t *chip, uint32_t address, const void *buffer, uint32_t length);
/** @brief Perform a write using on-chip flash encryption */
esp_flash_err_t spi_flash_generic_write_encrypted(const esp_flash_chip_t *chip, uint32_t address, const void *buffer, uint32_t length);
/** @brief Send the write enable (06h) command and verify the expected bit (1) in the status register is set. */
esp_flash_err_t spi_flash_generic_write_enable(const esp_flash_chip_t *chip);
/** @brief Wait for the SPI host hardware state machine to be idle.
This isn't a flash driver operation, but it's called by spi_flash_generic_wait_idle() and may be useful when implementing alternative drivers.
timeout_ms will be decremented if the function needs to wait until the host hardware is idle.
*/
esp_flash_err_t spi_flash_generic_wait_host_idle(const esp_flash_chip_t *chip, uint32_t *timeout_ms);
/** @brief Read flash status via the RDSR command (05h) and wait for bit 0 (write in progress bit) to be cleared. */
esp_flash_err_t spi_flash_generic_wait_idle(const esp_flash_chip_t *chip, uint32_t timeout_ms);
/** @brief Utility function to configure the SPI host hardware registers for the specified read mode.
Called by spi_flash_generic_set_read_mode() but may also be useful
when implementing other SPI flash drivers.
Note that calling this configures SPI host registers, so if running any other commands as part of set_read_mode() then these must be run before calling this function.
*/
esp_flash_err_t spi_flash_common_configure_host_read_mode(const esp_flash_chip_t *chip);
/** @brief Utility function for set_read_mode driver function
*
* Most setting of read mode follows a common pattern, except for how to enable Quad I/O modes (QIO/QOUT).
* These use different commands to read/write the status register, and a different bit is set/cleared.
*
* This is a generic utility function to implement set_read_mode() for this pattern. Also configures host
* registers via spi_flash_common_configure_host_read_mode().
*
* @param qe_rdsr_command SPI flash command to read status register
* @param qe_wrsr_command SPI flash command to write status register
* @param qe_sr_bitwidth Width of the status register these commands operate on, in bits.
* @param qe_sr_bit Bit mask for enabling Quad Enable functions on this chio.
*/
esp_flash_err_t spi_flash_common_set_read_mode(const esp_flash_chip_t *chip, uint8_t qe_rdsr_command, uint8_t qe_wrsr_command, uint8_t qe_sr_bitwidth, unsigned qe_sr_bit);
/** @brief Set the specified SPI read mode.
*
* Includes setting SPI host hardware registers, but also setting quad enable status register bit if needed.
*/
esp_flash_err_t spi_flash_generic_set_read_mode(const esp_flash_chip_t *chip);
/** @brief Returns true if chip is configured for Quad I/O or
Quad Fast Read */
inline static bool spi_flash_is_quad_mode(const esp_flash_chip_t *chip)
{
return chip->read_mode == ESP_FLASH_QIO || chip->read_mode == ESP_FLASH_QOUT;
}
/* Generic SPI flash driver, uses all the above functions for its operations. In default autodetection, this is used as
a catchall if a more specific driver is not found.
*/
extern const esp_flash_driver_t esp_flash_generic_chip_driver;

View file

@ -2,9 +2,7 @@
archive: libspi_flash.a
entries:
spi_flash_rom_patch (noflash_text)
spi_flash_lowlevel_api (noflash)
spi_flash_lowlevel_generic (noflash)
spi_flash_lowlevel_issi (noflash)
spi_flash_lowlevel_idf_app (noflash)
spi_flash_driver_hs (noflash)
spi_flash_chip_generic (noflash)
spi_flash_chip_issi (noflash)
memspi_host_driver (noflash)

View file

@ -0,0 +1,101 @@
// Copyright 2015-2019 Espressif Systems (Shanghai) PTE LTD
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
#include "spi_flash_defs.h"
#include "memspi_host_driver.h"
#include "string.h"
#include "esp_log.h"
#include "cache_utils.h"
#include "esp_flash_partitions.h"
static const char TAG[] = "memspi";
static const spi_flash_host_driver_t esp_flash_default_host = ESP_FLASH_DEFAULT_HOST_DRIVER();
esp_err_t memspi_host_init_pointers(spi_flash_host_driver_t *host, memspi_host_data_t *data, const memspi_host_config_t *cfg)
{
memcpy(host, &esp_flash_default_host, sizeof(spi_flash_host_driver_t));
esp_err_t err = spi_flash_hal_init(data, cfg);
if (err != ESP_OK) {
return err;
}
host->driver_data = data;
//some functions are not required if not SPI1
if (data->spi != &SPI1) {
host->flush_cache = NULL;
host->region_protected = NULL;
}
return ESP_OK;
}
esp_err_t memspi_host_read_id_hs(spi_flash_host_driver_t *chip_drv, uint32_t *id)
{
//NOTE: we do have a read id function, however it doesn't work in high freq
spi_flash_trans_t t = {
.command = CMD_RDID,
.mosi_data = 0,
.mosi_len = 0,
.miso_len = 24
};
chip_drv->common_command(chip_drv, &t);
uint32_t raw_flash_id = t.miso_data[0];
ESP_EARLY_LOGV(TAG, "raw_chip_id: %X\n", raw_flash_id);
if (raw_flash_id == 0xFFFFFF || raw_flash_id == 0) {
ESP_EARLY_LOGE(TAG, "no response\n");
return ESP_ERR_FLASH_NO_RESPONSE;
}
// Byte swap the flash id as it's usually written the other way around
uint8_t mfg_id = raw_flash_id & 0xFF;
uint16_t flash_id = (raw_flash_id >> 16) | (raw_flash_id & 0xFF00);
*id = ((uint32_t)mfg_id << 16) | flash_id;
ESP_EARLY_LOGV(TAG, "chip_id: %X\n", *id);
return ESP_OK;
}
esp_err_t memspi_host_read_status_hs(spi_flash_host_driver_t *driver, uint8_t *out_sr)
{
//NOTE: we do have a read id function, however it doesn't work in high freq
spi_flash_trans_t t = {
.command = CMD_RDSR,
.mosi_data = 0,
.mosi_len = 0,
.miso_len = 8
};
esp_err_t err = driver->common_command(driver, &t);
if (err != ESP_OK) {
return err;
}
*out_sr = t.miso_data[0];
return ESP_OK;
}
esp_err_t memspi_host_flush_cache(spi_flash_host_driver_t* driver, uint32_t addr, uint32_t size)
{
if (((memspi_host_data_t*)(driver->driver_data))->spi == &SPI1) {
spi_flash_check_and_flush_cache(addr, size);
}
return ESP_OK;
}
bool memspi_region_protected(spi_flash_host_driver_t* driver, uint32_t addr, uint32_t size)
{
if (((memspi_host_data_t*)(driver->driver_data))->spi != &SPI1) {
return false;
}
if (!esp_partition_main_flash_region_safe(addr, size)) {
return true;
}
return false;
}

View file

@ -24,6 +24,7 @@
#include "esp_flash_encrypt.h"
#include "esp_log.h"
#include "bootloader_common.h"
#include "esp_ota_ops.h"
#define HASH_LEN 32 /* SHA-256 digest length */
@ -355,3 +356,19 @@ bool esp_partition_check_identity(const esp_partition_t *partition_1, const esp_
}
return false;
}
bool esp_partition_main_flash_region_safe(size_t addr, size_t size)
{
bool result = true;
if (addr <= ESP_PARTITION_TABLE_OFFSET + ESP_PARTITION_TABLE_MAX_LEN) {
return false;
}
const esp_partition_t *p = esp_ota_get_running_partition();
if (addr >= p->address && addr < p->address + p->size) {
return false;
}
if (addr < p->address && addr + size > p->address) {
return false;
}
return result;
}

View file

@ -0,0 +1,44 @@
// Copyright 2015-2019 Espressif Systems (Shanghai) PTE LTD
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
#pragma once
/* SPI commands (actual on-wire commands not SPI controller bitmasks)
Suitable for use with spi_flash_hal_common_command static function.
*/
#define CMD_RDID 0x9F
#define CMD_WRSR 0x01
#define SR_WIP (1<<0) /* Status register write-in-progress bit */
#define SR_WREN (1<<1) /* Status register write enable bit */
#define CMD_WRSR2 0x31 /* Not all SPI flash uses this command */
#define CMD_WREN 0x06
#define CMD_WRDI 0x04
#define CMD_RDSR 0x05
#define CMD_RDSR2 0x35 /* Not all SPI flash uses this command */
#define CMD_FASTRD_QIO 0xEB
#define CMD_FASTRD_QUAD 0x6B
#define CMD_FASTRD_DIO 0xBB
#define CMD_FASTRD_DUAL 0x3B
#define CMD_FASTRD 0x0B
#define CMD_READ 0x03 /* Speed limited */
#define CMD_CHIP_ERASE 0xC7
#define CMD_SECTOR_ERASE 0x20
#define CMD_LARGE_BLOCK_ERASE 0xD8 /* 64KB block erase command */
#define CMD_RST_EN 0x66
#define CMD_RST_DEV 0x99

View file

@ -0,0 +1,38 @@
// Copyright 2015-2019 Espressif Systems (Shanghai) PTE LTD
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
#include <stdlib.h>
#include "spi_flash_chip_driver.h"
#include "spi_flash_chip_generic.h"
#include "spi_flash_chip_issi.h"
#include "sdkconfig.h"
/*
* 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
* esp_flash_registered_flash_ops pointer can be changed to point to a different
* array of registered ops, if desired.
*
* It can be configured to support only available chips in the sdkconfig, to
* avoid possible issues, and speed up the auto-detecting.
*/
static const spi_flash_chip_t *default_registered_chips[] = {
#ifdef CONFIG_SPI_FLASH_SUPPORT_ISSI_CHIP
&esp_flash_chip_issi,
#endif
&esp_flash_chip_generic,
NULL,
};
const spi_flash_chip_t **esp_flash_registered_chips = default_registered_chips;

View file

@ -0,0 +1,405 @@
// Copyright 2015-2019 Espressif Systems (Shanghai) PTE LTD
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
#include <stdlib.h>
#include <sys/param.h> // For MIN/MAX
#include "spi_flash_chip_generic.h"
#include "spi_flash_defs.h"
#include "esp_log.h"
static const char TAG[] = "chip_generic";
#define SPI_FLASH_GENERIC_CHIP_ERASE_TIMEOUT 4000
#define SPI_FLASH_GENERIC_SECTOR_ERASE_TIMEOUT 500
#define SPI_FLASH_GENERIC_BLOCK_ERASE_TIMEOUT 1000
#define DEFAULT_IDLE_TIMEOUT 200
#define DEFAULT_PAGE_PROGRAM_TIMEOUT 500
esp_err_t spi_flash_chip_generic_probe(esp_flash_t *chip, uint32_t flash_id)
{
// This is the catch-all probe function, claim the chip always if nothing
// else has claimed it yet.
return ESP_OK;
}
esp_err_t spi_flash_chip_generic_reset(esp_flash_t *chip)
{
//this is written following the winbond spec..
spi_flash_trans_t t;
t = (spi_flash_trans_t) {
.command = CMD_RST_EN,
};
esp_err_t err = chip->host->common_command(chip->host, &t);
if (err != ESP_OK) {
return err;
}
t = (spi_flash_trans_t) {
.command = CMD_RST_DEV,
};
err = chip->host->common_command(chip->host, &t);
if (err != ESP_OK) {
return err;
}
err = chip->chip_drv->wait_idle(chip, DEFAULT_IDLE_TIMEOUT);
return err;
}
esp_err_t spi_flash_chip_generic_detect_size(esp_flash_t *chip, uint32_t *size)
{
uint32_t id = 0;
*size = 0;
esp_err_t err = chip->host->read_id(chip->host, &id);
if (err != ESP_OK) {
return err;
}
/* Can't detect size unless the high byte of the product ID matches the same convention, which is usually 0x40 or
* 0xC0 or similar. */
if ((id & 0x0F00) != 0) {
return ESP_ERR_FLASH_UNSUPPORTED_CHIP;
}
*size = 1 << (id & 0xFF);
return ESP_OK;
}
esp_err_t spi_flash_chip_generic_erase_chip(esp_flash_t *chip)
{
esp_err_t err;
err = chip->chip_drv->set_write_protect(chip, false);
if (err == ESP_OK) {
err = chip->chip_drv->wait_idle(chip, DEFAULT_IDLE_TIMEOUT);
}
if (err == ESP_OK) {
chip->host->erase_chip(chip->host);
//to save time, flush cache here
if (chip->host->flush_cache) {
err = chip->host->flush_cache(chip->host, 0, chip->size);
if (err != ESP_OK) {
return err;
}
}
err = chip->chip_drv->wait_idle(chip, SPI_FLASH_GENERIC_CHIP_ERASE_TIMEOUT);
}
return err;
}
esp_err_t spi_flash_chip_generic_erase_sector(esp_flash_t *chip, uint32_t start_address)
{
esp_err_t err = chip->chip_drv->set_write_protect(chip, false);
if (err == ESP_OK) {
err = chip->chip_drv->wait_idle(chip, DEFAULT_IDLE_TIMEOUT);
}
if (err == ESP_OK) {
chip->host->erase_sector(chip->host, start_address);
//to save time, flush cache here
if (chip->host->flush_cache) {
err = chip->host->flush_cache(chip->host, start_address, chip->chip_drv->sector_size);
if (err != ESP_OK) {
return err;
}
}
err = chip->chip_drv->wait_idle(chip, SPI_FLASH_GENERIC_SECTOR_ERASE_TIMEOUT);
}
return err;
}
esp_err_t spi_flash_chip_generic_erase_block(esp_flash_t *chip, uint32_t start_address)
{
esp_err_t err = chip->chip_drv->set_write_protect(chip, false);
if (err == ESP_OK) {
err = chip->chip_drv->wait_idle(chip, DEFAULT_IDLE_TIMEOUT);
}
if (err == ESP_OK) {
chip->host->erase_block(chip->host, start_address);
//to save time, flush cache here
if (chip->host->flush_cache) {
err = chip->host->flush_cache(chip->host, start_address, chip->chip_drv->block_erase_size);
if (err != ESP_OK) {
return err;
}
}
err = chip->chip_drv->wait_idle(chip, SPI_FLASH_GENERIC_BLOCK_ERASE_TIMEOUT);
}
return err;
}
esp_err_t spi_flash_chip_generic_read(esp_flash_t *chip, void *buffer, uint32_t address, uint32_t length)
{
esp_err_t err = ESP_OK;
// Configure the host, and return
spi_flash_chip_generic_config_host_read_mode(chip);
while (err == ESP_OK && length > 0) {
uint32_t read_len = MIN(length, chip->host->max_read_bytes);
err = chip->host->read(chip->host, buffer, address, read_len);
buffer += read_len;
length -= read_len;
address += read_len;
}
return err;
}
esp_err_t spi_flash_chip_generic_page_program(esp_flash_t *chip, const void *buffer, uint32_t address, uint32_t length)
{
esp_err_t err;
err = chip->chip_drv->wait_idle(chip, DEFAULT_IDLE_TIMEOUT);
if (err == ESP_OK) {
// Perform the actual Page Program command
chip->host->program_page(chip->host, buffer, address, length);
err = chip->chip_drv->wait_idle(chip, DEFAULT_PAGE_PROGRAM_TIMEOUT);
}
return err;
}
esp_err_t spi_flash_chip_generic_write(esp_flash_t *chip, const void *buffer, uint32_t address, uint32_t length)
{
esp_err_t err = ESP_OK;
const uint32_t page_size = chip->chip_drv->page_size;
while (err == ESP_OK && length > 0) {
uint32_t page_len = MIN(chip->host->max_write_bytes, MIN(page_size, length));
if ((address + page_len) / page_size != address / page_size) {
// Most flash chips can't page write across a page boundary
page_len = page_size - (address % page_size);
}
err = chip->chip_drv->set_write_protect(chip, false);
if (err == ESP_OK) {
err = chip->chip_drv->program_page(chip, buffer, address, page_len);
address += page_len;
buffer = (void *)((intptr_t)buffer + page_len);
length -= page_len;
}
}
if (err == ESP_OK && chip->host->flush_cache) {
err = chip->host->flush_cache(chip->host, address, length);
}
return err;
}
esp_err_t spi_flash_chip_generic_write_encrypted(esp_flash_t *chip, const void *buffer, uint32_t address, uint32_t length)
{
return ESP_ERR_FLASH_UNSUPPORTED_HOST; // TODO
}
esp_err_t spi_flash_chip_generic_write_enable(esp_flash_t *chip, bool write_protect)
{
esp_err_t err = ESP_OK;
err = chip->chip_drv->wait_idle(chip, DEFAULT_IDLE_TIMEOUT);
if (err == ESP_OK) {
chip->host->set_write_protect(chip->host, write_protect);
}
uint8_t status;
err = chip->host->read_status(chip->host, &status);
if (err != ESP_OK) {
return err;
}
if ((status & SR_WREN) == 0) {
// WREN flag has not been set!
err = ESP_ERR_NOT_FOUND;
}
return err;
}
esp_err_t spi_flash_generic_wait_host_idle(esp_flash_t *chip, uint32_t *timeout_ms)
{
while (chip->host->host_idle(chip->host) && *timeout_ms > 0) {
if (*timeout_ms > 1) {
chip->os_func->delay_ms(chip->os_func_data, 1);
}
(*timeout_ms)--;
}
return (*timeout_ms > 0) ? ESP_OK : ESP_ERR_TIMEOUT;
}
esp_err_t spi_flash_chip_generic_wait_idle(esp_flash_t *chip, uint32_t timeout_ms)
{
timeout_ms++; // allow at least one pass before timeout, last one has no sleep cycle
uint8_t status = 0;
while (timeout_ms > 0) {
esp_err_t err = spi_flash_generic_wait_host_idle(chip, &timeout_ms);
if (err != ESP_OK) {
return err;
}
err = chip->host->read_status(chip->host, &status);
if (err != ESP_OK) {
return err;
}
if ((status & SR_WIP) == 0) {
break; // Write in progress is complete
}
if (timeout_ms > 1) {
chip->os_func->delay_ms(chip->os_func_data, 1);
}
timeout_ms--;
}
return (timeout_ms > 0) ? ESP_OK : ESP_ERR_TIMEOUT;
}
esp_err_t spi_flash_chip_generic_config_host_read_mode(esp_flash_t *chip)
{
uint32_t dummy_cyclelen_base;
uint32_t addr_bitlen;
uint32_t read_command;
switch (chip->read_mode) {
case SPI_FLASH_QIO:
//for QIO mode, the 4 bit right after the address are used for continuous mode, should be set to 0 to avoid that.
addr_bitlen = 32;
dummy_cyclelen_base = 4;
read_command = CMD_FASTRD_QIO;
break;
case SPI_FLASH_QOUT:
addr_bitlen = 24;
dummy_cyclelen_base = 8;
read_command = CMD_FASTRD_QUAD;
break;
case SPI_FLASH_DIO:
//for DIO mode, the 4 bit right after the address are used for continuous mode, should be set to 0 to avoid that.
addr_bitlen = 28;
dummy_cyclelen_base = 2;
read_command = CMD_FASTRD_DIO;
break;
case SPI_FLASH_DOUT:
addr_bitlen = 24;
dummy_cyclelen_base = 8;
read_command = CMD_FASTRD_DUAL;
break;
case SPI_FLASH_FASTRD:
addr_bitlen = 24;
dummy_cyclelen_base = 8;
read_command = CMD_FASTRD;
break;
case SPI_FLASH_SLOWRD:
addr_bitlen = 24;
dummy_cyclelen_base = 0;
read_command = CMD_READ;
break;
default:
return ESP_ERR_FLASH_NOT_INITIALISED;
}
return chip->host->configure_host_read_mode(chip->host, chip->read_mode, addr_bitlen, dummy_cyclelen_base, read_command);
}
esp_err_t spi_flash_common_set_read_mode(esp_flash_t *chip, uint8_t qe_rdsr_command, uint8_t qe_wrsr_command, uint8_t qe_sr_bitwidth, unsigned qe_sr_bit)
{
if (spi_flash_is_quad_mode(chip)) {
// Ensure quad modes are enabled, using the Quad Enable parameters supplied.
spi_flash_trans_t t = {
.command = qe_rdsr_command,
.mosi_data = 0,
.mosi_len = 0,
.miso_len = qe_sr_bitwidth,
};
chip->host->common_command(chip->host, &t);
unsigned sr = t.miso_data[0];
ESP_EARLY_LOGV(TAG, "set_read_mode: status before 0x%x", sr);
if ((sr & qe_sr_bit) == 0) {
//some chips needs the write protect to be disabled before writing to Status Register
chip->chip_drv->set_write_protect(chip, false);
sr |= qe_sr_bit;
spi_flash_trans_t t = {
.command = qe_wrsr_command,
.mosi_data = sr,
.mosi_len = qe_sr_bitwidth,
.miso_len = 0,
};
chip->host->common_command(chip->host, &t);
/* Check the new QE bit has stayed set */
spi_flash_trans_t t_rdsr = {
.command = qe_rdsr_command,
.mosi_data = 0,
.mosi_len = 0,
.miso_len = qe_sr_bitwidth
};
chip->host->common_command(chip->host, &t_rdsr);
sr = t_rdsr.miso_data[0];
ESP_EARLY_LOGV(TAG, "set_read_mode: status after 0x%x", sr);
if ((sr & qe_sr_bit) == 0) {
return ESP_ERR_FLASH_NO_RESPONSE;
}
chip->chip_drv->set_write_protect(chip, true);
}
}
return ESP_OK;
}
esp_err_t spi_flash_chip_generic_set_read_mode(esp_flash_t *chip)
{
// On "generic" 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 << 1;
return spi_flash_common_set_read_mode(chip, CMD_RDSR2, CMD_WRSR2, 8, BIT_QE);
}
static const char chip_name[] = "generic";
const spi_flash_chip_t esp_flash_chip_generic = {
.name = chip_name,
.probe = spi_flash_chip_generic_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,
// TODO: figure out if generic chip-wide protection bits exist across some manufacturers
.get_chip_write_protect = NULL,
.set_chip_write_protect = NULL,
// Chip write protection regions do not appear to be standardised
// at all, this is implemented in chip-specific drivers only.
.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,
.set_write_protect = spi_flash_chip_generic_write_enable,
.wait_idle = spi_flash_chip_generic_wait_idle,
.set_read_mode = spi_flash_chip_generic_set_read_mode,
};

View file

@ -1,9 +1,9 @@
// Copyright 2017 Espressif Systems (Shanghai) PTE LTD
// Copyright 2015-2019 Espressif Systems (Shanghai) PTE LTD
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
@ -11,44 +11,49 @@
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
#include <stdlib.h>
#include "spi_flash_lowlevel_driver.h"
#include "spi_flash_lowlevel_generic.h"
#include "spi_flash_chip_generic.h"
#include "spi_flash_defs.h"
/* Driver for ISSI flash chip, as used in ESP32 D2WD */
esp_flash_err_t issi_probe(esp_flash_chip_t *chip, uint32_t flash_id)
esp_err_t spi_flash_chip_issi_probe(esp_flash_t *chip, uint32_t flash_id)
{
/* Check manufacturer and product IDs match our desired masks */
const uint8_t MFG_ID = 0x9D;
if (flash_id >> 16 != MFG_ID) {
return FLASH_ERR_NOT_FOUND;
return ESP_ERR_NOT_FOUND;
}
const uint16_t FLASH_ID_MASK = 0xCF00;
const uint16_t FLASH_ID_VALUE = 0x4000;
if ((flash_id & FLASH_ID_MASK) != FLASH_ID_VALUE) {
return FLASH_ERR_NOT_FOUND;
return ESP_ERR_NOT_FOUND;
}
return FLASH_OK;
return ESP_OK;
}
esp_flash_err_t issi_set_read_mode(const esp_flash_chip_t *chip)
esp_err_t spi_flash_chip_issi_set_read_mode(esp_flash_t *chip)
{
/* ISSI uses bit 6 of "basic" SR as Quad Enable */
const uint8_t BIT_QE = 1<<6;
const uint8_t BIT_QE = 1 << 6;
return spi_flash_common_set_read_mode(chip, CMD_RDSR, CMD_WRSR, 8, BIT_QE);
}
static const char chip_name[] = "issi";
const esp_flash_driver_t esp_flash_issi_chip_driver = {
.probe = issi_probe,
.read_id = spi_flash_generic_read_id,
.detect_size = spi_flash_generic_detect_size,
.erase_chip = spi_flash_generic_erase_chip,
.erase_sector = spi_flash_generic_erase_sector,
.erase_block = spi_flash_generic_erase_block,
// 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_issi = {
.name = chip_name,
.probe = spi_flash_chip_issi_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,
@ -62,13 +67,13 @@ const esp_flash_driver_t esp_flash_issi_chip_driver = {
.get_protected_regions = NULL,
.set_protected_regions = NULL,
.read = spi_flash_generic_read,
.write = spi_flash_generic_write,
.page_program = spi_flash_generic_page_program,
.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_generic_write_encrypted,
.write_encrypted = spi_flash_chip_generic_write_encrypted,
.write_enable = spi_flash_generic_write_enable,
.wait_idle = spi_flash_generic_wait_idle,
.set_read_mode = issi_set_read_mode,
.set_write_protect = spi_flash_chip_generic_write_enable,
.wait_idle = spi_flash_chip_generic_wait_idle,
.set_read_mode = spi_flash_chip_issi_set_read_mode,
};

View file

@ -1,552 +0,0 @@
// Copyright 2017 Espressif Systems (Shanghai) PTE LTD
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
#include <stdlib.h>
#include <stdio.h>
#include <sys/param.h>
#include <string.h>
#include "spi_flash_lowlevel_driver.h"
#include "spi_flash_lowlevel_generic.h"
#include "soc/spi_reg.h"
#define MAX_WRITE_CHUNK 8192 /* write in chunks */
/* Static function to notify OS of a new SPI flash operation.
If returns an error result, caller must abort. If returns FLASH_OK, caller must
call spiflash_end() before returning.
*/
static esp_flash_err_t spiflash_start(const esp_flash_chip_t *chip)
{
if (esp_flash_os_functions != NULL
&& esp_flash_os_functions->start != NULL) {
esp_flash_err_t err = esp_flash_os_functions->start(chip);
if (err != FLASH_OK) {
return err;
}
}
return FLASH_OK;
}
/* Static function to notify OS that SPI flash operation is complete.
*/
static esp_flash_err_t spiflash_end(const esp_flash_chip_t *chip, esp_flash_err_t err)
{
if (esp_flash_os_functions != NULL
&& esp_flash_os_functions->end != NULL) {
esp_flash_err_t end_err = esp_flash_os_functions->end(chip);
if (err == FLASH_OK) {
err = end_err; // Only return the 'end' error if we haven't already failed
}
}
return err;
}
/* Return true if regions 'a' and 'b' overlap at all, based on their start offsets and lengths. */
inline static bool regions_overlap(uint32_t a_start, uint32_t a_len,uint32_t b_start, uint32_t b_len);
/* Top-level API functions, calling into driver functions via chip->drv */
static esp_flash_err_t detect_spi_flash_chip(esp_flash_chip_t *chip);
esp_flash_err_t esp_flash_init(esp_flash_chip_t *chip)
{
if (chip->spi == NULL) {
return FLASH_ERR_INVALID_ARG;
}
// TODO: configure SPI host clock speed, pin configuration
if (chip->drv == NULL) {
// Detect driver
esp_flash_err_t err = detect_spi_flash_chip(chip);
if (err != FLASH_OK) {
return err;
}
}
esp_flash_err_t err = spiflash_start(chip);
if (err != FLASH_OK) {
return err;
}
if (chip->size == 0) {
// Detect flash size
err = chip->drv->detect_size(chip, &chip->size);
}
if (err == FLASH_OK) {
// Try to set the flash mode to whatever default mode was chosen
// (this isn't necessary at this point for functionality, but init will fail
// if this mode can't be set on this chip.)
err = chip->drv->set_read_mode(chip);
}
// Done: all fields on 'chip' are initialised
return spiflash_end(chip, err);
}
static esp_flash_err_t detect_spi_flash_chip(esp_flash_chip_t *chip)
{
esp_flash_err_t err;
uint32_t flash_id;
int retries = 10;
do {
err = spiflash_start(chip);
if (err != FLASH_OK) {
return err;
}
// Send generic RDID command twice, check for a matching result and retry in case we just powered on (inner
// function fails if it sees all-ones or all-zeroes.)
err = spi_flash_generic_read_id(chip, &flash_id);
if (err == FLASH_OK) { // check we see the same ID twice, in case of transient power-on errors
uint32_t new_id;
err = spi_flash_generic_read_id(chip, &new_id);
if (err == FLASH_OK && (new_id != flash_id)) {
err = FLASH_ERR_NOT_INITIALISED;
}
}
err = spiflash_end(chip, err);
} while (err != FLASH_OK && retries-- > 0);
// Detect the chip and set the driver structure for it
const esp_flash_driver_t **drivers = esp_flash_registered_flash_drivers;
while (*drivers != NULL && chip->drv == NULL) {
chip->drv = *drivers;
// start/end SPI operation each time, for multitasking
// and also so esp_flash_registered_flash_drivers can live in flash
err = spiflash_start(chip);
if (err != FLASH_OK) {
return err;
}
if (chip->drv->probe(chip, flash_id) != FLASH_OK) {
chip->drv = NULL;
}
// if probe succeeded, chip->drv stays set
drivers++;
err = spiflash_end(chip, err);
if (err != FLASH_OK) {
return err;
}
}
return (chip->drv == NULL) ? FLASH_ERR_NOT_FOUND : FLASH_OK;
}
// Convenience macro for beginning of all API functions,
// check that the 'chip' parameter is properly initialised
// and supports the operation in question
#define VERIFY_OP(OP) do { \
if (chip == NULL) { \
chip = esp_flash_default_chip; \
} \
if (chip == NULL || chip->drv == NULL) { \
return FLASH_ERR_NOT_INITIALISED; \
} \
if (chip->drv->OP == NULL) { \
return FLASH_ERR_UNSUPPORTED_CHIP; \
} \
} while (0)
esp_flash_err_t esp_flash_read_id(const esp_flash_chip_t *chip, uint32_t *id)
{
printf("chip %p esp_flash_default_chip %p\n",
chip, esp_flash_default_chip);
VERIFY_OP(read_id);
if (id == NULL) {
return FLASH_ERR_INVALID_ARG;
}
esp_flash_err_t err = spiflash_start(chip);
if (err != FLASH_OK) {
return err;
}
err = chip->drv->read_id(chip, id);
return spiflash_end(chip, err);
}
esp_flash_err_t esp_flash_detect_size(const esp_flash_chip_t *chip, uint32_t *size)
{
VERIFY_OP(detect_size);
if (size == NULL) {
return FLASH_ERR_INVALID_ARG;
}
*size = 0;
esp_flash_err_t err = spiflash_start(chip);
if (err != FLASH_OK) {
return err;
}
err = chip->drv->detect_size(chip, size);
return spiflash_end(chip, err);
}
esp_flash_err_t esp_flash_erase_chip(const esp_flash_chip_t *chip)
{
VERIFY_OP(erase_chip);
bool write_protect = false;
esp_flash_err_t err = spiflash_start(chip);
if (err != FLASH_OK) {
return err;
}
err = esp_flash_get_chip_write_protect(chip, &write_protect);
if (err == FLASH_OK && write_protect) {
err = FLASH_ERR_PROTECTED;
}
if (err == FLASH_OK) {
err = chip->drv->erase_chip(chip);
}
return spiflash_end(chip, err);
}
esp_flash_err_t esp_flash_erase_region(const esp_flash_chip_t *chip, uint32_t start, uint32_t len)
{
VERIFY_OP(erase_sector);
uint32_t block_erase_size = chip->drv->erase_block == NULL ? 0 : chip->drv->block_erase_size;
uint32_t sector_size = chip->drv->sector_size;
bool write_protect = false;
if (sector_size == 0 || (block_erase_size % sector_size) != 0) {
return FLASH_ERR_NOT_INITIALISED;
}
if (start > chip->size || start + len > chip->size) {
return FLASH_ERR_INVALID_ARG;
}
if ((start % chip->drv->sector_size) != 0 || (len % chip->drv->sector_size) != 0) {
// Can only erase multiples of the sector size, starting at sector boundary
return FLASH_ERR_INVALID_ARG;
}
esp_flash_err_t err = spiflash_start(chip);
if (err != FLASH_OK) {
return err;
}
// Check for write protection on whole chip
if (chip->drv->get_chip_write_protect != NULL) {
err = chip->drv->get_chip_write_protect(chip, &write_protect);
if (err == FLASH_OK && write_protect) {
err = FLASH_ERR_PROTECTED;
}
}
// Check for write protected regions overlapping the erase region
if (err == FLASH_OK && chip->drv->get_protected_regions != NULL && chip->drv->num_protectable_regions > 0) {
uint64_t protected = 0;
err = chip->drv->get_protected_regions(chip, &protected);
if (protected != 0) {
for (int i = 0; i < chip->drv->num_protectable_regions && err == FLASH_OK; i++) {
const esp_flash_region_t *region = &chip->drv->protectable_regions[i];
if ((protected & (1LL << i))
&& regions_overlap(start, len, region->offset, region->size)) {
err = FLASH_ERR_PROTECTED;
}
}
}
}
// Don't lock the SPI flash for the entire erase, as this may be very long
err = spiflash_end(chip, err);
while (err == FLASH_OK && len >= sector_size) {
esp_flash_err_t err = spiflash_start(chip);
if (err != FLASH_OK) {
return err;
}
// If possible erase an entire multi-sector block
if (block_erase_size > 0 && len >= block_erase_size && (start % block_erase_size) == 0) {
err = chip->drv->erase_block(chip, start);
start += block_erase_size;
len -= block_erase_size;
}
else {
// Otherwise erase individual sector only
err = chip->drv->erase_sector(chip, start);
start += sector_size;
len -= sector_size;
}
err = spiflash_end(chip, err);
}
return err;
}
esp_flash_err_t esp_flash_get_chip_write_protect(const esp_flash_chip_t *chip, bool *write_protected)
{
VERIFY_OP(get_chip_write_protect);
if (write_protected == NULL) {
return FLASH_ERR_INVALID_ARG;
}
esp_flash_err_t err = spiflash_start(chip);
if (err != FLASH_OK) {
return err;
}
err = chip->drv->get_chip_write_protect(chip, write_protected);
return spiflash_end(chip, err);
}
esp_flash_err_t esp_flash_set_chip_write_protect(const esp_flash_chip_t *chip, bool write_protect_chip)
{
VERIFY_OP(set_chip_write_protect);
esp_flash_err_t err = spiflash_start(chip);
if (err != FLASH_OK) {
return err;
}
err = chip->drv->set_chip_write_protect(chip, write_protect_chip);
return spiflash_end(chip, err);
}
esp_flash_err_t esp_flash_get_protectable_regions(const esp_flash_chip_t *chip, const esp_flash_region_t **regions, uint32_t *num_regions)
{
if(num_regions != NULL) {
*num_regions = 0; // In case caller doesn't check result
}
VERIFY_OP(get_protected_regions);
if(regions == NULL || num_regions == NULL) {
return FLASH_ERR_INVALID_ARG;
}
*num_regions = chip->drv->num_protectable_regions;
*regions = chip->drv->protectable_regions;
return FLASH_OK;
}
static esp_flash_err_t find_region(const esp_flash_chip_t *chip, const esp_flash_region_t *region, uint8_t *index)
{
if (region == NULL) {
return FLASH_ERR_INVALID_ARG;
}
for(*index = 0; *index < chip->drv->num_protectable_regions; (*index)++) {
if (memcmp(&chip->drv->protectable_regions[*index],
region, sizeof(esp_flash_region_t)) == 0) {
return FLASH_OK;
}
}
return FLASH_ERR_NOT_FOUND;
}
esp_flash_err_t esp_flash_get_protected_region(const esp_flash_chip_t *chip, const esp_flash_region_t *region, bool *protected)
{
VERIFY_OP(get_protected_regions);
if (protected == NULL) {
return FLASH_ERR_INVALID_ARG;
}
uint8_t index;
esp_flash_err_t err = find_region(chip, region, &index);
if (err != FLASH_OK) {
return err;
}
uint64_t protection_mask = 0;
err = spiflash_start(chip);
if (err != FLASH_OK) {
return err;
}
err = chip->drv->get_protected_regions(chip, &protection_mask);
if (err == FLASH_OK) {
*protected = protection_mask & (1LL << index);
}
return spiflash_end(chip, err);
}
esp_flash_err_t esp_flash_set_protected_region(const esp_flash_chip_t *chip, const esp_flash_region_t *region, bool protected)
{
VERIFY_OP(set_protected_regions);
uint8_t index;
esp_flash_err_t err = find_region(chip, region, &index);
if (err != FLASH_OK) {
return err;
}
uint64_t protection_mask = 0;
err = spiflash_start(chip);
if (err != FLASH_OK) {
return err;
}
err = chip->drv->get_protected_regions(chip, &protection_mask);
if (err == FLASH_OK) {
if (protected) {
protection_mask |= (1LL << index);
} else {
protection_mask &= ~(1LL << index);
}
err = chip->drv->set_protected_regions(chip, protection_mask);
}
return spiflash_end(chip, err);
}
esp_flash_err_t esp_flash_read(const esp_flash_chip_t *chip, void *buffer, uint32_t address, uint32_t length)
{
VERIFY_OP(read);
if (buffer == NULL || address > chip->size || address+length > chip->size) {
return FLASH_ERR_INVALID_ARG;
}
esp_flash_err_t err = spiflash_start(chip);
if (err != FLASH_OK) {
return err;
}
if (err == FLASH_OK) {
err = chip->drv->set_read_mode(chip);
}
if (err == FLASH_OK) {
err = chip->drv->read(chip, buffer, address, length);
}
return spiflash_end(chip, err);
}
esp_flash_err_t esp_flash_write(const esp_flash_chip_t *chip, uint32_t address, const void *buffer, uint32_t length)
{
VERIFY_OP(write);
if (buffer == NULL || address > chip->size || address+length > chip->size) {
return FLASH_ERR_INVALID_ARG;
}
/* If 'chip' is connected to the main SPI bus, we can only write directly from regions that are accessible
with cache disabled. */
#ifdef ESP_PLATFORM
bool direct_write = ( chip->spi != &SPI1
|| ( (uintptr_t) address >= 0x3FFAE000
&& (uintptr_t) address < 0x40000000 ) );
#else
bool direct_write = true;
#endif
esp_flash_err_t err = FLASH_OK;
/* Write output in chunks, either by buffering on stack or
by artificially cutting into MAX_WRITE_CHUNK parts (in an OS
environment, this prevents writing from causing interrupt or higher priority task
starvation.) */
while(err == FLASH_OK && length > 0) {
uint32_t write_len;
const void *write_buf;
if (direct_write) {
write_len = MIN(length, MAX_WRITE_CHUNK);
write_buf = buffer;
} else {
uint32_t buf[8];
write_len = MIN(length, sizeof(buf));
memcpy(buf, buffer, write_len);
write_buf = buf;
}
err = spiflash_start(chip);
if (err != FLASH_OK) {
return err;
}
err = chip->drv->write(chip, address, write_buf, write_len);
address += write_len;
buffer = (void *)((intptr_t)buffer + write_len);
length -= write_len;
err = spiflash_end(chip, err);
}
return err;
}
esp_flash_err_t esp_flash_write_encrypted(const esp_flash_chip_t *chip, uint32_t address, const void *buffer, uint32_t length)
{
VERIFY_OP(write_encrypted);
if (chip->spi != 0) {
// Encrypted operations have to use SPI0
return FLASH_ERR_UNSUPPORTED_HOST;
}
if (buffer == NULL || address > chip->size || address+length > chip->size) {
return FLASH_ERR_INVALID_ARG;
}
esp_flash_err_t err = spiflash_start(chip);
if (err != FLASH_OK) {
return err;
}
err = chip->drv->write_encrypted(chip, address, buffer, length);
return spiflash_end(chip, err);
}
inline static bool regions_overlap(uint32_t a_start, uint32_t a_len,uint32_t b_start, uint32_t b_len)
{
uint32_t a_end = a_start + a_len;
uint32_t b_end = b_start + b_len;
return ((a_start >= b_start && a_start <= b_end)
|| (a_end >= b_start && a_end <= b_end)
|| (b_start >= a_start && b_start <= a_end)
|| (b_end >= a_start && b_end <= a_end));
}
const esp_flash_chip_t *esp_flash_default_chip;
static esp_flash_chip_t default_chip;
esp_flash_err_t esp_flash_init_default_chip()
{
default_chip.spi = &SPI1;
default_chip.read_mode = ESP_FLASH_FASTRD; // TODO: initialise properly
default_chip.speed = ESP_FLASH_20MHZ; // TODO: initialise properly
// ROM TODO: account for non-standard default pins in efuse
// ROM TODO: to account for chips which are slow to power on, maybe keep probing in a loop here
esp_flash_err_t err = esp_flash_init(&default_chip);
if (err != FLASH_OK) {
return err;
}
esp_flash_default_chip = &default_chip;
return FLASH_OK;
}
const esp_flash_os_functions_t *esp_flash_os_functions = &esp_flash_noos_functions;

View file

@ -1,34 +0,0 @@
// Copyright 2017 Espressif Systems (Shanghai) PTE LTD
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
#include <stdlib.h>
#include "spi_flash_lowlevel_driver.h"
extern const esp_flash_driver_t esp_flash_generic_chip_driver;
extern const esp_flash_driver_t esp_flash_issi_chip_driver;
/* 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 esp_flash_registered_flash_ops pointer can be
changed to point to a different array of registered ops, if desired.
*/
static const esp_flash_driver_t *default_registered_flash_drivers[] = {
&esp_flash_issi_chip_driver,
&esp_flash_generic_chip_driver,
NULL,
};
const esp_flash_driver_t **esp_flash_registered_flash_drivers = default_registered_flash_drivers;

View file

@ -1,498 +0,0 @@
// Copyright 2017 Espressif Systems (Shanghai) PTE LTD
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
#include <stdlib.h>
#include <sys/param.h> // For MIN/MAX
#include <xtensa/hal.h>
#include "spi_flash_lowlevel_driver.h"
#include "spi_flash_lowlevel_generic.h"
#define SPI_FLASH_GENERIC_CHIP_ERASE_TIMEOUT 4000
#define SPI_FLASH_GENERIC_SECTOR_ERASE_TIMEOUT 500
#define SPI_FLASH_GENERIC_BLOCK_ERASE_TIMEOUT 1000
#define DEFAULT_IDLE_TIMEOUT 200
#define DEFAULT_PAGE_PROGRAM_TIMEOUT 500
/* Hardware host-specific constants */
#define MAX_WRITE_BYTES 32
#define MAX_READ_BYTES 64
#define ADDRESS_MASK_24BIT 0xFFFFFF
uint32_t spi_flash_common_command(const esp_flash_chip_t *chip, uint8_t command, uint32_t mosi_data, uint8_t mosi_len, uint8_t miso_len)
{
typeof(chip->spi->user2) user2 = {
.usr_command_value = command,
.usr_command_bitlen = (8 -1),
};
chip->spi->user2 = user2;
typeof(chip->spi->user) user = {
.usr_miso = miso_len > 0,
.usr_mosi = mosi_len > 0,
.usr_dummy = 0,
.usr_command = 1,
};
chip->spi->user = user;
chip->spi->ctrl.val = 0;
chip->spi->miso_dlen.usr_miso_dbitlen = miso_len ? (miso_len - 1) : 0;
chip->spi->mosi_dlen.usr_mosi_dbitlen = mosi_len ? (mosi_len - 1) : 0;
// TODO: there's a bug(?) here where if multiple bytes are written
// with each byte MSB-first (correct), but the bytes are
// written out LSB first...
//
// May be easier to just document this in the function interface...
chip->spi->data_buf[0] = mosi_data;
if (spi_flash_uses_gpio_matrix(chip)) {
/* When flash pins are mapped via GPIO matrix, need a dummy cycle before reading via MISO */
if (chip->speed == ESP_FLASH_80MHZ) {
chip->spi->user.usr_dummy = 1;
chip->spi->user1.usr_dummy_cyclelen = 1;
} else {
chip->spi->user.usr_dummy = 1;
chip->spi->user1.usr_dummy_cyclelen = 0;
}
}
chip->spi->cmd.usr = 1;
while(chip->spi->cmd.usr != 0)
{ }
uint32_t miso = chip->spi->data_buf[0];
return miso;
}
esp_flash_err_t spi_flash_generic_probe(esp_flash_chip_t *chip, uint32_t flash_id)
{
// This is the catch-all probe function, claim the chip always if nothing
// else has claimed it yet.
return FLASH_OK;
}
esp_flash_err_t spi_flash_generic_read_id(const esp_flash_chip_t *chip, uint32_t *id)
{
uint32_t raw_flash_id = spi_flash_common_command(chip, CMD_RDID, 0, 0, 24);
if (raw_flash_id == 0xFFFFFF || raw_flash_id == 0) {
return FLASH_ERR_NO_RESPONSE;
}
// Byte swap the flash id as it's usually written the other way around
uint8_t mfg_id = raw_flash_id & 0xFF;
uint16_t flash_id = (raw_flash_id >> 16) | (raw_flash_id & 0xFF00);
*id = ((uint32_t)mfg_id << 16) | flash_id;
return FLASH_OK;
}
esp_flash_err_t spi_flash_generic_detect_size(const esp_flash_chip_t *chip, uint32_t *size)
{
uint32_t id = 0;
*size = 0;
esp_flash_err_t err = chip->drv->read_id(chip, &id);
if (err != FLASH_OK) {
return err;
}
/* Can't detect size unless the high byte of the product ID matches the same convention, which is usually 0x40 or
* 0xC0 or similar. */
if ((id & 0x0F00) != 0) {
return FLASH_ERR_UNSUPPORTED_CHIP;
}
*size = 1 << (id & 0xFF);
return FLASH_OK;
}
esp_flash_err_t spi_flash_generic_erase_chip(const esp_flash_chip_t *chip)
{
esp_flash_err_t err;
err = chip->drv->write_enable(chip);
if (err == FLASH_OK) {
err = chip->drv->wait_idle(chip, DEFAULT_IDLE_TIMEOUT);
}
if (err == FLASH_OK) {
chip->spi->ctrl.val = 0;
chip->spi->cmd.flash_ce = 1;
while(chip->spi->cmd.val != 0) { }
err = chip->drv->wait_idle(chip, SPI_FLASH_GENERIC_CHIP_ERASE_TIMEOUT);
}
return err;
}
esp_flash_err_t spi_flash_generic_erase_sector(const esp_flash_chip_t *chip, uint32_t start_address)
{
esp_flash_err_t err = chip->drv->write_enable(chip);
if (err == FLASH_OK) {
err = chip->drv->wait_idle(chip, DEFAULT_IDLE_TIMEOUT);
}
if (err == FLASH_OK) {
chip->spi->user1.usr_addr_bitlen = (24 - 1);
chip->spi->addr = start_address & ADDRESS_MASK_24BIT;
chip->spi->ctrl.val = 0;
chip->spi->cmd.flash_se = 1;
while(chip->spi->cmd.val != 0) { }
err = chip->drv->wait_idle(chip, SPI_FLASH_GENERIC_SECTOR_ERASE_TIMEOUT);
}
return err;
}
esp_flash_err_t spi_flash_generic_erase_block(const esp_flash_chip_t *chip, uint32_t start_address)
{
esp_flash_err_t err = chip->drv->write_enable(chip);
if (err == FLASH_OK) {
err = chip->drv->wait_idle(chip, DEFAULT_IDLE_TIMEOUT);
}
if (err == FLASH_OK) {
chip->spi->user1.usr_addr_bitlen = (24 - 1);
chip->spi->addr = start_address & ADDRESS_MASK_24BIT;
chip->spi->cmd.flash_be = 1;
while(chip->spi->cmd.val != 0) { }
err = chip->drv->wait_idle(chip, SPI_FLASH_GENERIC_BLOCK_ERASE_TIMEOUT);
}
return err;
}
esp_flash_err_t spi_flash_generic_read(const esp_flash_chip_t *chip, void *buffer, uint32_t address, uint32_t length)
{
esp_flash_err_t err = FLASH_OK;
while (err == FLASH_OK && length > 0) {
uint32_t read_len = MIN(length, MAX_READ_BYTES);
chip->spi->miso_dlen.usr_miso_dbitlen = (read_len * 8) - 1;
chip->spi->addr = address << 8;
chip->spi->cmd.usr = 1;
while(chip->spi->cmd.val != 0) {}
if(((intptr_t)buffer % 4 == 0) && (read_len % 4 == 0)) {
// If everything is word-aligned, do a faster memcpy
xthal_memcpy(buffer, (void *)chip->spi->data_buf, read_len);
length -= read_len;
buffer = (void *)((intptr_t)buffer + read_len);
address += read_len;
} else {
// Otherwise, slow(er) path copies word by word
for (int i = 0; i < (read_len+3)/4; i++) {
int word_len = MIN(sizeof(uint32_t), length);
uint32_t word = chip->spi->data_buf[i];
xthal_memcpy(buffer, &word, word_len);
length -= word_len;
buffer = (void *)((intptr_t)buffer + word_len);
address += word_len;
}
}
}
return err;
}
esp_flash_err_t spi_flash_generic_page_program(const esp_flash_chip_t *chip, uint32_t address, const void *buffer, uint32_t length)
{
esp_flash_err_t err;
err = chip->drv->wait_idle(chip, DEFAULT_IDLE_TIMEOUT);
if (err == FLASH_OK) {
err = chip->drv->write_enable(chip);
}
if (err == FLASH_OK) {
// Perform the actual Page Program command
chip->spi->user.usr_dummy = 0;
chip->spi->user1.usr_addr_bitlen = (24 - 1);
chip->spi->addr = (address & ADDRESS_MASK_24BIT) | (length << 24);
// Load data registers, word at a time
int num_words = (length+3) / 4;
for (int i = 0; i < num_words; i++) {
uint32_t word = 0;
uint32_t word_len = MIN(length, sizeof(word));
xthal_memcpy(&word, buffer, word_len);
chip->spi->data_buf[i] = word;
length -= word_len;
buffer = (void *)((intptr_t)buffer + word_len);
}
chip->spi->cmd.flash_pp = 1;
while (chip->spi->cmd.val != 0) { }
err = chip->drv->wait_idle(chip, DEFAULT_PAGE_PROGRAM_TIMEOUT);
}
return err;
}
esp_flash_err_t spi_flash_generic_write(const esp_flash_chip_t *chip, uint32_t address, const void *buffer, uint32_t length)
{
esp_flash_err_t err = FLASH_OK;
const uint32_t page_size = chip->drv->page_size;
while (err == FLASH_OK && length > 0) {
uint32_t page_len = MIN(MAX_WRITE_BYTES, MIN(page_size, length));
if ((address + page_len) / page_size != address / page_size) {
// Most flash chips can't page write across a page boundary
page_len = page_size - (address % page_size);
}
err = chip->drv->page_program(chip, address, buffer, page_len);
address += page_len;
buffer = (void *)((intptr_t)buffer + page_len);
length -= page_len;
}
return err;
}
esp_flash_err_t spi_flash_generic_write_encrypted(const esp_flash_chip_t *chip, uint32_t address, const void *buffer, uint32_t length)
{
return FLASH_ERR_UNSUPPORTED_HOST; // TODO
}
esp_flash_err_t spi_flash_generic_write_enable(const esp_flash_chip_t *chip)
{
esp_flash_err_t err = FLASH_OK;
err = chip->drv->wait_idle(chip, DEFAULT_IDLE_TIMEOUT);
if (err == FLASH_OK) {
chip->spi->cmd.flash_wren = 1;
while(chip->spi->cmd.val != 0) { }
}
uint8_t status = spi_flash_common_command(chip, CMD_RDSR, 0, 0, 8);
if ((status & SR_WREN) == 0) {
// WREN flag has not been set!
err = FLASH_ERR_NOT_FOUND;
}
return err;
}
esp_flash_err_t spi_flash_generic_wait_host_idle(const esp_flash_chip_t *chip, uint32_t *timeout_ms)
{
while(chip->spi->ext2.st != 0 && *timeout_ms > 0) {
if (*timeout_ms > 1) {
esp_flash_os_functions->delay_ms(1);
}
(*timeout_ms)--;
}
// Not clear if this is necessary, or only necessary if
// chip->spi == SPI1. But probably doesn't hurt...
while(SPI0.ext2.st != 0 && *timeout_ms > 0) {
if (*timeout_ms > 1) {
esp_flash_os_functions->delay_ms(1);
}
(*timeout_ms)--;
}
return (*timeout_ms > 0) ? FLASH_OK : FLASH_ERR_NO_RESPONSE;
}
esp_flash_err_t spi_flash_generic_wait_idle(const esp_flash_chip_t *chip, uint32_t timeout_ms)
{
timeout_ms++; // allow at least one pass before timeout, last one has no sleep cycle
uint8_t status = 0;
while(timeout_ms > 0) {
esp_flash_err_t err = spi_flash_generic_wait_host_idle(chip, &timeout_ms);
if (err != FLASH_OK) {
return err;
}
status = spi_flash_common_command(chip, CMD_RDSR, 0, 0, 8);
if ((status & SR_WIP) == 0) {
break; // Write in progress is complete
}
if (timeout_ms > 1) {
esp_flash_os_functions->delay_ms(1);
}
timeout_ms--;
}
return (timeout_ms > 0) ? FLASH_OK : FLASH_ERR_NO_RESPONSE;
}
esp_flash_err_t spi_flash_common_configure_host_read_mode(const esp_flash_chip_t *chip)
{
int dummy_cyclelen, addr_bitlen, read_command;
switch(chip->read_mode) {
case ESP_FLASH_QIO:
addr_bitlen = 32;
dummy_cyclelen = 4; // TODO check this works
read_command = CMD_FASTRD_QIO;
break;
case ESP_FLASH_QOUT:
addr_bitlen = 24;
dummy_cyclelen = 8; // TODO check this works
read_command = CMD_FASTRD_QUAD;
break;
case ESP_FLASH_DIO:
addr_bitlen = 32;
dummy_cyclelen = 0;
read_command = CMD_FASTRD_DIO;
break;
case ESP_FLASH_DOUT:
addr_bitlen = 24;
dummy_cyclelen = 8;
read_command = CMD_FASTRD_DUAL;
break;
case ESP_FLASH_FASTRD:
addr_bitlen = 24;
dummy_cyclelen = 8;
read_command = CMD_FASTRD;
break;
case ESP_FLASH_SLOWRD:
addr_bitlen = 24;
dummy_cyclelen = 0;
read_command = CMD_READ;
break;
default:
return FLASH_ERR_NOT_INITIALISED;
}
// Add dummy cycles to compensate for GPIO matrix
// latency, if necessary...
if (spi_flash_uses_gpio_matrix(chip)) {
if (chip->speed == ESP_FLASH_80MHZ) {
dummy_cyclelen += 2;
} else if (chip->speed == ESP_FLASH_40MHZ) {
dummy_cyclelen += 1;
}
}
chip->spi->user1.usr_dummy_cyclelen = (dummy_cyclelen - 1);
chip->spi->user1.usr_addr_bitlen = (addr_bitlen - 1);
chip->spi->user2.usr_command_value = read_command;
chip->spi->user2.usr_command_bitlen = (8 - 1);
typeof (chip->spi->user) user = {
.usr_command = 1,
.usr_mosi = 0,
.usr_miso = 1,
.usr_dummy = (dummy_cyclelen > 0) ? 1 : 0,
.usr_addr = 1,
};
chip->spi->user = user;
typeof (chip->spi->ctrl) ctrl = {
.fread_qio = (chip->read_mode == ESP_FLASH_QIO),
.fread_quad = (chip->read_mode == ESP_FLASH_QOUT),
.fread_dio = (chip->read_mode == ESP_FLASH_DIO),
.fread_dual = (chip->read_mode == ESP_FLASH_DOUT),
.fastrd_mode = (chip->read_mode != ESP_FLASH_SLOWRD),
};
chip->spi->ctrl = ctrl;
return FLASH_OK;
}
#include <rom/ets_sys.h>
esp_flash_err_t spi_flash_common_set_read_mode(const esp_flash_chip_t *chip, uint8_t qe_rdsr_command, uint8_t qe_wrsr_command, uint8_t qe_sr_bitwidth, unsigned qe_sr_bit)
{
if (spi_flash_is_quad_mode(chip)) {
// Ensure quad modes are enabled, using the Quad Enable parameters supplied.
unsigned sr = spi_flash_common_command(chip, qe_rdsr_command, 0, 0, qe_sr_bitwidth);
ets_printf("before 0x%x\n", sr);
if ((sr & qe_sr_bit) == 0) {
sr |= qe_sr_bit;
spi_flash_common_command(chip, qe_wrsr_command, sr, qe_sr_bitwidth, 0);
/* Check the new QE bit has stayed set */
sr = spi_flash_common_command(chip, qe_rdsr_command, 0, 0, qe_sr_bitwidth);
ets_printf("after 0x%x\n", sr);
if ((sr & qe_sr_bit) == 0) {
return FLASH_ERR_NO_RESPONSE;
}
}
}
// Configure the host, and return
return spi_flash_common_configure_host_read_mode(chip);
}
esp_flash_err_t spi_flash_generic_set_read_mode(const esp_flash_chip_t *chip)
{
// On "generic" 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<<1;
return spi_flash_common_set_read_mode(chip, CMD_RDSR2, CMD_WRSR2, 8, BIT_QE);
}
bool spi_flash_uses_gpio_matrix(const esp_flash_chip_t *chip)
{
if (chip->pins == NULL) {
return false;
}
if (chip->pins->mosi_io_num != -1
|| chip->pins->miso_io_num != -1
|| chip->pins->sclk_io_num != -1) {
return true;
}
if (spi_flash_is_quad_mode(chip)) {
if (chip->pins->quadwp_io_num != -1
|| chip->pins->quadhd_io_num != -1) {
return true;
}
}
return false;
}
const esp_flash_driver_t esp_flash_generic_chip_driver = {
.probe = spi_flash_generic_probe,
.read_id = spi_flash_generic_read_id,
.detect_size = spi_flash_generic_detect_size,
.erase_chip = spi_flash_generic_erase_chip,
.erase_sector = spi_flash_generic_erase_sector,
.erase_block = spi_flash_generic_erase_block,
.sector_size = 4 * 1024,
.block_erase_size = 64 * 1024,
// TODO: figure out if generic chip-wide protection bits exist across some manufacturers
.get_chip_write_protect = NULL,
.set_chip_write_protect = NULL,
// Chip write protection regions do not appear to be standardised
// at all, this is implemented in chip-specific drivers only.
.num_protectable_regions = 0,
.protectable_regions = NULL,
.get_protected_regions = NULL,
.set_protected_regions = NULL,
.read = spi_flash_generic_read,
.write = spi_flash_generic_write,
.page_program = spi_flash_generic_page_program,
.page_size = 256,
.write_encrypted = spi_flash_generic_write_encrypted,
.write_enable = spi_flash_generic_write_enable,
.wait_idle = spi_flash_generic_wait_idle,
.set_read_mode = spi_flash_generic_set_read_mode,
};

View file

@ -1,60 +0,0 @@
// Copyright 2017 Espressif Systems (Shanghai) PTE LTD
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
#include <stdarg.h>
#include "spi_flash_lowlevel_driver.h"
#include "rom/ets_sys.h"
#include "esp_attr.h"
#include "esp_spi_flash.h"
static esp_flash_err_t start(const esp_flash_chip_t *chip)
{
if (chip->spi == &SPI1) {
g_flash_guard_default_ops.start();
}
// TODO figure out if we can coexist with the SPI master driver here, for other peripherals
return FLASH_OK;
}
static esp_flash_err_t end(const esp_flash_chip_t *chip)
{
if (chip->spi == &SPI1) {
g_flash_guard_default_ops.end();
}
// TODO figure out if we can coexist with the SPI master driver here, for other peripherals
return FLASH_OK;
}
static esp_flash_err_t delay_ms(unsigned ms)
{
ets_delay_us(1000 * ms);
return FLASH_OK;
}
const esp_flash_os_functions_t default_os_functions = {
.start = start,
.end = end,
.delay_ms = delay_ms,
};
void esp_flash_low_level_app_init()
{
esp_flash_os_functions = &default_os_functions;
}

View file

@ -0,0 +1,125 @@
// Copyright 2015-2019 Espressif Systems (Shanghai) PTE LTD
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
#include <stdarg.h>
#include "esp32/rom/ets_sys.h"
#include "esp_attr.h"
#include "esp_spi_flash.h" //for ``g_flash_guard_default_ops``
#include "esp_flash.h"
/*
* OS functions providing delay service and arbitration among chips, and with the cache.
*
* The cache needs to be disabled when chips on the SPI1 bus is under operation, hence these functions need to be put
* into the IRAM,and their data should be put into the DRAM.
*/
typedef struct {
int host_id;
} app_func_arg_t;
// in the future we will have arbitration among devices, including flash on the same flash bus
static IRAM_ATTR esp_err_t spi_bus_acquire(int host_id)
{
return ESP_OK;
}
static IRAM_ATTR esp_err_t spi_bus_release(int host_id)
{
return ESP_OK;
}
//for SPI1, we have to disable the cache and interrupts before using the SPI bus
static IRAM_ATTR esp_err_t spi1_start(void *arg)
{
g_flash_guard_default_ops.start();
spi_bus_acquire(((app_func_arg_t *)arg)->host_id);
return ESP_OK;
}
static IRAM_ATTR esp_err_t spi1_end(void *arg)
{
g_flash_guard_default_ops.end();
spi_bus_release(((app_func_arg_t *)arg)->host_id);
return ESP_OK;
}
static esp_err_t spi23_start(void *arg)
{
spi_bus_acquire(((app_func_arg_t *)arg)->host_id);
return ESP_OK;
}
static esp_err_t spi23_end(void *arg)
{
spi_bus_release(((app_func_arg_t *)arg)->host_id);
return ESP_OK;
}
static IRAM_ATTR esp_err_t delay_ms(void *arg, unsigned ms)
{
ets_delay_us(1000 * ms);
return ESP_OK;
}
static DRAM_ATTR app_func_arg_t spi1_arg = {
.host_id = 0, //for SPI1,
};
static app_func_arg_t spi2_arg = {
.host_id = 1, //for SPI2,
};
static app_func_arg_t spi3_arg = {
.host_id = 2, //for SPI3,
};
//for SPI1, we have to disable the cache and interrupts before using the SPI bus
const DRAM_ATTR esp_flash_os_functions_t spi1_default_os_functions = {
.start = spi1_start,
.end = spi1_end,
.delay_ms = delay_ms,
};
const esp_flash_os_functions_t spi23_default_os_functions = {
.start = spi23_start,
.end = spi23_end,
.delay_ms = delay_ms,
};
esp_err_t esp_flash_init_os_functions(esp_flash_t *chip, int host_id)
{
if (host_id == 0) {
//SPI1
chip->os_func = &spi1_default_os_functions;
chip->os_func_data = &spi1_arg;
} else if (host_id == 1 || host_id == 2) {
//SPI2,3
chip->os_func = &spi23_default_os_functions;
chip->os_func_data = (host_id == 1) ? &spi2_arg : &spi3_arg;
} else {
return ESP_ERR_INVALID_ARG;
}
return ESP_OK;
}
esp_err_t esp_flash_app_init()
{
return esp_flash_init_os_functions(esp_flash_default_chip, 0);
}

View file

@ -1,9 +1,9 @@
// Copyright 2017 Espressif Systems (Shanghai) PTE LTD
// Copyright 2015-2019 Espressif Systems (Shanghai) PTE LTD
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
@ -11,37 +11,34 @@
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
#include <stdarg.h>
#include "spi_flash_lowlevel_driver.h"
#include "esp_flash.h"
#include "rom/ets_sys.h"
#include "rom/cache.h"
#include "esp32/rom/ets_sys.h"
#include "esp32/rom/cache.h"
#include "esp_attr.h"
#include "esp_spi_flash.h"
static esp_flash_err_t start(const esp_flash_chip_t *chip)
static esp_err_t start(void *arg)
{
if (chip->spi == &SPI1) {
Cache_Read_Disable(0);
Cache_Read_Disable(1);
}
return FLASH_OK;
Cache_Read_Disable(0);
Cache_Read_Disable(1);
return ESP_OK;
}
static esp_flash_err_t end(const esp_flash_chip_t *chip)
static esp_err_t end(void *arg)
{
if (chip->spi == &SPI1) {
Cache_Flush(0);
Cache_Flush(1);
Cache_Read_Enable(0);
Cache_Read_Enable(1);
}
return FLASH_OK;
Cache_Flush(0);
Cache_Flush(1);
Cache_Read_Enable(0);
Cache_Read_Enable(1);
return ESP_OK;
}
static esp_flash_err_t delay_ms(unsigned ms)
static esp_err_t delay_ms(void *arg, unsigned ms)
{
ets_delay_us(1000 * ms);
return FLASH_OK;
return ESP_OK;
}
const esp_flash_os_functions_t esp_flash_noos_functions = {

View file

@ -30,7 +30,7 @@ static IRAM_ATTR void cache_test_task(void *arg)
vTaskDelete(NULL);
}
TEST_CASE("spi_flash_cache_enabled() works on both CPUs", "[spi_flash]")
TEST_CASE("spi_flash_cache_enabled() works on both CPUs", "[spi_flash][esp_flash]")
{
result_queue = xQueueCreate(1, sizeof(bool));

View file

@ -0,0 +1,549 @@
#include <stdio.h>
#include <string.h>
#include <freertos/FreeRTOS.h>
#include <freertos/task.h>
#include <freertos/semphr.h>
#include <unity.h>
#include "esp_flash.h"
#include "spi_flash_chip_generic.h"
#include <esp_attr.h>
#include "esp_log.h"
#include <test_utils.h>
#include "unity.h"
#include "driver/spi_common.h"
#include "memspi_host_driver.h"
#include "driver/gpio.h"
#include "soc/io_mux_reg.h"
#define FUNC_SPI 1
static uint8_t sector_buf[4096];
// #define TEST_SPI1_CS1
// #define TEST_SPI2_CS0
// #define TEST_SPI3_CS0
#define TEST_SPI_SPEED ESP_FLASH_10MHZ
#define TEST_SPI_READ_MODE SPI_FLASH_FASTRD
//#define FORCE_GPIO_MATRIX
#ifdef TEST_SPI2_CS0
#define TEST_HOST HSPI_HOST
#define TEST_CS 0
#define TEST_CS_PIN HSPI_IOMUX_PIN_NUM_CS
#define HSPI_PIN_NUM_MOSI HSPI_IOMUX_PIN_NUM_MOSI
#define HSPI_PIN_NUM_MISO HSPI_IOMUX_PIN_NUM_MISO
#define HSPI_PIN_NUM_CLK HSPI_IOMUX_PIN_NUM_CLK
#define HSPI_PIN_NUM_HD HSPI_IOMUX_PIN_NUM_HD
#define HSPI_PIN_NUM_WP HSPI_IOMUX_PIN_NUM_WP
#define TEST_INPUT_DELAY 20
#elif defined TEST_SPI3_CS0
#define TEST_HOST VSPI_HOST
#define TEST_CS 0
#define TEST_CS_PIN VSPI_IOMUX_PIN_NUM_CS
#define VSPI_PIN_NUM_MOSI VSPI_IOMUX_PIN_NUM_MOSI
#define VSPI_PIN_NUM_MISO VSPI_IOMUX_PIN_NUM_MISO
#define VSPI_PIN_NUM_CLK VSPI_IOMUX_PIN_NUM_CLK
#define VSPI_PIN_NUM_HD VSPI_IOMUX_PIN_NUM_HD
#define VSPI_PIN_NUM_WP VSPI_IOMUX_PIN_NUM_WP
#define TEST_INPUT_DELAY 0
#elif defined TEST_SPI1_CS1
#define TEST_HOST SPI_HOST
#define TEST_CS 1
// #define TEST_CS_PIN 14
#define TEST_CS_PIN 16 //the pin which is usually used by the PSRAM
// #define TEST_CS_PIN 27
#define TEST_INPUT_DELAY 25
#define EXTRA_SPI1_CLK_IO 17 //the pin which is usually used by the PSRAM clk
#else
#define SKIP_EXTENDED_CHIP_TEST
#endif
static const char TAG[] = "test_esp_flash";
#ifndef SKIP_EXTENDED_CHIP_TEST
static esp_flash_t *test_chip = NULL;
static esp_flash_t chip_init;
static spi_flash_host_driver_t chip_host_driver;
static memspi_host_data_t driver_data = {};
static void IRAM_ATTR cs_initialize(spi_host_device_t host, int cs_io_num, int cs_num, bool use_iomux)
{
int spics_in = spi_periph_signal[host].spics_in;
int spics_out = spi_periph_signal[host].spics_out[cs_num];
uint32_t iomux_reg = GPIO_PIN_MUX_REG[TEST_CS_PIN];
//to avoid the panic caused by flash data line conflicts during cs line initialization, disable the cache temporarily
//some data from flash to be used should be read before the cache disabling
g_flash_guard_default_ops.start();
if (use_iomux) {
GPIO.func_in_sel_cfg[spics_in].sig_in_sel = 0;
PIN_INPUT_ENABLE(iomux_reg);
GPIO.func_out_sel_cfg[spics_out].oen_sel = 0;
GPIO.func_out_sel_cfg[spics_out].oen_inv_sel = false;
PIN_FUNC_SELECT(iomux_reg, FUNC_SPI);
} else {
PIN_INPUT_ENABLE(iomux_reg);
if (cs_io_num < 32) {
GPIO.enable_w1ts = (0x1 << cs_io_num);
} else {
GPIO.enable1_w1ts.data = (0x1 << (cs_io_num - 32));
}
GPIO.pin[cs_io_num].pad_driver = 0;
gpio_matrix_out(cs_io_num, spics_out, false, false);
if (cs_num == 0) {
gpio_matrix_in(cs_io_num, spics_in, false);
}
PIN_FUNC_SELECT(iomux_reg, PIN_FUNC_GPIO);
}
g_flash_guard_default_ops.end();
}
static void setup_new_chip(esp_flash_read_mode_t io_mode, esp_flash_speed_t speed)
{
chip_init = (esp_flash_t) {
.read_mode = io_mode,
};
#ifdef TEST_SPI2_CS0
bool spi_chan_claimed = spicommon_periph_claim(HSPI_HOST, "spi flash");
TEST_ASSERT(spi_chan_claimed);
spi_bus_config_t hspi_bus_cfg = {
.mosi_io_num = HSPI_PIN_NUM_MOSI,
.miso_io_num = HSPI_PIN_NUM_MISO,
.sclk_io_num = HSPI_PIN_NUM_CLK,
.quadhd_io_num = HSPI_PIN_NUM_HD,
.quadwp_io_num = HSPI_PIN_NUM_WP,
.max_transfer_sz = 64,
};
#ifdef FORCE_GPIO_MATRIX
hspi_bus_cfg.quadhd_io_num = 23;
#endif
uint32_t flags;
esp_err_t ret = spicommon_bus_initialize_io(HSPI_HOST, &hspi_bus_cfg, 0, SPICOMMON_BUSFLAG_MASTER | (&hspi_bus_cfg)->flags, &flags);
TEST_ESP_OK(ret);
bool use_iomux = (flags & SPICOMMON_BUSFLAG_NATIVE_PINS) ? 1 : 0;
printf("setup flash on SPI2 (HSPI) CS0...\n");
printf("use iomux:%d\n", use_iomux);
memspi_host_config_t cfg = {
.host_id = 2,
.speed = speed,
.iomux = use_iomux,
.cs_num = TEST_CS,
.input_delay_ns = TEST_INPUT_DELAY,
};
#elif defined TEST_SPI3_CS0
bool spi_chan_claimed = spicommon_periph_claim(VSPI_HOST, "spi flash");
TEST_ASSERT(spi_chan_claimed);
spi_bus_config_t vspi_bus_cfg = {
.mosi_io_num = VSPI_PIN_NUM_MOSI,
.miso_io_num = VSPI_PIN_NUM_MISO,
.sclk_io_num = VSPI_PIN_NUM_CLK,
.quadhd_io_num = VSPI_PIN_NUM_HD,
.quadwp_io_num = VSPI_PIN_NUM_WP,
.max_transfer_sz = 64,
};
#ifdef FORCE_GPIO_MATRIX
vspi_bus_cfg.quadhd_io_num = 23;
#endif
uint32_t flags;
esp_err_t ret = spicommon_bus_initialize_io(VSPI_HOST, &vspi_bus_cfg, 0, SPICOMMON_BUSFLAG_MASTER | (&vspi_bus_cfg)->flags, &flags);
TEST_ESP_OK(ret);
bool use_iomux = (flags & SPICOMMON_BUSFLAG_NATIVE_PINS) ? 1 : 0;
//TEST_ASSERT(use_iomux);
printf("setup flash on SPI3 (VSPI) CS0...\n");
printf("use iomux:%d\n", use_iomux);
memspi_host_config_t cfg = {
.host_id = 3,
.speed = speed,
.iomux = use_iomux,
.cs_num = TEST_CS,
.input_delay_ns = TEST_INPUT_DELAY,
};
#elif defined TEST_SPI1_CS1
printf("setup flash on SPI1 CS1...\n");
memspi_host_config_t cfg = {
.host_id = 1,
.speed = speed,
.iomux = true,
.cs_num = TEST_CS,
.input_delay_ns = TEST_INPUT_DELAY,
};
bool use_iomux = (TEST_CS_PIN == spi_periph_signal[TEST_HOST].spics0_iomux_pin) && (driver_data.cs_num == 0);
# ifdef EXTRA_SPI1_CLK_IO
gpio_matrix_out(EXTRA_SPI1_CLK_IO, SPICLK_OUT_IDX, 0, 0);
# endif
#endif
esp_err_t err = memspi_host_init_pointers(&chip_host_driver, &driver_data, &cfg);
cs_initialize(TEST_HOST, TEST_CS_PIN, driver_data.cs_num, use_iomux);
TEST_ESP_OK(err);
chip_init.host = &chip_host_driver;
esp_flash_init_os_functions(&chip_init, TEST_HOST);
err = esp_flash_init(&chip_init);
TEST_ESP_OK(err);
test_chip = &chip_init;
}
void teardown_test_chip()
{
if (TEST_HOST == HSPI_HOST || TEST_HOST == VSPI_HOST) {
spicommon_periph_free(TEST_HOST);
}
}
#endif
static void test_metadata(esp_flash_t *chip)
{
ESP_LOGI(TAG, "Testing chip %p...", chip);
uint32_t id, size;
TEST_ESP_OK(esp_flash_read_id(chip, &id));
TEST_ESP_OK(esp_flash_get_size(chip, &size));
printf("Flash ID %08x detected size %d bytes\n", id, size);
}
TEST_CASE("SPI flash metadata functions", "[esp_flash]")
{
#ifndef SKIP_EXTENDED_CHIP_TEST
setup_new_chip(TEST_SPI_READ_MODE, TEST_SPI_SPEED);
test_metadata(test_chip);
teardown_test_chip();
#endif
test_metadata(NULL);
}
static uint32_t erase_test_region(esp_flash_t *chip, int num_sectors)
{
const esp_partition_t *part = get_test_data_partition();
uint32_t offs = part->address;
/* chip should be initialised */
TEST_ASSERT(esp_flash_default_chip != NULL
&& esp_flash_chip_driver_initialized(esp_flash_default_chip));
TEST_ASSERT(num_sectors * 4096 <= part->size);
bzero(sector_buf, sizeof(sector_buf));
printf("Erase @ 0x%x...\n", offs);
TEST_ASSERT_EQUAL_HEX32(ESP_OK, esp_flash_erase_region(chip, offs, num_sectors * 4096) );
printf("Verify erased...\n");
for (int i = 0; i < num_sectors; i++) {
TEST_ASSERT_EQUAL_HEX32(ESP_OK, esp_flash_read(chip, sector_buf, offs + i * 4096, sizeof(sector_buf)));
printf("Buffer starts 0x%02x 0x%02x 0x%02x 0x%02x\n", sector_buf[0], sector_buf[1], sector_buf[2], sector_buf[3]);
for (int i = 0; i < sizeof(sector_buf); i++) {
TEST_ASSERT_EQUAL_HEX8(0xFF, sector_buf[i]);
}
}
return offs;
}
void test_simple_read_write(void *chip)
{
ESP_LOGI(TAG, "Testing chip %p...", chip);
uint32_t offs = erase_test_region(chip, 1);
const int test_seed = 778;
srand(test_seed);
for (int i = 0 ; i < sizeof(sector_buf); i++) {
sector_buf[i] = rand();
}
printf("Write %p...\n", (void *)offs);
TEST_ASSERT_EQUAL(ESP_OK, esp_flash_write(chip, sector_buf, offs, sizeof(sector_buf)) );
bzero(sector_buf, sizeof(sector_buf));
printf("Read back...\n");
TEST_ASSERT_EQUAL(ESP_OK, esp_flash_read(chip, sector_buf, offs, sizeof(sector_buf)) );
printf("Buffer starts 0x%02x 0x%02x 0x%02x 0x%02x\n", sector_buf[0], sector_buf[1], sector_buf[2], sector_buf[3]);
srand(test_seed);
for (int i = 0; i < sizeof(sector_buf); i++) {
TEST_ASSERT_EQUAL_HEX8(rand() & 0xFF, sector_buf[i]);
}
}
TEST_CASE("SPI flash simple read/write", "[esp_flash]")
{
test_simple_read_write(NULL);
#ifndef SKIP_EXTENDED_CHIP_TEST
setup_new_chip(TEST_SPI_READ_MODE, TEST_SPI_SPEED);
test_simple_read_write(test_chip);
teardown_test_chip();
#endif
}
void test_unaligned_read_write(void *chip)
{
ESP_LOGI(TAG, "Testing chip %p...", chip);
uint32_t offs = erase_test_region(chip, 2);
const char *msg = "i am a message";
TEST_ASSERT(strlen(msg) + 1 % 4 != 0);
TEST_ASSERT_EQUAL(ESP_OK, esp_flash_write(chip, msg, offs + 1, strlen(msg) + 1) );
char buf[strlen(msg) + 1];
memset(buf, 0xEE, sizeof(buf));
TEST_ASSERT_EQUAL(ESP_OK, esp_flash_read(chip, buf, offs + 1, strlen(msg) + 1) );
TEST_ASSERT_EQUAL_STRING_LEN(msg, buf, strlen(msg));
TEST_ASSERT(memcmp(buf, msg, strlen(msg) + 1) == 0);
}
TEST_CASE("SPI flash unaligned read/write", "[esp_flash]")
{
#ifndef SKIP_EXTENDED_CHIP_TEST
setup_new_chip(TEST_SPI_READ_MODE, TEST_SPI_SPEED);
test_unaligned_read_write(test_chip);
teardown_test_chip();
#endif
test_unaligned_read_write(NULL);
}
void test_single_read_write(void *chip)
{
ESP_LOGI(TAG, "Testing chip %p...", chip);
uint32_t offs = erase_test_region(chip, 2);
for (unsigned v = 0; v < 512; v++) {
TEST_ASSERT_EQUAL_HEX(ESP_OK, esp_flash_write(chip, &v, offs + v, 1) );
}
for (unsigned v = 0; v < 512; v++) {
uint8_t readback;
TEST_ASSERT_EQUAL_HEX(ESP_OK, esp_flash_read(chip, &readback, offs + v, 1) );
TEST_ASSERT_EQUAL_HEX8(v, readback);
}
}
TEST_CASE("SPI flash single byte reads/writes", "[esp_flash]")
{
test_single_read_write(NULL);
#ifndef SKIP_EXTENDED_CHIP_TEST
setup_new_chip(TEST_SPI_READ_MODE, TEST_SPI_SPEED);
test_single_read_write(test_chip);
teardown_test_chip();
#endif
}
/* this test is notable because it generates a lot of unaligned reads/writes,
and also reads/writes across both a sector boundary & many page boundaries.
*/
void test_three_byte_read_write(void *chip)
{
ESP_LOGI(TAG, "Testing chip %p...", chip);
uint32_t offs = erase_test_region(chip, 2);
ets_printf("offs:%X\n", offs);
for (uint32_t v = 0; v < 2000; v++) {
TEST_ASSERT_EQUAL(ESP_OK, esp_flash_write(chip, &v, offs + 3 * v, 3) );
}
for (uint32_t v = 0; v < 2000; v++) {
uint32_t readback;
TEST_ASSERT_EQUAL(ESP_OK, esp_flash_read(chip, &readback, offs + 3 * v, 3) );
TEST_ASSERT_EQUAL_HEX32(v & 0xFFFFFF, readback & 0xFFFFFF);
}
}
TEST_CASE("SPI flash three byte reads/writes", "[esp_flash]")
{
#ifndef SKIP_EXTENDED_CHIP_TEST
setup_new_chip(TEST_SPI_READ_MODE, TEST_SPI_SPEED);
test_three_byte_read_write(test_chip);
teardown_test_chip();
#endif
test_three_byte_read_write(NULL);
}
void test_erase_large_region(esp_flash_t *chip)
{
ESP_LOGI(TAG, "Testing chip %p...", chip);
const esp_partition_t *part = get_test_data_partition();
/* Write some noise at the start and the end of the region */
const char *ohai = "OHAI";
uint32_t readback;
TEST_ASSERT_EQUAL(ESP_OK, esp_flash_write(chip, ohai, part->address, 5));
TEST_ASSERT_EQUAL(ESP_OK, esp_flash_write(chip, ohai, part->address + part->size - 5, 5));
/* sanity check what we just wrote. since the partition may haven't been erased, we only check the part which is written to 0. */
uint32_t written_data = *((const uint32_t *)ohai);
TEST_ASSERT_EQUAL(ESP_OK, esp_flash_read(chip, &readback, part->address + part->size - 5, 4));
TEST_ASSERT_EQUAL_HEX32(0, readback & (~written_data));
TEST_ASSERT_EQUAL(ESP_OK, esp_flash_read(chip, &readback, part->address, 4));
TEST_ASSERT_EQUAL_HEX32(0, readback & (~written_data));
/* Erase whole region */
TEST_ASSERT_EQUAL(ESP_OK, esp_flash_erase_region(chip, part->address, part->size));
/* ensure both areas we wrote are now all-FFs */
TEST_ASSERT_EQUAL(ESP_OK, esp_flash_read(chip, &readback, part->address, 4));
TEST_ASSERT_EQUAL_HEX32(0xFFFFFFFF, readback);
TEST_ASSERT_EQUAL(ESP_OK, esp_flash_read(chip, &readback, part->address + part->size - 5, 4));
TEST_ASSERT_EQUAL_HEX32(0xFFFFFFFF, readback);
}
TEST_CASE("SPI flash erase large region", "[esp_flash]")
{
test_erase_large_region(NULL);
#ifndef SKIP_EXTENDED_CHIP_TEST
setup_new_chip(TEST_SPI_READ_MODE, TEST_SPI_SPEED);
test_erase_large_region(test_chip);
teardown_test_chip();
#endif
}
static const uint8_t large_const_buffer[16400] = {
203, // first byte
1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20,
21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37,
[50 ... 99] = 2,
[1600 ... 2000] = 3,
[8000 ... 9000] = 77,
[15000 ... 16398] = 8,
43 // last byte
};
static void test_write_large_buffer(esp_flash_t *chip, const uint8_t *source, size_t length);
static void write_large_buffer(esp_flash_t *chip, const esp_partition_t *part, const uint8_t *source, size_t length);
static void read_and_check(esp_flash_t *chip, const esp_partition_t *part, const uint8_t *source, size_t length);
TEST_CASE("SPI flash test reading with all speed/mode permutations", "[esp_flash]")
{
const int length = sizeof(large_const_buffer);
uint8_t *source_buf = malloc(length);
TEST_ASSERT_NOT_NULL(source_buf);
srand(778);
for (int i = 0; i < length; i++) {
source_buf[i] = rand();
}
const esp_partition_t *part = get_test_data_partition();
TEST_ASSERT(part->size > length + 2 + SPI_FLASH_SEC_SIZE);
#ifndef SKIP_EXTENDED_CHIP_TEST
//use the lowest speed to write and read to make sure success
setup_new_chip(TEST_SPI_READ_MODE, ESP_FLASH_SPEED_MIN);
write_large_buffer(test_chip, part, source_buf, length);
read_and_check(test_chip, part, source_buf, length);
teardown_test_chip();
esp_flash_read_mode_t io_mode = SPI_FLASH_READ_MODE_MIN;
while (io_mode != SPI_FLASH_READ_MODE_MAX) {
esp_flash_speed_t speed = ESP_FLASH_SPEED_MIN;
while (speed != ESP_FLASH_SPEED_MAX) {
ESP_LOGI(TAG, "test flash io mode: %d, speed: %d", io_mode, speed);
setup_new_chip(io_mode, speed);
read_and_check(test_chip, part, source_buf, length);
teardown_test_chip();
speed++;
}
io_mode++;
}
#endif
//test main flash BTW
write_large_buffer(NULL, part, source_buf, length);
read_and_check(NULL, part, source_buf, length);
free(source_buf);
}
TEST_CASE("Test esp_flash_write large const buffer", "[esp_flash]")
{
//buffer in flash
test_write_large_buffer(NULL, large_const_buffer, sizeof(large_const_buffer));
#ifndef SKIP_EXTENDED_CHIP_TEST
setup_new_chip(TEST_SPI_READ_MODE, TEST_SPI_SPEED);
test_write_large_buffer(test_chip, large_const_buffer, sizeof(large_const_buffer));
teardown_test_chip();
#endif
}
#ifndef SKIP_EXTENDED_CHIP_TEST
TEST_CASE("Test esp_flash_write large RAM buffer", "[esp_flash]")
{
// buffer in RAM
uint8_t *source_buf = malloc(sizeof(large_const_buffer));
TEST_ASSERT_NOT_NULL(source_buf);
memcpy(source_buf, large_const_buffer, sizeof(large_const_buffer));
setup_new_chip(TEST_SPI_READ_MODE, TEST_SPI_SPEED);
test_write_large_buffer(test_chip, source_buf, sizeof(large_const_buffer));
teardown_test_chip();
free(source_buf);
}
#endif
static void write_large_buffer(esp_flash_t *chip, const esp_partition_t *part, const uint8_t *source, size_t length)
{
printf("Writing chip %p, %d bytes from source %p\n", chip, length, source);
ESP_ERROR_CHECK( esp_flash_erase_region(chip, part->address, (length + SPI_FLASH_SEC_SIZE) & ~(SPI_FLASH_SEC_SIZE - 1)) );
// note writing to unaligned address
ESP_ERROR_CHECK( esp_flash_write(chip, source, part->address + 1, length) );
}
static void read_and_check(esp_flash_t *chip, const esp_partition_t *part, const uint8_t *source, size_t length)
{
printf("Checking chip %p, %d bytes\n", chip, length);
uint8_t *buf = malloc(length);
TEST_ASSERT_NOT_NULL(buf);
ESP_ERROR_CHECK( esp_flash_read(chip, buf, part->address + 1, length) );
TEST_ASSERT_EQUAL_HEX8_ARRAY(source, buf, length);
free(buf);
// check nothing was written at beginning or end
uint8_t ends[8];
ESP_ERROR_CHECK( esp_flash_read(chip, ends, part->address, sizeof(ends)) );
TEST_ASSERT_EQUAL_HEX8(0xFF, ends[0]);
TEST_ASSERT_EQUAL_HEX8(source[0], ends[1]);
ESP_ERROR_CHECK( esp_flash_read(chip, ends, part->address + length, sizeof(ends)) );
TEST_ASSERT_EQUAL_HEX8(source[length - 1], ends[0]);
TEST_ASSERT_EQUAL_HEX8(0xFF, ends[1]);
TEST_ASSERT_EQUAL_HEX8(0xFF, ends[2]);
TEST_ASSERT_EQUAL_HEX8(0xFF, ends[3]);
}
static void test_write_large_buffer(esp_flash_t *chip, const uint8_t *source, size_t length)
{
ESP_LOGI(TAG, "Testing chip %p...", chip);
const esp_partition_t *part = get_test_data_partition();
TEST_ASSERT(part->size > length + 2 + SPI_FLASH_SEC_SIZE);
write_large_buffer(chip, part, source, length);
read_and_check(chip, part, source, length);
}

View file

@ -42,13 +42,13 @@ static const uint8_t large_const_buffer[16400] = {
static void test_write_large_buffer(const uint8_t *source, size_t length);
TEST_CASE("Test spi_flash_write large const buffer", "[spi_flash]")
TEST_CASE("Test spi_flash_write large const buffer", "[spi_flash][esp_flash]")
{
// buffer in flash
test_write_large_buffer(large_const_buffer, sizeof(large_const_buffer));
}
TEST_CASE("Test spi_flash_write large RAM buffer", "[spi_flash]")
TEST_CASE("Test spi_flash_write large RAM buffer", "[spi_flash][esp_flash]")
{
// buffer in RAM
uint8_t *source_buf = malloc(sizeof(large_const_buffer));

View file

@ -1,181 +0,0 @@
#include <stdio.h>
#include <string.h>
#include <freertos/FreeRTOS.h>
#include <freertos/task.h>
#include <freertos/semphr.h>
#include <unity.h>
#include <spi_flash_lowlevel.h>
#include <esp_attr.h>
#include <test_utils.h>
static uint8_t sector_buf[4096];
TEST_CASE("SPI flash metadata functions", "[spi_flash_ll]")
{
uint32_t id, size;
TEST_ASSERT_EQUAL(FLASH_OK, esp_flash_read_id(NULL, &id) );
TEST_ASSERT_EQUAL(FLASH_OK, esp_flash_detect_size(NULL, &size) );
printf("Flash ID %08x detected size %d bytes\n", id, size);
}
static uint32_t erase_test_region(int num_sectors)
{
const esp_partition_t *part = get_test_data_partition();
uint32_t offs = part->address;
/* chip should be initialised */
TEST_ASSERT(esp_flash_default_chip != NULL
&& esp_flash_default_chip->drv != NULL);
TEST_ASSERT(num_sectors * 4096 <= part->size);
bzero(sector_buf, sizeof(sector_buf));
printf("Erase @ 0x%x...\n", offs);
TEST_ASSERT_EQUAL(FLASH_OK, esp_flash_erase_region(NULL, offs, num_sectors * 4096) );
printf("Verify erased...\n");
TEST_ASSERT_EQUAL(FLASH_OK, esp_flash_read(NULL, sector_buf, offs, sizeof(sector_buf)) );
printf("Buffer starts 0x%02x 0x%02x 0x%02x 0x%02x\n", sector_buf[0], sector_buf[1], sector_buf[2], sector_buf[3]);
for (int i = 0; i < sizeof(sector_buf); i++) {
TEST_ASSERT_EQUAL_HEX8(0xFF, sector_buf[i]);
}
return offs;
}
TEST_CASE("SPI flash simple read/write", "[spi_flash_ll]")
{
uint32_t offs = erase_test_region(1);
for (int i =0 ; i < sizeof(sector_buf); i++) {
sector_buf[i] = i & 0xFF;
}
printf("Write...\n");
TEST_ASSERT_EQUAL(FLASH_OK, esp_flash_write(NULL, offs, sector_buf, sizeof(sector_buf)) );
bzero(sector_buf, sizeof(sector_buf));
printf("Read back...\n");
TEST_ASSERT_EQUAL(FLASH_OK, esp_flash_read(NULL, sector_buf, offs, sizeof(sector_buf)) );
printf("Buffer starts 0x%02x 0x%02x 0x%02x 0x%02x\n", sector_buf[0], sector_buf[1], sector_buf[2], sector_buf[3]);
for (int i = 0; i < sizeof(sector_buf); i++) {
TEST_ASSERT_EQUAL_HEX8(i & 0xFF, sector_buf[i]);
}
}
TEST_CASE("SPI flash unaligned read/write", "[spi_flash_ll]")
{
uint32_t offs = erase_test_region(2);
const char *msg = "i am a message";
TEST_ASSERT(strlen(msg)+1 % 4 != 0);
TEST_ASSERT_EQUAL(FLASH_OK, esp_flash_write(NULL, offs+1, msg, strlen(msg)+1) );
char buf[strlen(msg) + 1];
memset(buf, 0xEE, sizeof(buf));
TEST_ASSERT_EQUAL(FLASH_OK, esp_flash_read(NULL, buf, offs+1, strlen(msg)+1) );
TEST_ASSERT_EQUAL_STRING_LEN(msg, buf, strlen(msg));
TEST_ASSERT(memcmp(buf, msg, strlen(msg)+1) == 0);
}
TEST_CASE("SPI flash single byte reads/writes", "[spi_flash_ll]")
{
uint32_t offs = erase_test_region(2);
for (unsigned v = 0; v < 512; v++) {
TEST_ASSERT_EQUAL(FLASH_OK, esp_flash_write(NULL, offs+v, &v, 1) );
}
for (unsigned v = 0; v < 512; v++) {
uint8_t readback;
TEST_ASSERT_EQUAL(FLASH_OK, esp_flash_read(NULL, &readback, offs+v, 1) );
TEST_ASSERT_EQUAL_HEX8(v, readback);
}
}
/* this test is notable because it generates a lot of unaligned reads/writes,
and also reads/writes across both a sector boundary & many page boundaries.
*/
TEST_CASE("SPI flash three byte reads/writes", "[spi_flash_ll]")
{
uint32_t offs = erase_test_region(2);
for (uint32_t v = 0; v < 2000; v++) {
TEST_ASSERT_EQUAL(FLASH_OK, esp_flash_write(NULL, offs+3*v, &v, 3) );
}
for (uint32_t v = 0; v < 2000; v++) {
uint32_t readback;
TEST_ASSERT_EQUAL(FLASH_OK, esp_flash_read(NULL, &readback, offs+3*v, 3) );
TEST_ASSERT_EQUAL_HEX32(v & 0xFFFFFF, readback & 0xFFFFFF);
}
}
TEST_CASE("SPI flash erase large region", "[spi_flash_ll]")
{
const esp_partition_t *part = get_test_data_partition();
/* Write some noise at the start and the end of the region */
const char *ohai = "OHAI";
uint32_t readback;
TEST_ASSERT_EQUAL(FLASH_OK, esp_flash_write(NULL, part->address, ohai, 5));
TEST_ASSERT_EQUAL(FLASH_OK, esp_flash_write(NULL, part->address + part->size - 5, ohai, 5));
/* sanity check what we just wrote */
TEST_ASSERT_EQUAL(FLASH_OK, esp_flash_read(NULL, &readback, part->address + part->size - 5, 4));
TEST_ASSERT_EQUAL_HEX32(*((const uint32_t*)ohai), readback);
/* Erase whole region */
TEST_ASSERT_EQUAL(FLASH_OK, esp_flash_erase_region(NULL, part->address, part->size));
/* ensure both areas we wrote are now all-FFs */
TEST_ASSERT_EQUAL(FLASH_OK, esp_flash_read(NULL, &readback, part->address, 4));
TEST_ASSERT_EQUAL_HEX32(0xFFFFFFFF, readback);
TEST_ASSERT_EQUAL(FLASH_OK, esp_flash_read(NULL, &readback, part->address + part->size - 5, 4));
TEST_ASSERT_EQUAL_HEX32(0xFFFFFFFF, readback);
}
TEST_CASE("SPI flash test reading with all speed/mode permutations", "[spi_flash_ll]")
{
/* Note: this only works if the SPI flash chip supports all these modes & speeds */
uint32_t offs = erase_test_region(1);
/* Write some test data */
const char *message = "This is some test data.";
TEST_ASSERT_EQUAL(FLASH_OK, esp_flash_write(NULL, offs, message, strlen(message)+1) );
// Start by copying the default chip to a structure we can tweak
esp_flash_chip_t chip = *esp_flash_default_chip;
for (chip.read_mode = 0;
chip.read_mode != ESP_FLASH_READ_MODE_MAX;
chip.read_mode++) {
for (chip.speed = 0;
chip.speed != ESP_FLASH_SPEED_MAX;
chip.speed++) {
printf("mode %d speed %d\n", chip.read_mode, chip.speed);
TEST_ASSERT_EQUAL(FLASH_OK, esp_flash_init(&chip) );
char *buf[strlen(message)+1];
memset(buf, 0xFF, sizeof(buf));
TEST_ASSERT_EQUAL(FLASH_OK, esp_flash_read(&chip, buf, offs, sizeof(buf)) );
TEST_ASSERT_EQUAL_STRING_LEN(message, buf, strlen(message));
}
}
}

View file

@ -200,7 +200,7 @@ TEST_CASE("Can mmap unordered pages into contiguous memory", "[spi_flash]")
pages[i]=startpage+(nopages-1)-i;
printf("Offset %x page %d\n", i*0x10000, pages[i]);
}
printf("Attempting mapping of unordered pages to contiguous memory area\n");
spi_flash_mmap_handle_t handle1;
@ -277,7 +277,7 @@ TEST_CASE("flash_mmap invalidates just-written data", "[spi_flash]")
TEST_CASE("flash_mmap can mmap after get enough free MMU pages", "[spi_flash]")
{
//this test case should make flash size >= 4MB, because max size of Dcache can mapped is 4MB
//this test case should make flash size >= 4MB, because max size of Dcache can mapped is 4MB
setup_mmap_tests();
printf("Mapping %x (+%x)\n", start, end - start);

View file

@ -9,9 +9,9 @@
static const char *data = "blah blah blah";
#if CONFIG_SPI_FLASH_DANGEROUS_WRITE_FAILS
#define TEST_TAGS "[spi_flash]"
#define TEST_TAGS "[spi_flash][esp_flash]"
#else // ABORTS
#define TEST_TAGS "[spi_flash][ignore]"
#define TEST_TAGS "[spi_flash][esp_flash][ignore]"
#endif
TEST_CASE("can't overwrite bootloader", TEST_TAGS)

View file

@ -25,7 +25,7 @@
#include <esp_partition.h>
#include <esp_attr.h>
TEST_CASE("Test erase partition", "[spi_flash]")
TEST_CASE("Test erase partition", "[spi_flash][esp_flash]")
{
const esp_partition_t *part = get_test_data_partition();

View file

@ -79,7 +79,7 @@ static void IRAM_ATTR test_read(int src_off, int dst_off, int len)
spi_flash_disable_interrupts_caches_and_other_cpu();
esp_rom_spiflash_result_t rc = esp_rom_spiflash_write(start, src_buf, sizeof(src_buf));
spi_flash_enable_interrupts_caches_and_other_cpu();
TEST_ASSERT_EQUAL_INT(rc, ESP_ROM_SPIFLASH_RESULT_OK);
TEST_ASSERT_EQUAL_HEX(rc, ESP_ROM_SPIFLASH_RESULT_OK);
memset(dst_buf, 0x55, sizeof(dst_buf));
memset(dst_gold, 0x55, sizeof(dst_gold));
fill(dst_gold + dst_off, src_off, len);
@ -87,7 +87,7 @@ static void IRAM_ATTR test_read(int src_off, int dst_off, int len)
TEST_ASSERT_EQUAL_INT(cmp_or_dump(dst_buf, dst_gold, sizeof(dst_buf)), 0);
}
TEST_CASE("Test spi_flash_read", "[spi_flash]")
TEST_CASE("Test spi_flash_read", "[spi_flash][esp_flash]")
{
setup_tests();
#if CONFIG_SPI_FLASH_MINIMAL_TEST
@ -158,14 +158,16 @@ static void IRAM_ATTR test_write(int dst_off, int src_off, int len)
fill(dst_gold + dst_off, src_off, len);
}
ESP_ERROR_CHECK(spi_flash_write(start + dst_off, src_buf + src_off, len));
spi_flash_disable_interrupts_caches_and_other_cpu();
esp_rom_spiflash_result_t rc = esp_rom_spiflash_read(start, dst_buf, sizeof(dst_buf));
spi_flash_enable_interrupts_caches_and_other_cpu();
TEST_ASSERT_EQUAL_INT(rc, ESP_ROM_SPIFLASH_RESULT_OK);
TEST_ASSERT_EQUAL_HEX(rc, ESP_ROM_SPIFLASH_RESULT_OK);
TEST_ASSERT_EQUAL_INT(cmp_or_dump(dst_buf, dst_gold, sizeof(dst_buf)), 0);
}
TEST_CASE("Test spi_flash_write", "[spi_flash]")
TEST_CASE("Test spi_flash_write", "[spi_flash][esp_flash]")
{
setup_tests();
#if CONFIG_SPI_FLASH_MINIMAL_TEST

View file

@ -124,7 +124,7 @@ static void read_task(void* varg) {
vTaskDelete(NULL);
}
TEST_CASE("spi flash functions can run along with IRAM interrupts", "[spi_flash]")
TEST_CASE("spi flash functions can run along with IRAM interrupts", "[spi_flash][esp_flash]")
{
const size_t size = 128;
read_task_arg_t read_arg = {
@ -170,7 +170,7 @@ TEST_CASE("spi flash functions can run along with IRAM interrupts", "[spi_flash]
#if portNUM_PROCESSORS > 1
TEST_CASE("spi_flash deadlock with high priority busy-waiting task", "[spi_flash]")
TEST_CASE("spi_flash deadlock with high priority busy-waiting task", "[spi_flash][esp_flash]")
{
typedef struct {
QueueHandle_t queue;

View file

@ -17,3 +17,5 @@
#define CONFIG_PARTITION_TABLE_OFFSET 0x8000
#define CONFIG_ESPTOOLPY_FLASHSIZE "8MB"
//currently use the legacy implementation, since the stubs for new HAL are not done yet
#define CONFIG_SPI_FLASH_USE_LEGACY_IMPL

View file

@ -4,3 +4,6 @@
#define CONFIG_LOG_DEFAULT_LEVEL 3
#define CONFIG_PARTITION_TABLE_OFFSET 0x8000
#define CONFIG_ESPTOOLPY_FLASHSIZE "8MB"
//currently use the legacy implementation, since the stubs for new HAL are not done yet
#define CONFIG_SPI_FLASH_USE_LEGACY_IMPL