[PATCH v3 15/15] mtd: rawnand: sunxi: reuse ECC status within each DMA read
James Hilliard
james.hilliard1 at gmail.com
Wed Sep 9 01:30:48 PDT 2026
The DMA completion loop reads the pattern ID repeatedly and reads the
same packed error-counter word for each of its four ECC steps.
Snapshot the completed operation status and pattern ID, and retain the
last counter word while visiting consecutive DMA steps. Read counters
only for steps which use them. Reuse the shared status register value on
older controllers which place the pattern flags in its upper half.
Keep the existing error and pattern priority in both the plain-marker
and randomized-format paths.
Start a fresh snapshot after every PIO ECC operation so reuse of hardware
slot zero cannot reuse status from a previous step. The snapshot is local
to one callback and does not survive controller resets or DMA-to-PIO
retries.
Signed-off-by: James Hilliard <james.hilliard1 at gmail.com>
---
drivers/mtd/nand/raw/sunxi_nand.c | 100 +++++++++++++++++++++++++-------------
1 file changed, 67 insertions(+), 33 deletions(-)
diff --git a/drivers/mtd/nand/raw/sunxi_nand.c b/drivers/mtd/nand/raw/sunxi_nand.c
index 1c1fcea69108..8710e91ffbaa 100644
--- a/drivers/mtd/nand/raw/sunxi_nand.c
+++ b/drivers/mtd/nand/raw/sunxi_nand.c
@@ -1138,46 +1138,87 @@ struct sunxi_nfc_ecc_status {
unsigned int max_bitflips;
};
+struct sunxi_nfc_ecc_snapshot {
+ u32 status;
+ u32 pattern_found;
+ u32 pattern_id;
+ u32 count_reg;
+ u32 count;
+};
+
+static void sunxi_nfc_hw_ecc_read_status(struct nand_chip *nand,
+ struct sunxi_nfc_ecc_snapshot *snapshot)
+{
+ struct sunxi_nfc *nfc = to_sunxi_nfc(nand->controller);
+ u32 pattern_found;
+
+ pattern_found = readl(nfc->regs + nfc->caps->reg_pat_found);
+ snapshot->pattern_found = field_get(NFC_ECC_PAT_FOUND_MSK(nfc), pattern_found);
+ if (nfc->caps->reg_pat_found == NFC_REG_ECC_ST)
+ snapshot->status = pattern_found;
+ else
+ snapshot->status = readl(nfc->regs + NFC_REG_ECC_ST);
+ snapshot->pattern_id = 0;
+ if (snapshot->pattern_found &&
+ (to_sunxi_nand(nand)->randomized_oob ||
+ (snapshot->pattern_found & ~snapshot->status)))
+ snapshot->pattern_id = readl(nfc->regs + NFC_REG_PAT_ID(nfc));
+ /* A new operation, including every PIO step, invalidates the count word. */
+ snapshot->count_reg = ~0U;
+}
+
+static unsigned int
+sunxi_nfc_hw_ecc_read_count(struct nand_chip *nand,
+ struct sunxi_nfc_ecc_snapshot *snapshot, int hw_step)
+{
+ struct sunxi_nfc *nfc = to_sunxi_nfc(nand->controller);
+ u32 reg = NFC_REG_ECC_ERR_CNT(nfc, hw_step);
+
+ /* Four consecutive DMA steps share one error-counter register. */
+ if (snapshot->count_reg != reg) {
+ snapshot->count = readl(nfc->regs + reg);
+ snapshot->count_reg = reg;
+ }
+
+ return NFC_ECC_ERR_CNT(hw_step, snapshot->count);
+}
+
static void sunxi_nfc_hw_ecc_record_status(struct nand_chip *nand,
struct sunxi_nfc_ecc_status *result,
- int logical_step, int hw_step, u32 status,
- u32 pattern_found)
+ int logical_step, int hw_step,
+ struct sunxi_nfc_ecc_snapshot *snapshot)
{
- struct sunxi_nfc *nfc = to_sunxi_nfc(nand->controller);
u32 count;
- if ((pattern_found & BIT(hw_step)) &&
- !(readl(nfc->regs + NFC_REG_PAT_ID(nfc)) & BIT(hw_step)))
+ if ((snapshot->pattern_found & BIT(hw_step)) &&
+ !(snapshot->pattern_id & BIT(hw_step)))
result->zero_steps |= BIT(logical_step);
- if (status & NFC_ECC_ERR(hw_step)) {
+ if (snapshot->status & NFC_ECC_ERR(hw_step)) {
result->error_steps |= BIT(logical_step);
return;
}
- count = readl(nfc->regs + NFC_REG_ECC_ERR_CNT(nfc, hw_step));
- count = NFC_ECC_ERR_CNT(hw_step, count);
+ count = sunxi_nfc_hw_ecc_read_count(nand, snapshot, hw_step);
result->corrected += count;
result->max_bitflips = max(result->max_bitflips, count);
}
static int sunxi_nfc_hw_ecc_correct(struct nand_chip *nand, u8 *data, u8 *oob,
- int hw_step, u32 status, u32 pattern_found,
+ int hw_step, struct sunxi_nfc_ecc_snapshot *snapshot,
unsigned int user_data_sz, bool *erased)
{
- struct sunxi_nfc *nfc = to_sunxi_nfc(nand->controller);
struct nand_ecc_ctrl *ecc = &nand->ecc;
- u32 tmp;
*erased = false;
- if (status & NFC_ECC_ERR(hw_step))
+ if (snapshot->status & NFC_ECC_ERR(hw_step))
return -EBADMSG;
- if (pattern_found & BIT(hw_step)) {
+ if (snapshot->pattern_found & BIT(hw_step)) {
u8 pattern;
- if (unlikely(!(readl(nfc->regs + NFC_REG_PAT_ID(nfc)) & BIT(hw_step)))) {
+ if (unlikely(!(snapshot->pattern_id & BIT(hw_step)))) {
pattern = 0x0;
} else {
pattern = 0xff;
@@ -1193,9 +1234,7 @@ static int sunxi_nfc_hw_ecc_correct(struct nand_chip *nand, u8 *data, u8 *oob,
return 0;
}
- tmp = readl(nfc->regs + NFC_REG_ECC_ERR_CNT(nfc, hw_step));
-
- return NFC_ECC_ERR_CNT(hw_step, tmp);
+ return sunxi_nfc_hw_ecc_read_count(nand, snapshot, hw_step);
}
static int sunxi_nfc_hw_ecc_read_chunk(struct nand_chip *nand,
@@ -1210,7 +1249,7 @@ static int sunxi_nfc_hw_ecc_read_chunk(struct nand_chip *nand,
struct sunxi_nand_chip *sunxi_nand = to_sunxi_nand(nand);
unsigned int user_data_sz = sunxi_nfc_user_data_sz(sunxi_nand, logical_step);
struct nand_ecc_ctrl *ecc = &nand->ecc;
- u32 pattern_found;
+ struct sunxi_nfc_ecc_snapshot snapshot;
bool bbm = !logical_step;
bool erased;
int ret, bitflips;
@@ -1250,13 +1289,11 @@ static int sunxi_nfc_hw_ecc_read_chunk(struct nand_chip *nand,
*cur_off = oob_off + ecc->bytes + user_data_sz;
- pattern_found = readl(nfc->regs + nfc->caps->reg_pat_found);
- pattern_found = field_get(NFC_ECC_PAT_FOUND_MSK(nfc), pattern_found);
+ sunxi_nfc_hw_ecc_read_status(nand, &snapshot);
if (sunxi_nand->randomized_oob) {
sunxi_nfc_hw_ecc_record_status(nand, result, logical_step, hw_step,
- readl(nfc->regs + NFC_REG_ECC_ST),
- pattern_found);
+ &snapshot);
memcpy_fromio(data, nfc->regs + NFC_RAM0_BASE, ecc->size);
sunxi_nfc_hw_ecc_get_prot_oob_bytes(nand, oob, hw_step, bbm,
page, user_data_sz);
@@ -1264,8 +1301,7 @@ static int sunxi_nfc_hw_ecc_read_chunk(struct nand_chip *nand,
}
bitflips = sunxi_nfc_hw_ecc_correct(nand, data, oob_required ? oob : NULL,
- hw_step, readl(nfc->regs + NFC_REG_ECC_ST),
- pattern_found, user_data_sz, &erased);
+ hw_step, &snapshot, user_data_sz, &erased);
if (erased)
return 1;
@@ -1530,13 +1566,14 @@ static int sunxi_nfc_hw_ecc_read_chunks_dma(struct nand_chip *nand, uint8_t *buf
struct mtd_info *mtd = nand_to_mtd(nand);
struct nand_ecc_ctrl *ecc = &nand->ecc;
struct sunxi_nfc_ecc_status result = {};
+ struct sunxi_nfc_ecc_snapshot snapshot;
unsigned int corrected = mtd->ecc_stats.corrected;
unsigned int failed = mtd->ecc_stats.failed;
unsigned int max_bitflips = 0;
bool erased_chunk_found = false;
int ret, i;
struct scatterlist sg;
- u32 status, pattern_found, wait;
+ u32 wait;
ret = sunxi_nfc_wait_cmd_fifo_empty(nfc);
if (ret)
@@ -1577,9 +1614,7 @@ static int sunxi_nfc_hw_ecc_read_chunks_dma(struct nand_chip *nand, uint8_t *buf
if (ret)
return ret;
- status = readl(nfc->regs + NFC_REG_ECC_ST);
- pattern_found = readl(nfc->regs + nfc->caps->reg_pat_found);
- pattern_found = field_get(NFC_ECC_PAT_FOUND_MSK(nfc), pattern_found);
+ sunxi_nfc_hw_ecc_read_status(nand, &snapshot);
for (i = 0; i < nchunks; i++) {
int data_off = i * ecc->size;
@@ -1591,8 +1626,7 @@ static int sunxi_nfc_hw_ecc_read_chunks_dma(struct nand_chip *nand, uint8_t *buf
int bitflips;
if (sunxi_nand->randomized_oob) {
- sunxi_nfc_hw_ecc_record_status(nand, &result, i, i, status,
- pattern_found);
+ sunxi_nfc_hw_ecc_record_status(nand, &result, i, i, &snapshot);
sunxi_nfc_hw_ecc_get_prot_oob_bytes(nand, oob, i, !i,
page, user_data_sz);
continue;
@@ -1600,7 +1634,7 @@ static int sunxi_nfc_hw_ecc_read_chunks_dma(struct nand_chip *nand, uint8_t *buf
bitflips = sunxi_nfc_hw_ecc_correct(nand, randomized ? data : NULL,
oob_required ? oob : NULL,
- i, status, pattern_found,
+ i, &snapshot,
user_data_sz, &erased);
/* ECC errors are handled in the second loop. */
@@ -1629,7 +1663,7 @@ static int sunxi_nfc_hw_ecc_read_chunks_dma(struct nand_chip *nand, uint8_t *buf
return sunxi_nfc_hw_ecc_finish_randomized_read(nand, buf, &result,
oob_required, true, page);
- if (status & NFC_ECC_ERR_MSK(nfc)) {
+ if (snapshot.status & NFC_ECC_ERR_MSK(nfc)) {
for (i = 0; i < nchunks; i++) {
int data_off = i * ecc->size;
unsigned int user_data_sz = sunxi_nfc_user_data_sz(sunxi_nand, i);
@@ -1637,7 +1671,7 @@ static int sunxi_nfc_hw_ecc_read_chunks_dma(struct nand_chip *nand, uint8_t *buf
u8 *data = buf + data_off;
u8 *oob = nand->oob_poi + oob_off;
- if (!(status & NFC_ECC_ERR(i)))
+ if (!(snapshot.status & NFC_ECC_ERR(i)))
continue;
ret = sunxi_nfc_hw_ecc_read_error(nand, data, data_off, oob,
--
2.53.0
More information about the linux-mtd
mailing list