Skip to content

Commit

Permalink
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
feat(spi_nand_flash): expose lower-level API and make it usable witho…
Browse files Browse the repository at this point in the history
…ut Dhara
RathiSonika committed Nov 11, 2024
1 parent d402e9c commit fe2657f
Showing 17 changed files with 962 additions and 549 deletions.
6 changes: 6 additions & 0 deletions spi_nand_flash/CMakeLists.txt
Original file line number Diff line number Diff line change
@@ -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})
2 changes: 1 addition & 1 deletion spi_nand_flash/idf_component.yml
Original file line number Diff line number Diff line change
@@ -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
36 changes: 29 additions & 7 deletions spi_nand_flash/include/spi_nand_flash.h
Original file line number Diff line number Diff line change
@@ -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.
76 changes: 76 additions & 0 deletions spi_nand_flash/priv_include/nand.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,76 @@
/*
* SPDX-FileCopyrightText: 2022 mikkeldamsgaard project
*
* SPDX-License-Identifier: Apache-2.0
*
* SPDX-FileContributor: 2015-2024 Espressif Systems (Shanghai) CO LTD
*/

#pragma once

#include <stdint.h>
#include "spi_nand_flash.h"
#include "freertos/semphr.h"

#ifdef __cplusplus
extern "C" {
#endif

#define INVALID_PAGE 0xFFFF

typedef enum {
STAT_ECC_OK = 0,
STAT_ECC_1_TO_3_BITS_CORRECTED = 1,
STAT_ECC_BITS_CORRECTED = STAT_ECC_1_TO_3_BITS_CORRECTED,
STAT_ECC_NOT_CORRECTED = 2,
STAT_ECC_4_TO_6_BITS_CORRECTED = 3,
STAT_ECC_MAX_BITS_CORRECTED = STAT_ECC_4_TO_6_BITS_CORRECTED,
STAT_ECC_7_8_BITS_CORRECTED = 5,
STAT_ECC_MAX
} ecc_status_t;

typedef struct {
uint8_t ecc_status_reg_len_in_bits;
uint8_t ecc_data_refresh_threshold;
ecc_status_t ecc_corrected_bits_status;
} ecc_data_t;

typedef struct {
uint8_t log2_page_size; //is power of 2, log2_page_size shift (1<<log2_page_size) is stored to page_size
uint8_t log2_ppb; //is power of 2, log2_ppb shift ((1<<log2_ppb) * page_size) will be stored in block size
uint32_t block_size;
uint32_t page_size;
uint32_t num_blocks;
uint32_t read_page_delay_us;
uint32_t erase_block_delay_us;
uint32_t program_page_delay_us;
ecc_data_t ecc_data;
} spi_nand_chip_t;

typedef struct {
esp_err_t (*init)(spi_nand_flash_device_t *handle);
esp_err_t (*deinit)(spi_nand_flash_device_t *handle);
esp_err_t (*read)(spi_nand_flash_device_t *handle, uint8_t *buffer, sector_t sector_id);
esp_err_t (*write)(spi_nand_flash_device_t *handle, const uint8_t *buffer, sector_t sector_id);
esp_err_t (*trim)(spi_nand_flash_device_t *handle, sector_t sector_id);
esp_err_t (*sync)(spi_nand_flash_device_t *handle);
esp_err_t (*copy_sector)(spi_nand_flash_device_t *handle, sector_t src_sec, sector_t dst_sec);
esp_err_t (*get_capacity)(spi_nand_flash_device_t *handle, sector_t *number_of_sectors);
} spi_nand_ops;

struct spi_nand_flash_device_t {
uint8_t dev_index;
spi_nand_flash_config_t config;
spi_nand_chip_t chip;
const spi_nand_ops *ops;
uint8_t *work_buffer;
uint8_t *read_buffer;
SemaphoreHandle_t mutex;
};

esp_err_t register_nand_dev(spi_nand_flash_device_t *handle);
esp_err_t unregister_nand_dev(spi_nand_flash_device_t *handle);

#ifdef __cplusplus
}
#endif
30 changes: 30 additions & 0 deletions spi_nand_flash/priv_include/spi_nand_api.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
/*
* SPDX-FileCopyrightText: 2022 mikkeldamsgaard project
*
* SPDX-License-Identifier: Apache-2.0
*
* SPDX-FileContributor: 2015-2023 Espressif Systems (Shanghai) CO LTD
*/

