📄 cafe_nand.c
字号:
.eccbytes = 14, .eccpos = { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13}, .oobfree = {{14, 50}}};/* Ick. The BBT code really ought to be able to work this bit out for itself from the above, at least for the 2KiB case */static uint8_t cafe_bbt_pattern_2048[] = { 'B', 'b', 't', '0' };static uint8_t cafe_mirror_pattern_2048[] = { '1', 't', 'b', 'B' };static uint8_t cafe_bbt_pattern_512[] = { 0xBB };static uint8_t cafe_mirror_pattern_512[] = { 0xBC };static struct nand_bbt_descr cafe_bbt_main_descr_2048 = { .options = NAND_BBT_LASTBLOCK | NAND_BBT_CREATE | NAND_BBT_WRITE | NAND_BBT_2BIT | NAND_BBT_VERSION, .offs = 14, .len = 4, .veroffs = 18, .maxblocks = 4, .pattern = cafe_bbt_pattern_2048};static struct nand_bbt_descr cafe_bbt_mirror_descr_2048 = { .options = NAND_BBT_LASTBLOCK | NAND_BBT_CREATE | NAND_BBT_WRITE | NAND_BBT_2BIT | NAND_BBT_VERSION, .offs = 14, .len = 4, .veroffs = 18, .maxblocks = 4, .pattern = cafe_mirror_pattern_2048};static struct nand_ecclayout cafe_oobinfo_512 = { .eccbytes = 14, .eccpos = { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13}, .oobfree = {{14, 2}}};static struct nand_bbt_descr cafe_bbt_main_descr_512 = { .options = NAND_BBT_LASTBLOCK | NAND_BBT_CREATE | NAND_BBT_WRITE | NAND_BBT_2BIT | NAND_BBT_VERSION, .offs = 14, .len = 1, .veroffs = 15, .maxblocks = 4, .pattern = cafe_bbt_pattern_512};static struct nand_bbt_descr cafe_bbt_mirror_descr_512 = { .options = NAND_BBT_LASTBLOCK | NAND_BBT_CREATE | NAND_BBT_WRITE | NAND_BBT_2BIT | NAND_BBT_VERSION, .offs = 14, .len = 1, .veroffs = 15, .maxblocks = 4, .pattern = cafe_mirror_pattern_512};static void cafe_nand_write_page_lowlevel(struct mtd_info *mtd, struct nand_chip *chip, const uint8_t *buf){ struct cafe_priv *cafe = mtd->priv; chip->write_buf(mtd, buf, mtd->writesize); chip->write_buf(mtd, chip->oob_poi, mtd->oobsize); /* Set up ECC autogeneration */ cafe->ctl2 |= (1<<30);}static int cafe_nand_write_page(struct mtd_info *mtd, struct nand_chip *chip, const uint8_t *buf, int page, int cached, int raw){ int status; chip->cmdfunc(mtd, NAND_CMD_SEQIN, 0x00, page); if (unlikely(raw)) chip->ecc.write_page_raw(mtd, chip, buf); else chip->ecc.write_page(mtd, chip, buf); /* * Cached progamming disabled for now, Not sure if its worth the * trouble. The speed gain is not very impressive. (2.3->2.6Mib/s) */ cached = 0; if (!cached || !(chip->options & NAND_CACHEPRG)) { chip->cmdfunc(mtd, NAND_CMD_PAGEPROG, -1, -1); status = chip->waitfunc(mtd, chip); /* * See if operation failed and additional status checks are * available */ if ((status & NAND_STATUS_FAIL) && (chip->errstat)) status = chip->errstat(mtd, chip, FL_WRITING, status, page); if (status & NAND_STATUS_FAIL) return -EIO; } else { chip->cmdfunc(mtd, NAND_CMD_CACHEDPROG, -1, -1); status = chip->waitfunc(mtd, chip); }#ifdef CONFIG_MTD_NAND_VERIFY_WRITE /* Send command to read back the data */ chip->cmdfunc(mtd, NAND_CMD_READ0, 0, page); if (chip->verify_buf(mtd, buf, mtd->writesize)) return -EIO;#endif return 0;}static int cafe_nand_block_bad(struct mtd_info *mtd, loff_t ofs, int getchip){ 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, const struct pci_device_id *ent){ struct mtd_info *mtd; struct cafe_priv *cafe; uint32_t ctrl; int err = 0;#ifdef CONFIG_MTD_PARTITIONS struct mtd_partition *parts; int nr_parts;#endif /* Very old versions shared the same PCI ident for all three functions on the chip. Verify the class too... */ if ((pdev->class >> 8) != PCI_CLASS_MEMORY_FLASH) return -ENODEV; err = pci_enable_device(pdev); if (err) return err; pci_set_master(pdev); mtd = kzalloc(sizeof(*mtd) + sizeof(struct cafe_priv), GFP_KERNEL); if (!mtd) { dev_warn(&pdev->dev, "failed to alloc mtd_info\n"); return -ENOMEM; } cafe = (void *)(&mtd[1]); mtd->priv = cafe; mtd->owner = THIS_MODULE; cafe->pdev = pdev; cafe->mmio = pci_iomap(pdev, 0, 0); if (!cafe->mmio) { dev_warn(&pdev->dev, "failed to iomap\n"); err = -ENOMEM; goto out_free_mtd; } cafe->dmabuf = dma_alloc_coherent(&cafe->pdev->dev, 2112 + sizeof(struct nand_buffers), &cafe->dmaaddr, GFP_KERNEL); if (!cafe->dmabuf) { err = -ENOMEM; goto out_ior; } 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.dev_ready = cafe_device_ready; cafe->nand.read_byte = cafe_read_byte; cafe->nand.read_buf = cafe_read_buf; cafe->nand.write_buf = cafe_write_buf; cafe->nand.select_chip = cafe_select_chip; cafe->nand.chip_delay = 0; /* Enable the following for a flash based bad block table */ cafe->nand.options = NAND_USE_FLASH_BBT | NAND_NO_AUTOINCR | NAND_OWN_BUFFERS; if (skipbbt) { cafe->nand.options |= NAND_SKIP_BBTSCAN; cafe->nand.block_bad = cafe_nand_block_bad; } if (numtimings && numtimings != 3) { dev_warn(&cafe->pdev->dev, "%d timing register values ignored; precisely three are required\n", numtimings); } if (numtimings == 3) { cafe_dev_dbg(&cafe->pdev->dev, "Using provided timings (%08x %08x %08x)\n", timing[0], timing[1], timing[2]); } else { timing[0] = cafe_readl(cafe, NAND_TIMING1); timing[1] = cafe_readl(cafe, NAND_TIMING2); timing[2] = cafe_readl(cafe, NAND_TIMING3); if (timing[0] | timing[1] | timing[2]) { cafe_dev_dbg(&cafe->pdev->dev, "Timing registers already set (%08x %08x %08x)\n", timing[0], timing[1], timing[2]); } else { dev_warn(&cafe->pdev->dev, "Timing registers unset; using most conservative defaults\n"); timing[0] = timing[1] = timing[2] = 0xffffffff; } } /* Start off by resetting the NAND controller completely */ cafe_writel(cafe, 1, NAND_RESET); cafe_writel(cafe, 0, NAND_RESET); cafe_writel(cafe, timing[0], NAND_TIMING1); cafe_writel(cafe, timing[1], NAND_TIMING2); cafe_writel(cafe, timing[2], NAND_TIMING3); cafe_writel(cafe, 0xffffffff, NAND_IRQ_MASK); err = request_irq(pdev->irq, &cafe_nand_interrupt, IRQF_SHARED, "CAFE NAND", mtd); if (err) { dev_warn(&pdev->dev, "Could not register IRQ %d\n", pdev->irq); goto out_free_dma; } /* Disable master reset, enable NAND clock */ ctrl = cafe_readl(cafe, GLOBAL_CTRL); ctrl &= 0xffffeff0; ctrl |= 0x00007000; cafe_writel(cafe, ctrl | 0x05, GLOBAL_CTRL); cafe_writel(cafe, ctrl | 0x0a, GLOBAL_CTRL); cafe_writel(cafe, 0, NAND_DMA_CTRL); cafe_writel(cafe, 0x7006, GLOBAL_CTRL); cafe_writel(cafe, 0x700a, GLOBAL_CTRL); /* Set up DMA address */ cafe_writel(cafe, cafe->dmaaddr & 0xffffffff, NAND_DMA_ADDR0); if (sizeof(cafe->dmaaddr) > 4) /* Shift in two parts to shut the compiler up */ cafe_writel(cafe, (cafe->dmaaddr >> 16) >> 16, NAND_DMA_ADDR1); else cafe_writel(cafe, 0, NAND_DMA_ADDR1); cafe_dev_dbg(&cafe->pdev->dev, "Set DMA address to %x (virt %p)\n", cafe_readl(cafe, NAND_DMA_ADDR0), cafe->dmabuf); /* Enable NAND IRQ in global IRQ mask register */ cafe_writel(cafe, 0x80000007, GLOBAL_IRQ_MASK); cafe_dev_dbg(&cafe->pdev->dev, "Control %x, IRQ mask %x\n", cafe_readl(cafe, GLOBAL_CTRL), cafe_readl(cafe, GLOBAL_IRQ_MASK)); /* Scan to find existence of the device */ if (nand_scan_ident(mtd, 2)) { err = -ENXIO; goto out_irq; } cafe->ctl2 = 1<<27; /* Reed-Solomon ECC */ if (mtd->writesize == 2048) cafe->ctl2 |= 1<<29; /* 2KiB page size */ /* Set up ECC according to the type of chip we found */ if (mtd->writesize == 2048) { cafe->nand.ecc.layout = &cafe_oobinfo_2048; cafe->nand.bbt_td = &cafe_bbt_main_descr_2048; cafe->nand.bbt_md = &cafe_bbt_mirror_descr_2048; } else if (mtd->writesize == 512) { cafe->nand.ecc.layout = &cafe_oobinfo_512; cafe->nand.bbt_td = &cafe_bbt_main_descr_512; cafe->nand.bbt_md = &cafe_bbt_mirror_descr_512; } else { printk(KERN_WARNING "Unexpected NAND flash writesize %d. Aborting\n", mtd->writesize); goto out_irq; } cafe->nand.ecc.mode = NAND_ECC_HW_SYNDROME; cafe->nand.ecc.size = mtd->writesize; cafe->nand.ecc.bytes = 14; cafe->nand.ecc.hwctl = (void *)cafe_nand_bug; cafe->nand.ecc.calculate = (void *)cafe_nand_bug; cafe->nand.ecc.correct = (void *)cafe_nand_bug; cafe->nand.write_page = cafe_nand_write_page; cafe->nand.ecc.write_page = cafe_nand_write_page_lowlevel; cafe->nand.ecc.write_oob = cafe_nand_write_oob; cafe->nand.ecc.read_page = cafe_nand_read_page; cafe->nand.ecc.read_oob = cafe_nand_read_oob; err = nand_scan_tail(mtd); if (err) goto out_irq; pci_set_drvdata(pdev, mtd); /* We register the whole device first, separate from the partitions */ add_mtd_device(mtd);#ifdef CONFIG_MTD_PARTITIONS nr_parts = parse_mtd_partitions(mtd, part_probes, &parts, 0); if (nr_parts > 0) { cafe->parts = parts; dev_info(&cafe->pdev->dev, "%d RedBoot partitions found\n", nr_parts); add_mtd_partitions(mtd, parts, nr_parts); }#endif goto out; out_irq: /* Disable NAND IRQ in global IRQ mask register */ cafe_writel(cafe, ~1 & cafe_readl(cafe, GLOBAL_IRQ_MASK), GLOBAL_IRQ_MASK); free_irq(pdev->irq, mtd); out_free_dma: dma_free_coherent(&cafe->pdev->dev, 2112, cafe->dmabuf, cafe->dmaaddr); out_ior: pci_iounmap(pdev, cafe->mmio); out_free_mtd: kfree(mtd); out: return err;}static void __devexit cafe_nand_remove(struct pci_dev *pdev){ struct mtd_info *mtd = pci_get_drvdata(pdev); struct cafe_priv *cafe = mtd->priv; del_mtd_device(mtd); /* Disable NAND IRQ in global IRQ mask register */ cafe_writel(cafe, ~1 & cafe_readl(cafe, GLOBAL_IRQ_MASK), GLOBAL_IRQ_MASK); free_irq(pdev->irq, mtd); nand_release(mtd); free_rs(cafe->rs); pci_iounmap(pdev, cafe->mmio); dma_free_coherent(&cafe->pdev->dev, 2112, cafe->dmabuf, cafe->dmaaddr); kfree(mtd);}static struct pci_device_id cafe_nand_tbl[] = { { PCI_VENDOR_ID_MARVELL, PCI_DEVICE_ID_MARVELL_88ALP01_NAND, PCI_ANY_ID, PCI_ANY_ID }, { }};MODULE_DEVICE_TABLE(pci, cafe_nand_tbl);static int cafe_nand_resume(struct pci_dev *pdev){ uint32_t ctrl; struct mtd_info *mtd = pci_get_drvdata(pdev); struct cafe_priv *cafe = mtd->priv; /* Start off by resetting the NAND controller completely */ cafe_writel(cafe, 1, NAND_RESET); cafe_writel(cafe, 0, NAND_RESET); cafe_writel(cafe, 0xffffffff, NAND_IRQ_MASK); /* Restore timing configuration */ cafe_writel(cafe, timing[0], NAND_TIMING1); cafe_writel(cafe, timing[1], NAND_TIMING2); cafe_writel(cafe, timing[2], NAND_TIMING3); /* Disable master reset, enable NAND clock */ ctrl = cafe_readl(cafe, GLOBAL_CTRL); ctrl &= 0xffffeff0; ctrl |= 0x00007000; cafe_writel(cafe, ctrl | 0x05, GLOBAL_CTRL); cafe_writel(cafe, ctrl | 0x0a, GLOBAL_CTRL); cafe_writel(cafe, 0, NAND_DMA_CTRL); cafe_writel(cafe, 0x7006, GLOBAL_CTRL); cafe_writel(cafe, 0x700a, GLOBAL_CTRL); /* Set up DMA address */ cafe_writel(cafe, cafe->dmaaddr & 0xffffffff, NAND_DMA_ADDR0); if (sizeof(cafe->dmaaddr) > 4) /* Shift in two parts to shut the compiler up */ cafe_writel(cafe, (cafe->dmaaddr >> 16) >> 16, NAND_DMA_ADDR1); else cafe_writel(cafe, 0, NAND_DMA_ADDR1); /* Enable NAND IRQ in global IRQ mask register */ cafe_writel(cafe, 0x80000007, GLOBAL_IRQ_MASK); return 0;}static struct pci_driver cafe_nand_pci_driver = { .name = "CAFÉ NAND", .id_table = cafe_nand_tbl, .probe = cafe_nand_probe, .remove = __devexit_p(cafe_nand_remove), .resume = cafe_nand_resume,};static int cafe_nand_init(void){ return pci_register_driver(&cafe_nand_pci_driver);}static void cafe_nand_exit(void){ pci_unregister_driver(&cafe_nand_pci_driver);}module_init(cafe_nand_init);module_exit(cafe_nand_exit);MODULE_LICENSE("GPL");MODULE_AUTHOR("David Woodhouse <dwmw2@infradead.org>");MODULE_DESCRIPTION("NAND flash driver for OLPC CAFÉ chip");
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -