mirror of
https://github.com/espressif/esp-idf.git
synced 2024-10-05 20:47:46 -04: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:
parent
9d21b17384
commit
a9c8895bb2
@ -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.
|
||||
*/
|
||||
|
@ -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
|
||||
|
@ -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.
|
||||
|
@ -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);
|
@ -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.
|
||||
|
@ -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;
|
@ -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);
|
||||
}
|
||||
|
||||
|
@ -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,
|
||||
|
@ -13,13 +13,38 @@
|
||||
// limitations under the License.
|
||||
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <sys/param.h> // 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;
|
||||
}
|
||||
|
@ -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,
|
||||
|
@ -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,
|
||||
|
@ -13,6 +13,7 @@
|
||||
// limitations under the License.
|
||||
|
||||
#include <stdarg.h>
|
||||
#include <sys/param.h> //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
|
||||
};
|
||||
|
||||
|
@ -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,
|
||||
};
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user