#pragma once

#include <stdint.h>
#include "esp_err.h"
#include "nand.h"

#ifdef __cplusplus
extern "C" {
#endif

esp_err_t nand_is_bad(spi_nand_flash_device_t *handle, block_t b, bool *is_bad_status);
esp_err_t nand_mark_bad(spi_nand_flash_device_t *handle, block_t b);
esp_err_t nand_erase_chip(spi_nand_flash_device_t *handle);
esp_err_t nand_erase_block(spi_nand_flash_device_t *handle, block_t b);
esp_err_t nand_prog(spi_nand_flash_device_t *handle, page_t p, const uint8_t *data);
esp_err_t nand_is_free(spi_nand_flash_device_t *handle, page_t p, bool *is_free_status);
esp_err_t nand_read(spi_nand_flash_device_t *handle, page_t p, size_t offset, size_t length, uint8_t *data);
esp_err_t nand_copy(spi_nand_flash_device_t *handle, page_t src, page_t dst);

#ifdef __cplusplus
}
#endif
File renamed without changes.
419 changes: 172 additions & 247 deletions spi_nand_flash/src/dhara_glue.c

Large diffs are not rendered by default.

300 changes: 67 additions & 233 deletions spi_nand_flash/src/nand.c

Large diffs are not rendered by default.

59 changes: 0 additions & 59 deletions spi_nand_flash/src/nand.h

This file was deleted.

56 changes: 56 additions & 0 deletions spi_nand_flash/src/nand_alliance.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,56 @@
/*
* SPDX-FileCopyrightText: 2015-2024 Espressif Systems (Shanghai) CO LTD
*
* SPDX-License-Identifier: Apache-2.0
*/

#include <string.h>
#include "esp_check.h"
#include "nand.h"
#include "spi_nand_oper.h"
#include "nand_flash_devices.h"

static const char *TAG = "nand_alliance";

esp_err_t spi_nand_alliance_init(spi_nand_flash_device_t *dev)
{
uint8_t device_id;
spi_nand_transaction_t t = {
.command = CMD_READ_ID,
.address = 1,
.address_bytes = 1,
.dummy_bits = 8,
.miso_len = 1,
.miso_data = &device_id,
.flags = SPI_TRANS_USE_RXDATA,
};
spi_nand_execute_transaction(dev->config.device_handle, &t);
dev->chip.erase_block_delay_us = 3000;
dev->chip.program_page_delay_us = 630;
ESP_LOGD(TAG, "%s: device_id: %x\n", __func__, device_id);
switch (device_id) {
case ALLIANCE_DI_25: //AS5F31G04SND-08LIN
dev->chip.num_blocks = 1024;
dev->chip.read_page_delay_us = 60;
break;
case ALLIANCE_DI_2E: //AS5F32G04SND-08LIN
case ALLIANCE_DI_8E: //AS5F12G04SND-10LIN
dev->chip.num_blocks = 2048;
dev->chip.read_page_delay_us = 60;
break;
case ALLIANCE_DI_2F: //AS5F34G04SND-08LIN
case ALLIANCE_DI_8F: //AS5F14G04SND-10LIN
dev->chip.num_blocks = 4096;
dev->chip.read_page_delay_us = 60;
break;
case ALLIANCE_DI_2D: //AS5F38G04SND-08LIN
case ALLIANCE_DI_8D: //AS5F18G04SND-10LIN
dev->chip.log2_page_size = 12; // 4k pages
dev->chip.num_blocks = 4096;
dev->chip.read_page_delay_us = 130; // somewhat slower reads
break;
default:
return ESP_ERR_INVALID_RESPONSE;
}
return ESP_OK;
}
53 changes: 53 additions & 0 deletions spi_nand_flash/src/nand_gigadevice.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
/*
* SPDX-FileCopyrightText: 2015-2024 Espressif Systems (Shanghai) CO LTD
*
* SPDX-License-Identifier: Apache-2.0
*/

#include <string.h>
#include "esp_check.h"
#include "nand.h"
#include "spi_nand_oper.h"
#include "nand_flash_devices.h"

static const char *TAG = "nand_gigadevice";

