From a9c8895bb29077b47c444b7ebf0f0e1bdc096494 Mon Sep 17 00:00:00 2001 From: "Michael (XIAO Xufeng)" Date: Thu, 30 Apr 2020 10:37:35 +0800 Subject: [PATCH] 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. --- components/soc/include/hal/spi_flash_types.h | 32 ++- components/spi_flash/esp_flash_api.c | 221 ++++++++++-------- components/spi_flash/include/esp_flash.h | 24 +- .../spi_flash/include/memspi_host_driver.h | 41 +++- .../spi_flash/include/spi_flash_chip_driver.h | 11 + .../include/spi_flash_chip_generic.h | 3 + components/spi_flash/memspi_host_driver.c | 69 ++++-- components/spi_flash/spi_flash_chip_gd.c | 1 + components/spi_flash/spi_flash_chip_generic.c | 110 ++++++--- components/spi_flash/spi_flash_chip_issi.c | 1 + components/spi_flash/spi_flash_chip_mxic.c | 1 + components/spi_flash/spi_flash_os_func_app.c | 34 ++- components/spi_flash/spi_flash_os_func_noos.c | 10 +- 13 files changed, 390 insertions(+), 168 deletions(-) diff --git a/components/soc/include/hal/spi_flash_types.h b/components/soc/include/hal/spi_flash_types.h index 0a468c6075..586dd6ee89 100644 --- a/components/soc/include/hal/spi_flash_types.h +++ b/components/soc/include/hal/spi_flash_types.h @@ -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. */ 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); - /** 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; + /** + * Slicer for write data. The `program_page` should be called iteratively with the return value + * of this function. + * + * @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. */ 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 given buffer can be directly used to read */ + 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. */ diff --git a/components/spi_flash/esp_flash_api.c b/components/spi_flash/esp_flash_api.c index 8ea142a7ca..802d69684a 100644 --- a/components/spi_flash/esp_flash_api.c +++ b/components/spi_flash/esp_flash_api.c @@ -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); +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. 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) { 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 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 && 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; } +// 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. */ 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]); - err = spiflash_start(chip); + err = rom_spiflash_api_funcs->start(chip); if (err != ESP_OK) { 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 - return spiflash_end(chip, err); + return rom_spiflash_api_funcs->end(chip, err); } //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 err = spiflash_start(chip); + esp_err_t err = rom_spiflash_api_funcs->start(chip); if (err != ESP_OK) { 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) @@ -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 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) { 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 drivers++; - err = spiflash_end(chip, err); + err = rom_spiflash_api_funcs->end(chip, err); if (err != ESP_OK) { return err; } @@ -228,16 +261,12 @@ static esp_err_t IRAM_ATTR detect_spi_flash_chip(esp_flash_t *chip) 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; \ - } \ +/* Convenience macro for beginning of all API functions. + * Check the return value of `rom_spiflash_api_funcs->chip_check` is correct, + * and the chip supports the operation in question. + */ +#define VERIFY_CHIP_OP(OP) do { \ + if (err != ESP_OK) return err; \ if (chip->chip_drv->OP == NULL) { \ 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) { - if (chip == NULL) { - chip = esp_flash_default_chip; - } - if (chip == NULL || !esp_flash_chip_driver_initialized(chip)) { - return ESP_ERR_FLASH_NOT_INITIALISED; - } - if (out_id == NULL) { - return ESP_ERR_INVALID_ARG; - } - esp_err_t err = spiflash_start(chip); + esp_err_t err = rom_spiflash_api_funcs->chip_check(&chip); + //Accept uninitialized chip when reading chip id + if (err != ESP_OK && !(err == ESP_ERR_FLASH_NOT_INITIALISED && chip != NULL)) return err; + if (out_id == NULL) return ESP_ERR_INVALID_ARG; + + err = rom_spiflash_api_funcs->start(chip); if (err != ESP_OK) { return err; } 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) { - VERIFY_OP(detect_size); + esp_err_t err = rom_spiflash_api_funcs->chip_check(&chip); + VERIFY_CHIP_OP(detect_size); if (out_size == NULL) { 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; } - esp_err_t err = spiflash_start(chip); + err = rom_spiflash_api_funcs->start(chip); if (err != ESP_OK) { 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) { 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) { - 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); - esp_err_t err = spiflash_start(chip); + err = rom_spiflash_api_funcs->start(chip); if (err != ESP_OK) { return err; } 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) { - VERIFY_OP(erase_sector); - VERIFY_OP(erase_block); + esp_err_t err = rom_spiflash_api_funcs->chip_check(&chip); + VERIFY_CHIP_OP(erase_sector); + VERIFY_CHIP_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; @@ -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; } - esp_err_t err = ESP_OK; + err = ESP_OK; // Check for write protected regions overlapping the erase region if (chip->chip_drv->get_protected_regions != NULL && chip->chip_drv->num_protectable_regions > 0) { - err = spiflash_start(chip); + err = rom_spiflash_api_funcs->start(chip); if (err != ESP_OK) { 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 - err = spiflash_end(chip, err); + err = rom_spiflash_api_funcs->end(chip, err); } #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 int64_t start_time_us = esp_timer_get_time(); #endif - err = spiflash_start(chip); + err = rom_spiflash_api_funcs->start(chip); if (err != ESP_OK) { 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; } - err = spiflash_end(chip, err); + err = rom_spiflash_api_funcs->end(chip, err); #ifdef CONFIG_SPI_FLASH_YIELD_DURING_ERASE 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) { - 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) { return ESP_ERR_INVALID_ARG; } - esp_err_t err = spiflash_start(chip); + err = rom_spiflash_api_funcs->start(chip); if (err != ESP_OK) { return err; } 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) { - 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 - esp_err_t err = spiflash_start(chip); + err = rom_spiflash_api_funcs->start(chip); if (err != ESP_OK) { return err; } 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) @@ -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) { *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) { 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) { - 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) { return ESP_ERR_INVALID_ARG; } uint8_t index; - esp_err_t err = find_region(chip, region, &index); + err = find_region(chip, region, &index); if (err != ESP_OK) { return err; } uint64_t protection_mask = 0; - err = spiflash_start(chip); + err = rom_spiflash_api_funcs->start(chip); if (err != ESP_OK) { 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); } - 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) { - 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; - esp_err_t err = find_region(chip, region, &index); + err = find_region(chip, region, &index); if (err != ESP_OK) { return err; } uint64_t protection_mask = 0; - err = spiflash_start(chip); + err = rom_spiflash_api_funcs->start(chip); if (err != ESP_OK) { 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); } - 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) @@ -512,7 +546,8 @@ esp_err_t IRAM_ATTR esp_flash_read(esp_flash_t *chip, void *buffer, uint32_t add if (length == 0) { 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) { 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); if (!direct_read) { - /* 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.) - */ - 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); - + size_t actual_len = 0; + temp_buffer = chip->os_func->get_temp_buffer(chip->os_func_data, read_chunk_size, &actual_len); + read_chunk_size = actual_len; if (temp_buffer == NULL) { return ESP_ERR_NO_MEM; } } - esp_err_t err = ESP_OK; - + err = ESP_OK; do { - err = spiflash_start(chip); + err = rom_spiflash_api_funcs->start(chip); if (err != ESP_OK) { 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); } if (err != ESP_OK) { - spiflash_end(chip, err); + rom_spiflash_api_funcs->end(chip, err); break; } //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 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; length -= length_to_read; - buffer += length_to_read; + buffer = (void*)((intptr_t)buffer + length_to_read); } while (err == ESP_OK && length > 0); - free(temp_buffer); + chip->os_func->release_temp_buffer(chip->os_func_data, temp_buffer); 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) { 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); if (buffer == NULL || address > chip->size || address+length > chip->size) { 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 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 by artificially cutting into MAX_WRITE_CHUNK parts (in an OS 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; } - err = spiflash_start(chip); + err = rom_spiflash_api_funcs->start(chip); if (err != ESP_OK) { 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); length -= write_len; - err = spiflash_end(chip, err); + err = rom_spiflash_api_funcs->end(chip, err); } while (err == ESP_OK && length > 0); 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 * implementation and skip the chip and driver layers. */ - if (chip == NULL) { - chip = esp_flash_default_chip; - } else if (chip != esp_flash_default_chip) { - return ESP_ERR_NOT_SUPPORTED; - } + esp_err_t err = rom_spiflash_api_funcs->chip_check(&chip); + if (err != ESP_OK) return err; if (buffer == NULL || address > chip->size || address+length > chip->size) { 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 * implementation and skip the chip and driver layers. */ - if (chip == NULL) { - chip = esp_flash_default_chip; - } else if (chip != esp_flash_default_chip) { - return ESP_ERR_NOT_SUPPORTED; - } + esp_err_t err = rom_spiflash_api_funcs->chip_check(&chip); + if (err != ESP_OK) return err; return spi_flash_read_encrypted(address, out_buffer, length); } // test only, non-public 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_err_t err = spiflash_start(chip); + err = rom_spiflash_api_funcs->start(chip); if (err != ESP_OK) { return err; } 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) { *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) { - 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); - esp_err_t err = spiflash_start(chip); + err = rom_spiflash_api_funcs->start(chip); if (err != ESP_OK) { return err; } 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 diff --git a/components/spi_flash/include/esp_flash.h b/components/spi_flash/include/esp_flash.h index ccea0d0722..c4951fe91e 100644 --- a/components/spi_flash/include/esp_flash.h +++ b/components/spi_flash/include/esp_flash.h @@ -34,7 +34,15 @@ typedef struct { uint32_t size; ///< Size of the region } 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 { /** * 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'. */ 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. */ esp_err_t (*yield)(void *arg); } esp_flash_os_functions_t; /** @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 { spi_flash_host_driver_t *host; ///< Pointer to hardware-specific "host_driver" structure. Must be initialized before used. diff --git a/components/spi_flash/include/memspi_host_driver.h b/components/spi_flash/include/memspi_host_driver.h index 8832adfcc2..8c476ad4c7 100644 --- a/components/spi_flash/include/memspi_host_driver.h +++ b/components/spi_flash/include/memspi_host_driver.h @@ -28,9 +28,9 @@ .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, \ + .write_data_slicer = memspi_host_write_data_slicer, \ .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, \ .configure_host_io_mode = spi_flash_hal_configure_host_io_mode, \ .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). */ 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); \ No newline at end of file diff --git a/components/spi_flash/include/spi_flash_chip_driver.h b/components/spi_flash/include/spi_flash_chip_driver.h index 46667350de..c0b19badf4 100644 --- a/components/spi_flash/include/spi_flash_chip_driver.h +++ b/components/spi_flash/include/spi_flash_chip_driver.h @@ -19,6 +19,16 @@ struct esp_flash_t; typedef struct esp_flash_t esp_flash_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. * * 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 { 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. * * Attempts to configure 'chip' with these operations and probes for a matching SPI flash chip. diff --git a/components/spi_flash/include/spi_flash_chip_generic.h b/components/spi_flash/include/spi_flash_chip_generic.h index 15ff8f7b11..814502d26e 100644 --- a/components/spi_flash/include/spi_flash_chip_generic.h +++ b/components/spi_flash/include/spi_flash_chip_generic.h @@ -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 */ 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; \ No newline at end of file diff --git a/components/spi_flash/memspi_host_driver.c b/components/spi_flash/memspi_host_driver.c index 3d87f8ae7a..645916bda0 100644 --- a/components/spi_flash/memspi_host_driver.c +++ b/components/spi_flash/memspi_host_driver.c @@ -19,6 +19,9 @@ #include "cache_utils.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 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); /** Default configuration for GPSPI */ -static const spi_flash_host_driver_t esp_flash_gpspi_host = { - .dev_config = spi_flash_hal_gpspi_device_config, - .common_command = spi_flash_hal_gpspi_common_command, - .read_id = memspi_host_read_id_hs, - .erase_chip = memspi_host_erase_chip, - .erase_sector = memspi_host_erase_sector, - .erase_block = memspi_host_erase_block, - .read_status = memspi_host_read_status_hs, - .set_write_protect = memspi_host_set_write_protect, - .supports_direct_write = spi_flash_hal_gpspi_supports_direct_write, - .supports_direct_read = spi_flash_hal_gpspi_supports_direct_read, - .program_page = memspi_host_program_page, - .max_write_bytes = SPI_FLASH_HAL_MAX_WRITE_BYTES, - .read = spi_flash_hal_gpspi_read, - .max_read_bytes = SPI_FLASH_HAL_MAX_READ_BYTES, - .host_idle = spi_flash_hal_gpspi_host_idle, - .configure_host_io_mode = spi_flash_hal_gpspi_configure_host_io_mode, - .poll_cmd_done = spi_flash_hal_gpspi_poll_cmd_done, - .flush_cache = NULL, +static const spi_flash_host_driver_t esp_flash_gpspi_host = { + .dev_config = spi_flash_hal_gpspi_device_config, + .common_command = spi_flash_hal_gpspi_common_command, + .read_id = memspi_host_read_id_hs, + .erase_chip = memspi_host_erase_chip, + .erase_sector = memspi_host_erase_sector, + .erase_block = memspi_host_erase_block, + .read_status = memspi_host_read_status_hs, + .set_write_protect = memspi_host_set_write_protect, + .supports_direct_write = spi_flash_hal_gpspi_supports_direct_write, + .supports_direct_read = spi_flash_hal_gpspi_supports_direct_read, + .program_page = memspi_host_program_page, + .write_data_slicer = memspi_host_write_data_slicer, + .read = spi_flash_hal_gpspi_read, + .read_data_slicer = memspi_host_read_data_slicer, + .host_idle = spi_flash_hal_gpspi_host_idle, + .configure_host_io_mode = spi_flash_hal_gpspi_configure_host_io_mode, + .poll_cmd_done = spi_flash_hal_gpspi_poll_cmd_done, + .flush_cache = NULL, }; #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) { - spi_flash_trans_t t = { + spi_flash_trans_t t = { .command = CMD_SECTOR_ERASE, .address_bitlen = 24, .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) { - spi_flash_trans_t t = { + spi_flash_trans_t t = { .command = CMD_LARGE_BLOCK_ERASE, .address_bitlen = 24, .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) { - spi_flash_trans_t t = { + spi_flash_trans_t t = { .command = CMD_PROGRAM_PAGE, .address_bitlen = 24, .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) { spi_flash_trans_t t = { + .command = CMD_READ, .address_bitlen = 24, .address = address, .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); 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); +} + diff --git a/components/spi_flash/spi_flash_chip_gd.c b/components/spi_flash/spi_flash_chip_gd.c index bc877d2cb1..e6c4539c1b 100644 --- a/components/spi_flash/spi_flash_chip_gd.c +++ b/components/spi_flash/spi_flash_chip_gd.c @@ -78,6 +78,7 @@ static const char chip_name[] = "gd"; // So we only replace these two functions. const spi_flash_chip_t esp_flash_chip_gd = { .name = chip_name, + .timeout = &spi_flash_chip_generic_timeout, .probe = spi_flash_chip_gd_probe, .reset = spi_flash_chip_generic_reset, .detect_size = spi_flash_chip_generic_detect_size, diff --git a/components/spi_flash/spi_flash_chip_generic.c b/components/spi_flash/spi_flash_chip_generic.c index e0c3badd50..cf9a306b64 100644 --- a/components/spi_flash/spi_flash_chip_generic.c +++ b/components/spi_flash/spi_flash_chip_generic.c @@ -13,13 +13,38 @@ // limitations under the License. #include +#include #include // For MIN/MAX #include "spi_flash_chip_generic.h" #include "spi_flash_defs.h" #include "esp_log.h" +#include "esp_attr.h" + 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_GENERIC_CHIP_ERASE_TIMEOUT_MS 4000 #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 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) { @@ -57,7 +89,7 @@ esp_err_t spi_flash_chip_generic_reset(esp_flash_t *chip) 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; } @@ -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 * 0xC0 or similar. */ - if ((id & 0x0F00) != 0) { + if (((id & 0xFFFF) == 0x0000) || ((id & 0xFFFF) == 0xFFFF)) { 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); 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) { 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; } } - 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; } @@ -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); 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) { 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; } } - 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; } @@ -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); 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) { 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; } } - 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; } @@ -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 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 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) { - uint32_t read_len = MIN(length, chip->host->max_read_bytes); - err = chip->host->read(chip->host, buffer, address, read_len); + memset(temp_buffer, 0xFF, sizeof(temp_buffer)); + 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; - length -= read_len; - address += read_len; + memcpy(buffer, temp_buffer + left_off, data_len); + + address += data_len; + buffer = (void *)((intptr_t)buffer + data_len); + length = length - data_len; } 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; - 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) { // Perform the actual Page Program command 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; } @@ -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; 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) { - 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); - } + memset(temp_buffer, 0xFF, sizeof(temp_buffer)); + uint32_t page_len = chip->host->write_data_slicer(address, length, &align_address, page_size); + uint32_t left_off = address - align_address; + 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); + if (err == ESP_OK && length > 0) { + err = chip->chip_drv->program_page(chip, temp_buffer, align_address, page_len); - 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; + address += write_len; + buffer = (void *)((intptr_t)buffer + write_len); + length -= write_len; } } 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; - 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) { 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: //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; - dummy_cyclelen_base = SPI_FLASH_QIO_DUMMY_BITLEN; + dummy_cyclelen_base = rom_flash_chip_dummy->qio_dummy_bitlen; read_command = CMD_FASTRD_QIO; break; case SPI_FLASH_QOUT: 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; 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 = 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; break; case SPI_FLASH_DOUT: 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; break; case SPI_FLASH_FASTRD: 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; break; case SPI_FLASH_SLOWRD: 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; break; default: @@ -364,6 +407,7 @@ static const char chip_name[] = "generic"; const spi_flash_chip_t esp_flash_chip_generic = { .name = chip_name, + .timeout = &spi_flash_chip_generic_timeout, .probe = spi_flash_chip_generic_probe, .reset = spi_flash_chip_generic_reset, .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; } - 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) { return ret; } diff --git a/components/spi_flash/spi_flash_chip_issi.c b/components/spi_flash/spi_flash_chip_issi.c index d5ecd4c040..e57d227686 100644 --- a/components/spi_flash/spi_flash_chip_issi.c +++ b/components/spi_flash/spi_flash_chip_issi.c @@ -64,6 +64,7 @@ static const char chip_name[] = "issi"; // So we only replace these two functions. const spi_flash_chip_t esp_flash_chip_issi = { .name = chip_name, + .timeout = &spi_flash_chip_generic_timeout, .probe = spi_flash_chip_issi_probe, .reset = spi_flash_chip_generic_reset, .detect_size = spi_flash_chip_generic_detect_size, diff --git a/components/spi_flash/spi_flash_chip_mxic.c b/components/spi_flash/spi_flash_chip_mxic.c index 4df6a4f723..36c27b9162 100644 --- a/components/spi_flash/spi_flash_chip_mxic.c +++ b/components/spi_flash/spi_flash_chip_mxic.c @@ -42,6 +42,7 @@ static const char chip_name[] = "mxic"; // So we only replace these two functions. const spi_flash_chip_t esp_flash_chip_mxic = { .name = chip_name, + .timeout = &spi_flash_chip_generic_timeout, .probe = spi_flash_chip_mxic_probe, .reset = spi_flash_chip_generic_reset, .detect_size = spi_flash_chip_generic_detect_size, diff --git a/components/spi_flash/spi_flash_os_func_app.c b/components/spi_flash/spi_flash_os_func_app.c index ceb046ec6d..8fd4d72e25 100644 --- a/components/spi_flash/spi_flash_os_func_app.c +++ b/components/spi_flash/spi_flash_os_func_app.c @@ -13,6 +13,7 @@ // limitations under the License. #include +#include //For max/min #include "esp_attr.h" #include "esp_spi_flash.h" //for ``g_flash_guard_default_ops`` #include "esp_flash.h" @@ -21,6 +22,7 @@ #include "freertos/task.h" #include "hal/spi_types.h" #include "sdkconfig.h" +#include "esp_log.h" #if CONFIG_IDF_TARGET_ESP32 #include "esp32/rom/ets_sys.h" @@ -30,6 +32,7 @@ #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. @@ -102,7 +105,6 @@ static IRAM_ATTR esp_err_t delay_us(void *arg, unsigned us) ets_delay_us(us); return ESP_OK; } - static IRAM_ATTR esp_err_t spi_flash_os_yield(void *arg) { #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; } +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) { 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, .region_protected = main_flash_region_protected, .delay_us = delay_us, + .get_temp_buffer = get_buffer_malloc, + .release_temp_buffer = release_buffer_malloc, .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, .end = spi_end, .delay_us = delay_us, + .get_temp_buffer = get_buffer_malloc, + .release_temp_buffer = release_buffer_malloc, .yield = spi_flash_os_yield }; diff --git a/components/spi_flash/spi_flash_os_func_noos.c b/components/spi_flash/spi_flash_os_func_noos.c index d565395e0b..27e87f3c42 100644 --- a/components/spi_flash/spi_flash_os_func_noos.c +++ b/components/spi_flash/spi_flash_os_func_noos.c @@ -31,7 +31,7 @@ typedef struct { uint32_t icache_autoload; uint32_t dcache_autoload; -} spi_noos_arg_t; +} spi_noos_arg_t; static DRAM_ATTR spi_noos_arg_t spi_arg = { 0 }; #endif @@ -71,11 +71,19 @@ static IRAM_ATTR esp_err_t delay_us(void *arg, unsigned us) 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 = { .start = start, .end = end, .delay_us = delay_us, .region_protected = NULL, + .get_temp_buffer = get_temp_buffer_not_supported, .yield = NULL, };