mirror of
https://github.com/espressif/esp-idf.git
synced 2025-08-02 12:14:32 +02:00
esp_flash: refactor to be compatible with the latest ROM
Including: 1. Change the write bytes/read bytes parameter in the host driver into slicers to meet the requirements of complicated cases. 2. Refactor the esp_flash_api code a bit so that we can use the code in the ROM laster 3. Provide get_temp_buffer and release_temp_buffer in the os_functions when the buffer passed by application cannot be used directly. 4. Make timeout of operations configurable in the chip_driver. 5. Make dummy number configurable.
This commit is contained in:
@@ -115,18 +115,36 @@ struct spi_flash_host_driver_t {
|
|||||||
* Program a page of the flash. Check ``max_write_bytes`` for the maximum allowed writing length.
|
* 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);
|
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 */
|
/** Check whether given buffer can be directly used to write */
|
||||||
bool (*supports_direct_write)(spi_flash_host_driver_t *driver, const void *p);
|
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);
|
* Slicer for write data. The `program_page` should be called iteratively with the return value
|
||||||
/** maximum length of program_page */
|
* of this function.
|
||||||
int max_write_bytes;
|
*
|
||||||
|
* @param address Beginning flash address to write
|
||||||
|
* @param len Length request to write
|
||||||
|
* @param align_addr Output of the aligned address to write to
|
||||||
|
* @param page_size Physical page size of the flash chip
|
||||||
|
* @return Length that can be actually written in one `program_page` call
|
||||||
|
*/
|
||||||
|
int (*write_data_slicer)(uint32_t address, uint32_t len, uint32_t *align_addr, uint32_t page_size);
|
||||||
/**
|
/**
|
||||||
* Read data from the flash. Check ``max_read_bytes`` for the maximum allowed reading length.
|
* 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);
|
esp_err_t (*read)(spi_flash_host_driver_t *driver, void *buffer, uint32_t address, uint32_t read_len);
|
||||||
/** maximum length of read */
|
/** Check whether given buffer can be directly used to read */
|
||||||
int max_read_bytes;
|
bool (*supports_direct_read)(spi_flash_host_driver_t *driver, const void *p);
|
||||||
|
/**
|
||||||
|
* Slicer for read data. The `read` should be called iteratively with the return value
|
||||||
|
* of this function.
|
||||||
|
*
|
||||||
|
* @param address Beginning flash address to read
|
||||||
|
* @param len Length request to read
|
||||||
|
* @param align_addr Output of the aligned address to read
|
||||||
|
* @param page_size Physical page size of the flash chip
|
||||||
|
* @return Length that can be actually read in one `read` call
|
||||||
|
*/
|
||||||
|
int (*read_data_slicer)(uint32_t address, uint32_t len, uint32_t *align_addr, uint32_t page_size);
|
||||||
/**
|
/**
|
||||||
* Check whether the host is idle to perform new operations.
|
* Check whether the host is idle to perform new operations.
|
||||||
*/
|
*/
|
||||||
|
@@ -71,12 +71,31 @@ _Static_assert(sizeof(io_mode_str)/IO_STR_LEN == SPI_FLASH_READ_MODE_MAX, "the i
|
|||||||
|
|
||||||
esp_err_t esp_flash_read_chip_id(esp_flash_t* chip, uint32_t* flash_id);
|
esp_err_t esp_flash_read_chip_id(esp_flash_t* chip, uint32_t* flash_id);
|
||||||
|
|
||||||
|
static esp_err_t spiflash_start_default(esp_flash_t *chip);
|
||||||
|
static esp_err_t spiflash_end_default(esp_flash_t *chip, esp_err_t err);
|
||||||
|
static esp_err_t check_chip_pointer_default(esp_flash_t **inout_chip);
|
||||||
|
|
||||||
|
typedef struct {
|
||||||
|
esp_err_t (*start)(esp_flash_t *chip);
|
||||||
|
esp_err_t (*end)(esp_flash_t *chip, esp_err_t err);
|
||||||
|
esp_err_t (*chip_check)(esp_flash_t **inout_chip);
|
||||||
|
} rom_spiflash_api_func_t;
|
||||||
|
|
||||||
|
// These functions can be placed in the ROM. For now we use the code in IDF.
|
||||||
|
DRAM_ATTR static rom_spiflash_api_func_t default_spiflash_rom_api = {
|
||||||
|
.start = spiflash_start_default,
|
||||||
|
.end = spiflash_end_default,
|
||||||
|
.chip_check = check_chip_pointer_default,
|
||||||
|
};
|
||||||
|
|
||||||
|
DRAM_ATTR rom_spiflash_api_func_t *rom_spiflash_api_funcs = &default_spiflash_rom_api;
|
||||||
|
|
||||||
/* Static function to notify OS of a new SPI flash operation.
|
/* 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
|
If returns an error result, caller must abort. If returns ESP_OK, caller must
|
||||||
call spiflash_end() before returning.
|
call rom_spiflash_api_funcs->end() before returning.
|
||||||
*/
|
*/
|
||||||
static esp_err_t IRAM_ATTR spiflash_start(esp_flash_t *chip)
|
static esp_err_t IRAM_ATTR spiflash_start_default(esp_flash_t *chip)
|
||||||
{
|
{
|
||||||
if (chip->os_func != NULL && chip->os_func->start != NULL) {
|
if (chip->os_func != NULL && chip->os_func->start != NULL) {
|
||||||
esp_err_t err = chip->os_func->start(chip->os_func_data);
|
esp_err_t err = chip->os_func->start(chip->os_func_data);
|
||||||
@@ -90,7 +109,7 @@ static esp_err_t IRAM_ATTR spiflash_start(esp_flash_t *chip)
|
|||||||
|
|
||||||
/* Static function to notify OS that SPI flash operation is complete.
|
/* 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)
|
static esp_err_t IRAM_ATTR spiflash_end_default(esp_flash_t *chip, esp_err_t err)
|
||||||
{
|
{
|
||||||
if (chip->os_func != NULL
|
if (chip->os_func != NULL
|
||||||
&& chip->os_func->end != NULL) {
|
&& chip->os_func->end != NULL) {
|
||||||
@@ -102,6 +121,20 @@ static esp_err_t IRAM_ATTR spiflash_end(const esp_flash_t *chip, esp_err_t err)
|
|||||||
return err;
|
return err;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// check that the 'chip' parameter is properly initialised
|
||||||
|
static esp_err_t check_chip_pointer_default(esp_flash_t **inout_chip)
|
||||||
|
{
|
||||||
|
esp_flash_t *chip = *inout_chip;
|
||||||
|
if (chip == NULL) {
|
||||||
|
chip = esp_flash_default_chip;
|
||||||
|
}
|
||||||
|
*inout_chip = chip;
|
||||||
|
if (chip == NULL || !esp_flash_chip_driver_initialized(chip)) {
|
||||||
|
return ESP_ERR_FLASH_NOT_INITIALISED;
|
||||||
|
}
|
||||||
|
return ESP_OK;
|
||||||
|
}
|
||||||
|
|
||||||
/* Return true if regions 'a' and 'b' overlap at all, based on their start offsets and lengths. */
|
/* 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);
|
inline static bool regions_overlap(uint32_t a_start, uint32_t a_len,uint32_t b_start, uint32_t b_len);
|
||||||
|
|
||||||
@@ -152,7 +185,7 @@ esp_err_t IRAM_ATTR esp_flash_init(esp_flash_t *chip)
|
|||||||
}
|
}
|
||||||
|
|
||||||
ESP_LOGI(TAG, "flash io: %s", io_mode_str[chip->read_mode]);
|
ESP_LOGI(TAG, "flash io: %s", io_mode_str[chip->read_mode]);
|
||||||
err = spiflash_start(chip);
|
err = rom_spiflash_api_funcs->start(chip);
|
||||||
if (err != ESP_OK) {
|
if (err != ESP_OK) {
|
||||||
return err;
|
return err;
|
||||||
}
|
}
|
||||||
@@ -166,13 +199,13 @@ esp_err_t IRAM_ATTR esp_flash_init(esp_flash_t *chip)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
// Done: all fields on 'chip' are initialised
|
// Done: all fields on 'chip' are initialised
|
||||||
return spiflash_end(chip, err);
|
return rom_spiflash_api_funcs->end(chip, err);
|
||||||
}
|
}
|
||||||
|
|
||||||
//this is not public, but useful in unit tests
|
//this is not public, but useful in unit tests
|
||||||
esp_err_t IRAM_ATTR esp_flash_read_chip_id(esp_flash_t* chip, uint32_t* flash_id)
|
esp_err_t IRAM_ATTR esp_flash_read_chip_id(esp_flash_t* chip, uint32_t* flash_id)
|
||||||
{
|
{
|
||||||
esp_err_t err = spiflash_start(chip);
|
esp_err_t err = rom_spiflash_api_funcs->start(chip);
|
||||||
if (err != ESP_OK) {
|
if (err != ESP_OK) {
|
||||||
return err;
|
return err;
|
||||||
}
|
}
|
||||||
@@ -189,7 +222,7 @@ esp_err_t IRAM_ATTR esp_flash_read_chip_id(esp_flash_t* chip, uint32_t* flash_id
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return spiflash_end(chip, err);
|
return rom_spiflash_api_funcs->end(chip, err);
|
||||||
}
|
}
|
||||||
|
|
||||||
static esp_err_t IRAM_ATTR detect_spi_flash_chip(esp_flash_t *chip)
|
static esp_err_t IRAM_ATTR detect_spi_flash_chip(esp_flash_t *chip)
|
||||||
@@ -205,7 +238,7 @@ static esp_err_t IRAM_ATTR detect_spi_flash_chip(esp_flash_t *chip)
|
|||||||
// and also so esp_flash_registered_flash_drivers can live in flash
|
// and also so esp_flash_registered_flash_drivers can live in flash
|
||||||
ESP_LOGD(TAG, "trying chip: %s", chip->chip_drv->name);
|
ESP_LOGD(TAG, "trying chip: %s", chip->chip_drv->name);
|
||||||
|
|
||||||
err = spiflash_start(chip);
|
err = rom_spiflash_api_funcs->start(chip);
|
||||||
if (err != ESP_OK) {
|
if (err != ESP_OK) {
|
||||||
return err;
|
return err;
|
||||||
}
|
}
|
||||||
@@ -216,7 +249,7 @@ static esp_err_t IRAM_ATTR detect_spi_flash_chip(esp_flash_t *chip)
|
|||||||
// if probe succeeded, chip->drv stays set
|
// if probe succeeded, chip->drv stays set
|
||||||
drivers++;
|
drivers++;
|
||||||
|
|
||||||
err = spiflash_end(chip, err);
|
err = rom_spiflash_api_funcs->end(chip, err);
|
||||||
if (err != ESP_OK) {
|
if (err != ESP_OK) {
|
||||||
return err;
|
return err;
|
||||||
}
|
}
|
||||||
@@ -228,16 +261,12 @@ static esp_err_t IRAM_ATTR detect_spi_flash_chip(esp_flash_t *chip)
|
|||||||
return ESP_OK;
|
return ESP_OK;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Convenience macro for beginning of all API functions,
|
/* Convenience macro for beginning of all API functions.
|
||||||
// check that the 'chip' parameter is properly initialised
|
* Check the return value of `rom_spiflash_api_funcs->chip_check` is correct,
|
||||||
// and supports the operation in question
|
* and the chip supports the operation in question.
|
||||||
#define VERIFY_OP(OP) do { \
|
*/
|
||||||
if (chip == NULL) { \
|
#define VERIFY_CHIP_OP(OP) do { \
|
||||||
chip = esp_flash_default_chip; \
|
if (err != ESP_OK) return err; \
|
||||||
} \
|
|
||||||
if (chip == NULL || !esp_flash_chip_driver_initialized(chip)) { \
|
|
||||||
return ESP_ERR_FLASH_NOT_INITIALISED; \
|
|
||||||
} \
|
|
||||||
if (chip->chip_drv->OP == NULL) { \
|
if (chip->chip_drv->OP == NULL) { \
|
||||||
return ESP_ERR_FLASH_UNSUPPORTED_CHIP; \
|
return ESP_ERR_FLASH_UNSUPPORTED_CHIP; \
|
||||||
} \
|
} \
|
||||||
@@ -245,28 +274,25 @@ static esp_err_t IRAM_ATTR detect_spi_flash_chip(esp_flash_t *chip)
|
|||||||
|
|
||||||
esp_err_t IRAM_ATTR esp_flash_read_id(esp_flash_t *chip, uint32_t *out_id)
|
esp_err_t IRAM_ATTR esp_flash_read_id(esp_flash_t *chip, uint32_t *out_id)
|
||||||
{
|
{
|
||||||
if (chip == NULL) {
|
esp_err_t err = rom_spiflash_api_funcs->chip_check(&chip);
|
||||||
chip = esp_flash_default_chip;
|
//Accept uninitialized chip when reading chip id
|
||||||
}
|
if (err != ESP_OK && !(err == ESP_ERR_FLASH_NOT_INITIALISED && chip != NULL)) return err;
|
||||||
if (chip == NULL || !esp_flash_chip_driver_initialized(chip)) {
|
if (out_id == NULL) return ESP_ERR_INVALID_ARG;
|
||||||
return ESP_ERR_FLASH_NOT_INITIALISED;
|
|
||||||
}
|
err = rom_spiflash_api_funcs->start(chip);
|
||||||
if (out_id == NULL) {
|
|
||||||
return ESP_ERR_INVALID_ARG;
|
|
||||||
}
|
|
||||||
esp_err_t err = spiflash_start(chip);
|
|
||||||
if (err != ESP_OK) {
|
if (err != ESP_OK) {
|
||||||
return err;
|
return err;
|
||||||
}
|
}
|
||||||
|
|
||||||
err = chip->host->read_id(chip->host, out_id);
|
err = chip->host->read_id(chip->host, out_id);
|
||||||
|
|
||||||
return spiflash_end(chip, err);
|
return rom_spiflash_api_funcs->end(chip, err);
|
||||||
}
|
}
|
||||||
|
|
||||||
esp_err_t IRAM_ATTR esp_flash_get_size(esp_flash_t *chip, uint32_t *out_size)
|
esp_err_t IRAM_ATTR esp_flash_get_size(esp_flash_t *chip, uint32_t *out_size)
|
||||||
{
|
{
|
||||||
VERIFY_OP(detect_size);
|
esp_err_t err = rom_spiflash_api_funcs->chip_check(&chip);
|
||||||
|
VERIFY_CHIP_OP(detect_size);
|
||||||
if (out_size == NULL) {
|
if (out_size == NULL) {
|
||||||
return ESP_ERR_INVALID_ARG;
|
return ESP_ERR_INVALID_ARG;
|
||||||
}
|
}
|
||||||
@@ -275,7 +301,7 @@ esp_err_t IRAM_ATTR esp_flash_get_size(esp_flash_t *chip, uint32_t *out_size)
|
|||||||
return ESP_OK;
|
return ESP_OK;
|
||||||
}
|
}
|
||||||
|
|
||||||
esp_err_t err = spiflash_start(chip);
|
err = rom_spiflash_api_funcs->start(chip);
|
||||||
if (err != ESP_OK) {
|
if (err != ESP_OK) {
|
||||||
return err;
|
return err;
|
||||||
}
|
}
|
||||||
@@ -284,28 +310,31 @@ esp_err_t IRAM_ATTR esp_flash_get_size(esp_flash_t *chip, uint32_t *out_size)
|
|||||||
if (err == ESP_OK) {
|
if (err == ESP_OK) {
|
||||||
chip->size = detect_size;
|
chip->size = detect_size;
|
||||||
}
|
}
|
||||||
return spiflash_end(chip, err);
|
return rom_spiflash_api_funcs->end(chip, err);
|
||||||
}
|
}
|
||||||
|
|
||||||
esp_err_t IRAM_ATTR esp_flash_erase_chip(esp_flash_t *chip)
|
esp_err_t IRAM_ATTR esp_flash_erase_chip(esp_flash_t *chip)
|
||||||
{
|
{
|
||||||
VERIFY_OP(erase_chip);
|
esp_err_t err = rom_spiflash_api_funcs->chip_check(&chip);
|
||||||
|
VERIFY_CHIP_OP(erase_chip);
|
||||||
CHECK_WRITE_ADDRESS(chip, 0, chip->size);
|
CHECK_WRITE_ADDRESS(chip, 0, chip->size);
|
||||||
|
|
||||||
esp_err_t err = spiflash_start(chip);
|
err = rom_spiflash_api_funcs->start(chip);
|
||||||
if (err != ESP_OK) {
|
if (err != ESP_OK) {
|
||||||
return err;
|
return err;
|
||||||
}
|
}
|
||||||
|
|
||||||
err = chip->chip_drv->erase_chip(chip);
|
err = chip->chip_drv->erase_chip(chip);
|
||||||
return spiflash_end(chip, err);
|
return rom_spiflash_api_funcs->end(chip, err);
|
||||||
}
|
}
|
||||||
|
|
||||||
esp_err_t IRAM_ATTR esp_flash_erase_region(esp_flash_t *chip, uint32_t start, uint32_t len)
|
esp_err_t IRAM_ATTR esp_flash_erase_region(esp_flash_t *chip, uint32_t start, uint32_t len)
|
||||||
{
|
{
|
||||||
VERIFY_OP(erase_sector);
|
esp_err_t err = rom_spiflash_api_funcs->chip_check(&chip);
|
||||||
VERIFY_OP(erase_block);
|
VERIFY_CHIP_OP(erase_sector);
|
||||||
|
VERIFY_CHIP_OP(erase_block);
|
||||||
CHECK_WRITE_ADDRESS(chip, start, len);
|
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 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;
|
uint32_t sector_size = chip->chip_drv->sector_size;
|
||||||
|
|
||||||
@@ -320,12 +349,12 @@ esp_err_t IRAM_ATTR esp_flash_erase_region(esp_flash_t *chip, uint32_t start, ui
|
|||||||
return ESP_ERR_INVALID_ARG;
|
return ESP_ERR_INVALID_ARG;
|
||||||
}
|
}
|
||||||
|
|
||||||
esp_err_t err = ESP_OK;
|
err = ESP_OK;
|
||||||
// Check for write protected regions overlapping the erase region
|
// Check for write protected regions overlapping the erase region
|
||||||
if (chip->chip_drv->get_protected_regions != NULL &&
|
if (chip->chip_drv->get_protected_regions != NULL &&
|
||||||
chip->chip_drv->num_protectable_regions > 0) {
|
chip->chip_drv->num_protectable_regions > 0) {
|
||||||
|
|
||||||
err = spiflash_start(chip);
|
err = rom_spiflash_api_funcs->start(chip);
|
||||||
if (err != ESP_OK) {
|
if (err != ESP_OK) {
|
||||||
return err;
|
return err;
|
||||||
}
|
}
|
||||||
@@ -341,7 +370,7 @@ esp_err_t IRAM_ATTR esp_flash_erase_region(esp_flash_t *chip, uint32_t start, ui
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
// Don't lock the SPI flash for the entire erase, as this may be very long
|
// Don't lock the SPI flash for the entire erase, as this may be very long
|
||||||
err = spiflash_end(chip, err);
|
err = rom_spiflash_api_funcs->end(chip, err);
|
||||||
}
|
}
|
||||||
|
|
||||||
#ifdef CONFIG_SPI_FLASH_YIELD_DURING_ERASE
|
#ifdef CONFIG_SPI_FLASH_YIELD_DURING_ERASE
|
||||||
@@ -351,7 +380,7 @@ esp_err_t IRAM_ATTR esp_flash_erase_region(esp_flash_t *chip, uint32_t start, ui
|
|||||||
#ifdef CONFIG_SPI_FLASH_YIELD_DURING_ERASE
|
#ifdef CONFIG_SPI_FLASH_YIELD_DURING_ERASE
|
||||||
int64_t start_time_us = esp_timer_get_time();
|
int64_t start_time_us = esp_timer_get_time();
|
||||||
#endif
|
#endif
|
||||||
err = spiflash_start(chip);
|
err = rom_spiflash_api_funcs->start(chip);
|
||||||
if (err != ESP_OK) {
|
if (err != ESP_OK) {
|
||||||
return err;
|
return err;
|
||||||
}
|
}
|
||||||
@@ -371,7 +400,7 @@ esp_err_t IRAM_ATTR esp_flash_erase_region(esp_flash_t *chip, uint32_t start, ui
|
|||||||
len -= sector_size;
|
len -= sector_size;
|
||||||
}
|
}
|
||||||
|
|
||||||
err = spiflash_end(chip, err);
|
err = rom_spiflash_api_funcs->end(chip, err);
|
||||||
|
|
||||||
#ifdef CONFIG_SPI_FLASH_YIELD_DURING_ERASE
|
#ifdef CONFIG_SPI_FLASH_YIELD_DURING_ERASE
|
||||||
no_yield_time_us += (esp_timer_get_time() - start_time_us);
|
no_yield_time_us += (esp_timer_get_time() - start_time_us);
|
||||||
@@ -388,34 +417,36 @@ esp_err_t IRAM_ATTR esp_flash_erase_region(esp_flash_t *chip, uint32_t start, ui
|
|||||||
|
|
||||||
esp_err_t IRAM_ATTR esp_flash_get_chip_write_protect(esp_flash_t *chip, bool *out_write_protected)
|
esp_err_t IRAM_ATTR esp_flash_get_chip_write_protect(esp_flash_t *chip, bool *out_write_protected)
|
||||||
{
|
{
|
||||||
VERIFY_OP(get_chip_write_protect);
|
esp_err_t err = rom_spiflash_api_funcs->chip_check(&chip);
|
||||||
|
VERIFY_CHIP_OP(get_chip_write_protect);
|
||||||
if (out_write_protected == NULL) {
|
if (out_write_protected == NULL) {
|
||||||
return ESP_ERR_INVALID_ARG;
|
return ESP_ERR_INVALID_ARG;
|
||||||
}
|
}
|
||||||
|
|
||||||
esp_err_t err = spiflash_start(chip);
|
err = rom_spiflash_api_funcs->start(chip);
|
||||||
if (err != ESP_OK) {
|
if (err != ESP_OK) {
|
||||||
return err;
|
return err;
|
||||||
}
|
}
|
||||||
|
|
||||||
err = chip->chip_drv->get_chip_write_protect(chip, out_write_protected);
|
err = chip->chip_drv->get_chip_write_protect(chip, out_write_protected);
|
||||||
|
|
||||||
return spiflash_end(chip, err);
|
return rom_spiflash_api_funcs->end(chip, err);
|
||||||
}
|
}
|
||||||
|
|
||||||
esp_err_t IRAM_ATTR esp_flash_set_chip_write_protect(esp_flash_t *chip, bool write_protect)
|
esp_err_t IRAM_ATTR esp_flash_set_chip_write_protect(esp_flash_t *chip, bool write_protect)
|
||||||
{
|
{
|
||||||
VERIFY_OP(set_chip_write_protect);
|
esp_err_t err = rom_spiflash_api_funcs->chip_check(&chip);
|
||||||
|
VERIFY_CHIP_OP(set_chip_write_protect);
|
||||||
//TODO: skip writing if already locked or unlocked
|
//TODO: skip writing if already locked or unlocked
|
||||||
|
|
||||||
esp_err_t err = spiflash_start(chip);
|
err = rom_spiflash_api_funcs->start(chip);
|
||||||
if (err != ESP_OK) {
|
if (err != ESP_OK) {
|
||||||
return err;
|
return err;
|
||||||
}
|
}
|
||||||
|
|
||||||
err = chip->chip_drv->set_chip_write_protect(chip, write_protect);
|
err = chip->chip_drv->set_chip_write_protect(chip, write_protect);
|
||||||
|
|
||||||
return spiflash_end(chip, err);
|
return rom_spiflash_api_funcs->end(chip, err);
|
||||||
}
|
}
|
||||||
|
|
||||||
esp_err_t esp_flash_get_protectable_regions(const esp_flash_t *chip, const esp_flash_region_t **out_regions, uint32_t *out_num_regions)
|
esp_err_t esp_flash_get_protectable_regions(const esp_flash_t *chip, const esp_flash_region_t **out_regions, uint32_t *out_num_regions)
|
||||||
@@ -423,7 +454,8 @@ esp_err_t esp_flash_get_protectable_regions(const esp_flash_t *chip, const esp_f
|
|||||||
if(out_num_regions != NULL) {
|
if(out_num_regions != NULL) {
|
||||||
*out_num_regions = 0; // In case caller doesn't check result
|
*out_num_regions = 0; // In case caller doesn't check result
|
||||||
}
|
}
|
||||||
VERIFY_OP(get_protected_regions);
|
esp_err_t err = rom_spiflash_api_funcs->chip_check((esp_flash_t **)&chip);
|
||||||
|
VERIFY_CHIP_OP(get_protected_regions);
|
||||||
|
|
||||||
if(out_regions == NULL || out_num_regions == NULL) {
|
if(out_regions == NULL || out_num_regions == NULL) {
|
||||||
return ESP_ERR_INVALID_ARG;
|
return ESP_ERR_INVALID_ARG;
|
||||||
@@ -452,20 +484,21 @@ static esp_err_t find_region(const esp_flash_t *chip, const esp_flash_region_t *
|
|||||||
|
|
||||||
esp_err_t IRAM_ATTR esp_flash_get_protected_region(esp_flash_t *chip, const esp_flash_region_t *region, bool *out_protected)
|
esp_err_t IRAM_ATTR esp_flash_get_protected_region(esp_flash_t *chip, const esp_flash_region_t *region, bool *out_protected)
|
||||||
{
|
{
|
||||||
VERIFY_OP(get_protected_regions);
|
esp_err_t err = rom_spiflash_api_funcs->chip_check(&chip);
|
||||||
|
VERIFY_CHIP_OP(get_protected_regions);
|
||||||
|
|
||||||
if (out_protected == NULL) {
|
if (out_protected == NULL) {
|
||||||
return ESP_ERR_INVALID_ARG;
|
return ESP_ERR_INVALID_ARG;
|
||||||
}
|
}
|
||||||
|
|
||||||
uint8_t index;
|
uint8_t index;
|
||||||
esp_err_t err = find_region(chip, region, &index);
|
err = find_region(chip, region, &index);
|
||||||
if (err != ESP_OK) {
|
if (err != ESP_OK) {
|
||||||
return err;
|
return err;
|
||||||
}
|
}
|
||||||
|
|
||||||
uint64_t protection_mask = 0;
|
uint64_t protection_mask = 0;
|
||||||
err = spiflash_start(chip);
|
err = rom_spiflash_api_funcs->start(chip);
|
||||||
if (err != ESP_OK) {
|
if (err != ESP_OK) {
|
||||||
return err;
|
return err;
|
||||||
}
|
}
|
||||||
@@ -475,21 +508,22 @@ esp_err_t IRAM_ATTR esp_flash_get_protected_region(esp_flash_t *chip, const esp_
|
|||||||
*out_protected = protection_mask & (1LL << index);
|
*out_protected = protection_mask & (1LL << index);
|
||||||
}
|
}
|
||||||
|
|
||||||
return spiflash_end(chip, err);
|
return rom_spiflash_api_funcs->end(chip, err);
|
||||||
}
|
}
|
||||||
|
|
||||||
esp_err_t IRAM_ATTR esp_flash_set_protected_region(esp_flash_t *chip, const esp_flash_region_t *region, bool protect)
|
esp_err_t IRAM_ATTR esp_flash_set_protected_region(esp_flash_t *chip, const esp_flash_region_t *region, bool protect)
|
||||||
{
|
{
|
||||||
VERIFY_OP(set_protected_regions);
|
esp_err_t err = rom_spiflash_api_funcs->chip_check(&chip);
|
||||||
|
VERIFY_CHIP_OP(set_protected_regions);
|
||||||
|
|
||||||
uint8_t index;
|
uint8_t index;
|
||||||
esp_err_t err = find_region(chip, region, &index);
|
err = find_region(chip, region, &index);
|
||||||
if (err != ESP_OK) {
|
if (err != ESP_OK) {
|
||||||
return err;
|
return err;
|
||||||
}
|
}
|
||||||
|
|
||||||
uint64_t protection_mask = 0;
|
uint64_t protection_mask = 0;
|
||||||
err = spiflash_start(chip);
|
err = rom_spiflash_api_funcs->start(chip);
|
||||||
if (err != ESP_OK) {
|
if (err != ESP_OK) {
|
||||||
return err;
|
return err;
|
||||||
}
|
}
|
||||||
@@ -504,7 +538,7 @@ esp_err_t IRAM_ATTR esp_flash_set_protected_region(esp_flash_t *chip, const esp_
|
|||||||
err = chip->chip_drv->set_protected_regions(chip, protection_mask);
|
err = chip->chip_drv->set_protected_regions(chip, protection_mask);
|
||||||
}
|
}
|
||||||
|
|
||||||
return spiflash_end(chip, err);
|
return rom_spiflash_api_funcs->end(chip, err);
|
||||||
}
|
}
|
||||||
|
|
||||||
esp_err_t IRAM_ATTR esp_flash_read(esp_flash_t *chip, void *buffer, uint32_t address, uint32_t length)
|
esp_err_t IRAM_ATTR esp_flash_read(esp_flash_t *chip, void *buffer, uint32_t address, uint32_t length)
|
||||||
@@ -512,7 +546,8 @@ esp_err_t IRAM_ATTR esp_flash_read(esp_flash_t *chip, void *buffer, uint32_t add
|
|||||||
if (length == 0) {
|
if (length == 0) {
|
||||||
return ESP_OK;
|
return ESP_OK;
|
||||||
}
|
}
|
||||||
VERIFY_OP(read);
|
esp_err_t err = rom_spiflash_api_funcs->chip_check(&chip);
|
||||||
|
VERIFY_CHIP_OP(read);
|
||||||
if (buffer == NULL || address > chip->size || address+length > chip->size) {
|
if (buffer == NULL || address > chip->size || address+length > chip->size) {
|
||||||
return ESP_ERR_INVALID_ARG;
|
return ESP_ERR_INVALID_ARG;
|
||||||
}
|
}
|
||||||
@@ -526,29 +561,17 @@ esp_err_t IRAM_ATTR esp_flash_read(esp_flash_t *chip, void *buffer, uint32_t add
|
|||||||
size_t read_chunk_size = MIN(MAX_READ_CHUNK, length);
|
size_t read_chunk_size = MIN(MAX_READ_CHUNK, length);
|
||||||
|
|
||||||
if (!direct_read) {
|
if (!direct_read) {
|
||||||
/* Allocate temporary internal buffer to use for the actual read. If the preferred size
|
size_t actual_len = 0;
|
||||||
doesn't fit in free internal memory, allocate the largest available free block.
|
temp_buffer = chip->os_func->get_temp_buffer(chip->os_func_data, read_chunk_size, &actual_len);
|
||||||
|
read_chunk_size = actual_len;
|
||||||
(May need to shrink read_chunk_size and retry due to race conditions with other tasks
|
|
||||||
also allocating from the heap.)
|
|
||||||
*/
|
|
||||||
unsigned retries = 5;
|
|
||||||
while(temp_buffer == NULL && retries--) {
|
|
||||||
read_chunk_size = MIN(read_chunk_size, heap_caps_get_largest_free_block(MALLOC_CAP_INTERNAL | MALLOC_CAP_8BIT));
|
|
||||||
read_chunk_size = (read_chunk_size + 3) & ~3;
|
|
||||||
temp_buffer = heap_caps_malloc(read_chunk_size, MALLOC_CAP_INTERNAL | MALLOC_CAP_8BIT);
|
|
||||||
}
|
|
||||||
ESP_LOGV(TAG, "allocate temp buffer: %p (%d)", temp_buffer, read_chunk_size);
|
|
||||||
|
|
||||||
if (temp_buffer == NULL) {
|
if (temp_buffer == NULL) {
|
||||||
return ESP_ERR_NO_MEM;
|
return ESP_ERR_NO_MEM;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
esp_err_t err = ESP_OK;
|
err = ESP_OK;
|
||||||
|
|
||||||
do {
|
do {
|
||||||
err = spiflash_start(chip);
|
err = rom_spiflash_api_funcs->start(chip);
|
||||||
if (err != ESP_OK) {
|
if (err != ESP_OK) {
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
@@ -562,11 +585,11 @@ esp_err_t IRAM_ATTR esp_flash_read(esp_flash_t *chip, void *buffer, uint32_t add
|
|||||||
err = chip->chip_drv->read(chip, buffer_to_read, address, length_to_read);
|
err = chip->chip_drv->read(chip, buffer_to_read, address, length_to_read);
|
||||||
}
|
}
|
||||||
if (err != ESP_OK) {
|
if (err != ESP_OK) {
|
||||||
spiflash_end(chip, err);
|
rom_spiflash_api_funcs->end(chip, err);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
//even if this is failed, the data is still valid, copy before quit
|
//even if this is failed, the data is still valid, copy before quit
|
||||||
err = spiflash_end(chip, err);
|
err = rom_spiflash_api_funcs->end(chip, err);
|
||||||
|
|
||||||
//copy back to the original buffer
|
//copy back to the original buffer
|
||||||
if (temp_buffer) {
|
if (temp_buffer) {
|
||||||
@@ -574,10 +597,10 @@ esp_err_t IRAM_ATTR esp_flash_read(esp_flash_t *chip, void *buffer, uint32_t add
|
|||||||
}
|
}
|
||||||
address += length_to_read;
|
address += length_to_read;
|
||||||
length -= length_to_read;
|
length -= length_to_read;
|
||||||
buffer += length_to_read;
|
buffer = (void*)((intptr_t)buffer + length_to_read);
|
||||||
} while (err == ESP_OK && length > 0);
|
} while (err == ESP_OK && length > 0);
|
||||||
|
|
||||||
free(temp_buffer);
|
chip->os_func->release_temp_buffer(chip->os_func_data, temp_buffer);
|
||||||
return err;
|
return err;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -586,7 +609,8 @@ esp_err_t IRAM_ATTR esp_flash_write(esp_flash_t *chip, const void *buffer, uint3
|
|||||||
if (length == 0) {
|
if (length == 0) {
|
||||||
return ESP_OK;
|
return ESP_OK;
|
||||||
}
|
}
|
||||||
VERIFY_OP(write);
|
esp_err_t err = rom_spiflash_api_funcs->chip_check(&chip);
|
||||||
|
VERIFY_CHIP_OP(write);
|
||||||
CHECK_WRITE_ADDRESS(chip, address, length);
|
CHECK_WRITE_ADDRESS(chip, address, length);
|
||||||
if (buffer == NULL || address > chip->size || address+length > chip->size) {
|
if (buffer == NULL || address > chip->size || address+length > chip->size) {
|
||||||
return ESP_ERR_INVALID_ARG;
|
return ESP_ERR_INVALID_ARG;
|
||||||
@@ -595,7 +619,7 @@ esp_err_t IRAM_ATTR esp_flash_write(esp_flash_t *chip, const void *buffer, uint3
|
|||||||
//when the cache is disabled, only the DRAM can be read, check whether we need to copy the data first
|
//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);
|
bool direct_write = chip->host->supports_direct_write(chip->host, buffer);
|
||||||
|
|
||||||
esp_err_t err = ESP_OK;
|
err = ESP_OK;
|
||||||
/* Write output in chunks, either by buffering on stack or
|
/* Write output in chunks, either by buffering on stack or
|
||||||
by artificially cutting into MAX_WRITE_CHUNK parts (in an OS
|
by artificially cutting into MAX_WRITE_CHUNK parts (in an OS
|
||||||
environment, this prevents writing from causing interrupt or higher priority task
|
environment, this prevents writing from causing interrupt or higher priority task
|
||||||
@@ -613,7 +637,7 @@ esp_err_t IRAM_ATTR esp_flash_write(esp_flash_t *chip, const void *buffer, uint3
|
|||||||
write_buf = buf;
|
write_buf = buf;
|
||||||
}
|
}
|
||||||
|
|
||||||
err = spiflash_start(chip);
|
err = rom_spiflash_api_funcs->start(chip);
|
||||||
if (err != ESP_OK) {
|
if (err != ESP_OK) {
|
||||||
return err;
|
return err;
|
||||||
}
|
}
|
||||||
@@ -624,7 +648,7 @@ esp_err_t IRAM_ATTR esp_flash_write(esp_flash_t *chip, const void *buffer, uint3
|
|||||||
buffer = (void *)((intptr_t)buffer + write_len);
|
buffer = (void *)((intptr_t)buffer + write_len);
|
||||||
length -= write_len;
|
length -= write_len;
|
||||||
|
|
||||||
err = spiflash_end(chip, err);
|
err = rom_spiflash_api_funcs->end(chip, err);
|
||||||
} while (err == ESP_OK && length > 0);
|
} while (err == ESP_OK && length > 0);
|
||||||
return err;
|
return err;
|
||||||
}
|
}
|
||||||
@@ -639,11 +663,8 @@ esp_err_t IRAM_ATTR esp_flash_write_encrypted(esp_flash_t *chip, uint32_t addres
|
|||||||
* is no way to support non-standard chips. We use the legacy
|
* is no way to support non-standard chips. We use the legacy
|
||||||
* implementation and skip the chip and driver layers.
|
* implementation and skip the chip and driver layers.
|
||||||
*/
|
*/
|
||||||
if (chip == NULL) {
|
esp_err_t err = rom_spiflash_api_funcs->chip_check(&chip);
|
||||||
chip = esp_flash_default_chip;
|
if (err != ESP_OK) return err;
|
||||||
} else if (chip != esp_flash_default_chip) {
|
|
||||||
return ESP_ERR_NOT_SUPPORTED;
|
|
||||||
}
|
|
||||||
if (buffer == NULL || address > chip->size || address+length > chip->size) {
|
if (buffer == NULL || address > chip->size || address+length > chip->size) {
|
||||||
return ESP_ERR_INVALID_ARG;
|
return ESP_ERR_INVALID_ARG;
|
||||||
}
|
}
|
||||||
@@ -667,26 +688,24 @@ esp_err_t IRAM_ATTR esp_flash_read_encrypted(esp_flash_t *chip, uint32_t address
|
|||||||
* is no way to support non-standard chips. We use the legacy
|
* is no way to support non-standard chips. We use the legacy
|
||||||
* implementation and skip the chip and driver layers.
|
* implementation and skip the chip and driver layers.
|
||||||
*/
|
*/
|
||||||
if (chip == NULL) {
|
esp_err_t err = rom_spiflash_api_funcs->chip_check(&chip);
|
||||||
chip = esp_flash_default_chip;
|
if (err != ESP_OK) return err;
|
||||||
} else if (chip != esp_flash_default_chip) {
|
|
||||||
return ESP_ERR_NOT_SUPPORTED;
|
|
||||||
}
|
|
||||||
return spi_flash_read_encrypted(address, out_buffer, length);
|
return spi_flash_read_encrypted(address, out_buffer, length);
|
||||||
}
|
}
|
||||||
|
|
||||||
// test only, non-public
|
// test only, non-public
|
||||||
IRAM_ATTR esp_err_t esp_flash_get_io_mode(esp_flash_t* chip, bool* qe)
|
IRAM_ATTR esp_err_t esp_flash_get_io_mode(esp_flash_t* chip, bool* qe)
|
||||||
{
|
{
|
||||||
VERIFY_OP(get_io_mode);
|
esp_err_t err = rom_spiflash_api_funcs->chip_check(&chip);
|
||||||
|
VERIFY_CHIP_OP(get_io_mode);
|
||||||
esp_flash_io_mode_t io_mode;
|
esp_flash_io_mode_t io_mode;
|
||||||
|
|
||||||
esp_err_t err = spiflash_start(chip);
|
err = rom_spiflash_api_funcs->start(chip);
|
||||||
if (err != ESP_OK) {
|
if (err != ESP_OK) {
|
||||||
return err;
|
return err;
|
||||||
}
|
}
|
||||||
err = chip->chip_drv->get_io_mode(chip, &io_mode);
|
err = chip->chip_drv->get_io_mode(chip, &io_mode);
|
||||||
err = spiflash_end(chip, err);
|
err = rom_spiflash_api_funcs->end(chip, err);
|
||||||
if (err == ESP_OK) {
|
if (err == ESP_OK) {
|
||||||
*qe = (io_mode == SPI_FLASH_QOUT);
|
*qe = (io_mode == SPI_FLASH_QOUT);
|
||||||
}
|
}
|
||||||
@@ -695,14 +714,16 @@ IRAM_ATTR esp_err_t esp_flash_get_io_mode(esp_flash_t* chip, bool* qe)
|
|||||||
|
|
||||||
IRAM_ATTR esp_err_t esp_flash_set_io_mode(esp_flash_t* chip, bool qe)
|
IRAM_ATTR esp_err_t esp_flash_set_io_mode(esp_flash_t* chip, bool qe)
|
||||||
{
|
{
|
||||||
VERIFY_OP(set_io_mode);
|
esp_err_t err = rom_spiflash_api_funcs->chip_check(&chip);
|
||||||
|
VERIFY_CHIP_OP(set_io_mode);
|
||||||
|
|
||||||
chip->read_mode = (qe? SPI_FLASH_QOUT: SPI_FLASH_SLOWRD);
|
chip->read_mode = (qe? SPI_FLASH_QOUT: SPI_FLASH_SLOWRD);
|
||||||
esp_err_t err = spiflash_start(chip);
|
err = rom_spiflash_api_funcs->start(chip);
|
||||||
if (err != ESP_OK) {
|
if (err != ESP_OK) {
|
||||||
return err;
|
return err;
|
||||||
}
|
}
|
||||||
err = chip->chip_drv->set_io_mode(chip);
|
err = chip->chip_drv->set_io_mode(chip);
|
||||||
return spiflash_end(chip, err);
|
return rom_spiflash_api_funcs->end(chip, err);
|
||||||
}
|
}
|
||||||
|
|
||||||
#ifndef CONFIG_SPI_FLASH_USE_LEGACY_IMPL
|
#ifndef CONFIG_SPI_FLASH_USE_LEGACY_IMPL
|
||||||
|
@@ -34,7 +34,15 @@ typedef struct {
|
|||||||
uint32_t size; ///< Size of the region
|
uint32_t size; ///< Size of the region
|
||||||
} esp_flash_region_t;
|
} esp_flash_region_t;
|
||||||
|
|
||||||
/** OS-level integration hooks for accessing flash chips inside a running OS */
|
/** @brief OS-level integration hooks for accessing flash chips inside a running OS
|
||||||
|
*
|
||||||
|
* It's in the public header because some instances should be allocated statically in the startup
|
||||||
|
* code. May be updated according to hardware version and new flash chip feature requirements,
|
||||||
|
* shouldn't be treated as public API.
|
||||||
|
*
|
||||||
|
* For advanced developers, you may replace some of them with your implementations at your own
|
||||||
|
* risk.
|
||||||
|
*/
|
||||||
typedef struct {
|
typedef struct {
|
||||||
/**
|
/**
|
||||||
* Called before commencing any flash operation. Does not need to be
|
* Called before commencing any flash operation. Does not need to be
|
||||||
@@ -51,13 +59,25 @@ typedef struct {
|
|||||||
/** Delay for at least 'us' microseconds. Called in between 'start' and 'end'. */
|
/** Delay for at least 'us' microseconds. Called in between 'start' and 'end'. */
|
||||||
esp_err_t (*delay_us)(void *arg, unsigned us);
|
esp_err_t (*delay_us)(void *arg, unsigned us);
|
||||||
|
|
||||||
|
/** Called for get temp buffer when buffer from application cannot be directly read into/write from. */
|
||||||
|
void *(*get_temp_buffer)(void* arg, size_t reqest_size, size_t* out_size);
|
||||||
|
|
||||||
|
/** Called for release temp buffer. */
|
||||||
|
void (*release_temp_buffer)(void* arg, void *temp_buf);
|
||||||
|
|
||||||
/** Yield to other tasks. Called during erase operations. */
|
/** Yield to other tasks. Called during erase operations. */
|
||||||
esp_err_t (*yield)(void *arg);
|
esp_err_t (*yield)(void *arg);
|
||||||
} esp_flash_os_functions_t;
|
} esp_flash_os_functions_t;
|
||||||
|
|
||||||
/** @brief Structure to describe a SPI flash chip connected to the system.
|
/** @brief Structure to describe a SPI flash chip connected to the system.
|
||||||
|
|
||||||
Structure must be initialized before use (passed to esp_flash_init()).
|
Structure must be initialized before use (passed to esp_flash_init()). It's in the public
|
||||||
|
header because some instances should be allocated statically in the startup code. May be
|
||||||
|
updated according to hardware version and new flash chip feature requirements, shouldn't be
|
||||||
|
treated as public API.
|
||||||
|
|
||||||
|
For advanced developers, you may replace some of them with your implementations at your own
|
||||||
|
risk.
|
||||||
*/
|
*/
|
||||||
struct esp_flash_t {
|
struct esp_flash_t {
|
||||||
spi_flash_host_driver_t *host; ///< Pointer to hardware-specific "host_driver" structure. Must be initialized before used.
|
spi_flash_host_driver_t *host; ///< Pointer to hardware-specific "host_driver" structure. Must be initialized before used.
|
||||||
|
@@ -28,9 +28,9 @@
|
|||||||
.supports_direct_write = spi_flash_hal_supports_direct_write, \
|
.supports_direct_write = spi_flash_hal_supports_direct_write, \
|
||||||
.supports_direct_read = spi_flash_hal_supports_direct_read, \
|
.supports_direct_read = spi_flash_hal_supports_direct_read, \
|
||||||
.program_page = spi_flash_hal_program_page, \
|
.program_page = spi_flash_hal_program_page, \
|
||||||
.max_write_bytes = SPI_FLASH_HAL_MAX_WRITE_BYTES, \
|
.write_data_slicer = memspi_host_write_data_slicer, \
|
||||||
.read = spi_flash_hal_read, \
|
.read = spi_flash_hal_read, \
|
||||||
.max_read_bytes = SPI_FLASH_HAL_MAX_READ_BYTES, \
|
.read_data_slicer = memspi_host_read_data_slicer, \
|
||||||
.host_idle = spi_flash_hal_host_idle, \
|
.host_idle = spi_flash_hal_host_idle, \
|
||||||
.configure_host_io_mode = spi_flash_hal_configure_host_io_mode, \
|
.configure_host_io_mode = spi_flash_hal_configure_host_io_mode, \
|
||||||
.poll_cmd_done = spi_flash_hal_poll_cmd_done, \
|
.poll_cmd_done = spi_flash_hal_poll_cmd_done, \
|
||||||
@@ -140,3 +140,40 @@ void memspi_host_program_page(spi_flash_host_driver_t *driver, const void *buffe
|
|||||||
* @param wp Enable or disable write protect (true - enable, false - disable).
|
* @param wp Enable or disable write protect (true - enable, false - disable).
|
||||||
*/
|
*/
|
||||||
esp_err_t memspi_host_set_write_protect(spi_flash_host_driver_t *driver, bool wp);
|
esp_err_t memspi_host_set_write_protect(spi_flash_host_driver_t *driver, bool wp);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Read data to buffer.
|
||||||
|
*
|
||||||
|
* @param driver The driver context.
|
||||||
|
* @param buffer Buffer which contains the data to be read.
|
||||||
|
* @param address Starting address of where to read the data.
|
||||||
|
* @param length The number of bytes to read.
|
||||||
|
*/
|
||||||
|
esp_err_t memspi_host_read(spi_flash_host_driver_t *driver, void *buffer, uint32_t address, uint32_t read_len);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Slicer for read data used in non-encrypted regions. This slicer does nothing but
|
||||||
|
* limit the length to the maximum size the host supports.
|
||||||
|
*
|
||||||
|
* @param address Flash address to read
|
||||||
|
* @param len Length to read
|
||||||
|
* @param align_address Output of the address to read, should be equal to the input `address`
|
||||||
|
* @param page_size Physical SPI flash page size
|
||||||
|
*
|
||||||
|
* @return Length that can actually be read in one `read` call in `spi_flash_host_driver_t`.
|
||||||
|
*/
|
||||||
|
int memspi_host_read_data_slicer(uint32_t address, uint32_t len, uint32_t *align_address, uint32_t page_size);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Slicer for write data used in non-encrypted regions. This slicer limit the length to the
|
||||||
|
* maximum size the host supports, and truncate if the write data lie accross the page boundary
|
||||||
|
* (256 bytes)
|
||||||
|
*
|
||||||
|
* @param address Flash address to write
|
||||||
|
* @param len Length to write
|
||||||
|
* @param align_address Output of the address to write, should be equal to the input `address`
|
||||||
|
* @param page_size Physical SPI flash page size
|
||||||
|
*
|
||||||
|
* @return Length that can actually be written in one `program_page` call in `spi_flash_host_driver_t`.
|
||||||
|
*/
|
||||||
|
int memspi_host_write_data_slicer(uint32_t address, uint32_t len, uint32_t *align_address, uint32_t page_size);
|
@@ -19,6 +19,16 @@ struct esp_flash_t;
|
|||||||
typedef struct esp_flash_t esp_flash_t;
|
typedef struct esp_flash_t esp_flash_t;
|
||||||
|
|
||||||
typedef struct spi_flash_chip_t spi_flash_chip_t;
|
typedef struct spi_flash_chip_t spi_flash_chip_t;
|
||||||
|
|
||||||
|
/** Timeout configurations for flash operations, all in us */
|
||||||
|
typedef struct {
|
||||||
|
uint32_t chip_erase_timeout; ///< Timeout for chip erase operation
|
||||||
|
uint32_t block_erase_timeout; ///< Timeout for block erase operation
|
||||||
|
uint32_t sector_erase_timeout; ///< Timeout for sector erase operation
|
||||||
|
uint32_t idle_timeout; ///< Default timeout for other commands to be sent by host and get done by flash
|
||||||
|
uint32_t page_program_timeout; ///< Timeout for page program operation
|
||||||
|
} flash_chip_op_timeout_t;
|
||||||
|
|
||||||
/** @brief SPI flash chip driver definition structure.
|
/** @brief SPI flash chip driver definition structure.
|
||||||
*
|
*
|
||||||
* The chip 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
|
||||||
@@ -38,6 +48,7 @@ typedef struct spi_flash_chip_t spi_flash_chip_t;
|
|||||||
*/
|
*/
|
||||||
struct spi_flash_chip_t {
|
struct spi_flash_chip_t {
|
||||||
const char *name; ///< Name of the chip driver
|
const char *name; ///< Name of the chip driver
|
||||||
|
const flash_chip_op_timeout_t *timeout; ///< Timeout configuration for this chip
|
||||||
/* Probe to detect if a supported SPI flash chip is found.
|
/* 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.
|
* Attempts to configure 'chip' with these operations and probes for a matching SPI flash chip.
|
||||||
|
@@ -368,3 +368,6 @@ esp_err_t spi_flash_common_set_io_mode(esp_flash_t *chip, esp_flash_wrsr_func_t
|
|||||||
* - or other error passed from the ``configure_host_mode`` function of host driver
|
* - or other error passed from the ``configure_host_mode`` function of host driver
|
||||||
*/
|
*/
|
||||||
esp_err_t spi_flash_chip_generic_config_host_io_mode(esp_flash_t *chip);
|
esp_err_t spi_flash_chip_generic_config_host_io_mode(esp_flash_t *chip);
|
||||||
|
|
||||||
|
/// Default timeout configuration used by most chips
|
||||||
|
const flash_chip_op_timeout_t spi_flash_chip_generic_timeout;
|
@@ -19,6 +19,9 @@
|
|||||||
#include "cache_utils.h"
|
#include "cache_utils.h"
|
||||||
#include "esp_flash_partitions.h"
|
#include "esp_flash_partitions.h"
|
||||||
|
|
||||||
|
#define SPI_FLASH_HAL_MAX_WRITE_BYTES 64
|
||||||
|
#define SPI_FLASH_HAL_MAX_READ_BYTES 64
|
||||||
|
|
||||||
static const char TAG[] = "memspi";
|
static const char TAG[] = "memspi";
|
||||||
static const spi_flash_host_driver_t esp_flash_default_host = ESP_FLASH_DEFAULT_HOST_DRIVER();
|
static const spi_flash_host_driver_t esp_flash_default_host = ESP_FLASH_DEFAULT_HOST_DRIVER();
|
||||||
|
|
||||||
@@ -38,25 +41,25 @@ extern bool spi_flash_hal_gpspi_supports_direct_write(spi_flash_host_driver_t *d
|
|||||||
extern bool spi_flash_hal_gpspi_supports_direct_read(spi_flash_host_driver_t *driver, const void *p);
|
extern bool spi_flash_hal_gpspi_supports_direct_read(spi_flash_host_driver_t *driver, const void *p);
|
||||||
|
|
||||||
/** Default configuration for GPSPI */
|
/** Default configuration for GPSPI */
|
||||||
static const spi_flash_host_driver_t esp_flash_gpspi_host = {
|
static const spi_flash_host_driver_t esp_flash_gpspi_host = {
|
||||||
.dev_config = spi_flash_hal_gpspi_device_config,
|
.dev_config = spi_flash_hal_gpspi_device_config,
|
||||||
.common_command = spi_flash_hal_gpspi_common_command,
|
.common_command = spi_flash_hal_gpspi_common_command,
|
||||||
.read_id = memspi_host_read_id_hs,
|
.read_id = memspi_host_read_id_hs,
|
||||||
.erase_chip = memspi_host_erase_chip,
|
.erase_chip = memspi_host_erase_chip,
|
||||||
.erase_sector = memspi_host_erase_sector,
|
.erase_sector = memspi_host_erase_sector,
|
||||||
.erase_block = memspi_host_erase_block,
|
.erase_block = memspi_host_erase_block,
|
||||||
.read_status = memspi_host_read_status_hs,
|
.read_status = memspi_host_read_status_hs,
|
||||||
.set_write_protect = memspi_host_set_write_protect,
|
.set_write_protect = memspi_host_set_write_protect,
|
||||||
.supports_direct_write = spi_flash_hal_gpspi_supports_direct_write,
|
.supports_direct_write = spi_flash_hal_gpspi_supports_direct_write,
|
||||||
.supports_direct_read = spi_flash_hal_gpspi_supports_direct_read,
|
.supports_direct_read = spi_flash_hal_gpspi_supports_direct_read,
|
||||||
.program_page = memspi_host_program_page,
|
.program_page = memspi_host_program_page,
|
||||||
.max_write_bytes = SPI_FLASH_HAL_MAX_WRITE_BYTES,
|
.write_data_slicer = memspi_host_write_data_slicer,
|
||||||
.read = spi_flash_hal_gpspi_read,
|
.read = spi_flash_hal_gpspi_read,
|
||||||
.max_read_bytes = SPI_FLASH_HAL_MAX_READ_BYTES,
|
.read_data_slicer = memspi_host_read_data_slicer,
|
||||||
.host_idle = spi_flash_hal_gpspi_host_idle,
|
.host_idle = spi_flash_hal_gpspi_host_idle,
|
||||||
.configure_host_io_mode = spi_flash_hal_gpspi_configure_host_io_mode,
|
.configure_host_io_mode = spi_flash_hal_gpspi_configure_host_io_mode,
|
||||||
.poll_cmd_done = spi_flash_hal_gpspi_poll_cmd_done,
|
.poll_cmd_done = spi_flash_hal_gpspi_poll_cmd_done,
|
||||||
.flush_cache = NULL,
|
.flush_cache = NULL,
|
||||||
};
|
};
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
@@ -143,7 +146,7 @@ void memspi_host_erase_chip(spi_flash_host_driver_t *chip_drv)
|
|||||||
|
|
||||||
void memspi_host_erase_sector(spi_flash_host_driver_t *chip_drv, uint32_t start_address)
|
void memspi_host_erase_sector(spi_flash_host_driver_t *chip_drv, uint32_t start_address)
|
||||||
{
|
{
|
||||||
spi_flash_trans_t t = {
|
spi_flash_trans_t t = {
|
||||||
.command = CMD_SECTOR_ERASE,
|
.command = CMD_SECTOR_ERASE,
|
||||||
.address_bitlen = 24,
|
.address_bitlen = 24,
|
||||||
.address = start_address
|
.address = start_address
|
||||||
@@ -153,7 +156,7 @@ void memspi_host_erase_sector(spi_flash_host_driver_t *chip_drv, uint32_t start_
|
|||||||
|
|
||||||
void memspi_host_erase_block(spi_flash_host_driver_t *chip_drv, uint32_t start_address)
|
void memspi_host_erase_block(spi_flash_host_driver_t *chip_drv, uint32_t start_address)
|
||||||
{
|
{
|
||||||
spi_flash_trans_t t = {
|
spi_flash_trans_t t = {
|
||||||
.command = CMD_LARGE_BLOCK_ERASE,
|
.command = CMD_LARGE_BLOCK_ERASE,
|
||||||
.address_bitlen = 24,
|
.address_bitlen = 24,
|
||||||
.address = start_address,
|
.address = start_address,
|
||||||
@@ -163,7 +166,7 @@ void memspi_host_erase_block(spi_flash_host_driver_t *chip_drv, uint32_t start_a
|
|||||||
|
|
||||||
void memspi_host_program_page(spi_flash_host_driver_t *chip_drv, const void *buffer, uint32_t address, uint32_t length)
|
void memspi_host_program_page(spi_flash_host_driver_t *chip_drv, const void *buffer, uint32_t address, uint32_t length)
|
||||||
{
|
{
|
||||||
spi_flash_trans_t t = {
|
spi_flash_trans_t t = {
|
||||||
.command = CMD_PROGRAM_PAGE,
|
.command = CMD_PROGRAM_PAGE,
|
||||||
.address_bitlen = 24,
|
.address_bitlen = 24,
|
||||||
.address = address,
|
.address = address,
|
||||||
@@ -176,6 +179,7 @@ void memspi_host_program_page(spi_flash_host_driver_t *chip_drv, const void *buf
|
|||||||
esp_err_t memspi_host_read(spi_flash_host_driver_t *chip_drv, void *buffer, uint32_t address, uint32_t read_len)
|
esp_err_t memspi_host_read(spi_flash_host_driver_t *chip_drv, void *buffer, uint32_t address, uint32_t read_len)
|
||||||
{
|
{
|
||||||
spi_flash_trans_t t = {
|
spi_flash_trans_t t = {
|
||||||
|
.command = CMD_READ,
|
||||||
.address_bitlen = 24,
|
.address_bitlen = 24,
|
||||||
.address = address,
|
.address = address,
|
||||||
.miso_len = read_len,
|
.miso_len = read_len,
|
||||||
@@ -193,3 +197,24 @@ esp_err_t memspi_host_set_write_protect(spi_flash_host_driver_t *chip_drv, bool
|
|||||||
chip_drv->common_command(chip_drv, &t);
|
chip_drv->common_command(chip_drv, &t);
|
||||||
return ESP_OK;
|
return ESP_OK;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// When encryption is enabled, etc. the data slicer may be complicated
|
||||||
|
// This is the simple case where the hardware has no other requirements than the size and page boundary
|
||||||
|
int memspi_host_write_data_slicer(uint32_t address, uint32_t len, uint32_t *align_address, uint32_t page_size)
|
||||||
|
{
|
||||||
|
uint32_t align_addr = address;
|
||||||
|
uint32_t end_bound = (align_addr/page_size + 1) * page_size;
|
||||||
|
// Shouldn't program cross the page, or longer than SPI_FLASH_HAL_MAX_WRITE_BYTES
|
||||||
|
uint32_t max_len = MIN(end_bound - align_addr, SPI_FLASH_HAL_MAX_WRITE_BYTES);
|
||||||
|
*align_address = address;
|
||||||
|
return MIN(max_len, len);
|
||||||
|
}
|
||||||
|
|
||||||
|
int memspi_host_read_data_slicer(uint32_t address, uint32_t len, uint32_t *align_address, uint32_t page_size)
|
||||||
|
{
|
||||||
|
// Shouldn't read longer than SPI_FLASH_HAL_MAX_READ_BYTES
|
||||||
|
uint32_t max_len = SPI_FLASH_HAL_MAX_READ_BYTES;
|
||||||
|
*align_address = address;
|
||||||
|
return MIN(max_len, len);
|
||||||
|
}
|
||||||
|
|
||||||
|
@@ -78,6 +78,7 @@ static const char chip_name[] = "gd";
|
|||||||
// So we only replace these two functions.
|
// So we only replace these two functions.
|
||||||
const spi_flash_chip_t esp_flash_chip_gd = {
|
const spi_flash_chip_t esp_flash_chip_gd = {
|
||||||
.name = chip_name,
|
.name = chip_name,
|
||||||
|
.timeout = &spi_flash_chip_generic_timeout,
|
||||||
.probe = spi_flash_chip_gd_probe,
|
.probe = spi_flash_chip_gd_probe,
|
||||||
.reset = spi_flash_chip_generic_reset,
|
.reset = spi_flash_chip_generic_reset,
|
||||||
.detect_size = spi_flash_chip_generic_detect_size,
|
.detect_size = spi_flash_chip_generic_detect_size,
|
||||||
|
@@ -13,13 +13,38 @@
|
|||||||
// limitations under the License.
|
// limitations under the License.
|
||||||
|
|
||||||
#include <stdlib.h>
|
#include <stdlib.h>
|
||||||
|
#include <string.h>
|
||||||
#include <sys/param.h> // For MIN/MAX
|
#include <sys/param.h> // For MIN/MAX
|
||||||
#include "spi_flash_chip_generic.h"
|
#include "spi_flash_chip_generic.h"
|
||||||
#include "spi_flash_defs.h"
|
#include "spi_flash_defs.h"
|
||||||
#include "esp_log.h"
|
#include "esp_log.h"
|
||||||
|
#include "esp_attr.h"
|
||||||
|
|
||||||
|
|
||||||
static const char TAG[] = "chip_generic";
|
static const char TAG[] = "chip_generic";
|
||||||
|
|
||||||
|
|
||||||
|
typedef struct flash_chip_dummy {
|
||||||
|
uint8_t dio_dummy_bitlen;
|
||||||
|
uint8_t qio_dummy_bitlen;
|
||||||
|
uint8_t qout_dummy_bitlen;
|
||||||
|
uint8_t dout_dummy_bitlen;
|
||||||
|
uint8_t fastrd_dummy_bitlen;
|
||||||
|
uint8_t slowrd_dummy_bitlen;
|
||||||
|
} flash_chip_dummy_t;
|
||||||
|
|
||||||
|
// These parameters can be placed in the ROM. For now we use the code in IDF.
|
||||||
|
DRAM_ATTR const static flash_chip_dummy_t default_flash_chip_dummy = {
|
||||||
|
.dio_dummy_bitlen = SPI_FLASH_DIO_DUMMY_BITLEN,
|
||||||
|
.qio_dummy_bitlen = SPI_FLASH_QIO_DUMMY_BITLEN,
|
||||||
|
.qout_dummy_bitlen = SPI_FLASH_QOUT_DUMMY_BITLEN,
|
||||||
|
.dout_dummy_bitlen = SPI_FLASH_DOUT_DUMMY_BITLEN,
|
||||||
|
.fastrd_dummy_bitlen = SPI_FLASH_FASTRD_DUMMY_BITLEN,
|
||||||
|
.slowrd_dummy_bitlen = SPI_FLASH_SLOWRD_DUMMY_BITLEN,
|
||||||
|
};
|
||||||
|
|
||||||
|
DRAM_ATTR flash_chip_dummy_t *rom_flash_chip_dummy = (flash_chip_dummy_t *)&default_flash_chip_dummy;
|
||||||
|
|
||||||
#define SPI_FLASH_DEFAULT_IDLE_TIMEOUT_MS 200
|
#define SPI_FLASH_DEFAULT_IDLE_TIMEOUT_MS 200
|
||||||
#define SPI_FLASH_GENERIC_CHIP_ERASE_TIMEOUT_MS 4000
|
#define SPI_FLASH_GENERIC_CHIP_ERASE_TIMEOUT_MS 4000
|
||||||
#define SPI_FLASH_GENERIC_SECTOR_ERASE_TIMEOUT_MS 500 //according to GD25Q127 + 100ms
|
#define SPI_FLASH_GENERIC_SECTOR_ERASE_TIMEOUT_MS 500 //according to GD25Q127 + 100ms
|
||||||
@@ -29,6 +54,13 @@ static const char TAG[] = "chip_generic";
|
|||||||
#define HOST_DELAY_INTERVAL_US 1
|
#define HOST_DELAY_INTERVAL_US 1
|
||||||
#define CHIP_WAIT_IDLE_INTERVAL_US 20
|
#define CHIP_WAIT_IDLE_INTERVAL_US 20
|
||||||
|
|
||||||
|
const DRAM_ATTR flash_chip_op_timeout_t spi_flash_chip_generic_timeout = {
|
||||||
|
.chip_erase_timeout = SPI_FLASH_GENERIC_CHIP_ERASE_TIMEOUT_MS * 1000,
|
||||||
|
.block_erase_timeout = SPI_FLASH_GENERIC_BLOCK_ERASE_TIMEOUT_MS * 1000,
|
||||||
|
.sector_erase_timeout = SPI_FLASH_GENERIC_SECTOR_ERASE_TIMEOUT_MS * 1000,
|
||||||
|
.idle_timeout = SPI_FLASH_DEFAULT_IDLE_TIMEOUT_MS * 1000,
|
||||||
|
.page_program_timeout = SPI_FLASH_GENERIC_PAGE_PROGRAM_TIMEOUT_MS * 1000,
|
||||||
|
};
|
||||||
|
|
||||||
esp_err_t spi_flash_chip_generic_probe(esp_flash_t *chip, uint32_t flash_id)
|
esp_err_t spi_flash_chip_generic_probe(esp_flash_t *chip, uint32_t flash_id)
|
||||||
{
|
{
|
||||||
@@ -57,7 +89,7 @@ esp_err_t spi_flash_chip_generic_reset(esp_flash_t *chip)
|
|||||||
return err;
|
return err;
|
||||||
}
|
}
|
||||||
|
|
||||||
err = chip->chip_drv->wait_idle(chip, SPI_FLASH_DEFAULT_IDLE_TIMEOUT_MS * 1000);
|
err = chip->chip_drv->wait_idle(chip, chip->chip_drv->timeout->idle_timeout);
|
||||||
return err;
|
return err;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -68,7 +100,7 @@ esp_err_t spi_flash_chip_generic_detect_size(esp_flash_t *chip, uint32_t *size)
|
|||||||
|
|
||||||
/* Can't detect size unless the high byte of the product ID matches the same convention, which is usually 0x40 or
|
/* Can't detect size unless the high byte of the product ID matches the same convention, which is usually 0x40 or
|
||||||
* 0xC0 or similar. */
|
* 0xC0 or similar. */
|
||||||
if ((id & 0x0F00) != 0) {
|
if (((id & 0xFFFF) == 0x0000) || ((id & 0xFFFF) == 0xFFFF)) {
|
||||||
return ESP_ERR_FLASH_UNSUPPORTED_CHIP;
|
return ESP_ERR_FLASH_UNSUPPORTED_CHIP;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -83,7 +115,7 @@ esp_err_t spi_flash_chip_generic_erase_chip(esp_flash_t *chip)
|
|||||||
|
|
||||||
err = chip->chip_drv->set_chip_write_protect(chip, false);
|
err = chip->chip_drv->set_chip_write_protect(chip, false);
|
||||||
if (err == ESP_OK) {
|
if (err == ESP_OK) {
|
||||||
err = chip->chip_drv->wait_idle(chip, SPI_FLASH_DEFAULT_IDLE_TIMEOUT_MS * 1000);
|
err = chip->chip_drv->wait_idle(chip, chip->chip_drv->timeout->idle_timeout);
|
||||||
}
|
}
|
||||||
if (err == ESP_OK) {
|
if (err == ESP_OK) {
|
||||||
chip->host->erase_chip(chip->host);
|
chip->host->erase_chip(chip->host);
|
||||||
@@ -94,7 +126,7 @@ esp_err_t spi_flash_chip_generic_erase_chip(esp_flash_t *chip)
|
|||||||
return err;
|
return err;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
err = chip->chip_drv->wait_idle(chip, SPI_FLASH_GENERIC_CHIP_ERASE_TIMEOUT_MS * 1000);
|
err = chip->chip_drv->wait_idle(chip, chip->chip_drv->timeout->chip_erase_timeout);
|
||||||
}
|
}
|
||||||
return err;
|
return err;
|
||||||
}
|
}
|
||||||
@@ -103,7 +135,7 @@ esp_err_t spi_flash_chip_generic_erase_sector(esp_flash_t *chip, uint32_t start_
|
|||||||
{
|
{
|
||||||
esp_err_t err = chip->chip_drv->set_chip_write_protect(chip, false);
|
esp_err_t err = chip->chip_drv->set_chip_write_protect(chip, false);
|
||||||
if (err == ESP_OK) {
|
if (err == ESP_OK) {
|
||||||
err = chip->chip_drv->wait_idle(chip, SPI_FLASH_DEFAULT_IDLE_TIMEOUT_MS * 1000);
|
err = chip->chip_drv->wait_idle(chip, chip->chip_drv->timeout->idle_timeout);
|
||||||
}
|
}
|
||||||
if (err == ESP_OK) {
|
if (err == ESP_OK) {
|
||||||
chip->host->erase_sector(chip->host, start_address);
|
chip->host->erase_sector(chip->host, start_address);
|
||||||
@@ -114,7 +146,7 @@ esp_err_t spi_flash_chip_generic_erase_sector(esp_flash_t *chip, uint32_t start_
|
|||||||
return err;
|
return err;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
err = chip->chip_drv->wait_idle(chip, SPI_FLASH_GENERIC_SECTOR_ERASE_TIMEOUT_MS * 1000);
|
err = chip->chip_drv->wait_idle(chip, chip->chip_drv->timeout->sector_erase_timeout);
|
||||||
}
|
}
|
||||||
return err;
|
return err;
|
||||||
}
|
}
|
||||||
@@ -123,7 +155,7 @@ esp_err_t spi_flash_chip_generic_erase_block(esp_flash_t *chip, uint32_t start_a
|
|||||||
{
|
{
|
||||||
esp_err_t err = chip->chip_drv->set_chip_write_protect(chip, false);
|
esp_err_t err = chip->chip_drv->set_chip_write_protect(chip, false);
|
||||||
if (err == ESP_OK) {
|
if (err == ESP_OK) {
|
||||||
err = chip->chip_drv->wait_idle(chip, SPI_FLASH_DEFAULT_IDLE_TIMEOUT_MS * 1000);
|
err = chip->chip_drv->wait_idle(chip, chip->chip_drv->timeout->idle_timeout);
|
||||||
}
|
}
|
||||||
if (err == ESP_OK) {
|
if (err == ESP_OK) {
|
||||||
chip->host->erase_block(chip->host, start_address);
|
chip->host->erase_block(chip->host, start_address);
|
||||||
@@ -134,7 +166,7 @@ esp_err_t spi_flash_chip_generic_erase_block(esp_flash_t *chip, uint32_t start_a
|
|||||||
return err;
|
return err;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
err = chip->chip_drv->wait_idle(chip, SPI_FLASH_GENERIC_BLOCK_ERASE_TIMEOUT_MS * 1000);
|
err = chip->chip_drv->wait_idle(chip, chip->chip_drv->timeout->block_erase_timeout);
|
||||||
}
|
}
|
||||||
return err;
|
return err;
|
||||||
}
|
}
|
||||||
@@ -142,6 +174,10 @@ esp_err_t spi_flash_chip_generic_erase_block(esp_flash_t *chip, uint32_t start_a
|
|||||||
esp_err_t spi_flash_chip_generic_read(esp_flash_t *chip, void *buffer, uint32_t address, uint32_t length)
|
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;
|
esp_err_t err = ESP_OK;
|
||||||
|
const uint32_t page_size = chip->chip_drv->page_size;
|
||||||
|
uint32_t align_address;
|
||||||
|
uint8_t temp_buffer[64]; //spiflash hal max length of read no longer than 64byte
|
||||||
|
|
||||||
// Configure the host, and return
|
// Configure the host, and return
|
||||||
err = spi_flash_chip_generic_config_host_io_mode(chip);
|
err = spi_flash_chip_generic_config_host_io_mode(chip);
|
||||||
|
|
||||||
@@ -151,12 +187,17 @@ esp_err_t spi_flash_chip_generic_read(esp_flash_t *chip, void *buffer, uint32_t
|
|||||||
}
|
}
|
||||||
|
|
||||||
while (err == ESP_OK && length > 0) {
|
while (err == ESP_OK && length > 0) {
|
||||||
uint32_t read_len = MIN(length, chip->host->max_read_bytes);
|
memset(temp_buffer, 0xFF, sizeof(temp_buffer));
|
||||||
err = chip->host->read(chip->host, buffer, address, read_len);
|
uint32_t read_len = chip->host->read_data_slicer(address, length, &align_address, page_size);
|
||||||
|
uint32_t left_off = address - align_address;
|
||||||
|
uint32_t data_len = MIN(align_address + read_len, address + length) - address;
|
||||||
|
err = chip->host->read(chip->host, temp_buffer, align_address, read_len);
|
||||||
|
|
||||||
buffer += read_len;
|
memcpy(buffer, temp_buffer + left_off, data_len);
|
||||||
length -= read_len;
|
|
||||||
address += read_len;
|
address += data_len;
|
||||||
|
buffer = (void *)((intptr_t)buffer + data_len);
|
||||||
|
length = length - data_len;
|
||||||
}
|
}
|
||||||
|
|
||||||
return err;
|
return err;
|
||||||
@@ -166,13 +207,13 @@ esp_err_t spi_flash_chip_generic_page_program(esp_flash_t *chip, const void *buf
|
|||||||
{
|
{
|
||||||
esp_err_t err;
|
esp_err_t err;
|
||||||
|
|
||||||
err = chip->chip_drv->wait_idle(chip, SPI_FLASH_DEFAULT_IDLE_TIMEOUT_MS * 1000);
|
err = chip->chip_drv->wait_idle(chip, chip->chip_drv->timeout->idle_timeout);
|
||||||
|
|
||||||
if (err == ESP_OK) {
|
if (err == ESP_OK) {
|
||||||
// Perform the actual Page Program command
|
// Perform the actual Page Program command
|
||||||
chip->host->program_page(chip->host, buffer, address, length);
|
chip->host->program_page(chip->host, buffer, address, length);
|
||||||
|
|
||||||
err = chip->chip_drv->wait_idle(chip, SPI_FLASH_GENERIC_PAGE_PROGRAM_TIMEOUT_MS * 1000);
|
err = chip->chip_drv->wait_idle(chip, chip->chip_drv->timeout->page_program_timeout);
|
||||||
}
|
}
|
||||||
return err;
|
return err;
|
||||||
}
|
}
|
||||||
@@ -181,21 +222,23 @@ esp_err_t spi_flash_chip_generic_write(esp_flash_t *chip, const void *buffer, ui
|
|||||||
{
|
{
|
||||||
esp_err_t err = ESP_OK;
|
esp_err_t err = ESP_OK;
|
||||||
const uint32_t page_size = chip->chip_drv->page_size;
|
const uint32_t page_size = chip->chip_drv->page_size;
|
||||||
|
uint32_t align_address;
|
||||||
|
uint8_t temp_buffer[64]; //spiflash hal max length of write no longer than 64byte
|
||||||
|
|
||||||
while (err == ESP_OK && length > 0) {
|
while (err == ESP_OK && length > 0) {
|
||||||
uint32_t page_len = MIN(chip->host->max_write_bytes, MIN(page_size, length));
|
memset(temp_buffer, 0xFF, sizeof(temp_buffer));
|
||||||
if ((address + page_len) / page_size != address / page_size) {
|
uint32_t page_len = chip->host->write_data_slicer(address, length, &align_address, page_size);
|
||||||
// Most flash chips can't page write across a page boundary
|
uint32_t left_off = address - align_address;
|
||||||
page_len = page_size - (address % page_size);
|
uint32_t write_len = MIN(align_address + page_len, address + length) - address;
|
||||||
}
|
memcpy(temp_buffer + left_off, buffer, write_len);
|
||||||
|
|
||||||
err = chip->chip_drv->set_chip_write_protect(chip, false);
|
err = chip->chip_drv->set_chip_write_protect(chip, false);
|
||||||
|
if (err == ESP_OK && length > 0) {
|
||||||
|
err = chip->chip_drv->program_page(chip, temp_buffer, align_address, page_len);
|
||||||
|
|
||||||
if (err == ESP_OK) {
|
address += write_len;
|
||||||
err = chip->chip_drv->program_page(chip, buffer, address, page_len);
|
buffer = (void *)((intptr_t)buffer + write_len);
|
||||||
address += page_len;
|
length -= write_len;
|
||||||
buffer = (void *)((intptr_t)buffer + page_len);
|
|
||||||
length -= page_len;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (err == ESP_OK && chip->host->flush_cache) {
|
if (err == ESP_OK && chip->host->flush_cache) {
|
||||||
@@ -213,7 +256,7 @@ esp_err_t spi_flash_chip_generic_set_write_protect(esp_flash_t *chip, bool write
|
|||||||
{
|
{
|
||||||
esp_err_t err = ESP_OK;
|
esp_err_t err = ESP_OK;
|
||||||
|
|
||||||
err = chip->chip_drv->wait_idle(chip, SPI_FLASH_DEFAULT_IDLE_TIMEOUT_MS * 1000);
|
err = chip->chip_drv->wait_idle(chip, chip->chip_drv->timeout->idle_timeout);
|
||||||
|
|
||||||
if (err == ESP_OK) {
|
if (err == ESP_OK) {
|
||||||
chip->host->set_write_protect(chip->host, write_protect);
|
chip->host->set_write_protect(chip->host, write_protect);
|
||||||
@@ -298,33 +341,33 @@ esp_err_t spi_flash_chip_generic_config_host_io_mode(esp_flash_t *chip)
|
|||||||
case SPI_FLASH_QIO:
|
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.
|
//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 = SPI_FLASH_QIO_ADDR_BITLEN;
|
addr_bitlen = SPI_FLASH_QIO_ADDR_BITLEN;
|
||||||
dummy_cyclelen_base = SPI_FLASH_QIO_DUMMY_BITLEN;
|
dummy_cyclelen_base = rom_flash_chip_dummy->qio_dummy_bitlen;
|
||||||
read_command = CMD_FASTRD_QIO;
|
read_command = CMD_FASTRD_QIO;
|
||||||
break;
|
break;
|
||||||
case SPI_FLASH_QOUT:
|
case SPI_FLASH_QOUT:
|
||||||
addr_bitlen = SPI_FLASH_QOUT_ADDR_BITLEN;
|
addr_bitlen = SPI_FLASH_QOUT_ADDR_BITLEN;
|
||||||
dummy_cyclelen_base = SPI_FLASH_QOUT_DUMMY_BITLEN;
|
dummy_cyclelen_base = rom_flash_chip_dummy->qout_dummy_bitlen;
|
||||||
read_command = CMD_FASTRD_QUAD;
|
read_command = CMD_FASTRD_QUAD;
|
||||||
break;
|
break;
|
||||||
case SPI_FLASH_DIO:
|
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.
|
//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 = SPI_FLASH_DIO_ADDR_BITLEN;
|
addr_bitlen = SPI_FLASH_DIO_ADDR_BITLEN;
|
||||||
dummy_cyclelen_base = SPI_FLASH_DIO_DUMMY_BITLEN;
|
dummy_cyclelen_base = rom_flash_chip_dummy->dio_dummy_bitlen;
|
||||||
read_command = CMD_FASTRD_DIO;
|
read_command = CMD_FASTRD_DIO;
|
||||||
break;
|
break;
|
||||||
case SPI_FLASH_DOUT:
|
case SPI_FLASH_DOUT:
|
||||||
addr_bitlen = SPI_FLASH_DOUT_ADDR_BITLEN;
|
addr_bitlen = SPI_FLASH_DOUT_ADDR_BITLEN;
|
||||||
dummy_cyclelen_base = SPI_FLASH_DOUT_DUMMY_BITLEN;
|
dummy_cyclelen_base = rom_flash_chip_dummy->dout_dummy_bitlen;
|
||||||
read_command = CMD_FASTRD_DUAL;
|
read_command = CMD_FASTRD_DUAL;
|
||||||
break;
|
break;
|
||||||
case SPI_FLASH_FASTRD:
|
case SPI_FLASH_FASTRD:
|
||||||
addr_bitlen = SPI_FLASH_FASTRD_ADDR_BITLEN;
|
addr_bitlen = SPI_FLASH_FASTRD_ADDR_BITLEN;
|
||||||
dummy_cyclelen_base = SPI_FLASH_FASTRD_DUMMY_BITLEN;
|
dummy_cyclelen_base = rom_flash_chip_dummy->fastrd_dummy_bitlen;
|
||||||
read_command = CMD_FASTRD;
|
read_command = CMD_FASTRD;
|
||||||
break;
|
break;
|
||||||
case SPI_FLASH_SLOWRD:
|
case SPI_FLASH_SLOWRD:
|
||||||
addr_bitlen = SPI_FLASH_SLOWRD_ADDR_BITLEN;
|
addr_bitlen = SPI_FLASH_SLOWRD_ADDR_BITLEN;
|
||||||
dummy_cyclelen_base = SPI_FLASH_SLOWRD_DUMMY_BITLEN;
|
dummy_cyclelen_base = rom_flash_chip_dummy->slowrd_dummy_bitlen;
|
||||||
read_command = CMD_READ;
|
read_command = CMD_READ;
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
@@ -364,6 +407,7 @@ static const char chip_name[] = "generic";
|
|||||||
|
|
||||||
const spi_flash_chip_t esp_flash_chip_generic = {
|
const spi_flash_chip_t esp_flash_chip_generic = {
|
||||||
.name = chip_name,
|
.name = chip_name,
|
||||||
|
.timeout = &spi_flash_chip_generic_timeout,
|
||||||
.probe = spi_flash_chip_generic_probe,
|
.probe = spi_flash_chip_generic_probe,
|
||||||
.reset = spi_flash_chip_generic_reset,
|
.reset = spi_flash_chip_generic_reset,
|
||||||
.detect_size = spi_flash_chip_generic_detect_size,
|
.detect_size = spi_flash_chip_generic_detect_size,
|
||||||
@@ -505,7 +549,7 @@ esp_err_t spi_flash_common_set_io_mode(esp_flash_t *chip, esp_flash_wrsr_func_t
|
|||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
ret = chip->chip_drv->wait_idle(chip, SPI_FLASH_DEFAULT_IDLE_TIMEOUT_MS * 1000);
|
ret = chip->chip_drv->wait_idle(chip, chip->chip_drv->timeout->idle_timeout);
|
||||||
if (ret != ESP_OK) {
|
if (ret != ESP_OK) {
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
@@ -64,6 +64,7 @@ static const char chip_name[] = "issi";
|
|||||||
// So we only replace these two functions.
|
// So we only replace these two functions.
|
||||||
const spi_flash_chip_t esp_flash_chip_issi = {
|
const spi_flash_chip_t esp_flash_chip_issi = {
|
||||||
.name = chip_name,
|
.name = chip_name,
|
||||||
|
.timeout = &spi_flash_chip_generic_timeout,
|
||||||
.probe = spi_flash_chip_issi_probe,
|
.probe = spi_flash_chip_issi_probe,
|
||||||
.reset = spi_flash_chip_generic_reset,
|
.reset = spi_flash_chip_generic_reset,
|
||||||
.detect_size = spi_flash_chip_generic_detect_size,
|
.detect_size = spi_flash_chip_generic_detect_size,
|
||||||
|
@@ -42,6 +42,7 @@ static const char chip_name[] = "mxic";
|
|||||||
// So we only replace these two functions.
|
// So we only replace these two functions.
|
||||||
const spi_flash_chip_t esp_flash_chip_mxic = {
|
const spi_flash_chip_t esp_flash_chip_mxic = {
|
||||||
.name = chip_name,
|
.name = chip_name,
|
||||||
|
.timeout = &spi_flash_chip_generic_timeout,
|
||||||
.probe = spi_flash_chip_mxic_probe,
|
.probe = spi_flash_chip_mxic_probe,
|
||||||
.reset = spi_flash_chip_generic_reset,
|
.reset = spi_flash_chip_generic_reset,
|
||||||
.detect_size = spi_flash_chip_generic_detect_size,
|
.detect_size = spi_flash_chip_generic_detect_size,
|
||||||
|
@@ -13,6 +13,7 @@
|
|||||||
// limitations under the License.
|
// limitations under the License.
|
||||||
|
|
||||||
#include <stdarg.h>
|
#include <stdarg.h>
|
||||||
|
#include <sys/param.h> //For max/min
|
||||||
#include "esp_attr.h"
|
#include "esp_attr.h"
|
||||||
#include "esp_spi_flash.h" //for ``g_flash_guard_default_ops``
|
#include "esp_spi_flash.h" //for ``g_flash_guard_default_ops``
|
||||||
#include "esp_flash.h"
|
#include "esp_flash.h"
|
||||||
@@ -21,6 +22,7 @@
|
|||||||
#include "freertos/task.h"
|
#include "freertos/task.h"
|
||||||
#include "hal/spi_types.h"
|
#include "hal/spi_types.h"
|
||||||
#include "sdkconfig.h"
|
#include "sdkconfig.h"
|
||||||
|
#include "esp_log.h"
|
||||||
|
|
||||||
#if CONFIG_IDF_TARGET_ESP32
|
#if CONFIG_IDF_TARGET_ESP32
|
||||||
#include "esp32/rom/ets_sys.h"
|
#include "esp32/rom/ets_sys.h"
|
||||||
@@ -30,6 +32,7 @@
|
|||||||
|
|
||||||
#include "driver/spi_common_internal.h"
|
#include "driver/spi_common_internal.h"
|
||||||
|
|
||||||
|
static const char TAG[] = "spi_flash";
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* OS functions providing delay service and arbitration among chips, and with the cache.
|
* OS functions providing delay service and arbitration among chips, and with the cache.
|
||||||
@@ -102,7 +105,6 @@ static IRAM_ATTR esp_err_t delay_us(void *arg, unsigned us)
|
|||||||
ets_delay_us(us);
|
ets_delay_us(us);
|
||||||
return ESP_OK;
|
return ESP_OK;
|
||||||
}
|
}
|
||||||
|
|
||||||
static IRAM_ATTR esp_err_t spi_flash_os_yield(void *arg)
|
static IRAM_ATTR esp_err_t spi_flash_os_yield(void *arg)
|
||||||
{
|
{
|
||||||
#ifdef CONFIG_SPI_FLASH_YIELD_DURING_ERASE
|
#ifdef CONFIG_SPI_FLASH_YIELD_DURING_ERASE
|
||||||
@@ -111,6 +113,32 @@ static IRAM_ATTR esp_err_t spi_flash_os_yield(void *arg)
|
|||||||
return ESP_OK;
|
return ESP_OK;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static IRAM_ATTR void* get_buffer_malloc(void* arg, size_t reqest_size, size_t* out_size)
|
||||||
|
{
|
||||||
|
/* Allocate temporary internal buffer to use for the actual read. If the preferred size
|
||||||
|
doesn't fit in free internal memory, allocate the largest available free block.
|
||||||
|
|
||||||
|
(May need to shrink read_chunk_size and retry due to race conditions with other tasks
|
||||||
|
also allocating from the heap.)
|
||||||
|
*/
|
||||||
|
void* ret = NULL;
|
||||||
|
unsigned retries = 5;
|
||||||
|
size_t read_chunk_size = reqest_size;
|
||||||
|
while(ret == NULL && retries--) {
|
||||||
|
read_chunk_size = MIN(read_chunk_size, heap_caps_get_largest_free_block(MALLOC_CAP_INTERNAL | MALLOC_CAP_8BIT));
|
||||||
|
read_chunk_size = (read_chunk_size + 3) & ~3;
|
||||||
|
ret = heap_caps_malloc(read_chunk_size, MALLOC_CAP_INTERNAL | MALLOC_CAP_8BIT);
|
||||||
|
}
|
||||||
|
ESP_LOGV(TAG, "allocate temp buffer: %p (%d)", ret, read_chunk_size);
|
||||||
|
*out_size = (ret != NULL? read_chunk_size: 0);
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
static IRAM_ATTR void release_buffer_malloc(void* arg, void *temp_buf)
|
||||||
|
{
|
||||||
|
free(temp_buf);
|
||||||
|
}
|
||||||
|
|
||||||
static IRAM_ATTR esp_err_t main_flash_region_protected(void* arg, size_t start_addr, size_t size)
|
static IRAM_ATTR esp_err_t main_flash_region_protected(void* arg, size_t start_addr, size_t size)
|
||||||
{
|
{
|
||||||
if (((spi1_app_func_arg_t*)arg)->no_protect || esp_partition_main_flash_region_safe(start_addr, size)) {
|
if (((spi1_app_func_arg_t*)arg)->no_protect || esp_partition_main_flash_region_safe(start_addr, size)) {
|
||||||
@@ -129,6 +157,8 @@ static const DRAM_ATTR esp_flash_os_functions_t esp_flash_spi1_default_os_functi
|
|||||||
.end = spi1_end,
|
.end = spi1_end,
|
||||||
.region_protected = main_flash_region_protected,
|
.region_protected = main_flash_region_protected,
|
||||||
.delay_us = delay_us,
|
.delay_us = delay_us,
|
||||||
|
.get_temp_buffer = get_buffer_malloc,
|
||||||
|
.release_temp_buffer = release_buffer_malloc,
|
||||||
.yield = spi_flash_os_yield,
|
.yield = spi_flash_os_yield,
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -136,6 +166,8 @@ static const esp_flash_os_functions_t esp_flash_spi23_default_os_functions = {
|
|||||||
.start = spi_start,
|
.start = spi_start,
|
||||||
.end = spi_end,
|
.end = spi_end,
|
||||||
.delay_us = delay_us,
|
.delay_us = delay_us,
|
||||||
|
.get_temp_buffer = get_buffer_malloc,
|
||||||
|
.release_temp_buffer = release_buffer_malloc,
|
||||||
.yield = spi_flash_os_yield
|
.yield = spi_flash_os_yield
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@@ -31,7 +31,7 @@
|
|||||||
typedef struct {
|
typedef struct {
|
||||||
uint32_t icache_autoload;
|
uint32_t icache_autoload;
|
||||||
uint32_t dcache_autoload;
|
uint32_t dcache_autoload;
|
||||||
} spi_noos_arg_t;
|
} spi_noos_arg_t;
|
||||||
|
|
||||||
static DRAM_ATTR spi_noos_arg_t spi_arg = { 0 };
|
static DRAM_ATTR spi_noos_arg_t spi_arg = { 0 };
|
||||||
#endif
|
#endif
|
||||||
@@ -71,11 +71,19 @@ static IRAM_ATTR esp_err_t delay_us(void *arg, unsigned us)
|
|||||||
return ESP_OK;
|
return ESP_OK;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Currently when the os is not up yet, the caller is supposed to call esp_flash APIs with proper
|
||||||
|
// buffers.
|
||||||
|
IRAM_ATTR void* get_temp_buffer_not_supported(void* arg, size_t reqest_size, size_t* out_size)
|
||||||
|
{
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
const DRAM_ATTR esp_flash_os_functions_t esp_flash_noos_functions = {
|
const DRAM_ATTR esp_flash_os_functions_t esp_flash_noos_functions = {
|
||||||
.start = start,
|
.start = start,
|
||||||
.end = end,
|
.end = end,
|
||||||
.delay_us = delay_us,
|
.delay_us = delay_us,
|
||||||
.region_protected = NULL,
|
.region_protected = NULL,
|
||||||
|
.get_temp_buffer = get_temp_buffer_not_supported,
|
||||||
.yield = NULL,
|
.yield = NULL,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
Reference in New Issue
Block a user