esp_err_t spi_nand_gigadevice_init(spi_nand_flash_device_t *dev)
{
uint8_t device_id;
spi_nand_transaction_t t = {
.command = CMD_READ_ID,
.dummy_bits = 16,
.miso_len = 1,
.miso_data = &device_id,
.flags = SPI_TRANS_USE_RXDATA,
};
spi_nand_execute_transaction(dev->config.device_handle, &t);
dev->chip.read_page_delay_us = 25;
dev->chip.erase_block_delay_us = 3200;
dev->chip.program_page_delay_us = 380;
ESP_LOGD(TAG, "%s: device_id: %x\n", __func__, device_id);
switch (device_id) {
case GIGADEVICE_DI_51:
case GIGADEVICE_DI_41:
case GIGADEVICE_DI_31:
case GIGADEVICE_DI_21:
dev->chip.num_blocks = 1024;
break;
case GIGADEVICE_DI_52:
case GIGADEVICE_DI_42:
case GIGADEVICE_DI_32:
case GIGADEVICE_DI_22:
dev->chip.num_blocks = 2048;
break;
case GIGADEVICE_DI_55:
case GIGADEVICE_DI_45:
case GIGADEVICE_DI_35:
case GIGADEVICE_DI_25:
dev->chip.num_blocks = 4096;
break;
default:
return ESP_ERR_INVALID_RESPONSE;
}
return ESP_OK;
}
49 changes: 49 additions & 0 deletions spi_nand_flash/src/nand_micron.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
/*
* SPDX-FileCopyrightText: 2015-2024 Espressif Systems (Shanghai) CO LTD
*
* SPDX-License-Identifier: Apache-2.0
*/

#include <string.h>
#include "esp_check.h"
#include "nand.h"
#include "spi_nand_oper.h"
#include "nand_flash_devices.h"

static const char *TAG = "nand_micron";

esp_err_t spi_nand_micron_init(spi_nand_flash_device_t *dev)
{
uint8_t device_id;
spi_nand_transaction_t t = {
.command = CMD_READ_ID,
.dummy_bits = 16,
.miso_len = 1,
.miso_data = &device_id,
.flags = SPI_TRANS_USE_RXDATA,
};
spi_nand_execute_transaction(dev->config.device_handle, &t);
dev->chip.ecc_data.ecc_status_reg_len_in_bits = 3;
dev->chip.erase_block_delay_us = 2000;
ESP_LOGD(TAG, "%s: device_id: %x\n", __func__, device_id);
switch (device_id) {
case MICRON_DI_34:
dev->chip.read_page_delay_us = 115;
dev->chip.program_page_delay_us = 240;
dev->chip.num_blocks = 2048;
dev->chip.log2_ppb = 6; // 64 pages per block
dev->chip.log2_page_size = 12; // 4096 bytes per page
break;
case MICRON_DI_14:
case MICRON_DI_15:
dev->chip.read_page_delay_us = 46;
dev->chip.program_page_delay_us = 220;
dev->chip.num_blocks = 1024;
dev->chip.log2_ppb = 6; // 64 pages per block
dev->chip.log2_page_size = 11; // 2048 bytes per page
break;
default:
return ESP_ERR_INVALID_RESPONSE;
}
return ESP_OK;
}
45 changes: 45 additions & 0 deletions spi_nand_flash/src/nand_winbond.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
/*
* SPDX-FileCopyrightText: 2015-2024 Espressif Systems (Shanghai) CO LTD
*
* SPDX-License-Identifier: Apache-2.0
*/

#include <string.h>
#include "esp_check.h"
#include "nand.h"
#include "spi_nand_oper.h"
#include "nand_flash_devices.h"

static const char *TAG = "nand_winbond";

esp_err_t spi_nand_winbond_init(spi_nand_flash_device_t *dev)
{
uint8_t device_id_buf[2];
spi_nand_transaction_t t = {
.command = CMD_READ_ID,
.dummy_bits = 16,
.miso_len = 2,
.miso_data = device_id_buf,
.flags = SPI_TRANS_USE_RXDATA,
};
spi_nand_execute_transaction(dev->config.device_handle, &t);
uint16_t device_id = (device_id_buf[0] << 8) + device_id_buf[1];
dev->chip.read_page_delay_us = 10;
dev->chip.erase_block_delay_us = 2500;
dev->chip.program_page_delay_us = 320;
ESP_LOGD(TAG, "%s: device_id: %x\n", __func__, device_id);
switch (device_id) {
case WINBOND_DI_AA20:
case WINBOND_DI_BA20:
dev->chip.num_blocks = 512;
break;
case WINBOND_DI_AA21:
case WINBOND_DI_BA21:
case WINBOND_DI_BC21:
dev->chip.num_blocks = 1024;
break;
default:
return ESP_ERR_INVALID_RESPONSE;
}
return ESP_OK;
}
353 changes: 353 additions & 0 deletions spi_nand_flash/src/spi_nand_api.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,353 @@
/*
* SPDX-FileCopyrightText: 2022 mikkeldamsgaard project
*
* SPDX-License-Identifier: Apache-2.0
*
* SPDX-FileContributor: 2015-2024 Espressif Systems (Shanghai) CO LTD
*/

#include <string.h>
#include "esp_check.h"
#include "esp_err.h"
#include "spi_nand_oper.h"
#include "spi_nand_flash.h"
#include "nand.h"

#define ROM_WAIT_THRESHOLD_US 1000

static const char *TAG = "spi_nand";

#if CONFIG_NAND_FLASH_VERIFY_WRITE
static esp_err_t s_verify_write(spi_nand_flash_device_t *handle, const uint8_t *expected_buffer, uint16_t offset, uint16_t length)
{
uint8_t *temp_buf = NULL;
temp_buf = heap_caps_malloc(length, MALLOC_CAP_DMA | MALLOC_CAP_8BIT);
ESP_RETURN_ON_FALSE(temp_buf != NULL, ESP_ERR_NO_MEM, TAG, "nomem");
if (spi_nand_read(handle->config.device_handle, temp_buf, offset, length)) {
ESP_LOGE(TAG, "%s: Failed to read nand flash to verify previous write", __func__);
free(temp_buf);
return ESP_FAIL;
}

if (memcmp(temp_buf, expected_buffer, length)) {
ESP_LOGE(TAG, "%s: Data mismatch detected. The previously written buffer does not match the read buffer.", __func__);
free(temp_buf);
return ESP_FAIL;
}
free(temp_buf);
return ESP_OK;
}
#endif //CONFIG_NAND_FLASH_VERIFY_WRITE

static esp_err_t wait_for_ready(spi_device_handle_t device, uint32_t expected_operation_time_us, uint8_t *status_out)
{
if (expected_operation_time_us < ROM_WAIT_THRESHOLD_US) {
esp_rom_delay_us(expected_operation_time_us);
}

while (true) {
uint8_t status;
ESP_RETURN_ON_ERROR(spi_nand_read_register(device, REG_STATUS, &status), TAG, "");

if ((status & STAT_BUSY) == 0) {
if (status_out) {
*status_out = status;
}
break;
}

if (expected_operation_time_us >= ROM_WAIT_THRESHOLD_US) {
vTaskDelay(1);
}
}

return ESP_OK;
}

static esp_err_t read_page_and_wait(spi_nand_flash_device_t *dev, uint32_t page, uint8_t *status_out)
{
ESP_RETURN_ON_ERROR(spi_nand_read_page(dev->config.device_handle, page), TAG, "");

return wait_for_ready(dev->config.device_handle, dev->chip.read_page_delay_us, status_out);
}

static esp_err_t program_execute_and_wait(spi_nand_flash_device_t *dev, uint32_t page, uint8_t *status_out)
{
ESP_RETURN_ON_ERROR(spi_nand_program_execute(dev->config.device_handle, page), TAG, "");

return wait_for_ready(dev->config.device_handle, dev->chip.program_page_delay_us, status_out);
}

esp_err_t nand_is_bad(spi_nand_flash_device_t *handle, block_t block, bool *is_bad_status)
{
uint32_t first_block_page = block * (1 << handle->chip.log2_ppb);
uint16_t bad_block_indicator;
esp_err_t ret = ESP_OK;

ESP_GOTO_ON_ERROR(read_page_and_wait(handle, first_block_page, NULL), fail, TAG, "");

// Read the first 2 bytes on the OOB of the first page in the block. This should be 0xFFFF for a good block
ESP_GOTO_ON_ERROR(spi_nand_read(handle->config.device_handle, (uint8_t *) &bad_block_indicator, handle->chip.page_size, 2),
fail, TAG, "");

ESP_LOGD(TAG, "is_bad, block=%"PRIu32", page=%"PRIu32",indicator = %04x", block, first_block_page, bad_block_indicator);
if (bad_block_indicator == 0xFFFF) {
*is_bad_status = false;
} else {
*is_bad_status = true;
}
return ret;

fail:
ESP_LOGE(TAG, "Error in nand_is_bad %d", ret);
return ret;
}

esp_err_t nand_mark_bad(spi_nand_flash_device_t *handle, block_t block)
{
esp_err_t ret = ESP_OK;

uint32_t first_block_page = block * (1 << handle->chip.log2_ppb);
uint16_t bad_block_indicator = 0;
uint8_t status;
ESP_LOGD(TAG, "mark_bad, block=%"PRIu32", page=%"PRIu32",indicator = %04x", block, first_block_page, bad_block_indicator);

ESP_GOTO_ON_ERROR(read_page_and_wait(handle, first_block_page, NULL), fail, TAG, "");
ESP_GOTO_ON_ERROR(spi_nand_write_enable(handle->config.device_handle), fail, TAG, "");
ESP_GOTO_ON_ERROR(spi_nand_erase_block(handle->config.device_handle, first_block_page),
fail, TAG, "");
ESP_GOTO_ON_ERROR(wait_for_ready(handle->config.device_handle, handle->chip.erase_block_delay_us, &status),
fail, TAG, "");
if ((status & STAT_ERASE_FAILED) != 0) {
ret = ESP_ERR_NOT_FINISHED;
goto fail;
}

ESP_GOTO_ON_ERROR(spi_nand_write_enable(handle->config.device_handle), fail, TAG, "");
ESP_GOTO_ON_ERROR(spi_nand_program_load(handle->config.device_handle, (const uint8_t *) &bad_block_indicator,
handle->chip.page_size, 2),
fail, TAG, "");
ESP_GOTO_ON_ERROR(program_execute_and_wait(handle, first_block_page, NULL), fail, TAG, "");

#if CONFIG_NAND_FLASH_VERIFY_WRITE
ret = s_verify_write(handle, (uint8_t *)&bad_block_indicator, handle->chip.page_size, 2);
if (ret != ESP_OK) {
ESP_LOGE(TAG, "%s: mark_bad write verification failed for block=%"PRIu32" and page=%"PRIu32"", __func__, block, first_block_page);
}
#endif //CONFIG_NAND_FLASH_VERIFY_WRITE
return ret;
fail:
ESP_LOGE(TAG, "Error in nand_mark_bad %d", ret);
return ret;
}

esp_err_t nand_erase_chip(spi_nand_flash_device_t *handle)
{
esp_err_t ret = ESP_OK;
uint8_t status;

for (int i = 0; i < handle->chip.num_blocks; i++) {
ESP_GOTO_ON_ERROR(spi_nand_write_enable(handle->config.device_handle), end, TAG, "");
ESP_GOTO_ON_ERROR(spi_nand_erase_block(handle->config.device_handle, i * (1 << handle->chip.log2_ppb)),
end, TAG, "");
ESP_GOTO_ON_ERROR(wait_for_ready(handle->config.device_handle, handle->chip.erase_block_delay_us, &status),
end, TAG, "");
if ((status & STAT_ERASE_FAILED) != 0) {
ret = ESP_ERR_NOT_FINISHED;
}
}
return ret;

end:
ESP_LOGE(TAG, "Error in nand_erase_chip %d", ret);
return ret;
}

int nand_erase_block(spi_nand_flash_device_t *handle, block_t block)
{
ESP_LOGD(TAG, "erase_block, block=%"PRIu32",", block);
esp_err_t ret = ESP_OK;
uint8_t status;

uint32_t first_block_page = block * (1 << handle->chip.log2_ppb);

ESP_GOTO_ON_ERROR(spi_nand_write_enable(handle->config.device_handle), fail, TAG, "");
ESP_GOTO_ON_ERROR(spi_nand_erase_block(handle->config.device_handle, first_block_page),
fail, TAG, "");
ESP_GOTO_ON_ERROR(wait_for_ready(handle->config.device_handle,
handle->chip.erase_block_delay_us, &status),
fail, TAG, "");

if ((status & STAT_ERASE_FAILED) != 0) {
ret = ESP_ERR_NOT_FINISHED;
}
return ret;

fail:
ESP_LOGE(TAG, "Error in nand_erase %d", ret);
return ret;
}

