diff --git a/spi_nand_flash/CMakeLists.txt b/spi_nand_flash/CMakeLists.txt index ec6be69fc7..105fcb6756 100644 --- a/spi_nand_flash/CMakeLists.txt +++ b/spi_nand_flash/CMakeLists.txt @@ -1,5 +1,10 @@ set(srcs "src/nand.c" + "src/nand_winbond.c" + "src/nand_gigadevice.c" + "src/nand_alliance.c" + "src/nand_micron.c" "src/spi_nand_oper.c" + "src/spi_nand_api.c" "src/dhara_glue.c" "vfs/vfs_fat_spinandflash.c" "diskio/diskio_nand.c") @@ -16,5 +21,6 @@ set(priv_reqs vfs) idf_component_register(SRCS ${srcs} INCLUDE_DIRS include vfs diskio + PRIV_INCLUDE_DIRS "priv_include" REQUIRES ${reqs} PRIV_REQUIRES ${priv_reqs}) diff --git a/spi_nand_flash/idf_component.yml b/spi_nand_flash/idf_component.yml index c730e064c8..5601020289 100644 --- a/spi_nand_flash/idf_component.yml +++ b/spi_nand_flash/idf_component.yml @@ -1,4 +1,4 @@ -version: "0.6.0" +version: "0.7.0" description: Driver for accessing SPI NAND Flash url: https://github.com/espressif/idf-extra-components/tree/master/spi_nand_flash issues: https://github.com/espressif/idf-extra-components/issues diff --git a/spi_nand_flash/include/spi_nand_flash.h b/spi_nand_flash/include/spi_nand_flash.h index bd283a64a0..7a2b2c9b29 100644 --- a/spi_nand_flash/include/spi_nand_flash.h +++ b/spi_nand_flash/include/spi_nand_flash.h @@ -3,7 +3,7 @@ * * SPDX-License-Identifier: Apache-2.0 * - * SPDX-FileContributor: 2015-2023 Espressif Systems (Shanghai) CO LTD + * SPDX-FileContributor: 2015-2024 Espressif Systems (Shanghai) CO LTD */ #pragma once @@ -12,12 +12,17 @@ #include "esp_err.h" #include "driver/spi_common.h" #include "driver/spi_master.h" -#include "dhara/map.h" #ifdef __cplusplus extern "C" { #endif +#define NAND_MAX_DEVICES 1 + +typedef uint32_t page_t; +typedef uint32_t block_t; +typedef uint32_t sector_t; + /** @brief Structure to describe how to configure the nand access layer. @note The spi_device_handle_t must be initialized with the flag SPI_DEVICE_HALFDUPLEX */ @@ -47,7 +52,7 @@ esp_err_t spi_nand_flash_init_device(spi_nand_flash_config_t *config, spi_nand_f * @param sector_id The id of the sector to read. * @return ESP_OK on success, or a flash error code if the read failed. */ -esp_err_t spi_nand_flash_read_sector(spi_nand_flash_device_t *handle, uint8_t *buffer, dhara_sector_t sector_id); +esp_err_t spi_nand_flash_read_sector(spi_nand_flash_device_t *handle, uint8_t *buffer, sector_t sector_id); /** @brief Copy a sector to another sector from the nand flash. * @@ -56,7 +61,7 @@ esp_err_t spi_nand_flash_read_sector(spi_nand_flash_device_t *handle, uint8_t *b * @param dst_sec The destination sector id to which data should be copied. * @return ESP_OK on success, or a flash error code if the copy failed. */ -esp_err_t spi_nand_flash_copy_sector(spi_nand_flash_device_t *handle, dhara_sector_t src_sec, dhara_sector_t dst_sec); +esp_err_t spi_nand_flash_copy_sector(spi_nand_flash_device_t *handle, sector_t src_sec, sector_t dst_sec); /** @brief Write a sector to the nand flash. * @@ -65,7 +70,7 @@ esp_err_t spi_nand_flash_copy_sector(spi_nand_flash_device_t *handle, dhara_sect * @param sector_id The id of the sector to write. * @return ESP_OK on success, or a flash error code if the write failed. */ -esp_err_t spi_nand_flash_write_sector(spi_nand_flash_device_t *handle, const uint8_t *buffer, dhara_sector_t sector_id); +esp_err_t spi_nand_flash_write_sector(spi_nand_flash_device_t *handle, const uint8_t *buffer, sector_t sector_id); /** @brief Trim sector from the nand flash. * @@ -77,7 +82,7 @@ esp_err_t spi_nand_flash_write_sector(spi_nand_flash_device_t *handle, const uin * @param sector_id The id of the sector to be trimmed. * @return ESP_OK on success, or a flash error code if the trim failed. */ -esp_err_t spi_nand_flash_trim(spi_nand_flash_device_t *handle, dhara_sector_t sector_id); +esp_err_t spi_nand_flash_trim(spi_nand_flash_device_t *handle, sector_t sector_id); /** @brief Synchronizes any cache to the device. * @@ -94,7 +99,7 @@ esp_err_t spi_nand_flash_sync(spi_nand_flash_device_t *handle); * @param[out] number_of_sectors A pointer of where to put the return value * @return ESP_OK on success, or a flash error code if the operation failed. */ -esp_err_t spi_nand_flash_get_capacity(spi_nand_flash_device_t *handle, dhara_sector_t *number_of_sectors); +esp_err_t spi_nand_flash_get_capacity(spi_nand_flash_device_t *handle, sector_t *number_of_sectors); /** @brief Retrieve the size of each sector. * @@ -111,6 +116,23 @@ esp_err_t spi_nand_flash_get_sector_size(spi_nand_flash_device_t *handle, uint32 */ esp_err_t spi_nand_erase_chip(spi_nand_flash_device_t *handle); +/** @brief Retrieve the number of blocks available. + * + * @param handle The handle to the SPI nand flash chip. + * @param[out] number_of_blocks A pointer of where to put the return value + * @return ESP_OK on success, or a flash error code if the operation failed. + */ +esp_err_t spi_nand_flash_get_block_num(spi_nand_flash_device_t *handle, block_t *number_of_blocks); + +/** @brief Check if the given block is bad block. + * + * @param handle The handle to the SPI nand flash chip. + * @param block The block number to verify if it is bad block + * @param[out] is_bad A pointer of where to put the return value + * @return ESP_OK on success, or a flash error code if the operation failed. + */ +esp_err_t spi_nand_flash_is_bad_block(spi_nand_flash_device_t *handle, block_t block, bool *is_bad); + /** @brief De-initialize the handle, releasing any resources reserved. * * @param handle The handle to the SPI nand flash chip. diff --git a/spi_nand_flash/priv_include/nand.h b/spi_nand_flash/priv_include/nand.h new file mode 100644 index 0000000000..54da754854 --- /dev/null +++ b/spi_nand_flash/priv_include/nand.h @@ -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 +#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< +#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 diff --git a/spi_nand_flash/src/spi_nand_oper.h b/spi_nand_flash/priv_include/spi_nand_oper.h similarity index 100% rename from spi_nand_flash/src/spi_nand_oper.h rename to spi_nand_flash/priv_include/spi_nand_oper.h diff --git a/spi_nand_flash/src/dhara_glue.c b/spi_nand_flash/src/dhara_glue.c index 1d815e7420..c83e62a4ad 100644 --- a/spi_nand_flash/src/dhara_glue.c +++ b/spi_nand_flash/src/dhara_glue.c @@ -3,329 +3,254 @@ * * SPDX-License-Identifier: Apache-2.0 * - * SPDX-FileContributor: 2015-2023 Espressif Systems (Shanghai) CO LTD + * SPDX-FileContributor: 2015-2024 Espressif Systems (Shanghai) CO LTD */ #include +#include #include "dhara/nand.h" +#include "dhara/map.h" +#include "dhara/error.h" #include "esp_check.h" #include "esp_err.h" -#include "freertos/FreeRTOS.h" -#include "freertos/task.h" #include "spi_nand_oper.h" -#include "spi_nand_flash.h" +#include "spi_nand_api.h" #include "nand.h" -#define ROM_WAIT_THRESHOLD_US 1000 +static struct dhara_nand dhara_nand[NAND_MAX_DEVICES]; +static struct dhara_map dhara_map[NAND_MAX_DEVICES]; +static spi_nand_flash_device_t *s_handle_t[NAND_MAX_DEVICES] = {NULL}; +static uint8_t s_handle_index; +static _lock_t s_handle_index_lock; -static const char *TAG = "dhara_glue"; +static esp_err_t dhara_init(spi_nand_flash_device_t *handle) +{ + dhara_nand[handle->dev_index].log2_page_size = handle->chip.log2_page_size; + dhara_nand[handle->dev_index].log2_ppb = handle->chip.log2_ppb; + dhara_nand[handle->dev_index].num_blocks = handle->chip.num_blocks; + + dhara_map_init(&dhara_map[handle->dev_index], &dhara_nand[handle->dev_index], handle->work_buffer, handle->config.gc_factor); + dhara_error_t ignored; + dhara_map_resume(&dhara_map[handle->dev_index], &ignored); + + return ESP_OK; +} -#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) +static esp_err_t dhara_deinit(spi_nand_flash_device_t *handle) { - 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; - } + // clear dhara map + dhara_map_init(&dhara_map[handle->dev_index], &dhara_nand[handle->dev_index], handle->work_buffer, handle->config.gc_factor); + dhara_map_clear(&dhara_map[handle->dev_index]); + return ESP_OK; +} - 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; +static esp_err_t dhara_read(spi_nand_flash_device_t *handle, uint8_t *buffer, dhara_sector_t sector_id) +{ + dhara_error_t err; + _lock_acquire(&s_handle_index_lock); + s_handle_index = handle->dev_index; + if (dhara_map_read(&dhara_map[handle->dev_index], sector_id, handle->read_buffer, &err)) { + _lock_release(&s_handle_index_lock); + return ESP_ERR_FLASH_BASE + err; } - free(temp_buf); + _lock_release(&s_handle_index_lock); + memcpy(buffer, handle->read_buffer, handle->chip.page_size); return ESP_OK; } -#endif //CONFIG_NAND_FLASH_VERIFY_WRITE -esp_err_t wait_for_ready(spi_device_handle_t device, uint32_t expected_operation_time_us, uint8_t *status_out) +static esp_err_t dhara_write(spi_nand_flash_device_t *handle, const uint8_t *buffer, dhara_sector_t sector_id) { - if (expected_operation_time_us < ROM_WAIT_THRESHOLD_US) { - esp_rom_delay_us(expected_operation_time_us); + dhara_error_t err; + _lock_acquire(&s_handle_index_lock); + s_handle_index = handle->dev_index; + if (dhara_map_write(&dhara_map[handle->dev_index], sector_id, buffer, &err)) { + _lock_release(&s_handle_index_lock); + return ESP_ERR_FLASH_BASE + err; } + _lock_release(&s_handle_index_lock); + return ESP_OK; +} - 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); - } +static esp_err_t dhara_copy_sector(spi_nand_flash_device_t *handle, dhara_sector_t src_sec, dhara_sector_t dst_sec) +{ + dhara_error_t err; + _lock_acquire(&s_handle_index_lock); + s_handle_index = handle->dev_index; + if (dhara_map_copy_sector(&dhara_map[handle->dev_index], src_sec, dst_sec, &err)) { + _lock_release(&s_handle_index_lock); + return ESP_ERR_FLASH_BASE + err; } + _lock_release(&s_handle_index_lock); + return ESP_OK; +} +static esp_err_t dhara_trim(spi_nand_flash_device_t *handle, dhara_sector_t sector_id) +{ + dhara_error_t err; + _lock_acquire(&s_handle_index_lock); + s_handle_index = handle->dev_index; + if (dhara_map_trim(&dhara_map[handle->dev_index], sector_id, &err)) { + _lock_release(&s_handle_index_lock); + return ESP_ERR_FLASH_BASE + err; + } + _lock_release(&s_handle_index_lock); return ESP_OK; } -static esp_err_t read_page_and_wait(spi_nand_flash_device_t *dev, uint32_t page, uint8_t *status_out) +static esp_err_t dhara_sync(spi_nand_flash_device_t *handle) { - ESP_RETURN_ON_ERROR(spi_nand_read_page(dev->config.device_handle, page), TAG, ""); + dhara_error_t err; + _lock_acquire(&s_handle_index_lock); + s_handle_index = handle->dev_index; + if (dhara_map_sync(&dhara_map[handle->dev_index], &err)) { + _lock_release(&s_handle_index_lock); + return ESP_ERR_FLASH_BASE + err; + } + _lock_release(&s_handle_index_lock); + return ESP_OK; +} - return wait_for_ready(dev->config.device_handle, dev->read_page_delay_us, status_out); +static esp_err_t dhara_get_capacity(spi_nand_flash_device_t *handle, dhara_sector_t *number_of_sectors) +{ + *number_of_sectors = dhara_map_capacity(&dhara_map[handle->dev_index]); + return ESP_OK; } -static esp_err_t program_execute_and_wait(spi_nand_flash_device_t *dev, uint32_t page, uint8_t *status_out) +const spi_nand_ops dhara_nand_ops = { + .init = &dhara_init, + .deinit = &dhara_deinit, + .read = &dhara_read, + .write = &dhara_write, + .trim = &dhara_trim, + .sync = &dhara_sync, + .copy_sector = &dhara_copy_sector, + .get_capacity = &dhara_get_capacity, +}; + +esp_err_t register_nand_dev(spi_nand_flash_device_t *handle) { - ESP_RETURN_ON_ERROR(spi_nand_program_execute(dev->config.device_handle, page), TAG, ""); + if (handle == NULL) { + return ESP_ERR_INVALID_ARG; + } + uint8_t index; + for (index = 0; index < NAND_MAX_DEVICES; index++) { + if (s_handle_t[index] == NULL) { + handle->dev_index = index; + break; + } + } + if (index >= NAND_MAX_DEVICES) { + return ESP_FAIL; + } - return wait_for_ready(dev->config.device_handle, dev->program_page_delay_us, status_out); + s_handle_t[handle->dev_index] = handle; + handle->ops = &dhara_nand_ops; + return ESP_OK; } -int dhara_nand_is_bad(const struct dhara_nand *n, dhara_block_t b) +esp_err_t unregister_nand_dev(spi_nand_flash_device_t *handle) { - spi_nand_flash_device_t *dev = __containerof(n, spi_nand_flash_device_t, dhara_nand); - - dhara_page_t first_block_page = b * (1 << n->log2_ppb); - uint16_t bad_block_indicator; - esp_err_t ret; + s_handle_t[handle->dev_index] = NULL; + handle->ops = NULL; + return ESP_OK; +} - ESP_GOTO_ON_ERROR(read_page_and_wait(dev, 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(dev->config.device_handle, (uint8_t *) &bad_block_indicator, dev->page_size, 2), - fail, TAG, ""); - ESP_LOGD(TAG, "is_bad, block=%"PRIu32", page=%"PRIu32",indicator = %04x", b, first_block_page, bad_block_indicator); - return bad_block_indicator != 0xFFFF; +// The following APIs are implementations required by the Dhara library. +// Please refer to the header file dhara/nand.h for details. -fail: - ESP_LOGE(TAG, "Error in dhara_nand_is_bad %d", ret); - return 1; +int dhara_nand_is_bad(const struct dhara_nand *n, dhara_block_t b) +{ + spi_nand_flash_device_t *dev_handle = s_handle_t[s_handle_index]; + bool is_bad_status = false; + if (nand_is_bad(dev_handle, b, &is_bad_status)) { + return 1; + } + if (is_bad_status == true) { + return 1; + } + return 0; } void dhara_nand_mark_bad(const struct dhara_nand *n, dhara_block_t b) { - spi_nand_flash_device_t *dev = __containerof(n, spi_nand_flash_device_t, dhara_nand); - esp_err_t ret = ESP_OK; - - dhara_page_t first_block_page = b * (1 << n->log2_ppb); - uint16_t bad_block_indicator = 0; - ESP_LOGD(TAG, "mark_bad, block=%"PRIu32", page=%"PRIu32",indicator = %04x", b, first_block_page, bad_block_indicator); - - ESP_GOTO_ON_ERROR(spi_nand_write_enable(dev->config.device_handle), fail, TAG, ""); - ESP_GOTO_ON_ERROR(spi_nand_erase_block(dev->config.device_handle, first_block_page), - fail, TAG, ""); - - ESP_GOTO_ON_ERROR(spi_nand_write_enable(dev->config.device_handle), fail, TAG, ""); - ESP_GOTO_ON_ERROR(spi_nand_program_load(dev->config.device_handle, (const uint8_t *) &bad_block_indicator, - dev->page_size, 2), - fail, TAG, ""); - ESP_GOTO_ON_ERROR(program_execute_and_wait(dev, first_block_page, NULL), fail, TAG, ""); - -#if CONFIG_NAND_FLASH_VERIFY_WRITE - ret = s_verify_write(dev, (uint8_t *)&bad_block_indicator, dev->page_size, 2); - if (ret != ESP_OK) { - ESP_LOGE(TAG, "%s: mark_bad write verification failed for block=%"PRIu32" and page=%"PRIu32"", __func__, b, first_block_page); + spi_nand_flash_device_t *dev_handle = s_handle_t[s_handle_index]; + esp_err_t ret = nand_mark_bad(dev_handle, b); + if (ret) { + if (ret == ESP_ERR_NOT_FINISHED) { + dhara_set_error(err, DHARA_E_BAD_BLOCK); + } } -#endif //CONFIG_NAND_FLASH_VERIFY_WRITE return; -fail: - ESP_LOGE(TAG, "Error in dhara_nand_mark_bad %d", ret); } int dhara_nand_erase(const struct dhara_nand *n, dhara_block_t b, dhara_error_t *err) { - ESP_LOGD(TAG, "erase_block, block=%"PRIu32",", b); - spi_nand_flash_device_t *dev = __containerof(n, spi_nand_flash_device_t, dhara_nand); - esp_err_t ret; - - dhara_page_t first_block_page = b * (1 << n->log2_ppb); - uint8_t status; - - ESP_GOTO_ON_ERROR(spi_nand_write_enable(dev->config.device_handle), fail, TAG, ""); - ESP_GOTO_ON_ERROR(spi_nand_erase_block(dev->config.device_handle, first_block_page), - fail, TAG, ""); - ESP_GOTO_ON_ERROR(wait_for_ready(dev->config.device_handle, - dev->erase_block_delay_us, &status), - fail, TAG, ""); - - if ((status & STAT_ERASE_FAILED) != 0) { - dhara_set_error(err, DHARA_E_BAD_BLOCK); + spi_nand_flash_device_t *dev_handle = s_handle_t[s_handle_index]; + esp_err_t ret = nand_erase_block(dev_handle, b); + if (ret) { + if (ret == ESP_ERR_NOT_FINISHED) { + dhara_set_error(err, DHARA_E_BAD_BLOCK); + } return -1; } - return 0; - -fail: - ESP_LOGE(TAG, "Error in dhara_nand_erase %d", ret); - return -1; } int dhara_nand_prog(const struct dhara_nand *n, dhara_page_t p, const uint8_t *data, dhara_error_t *err) { - ESP_LOGV(TAG, "prog, page=%"PRIu32",", p); - spi_nand_flash_device_t *dev = __containerof(n, spi_nand_flash_device_t, dhara_nand); - esp_err_t ret = ESP_OK; - uint8_t status; - uint16_t used_marker = 0; - - ESP_GOTO_ON_ERROR(read_page_and_wait(dev, p, NULL), fail, TAG, ""); - ESP_GOTO_ON_ERROR(spi_nand_write_enable(dev->config.device_handle), fail, TAG, ""); - ESP_GOTO_ON_ERROR(spi_nand_program_load(dev->config.device_handle, data, 0, dev->page_size), - fail, TAG, ""); - ESP_GOTO_ON_ERROR(spi_nand_program_load(dev->config.device_handle, (uint8_t *)&used_marker, - dev->page_size + 2, 2), - fail, TAG, ""); - ESP_GOTO_ON_ERROR(program_execute_and_wait(dev, p, &status), fail, TAG, ""); - - if ((status & STAT_PROGRAM_FAILED) != 0) { - ESP_LOGD(TAG, "prog failed, page=%"PRIu32",", p); - dhara_set_error(err, DHARA_E_BAD_BLOCK); + spi_nand_flash_device_t *dev_handle = s_handle_t[s_handle_index]; + esp_err_t ret = nand_prog(dev_handle, p, data); + if (ret) { + if (ret == ESP_ERR_NOT_FINISHED) { + dhara_set_error(err, DHARA_E_BAD_BLOCK); + } return -1; } - -#if CONFIG_NAND_FLASH_VERIFY_WRITE - ret = s_verify_write(dev, data, 0, dev->page_size); - if (ret != ESP_OK) { - ESP_LOGE(TAG, "%s: prog page=%"PRIu32" write verification failed", __func__, p); - } - ret = s_verify_write(dev, (uint8_t *)&used_marker, dev->page_size + 2, 2); - if (ret != ESP_OK) { - ESP_LOGE(TAG, "%s: prog page=%"PRIu32" used marker write verification failed", __func__, p); - } -#endif //CONFIG_NAND_FLASH_VERIFY_WRITE - return 0; -fail: - ESP_LOGE(TAG, "Error in dhara_nand_prog %d", ret); - return -1; } int dhara_nand_is_free(const struct dhara_nand *n, dhara_page_t p) { - spi_nand_flash_device_t *dev = __containerof(n, spi_nand_flash_device_t, dhara_nand); - esp_err_t ret; - uint16_t used_marker; - - ESP_GOTO_ON_ERROR(read_page_and_wait(dev, p, NULL), fail, TAG, ""); - ESP_GOTO_ON_ERROR(spi_nand_read(dev->config.device_handle, (uint8_t *)&used_marker, - dev->page_size + 2, 2), - fail, TAG, ""); - - ESP_LOGD(TAG, "is free, page=%"PRIu32", used_marker=%04x,", p, used_marker); - return used_marker == 0xFFFF; -fail: - ESP_LOGE(TAG, "Error in dhara_nand_is_free %d", ret); - return 0; -} - -#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, dhara_error_t *err) -{ - bool is_ecc_err = false; - ecc_status_t bits_corrected_status = STAT_ECC_OK; - if (dev->ecc_data.ecc_status_reg_len_in_bits == 2) { - bits_corrected_status = PACK_2BITS_STATUS(status, STAT_ECC1, STAT_ECC0); - } else if (dev->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; + spi_nand_flash_device_t *dev_handle = s_handle_t[s_handle_index]; + bool is_free_status = true; + if (nand_is_free(dev_handle, p, &is_free_status)) { + return 0; } - dev->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) { - dhara_set_error(err, DHARA_E_ECC); - is_ecc_err = true; - } + if (is_free_status == true) { + return 1; } - return is_ecc_err; + return 0; } int dhara_nand_read(const struct dhara_nand *n, dhara_page_t p, size_t offset, size_t length, uint8_t *data, dhara_error_t *err) { - ESP_LOGV(TAG, "read, page=%"PRIu32", offset=%d, length=%d", p, offset, length); - assert(p < n->num_blocks * (1 << n->log2_ppb)); - spi_nand_flash_device_t *dev = __containerof(n, spi_nand_flash_device_t, dhara_nand); - esp_err_t ret; - uint8_t status; - - ESP_GOTO_ON_ERROR(read_page_and_wait(dev, p, &status), fail, TAG, ""); - - if (is_ecc_error(dev, status, err)) { - ESP_LOGD(TAG, "read ecc error, page=%"PRIu32"", p); + spi_nand_flash_device_t *dev_handle = s_handle_t[s_handle_index]; + if (nand_read(dev_handle, p, offset, length, data)) { + if (dev_handle->chip.ecc_data.ecc_corrected_bits_status == STAT_ECC_NOT_CORRECTED) { + dhara_set_error(err, DHARA_E_ECC); + } return -1; } - - ESP_GOTO_ON_ERROR(spi_nand_read(dev->config.device_handle, data, offset, length), fail, TAG, ""); - return 0; -fail: - ESP_LOGE(TAG, "Error in dhara_nand_read %d", ret); - return -1; } int dhara_nand_copy(const struct dhara_nand *n, dhara_page_t src, dhara_page_t dst, dhara_error_t *err) { - ESP_LOGD(TAG, "copy, src=%"PRIu32", dst=%"PRIu32"", src, dst); - spi_nand_flash_device_t *dev = __containerof(n, spi_nand_flash_device_t, dhara_nand); - esp_err_t ret = ESP_OK; - uint8_t status; -#if CONFIG_NAND_FLASH_VERIFY_WRITE - uint8_t *temp_buf = NULL; -#endif //CONFIG_NAND_FLASH_VERIFY_WRITE - - ESP_GOTO_ON_ERROR(read_page_and_wait(dev, src, &status), fail, TAG, ""); - - if (is_ecc_error(dev, status, err)) { - ESP_LOGD(TAG, "copy, ecc error"); - return -1; - } - - ESP_GOTO_ON_ERROR(spi_nand_write_enable(dev->config.device_handle), fail, TAG, ""); - ESP_GOTO_ON_ERROR(program_execute_and_wait(dev, dst, &status), fail, TAG, ""); - - if ((status & STAT_PROGRAM_FAILED) != 0) { - ESP_LOGD(TAG, "copy, prog failed"); - dhara_set_error(err, DHARA_E_BAD_BLOCK); + spi_nand_flash_device_t *dev_handle = s_handle_t[s_handle_index]; + esp_err_t ret = nand_copy(dev_handle, src, dst); + if (ret) { + if (dev_handle->chip.ecc_data.ecc_corrected_bits_status == STAT_ECC_NOT_CORRECTED) { + dhara_set_error(err, DHARA_E_ECC); + } + if (ret == ESP_ERR_NOT_FINISHED) { + dhara_set_error(err, DHARA_E_BAD_BLOCK); + } return -1; } - -#if CONFIG_NAND_FLASH_VERIFY_WRITE - // First read src page data from cache to temp_buf - temp_buf = heap_caps_malloc(dev->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(dev->config.device_handle, temp_buf, 0, dev->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(dev, dst, &status), fail, TAG, ""); - - if (is_ecc_error(dev, status, err)) { - 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(dev, temp_buf, 0, dev->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 dhara_nand_copy %d", ret); - return -1; + return 0; } +/*------------------------------------------------------------------------------------------------------*/ diff --git a/spi_nand_flash/src/nand.c b/spi_nand_flash/src/nand.c index afc7b96b2d..7821a02749 100644 --- a/spi_nand_flash/src/nand.c +++ b/spi_nand_flash/src/nand.c @@ -3,172 +3,24 @@ * * SPDX-License-Identifier: Apache-2.0 * - * SPDX-FileContributor: 2015-2023 Espressif Systems (Shanghai) CO LTD + * SPDX-FileContributor: 2015-2024 Espressif Systems (Shanghai) CO LTD */ #include #include "esp_check.h" -#include "freertos/FreeRTOS.h" -#include "freertos/semphr.h" #include "spi_nand_flash.h" -#include "dhara/error.h" #include "spi_nand_oper.h" +#include "spi_nand_api.h" #include "nand.h" #include "nand_flash_devices.h" #include "esp_vfs_fat_nand.h" static const char *TAG = "nand_flash"; -static 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->read_page_delay_us = 10; - dev->erase_block_delay_us = 2500; - dev->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->dhara_nand.num_blocks = 512; - break; - case WINBOND_DI_AA21: - case WINBOND_DI_BA21: - case WINBOND_DI_BC21: - dev->dhara_nand.num_blocks = 1024; - break; - default: - return ESP_ERR_INVALID_RESPONSE; - } - return ESP_OK; -} - -static 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->erase_block_delay_us = 3000; - dev->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->dhara_nand.num_blocks = 1024; - dev->read_page_delay_us = 60; - break; - case ALLIANCE_DI_2E: //AS5F32G04SND-08LIN - case ALLIANCE_DI_8E: //AS5F12G04SND-10LIN - dev->dhara_nand.num_blocks = 2048; - dev->read_page_delay_us = 60; - break; - case ALLIANCE_DI_2F: //AS5F34G04SND-08LIN - case ALLIANCE_DI_8F: //AS5F14G04SND-10LIN - dev->dhara_nand.num_blocks = 4096; - dev->read_page_delay_us = 60; - break; - case ALLIANCE_DI_2D: //AS5F38G04SND-08LIN - case ALLIANCE_DI_8D: //AS5F18G04SND-10LIN - dev->dhara_nand.log2_page_size = 12; // 4k pages - dev->dhara_nand.num_blocks = 4096; - dev->read_page_delay_us = 130; // somewhat slower reads - break; - default: - return ESP_ERR_INVALID_RESPONSE; - } - return ESP_OK; -} - -static 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->read_page_delay_us = 25; - dev->erase_block_delay_us = 3200; - dev->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->dhara_nand.num_blocks = 1024; - break; - case GIGADEVICE_DI_52: - case GIGADEVICE_DI_42: - case GIGADEVICE_DI_32: - case GIGADEVICE_DI_22: - dev->dhara_nand.num_blocks = 2048; - break; - case GIGADEVICE_DI_55: - case GIGADEVICE_DI_45: - case GIGADEVICE_DI_35: - case GIGADEVICE_DI_25: - dev->dhara_nand.num_blocks = 4096; - break; - default: - return ESP_ERR_INVALID_RESPONSE; - } - return ESP_OK; -} - -static 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->ecc_data.ecc_status_reg_len_in_bits = 3; - dev->erase_block_delay_us = 2000; - ESP_LOGD(TAG, "%s: device_id: %x\n", __func__, device_id); - switch (device_id) { - case MICRON_DI_34: - dev->read_page_delay_us = 115; - dev->program_page_delay_us = 240; - dev->dhara_nand.num_blocks = 2048; - dev->dhara_nand.log2_ppb = 6; // 64 pages per block - dev->dhara_nand.log2_page_size = 12; // 4096 bytes per page - break; - case MICRON_DI_14: - case MICRON_DI_15: - dev->read_page_delay_us = 46; - dev->program_page_delay_us = 220; - dev->dhara_nand.num_blocks = 1024; - dev->dhara_nand.log2_ppb = 6; // 64 pages per block - dev->dhara_nand.log2_page_size = 11; // 2048 bytes per page - break; - default: - return ESP_ERR_INVALID_RESPONSE; - } - return ESP_OK; -} +extern esp_err_t spi_nand_winbond_init(spi_nand_flash_device_t *dev); +extern esp_err_t spi_nand_alliance_init(spi_nand_flash_device_t *dev); +extern esp_err_t spi_nand_gigadevice_init(spi_nand_flash_device_t *dev); +extern esp_err_t spi_nand_micron_init(spi_nand_flash_device_t *dev); static esp_err_t detect_chip(spi_nand_flash_device_t *dev) { @@ -228,24 +80,23 @@ esp_err_t spi_nand_flash_init_device(spi_nand_flash_config_t *config, spi_nand_f memcpy(&(*handle)->config, config, sizeof(spi_nand_flash_config_t)); - (*handle)->ecc_data.ecc_status_reg_len_in_bits = 2; - (*handle)->ecc_data.ecc_data_refresh_threshold = 4; - (*handle)->dhara_nand.log2_ppb = 6; // 64 pages per block is standard - (*handle)->dhara_nand.log2_page_size = 11; // 2048 bytes per page is fairly standard + (*handle)->chip.ecc_data.ecc_status_reg_len_in_bits = 2; + (*handle)->chip.ecc_data.ecc_data_refresh_threshold = 4; + (*handle)->chip.log2_ppb = 6; // 64 pages per block is standard + (*handle)->chip.log2_page_size = 11; // 2048 bytes per page is fairly standard esp_err_t ret = ESP_OK; ESP_GOTO_ON_ERROR(detect_chip(*handle), fail, TAG, "Failed to detect nand chip"); ESP_GOTO_ON_ERROR(unprotect_chip(*handle), fail, TAG, "Failed to clear protection register"); - (*handle)->page_size = 1 << (*handle)->dhara_nand.log2_page_size; - (*handle)->block_size = (1 << (*handle)->dhara_nand.log2_ppb) * (*handle)->page_size; - (*handle)->num_blocks = (*handle)->dhara_nand.num_blocks; + (*handle)->chip.page_size = 1 << (*handle)->chip.log2_page_size; + (*handle)->chip.block_size = (1 << (*handle)->chip.log2_ppb) * (*handle)->chip.page_size; - (*handle)->work_buffer = heap_caps_malloc((*handle)->page_size, MALLOC_CAP_DMA | MALLOC_CAP_8BIT); + (*handle)->work_buffer = heap_caps_malloc((*handle)->chip.page_size, MALLOC_CAP_DMA | MALLOC_CAP_8BIT); ESP_GOTO_ON_FALSE((*handle)->work_buffer != NULL, ESP_ERR_NO_MEM, fail, TAG, "nomem"); - (*handle)->read_buffer = heap_caps_malloc((*handle)->page_size, MALLOC_CAP_DMA | MALLOC_CAP_8BIT); + (*handle)->read_buffer = heap_caps_malloc((*handle)->chip.page_size, MALLOC_CAP_DMA | MALLOC_CAP_8BIT); ESP_GOTO_ON_FALSE((*handle)->read_buffer != NULL, ESP_ERR_NO_MEM, fail, TAG, "nomem"); (*handle)->mutex = xSemaphoreCreateMutex(); @@ -254,23 +105,21 @@ esp_err_t spi_nand_flash_init_device(spi_nand_flash_config_t *config, spi_nand_f goto fail; } - dhara_map_init(&(*handle)->dhara_map, &(*handle)->dhara_nand, (*handle)->work_buffer, config->gc_factor); + ESP_GOTO_ON_ERROR(register_nand_dev(*handle), fail, TAG, "Failed to register nand dev"); - dhara_error_t ignored; - dhara_map_resume(&(*handle)->dhara_map, &ignored); + if ((*handle)->ops->init == NULL) { + ESP_LOGE(TAG, "Failed to initialize spi_nand_ops"); + ret = ESP_FAIL; + goto fail; + } + (*handle)->ops->init(*handle); return ret; fail: - if ((*handle)->work_buffer != NULL) { - free((*handle)->work_buffer); - } - if ((*handle)->read_buffer != NULL) { - free((*handle)->read_buffer); - } - if ((*handle)->mutex != NULL) { - vSemaphoreDelete((*handle)->mutex); - } + free((*handle)->work_buffer); + free((*handle)->read_buffer); + vSemaphoreDelete((*handle)->mutex); free(*handle); return ret; } @@ -281,18 +130,8 @@ esp_err_t spi_nand_erase_chip(spi_nand_flash_device_t *handle) esp_err_t ret = ESP_OK; xSemaphoreTake(handle->mutex, portMAX_DELAY); - - for (int i = 0; i < handle->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->dhara_nand.log2_ppb)), - end, TAG, ""); - ESP_GOTO_ON_ERROR(wait_for_ready(handle->config.device_handle, handle->erase_block_delay_us, NULL), - end, TAG, ""); - } - - // clear dhara map - dhara_map_init(&handle->dhara_map, &handle->dhara_nand, handle->work_buffer, handle->config.gc_factor); - dhara_map_clear(&handle->dhara_map); + ESP_GOTO_ON_ERROR(nand_erase_chip(handle), end, TAG, ""); + handle->ops->deinit(handle); end: xSemaphoreGive(handle->mutex); @@ -303,119 +142,114 @@ static bool s_need_data_refresh(spi_nand_flash_device_t *handle) { uint8_t min_bits_corrected = 0; bool ret = false; - if (handle->ecc_data.ecc_corrected_bits_status == STAT_ECC_1_TO_3_BITS_CORRECTED) { + if (handle->chip.ecc_data.ecc_corrected_bits_status == STAT_ECC_1_TO_3_BITS_CORRECTED) { min_bits_corrected = 1; - } else if (handle->ecc_data.ecc_corrected_bits_status == STAT_ECC_4_TO_6_BITS_CORRECTED) { + } else if (handle->chip.ecc_data.ecc_corrected_bits_status == STAT_ECC_4_TO_6_BITS_CORRECTED) { min_bits_corrected = 4; - } else if (handle->ecc_data.ecc_corrected_bits_status == STAT_ECC_7_8_BITS_CORRECTED) { + } else if (handle->chip.ecc_data.ecc_corrected_bits_status == STAT_ECC_7_8_BITS_CORRECTED) { min_bits_corrected = 7; } // if number of corrected bits is greater than refresh threshold then rewite the sector - if (min_bits_corrected >= handle->ecc_data.ecc_data_refresh_threshold) { + if (min_bits_corrected >= handle->chip.ecc_data.ecc_data_refresh_threshold) { ret = true; } return ret; } -esp_err_t spi_nand_flash_read_sector(spi_nand_flash_device_t *handle, uint8_t *buffer, dhara_sector_t sector_id) +esp_err_t spi_nand_flash_read_sector(spi_nand_flash_device_t *handle, uint8_t *buffer, sector_t sector_id) { - dhara_error_t err; esp_err_t ret = ESP_OK; xSemaphoreTake(handle->mutex, portMAX_DELAY); - + ret = handle->ops->read(handle, buffer, sector_id); // After a successful read operation, check the ECC corrected bit status; if the read fails, return an error - if (dhara_map_read(&handle->dhara_map, sector_id, handle->read_buffer, &err)) { - ret = ESP_ERR_FLASH_BASE + err; - } else if (handle->ecc_data.ecc_corrected_bits_status) { + if (ret == ESP_OK && handle->chip.ecc_data.ecc_corrected_bits_status) { // This indicates a soft ECC error, we rewrite the sector to recover if corrected bits are greater than refresh threshold if (s_need_data_refresh(handle)) { - if (dhara_map_write(&handle->dhara_map, sector_id, handle->read_buffer, &err)) { - ret = ESP_ERR_FLASH_BASE + err; - } + ret = handle->ops->write(handle, buffer, sector_id); } } - xSemaphoreGive(handle->mutex); - memcpy(buffer, handle->read_buffer, handle->page_size); + return ret; } -esp_err_t spi_nand_flash_copy_sector(spi_nand_flash_device_t *handle, dhara_sector_t src_sec, dhara_sector_t dst_sec) +esp_err_t spi_nand_flash_copy_sector(spi_nand_flash_device_t *handle, sector_t src_sec, sector_t dst_sec) { - dhara_error_t err; esp_err_t ret = ESP_OK; xSemaphoreTake(handle->mutex, portMAX_DELAY); - - if (dhara_map_copy_sector(&handle->dhara_map, src_sec, dst_sec, &err)) { - ret = ESP_ERR_FLASH_BASE + err; - } - + ret = handle->ops->copy_sector(handle, src_sec, dst_sec); xSemaphoreGive(handle->mutex); + return ret; } -esp_err_t spi_nand_flash_write_sector(spi_nand_flash_device_t *handle, const uint8_t *buffer, dhara_sector_t sector_id) +esp_err_t spi_nand_flash_write_sector(spi_nand_flash_device_t *handle, const uint8_t *buffer, sector_t sector_id) { - dhara_error_t err; esp_err_t ret = ESP_OK; xSemaphoreTake(handle->mutex, portMAX_DELAY); - - if (dhara_map_write(&handle->dhara_map, sector_id, buffer, &err)) { - ret = ESP_ERR_FLASH_BASE + err; - } - + ret = handle->ops->write(handle, buffer, sector_id); xSemaphoreGive(handle->mutex); + return ret; } -esp_err_t spi_nand_flash_trim(spi_nand_flash_device_t *handle, dhara_sector_t sector_id) +esp_err_t spi_nand_flash_trim(spi_nand_flash_device_t *handle, sector_t sector_id) { - dhara_error_t err; esp_err_t ret = ESP_OK; xSemaphoreTake(handle->mutex, portMAX_DELAY); - - if (dhara_map_trim(&handle->dhara_map, sector_id, &err)) { - ret = ESP_ERR_FLASH_BASE + err; - } - + ret = handle->ops->trim(handle, sector_id); xSemaphoreGive(handle->mutex); + return ret; } esp_err_t spi_nand_flash_sync(spi_nand_flash_device_t *handle) { - dhara_error_t err; esp_err_t ret = ESP_OK; xSemaphoreTake(handle->mutex, portMAX_DELAY); - - if (dhara_map_sync(&handle->dhara_map, &err)) { - return ESP_ERR_FLASH_BASE + err; - } - + ret = handle->ops->sync(handle); xSemaphoreGive(handle->mutex); + return ret; } -esp_err_t spi_nand_flash_get_capacity(spi_nand_flash_device_t *handle, dhara_sector_t *number_of_sectors) +esp_err_t spi_nand_flash_get_capacity(spi_nand_flash_device_t *handle, sector_t *number_of_sectors) { - *number_of_sectors = dhara_map_capacity(&handle->dhara_map); - return ESP_OK; + return handle->ops->get_capacity(handle, number_of_sectors); } esp_err_t spi_nand_flash_get_sector_size(spi_nand_flash_device_t *handle, uint32_t *sector_size) { - *sector_size = handle->page_size; + *sector_size = handle->chip.page_size; return ESP_OK; } +esp_err_t spi_nand_flash_get_block_num(spi_nand_flash_device_t *handle, uint32_t *num_blocks) +{ + *num_blocks = handle->chip.num_blocks; + return ESP_OK; +} + +esp_err_t spi_nand_flash_is_bad_block(spi_nand_flash_device_t *handle, uint32_t blk, bool *is_bad) +{ + esp_err_t ret = ESP_OK; + + xSemaphoreTake(handle->mutex, portMAX_DELAY); + ret = nand_is_bad(handle, blk, is_bad); + xSemaphoreGive(handle->mutex); + + return ret; +} + esp_err_t spi_nand_flash_deinit_device(spi_nand_flash_device_t *handle) { + unregister_nand_dev(handle); free(handle->work_buffer); free(handle->read_buffer); vSemaphoreDelete(handle->mutex); diff --git a/spi_nand_flash/src/nand.h b/spi_nand_flash/src/nand.h deleted file mode 100644 index bd78227901..0000000000 --- a/spi_nand_flash/src/nand.h +++ /dev/null @@ -1,59 +0,0 @@ -/* - * SPDX-FileCopyrightText: 2022 mikkeldamsgaard project - * - * SPDX-License-Identifier: Apache-2.0 - * - * SPDX-FileContributor: 2015-2023 Espressif Systems (Shanghai) CO LTD - */ - -#pragma once - -#include -#include "spi_nand_flash.h" -#include "freertos/FreeRTOS.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; - -struct spi_nand_flash_device_t { - spi_nand_flash_config_t config; - uint32_t block_size; - uint32_t page_size; - uint32_t num_blocks; - struct dhara_map dhara_map; - struct dhara_nand dhara_nand; - uint8_t *work_buffer; - uint8_t *read_buffer; - uint32_t read_page_delay_us; - uint32_t erase_block_delay_us; - uint32_t program_page_delay_us; - ecc_data_t ecc_data; - SemaphoreHandle_t mutex; -}; - -esp_err_t wait_for_ready(spi_device_handle_t device, uint32_t expected_operation_time_us, uint8_t *status_out); - -#ifdef __cplusplus -} -#endif diff --git a/spi_nand_flash/src/nand_alliance.c b/spi_nand_flash/src/nand_alliance.c new file mode 100644 index 0000000000..fee26fa5db --- /dev/null +++ b/spi_nand_flash/src/nand_alliance.c @@ -0,0 +1,56 @@ +/* + * SPDX-FileCopyrightText: 2015-2024 Espressif Systems (Shanghai) CO LTD + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#include +#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; +} diff --git a/spi_nand_flash/src/nand_gigadevice.c b/spi_nand_flash/src/nand_gigadevice.c new file mode 100644 index 0000000000..0bc9947039 --- /dev/null +++ b/spi_nand_flash/src/nand_gigadevice.c @@ -0,0 +1,53 @@ +/* + * SPDX-FileCopyrightText: 2015-2024 Espressif Systems (Shanghai) CO LTD + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#include +#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; +} diff --git a/spi_nand_flash/src/nand_micron.c b/spi_nand_flash/src/nand_micron.c new file mode 100644 index 0000000000..01a0f6ea2d --- /dev/null +++ b/spi_nand_flash/src/nand_micron.c @@ -0,0 +1,49 @@ +/* + * SPDX-FileCopyrightText: 2015-2024 Espressif Systems (Shanghai) CO LTD + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#include +#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; +} diff --git a/spi_nand_flash/src/nand_winbond.c b/spi_nand_flash/src/nand_winbond.c new file mode 100644 index 0000000000..acf80e3de3 --- /dev/null +++ b/spi_nand_flash/src/nand_winbond.c @@ -0,0 +1,45 @@ +/* + * SPDX-FileCopyrightText: 2015-2024 Espressif Systems (Shanghai) CO LTD + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#include +#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; +} diff --git a/spi_nand_flash/src/spi_nand_api.c b/spi_nand_flash/src/spi_nand_api.c new file mode 100644 index 0000000000..e5aeb00900 --- /dev/null +++ b/spi_nand_flash/src/spi_nand_api.c @@ -0,0 +1,352 @@ +/* + * SPDX-FileCopyrightText: 2022 mikkeldamsgaard project + * + * SPDX-License-Identifier: Apache-2.0 + * + * SPDX-FileContributor: 2015-2024 Espressif Systems (Shanghai) CO LTD + */ + +#include +#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; + } + + 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; +} diff --git a/spi_nand_flash/test_app/main/CMakeLists.txt b/spi_nand_flash/test_app/main/CMakeLists.txt index 1b170933ac..3d514dc0ef 100644 --- a/spi_nand_flash/test_app/main/CMakeLists.txt +++ b/spi_nand_flash/test_app/main/CMakeLists.txt @@ -8,7 +8,7 @@ else() endif() idf_component_register(SRCS ${src} - PRIV_INCLUDE_DIRS . + PRIV_INCLUDE_DIRS . "../../priv_include" PRIV_REQUIRES ${priv_reqs} WHOLE_ARCHIVE ) diff --git a/spi_nand_flash/test_app/main/test_app_main.c b/spi_nand_flash/test_app/main/test_app_main.c index 378903599a..aba5cd70dd 100644 --- a/spi_nand_flash/test_app/main/test_app_main.c +++ b/spi_nand_flash/test_app/main/test_app_main.c @@ -18,7 +18,7 @@ void setUp(void) void tearDown(void) { esp_reent_cleanup(); //clean up some of the newlib's lazy allocations - unity_utils_evaluate_leaks_direct(20); + unity_utils_evaluate_leaks_direct(200); } void app_main(void) diff --git a/spi_nand_flash/test_app/main/test_spi_nand_flash.c b/spi_nand_flash/test_app/main/test_spi_nand_flash.c index 06a2232b25..ae74d43314 100644 --- a/spi_nand_flash/test_app/main/test_spi_nand_flash.c +++ b/spi_nand_flash/test_app/main/test_spi_nand_flash.c @@ -18,6 +18,7 @@ #include "freertos/semphr.h" #include "driver/spi_master.h" #include "spi_nand_flash.h" +#include "spi_nand_api.h" #include "unity.h" #include "soc/spi_pins.h" #include "sdkconfig.h" @@ -210,3 +211,25 @@ TEST_CASE("copy nand flash sectors", "[spi_nand_flash]") } deinit_nand_flash(nand_flash_device_handle, spi); } + +TEST_CASE("verify mark_bad_block works", "[spi_nand_flash]") +{ + spi_nand_flash_device_t *nand_flash_device_handle; + spi_device_handle_t spi; + setup_nand_flash(&nand_flash_device_handle, &spi); + uint32_t sector_num, sector_size; + + TEST_ESP_OK(spi_nand_flash_get_capacity(nand_flash_device_handle, §or_num)); + TEST_ESP_OK(spi_nand_flash_get_sector_size(nand_flash_device_handle, §or_size)); + printf("Number of sectors: %" PRIu32 ", Sector size: %" PRIu32 "\n", sector_num, sector_size); + + uint32_t test_block = 10; + if (test_block < sector_num) { + TEST_ESP_OK(nand_mark_bad(nand_flash_device_handle, test_block)); + bool is_bad_status = false; + TEST_ESP_OK(nand_is_bad(nand_flash_device_handle, test_block, &is_bad_status)); + TEST_ASSERT_TRUE(is_bad_status == true); + } + + deinit_nand_flash(nand_flash_device_handle, spi); +}