summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorStefan Agner <stefan.agner@toradex.com>2014-04-07 14:47:42 +0200
committerStefan Agner <stefan@agner.ch>2014-05-15 14:01:06 +0200
commit2d8f62e795944ddc719623028484b28354ba185d (patch)
tree592c7220cef667363841164873b9548cfe74e300
parent2eacc6f1101d197259cd6cb8200aaa8918905532 (diff)
fsl_nfc: check hardware ECC result
Hardware ECC was enabled, however the result of the ECC check was actually ignored. This patch checks the result and acts accordingly. Also, enable ECC hardware for every page (which is required for UBIFS, this might be a relict of JFFS2 support). The driver passes the mtd_pagetest. Bits an pieces taken from the RFC mainline driver from Bill Pringlemeir.
-rw-r--r--drivers/mtd/nand/fsl_nfc.c72
1 files changed, 49 insertions, 23 deletions
diff --git a/drivers/mtd/nand/fsl_nfc.c b/drivers/mtd/nand/fsl_nfc.c
index 8def52a718e0..5f95b46b1a30 100644
--- a/drivers/mtd/nand/fsl_nfc.c
+++ b/drivers/mtd/nand/fsl_nfc.c
@@ -424,19 +424,8 @@ fsl_nfc_command(struct mtd_info *mtd, unsigned command,
CONFIG_ECC_MODE_MASK,
CONFIG_ECC_MODE_SHIFT, ECC_BYPASS);
- if (!(page%0x40)) {
- nfc_set_field(mtd, NFC_FLASH_CONFIG,
- CONFIG_ECC_MODE_MASK,
- CONFIG_ECC_MODE_SHIFT, ECC_BYPASS);
- }
-
switch (command) {
case NAND_CMD_PAGEPROG:
- if (!(prv->page%0x40))
- nfc_set_field(mtd, NFC_FLASH_CONFIG,
- CONFIG_ECC_MODE_MASK,
- CONFIG_ECC_MODE_SHIFT, ECC_BYPASS);
-
fsl_nfc_send_cmd(mtd,
PROGRAM_PAGE_CMD_BYTE1,
PROGRAM_PAGE_CMD_BYTE2,
@@ -664,25 +653,55 @@ fsl_nfc_read_word(struct mtd_info *mtd)
return tmp;
}
-#if 0
-static void fsl_nfc_check_ecc_status(struct mtd_info *mtd)
+static int nfc_calculate_ecc(struct mtd_info *mtd, const u_char *dat,
+ u_char *ecc_code)
+{
+ return 0;
+}
+
+/* Count the number of 0's in buff upto max_bits */
+static int count_written_bits(uint8_t *buff, int size, int max_bits)
+{
+ int k, written_bits = 0;
+
+ for (k = 0; k < size; k++) {
+ written_bits += hweight8(~buff[k]);
+ if (written_bits > max_bits)
+ break;
+ }
+
+ return written_bits;
+}
+
+static int fsl_nfc_check_ecc_status(struct mtd_info *mtd, u_char *dat)
{
struct nand_chip *chip = mtd->priv;
struct fsl_nfc_prv *prv = chip->priv;
- u8 ecc_status, ecc_count;
+ u32 ecc_status;
+ u8 ecc_count;
+ int flip;
- ecc_status = *(u8 *)(prv->regs + ECC_SRAM_ADDR * 8 + 7);
+ /*
+ * ECC status is stored at NFC_CFG[ECCADD] +4 for
+ * little-endian and +7 for big-endian SOC. Access as 32 bits
+ * and use low byte.
+ */
+ ecc_status = __raw_readl(prv->regs + ECC_SRAM_ADDR * 8 + 4);
ecc_count = ecc_status & ECC_ERR_COUNT;
- if (ecc_status & ECC_STATUS_MASK) {
- /*mtd->ecc_stats.failed++;*/
- printk("ECC failed to correct all errors!\n");
- } else if (ecc_count) {
- /*mtd->ecc_stats.corrected += ecc_count;*/
- printk(KERN_INFO"ECC corrected %d errors\n", ecc_count);
+ if (!(ecc_status & ECC_STATUS_MASK))
+ return ecc_count;
+
+ /* If 'ecc_count' zero or less then buffer is all 0xff or erased. */
+ flip = count_written_bits(dat, chip->ecc.size, ecc_count);
+
+
+ if (flip > ecc_count) {
+ printk("ECC failed to correct all errors (%08x)\n", ecc_status);
+ return -1;
}
+ return 0;
}
-#endif
static void
copy_from_to_spare(struct mtd_info *mtd, void *pbuf, int len, int wr)
@@ -749,12 +768,19 @@ static int fsl_nfc_write_oob(struct mtd_info *mtd, struct nand_chip *chip,
static int fsl_nfc_read_page(struct mtd_info *mtd, struct nand_chip *chip,
uint8_t *buf, int page)
{
+ int stat;
struct fsl_nfc_prv *prv = chip->priv;
- /*fsl_nfc_check_ecc_status(mtd);*/
memcpy_fromio((void *)buf, prv->regs + NFC_MAIN_AREA(0),
mtd->writesize);
copy_from_to_spare(mtd, chip->oob_poi, mtd->oobsize, 0);
+
+ stat = fsl_nfc_check_ecc_status(mtd, buf);
+ if (stat < 0)
+ mtd->ecc_stats.failed++;
+ else
+ mtd->ecc_stats.corrected += stat;
+
return 0;
}