From e2d3adc52718735fa9b8525b965d52f5b4dee3ba Mon Sep 17 00:00:00 2001 From: Abhishek Sahu Date: Tue, 7 Nov 2017 13:09:31 +0530 Subject: [PATCH] mtd: nand: qcom: reorganize read page error handling MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Following are the major issues in current implementation for checking the read errors 1. For checking the erased CW, NAND_ERASED_CW_DETECT_STATUS is being read inside qpic_nand_check_status. The qpic_nand_check_status will be called after complete page read so reading status register won’t help in getting the register value after each CW reads. 2. The mtd layer expects the driver to return non-negative integer representing the maximum number of bitflips that were corrected on any one ecc region. The mtd layer takes care of returning EUCLEAN based on returned number. 3. mtd->ecc_stats is only applicable when ECC engine is doing ECC correction. For raw reads, the stats should not be incremented. Now the changes have been done to reorganize the error handling 1. schedule the NAND_ERASED_CW_DETECT_STATUS reading after every CW read and check the same if ECC engine generates uncorrectable error. 2. For raw read, the ECC engine will never generate the uncorrectable error or erased CW so check only NAND_FLASH_STATUS. 3. The qpic_nand_read_oob should return the maximum number of bitflips that were corrected on any one ecc region so introduce the max_bitflips for maintaining the same. 4. The read should return the complete data in case of BADMSG so move the BADMSG check in the main read function. Change-Id: Ibef56294ace00d7cd67b501f623fb1d3aeb2c6ec Signed-off-by: Abhishek Sahu --- .../include/asm/arch-qca-common/qpic_nand.h | 11 ++ drivers/mtd/nand/qpic_nand.c | 125 ++++++++++-------- 2 files changed, 83 insertions(+), 53 deletions(-) diff --git a/arch/arm/include/asm/arch-qca-common/qpic_nand.h b/arch/arm/include/asm/arch-qca-common/qpic_nand.h index ef12180023..3ec31a2cfd 100644 --- a/arch/arm/include/asm/arch-qca-common/qpic_nand.h +++ b/arch/arm/include/asm/arch-qca-common/qpic_nand.h @@ -130,6 +130,8 @@ #define NAND_ERASED_CW_DETECT_STATUS_PAGE_ALL_ERASED 7 #define NAND_ERASED_CW_DETECT_STATUS_CODEWORD_ALL_ERASED 6 #define NAND_ERASED_CW_DETECT_STATUS_CODEWORD_ERASED 4 +#define NAND_CW_ERASED (BIT(NAND_ERASED_CW_DETECT_STATUS_CODEWORD_ERASED) | \ + BIT(NAND_ERASED_CW_DETECT_STATUS_CODEWORD_ALL_ERASED)) #define NAND_ERASED_CW_DETECT_CFG_RESET_CTRL 1 #define NAND_ERASED_CW_DETECT_CFG_ACTIVATE_CTRL 0 @@ -159,6 +161,8 @@ #define PROG_ERASE_OP_RESULT (1 << 7) +/* NAND buffer status */ +#define NAND_BUFFER_UNCORRECTABLE (1 << 8) #define NUM_ERRORS_MASK 0x0000001f #define NUM_ERRORS(i) ((i) << 0) @@ -404,6 +408,12 @@ struct qpic_nand_init_config struct qpic_nand_bam_pipes pipes; }; +struct read_stats { + uint32_t flash_sts; + uint32_t buffer_sts; + uint32_t erased_cw_sts; +}; + struct qpic_nand_dev { unsigned id; unsigned type; @@ -433,6 +443,7 @@ struct qpic_nand_dev { unsigned char *zero_page; unsigned char *zero_oob; uint16_t timing_mode_support; + struct read_stats stats[QPIC_NAND_MAX_CWS_IN_PAGE]; }; void qpic_nand_init(void); diff --git a/drivers/mtd/nand/qpic_nand.c b/drivers/mtd/nand/qpic_nand.c index 8d0dc10447..7534ba8bd7 100644 --- a/drivers/mtd/nand/qpic_nand.c +++ b/drivers/mtd/nand/qpic_nand.c @@ -171,48 +171,52 @@ qpic_nand_erased_status_reset(struct cmd_element *cmd_list_ptr, uint8_t flags) qpic_nand_wait_for_cmd_exec(1); } -static nand_result_t -qpic_nand_check_status(struct mtd_info *mtd, uint32_t status) +static int +qpic_nand_check_read_status(struct mtd_info *mtd, struct read_stats *stats) { - uint32_t erase_sts; + uint32_t status = stats->flash_sts; /* Check for errors */ - if (status & NAND_FLASH_ERR) { + if (!(status & NAND_FLASH_ERR)) { + uint32_t corrected = stats->buffer_sts & NUM_ERRORS_MASK; + mtd->ecc_stats.corrected += corrected; + return corrected; + } + + if (status & NAND_FLASH_MPU_ERR) + return -EPERM; + + if (status & NAND_FLASH_TIMEOUT_ERR) + return -ETIMEDOUT; + + if (stats->buffer_sts & NAND_BUFFER_UNCORRECTABLE) { /* Check if this is an ECC error on an erased page. */ - if (status & NAND_FLASH_OP_ERR) { - erase_sts = qpic_nand_read_reg( - NAND_ERASED_CW_DETECT_STATUS, 0); - if ((erase_sts & - (1 << NAND_ERASED_CW_DETECT_STATUS_PAGE_ALL_ERASED))) { - /* Mask the OP ERROR. */ - status &= ~NAND_FLASH_OP_ERR; - qpic_nand_erased_status_reset(ce_array, 0); - } - } - - /* ECC error flagged on an erased page read. - * Ignore and return success. - */ - if (!(status & NAND_FLASH_ERR)) - return NANDC_RESULT_SUCCESS; - - printf("Nand Flash error. Status = %d\n", status); - - if (status & NAND_FLASH_OP_ERR) { - mtd->ecc_stats.failed++; + if ((stats->erased_cw_sts & NAND_CW_ERASED) != NAND_CW_ERASED) return -EBADMSG; - } + + return 0; + } + + return -EIO; +} + +static int +qpic_nand_check_status(struct mtd_info *mtd, uint32_t status) +{ + /* Check for errors */ + if (status & NAND_FLASH_ERR) { + printf("Nand Flash error. Status = %d\n", status); if (status & NAND_FLASH_MPU_ERR) return -EPERM; if (status & NAND_FLASH_TIMEOUT_ERR) return -ETIMEDOUT; - else - return NANDC_RESULT_FAILURE; + + return -EIO; } - return NANDC_RESULT_SUCCESS; + return 0; } static uint32_t @@ -1594,8 +1598,7 @@ qpic_nand_read_page(struct mtd_info *mtd, uint32_t page, struct qpic_nand_dev *dev = MTD_QPIC_NAND_DEV(mtd); struct cfg_params params; uint32_t ecc; - uint32_t flash_sts[QPIC_NAND_MAX_CWS_IN_PAGE]; - uint32_t buffer_sts[QPIC_NAND_MAX_CWS_IN_PAGE]; + struct read_stats *stats = dev->stats; uint32_t addr_loc_0; uint32_t addr_loc_1; struct cmd_element *cmd_list_ptr = ce_array; @@ -1606,12 +1609,12 @@ qpic_nand_read_page(struct mtd_info *mtd, uint32_t page, int nand_ret = NANDC_RESULT_SUCCESS; uint8_t flags = 0; uint32_t *cmd_list_temp = NULL; - uint32_t num_errors; uint16_t data_bytes; uint16_t ud_bytes_in_last_cw; uint16_t oob_bytes; unsigned char *buffer; unsigned char *spareaddr; + unsigned int max_bitflips = 0; params.addr0 = page << 16; params.addr1 = (page >> 16) & 0xff; @@ -1735,7 +1738,7 @@ qpic_nand_read_page(struct mtd_info *mtd, uint32_t page, num_cmd_desc++; bam_add_cmd_element(cmd_list_ptr, NAND_FLASH_STATUS, - (uint32_t)((addr_t)&(flash_sts[i])), + (uint32_t)((addr_t)&(stats[i].flash_sts)), CE_READ_TYPE); cmd_list_temp = (uint32_t *)cmd_list_ptr; @@ -1743,10 +1746,15 @@ qpic_nand_read_page(struct mtd_info *mtd, uint32_t page, cmd_list_ptr++; bam_add_cmd_element(cmd_list_ptr, NAND_BUFFER_STATUS, - (uint32_t)((addr_t)&(buffer_sts[i])), + (uint32_t)((addr_t)&(stats[i].buffer_sts)), CE_READ_TYPE); cmd_list_ptr++; + bam_add_cmd_element(cmd_list_ptr, NAND_ERASED_CW_DETECT_STATUS, + (uint32_t)((addr_t)&(stats[i].erased_cw_sts)), + CE_READ_TYPE); + cmd_list_ptr++; + if (i == (dev->cws_per_page) - 1) { flags = BAM_DESC_CMD_FLAG | BAM_DESC_UNLOCK_FLAG; } else @@ -1794,22 +1802,31 @@ qpic_nand_read_page(struct mtd_info *mtd, uint32_t page, /* Check status */ for (i = 0; i < (dev->cws_per_page) ; i ++) { - flash_sts[i] = qpic_nand_check_status(mtd, flash_sts[i]); - if (flash_sts[i]) { - printf("NAND page read failed. page: %x status %x\n", - page, flash_sts[i]); + if (cfg_mode == NAND_CFG_RAW) + nand_ret = qpic_nand_check_status(mtd, + stats[i].flash_sts); + else + nand_ret = qpic_nand_check_read_status(mtd, &stats[i]); + + if (nand_ret < 0) { + if (nand_ret == -EBADMSG) { + mtd->ecc_stats.failed++; + continue; + } + goto qpic_nand_read_page_error; } + + max_bitflips = max_t(unsigned int, max_bitflips, nand_ret); } - for (i = 0; i < (dev->cws_per_page); i++) { - num_errors = buffer_sts[i]; - num_errors &= NUM_ERRORS_MASK; - if(num_errors) - mtd->ecc_stats.corrected++; - } + + return max_bitflips; + qpic_nand_read_page_error: -return nand_ret; + printf("NAND page read failed. page: %x status %x\n", + page, nand_ret); + return nand_ret; } static int qpic_nand_read_oob(struct mtd_info *mtd, loff_t to, @@ -1820,8 +1837,9 @@ static int qpic_nand_read_oob(struct mtd_info *mtd, loff_t to, struct nand_chip *chip = MTD_NAND_CHIP(mtd); uint32_t start_page; uint32_t num_pages; - uint32_t corrected; enum nand_cfg_value cfg_mode; + unsigned int max_bitflips = 0; + unsigned int ecc_failures = mtd->ecc_stats.failed; /* We don't support MTD_OOB_PLACE as of yet. */ if (ops->mode == MTD_OPS_PLACE_OOB) @@ -1848,8 +1866,6 @@ static int qpic_nand_read_oob(struct mtd_info *mtd, loff_t to, start_page = ((to >> chip->page_shift)); num_pages = qpic_get_read_page_count(mtd, ops); - corrected = mtd->ecc_stats.corrected; - for (i = 0; i < num_pages; i++) { struct mtd_oob_ops page_ops; page_ops.mode = ops->mode; @@ -1862,21 +1878,24 @@ static int qpic_nand_read_oob(struct mtd_info *mtd, loff_t to, ret = qpic_nand_read_page(mtd, start_page + i, cfg_mode, &page_ops); - if (ret) { + if (ret < 0) { printf("%s: reading page %d failed with %d err\n", __func__, start_page + i, ret); return ret; } + + max_bitflips = max_t(unsigned int, max_bitflips, ret); qpic_nand_read_datcopy(mtd, ops); qpic_nand_read_oobcopy(mtd, ops); } - if (mtd->ecc_stats.corrected != corrected) { - ret = -EUCLEAN; - return ret; + if (ecc_failures != mtd->ecc_stats.failed) { + printf("%s: ecc failure while reading from %llx\n", + __func__, to); + return -EBADMSG; } - return NANDC_RESULT_SUCCESS; + return max_bitflips; } /**