[PATCH v9 13/18] mtd: rawnand: sunxi: reuse ECC status within each transfer
From: James Hilliard
Date: Sat Oct 17 2026 - 15:53:24 EST
A completed DMA batch leaves one ECC status bitmap and one pattern
bitmap for all its steps. Four consecutive steps also share a packed
correction-counter register. Reading the pattern ID for every matching
step and the counter word for every corrected step repeats MMIO accesses
to the same completed result.
Snapshot status and pattern information once per completed transfer.
Reuse the status read when the controller places pattern-found bits in
the same register, and read the pattern ID only when a non-error step
needs it. Use a cached accessor so a reader can request pattern IDs
without knowing whether the initial snapshot already fetched them.
Cache each packed counter word while consuming its steps.
Keep the snapshot local to a single transfer and invalidate the cached
counter before processing it. Consume lazily read fields before starting
another ECC transfer; each PIO step and DMA batch gets a fresh snapshot.
The ECC decisions and returned correction counts are unchanged, as is
the page layout; only redundant register reads are removed.
Signed-off-by: James Hilliard <james.hilliard1@xxxxxxxxx>
---
drivers/mtd/nand/raw/sunxi_nand.c | 108 ++++++++++++++++++++++++++++----------
1 file changed, 80 insertions(+), 28 deletions(-)
diff --git a/drivers/mtd/nand/raw/sunxi_nand.c b/drivers/mtd/nand/raw/sunxi_nand.c
index 69e92fe21040..a12ec60c63b4 100644
--- a/drivers/mtd/nand/raw/sunxi_nand.c
+++ b/drivers/mtd/nand/raw/sunxi_nand.c
@@ -258,6 +258,7 @@ struct sunxi_nfc_timings {
};
struct sunxi_nfc_ecc_read;
+struct sunxi_nfc_ecc_snapshot;
/* OOB geometry and interpretation; callbacks may issue supplementary reads. */
struct sunxi_nfc_oob_ops {
@@ -273,7 +274,7 @@ struct sunxi_nfc_oob_ops {
* Return zero on success or a negative I/O error.
*/
int (*read_batch)(struct nand_chip *nand, struct sunxi_nfc_ecc_read *read,
- u32 status, u32 pattern_found, int first_step, int nsteps);
+ struct sunxi_nfc_ecc_snapshot *snapshot, int first_step, int nsteps);
/*
* Finish remaining OOB; return max bitflips or a negative I/O error.
* cur_off is an absolute NAND column, or NULL if unknown.
@@ -1155,23 +1156,82 @@ struct sunxi_nfc_ecc_read {
bool has_erased_chunk;
};
+/*
+ * Transfer-local ECC result cache. Pattern IDs and correction counts may be
+ * read lazily, so finish consuming them before the next ECC transfer.
+ */
+struct sunxi_nfc_ecc_snapshot {
+ u32 status;
+ u32 pattern_found;
+ u32 pattern_id;
+ u32 count_reg;
+ u32 count;
+ bool pattern_id_valid;
+};
+
+static void sunxi_nfc_hw_ecc_read_pattern_id(struct nand_chip *nand,
+ struct sunxi_nfc_ecc_snapshot *snapshot)
+{
+ struct sunxi_nfc *nfc = to_sunxi_nfc(nand->controller);
+
+ /* Zero is a valid pattern ID, so track whether it has been read. */
+ if (snapshot->pattern_id_valid)
+ return;
+
+ snapshot->pattern_id = readl(nfc->regs + NFC_REG_PAT_ID(nfc));
+ snapshot->pattern_id_valid = true;
+}
+
+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_valid = false;
+ if (snapshot->pattern_found & ~snapshot->status)
+ sunxi_nfc_hw_ecc_read_pattern_id(nand, snapshot);
+ /* 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 step)
+{
+ struct sunxi_nfc *nfc = to_sunxi_nfc(nand->controller);
+ u32 reg = NFC_REG_ECC_ERR_CNT(nfc, 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(step, snapshot->count);
+}
+
static int sunxi_nfc_hw_ecc_correct(struct nand_chip *nand, u8 *data, u8 *oob,
- int step, u32 status, u32 pattern_found,
+ int 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(step))
+ if (snapshot->status & NFC_ECC_ERR(step))
return -EBADMSG;
- if (pattern_found & BIT(step)) {
+ if (snapshot->pattern_found & BIT(step)) {
u8 pattern;
- if (unlikely(!(readl(nfc->regs + NFC_REG_PAT_ID(nfc)) & BIT(step)))) {
+ if (unlikely(!(snapshot->pattern_id & BIT(step)))) {
pattern = 0x0;
} else {
pattern = 0xff;
@@ -1187,9 +1247,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, step));
-
- return NFC_ECC_ERR_CNT(step, tmp);
+ return sunxi_nfc_hw_ecc_read_count(nand, snapshot, step);
}
/*
@@ -1258,7 +1316,7 @@ static int sunxi_nfc_hw_ecc_read_extra_oob(struct nand_chip *nand,
/* Interpret one PIO step or a completed DMA batch before reusing its status. */
static int sunxi_nfc_oob_read_batch(struct nand_chip *nand,
struct sunxi_nfc_ecc_read *read,
- u32 status, u32 pattern_found,
+ struct sunxi_nfc_ecc_snapshot *snapshot,
int first_step, int nsteps)
{
struct sunxi_nand_chip *sunxi_nand = to_sunxi_nand(nand);
@@ -1281,8 +1339,7 @@ static int sunxi_nfc_oob_read_batch(struct nand_chip *nand,
bitflips = sunxi_nfc_hw_ecc_correct(nand,
!read->dma || randomized ? data : NULL,
read->oob_required ? oob : NULL,
- i, status, pattern_found,
- user_data_sz, &erased);
+ i, snapshot, user_data_sz, &erased);
if (bitflips < 0) {
/* Preserve PIO data before the raw erased-page check. */
if (!read->dma && !randomized)
@@ -1291,7 +1348,7 @@ static int sunxi_nfc_oob_read_batch(struct nand_chip *nand,
}
/* Both patterns are already filled; only all-ones is erased. */
- if (pattern_found & BIT(i)) {
+ if (snapshot->pattern_found & BIT(i)) {
read->has_erased_chunk |= erased;
continue;
}
@@ -1327,7 +1384,7 @@ static int sunxi_nfc_oob_read_batch(struct nand_chip *nand,
sunxi_nfc_hw_ecc_update_stats(nand, &read->max_bitflips, bitflips);
}
- if (!(status & NFC_ECC_ERR_MSK(nfc)))
+ if (!(snapshot->status & NFC_ECC_ERR_MSK(nfc)))
return 0;
/* Finish consuming the batch's corrected OOB before raw error rereads. */
@@ -1335,7 +1392,7 @@ static int sunxi_nfc_oob_read_batch(struct nand_chip *nand,
int step = first_step + i;
int data_off, oob_off;
- if (!(status & NFC_ECC_ERR(i)))
+ if (!(snapshot->status & NFC_ECC_ERR(i)))
continue;
data_off = step * ecc->size;
@@ -1377,10 +1434,10 @@ static int sunxi_nfc_hw_ecc_read_chunk(struct nand_chip *nand,
unsigned int user_data_sz = sunxi_nfc_user_data_sz(sunxi_nand, step);
struct sunxi_nfc *nfc = to_sunxi_nfc(nand->controller);
struct nand_ecc_ctrl *ecc = &nand->ecc;
+ struct sunxi_nfc_ecc_snapshot snapshot;
int data_off = step * ecc->size;
int oob_off = nand_to_mtd(nand)->writesize + sunxi_get_oob_offset(sunxi_nand, ecc, step);
int page = read->page;
- u32 pattern_found;
int ret;
if (*cur_off != data_off) {
@@ -1416,12 +1473,9 @@ 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);
- return sunxi_nand->oob_ops->read_batch(nand, read,
- readl(nfc->regs + NFC_REG_ECC_ST),
- pattern_found, step, 1);
+ return sunxi_nand->oob_ops->read_batch(nand, read, &snapshot, step, 1);
}
static int sunxi_nfc_hw_ecc_read_batch_dma(struct nand_chip *nand,
@@ -1432,10 +1486,11 @@ static int sunxi_nfc_hw_ecc_read_batch_dma(struct nand_chip *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_snapshot snapshot;
struct scatterlist sg;
int page = read->page;
int ret, i;
- u32 status, pattern_found, wait;
+ u32 wait;
if (first_step) {
ret = sunxi_nfc_read_column(nand, page, first_step * ecc->size,
@@ -1491,12 +1546,9 @@ static int sunxi_nfc_hw_ecc_read_batch_dma(struct nand_chip *nand,
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);
- return sunxi_nand->oob_ops->read_batch(nand, read, status, pattern_found,
- first_step, nchunks);
+ return sunxi_nand->oob_ops->read_batch(nand, read, &snapshot, first_step, nchunks);
}
static int sunxi_nfc_hw_ecc_read_chunks_dma(struct nand_chip *nand, u8 *buf,
--
2.53.0