Commit 04459d7c authored by David Woodhouse's avatar David Woodhouse

[MTD] NAND: Add hardware ECC correction support to CAFÉ NAND driver

Signed-off-by: default avatarDavid Woodhouse <dwmw2@infradead.org>
parent c45aa055
...@@ -29,12 +29,20 @@ ...@@ -29,12 +29,20 @@
#define CAFE_NAND_TIMING2 0x28 #define CAFE_NAND_TIMING2 0x28
#define CAFE_NAND_TIMING3 0x2c #define CAFE_NAND_TIMING3 0x2c
#define CAFE_NAND_NONMEM 0x30 #define CAFE_NAND_NONMEM 0x30
#define CAFE_NAND_ECC_RESULT 0x3C
#define CAFE_NAND_ECC_SYN01 0x50
#define CAFE_NAND_ECC_SYN23 0x54
#define CAFE_NAND_ECC_SYN45 0x58
#define CAFE_NAND_ECC_SYN67 0x5c
#define CAFE_NAND_DMA_CTRL 0x40 #define CAFE_NAND_DMA_CTRL 0x40
#define CAFE_NAND_DMA_ADDR0 0x44 #define CAFE_NAND_DMA_ADDR0 0x44
#define CAFE_NAND_DMA_ADDR1 0x48 #define CAFE_NAND_DMA_ADDR1 0x48
#define CAFE_NAND_READ_DATA 0x1000 #define CAFE_NAND_READ_DATA 0x1000
#define CAFE_NAND_WRITE_DATA 0x2000 #define CAFE_NAND_WRITE_DATA 0x2000
int cafe_correct_ecc(unsigned char *buf,
unsigned short *chk_syndrome_list);
struct cafe_priv { struct cafe_priv {
struct nand_chip nand; struct nand_chip nand;
struct pci_dev *pdev; struct pci_dev *pdev;
...@@ -50,7 +58,7 @@ struct cafe_priv { ...@@ -50,7 +58,7 @@ struct cafe_priv {
}; };
static int usedma = 1; static int usedma = 0;
module_param(usedma, int, 0644); module_param(usedma, int, 0644);
static int skipbbt = 0; static int skipbbt = 0;
...@@ -59,6 +67,7 @@ module_param(skipbbt, int, 0644); ...@@ -59,6 +67,7 @@ module_param(skipbbt, int, 0644);
static int debug = 0; static int debug = 0;
module_param(debug, int, 0644); module_param(debug, int, 0644);
/* Hrm. Why isn't this already conditional on something in the struct device? */
#define cafe_dev_dbg(dev, args...) do { if (debug) dev_dbg(dev, ##args); } while(0) #define cafe_dev_dbg(dev, args...) do { if (debug) dev_dbg(dev, ##args); } while(0)
...@@ -203,9 +212,11 @@ static void cafe_nand_cmdfunc(struct mtd_info *mtd, unsigned command, ...@@ -203,9 +212,11 @@ static void cafe_nand_cmdfunc(struct mtd_info *mtd, unsigned command,
writel(cafe->ctl2 | 0x100 | NAND_CMD_READSTART, cafe->mmio + CAFE_NAND_CTRL2); writel(cafe->ctl2 | 0x100 | NAND_CMD_READSTART, cafe->mmio + CAFE_NAND_CTRL2);
do_command: do_command:
#if 0
// ECC on read only works if we ... // ECC on read only works if we ...
// if (cafe->datalen == 2112) if (cafe->datalen == 2112)
// cafe->datalen = 2062; cafe->datalen = 2062;
#endif
cafe_dev_dbg(&cafe->pdev->dev, "dlen %x, ctl1 %x, ctl2 %x\n", cafe_dev_dbg(&cafe->pdev->dev, "dlen %x, ctl1 %x, ctl2 %x\n",
cafe->datalen, ctl1, readl(cafe->mmio+CAFE_NAND_CTRL2)); cafe->datalen, ctl1, readl(cafe->mmio+CAFE_NAND_CTRL2));
/* NB: The datasheet lies -- we really should be subtracting 1 here */ /* NB: The datasheet lies -- we really should be subtracting 1 here */
...@@ -311,8 +322,6 @@ static int cafe_nand_write_oob(struct mtd_info *mtd, ...@@ -311,8 +322,6 @@ static int cafe_nand_write_oob(struct mtd_info *mtd,
{ {
int status = 0; int status = 0;
WARN_ON(chip->oob_poi != chip->buffers->oobwbuf);
chip->cmdfunc(mtd, NAND_CMD_SEQIN, mtd->writesize, page); chip->cmdfunc(mtd, NAND_CMD_SEQIN, mtd->writesize, page);
chip->write_buf(mtd, chip->oob_poi, mtd->oobsize); chip->write_buf(mtd, chip->oob_poi, mtd->oobsize);
chip->cmdfunc(mtd, NAND_CMD_PAGEPROG, -1, -1); chip->cmdfunc(mtd, NAND_CMD_PAGEPROG, -1, -1);
...@@ -343,13 +352,33 @@ static int cafe_nand_read_page(struct mtd_info *mtd, struct nand_chip *chip, ...@@ -343,13 +352,33 @@ static int cafe_nand_read_page(struct mtd_info *mtd, struct nand_chip *chip,
{ {
struct cafe_priv *cafe = mtd->priv; struct cafe_priv *cafe = mtd->priv;
WARN_ON(chip->oob_poi != chip->buffers->oobrbuf); dev_dbg(&cafe->pdev->dev, "ECC result %08x SYN1,2 %08x\n",
readl(cafe->mmio + CAFE_NAND_ECC_RESULT),
cafe_dev_dbg(&cafe->pdev->dev, "ECC result %08x SYN1,2 %08x\n", readl(cafe->mmio + 0x3c), readl(cafe->mmio + 0x50)); readl(cafe->mmio + CAFE_NAND_ECC_SYN01));
chip->read_buf(mtd, buf, mtd->writesize); chip->read_buf(mtd, buf, mtd->writesize);
chip->read_buf(mtd, chip->oob_poi, mtd->oobsize); chip->read_buf(mtd, chip->oob_poi, mtd->oobsize);
if (readl(cafe->mmio + CAFE_NAND_ECC_RESULT) & (1<<18)) {
unsigned short syn[8];
int i;
for (i=0; i<8; i+=2) {
uint32_t tmp = readl(cafe->mmio + CAFE_NAND_ECC_SYN01 + (i*2));
syn[i] = tmp & 0xfff;
syn[i+1] = (tmp >> 16) & 0xfff;
}
if ((i = cafe_correct_ecc(buf, syn)) < 0) {
dev_dbg(&cafe->pdev->dev, "Failed to correct ECC\n");
mtd->ecc_stats.failed++;
} else {
dev_dbg(&cafe->pdev->dev, "Corrected %d symbol errors\n", i);
mtd->ecc_stats.corrected += i;
}
}
return 0; return 0;
} }
...@@ -390,14 +419,11 @@ static struct nand_ecclayout cafe_oobinfo_512 = { ...@@ -390,14 +419,11 @@ static struct nand_ecclayout cafe_oobinfo_512 = {
.oobfree = {{14, 2}} .oobfree = {{14, 2}}
}; };
static void cafe_nand_write_page_lowlevel(struct mtd_info *mtd, static void cafe_nand_write_page_lowlevel(struct mtd_info *mtd,
struct nand_chip *chip, const uint8_t *buf) struct nand_chip *chip, const uint8_t *buf)
{ {
struct cafe_priv *cafe = mtd->priv; struct cafe_priv *cafe = mtd->priv;
WARN_ON(chip->oob_poi != chip->buffers->oobwbuf);
chip->write_buf(mtd, buf, mtd->writesize); chip->write_buf(mtd, buf, mtd->writesize);
chip->write_buf(mtd, chip->oob_poi, mtd->oobsize); chip->write_buf(mtd, chip->oob_poi, mtd->oobsize);
...@@ -412,8 +438,6 @@ static int cafe_nand_write_page(struct mtd_info *mtd, struct nand_chip *chip, ...@@ -412,8 +438,6 @@ static int cafe_nand_write_page(struct mtd_info *mtd, struct nand_chip *chip,
{ {
int status; int status;
WARN_ON(chip->oob_poi != chip->buffers->oobwbuf);
chip->cmdfunc(mtd, NAND_CMD_SEQIN, 0x00, page); chip->cmdfunc(mtd, NAND_CMD_SEQIN, 0x00, page);
if (unlikely(raw)) if (unlikely(raw))
......
This diff is collapsed.
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment