@@ -258,6 +258,7 @@ struct sunxi_nfc_timings {
};
struct sunxi_nfc_ecc_read;
+struct sunxi_nfc_ecc_snapshot;
/* OOB interpretation is independent of PIO/DMA command sequencing. */
struct sunxi_nfc_oob_ops {
@@ -268,7 +269,7 @@ struct sunxi_nfc_oob_ops {
void (*transform_bbm)(struct nand_chip *nand, int page, u8 *bbm);
/* Consume one PIO step or a DMA batch; negative values are I/O errors. */
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);
/* Read any remaining OOB and return the maximum correction count. */
int (*finish_read)(struct nand_chip *nand, struct sunxi_nfc_ecc_read *read,
int *cur_off);
@@ -1143,23 +1144,64 @@ struct sunxi_nfc_ecc_read {
bool has_erased_chunk;
};
+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 & ~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 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;
@@ -1175,9 +1217,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);
}
/*
@@ -1246,7 +1286,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);
@@ -1269,8 +1309,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)
@@ -1279,7 +1318,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;
}
@@ -1310,7 +1349,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. */
@@ -1318,7 +1357,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;
@@ -1360,10 +1399,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) {
@@ -1399,12 +1438,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,
@@ -1415,10 +1451,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,
@@ -1474,12 +1511,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,