forked from Minki/linux
[MTD] [NAND] Use rslib for CAFÉ ECC
Signed-off-by: Segher Boessenkool <segher@kernel.crashing.org> Signed-off-by: David Woodhouse <dwmw2@infradead.org>
This commit is contained in:
parent
d7e5a5462f
commit
8c61b7a7f4
@ -232,11 +232,13 @@ config MTD_NAND_BASLER_EXCITE
|
|||||||
will be named "excite_nandflash.ko".
|
will be named "excite_nandflash.ko".
|
||||||
|
|
||||||
config MTD_NAND_CAFE
|
config MTD_NAND_CAFE
|
||||||
tristate "NAND support for OLPC CAFÉ chip"
|
tristate "NAND support for OLPC CAFÉ chip"
|
||||||
depends on PCI
|
depends on PCI
|
||||||
help
|
select REED_SOLOMON
|
||||||
Use NAND flash attached to the CAFÉ chip designed for the $100
|
select REED_SOLOMON_DEC16
|
||||||
laptop.
|
help
|
||||||
|
Use NAND flash attached to the CAFÉ chip designed for the $100
|
||||||
|
laptop.
|
||||||
|
|
||||||
config MTD_NAND_CS553X
|
config MTD_NAND_CS553X
|
||||||
tristate "NAND support for CS5535/CS5536 (AMD Geode companion chip)"
|
tristate "NAND support for CS5535/CS5536 (AMD Geode companion chip)"
|
||||||
|
@ -28,4 +28,4 @@ obj-$(CONFIG_MTD_NAND_CM_X270) += cmx270_nand.o
|
|||||||
obj-$(CONFIG_MTD_NAND_BASLER_EXCITE) += excite_nandflash.o
|
obj-$(CONFIG_MTD_NAND_BASLER_EXCITE) += excite_nandflash.o
|
||||||
|
|
||||||
nand-objs := nand_base.o nand_bbt.o
|
nand-objs := nand_base.o nand_bbt.o
|
||||||
cafe_nand-objs := cafe.o cafe_ecc.o
|
cafe_nand-objs := cafe.o
|
||||||
|
@ -11,6 +11,7 @@
|
|||||||
#undef DEBUG
|
#undef DEBUG
|
||||||
#include <linux/mtd/mtd.h>
|
#include <linux/mtd/mtd.h>
|
||||||
#include <linux/mtd/nand.h>
|
#include <linux/mtd/nand.h>
|
||||||
|
#include <linux/rslib.h>
|
||||||
#include <linux/pci.h>
|
#include <linux/pci.h>
|
||||||
#include <linux/delay.h>
|
#include <linux/delay.h>
|
||||||
#include <linux/interrupt.h>
|
#include <linux/interrupt.h>
|
||||||
@ -46,13 +47,11 @@
|
|||||||
#define CAFE_GLOBAL_IRQ_MASK 0x300c
|
#define CAFE_GLOBAL_IRQ_MASK 0x300c
|
||||||
#define CAFE_NAND_RESET 0x3034
|
#define CAFE_NAND_RESET 0x3034
|
||||||
|
|
||||||
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;
|
||||||
void __iomem *mmio;
|
void __iomem *mmio;
|
||||||
|
struct rs_control *rs;
|
||||||
uint32_t ctl1;
|
uint32_t ctl1;
|
||||||
uint32_t ctl2;
|
uint32_t ctl2;
|
||||||
int datalen;
|
int datalen;
|
||||||
@ -374,28 +373,66 @@ static int cafe_nand_read_page(struct mtd_info *mtd, struct nand_chip *chip,
|
|||||||
chip->read_buf(mtd, chip->oob_poi, mtd->oobsize);
|
chip->read_buf(mtd, chip->oob_poi, mtd->oobsize);
|
||||||
|
|
||||||
if (checkecc && cafe_readl(cafe, NAND_ECC_RESULT) & (1<<18)) {
|
if (checkecc && cafe_readl(cafe, NAND_ECC_RESULT) & (1<<18)) {
|
||||||
unsigned short syn[8];
|
unsigned short syn[8], pat[4];
|
||||||
int i;
|
int pos[4];
|
||||||
|
u8 *oob = chip->oob_poi;
|
||||||
|
int i, n;
|
||||||
|
|
||||||
for (i=0; i<8; i+=2) {
|
for (i=0; i<8; i+=2) {
|
||||||
uint32_t tmp = cafe_readl(cafe, NAND_ECC_SYN01 + (i*2));
|
uint32_t tmp = cafe_readl(cafe, NAND_ECC_SYN01 + (i*2));
|
||||||
syn[i] = tmp & 0xfff;
|
syn[i] = cafe->rs->index_of[tmp & 0xfff];
|
||||||
syn[i+1] = (tmp >> 16) & 0xfff;
|
syn[i+1] = cafe->rs->index_of[(tmp >> 16) & 0xfff];
|
||||||
}
|
}
|
||||||
|
|
||||||
if ((i = cafe_correct_ecc(buf, syn)) < 0) {
|
n = decode_rs16(cafe->rs, NULL, NULL, 1367, syn, 0, pos, 0,
|
||||||
|
pat);
|
||||||
|
|
||||||
|
for (i = 0; i < n; i++) {
|
||||||
|
int p = pos[i];
|
||||||
|
|
||||||
|
/* The 12-bit symbols are mapped to bytes here */
|
||||||
|
|
||||||
|
if (p > 1374) {
|
||||||
|
/* out of range */
|
||||||
|
n = -1374;
|
||||||
|
} else if (p == 0) {
|
||||||
|
/* high four bits do not correspond to data */
|
||||||
|
if (pat[i] > 0xff)
|
||||||
|
n = -2048;
|
||||||
|
else
|
||||||
|
buf[0] ^= pat[i];
|
||||||
|
} else if (p == 1365) {
|
||||||
|
buf[2047] ^= pat[i] >> 4;
|
||||||
|
oob[0] ^= pat[i] << 4;
|
||||||
|
} else if (p > 1365) {
|
||||||
|
if ((p & 1) == 1) {
|
||||||
|
oob[3*p/2 - 2048] ^= pat[i] >> 4;
|
||||||
|
oob[3*p/2 - 2047] ^= pat[i] << 4;
|
||||||
|
} else {
|
||||||
|
oob[3*p/2 - 2049] ^= pat[i] >> 8;
|
||||||
|
oob[3*p/2 - 2048] ^= pat[i];
|
||||||
|
}
|
||||||
|
} else if ((p & 1) == 1) {
|
||||||
|
buf[3*p/2] ^= pat[i] >> 4;
|
||||||
|
buf[3*p/2 + 1] ^= pat[i] << 4;
|
||||||
|
} else {
|
||||||
|
buf[3*p/2 - 1] ^= pat[i] >> 8;
|
||||||
|
buf[3*p/2] ^= pat[i];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (n < 0) {
|
||||||
dev_dbg(&cafe->pdev->dev, "Failed to correct ECC at %08x\n",
|
dev_dbg(&cafe->pdev->dev, "Failed to correct ECC at %08x\n",
|
||||||
cafe_readl(cafe, NAND_ADDR2) * 2048);
|
cafe_readl(cafe, NAND_ADDR2) * 2048);
|
||||||
for (i=0; i< 0x5c; i+=4)
|
for (i = 0; i < 0x5c; i += 4)
|
||||||
printk("Register %x: %08x\n", i, readl(cafe->mmio + i));
|
printk("Register %x: %08x\n", i, readl(cafe->mmio + i));
|
||||||
mtd->ecc_stats.failed++;
|
mtd->ecc_stats.failed++;
|
||||||
} else {
|
} else {
|
||||||
dev_dbg(&cafe->pdev->dev, "Corrected %d symbol errors\n", i);
|
dev_dbg(&cafe->pdev->dev, "Corrected %d symbol errors\n", n);
|
||||||
mtd->ecc_stats.corrected += i;
|
mtd->ecc_stats.corrected += n;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -525,6 +562,48 @@ static int cafe_nand_block_bad(struct mtd_info *mtd, loff_t ofs, int getchip)
|
|||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* F_2[X]/(X**6+X+1) */
|
||||||
|
static unsigned short __devinit gf64_mul(u8 a, u8 b)
|
||||||
|
{
|
||||||
|
u8 c;
|
||||||
|
unsigned int i;
|
||||||
|
|
||||||
|
c = 0;
|
||||||
|
for (i = 0; i < 6; i++) {
|
||||||
|
if (a & 1)
|
||||||
|
c ^= b;
|
||||||
|
a >>= 1;
|
||||||
|
b <<= 1;
|
||||||
|
if ((b & 0x40) != 0)
|
||||||
|
b ^= 0x43;
|
||||||
|
}
|
||||||
|
|
||||||
|
return c;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* F_64[X]/(X**2+X+A**-1) with A the generator of F_64[X] */
|
||||||
|
static u16 __devinit gf4096_mul(u16 a, u16 b)
|
||||||
|
{
|
||||||
|
u8 ah, al, bh, bl, ch, cl;
|
||||||
|
|
||||||
|
ah = a >> 6;
|
||||||
|
al = a & 0x3f;
|
||||||
|
bh = b >> 6;
|
||||||
|
bl = b & 0x3f;
|
||||||
|
|
||||||
|
ch = gf64_mul(ah ^ al, bh ^ bl) ^ gf64_mul(al, bl);
|
||||||
|
cl = gf64_mul(gf64_mul(ah, bh), 0x21) ^ gf64_mul(al, bl);
|
||||||
|
|
||||||
|
return (ch << 6) ^ cl;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int __devinit cafe_mul(int x)
|
||||||
|
{
|
||||||
|
if (x == 0)
|
||||||
|
return 1;
|
||||||
|
return gf4096_mul(x, 0xe01);
|
||||||
|
}
|
||||||
|
|
||||||
static int __devinit cafe_nand_probe(struct pci_dev *pdev,
|
static int __devinit cafe_nand_probe(struct pci_dev *pdev,
|
||||||
const struct pci_device_id *ent)
|
const struct pci_device_id *ent)
|
||||||
{
|
{
|
||||||
@ -564,6 +643,12 @@ static int __devinit cafe_nand_probe(struct pci_dev *pdev,
|
|||||||
}
|
}
|
||||||
cafe->nand.buffers = (void *)cafe->dmabuf + 2112;
|
cafe->nand.buffers = (void *)cafe->dmabuf + 2112;
|
||||||
|
|
||||||
|
cafe->rs = init_rs_non_canonical(12, &cafe_mul, 0, 1, 8);
|
||||||
|
if (!cafe->rs) {
|
||||||
|
err = -ENOMEM;
|
||||||
|
goto out_ior;
|
||||||
|
}
|
||||||
|
|
||||||
cafe->nand.cmdfunc = cafe_nand_cmdfunc;
|
cafe->nand.cmdfunc = cafe_nand_cmdfunc;
|
||||||
cafe->nand.dev_ready = cafe_device_ready;
|
cafe->nand.dev_ready = cafe_device_ready;
|
||||||
cafe->nand.read_byte = cafe_read_byte;
|
cafe->nand.read_byte = cafe_read_byte;
|
||||||
@ -713,6 +798,7 @@ static void __devexit cafe_nand_remove(struct pci_dev *pdev)
|
|||||||
cafe_writel(cafe, ~1 & cafe_readl(cafe, GLOBAL_IRQ_MASK), GLOBAL_IRQ_MASK);
|
cafe_writel(cafe, ~1 & cafe_readl(cafe, GLOBAL_IRQ_MASK), GLOBAL_IRQ_MASK);
|
||||||
free_irq(pdev->irq, mtd);
|
free_irq(pdev->irq, mtd);
|
||||||
nand_release(mtd);
|
nand_release(mtd);
|
||||||
|
free_rs(cafe->rs);
|
||||||
pci_iounmap(pdev, cafe->mmio);
|
pci_iounmap(pdev, cafe->mmio);
|
||||||
dma_free_coherent(&cafe->pdev->dev, 2112, cafe->dmabuf, cafe->dmaaddr);
|
dma_free_coherent(&cafe->pdev->dev, 2112, cafe->dmabuf, cafe->dmaaddr);
|
||||||
kfree(mtd);
|
kfree(mtd);
|
||||||
|
File diff suppressed because it is too large
Load Diff
Loading…
Reference in New Issue
Block a user