From eb4e40c4adc6cc98e4e26225691d8b214417585a Mon Sep 17 00:00:00 2001 From: "sonika.rathi" Date: Mon, 11 Nov 2024 09:26:25 +0100 Subject: [PATCH] feat(spi_nand_flash): expose lower-level API and make it usable without Dhara --- spi_nand_flash/CMakeLists.txt | 7 + spi_nand_flash/idf_component.yml | 2 +- .../include/nand_private/nand_impl_wrap.h | 31 ++ spi_nand_flash/include/spi_nand_flash.h | 31 +- spi_nand_flash/priv_include/nand.h | 78 ++++ spi_nand_flash/priv_include/nand_flash_chip.h | 24 + .../nand_flash_devices.h | 0 spi_nand_flash/priv_include/nand_impl.h | 30 ++ .../{src => priv_include}/spi_nand_oper.h | 0 spi_nand_flash/src/dhara_glue.c | 416 +++++++----------- spi_nand_flash/src/nand.c | 294 +++---------- spi_nand_flash/src/nand.h | 59 --- spi_nand_flash/src/nand_alliance.c | 56 +++ spi_nand_flash/src/nand_gigadevice.c | 53 +++ spi_nand_flash/src/nand_impl.c | 353 +++++++++++++++ spi_nand_flash/src/nand_impl_wrap.c | 84 ++++ spi_nand_flash/src/nand_micron.c | 49 +++ spi_nand_flash/src/nand_winbond.c | 45 ++ spi_nand_flash/test_app/main/CMakeLists.txt | 1 - .../test_app/main/test_spi_nand_flash.c | 74 ++++ 20 files changed, 1138 insertions(+), 549 deletions(-) create mode 100644 spi_nand_flash/include/nand_private/nand_impl_wrap.h create mode 100644 spi_nand_flash/priv_include/nand.h create mode 100644 spi_nand_flash/priv_include/nand_flash_chip.h rename spi_nand_flash/{src => priv_include}/nand_flash_devices.h (100%) create mode 100644 spi_nand_flash/priv_include/nand_impl.h rename spi_nand_flash/{src => priv_include}/spi_nand_oper.h (100%) delete mode 100644 spi_nand_flash/src/nand.h create mode 100644 spi_nand_flash/src/nand_alliance.c create mode 100644 spi_nand_flash/src/nand_gigadevice.c create mode 100644 spi_nand_flash/src/nand_impl.c create mode 100644 spi_nand_flash/src/nand_impl_wrap.c create mode 100644 spi_nand_flash/src/nand_micron.c create mode 100644 spi_nand_flash/src/nand_winbond.c diff --git a/spi_nand_flash/CMakeLists.txt b/spi_nand_flash/CMakeLists.txt index ec6be69fc7..b7aed7cc4c 100644 --- a/spi_nand_flash/CMakeLists.txt +++ b/spi_nand_flash/CMakeLists.txt @@ -1,4 +1,10 @@ set(srcs "src/nand.c" + "src/nand_winbond.c" + "src/nand_gigadevice.c" + "src/nand_alliance.c" + "src/nand_micron.c" + "src/nand_impl.c" + "src/nand_impl_wrap.c" "src/spi_nand_oper.c" "src/dhara_glue.c" "vfs/vfs_fat_spinandflash.c" @@ -16,5 +22,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/nand_private/nand_impl_wrap.h b/spi_nand_flash/include/nand_private/nand_impl_wrap.h new file mode 100644 index 0000000000..18de8cf621 --- /dev/null +++ b/spi_nand_flash/include/nand_private/nand_impl_wrap.h @@ -0,0 +1,31 @@ +/* + * SPDX-FileCopyrightText: 2015-2024 Espressif Systems (Shanghai) CO LTD + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#pragma once + +#include +#include "esp_err.h" +#include "spi_nand_flash.h" + +#ifdef __cplusplus +extern "C" { +#endif + +// These APIs provide direct access to lower-level NAND functions, bypassing the Dhara library. +// These functions differ from the similarly named `nand_*` functions in that they also take the mutex for the duration of the call. + +esp_err_t nand_wrap_is_bad(spi_nand_flash_device_t *handle, uint32_t b, bool *is_bad_status); +esp_err_t nand_wrap_mark_bad(spi_nand_flash_device_t *handle, uint32_t b); +esp_err_t nand_wrap_erase_chip(spi_nand_flash_device_t *handle); +esp_err_t nand_wrap_erase_block(spi_nand_flash_device_t *handle, uint32_t b); +esp_err_t nand_wrap_prog(spi_nand_flash_device_t *handle, uint32_t p, const uint8_t *data); +esp_err_t nand_wrap_is_free(spi_nand_flash_device_t *handle, uint32_t p, bool *is_free_status); +esp_err_t nand_wrap_read(spi_nand_flash_device_t *handle, uint32_t p, size_t offset, size_t length, uint8_t *data); +esp_err_t nand_wrap_copy(spi_nand_flash_device_t *handle, uint32_t src, uint32_t dst); + +#ifdef __cplusplus +} +#endif diff --git a/spi_nand_flash/include/spi_nand_flash.h b/spi_nand_flash/include/spi_nand_flash.h index bd283a64a0..a11f1ed395 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,7 +12,6 @@ #include "esp_err.h" #include "driver/spi_common.h" #include "driver/spi_master.h" -#include "dhara/map.h" #ifdef __cplusplus extern "C" { @@ -47,7 +46,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, uint32_t sector_id); /** @brief Copy a sector to another sector from the nand flash. * @@ -56,7 +55,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, uint32_t src_sec, uint32_t dst_sec); /** @brief Write a sector to the nand flash. * @@ -65,7 +64,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, uint32_t sector_id); /** @brief Trim sector from the nand flash. * @@ -77,7 +76,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, uint32_t sector_id); /** @brief Synchronizes any cache to the device. * @@ -94,16 +93,24 @@ 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, uint32_t *number_of_sectors); /** @brief Retrieve the size of each sector. * * @param handle The handle to the SPI nand flash chip. - * @param[out] number_of_sectors A pointer of where to put the return value + * @param[out] sectors_size 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_sector_size(spi_nand_flash_device_t *handle, uint32_t *sector_size); +/** @brief Retrieve the size of each block. + * + * @param handle The handle to the SPI nand flash chip. + * @param[out] block_size 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_size(spi_nand_flash_device_t *handle, uint32_t *block_size); + /** @brief Erases the entire chip, invalidating any data on the chip. * * @param handle The handle to the SPI nand flash chip. @@ -111,6 +118,14 @@ 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, uint32_t *number_of_blocks); + /** @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..c73b07adf6 --- /dev/null +++ b/spi_nand_flash/priv_include/nand.h @@ -0,0 +1,78 @@ +/* + * 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 "spi_nand_flash.h" + +#ifdef __cplusplus +extern "C" { +#endif + +esp_err_t spi_nand_winbond_init(spi_nand_flash_device_t *dev); +esp_err_t spi_nand_alliance_init(spi_nand_flash_device_t *dev); +esp_err_t spi_nand_gigadevice_init(spi_nand_flash_device_t *dev); +esp_err_t spi_nand_micron_init(spi_nand_flash_device_t *dev); + +#ifdef __cplusplus +} +#endif diff --git a/spi_nand_flash/src/nand_flash_devices.h b/spi_nand_flash/priv_include/nand_flash_devices.h similarity index 100% rename from spi_nand_flash/src/nand_flash_devices.h rename to spi_nand_flash/priv_include/nand_flash_devices.h diff --git a/spi_nand_flash/priv_include/nand_impl.h b/spi_nand_flash/priv_include/nand_impl.h new file mode 100644 index 0000000000..a6546965e5 --- /dev/null +++ b/spi_nand_flash/priv_include/nand_impl.h @@ -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 +#include "esp_err.h" +#include "nand.h" + +#ifdef __cplusplus +extern "C" { +#endif + +esp_err_t nand_is_bad(spi_nand_flash_device_t *handle, uint32_t b, bool *is_bad_status); +esp_err_t nand_mark_bad(spi_nand_flash_device_t *handle, uint32_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, uint32_t b); +esp_err_t nand_prog(spi_nand_flash_device_t *handle, uint32_t p, const uint8_t *data); +esp_err_t nand_is_free(spi_nand_flash_device_t *handle, uint32_t p, bool *is_free_status); +esp_err_t nand_read(spi_nand_flash_device_t *handle, uint32_t p, size_t offset, size_t length, uint8_t *data); +esp_err_t nand_copy(spi_nand_flash_device_t *handle, uint32_t src, uint32_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..ec3ccb6b68 100644 --- a/spi_nand_flash/src/dhara_glue.c +++ b/spi_nand_flash/src/dhara_glue.c @@ -3,329 +3,251 @@ * * 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 "nand_impl.h" #include "nand.h" -#define ROM_WAIT_THRESHOLD_US 1000 +typedef struct { + struct dhara_nand dhara_nand; + struct dhara_map dhara_map; + spi_nand_flash_device_t *parent_handle; +} spi_nand_flash_dhara_priv_data_t; -static const char *TAG = "dhara_glue"; +static esp_err_t dhara_init(spi_nand_flash_device_t *handle) +{ + // create a holder structure for dhara context + spi_nand_flash_dhara_priv_data_t *dhara_priv_data = calloc(1, sizeof(spi_nand_flash_dhara_priv_data_t)); + // save the holder inside the device structure + handle->ops_priv_data = dhara_priv_data; + // store the pointer back to device structure in the holder stucture + dhara_priv_data->parent_handle = handle; + + dhara_priv_data->dhara_nand.log2_page_size = handle->chip.log2_page_size; + dhara_priv_data->dhara_nand.log2_ppb = handle->chip.log2_ppb; + dhara_priv_data->dhara_nand.num_blocks = handle->chip.num_blocks; + + dhara_map_init(&dhara_priv_data->dhara_map, &dhara_priv_data->dhara_nand, handle->work_buffer, handle->config.gc_factor); + dhara_error_t ignored; + dhara_map_resume(&dhara_priv_data->dhara_map, &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; - } + spi_nand_flash_dhara_priv_data_t *dhara_priv_data = (spi_nand_flash_dhara_priv_data_t *)handle->ops_priv_data; + // clear dhara map + dhara_map_init(&dhara_priv_data->dhara_map, &dhara_priv_data->dhara_nand, handle->work_buffer, handle->config.gc_factor); + dhara_map_clear(&dhara_priv_data->dhara_map); + 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) +{ + spi_nand_flash_dhara_priv_data_t *dhara_priv_data = (spi_nand_flash_dhara_priv_data_t *)handle->ops_priv_data; + dhara_error_t err; + if (dhara_map_read(&dhara_priv_data->dhara_map, sector_id, handle->read_buffer, &err)) { + return ESP_ERR_FLASH_BASE + err; } - free(temp_buf); + 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); + spi_nand_flash_dhara_priv_data_t *dhara_priv_data = (spi_nand_flash_dhara_priv_data_t *)handle->ops_priv_data; + dhara_error_t err; + if (dhara_map_write(&dhara_priv_data->dhara_map, sector_id, buffer, &err)) { + return ESP_ERR_FLASH_BASE + err; } + 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) +{ + spi_nand_flash_dhara_priv_data_t *dhara_priv_data = (spi_nand_flash_dhara_priv_data_t *)handle->ops_priv_data; + dhara_error_t err; + if (dhara_map_copy_sector(&dhara_priv_data->dhara_map, src_sec, dst_sec, &err)) { + return ESP_ERR_FLASH_BASE + err; } - 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_trim(spi_nand_flash_device_t *handle, dhara_sector_t sector_id) { - ESP_RETURN_ON_ERROR(spi_nand_read_page(dev->config.device_handle, page), TAG, ""); - - return wait_for_ready(dev->config.device_handle, dev->read_page_delay_us, status_out); + spi_nand_flash_dhara_priv_data_t *dhara_priv_data = (spi_nand_flash_dhara_priv_data_t *)handle->ops_priv_data; + dhara_error_t err; + if (dhara_map_trim(&dhara_priv_data->dhara_map, sector_id, &err)) { + return ESP_ERR_FLASH_BASE + err; + } + return ESP_OK; } -static esp_err_t program_execute_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_program_execute(dev->config.device_handle, page), TAG, ""); - - return wait_for_ready(dev->config.device_handle, dev->program_page_delay_us, status_out); + spi_nand_flash_dhara_priv_data_t *dhara_priv_data = (spi_nand_flash_dhara_priv_data_t *)handle->ops_priv_data; + dhara_error_t err; + if (dhara_map_sync(&dhara_priv_data->dhara_map, &err)) { + return ESP_ERR_FLASH_BASE + err; + } + return ESP_OK; } -int dhara_nand_is_bad(const struct dhara_nand *n, dhara_block_t b) +static esp_err_t dhara_get_capacity(spi_nand_flash_device_t *handle, dhara_sector_t *number_of_sectors) { - 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; + spi_nand_flash_dhara_priv_data_t *dhara_priv_data = (spi_nand_flash_dhara_priv_data_t *)handle->ops_priv_data; + *number_of_sectors = dhara_map_capacity(&dhara_priv_data->dhara_map); + return ESP_OK; +} - ESP_GOTO_ON_ERROR(read_page_and_wait(dev, first_block_page, NULL), fail, TAG, ""); +static esp_err_t dhara_erase_chip(spi_nand_flash_device_t *handle) +{ + return nand_erase_chip(handle); +} - // 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, ""); +static esp_err_t dhara_erase_block(spi_nand_flash_device_t *handle, uint32_t block) +{ + return nand_erase_block(handle, block); +} - ESP_LOGD(TAG, "is_bad, block=%"PRIu32", page=%"PRIu32",indicator = %04x", b, first_block_page, bad_block_indicator); - return bad_block_indicator != 0xFFFF; -fail: - ESP_LOGE(TAG, "Error in dhara_nand_is_bad %d", ret); - return 1; +const spi_nand_ops dhara_nand_ops = { + .init = &dhara_init, + .deinit = &dhara_deinit, + .read = &dhara_read, + .write = &dhara_write, + .erase_chip = &dhara_erase_chip, + .erase_block = &dhara_erase_block, + .trim = &dhara_trim, + .sync = &dhara_sync, + .copy_sector = &dhara_copy_sector, + .get_capacity = &dhara_get_capacity, +}; + +esp_err_t nand_register_dev(spi_nand_flash_device_t *handle) +{ + if (handle == NULL) { + return ESP_ERR_INVALID_ARG; + } + handle->ops = &dhara_nand_ops; + return ESP_OK; } -void dhara_nand_mark_bad(const struct dhara_nand *n, dhara_block_t b) +esp_err_t nand_unregister_dev(spi_nand_flash_device_t *handle) { - spi_nand_flash_device_t *dev = __containerof(n, spi_nand_flash_device_t, dhara_nand); - esp_err_t ret = ESP_OK; + free(handle->ops_priv_data); + handle->ops = NULL; + return 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, ""); +// The following APIs are implementations required by the Dhara library. +// Please refer to the header file dhara/nand.h for details. -#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); +int dhara_nand_is_bad(const struct dhara_nand *n, dhara_block_t b) +{ + spi_nand_flash_dhara_priv_data_t *dhara_priv_data = __containerof(n, spi_nand_flash_dhara_priv_data_t, dhara_nand); + spi_nand_flash_device_t *dev_handle = dhara_priv_data->parent_handle; + bool is_bad_status = false; + if (nand_is_bad(dev_handle, b, &is_bad_status)) { + return 1; + } + if (is_bad_status == true) { + return 1; } -#endif //CONFIG_NAND_FLASH_VERIFY_WRITE + return 0; +} + +void dhara_nand_mark_bad(const struct dhara_nand *n, dhara_block_t b) +{ + spi_nand_flash_dhara_priv_data_t *dhara_priv_data = __containerof(n, spi_nand_flash_dhara_priv_data_t, dhara_nand); + spi_nand_flash_device_t *dev_handle = dhara_priv_data->parent_handle; + nand_mark_bad(dev_handle, b); 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_dhara_priv_data_t *dhara_priv_data = __containerof(n, spi_nand_flash_dhara_priv_data_t, dhara_nand); + spi_nand_flash_device_t *dev_handle = dhara_priv_data->parent_handle; + 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_dhara_priv_data_t *dhara_priv_data = __containerof(n, spi_nand_flash_dhara_priv_data_t, dhara_nand); + spi_nand_flash_device_t *dev_handle = dhara_priv_data->parent_handle; + 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_dhara_priv_data_t *dhara_priv_data = __containerof(n, spi_nand_flash_dhara_priv_data_t, dhara_nand); + spi_nand_flash_device_t *dev_handle = dhara_priv_data->parent_handle; + 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_dhara_priv_data_t *dhara_priv_data = __containerof(n, spi_nand_flash_dhara_priv_data_t, dhara_nand); + spi_nand_flash_device_t *dev_handle = dhara_priv_data->parent_handle; + 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_dhara_priv_data_t *dhara_priv_data = __containerof(n, spi_nand_flash_dhara_priv_data_t, dhara_nand); + spi_nand_flash_device_t *dev_handle = dhara_priv_data->parent_handle; + 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..38256b5485 100644 --- a/spi_nand_flash/src/nand.c +++ b/spi_nand_flash/src/nand.c @@ -3,173 +3,21 @@ * * 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 "nand_impl.h" #include "nand.h" #include "nand_flash_devices.h" +#include "nand_flash_chip.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; -} - static esp_err_t detect_chip(spi_nand_flash_device_t *dev) { uint8_t manufacturer_id; @@ -228,24 +76,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 +101,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(nand_register_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 +126,11 @@ 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, ""); + ret = handle->ops->erase_chip(handle); + if (ret) { + goto end; } - - // 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); + handle->ops->deinit(handle); end: xSemaphoreGive(handle->mutex); @@ -303,119 +141,109 @@ 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, uint32_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, uint32_t src_sec, uint32_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, uint32_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, uint32_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, uint32_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_size(spi_nand_flash_device_t *handle, uint32_t *block_size) +{ + *block_size = handle->chip.block_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_deinit_device(spi_nand_flash_device_t *handle) { + nand_unregister_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_impl.c b/spi_nand_flash/src/nand_impl.c new file mode 100644 index 0000000000..bcd3f89a0c --- /dev/null +++ b/spi_nand_flash/src/nand_impl.c @@ -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 +#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, uint32_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, uint32_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; +} + +esp_err_t nand_erase_block(spi_nand_flash_device_t *handle, uint32_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, uint32_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, uint32_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, uint32_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, uint32_t src, uint32_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/src/nand_impl_wrap.c b/spi_nand_flash/src/nand_impl_wrap.c new file mode 100644 index 0000000000..202e3d28e5 --- /dev/null +++ b/spi_nand_flash/src/nand_impl_wrap.c @@ -0,0 +1,84 @@ +/* + * SPDX-FileCopyrightText: 2015-2024 Espressif Systems (Shanghai) CO LTD + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#include +#include "esp_check.h" +#include "esp_err.h" +#include "spi_nand_flash.h" +#include "nand.h" +#include "nand_impl.h" + +esp_err_t nand_wrap_is_bad(spi_nand_flash_device_t *handle, uint32_t block, bool *is_bad_status) +{ + esp_err_t ret = ESP_OK; + xSemaphoreTake(handle->mutex, portMAX_DELAY); + ret = nand_is_bad(handle, block, is_bad_status); + xSemaphoreGive(handle->mutex); + return ret; +} + +esp_err_t nand_wrap_mark_bad(spi_nand_flash_device_t *handle, uint32_t block) +{ + esp_err_t ret = ESP_OK; + xSemaphoreTake(handle->mutex, portMAX_DELAY); + ret = nand_mark_bad(handle, block); + xSemaphoreGive(handle->mutex); + return ret; +} + +esp_err_t nand_wrap_erase_chip(spi_nand_flash_device_t *handle) +{ + esp_err_t ret = ESP_OK; + xSemaphoreTake(handle->mutex, portMAX_DELAY); + ret = nand_erase_chip(handle); + xSemaphoreGive(handle->mutex); + return ret; +} + +esp_err_t nand_wrap_erase_block(spi_nand_flash_device_t *handle, uint32_t block) +{ + esp_err_t ret = ESP_OK; + xSemaphoreTake(handle->mutex, portMAX_DELAY); + ret = nand_erase_block(handle, block); + xSemaphoreGive(handle->mutex); + return ret; +} + +esp_err_t nand_wrap_prog(spi_nand_flash_device_t *handle, uint32_t page, const uint8_t *data) +{ + esp_err_t ret = ESP_OK; + xSemaphoreTake(handle->mutex, portMAX_DELAY); + ret = nand_prog(handle, page, data); + xSemaphoreGive(handle->mutex); + return ret; +} + +esp_err_t nand_wrap_is_free(spi_nand_flash_device_t *handle, uint32_t page, bool *is_free_status) +{ + esp_err_t ret = ESP_OK; + xSemaphoreTake(handle->mutex, portMAX_DELAY); + ret = nand_is_free(handle, page, is_free_status); + xSemaphoreGive(handle->mutex); + return ret; +} + +esp_err_t nand_wrap_read(spi_nand_flash_device_t *handle, uint32_t page, size_t offset, size_t length, uint8_t *data) +{ + esp_err_t ret = ESP_OK; + xSemaphoreTake(handle->mutex, portMAX_DELAY); + ret = nand_read(handle, page, offset, length, data); + xSemaphoreGive(handle->mutex); + return ret; +} + +esp_err_t nand_wrap_copy(spi_nand_flash_device_t *handle, uint32_t src, uint32_t dst) +{ + esp_err_t ret = ESP_OK; + xSemaphoreTake(handle->mutex, portMAX_DELAY); + ret = nand_copy(handle, src, dst); + xSemaphoreGive(handle->mutex); + return ret; +} 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/test_app/main/CMakeLists.txt b/spi_nand_flash/test_app/main/CMakeLists.txt index 1b170933ac..19ccf2195d 100644 --- a/spi_nand_flash/test_app/main/CMakeLists.txt +++ b/spi_nand_flash/test_app/main/CMakeLists.txt @@ -8,7 +8,6 @@ else() endif() idf_component_register(SRCS ${src} - PRIV_INCLUDE_DIRS . PRIV_REQUIRES ${priv_reqs} WHOLE_ARCHIVE ) 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..b0be05ad64 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 "nand_private/nand_impl_wrap.h" #include "unity.h" #include "soc/spi_pins.h" #include "sdkconfig.h" @@ -210,3 +211,76 @@ 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 = 15; + if (test_block < sector_num) { + bool is_bad_status = false; + // Verify if test_block is not bad block + TEST_ESP_OK(nand_wrap_is_bad(nand_flash_device_handle, test_block, &is_bad_status)); + TEST_ASSERT_TRUE(is_bad_status == false); + // mark test_block as a bad block + TEST_ESP_OK(nand_wrap_mark_bad(nand_flash_device_handle, test_block)); + // Verify if test_block is marked as bad block + TEST_ESP_OK(nand_wrap_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); +} + +TEST_CASE("verify nand_prog, nand_read, nand_copy, nand_is_free 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, block_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)); + TEST_ESP_OK(spi_nand_flash_get_block_size(nand_flash_device_handle, &block_size)); + printf("Number of sectors: %" PRIu32 ", Sector size: %" PRIu32 "\n", sector_num, sector_size); + + uint8_t *pattern_buf = (uint8_t *)heap_caps_malloc(sector_size, MALLOC_CAP_DEFAULT); + TEST_ASSERT_NOT_NULL(pattern_buf); + uint8_t *temp_buf = (uint8_t *)heap_caps_malloc(sector_size, MALLOC_CAP_DEFAULT); + TEST_ASSERT_NOT_NULL(temp_buf); + + fill_buffer(PATTERN_SEED, pattern_buf, sector_size / sizeof(uint32_t)); + + bool is_page_free = true; + uint32_t test_block = 20; + uint32_t test_page = test_block * (block_size / sector_size); //(block_num * pages_per_block) + uint32_t dst_page = test_page + 1; + if (test_page < sector_num) { + // Verify if test_page is free + TEST_ESP_OK(nand_wrap_is_free(nand_flash_device_handle, test_page, &is_page_free)); + TEST_ASSERT_TRUE(is_page_free == true); + // Write/program test_page + TEST_ESP_OK(nand_wrap_prog(nand_flash_device_handle, test_page, pattern_buf)); + // Verify if test_page is used/programmed + TEST_ESP_OK(nand_wrap_is_free(nand_flash_device_handle, test_page, &is_page_free)); + TEST_ASSERT_TRUE(is_page_free == false); + // read test_page and verify with pattern_buf + TEST_ESP_OK(nand_wrap_read(nand_flash_device_handle, test_page, 0, sector_size, temp_buf)); + check_buffer(PATTERN_SEED, temp_buf, sector_size / sizeof(uint32_t)); + // Copy test_page to dst_page + TEST_ESP_OK(nand_wrap_copy(nand_flash_device_handle, test_page, dst_page)); + // read dst_page and verify with pattern_buf + TEST_ESP_OK(nand_wrap_read(nand_flash_device_handle, dst_page, 0, sector_size, temp_buf)); + check_buffer(PATTERN_SEED, temp_buf, sector_size / sizeof(uint32_t)); + } + free(pattern_buf); + free(temp_buf); + deinit_nand_flash(nand_flash_device_handle, spi); +}