-
Notifications
You must be signed in to change notification settings - Fork 98
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Loading status checks…
feat(spi_nand_flash): expose lower-level API and make it usable witho…
…ut Dhara
1 parent
d402e9c
commit fe2657f
Showing
17 changed files
with
962 additions
and
549 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,76 @@ | ||
/* | ||
* SPDX-FileCopyrightText: 2022 mikkeldamsgaard project | ||
* | ||
* SPDX-License-Identifier: Apache-2.0 | ||
* | ||
* SPDX-FileContributor: 2015-2024 Espressif Systems (Shanghai) CO LTD | ||
*/ | ||
|
||
#pragma once | ||
|
||
#include <stdint.h> | ||
#include "spi_nand_flash.h" | ||
#include "freertos/semphr.h" | ||
|
||
#ifdef __cplusplus | ||
extern "C" { | ||
#endif | ||
|
||
#define INVALID_PAGE 0xFFFF | ||
|
||
typedef enum { | ||
STAT_ECC_OK = 0, | ||
STAT_ECC_1_TO_3_BITS_CORRECTED = 1, | ||
STAT_ECC_BITS_CORRECTED = STAT_ECC_1_TO_3_BITS_CORRECTED, | ||
STAT_ECC_NOT_CORRECTED = 2, | ||
STAT_ECC_4_TO_6_BITS_CORRECTED = 3, | ||
STAT_ECC_MAX_BITS_CORRECTED = STAT_ECC_4_TO_6_BITS_CORRECTED, | ||
STAT_ECC_7_8_BITS_CORRECTED = 5, | ||
STAT_ECC_MAX | ||
} ecc_status_t; | ||
|
||
typedef struct { | ||
uint8_t ecc_status_reg_len_in_bits; | ||
uint8_t ecc_data_refresh_threshold; | ||
ecc_status_t ecc_corrected_bits_status; | ||
} ecc_data_t; | ||
|
||
typedef struct { | ||
uint8_t log2_page_size; //is power of 2, log2_page_size shift (1<<log2_page_size) is stored to page_size | ||
uint8_t log2_ppb; //is power of 2, log2_ppb shift ((1<<log2_ppb) * page_size) will be stored in block size | ||
uint32_t block_size; | ||
uint32_t page_size; | ||
uint32_t num_blocks; | ||
uint32_t read_page_delay_us; | ||
uint32_t erase_block_delay_us; | ||
uint32_t program_page_delay_us; | ||
ecc_data_t ecc_data; | ||
} spi_nand_chip_t; | ||
|
||
typedef struct { | ||
esp_err_t (*init)(spi_nand_flash_device_t *handle); | ||
esp_err_t (*deinit)(spi_nand_flash_device_t *handle); | ||
esp_err_t (*read)(spi_nand_flash_device_t *handle, uint8_t *buffer, sector_t sector_id); | ||
esp_err_t (*write)(spi_nand_flash_device_t *handle, const uint8_t *buffer, sector_t sector_id); | ||
esp_err_t (*trim)(spi_nand_flash_device_t *handle, sector_t sector_id); | ||
esp_err_t (*sync)(spi_nand_flash_device_t *handle); | ||
esp_err_t (*copy_sector)(spi_nand_flash_device_t *handle, sector_t src_sec, sector_t dst_sec); | ||
esp_err_t (*get_capacity)(spi_nand_flash_device_t *handle, sector_t *number_of_sectors); | ||
} spi_nand_ops; | ||
|
||
struct spi_nand_flash_device_t { | ||
uint8_t dev_index; | ||
spi_nand_flash_config_t config; | ||
spi_nand_chip_t chip; | ||
const spi_nand_ops *ops; | ||
uint8_t *work_buffer; | ||
uint8_t *read_buffer; | ||
SemaphoreHandle_t mutex; | ||
}; | ||
|
||
esp_err_t register_nand_dev(spi_nand_flash_device_t *handle); | ||
esp_err_t unregister_nand_dev(spi_nand_flash_device_t *handle); | ||
|
||
#ifdef __cplusplus | ||
} | ||
#endif |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,30 @@ | ||
/* | ||
* SPDX-FileCopyrightText: 2022 mikkeldamsgaard project | ||
* | ||
* SPDX-License-Identifier: Apache-2.0 | ||
* | ||
* SPDX-FileContributor: 2015-2023 Espressif Systems (Shanghai) CO LTD | ||
*/ | ||
|
||
#pragma once | ||
|
||
#include <stdint.h> | ||
#include "esp_err.h" | ||
#include "nand.h" | ||
|
||
#ifdef __cplusplus | ||
extern "C" { | ||
#endif | ||
|
||
esp_err_t nand_is_bad(spi_nand_flash_device_t *handle, block_t b, bool *is_bad_status); | ||
esp_err_t nand_mark_bad(spi_nand_flash_device_t *handle, block_t b); | ||
esp_err_t nand_erase_chip(spi_nand_flash_device_t *handle); | ||
esp_err_t nand_erase_block(spi_nand_flash_device_t *handle, block_t b); | ||
esp_err_t nand_prog(spi_nand_flash_device_t *handle, page_t p, const uint8_t *data); | ||
esp_err_t nand_is_free(spi_nand_flash_device_t *handle, page_t p, bool *is_free_status); | ||
esp_err_t nand_read(spi_nand_flash_device_t *handle, page_t p, size_t offset, size_t length, uint8_t *data); | ||
esp_err_t nand_copy(spi_nand_flash_device_t *handle, page_t src, page_t dst); | ||
|
||
#ifdef __cplusplus | ||
} | ||
#endif |
File renamed without changes.
Large diffs are not rendered by default.
Oops, something went wrong.
Large diffs are not rendered by default.
Oops, something went wrong.
This file was deleted.
Oops, something went wrong.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,56 @@ | ||
/* | ||
* SPDX-FileCopyrightText: 2015-2024 Espressif Systems (Shanghai) CO LTD | ||
* | ||
* SPDX-License-Identifier: Apache-2.0 | ||
*/ | ||
|
||
#include <string.h> | ||
#include "esp_check.h" | ||
#include "nand.h" | ||
#include "spi_nand_oper.h" | ||
#include "nand_flash_devices.h" | ||
|
||
static const char *TAG = "nand_alliance"; | ||
|
||
esp_err_t spi_nand_alliance_init(spi_nand_flash_device_t *dev) | ||
{ | ||
uint8_t device_id; | ||
spi_nand_transaction_t t = { | ||
.command = CMD_READ_ID, | ||
.address = 1, | ||
.address_bytes = 1, | ||
.dummy_bits = 8, | ||
.miso_len = 1, | ||
.miso_data = &device_id, | ||
.flags = SPI_TRANS_USE_RXDATA, | ||
}; | ||
spi_nand_execute_transaction(dev->config.device_handle, &t); | ||
dev->chip.erase_block_delay_us = 3000; | ||
dev->chip.program_page_delay_us = 630; | ||
ESP_LOGD(TAG, "%s: device_id: %x\n", __func__, device_id); | ||
switch (device_id) { | ||
case ALLIANCE_DI_25: //AS5F31G04SND-08LIN | ||
dev->chip.num_blocks = 1024; | ||
dev->chip.read_page_delay_us = 60; | ||
break; | ||
case ALLIANCE_DI_2E: //AS5F32G04SND-08LIN | ||
case ALLIANCE_DI_8E: //AS5F12G04SND-10LIN | ||
dev->chip.num_blocks = 2048; | ||
dev->chip.read_page_delay_us = 60; | ||
break; | ||
case ALLIANCE_DI_2F: //AS5F34G04SND-08LIN | ||
case ALLIANCE_DI_8F: //AS5F14G04SND-10LIN | ||
dev->chip.num_blocks = 4096; | ||
dev->chip.read_page_delay_us = 60; | ||
break; | ||
case ALLIANCE_DI_2D: //AS5F38G04SND-08LIN | ||
case ALLIANCE_DI_8D: //AS5F18G04SND-10LIN | ||
dev->chip.log2_page_size = 12; // 4k pages | ||
dev->chip.num_blocks = 4096; | ||
dev->chip.read_page_delay_us = 130; // somewhat slower reads | ||
break; | ||
default: | ||
return ESP_ERR_INVALID_RESPONSE; | ||
} | ||
return ESP_OK; | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,53 @@ | ||
/* | ||
* SPDX-FileCopyrightText: 2015-2024 Espressif Systems (Shanghai) CO LTD | ||
* | ||
* SPDX-License-Identifier: Apache-2.0 | ||
*/ | ||
|
||
#include <string.h> | ||
#include "esp_check.h" | ||
#include "nand.h" | ||
#include "spi_nand_oper.h" | ||
#include "nand_flash_devices.h" | ||
|
||
static const char *TAG = "nand_gigadevice"; | ||
|
||
esp_err_t spi_nand_gigadevice_init(spi_nand_flash_device_t *dev) | ||
{ | ||
uint8_t device_id; | ||
spi_nand_transaction_t t = { | ||
.command = CMD_READ_ID, | ||
.dummy_bits = 16, | ||
.miso_len = 1, | ||
.miso_data = &device_id, | ||
.flags = SPI_TRANS_USE_RXDATA, | ||
}; | ||
spi_nand_execute_transaction(dev->config.device_handle, &t); | ||
dev->chip.read_page_delay_us = 25; | ||
dev->chip.erase_block_delay_us = 3200; | ||
dev->chip.program_page_delay_us = 380; | ||
ESP_LOGD(TAG, "%s: device_id: %x\n", __func__, device_id); | ||
switch (device_id) { | ||
case GIGADEVICE_DI_51: | ||
case GIGADEVICE_DI_41: | ||
case GIGADEVICE_DI_31: | ||
case GIGADEVICE_DI_21: | ||
dev->chip.num_blocks = 1024; | ||
break; | ||
case GIGADEVICE_DI_52: | ||
case GIGADEVICE_DI_42: | ||
case GIGADEVICE_DI_32: | ||
case GIGADEVICE_DI_22: | ||
dev->chip.num_blocks = 2048; | ||
break; | ||
case GIGADEVICE_DI_55: | ||
case GIGADEVICE_DI_45: | ||
case GIGADEVICE_DI_35: | ||
case GIGADEVICE_DI_25: | ||
dev->chip.num_blocks = 4096; | ||
break; | ||
default: | ||
return ESP_ERR_INVALID_RESPONSE; | ||
} | ||
return ESP_OK; | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,49 @@ | ||
/* | ||
* SPDX-FileCopyrightText: 2015-2024 Espressif Systems (Shanghai) CO LTD | ||
* | ||
* SPDX-License-Identifier: Apache-2.0 | ||
*/ | ||
|
||
#include <string.h> | ||
#include "esp_check.h" | ||
#include "nand.h" | ||
#include "spi_nand_oper.h" | ||
#include "nand_flash_devices.h" | ||
|
||
static const char *TAG = "nand_micron"; | ||
|
||
esp_err_t spi_nand_micron_init(spi_nand_flash_device_t *dev) | ||
{ | ||
uint8_t device_id; | ||
spi_nand_transaction_t t = { | ||
.command = CMD_READ_ID, | ||
.dummy_bits = 16, | ||
.miso_len = 1, | ||
.miso_data = &device_id, | ||
.flags = SPI_TRANS_USE_RXDATA, | ||
}; | ||
spi_nand_execute_transaction(dev->config.device_handle, &t); | ||
dev->chip.ecc_data.ecc_status_reg_len_in_bits = 3; | ||
dev->chip.erase_block_delay_us = 2000; | ||
ESP_LOGD(TAG, "%s: device_id: %x\n", __func__, device_id); | ||
switch (device_id) { | ||
case MICRON_DI_34: | ||
dev->chip.read_page_delay_us = 115; | ||
dev->chip.program_page_delay_us = 240; | ||
dev->chip.num_blocks = 2048; | ||
dev->chip.log2_ppb = 6; // 64 pages per block | ||
dev->chip.log2_page_size = 12; // 4096 bytes per page | ||
break; | ||
case MICRON_DI_14: | ||
case MICRON_DI_15: | ||
dev->chip.read_page_delay_us = 46; | ||
dev->chip.program_page_delay_us = 220; | ||
dev->chip.num_blocks = 1024; | ||
dev->chip.log2_ppb = 6; // 64 pages per block | ||
dev->chip.log2_page_size = 11; // 2048 bytes per page | ||
break; | ||
default: | ||
return ESP_ERR_INVALID_RESPONSE; | ||
} | ||
return ESP_OK; | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,45 @@ | ||
/* | ||
* SPDX-FileCopyrightText: 2015-2024 Espressif Systems (Shanghai) CO LTD | ||
* | ||
* SPDX-License-Identifier: Apache-2.0 | ||
*/ | ||
|
||
#include <string.h> | ||
#include "esp_check.h" | ||
#include "nand.h" | ||
#include "spi_nand_oper.h" | ||
#include "nand_flash_devices.h" | ||
|
||
static const char *TAG = "nand_winbond"; | ||
|
||
esp_err_t spi_nand_winbond_init(spi_nand_flash_device_t *dev) | ||
{ | ||
uint8_t device_id_buf[2]; | ||
spi_nand_transaction_t t = { | ||
.command = CMD_READ_ID, | ||
.dummy_bits = 16, | ||
.miso_len = 2, | ||
.miso_data = device_id_buf, | ||
.flags = SPI_TRANS_USE_RXDATA, | ||
}; | ||
spi_nand_execute_transaction(dev->config.device_handle, &t); | ||
uint16_t device_id = (device_id_buf[0] << 8) + device_id_buf[1]; | ||
dev->chip.read_page_delay_us = 10; | ||
dev->chip.erase_block_delay_us = 2500; | ||
dev->chip.program_page_delay_us = 320; | ||
ESP_LOGD(TAG, "%s: device_id: %x\n", __func__, device_id); | ||
switch (device_id) { | ||
case WINBOND_DI_AA20: | ||
case WINBOND_DI_BA20: | ||
dev->chip.num_blocks = 512; | ||
break; | ||
case WINBOND_DI_AA21: | ||
case WINBOND_DI_BA21: | ||
case WINBOND_DI_BC21: | ||
dev->chip.num_blocks = 1024; | ||
break; | ||
default: | ||
return ESP_ERR_INVALID_RESPONSE; | ||
} | ||
return ESP_OK; | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,353 @@ | ||
/* | ||
* SPDX-FileCopyrightText: 2022 mikkeldamsgaard project | ||
* | ||
* SPDX-License-Identifier: Apache-2.0 | ||
* | ||
* SPDX-FileContributor: 2015-2024 Espressif Systems (Shanghai) CO LTD | ||
*/ | ||
|
||
#include <string.h> | ||
#include "esp_check.h" | ||
#include "esp_err.h" | ||
#include "spi_nand_oper.h" | ||
#include "spi_nand_flash.h" | ||
#include "nand.h" | ||
|
||
#define ROM_WAIT_THRESHOLD_US 1000 | ||
|
||
static const char *TAG = "spi_nand"; | ||
|
||
#if CONFIG_NAND_FLASH_VERIFY_WRITE | ||
static esp_err_t s_verify_write(spi_nand_flash_device_t *handle, const uint8_t *expected_buffer, uint16_t offset, uint16_t length) | ||
{ | ||
uint8_t *temp_buf = NULL; | ||
temp_buf = heap_caps_malloc(length, MALLOC_CAP_DMA | MALLOC_CAP_8BIT); | ||
ESP_RETURN_ON_FALSE(temp_buf != NULL, ESP_ERR_NO_MEM, TAG, "nomem"); | ||
if (spi_nand_read(handle->config.device_handle, temp_buf, offset, length)) { | ||
ESP_LOGE(TAG, "%s: Failed to read nand flash to verify previous write", __func__); | ||
free(temp_buf); | ||
return ESP_FAIL; | ||
} | ||
|
||
if (memcmp(temp_buf, expected_buffer, length)) { | ||
ESP_LOGE(TAG, "%s: Data mismatch detected. The previously written buffer does not match the read buffer.", __func__); | ||
free(temp_buf); | ||
return ESP_FAIL; | ||
} | ||
free(temp_buf); | ||
return ESP_OK; | ||
} | ||
#endif //CONFIG_NAND_FLASH_VERIFY_WRITE | ||
|
||
static esp_err_t wait_for_ready(spi_device_handle_t device, uint32_t expected_operation_time_us, uint8_t *status_out) | ||
{ | ||
if (expected_operation_time_us < ROM_WAIT_THRESHOLD_US) { | ||
esp_rom_delay_us(expected_operation_time_us); | ||
} | ||
|
||
while (true) { | ||
uint8_t status; | ||
ESP_RETURN_ON_ERROR(spi_nand_read_register(device, REG_STATUS, &status), TAG, ""); | ||
|
||
if ((status & STAT_BUSY) == 0) { | ||
if (status_out) { | ||
*status_out = status; | ||
} | ||
break; | ||
} | ||
|
||
if (expected_operation_time_us >= ROM_WAIT_THRESHOLD_US) { | ||
vTaskDelay(1); | ||
} | ||
} | ||
|
||
return ESP_OK; | ||
} | ||
|
||
static esp_err_t read_page_and_wait(spi_nand_flash_device_t *dev, uint32_t page, uint8_t *status_out) | ||
{ | ||
ESP_RETURN_ON_ERROR(spi_nand_read_page(dev->config.device_handle, page), TAG, ""); | ||
|
||
return wait_for_ready(dev->config.device_handle, dev->chip.read_page_delay_us, status_out); | ||
} | ||
|
||
static esp_err_t program_execute_and_wait(spi_nand_flash_device_t *dev, uint32_t page, uint8_t *status_out) | ||
{ | ||
ESP_RETURN_ON_ERROR(spi_nand_program_execute(dev->config.device_handle, page), TAG, ""); | ||
|
||
return wait_for_ready(dev->config.device_handle, dev->chip.program_page_delay_us, status_out); | ||
} | ||
|
||
esp_err_t nand_is_bad(spi_nand_flash_device_t *handle, block_t block, bool *is_bad_status) | ||
{ | ||
uint32_t first_block_page = block * (1 << handle->chip.log2_ppb); | ||
uint16_t bad_block_indicator; | ||
esp_err_t ret = ESP_OK; | ||
|
||
ESP_GOTO_ON_ERROR(read_page_and_wait(handle, first_block_page, NULL), fail, TAG, ""); | ||
|
||
// Read the first 2 bytes on the OOB of the first page in the block. This should be 0xFFFF for a good block | ||
ESP_GOTO_ON_ERROR(spi_nand_read(handle->config.device_handle, (uint8_t *) &bad_block_indicator, handle->chip.page_size, 2), | ||
fail, TAG, ""); | ||
|
||
ESP_LOGD(TAG, "is_bad, block=%"PRIu32", page=%"PRIu32",indicator = %04x", block, first_block_page, bad_block_indicator); | ||
if (bad_block_indicator == 0xFFFF) { | ||
*is_bad_status = false; | ||
} else { | ||
*is_bad_status = true; | ||
} | ||
return ret; | ||
|
||
fail: | ||
ESP_LOGE(TAG, "Error in nand_is_bad %d", ret); | ||
return ret; | ||
} | ||
|
||
esp_err_t nand_mark_bad(spi_nand_flash_device_t *handle, block_t block) | ||
{ | ||
esp_err_t ret = ESP_OK; | ||
|
||
uint32_t first_block_page = block * (1 << handle->chip.log2_ppb); | ||
uint16_t bad_block_indicator = 0; | ||
uint8_t status; | ||
ESP_LOGD(TAG, "mark_bad, block=%"PRIu32", page=%"PRIu32",indicator = %04x", block, first_block_page, bad_block_indicator); | ||
|
||
ESP_GOTO_ON_ERROR(read_page_and_wait(handle, first_block_page, NULL), fail, TAG, ""); | ||
ESP_GOTO_ON_ERROR(spi_nand_write_enable(handle->config.device_handle), fail, TAG, ""); | ||
ESP_GOTO_ON_ERROR(spi_nand_erase_block(handle->config.device_handle, first_block_page), | ||
fail, TAG, ""); | ||
ESP_GOTO_ON_ERROR(wait_for_ready(handle->config.device_handle, handle->chip.erase_block_delay_us, &status), | ||
fail, TAG, ""); | ||
if ((status & STAT_ERASE_FAILED) != 0) { | ||
ret = ESP_ERR_NOT_FINISHED; | ||
goto fail; | ||
} | ||
|
||
ESP_GOTO_ON_ERROR(spi_nand_write_enable(handle->config.device_handle), fail, TAG, ""); | ||
ESP_GOTO_ON_ERROR(spi_nand_program_load(handle->config.device_handle, (const uint8_t *) &bad_block_indicator, | ||
handle->chip.page_size, 2), | ||
fail, TAG, ""); | ||
ESP_GOTO_ON_ERROR(program_execute_and_wait(handle, first_block_page, NULL), fail, TAG, ""); | ||
|
||
#if CONFIG_NAND_FLASH_VERIFY_WRITE | ||
ret = s_verify_write(handle, (uint8_t *)&bad_block_indicator, handle->chip.page_size, 2); | ||
if (ret != ESP_OK) { | ||
ESP_LOGE(TAG, "%s: mark_bad write verification failed for block=%"PRIu32" and page=%"PRIu32"", __func__, block, first_block_page); | ||
} | ||
#endif //CONFIG_NAND_FLASH_VERIFY_WRITE | ||
return ret; | ||
fail: | ||
ESP_LOGE(TAG, "Error in nand_mark_bad %d", ret); | ||
return ret; | ||
} | ||
|
||
esp_err_t nand_erase_chip(spi_nand_flash_device_t *handle) | ||
{ | ||
esp_err_t ret = ESP_OK; | ||
uint8_t status; | ||
|
||
for (int i = 0; i < handle->chip.num_blocks; i++) { | ||
ESP_GOTO_ON_ERROR(spi_nand_write_enable(handle->config.device_handle), end, TAG, ""); | ||
ESP_GOTO_ON_ERROR(spi_nand_erase_block(handle->config.device_handle, i * (1 << handle->chip.log2_ppb)), | ||
end, TAG, ""); | ||
ESP_GOTO_ON_ERROR(wait_for_ready(handle->config.device_handle, handle->chip.erase_block_delay_us, &status), | ||
end, TAG, ""); | ||
if ((status & STAT_ERASE_FAILED) != 0) { | ||
ret = ESP_ERR_NOT_FINISHED; | ||
} | ||
} | ||
return ret; | ||
|
||
end: | ||
ESP_LOGE(TAG, "Error in nand_erase_chip %d", ret); | ||
return ret; | ||
} | ||
|
||
int nand_erase_block(spi_nand_flash_device_t *handle, block_t block) | ||
{ | ||
ESP_LOGD(TAG, "erase_block, block=%"PRIu32",", block); | ||
esp_err_t ret = ESP_OK; | ||
uint8_t status; | ||
|
||
uint32_t first_block_page = block * (1 << handle->chip.log2_ppb); | ||
|
||
ESP_GOTO_ON_ERROR(spi_nand_write_enable(handle->config.device_handle), fail, TAG, ""); | ||
ESP_GOTO_ON_ERROR(spi_nand_erase_block(handle->config.device_handle, first_block_page), | ||
fail, TAG, ""); | ||
ESP_GOTO_ON_ERROR(wait_for_ready(handle->config.device_handle, | ||
handle->chip.erase_block_delay_us, &status), | ||
fail, TAG, ""); | ||
|
||
if ((status & STAT_ERASE_FAILED) != 0) { | ||
ret = ESP_ERR_NOT_FINISHED; | ||
} | ||
return ret; | ||
|
||
fail: | ||
ESP_LOGE(TAG, "Error in nand_erase %d", ret); | ||
return ret; | ||
} | ||
|
||
esp_err_t nand_prog(spi_nand_flash_device_t *handle, page_t page, const uint8_t *data) | ||
{ | ||
ESP_LOGV(TAG, "prog, page=%"PRIu32",", page); | ||
esp_err_t ret = ESP_OK; | ||
uint16_t used_marker = 0; | ||
uint8_t status; | ||
|
||
ESP_GOTO_ON_ERROR(read_page_and_wait(handle, page, NULL), fail, TAG, ""); | ||
ESP_GOTO_ON_ERROR(spi_nand_write_enable(handle->config.device_handle), fail, TAG, ""); | ||
ESP_GOTO_ON_ERROR(spi_nand_program_load(handle->config.device_handle, data, 0, handle->chip.page_size), | ||
fail, TAG, ""); | ||
ESP_GOTO_ON_ERROR(spi_nand_program_load(handle->config.device_handle, (uint8_t *)&used_marker, | ||
handle->chip.page_size + 2, 2), | ||
fail, TAG, ""); | ||
ESP_GOTO_ON_ERROR(program_execute_and_wait(handle, page, &status), fail, TAG, ""); | ||
|
||
if ((status & STAT_PROGRAM_FAILED) != 0) { | ||
ESP_LOGD(TAG, "prog failed, page=%"PRIu32",", page); | ||
return ESP_ERR_NOT_FINISHED; | ||
} | ||
|
||
#if CONFIG_NAND_FLASH_VERIFY_WRITE | ||
ret = s_verify_write(handle, data, 0, handle->chip.page_size); | ||
if (ret != ESP_OK) { | ||
ESP_LOGE(TAG, "%s: prog page=%"PRIu32" write verification failed", __func__, page); | ||
} | ||
ret = s_verify_write(handle, (uint8_t *)&used_marker, handle->chip.page_size + 2, 2); | ||
if (ret != ESP_OK) { | ||
ESP_LOGE(TAG, "%s: prog page=%"PRIu32" used marker write verification failed", __func__, page); | ||
} | ||
#endif //CONFIG_NAND_FLASH_VERIFY_WRITE | ||
|
||
return ret; | ||
fail: | ||
ESP_LOGE(TAG, "Error in nand_prog %d", ret); | ||
return ret; | ||
} | ||
|
||
esp_err_t nand_is_free(spi_nand_flash_device_t *handle, page_t page, bool *is_free_status) | ||
{ | ||
esp_err_t ret = ESP_OK; | ||
uint16_t used_marker; | ||
|
||
ESP_GOTO_ON_ERROR(read_page_and_wait(handle, page, NULL), fail, TAG, ""); | ||
ESP_GOTO_ON_ERROR(spi_nand_read(handle->config.device_handle, (uint8_t *)&used_marker, | ||
handle->chip.page_size + 2, 2), | ||
fail, TAG, ""); | ||
|
||
ESP_LOGD(TAG, "is free, page=%"PRIu32", used_marker=%04x,", page, used_marker); | ||
if (used_marker == 0xFFFF) { | ||
*is_free_status = true; | ||
} else { | ||
*is_free_status = false; | ||
} | ||
return ret; | ||
fail: | ||
ESP_LOGE(TAG, "Error in nand_is_free %d", ret); | ||
return ret; | ||
} | ||
|
||
#define PACK_2BITS_STATUS(status, bit1, bit0) ((((status) & (bit1)) << 1) | ((status) & (bit0))) | ||
#define PACK_3BITS_STATUS(status, bit2, bit1, bit0) ((((status) & (bit2)) << 2) | (((status) & (bit1)) << 1) | ((status) & (bit0))) | ||
|
||
static bool is_ecc_error(spi_nand_flash_device_t *dev, uint8_t status) | ||
{ | ||
bool is_ecc_err = false; | ||
ecc_status_t bits_corrected_status = STAT_ECC_OK; | ||
if (dev->chip.ecc_data.ecc_status_reg_len_in_bits == 2) { | ||
bits_corrected_status = PACK_2BITS_STATUS(status, STAT_ECC1, STAT_ECC0); | ||
} else if (dev->chip.ecc_data.ecc_status_reg_len_in_bits == 3) { | ||
bits_corrected_status = PACK_3BITS_STATUS(status, STAT_ECC2, STAT_ECC1, STAT_ECC0); | ||
} else { | ||
bits_corrected_status = STAT_ECC_MAX; | ||
} | ||
dev->chip.ecc_data.ecc_corrected_bits_status = bits_corrected_status; | ||
if (bits_corrected_status) { | ||
if (bits_corrected_status == STAT_ECC_MAX) { | ||
ESP_LOGE(TAG, "%s: Error while initializing value of ecc_status_reg_len_in_bits", __func__); | ||
is_ecc_err = true; | ||
} else if (bits_corrected_status == STAT_ECC_NOT_CORRECTED) { | ||
is_ecc_err = true; | ||
} | ||
} | ||
return is_ecc_err; | ||
} | ||
|
||
esp_err_t nand_read(spi_nand_flash_device_t *handle, page_t page, size_t offset, size_t length, uint8_t *data) | ||
{ | ||
ESP_LOGV(TAG, "read, page=%"PRIu32", offset=%d, length=%d", page, offset, length); | ||
assert(page < handle->chip.num_blocks * (1 << handle->chip.log2_ppb)); | ||
esp_err_t ret = ESP_OK; | ||
uint8_t status; | ||
|
||
ESP_GOTO_ON_ERROR(read_page_and_wait(handle, page, &status), fail, TAG, ""); | ||
|
||
if (is_ecc_error(handle, status)) { | ||
ESP_LOGD(TAG, "read ecc error, page=%"PRIu32"", page); | ||
return ESP_FAIL; | ||
} | ||
|
||
ESP_GOTO_ON_ERROR(spi_nand_read(handle->config.device_handle, data, offset, length), fail, TAG, ""); | ||
|
||
return ret; | ||
fail: | ||
ESP_LOGE(TAG, "Error in nand_read %d", ret); | ||
return ret; | ||
} | ||
|
||
esp_err_t nand_copy(spi_nand_flash_device_t *handle, page_t src, page_t dst) | ||
{ | ||
ESP_LOGD(TAG, "copy, src=%"PRIu32", dst=%"PRIu32"", src, dst); | ||
esp_err_t ret = ESP_OK; | ||
#if CONFIG_NAND_FLASH_VERIFY_WRITE | ||
uint8_t *temp_buf = NULL; | ||
#endif //CONFIG_NAND_FLASH_VERIFY_WRITE | ||
|
||
uint8_t status; | ||
ESP_GOTO_ON_ERROR(read_page_and_wait(handle, src, &status), fail, TAG, ""); | ||
|
||
if (is_ecc_error(handle, status)) { | ||
ESP_LOGD(TAG, "copy, ecc error"); | ||
return ESP_FAIL; | ||
} | ||
|
||
ESP_GOTO_ON_ERROR(spi_nand_write_enable(handle->config.device_handle), fail, TAG, ""); | ||
ESP_GOTO_ON_ERROR(program_execute_and_wait(handle, dst, &status), fail, TAG, ""); | ||
|
||
if ((status & STAT_PROGRAM_FAILED) != 0) { | ||
ESP_LOGD(TAG, "copy, prog failed"); | ||
return ESP_ERR_NOT_FINISHED; | ||
} | ||
|
||
#if CONFIG_NAND_FLASH_VERIFY_WRITE | ||
// First read src page data from cache to temp_buf | ||
temp_buf = heap_caps_malloc(handle->chip.page_size, MALLOC_CAP_DMA | MALLOC_CAP_8BIT); | ||
ESP_RETURN_ON_FALSE(temp_buf != NULL, ESP_ERR_NO_MEM, TAG, "nomem"); | ||
if (spi_nand_read(handle->config.device_handle, temp_buf, 0, handle->chip.page_size)) { | ||
ESP_LOGE(TAG, "%s: Failed to read src_page=%"PRIu32"", __func__, src); | ||
goto fail; | ||
} | ||
// Then read dst page data from nand memory array and load it in cache | ||
ESP_GOTO_ON_ERROR(read_page_and_wait(handle, dst, &status), fail, TAG, ""); | ||
if (is_ecc_error(handle, status)) { | ||
ESP_LOGE(TAG, "%s: dst_page=%"PRIu32" read, ecc error", __func__, dst); | ||
goto fail; | ||
} | ||
// Check if the data in the src page matches the dst page | ||
ret = s_verify_write(handle, temp_buf, 0, handle->chip.page_size); | ||
if (ret != ESP_OK) { | ||
ESP_LOGE(TAG, "%s: dst_page=%"PRIu32" write verification failed", __func__, dst); | ||
} | ||
|
||
free(temp_buf); | ||
#endif //CONFIG_NAND_FLASH_VERIFY_WRITE | ||
return ret; | ||
|
||
fail: | ||
#if CONFIG_NAND_FLASH_VERIFY_WRITE | ||
free(temp_buf); | ||
#endif //CONFIG_NAND_FLASH_VERIFY_WRITE | ||
ESP_LOGE(TAG, "Error in nand_copy %d", ret); | ||
return ret; | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters