Skip to content

Commit

Permalink
Merge pull request #437 from RathiSonika/feat/nand_flash_add_refresh_…
Browse files Browse the repository at this point in the history
…threshold

feat(spi_nand_flash): Add data refresh threshold for ECC correction
  • Loading branch information
RathiSonika authored Nov 5, 2024
2 parents 3987e36 + 53cfdc9 commit bb6abc0
Show file tree
Hide file tree
Showing 4 changed files with 77 additions and 12 deletions.
37 changes: 29 additions & 8 deletions spi_nand_flash/src/dhara_glue.c
Original file line number Diff line number Diff line change
Expand Up @@ -219,9 +219,31 @@ int dhara_nand_is_free(const struct dhara_nand *n, dhara_page_t p)
return 0;
}

static int is_ecc_error(uint8_t status)
#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)
{
return (status & STAT_ECC1) != 0 && (status & STAT_ECC0) == 0;
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;
}
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;
}
}
return is_ecc_err;
}

int dhara_nand_read(const struct dhara_nand *n, dhara_page_t p, size_t offset, size_t length,
Expand All @@ -235,9 +257,8 @@ int dhara_nand_read(const struct dhara_nand *n, dhara_page_t p, size_t offset, s

ESP_GOTO_ON_ERROR(read_page_and_wait(dev, p, &status), fail, TAG, "");

if (is_ecc_error(status)) {
if (is_ecc_error(dev, status, err)) {
ESP_LOGD(TAG, "read ecc error, page=%"PRIu32"", p);
dhara_set_error(err, DHARA_E_ECC);
return -1;
}

Expand All @@ -261,9 +282,8 @@ int dhara_nand_copy(const struct dhara_nand *n, dhara_page_t src, dhara_page_t d

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

if (is_ecc_error(status)) {
if (is_ecc_error(dev, status, err)) {
ESP_LOGD(TAG, "copy, ecc error");
dhara_set_error(err, DHARA_E_ECC);
return -1;
}

Expand All @@ -286,11 +306,12 @@ int dhara_nand_copy(const struct dhara_nand *n, dhara_page_t src, dhara_page_t d
}
// 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(status)) {

if (is_ecc_error(dev, status, err)) {
ESP_LOGE(TAG, "%s: dst_page=%"PRIu32" read, ecc error", __func__, dst);
dhara_set_error(err, DHARA_E_ECC);
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) {
Expand Down
33 changes: 29 additions & 4 deletions spi_nand_flash/src/nand.c
Original file line number Diff line number Diff line change
Expand Up @@ -145,6 +145,7 @@ static esp_err_t spi_nand_micron_init(spi_nand_flash_device_t *dev)
.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) {
Expand Down Expand Up @@ -227,6 +228,8 @@ 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

Expand Down Expand Up @@ -296,19 +299,41 @@ esp_err_t spi_nand_erase_chip(spi_nand_flash_device_t *handle)
return ret;
}

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) {
min_bits_corrected = 1;
} else if (handle->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) {
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) {
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)
{
dhara_error_t err;
esp_err_t ret = ESP_OK;

xSemaphoreTake(handle->mutex, portMAX_DELAY);

// 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 (err) {
// This indicates a soft ECC error, we rewrite the sector to recover
if (dhara_map_write(&handle->dhara_map, sector_id, handle->read_buffer, &err)) {
ret = ESP_ERR_FLASH_BASE + err;
} else if (handle->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;
}
}
}

Expand Down
18 changes: 18 additions & 0 deletions spi_nand_flash/src/nand.h
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,23 @@ extern "C" {

#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;
Expand All @@ -31,6 +48,7 @@ struct spi_nand_flash_device_t {
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;
};

Expand Down
1 change: 1 addition & 0 deletions spi_nand_flash/src/spi_nand_oper.h
Original file line number Diff line number Diff line change
Expand Up @@ -53,6 +53,7 @@ typedef struct spi_nand_transaction_t spi_nand_transaction_t;
#define STAT_PROGRAM_FAILED 1 << 3
#define STAT_ECC0 1 << 4
#define STAT_ECC1 1 << 5
#define STAT_ECC2 1 << 6

esp_err_t spi_nand_execute_transaction(spi_device_handle_t device, spi_nand_transaction_t *transaction);

Expand Down

0 comments on commit bb6abc0

Please sign in to comment.