@@ -1038,7 +1038,10 @@ static void sunxi_nfc_hw_ecc_update_stats(struct nand_chip *nand,
}
}
-/* Return whether the chunk is erased, and account for its ECC result. */
+/*
+ * Return 1 for an erased chunk or 0 for an uncorrectable chunk, with ECC
+ * statistics updated in either case. Negative values report transport errors.
+ */
static int sunxi_nfc_hw_ecc_read_error(struct nand_chip *nand,
u8 *data, int data_off,
u8 *oob, int oob_off,
@@ -1050,10 +1053,16 @@ static int sunxi_nfc_hw_ecc_read_error(struct nand_chip *nand,
int ret;
/* Check the physical representation for bitflips in erased pages. */
- if (nand->options & NAND_NEED_SCRAMBLING)
- nand_change_read_column_op(nand, data_off, data, ecc->size, false);
+ if (nand->options & NAND_NEED_SCRAMBLING) {
+ ret = nand_change_read_column_op(nand, data_off, data,
+ ecc->size, false);
+ if (ret)
+ return ret;
+ }
- nand_change_read_column_op(nand, oob_off, oob, oob_len, false);
+ ret = nand_change_read_column_op(nand, oob_off, oob, oob_len, false);
+ if (ret)
+ return ret;
ret = nand_check_erased_ecc_chunk(data, ecc->size, oob, oob_len, NULL, 0,
ecc->strength);
@@ -1317,6 +1326,7 @@ static int sunxi_nfc_hw_ecc_read_chunks_dma(struct nand_chip *nand, u8 *buf,
{
struct sunxi_nand_chip *sunxi_nand = to_sunxi_nand(nand);
struct sunxi_nfc *nfc = to_sunxi_nfc(nand->controller);
+ struct mtd_info *mtd = nand_to_mtd(nand);
struct nand_ecc_ctrl *ecc = &nand->ecc;
struct sunxi_nfc_ecc_read read = {
.buf = buf,
@@ -1324,6 +1334,8 @@ static int sunxi_nfc_hw_ecc_read_chunks_dma(struct nand_chip *nand, u8 *buf,
.oob_required = oob_required,
.dma = true,
};
+ unsigned int corrected = mtd->ecc_stats.corrected;
+ unsigned int failed = mtd->ecc_stats.failed;
struct scatterlist sg;
int ret, i;
u32 status, pattern_found, wait;
@@ -1375,9 +1387,19 @@ static int sunxi_nfc_hw_ecc_read_chunks_dma(struct nand_chip *nand, u8 *buf,
ret = sunxi_nand->oob_ops->read_batch(nand, &read, status, pattern_found,
0, nchunks);
if (ret)
- return ret;
+ goto err_stats;
+
+ ret = sunxi_nand->oob_ops->finish_read(nand, &read, NULL);
+ if (ret < 0)
+ goto err_stats;
+
+ return ret;
- return sunxi_nand->oob_ops->finish_read(nand, &read, NULL);
+err_stats:
+ /* The caller retries the whole read in PIO mode. */
+ mtd->ecc_stats.corrected = corrected;
+ mtd->ecc_stats.failed = failed;
+ return ret;
}
static int sunxi_nfc_hw_ecc_write_chunk(struct nand_chip *nand,
@@ -1465,7 +1487,9 @@ static int sunxi_nfc_hw_ecc_read_page(struct nand_chip *nand, uint8_t *buf,
sunxi_nfc_select_chip(nand, nand->cur_cs);
- nand_read_page_op(nand, page, 0, NULL, 0);
+ ret = nand_read_page_op(nand, page, 0, NULL, 0);
+ if (ret)
+ return ret;
sunxi_nfc_hw_ecc_enable(nand);
@@ -1473,10 +1497,11 @@ static int sunxi_nfc_hw_ecc_read_page(struct nand_chip *nand, uint8_t *buf,
for (i = 0; i < ecc->steps; i++) {
ret = sunxi_nfc_hw_ecc_read_chunk(nand, &read, i, &cur_off);
if (ret < 0)
- return ret;
+ goto out;
}
ret = sunxi_nand->oob_ops->finish_read(nand, &read, &cur_off);
+out:
sunxi_nfc_hw_ecc_disable(nand);
return ret;
@@ -1489,7 +1514,9 @@ static int sunxi_nfc_hw_ecc_read_page_dma(struct nand_chip *nand, u8 *buf,
sunxi_nfc_select_chip(nand, nand->cur_cs);
- nand_read_page_op(nand, page, 0, NULL, 0);
+ ret = nand_read_page_op(nand, page, 0, NULL, 0);
+ if (ret)
+ return ret;
ret = sunxi_nfc_hw_ecc_read_chunks_dma(nand, buf, oob_required, page,
nand->ecc.steps);
@@ -1514,7 +1541,9 @@ static int sunxi_nfc_hw_ecc_read_subpage(struct nand_chip *nand,
sunxi_nfc_select_chip(nand, nand->cur_cs);
- nand_read_page_op(nand, page, 0, NULL, 0);
+ ret = nand_read_page_op(nand, page, 0, NULL, 0);
+ if (ret)
+ return ret;
sunxi_nfc_hw_ecc_enable(nand);
@@ -1523,10 +1552,11 @@ static int sunxi_nfc_hw_ecc_read_subpage(struct nand_chip *nand,
i < DIV_ROUND_UP(data_offs + readlen, ecc->size); i++) {
ret = sunxi_nfc_hw_ecc_read_chunk(nand, &read, i, &cur_off);
if (ret < 0)
- return ret;
+ goto out;
}
ret = read.max_bitflips;
+out:
sunxi_nfc_hw_ecc_disable(nand);
return ret;
@@ -1541,7 +1571,9 @@ static int sunxi_nfc_hw_ecc_read_subpage_dma(struct nand_chip *nand,
sunxi_nfc_select_chip(nand, nand->cur_cs);
- nand_read_page_op(nand, page, 0, NULL, 0);
+ ret = nand_read_page_op(nand, page, 0, NULL, 0);
+ if (ret)
+ return ret;
ret = sunxi_nfc_hw_ecc_read_chunks_dma(nand, buf, false, page, nchunks);
if (ret >= 0)