diff options
Diffstat (limited to 'drivers/mtd/nand/cafe.c')
-rw-r--r-- | drivers/mtd/nand/cafe.c | 110 |
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 | ||
49 | int cafe_correct_ecc(unsigned char *buf, | ||
50 | unsigned short *chk_syndrome_list); | ||
51 | |||
52 | struct cafe_priv { | 50 | struct 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) */ | ||
566 | static 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] */ | ||
585 | static 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 | |||
600 | static int __devinit cafe_mul(int x) | ||
601 | { | ||
602 | if (x == 0) | ||
603 | return 1; | ||
604 | return gf4096_mul(x, 0xe01); | ||
605 | } | ||
606 | |||
528 | static int __devinit cafe_nand_probe(struct pci_dev *pdev, | 607 | static 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); |