esp_err_t nand_prog(spi_nand_flash_device_t *handle, page_t page, const uint8_t *data)
{
ESP_LOGV(TAG, "prog, page=%"PRIu32",", page);
esp_err_t ret = ESP_OK;
uint16_t used_marker = 0;
uint8_t status;

ESP_GOTO_ON_ERROR(read_page_and_wait(handle, page, NULL), fail, TAG, "");
ESP_GOTO_ON_ERROR(spi_nand_write_enable(handle->config.device_handle), fail, TAG, "");
ESP_GOTO_ON_ERROR(spi_nand_program_load(handle->config.device_handle, data, 0, handle->chip.page_size),
fail, TAG, "");
ESP_GOTO_ON_ERROR(spi_nand_program_load(handle->config.device_handle, (uint8_t *)&used_marker,
handle->chip.page_size + 2, 2),
fail, TAG, "");
ESP_GOTO_ON_ERROR(program_execute_and_wait(handle, page, &status), fail, TAG, "");

if ((status & STAT_PROGRAM_FAILED) != 0) {
ESP_LOGD(TAG, "prog failed, page=%"PRIu32",", page);
return ESP_ERR_NOT_FINISHED;
}

#if CONFIG_NAND_FLASH_VERIFY_WRITE
ret = s_verify_write(handle, data, 0, handle->chip.page_size);
if (ret != ESP_OK) {
ESP_LOGE(TAG, "%s: prog page=%"PRIu32" write verification failed", __func__, page);
}
ret = s_verify_write(handle, (uint8_t *)&used_marker, handle->chip.page_size + 2, 2);
if (ret != ESP_OK) {
ESP_LOGE(TAG, "%s: prog page=%"PRIu32" used marker write verification failed", __func__, page);
}
#endif //CONFIG_NAND_FLASH_VERIFY_WRITE

return ret;
fail:
ESP_LOGE(TAG, "Error in nand_prog %d", ret);
return ret;
}

esp_err_t nand_is_free(spi_nand_flash_device_t *handle, page_t page, bool *is_free_status)
{
esp_err_t ret = ESP_OK;
uint16_t used_marker;

ESP_GOTO_ON_ERROR(read_page_and_wait(handle, page, NULL), fail, TAG, "");
ESP_GOTO_ON_ERROR(spi_nand_read(handle->config.device_handle, (uint8_t *)&used_marker,
handle->chip.page_size + 2, 2),
fail, TAG, "");

ESP_LOGD(TAG, "is free, page=%"PRIu32", used_marker=%04x,", page, used_marker);
if (used_marker == 0xFFFF) {
*is_free_status = true;
} else {
*is_free_status = false;
}
return ret;
fail:
ESP_LOGE(TAG, "Error in nand_is_free %d", ret);
return ret;
}

#define PACK_2BITS_STATUS(status, bit1, bit0) ((((status) & (bit1)) << 1) | ((status) & (bit0)))
#define PACK_3BITS_STATUS(status, bit2, bit1, bit0) ((((status) & (bit2)) << 2) | (((status) & (bit1)) << 1) | ((status) & (bit0)))

static bool is_ecc_error(spi_nand_flash_device_t *dev, uint8_t status)
{
bool is_ecc_err = false;
ecc_status_t bits_corrected_status = STAT_ECC_OK;
if (dev->chip.ecc_data.ecc_status_reg_len_in_bits == 2) {
bits_corrected_status = PACK_2BITS_STATUS(status, STAT_ECC1, STAT_ECC0);
} else if (dev->chip.ecc_data.ecc_status_reg_len_in_bits == 3) {
bits_corrected_status = PACK_3BITS_STATUS(status, STAT_ECC2, STAT_ECC1, STAT_ECC0);
} else {
bits_corrected_status = STAT_ECC_MAX;
}
dev->chip.ecc_data.ecc_corrected_bits_status = bits_corrected_status;
if (bits_corrected_status) {
if (bits_corrected_status == STAT_ECC_MAX) {
ESP_LOGE(TAG, "%s: Error while initializing value of ecc_status_reg_len_in_bits", __func__);
is_ecc_err = true;
} else if (bits_corrected_status == STAT_ECC_NOT_CORRECTED) {
is_ecc_err = true;
}
}
return is_ecc_err;
}

