@@ -1150,46 +1150,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;
@@ -1205,9 +1246,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,
@@ -1222,7 +1261,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;
@@ -1262,13 +1301,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);
@@ -1276,8 +1313,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;
@@ -1542,13 +1578,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)
@@ -1589,9 +1626,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;
@@ -1603,8 +1638,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;
@@ -1612,7 +1646,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. */
@@ -1641,7 +1675,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);
@@ -1649,7 +1683,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,