|
@@ -1655,21 +1655,95 @@ qcom_nandc_read_cw_raw(struct mtd_info *mtd, struct nand_chip *chip,
|
|
|
return check_flash_errors(host, 1);
|
|
|
}
|
|
|
|
|
|
+/*
|
|
|
+ * Bitflips can happen in erased codewords also so this function counts the
|
|
|
+ * number of 0 in each CW for which ECC engine returns the uncorrectable
|
|
|
+ * error. The page will be assumed as erased if this count is less than or
|
|
|
+ * equal to the ecc->strength for each CW.
|
|
|
+ *
|
|
|
+ * 1. Both DATA and OOB need to be checked for number of 0. The
|
|
|
+ * top-level API can be called with only data buf or OOB buf so use
|
|
|
+ * chip->data_buf if data buf is null and chip->oob_poi if oob buf
|
|
|
+ * is null for copying the raw bytes.
|
|
|
+ * 2. Perform raw read for all the CW which has uncorrectable errors.
|
|
|
+ * 3. For each CW, check the number of 0 in cw_data and usable OOB bytes.
|
|
|
+ * The BBM and spare bytes bit flip won’t affect the ECC so don’t check
|
|
|
+ * the number of bitflips in this area.
|
|
|
+ */
|
|
|
+static int
|
|
|
+check_for_erased_page(struct qcom_nand_host *host, u8 *data_buf,
|
|
|
+ u8 *oob_buf, unsigned long uncorrectable_cws,
|
|
|
+ int page, unsigned int max_bitflips)
|
|
|
+{
|
|
|
+ struct nand_chip *chip = &host->chip;
|
|
|
+ struct mtd_info *mtd = nand_to_mtd(chip);
|
|
|
+ struct nand_ecc_ctrl *ecc = &chip->ecc;
|
|
|
+ u8 *cw_data_buf, *cw_oob_buf;
|
|
|
+ int cw, data_size, oob_size, ret = 0;
|
|
|
+
|
|
|
+ if (!data_buf) {
|
|
|
+ data_buf = chip->data_buf;
|
|
|
+ chip->pagebuf = -1;
|
|
|
+ }
|
|
|
+
|
|
|
+ if (!oob_buf) {
|
|
|
+ oob_buf = chip->oob_poi;
|
|
|
+ chip->pagebuf = -1;
|
|
|
+ }
|
|
|
+
|
|
|
+ for_each_set_bit(cw, &uncorrectable_cws, ecc->steps) {
|
|
|
+ if (cw == (ecc->steps - 1)) {
|
|
|
+ data_size = ecc->size - ((ecc->steps - 1) * 4);
|
|
|
+ oob_size = (ecc->steps * 4) + host->ecc_bytes_hw;
|
|
|
+ } else {
|
|
|
+ data_size = host->cw_data;
|
|
|
+ oob_size = host->ecc_bytes_hw;
|
|
|
+ }
|
|
|
+
|
|
|
+ /* determine starting buffer address for current CW */
|
|
|
+ cw_data_buf = data_buf + (cw * host->cw_data);
|
|
|
+ cw_oob_buf = oob_buf + (cw * ecc->bytes);
|
|
|
+
|
|
|
+ ret = qcom_nandc_read_cw_raw(mtd, chip, cw_data_buf,
|
|
|
+ cw_oob_buf, page, cw);
|
|
|
+ if (ret)
|
|
|
+ return ret;
|
|
|
+
|
|
|
+ /*
|
|
|
+ * make sure it isn't an erased page reported
|
|
|
+ * as not-erased by HW because of a few bitflips
|
|
|
+ */
|
|
|
+ ret = nand_check_erased_ecc_chunk(cw_data_buf, data_size,
|
|
|
+ cw_oob_buf + host->bbm_size,
|
|
|
+ oob_size, NULL,
|
|
|
+ 0, ecc->strength);
|
|
|
+ if (ret < 0) {
|
|
|
+ mtd->ecc_stats.failed++;
|
|
|
+ } else {
|
|
|
+ mtd->ecc_stats.corrected += ret;
|
|
|
+ max_bitflips = max_t(unsigned int, max_bitflips, ret);
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ return max_bitflips;
|
|
|
+}
|
|
|
+
|
|
|
/*
|
|
|
* reads back status registers set by the controller to notify page read
|
|
|
* errors. this is equivalent to what 'ecc->correct()' would do.
|
|
|
*/
|
|
|
static int parse_read_errors(struct qcom_nand_host *host, u8 *data_buf,
|
|
|
- u8 *oob_buf)
|
|
|
+ u8 *oob_buf, int page)
|
|
|
{
|
|
|
struct nand_chip *chip = &host->chip;
|
|
|
struct qcom_nand_controller *nandc = get_qcom_nand_controller(chip);
|
|
|
struct mtd_info *mtd = nand_to_mtd(chip);
|
|
|
struct nand_ecc_ctrl *ecc = &chip->ecc;
|
|
|
- unsigned int max_bitflips = 0;
|
|
|
+ unsigned int max_bitflips = 0, uncorrectable_cws = 0;
|
|
|
struct read_stats *buf;
|
|
|
- bool flash_op_err = false;
|
|
|
+ bool flash_op_err = false, erased;
|
|
|
int i;
|
|
|
+ u8 *data_buf_start = data_buf, *oob_buf_start = oob_buf;
|
|
|
|
|
|
buf = (struct read_stats *)nandc->reg_read_buf;
|
|
|
nandc_read_buffer_sync(nandc, true);
|
|
@@ -1699,10 +1773,6 @@ static int parse_read_errors(struct qcom_nand_host *host, u8 *data_buf,
|
|
|
* codeword detection check will be done.
|
|
|
*/
|
|
|
if ((flash & FS_OP_ERR) && (buffer & BS_UNCORRECTABLE_BIT)) {
|
|
|
- bool erased;
|
|
|
- int ret, ecclen, extraooblen;
|
|
|
- void *eccbuf;
|
|
|
-
|
|
|
/*
|
|
|
* For BCH ECC, ignore erased codeword errors, if
|
|
|
* ERASED_CW bits are set.
|
|
@@ -1723,31 +1793,8 @@ static int parse_read_errors(struct qcom_nand_host *host, u8 *data_buf,
|
|
|
erased = false;
|
|
|
}
|
|
|
|
|
|
- if (erased) {
|
|
|
- data_buf += data_len;
|
|
|
- if (oob_buf)
|
|
|
- oob_buf += oob_len + ecc->bytes;
|
|
|
- continue;
|
|
|
- }
|
|
|
-
|
|
|
- eccbuf = oob_buf ? oob_buf + oob_len : NULL;
|
|
|
- ecclen = oob_buf ? host->ecc_bytes_hw : 0;
|
|
|
- extraooblen = oob_buf ? oob_len : 0;
|
|
|
-
|
|
|
- /*
|
|
|
- * make sure it isn't an erased page reported
|
|
|
- * as not-erased by HW because of a few bitflips
|
|
|
- */
|
|
|
- ret = nand_check_erased_ecc_chunk(data_buf,
|
|
|
- data_len, eccbuf, ecclen, oob_buf,
|
|
|
- extraooblen, ecc->strength);
|
|
|
- if (ret < 0) {
|
|
|
- mtd->ecc_stats.failed++;
|
|
|
- } else {
|
|
|
- mtd->ecc_stats.corrected += ret;
|
|
|
- max_bitflips =
|
|
|
- max_t(unsigned int, max_bitflips, ret);
|
|
|
- }
|
|
|
+ if (!erased)
|
|
|
+ uncorrectable_cws |= BIT(i);
|
|
|
/*
|
|
|
* Check if MPU or any other operational error (timeout,
|
|
|
* device failure, etc.) happened for this codeword and
|
|
@@ -1777,7 +1824,12 @@ static int parse_read_errors(struct qcom_nand_host *host, u8 *data_buf,
|
|
|
if (flash_op_err)
|
|
|
return -EIO;
|
|
|
|
|
|
- return max_bitflips;
|
|
|
+ if (!uncorrectable_cws)
|
|
|
+ return max_bitflips;
|
|
|
+
|
|
|
+ return check_for_erased_page(host, data_buf_start, oob_buf_start,
|
|
|
+ uncorrectable_cws, page,
|
|
|
+ max_bitflips);
|
|
|
}
|
|
|
|
|
|
/*
|
|
@@ -1785,7 +1837,7 @@ static int parse_read_errors(struct qcom_nand_host *host, u8 *data_buf,
|
|
|
* ecc->read_oob()
|
|
|
*/
|
|
|
static int read_page_ecc(struct qcom_nand_host *host, u8 *data_buf,
|
|
|
- u8 *oob_buf)
|
|
|
+ u8 *oob_buf, int page)
|
|
|
{
|
|
|
struct nand_chip *chip = &host->chip;
|
|
|
struct qcom_nand_controller *nandc = get_qcom_nand_controller(chip);
|
|
@@ -1858,7 +1910,7 @@ static int read_page_ecc(struct qcom_nand_host *host, u8 *data_buf,
|
|
|
return ret;
|
|
|
}
|
|
|
|
|
|
- return parse_read_errors(host, data_buf_start, oob_buf_start);
|
|
|
+ return parse_read_errors(host, data_buf_start, oob_buf_start, page);
|
|
|
}
|
|
|
|
|
|
/*
|
|
@@ -1910,7 +1962,7 @@ static int qcom_nandc_read_page(struct mtd_info *mtd, struct nand_chip *chip,
|
|
|
|
|
|
clear_bam_transaction(nandc);
|
|
|
|
|
|
- return read_page_ecc(host, data_buf, oob_buf);
|
|
|
+ return read_page_ecc(host, data_buf, oob_buf, page);
|
|
|
}
|
|
|
|
|
|
/* implements ecc->read_page_raw() */
|
|
@@ -1951,7 +2003,7 @@ static int qcom_nandc_read_oob(struct mtd_info *mtd, struct nand_chip *chip,
|
|
|
set_address(host, 0, page);
|
|
|
update_rw_regs(host, ecc->steps, true);
|
|
|
|
|
|
- return read_page_ecc(host, NULL, chip->oob_poi);
|
|
|
+ return read_page_ecc(host, NULL, chip->oob_poi, page);
|
|
|
}
|
|
|
|
|
|
/* implements ecc->write_page() */
|