esp_err_t nand_read(spi_nand_flash_device_t *handle, page_t page, size_t offset, size_t length, uint8_t *data)
{
ESP_LOGV(TAG, "read, page=%"PRIu32", offset=%d, length=%d", page, offset, length);
assert(page < handle->chip.num_blocks * (1 << handle->chip.log2_ppb));
esp_err_t ret = ESP_OK;
uint8_t status;

ESP_GOTO_ON_ERROR(read_page_and_wait(handle, page, &status), fail, TAG, "");

if (is_ecc_error(handle, status)) {
ESP_LOGD(TAG, "read ecc error, page=%"PRIu32"", page);
return ESP_FAIL;
}

ESP_GOTO_ON_ERROR(spi_nand_read(handle->config.device_handle, data, offset, length), fail, TAG, "");

return ret;
fail:
ESP_LOGE(TAG, "Error in nand_read %d", ret);
return ret;
}

esp_err_t nand_copy(spi_nand_flash_device_t *handle, page_t src, page_t dst)
{
ESP_LOGD(TAG, "copy, src=%"PRIu32", dst=%"PRIu32"", src, dst);
esp_err_t ret = ESP_OK;
#if CONFIG_NAND_FLASH_VERIFY_WRITE
uint8_t *temp_buf = NULL;
#endif //CONFIG_NAND_FLASH_VERIFY_WRITE

uint8_t status;
ESP_GOTO_ON_ERROR(read_page_and_wait(handle, src, &status), fail, TAG, "");

if (is_ecc_error(handle, status)) {
ESP_LOGD(TAG, "copy, ecc error");
return ESP_FAIL;
}

ESP_GOTO_ON_ERROR(spi_nand_write_enable(handle->config.device_handle), fail, TAG, "");
ESP_GOTO_ON_ERROR(program_execute_and_wait(handle, dst, &status), fail, TAG, "");

if ((status & STAT_PROGRAM_FAILED) != 0) {
ESP_LOGD(TAG, "copy, prog failed");
return ESP_ERR_NOT_FINISHED;
}

#if CONFIG_NAND_FLASH_VERIFY_WRITE
// First read src page data from cache to temp_buf
temp_buf = heap_caps_malloc(handle->chip.page_size, MALLOC_CAP_DMA | MALLOC_CAP_8BIT);
ESP_RETURN_ON_FALSE(temp_buf != NULL, ESP_ERR_NO_MEM, TAG, "nomem");
if (spi_nand_read(handle->config.device_handle, temp_buf, 0, handle->chip.page_size)) {
ESP_LOGE(TAG, "%s: Failed to read src_page=%"PRIu32"", __func__, src);
goto fail;
}
// Then read dst page data from nand memory array and load it in cache
ESP_GOTO_ON_ERROR(read_page_and_wait(handle, dst, &status), fail, TAG, "");
if (is_ecc_error(handle, status)) {
ESP_LOGE(TAG, "%s: dst_page=%"PRIu32" read, ecc error", __func__, dst);
goto fail;
}
// Check if the data in the src page matches the dst page
ret = s_verify_write(handle, temp_buf, 0, handle->chip.page_size);
if (ret != ESP_OK) {
ESP_LOGE(TAG, "%s: dst_page=%"PRIu32" write verification failed", __func__, dst);
}

free(temp_buf);
#endif //CONFIG_NAND_FLASH_VERIFY_WRITE
return ret;

fail:
#if CONFIG_NAND_FLASH_VERIFY_WRITE
free(temp_buf);
#endif //CONFIG_NAND_FLASH_VERIFY_WRITE
ESP_LOGE(TAG, "Error in nand_copy %d", ret);
return ret;
}
2 changes: 1 addition & 1 deletion spi_nand_flash/test_app/main/CMakeLists.txt
Original file line number Diff line number Diff line change
@@ -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
)
2 changes: 1 addition & 1 deletion spi_nand_flash/test_app/main/test_app_main.c
Original file line number Diff line number Diff line change
@@ -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)
23 changes: 23 additions & 0 deletions spi_nand_flash/test_app/main/test_spi_nand_flash.c
Original file line number Diff line number Diff line change
@@ -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, &sector_num));
TEST_ESP_OK(spi_nand_flash_get_sector_size(nand_flash_device_handle, &sector_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);
}

0 comments on commit fe2657f

Please sign in to comment.