aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/mtd/nand/cafe.c
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/mtd/nand/cafe.c')
-rw-r--r--drivers/mtd/nand/cafe.c110
1 files changed, 98 insertions, 12 deletions
diff --git a/drivers/mtd/nand/cafe.c b/drivers/mtd/nand/cafe.c
index c328a7514510..05f6ec9f5aa7 100644
--- a/drivers/mtd/nand/cafe.c
+++ b/drivers/mtd/nand/cafe.c
@@ -11,6 +11,7 @@
11#undef DEBUG 11#undef DEBUG
12#include <linux/mtd/mtd.h> 12#include <linux/mtd/mtd.h>
13#include <linux/mtd/nand.h> 13#include <linux/mtd/nand.h>
14#include <linux/rslib.h>
14#include <linux/pci.h> 15#include <linux/pci.h>
15#include <linux/delay.h> 16#include <linux/delay.h>
16#include <linux/interrupt.h> 17#include <linux/interrupt.h>
@@ -46,13 +47,11 @@
46#define CAFE_GLOBAL_IRQ_MASK 0x300c 47#define CAFE_GLOBAL_IRQ_MASK 0x300c
47#define CAFE_NAND_RESET 0x3034 48#define CAFE_NAND_RESET 0x3034
48 49
49int cafe_correct_ecc(unsigned char *buf,
50 unsigned short *chk_syndrome_list);
51
52struct cafe_priv { 50struct cafe_priv {
53 struct nand_chip nand; 51 struct nand_chip nand;
54 struct pci_dev *pdev; 52 struct pci_dev *pdev;
55 void __iomem *mmio; 53 void __iomem *mmio;
54 struct rs_control *rs;
56 uint32_t ctl1; 55 uint32_t ctl1;
57 uint32_t ctl2; 56 uint32_t ctl2;
58 int datalen; 57 int datalen;
@@ -374,28 +373,66 @@ static int cafe_nand_read_page(struct mtd_info *mtd, struct nand_chip *chip,
374 chip->read_buf(mtd, chip->oob_poi, mtd->oobsize); 373 chip->read_buf(mtd, chip->oob_poi, mtd->oobsize);
375 374
376 if (checkecc && cafe_readl(cafe, NAND_ECC_RESULT) & (1<<18)) { 375 if (checkecc && cafe_readl(cafe, NAND_ECC_RESULT) & (1<<18)) {
377 unsigned short syn[8]; 376 unsigned short syn[8], pat[4];
378 int i; 377 int pos[4];
378 u8 *oob = chip->oob_poi;
379 int i, n;
379 380
380 for (i=0; i<8; i+=2) { 381 for (i=0; i<8; i+=2) {
381 uint32_t tmp = cafe_readl(cafe, NAND_ECC_SYN01 + (i*2)); 382 uint32_t tmp = cafe_readl(cafe, NAND_ECC_SYN01 + (i*2));
382 syn[i] = tmp & 0xfff; 383 syn[i] = cafe->rs->index_of[tmp & 0xfff];
383 syn[i+1] = (tmp >> 16) & 0xfff; 384 syn[i+1] = cafe->rs->index_of[(tmp >> 16) & 0xfff];
385 }
386
387 n = decode_rs16(cafe->rs, NULL, NULL, 1367, syn, 0, pos, 0,
388 pat);
389
390 for (i = 0; i < n; i++) {
391 int p = pos[i];
392
393 /* The 12-bit symbols are mapped to bytes here */
394
395 if (p > 1374) {
396 /* out of range */
397 n = -1374;
398 } else if (p == 0) {
399 /* high four bits do not correspond to data */
400 if (pat[i] > 0xff)
401 n = -2048;
402 else
403 buf[0] ^= pat[i];
404 } else if (p == 1365) {
405 buf[2047] ^= pat[i] >> 4;
406 oob[0] ^= pat[i] << 4;
407 } else if (p > 1365) {
408 if ((p & 1) == 1) {
409 oob[3*p/2 - 2048] ^= pat[i] >> 4;
410 oob[3*p/2 - 2047] ^= pat[i] << 4;
411 } else {
412 oob[3*p/2 - 2049] ^= pat[i] >> 8;
413 oob[3*p/2 - 2048] ^= pat[i];
414 }
415 } else if ((p & 1) == 1) {
416 buf[3*p/2] ^= pat[i] >> 4;
417 buf[3*p/2 + 1] ^= pat[i] << 4;
418 } else {
419 buf[3*p/2 - 1] ^= pat[i] >> 8;
420 buf[3*p/2] ^= pat[i];
421 }
384 } 422 }
385 423
386 if ((i = cafe_correct_ecc(buf, syn)) < 0) { 424 if (n < 0) {
387 dev_dbg(&cafe->pdev->dev, "Failed to correct ECC at %08x\n", 425 dev_dbg(&cafe->pdev->dev, "Failed to correct ECC at %08x\n",
388 cafe_readl(cafe, NAND_ADDR2) * 2048); 426 cafe_readl(cafe, NAND_ADDR2) * 2048);
389 for (i=0; i< 0x5c; i+=4) 427 for (i = 0; i < 0x5c; i += 4)
390 printk("Register %x: %08x\n", i, readl(cafe->mmio + i)); 428 printk("Register %x: %08x\n", i, readl(cafe->mmio + i));
391 mtd->ecc_stats.failed++; 429 mtd->ecc_stats.failed++;
392 } else { 430 } else {
393 dev_dbg(&cafe->pdev->dev, "Corrected %d symbol errors\n", i); 431 dev_dbg(&cafe->pdev->dev, "Corrected %d symbol errors\n", n);
394 mtd->ecc_stats.corrected += i; 432 mtd->ecc_stats.corrected += n;
395 } 433 }
396 } 434 }
397 435
398
399 return 0; 436 return 0;
400} 437}
401 438
@@ -525,6 +562,48 @@ static int cafe_nand_block_bad(struct mtd_info *mtd, loff_t ofs, int getchip)
525 return 0; 562 return 0;
526} 563}
527 564
565/* F_2[X]/(X**6+X+1) */
566static unsigned short __devinit gf64_mul(u8 a, u8 b)
567{
568 u8 c;
569 unsigned int i;
570
571 c = 0;
572 for (i = 0; i < 6; i++) {
573 if (a & 1)
574 c ^= b;
575 a >>= 1;
576 b <<= 1;
577 if ((b & 0x40) != 0)
578 b ^= 0x43;
579 }
580
581 return c;
582}
583
584/* F_64[X]/(X**2+X+A**-1) with A the generator of F_64[X] */
585static u16 __devinit gf4096_mul(u16 a, u16 b)
586{
587 u8 ah, al, bh, bl, ch, cl;
588
589 ah = a >> 6;
590 al = a & 0x3f;
591 bh = b >> 6;
592 bl = b & 0x3f;
593
594 ch = gf64_mul(ah ^ al, bh ^ bl) ^ gf64_mul(al, bl);
595 cl = gf64_mul(gf64_mul(ah, bh), 0x21) ^ gf64_mul(al, bl);
596
597 return (ch << 6) ^ cl;
598}
599
600static int __devinit cafe_mul(int x)
601{
602 if (x == 0)
603 return 1;
604 return gf4096_mul(x, 0xe01);
605}
606
528static int __devinit cafe_nand_probe(struct pci_dev *pdev, 607static int __devinit cafe_nand_probe(struct pci_dev *pdev,
529 const struct pci_device_id *ent) 608 const struct pci_device_id *ent)
530{ 609{
@@ -564,6 +643,12 @@ static int __devinit cafe_nand_probe(struct pci_dev *pdev,
564 } 643 }
565 cafe->nand.buffers = (void *)cafe->dmabuf + 2112; 644 cafe->nand.buffers = (void *)cafe->dmabuf + 2112;
566 645
646 cafe->rs = init_rs_non_canonical(12, &cafe_mul, 0, 1, 8);
647 if (!cafe->rs) {
648 err = -ENOMEM;
649 goto out_ior;
650 }
651
567 cafe->nand.cmdfunc = cafe_nand_cmdfunc; 652 cafe->nand.cmdfunc = cafe_nand_cmdfunc;
568 cafe->nand.dev_ready = cafe_device_ready; 653 cafe->nand.dev_ready = cafe_device_ready;
569 cafe->nand.read_byte = cafe_read_byte; 654 cafe->nand.read_byte = cafe_read_byte;
@@ -713,6 +798,7 @@ static void __devexit cafe_nand_remove(struct pci_dev *pdev)
713 cafe_writel(cafe, ~1 & cafe_readl(cafe, GLOBAL_IRQ_MASK), GLOBAL_IRQ_MASK); 798 cafe_writel(cafe, ~1 & cafe_readl(cafe, GLOBAL_IRQ_MASK), GLOBAL_IRQ_MASK);
714 free_irq(pdev->irq, mtd); 799 free_irq(pdev->irq, mtd);
715 nand_release(mtd); 800 nand_release(mtd);
801 free_rs(cafe->rs);
716 pci_iounmap(pdev, cafe->mmio); 802 pci_iounmap(pdev, cafe->mmio);
717 dma_free_coherent(&cafe->pdev->dev, 2112, cafe->dmabuf, cafe->dmaaddr); 803 dma_free_coherent(&cafe->pdev->dev, 2112, cafe->dmabuf, cafe->dmaaddr);
718 kfree(mtd); 804 kfree(mtd);