You've already forked linux-apfs
mirror of
https://github.com/linux-apfs/linux-apfs.git
synced 2026-05-01 15:00:59 -07:00
Merge git://git.infradead.org/mtd-2.6
* git://git.infradead.org/mtd-2.6: (49 commits) [MTD] [NAND] S3C2412 fix hw ecc [MTD] [NAND] Work around false compiler warning in CAFÉ driver [JFFS2] printk warning fixes [MTD] [MAPS] ichxrom warning fix [MTD] [MAPS] amd76xrom warning fix [MTD] [MAPS] esb2rom warning fixes [MTD] [MAPS] ck804xrom warning fix [MTD] [MAPS] netsc520 warning fix [MTD] [MAPS] sc520cdp warning fix [MTD] [ONENAND] onenand_base warning fix [MTD] [NAND] eXcite nand flash driver [MTD] Improve heuristic for detecting wrong-endian RedBoot partition table [MTD] Fix RedBoot partition parsing regression harder. [MTD] [NAND] S3C2410: Hardware ECC correction code [JFFS2] Use MTD_OOB_AUTO to automatically place cleanmarker on NAND [MTD] Clarify OOB-operation interface comments [MTD] remove unused ecctype,eccsize fields from struct mtd_info [MTD] [NOR] Intel: remove ugly PROGREGION macros [MTD] [NOR] STAA: use writesize instead off eccsize to represent ECC block [MTD] OneNAND: Invalidate bufferRAM after erase ...
This commit is contained in:
@@ -397,9 +397,23 @@ struct mtd_info *cfi_cmdset_0001(struct map_info *map, int primary)
|
||||
cfi_fixup(mtd, fixup_table);
|
||||
|
||||
for (i=0; i< cfi->numchips; i++) {
|
||||
cfi->chips[i].word_write_time = 1<<cfi->cfiq->WordWriteTimeoutTyp;
|
||||
cfi->chips[i].buffer_write_time = 1<<cfi->cfiq->BufWriteTimeoutTyp;
|
||||
cfi->chips[i].erase_time = 1000<<cfi->cfiq->BlockEraseTimeoutTyp;
|
||||
if (cfi->cfiq->WordWriteTimeoutTyp)
|
||||
cfi->chips[i].word_write_time =
|
||||
1<<cfi->cfiq->WordWriteTimeoutTyp;
|
||||
else
|
||||
cfi->chips[i].word_write_time = 50000;
|
||||
|
||||
if (cfi->cfiq->BufWriteTimeoutTyp)
|
||||
cfi->chips[i].buffer_write_time =
|
||||
1<<cfi->cfiq->BufWriteTimeoutTyp;
|
||||
/* No default; if it isn't specified, we won't use it */
|
||||
|
||||
if (cfi->cfiq->BlockEraseTimeoutTyp)
|
||||
cfi->chips[i].erase_time =
|
||||
1000<<cfi->cfiq->BlockEraseTimeoutTyp;
|
||||
else
|
||||
cfi->chips[i].erase_time = 2000000;
|
||||
|
||||
cfi->chips[i].ref_point_counter = 0;
|
||||
init_waitqueue_head(&(cfi->chips[i].wq));
|
||||
}
|
||||
@@ -546,13 +560,11 @@ static int cfi_intelext_partition_fixup(struct mtd_info *mtd,
|
||||
struct cfi_intelext_programming_regioninfo *prinfo;
|
||||
prinfo = (struct cfi_intelext_programming_regioninfo *)&extp->extra[offs];
|
||||
mtd->writesize = cfi->interleave << prinfo->ProgRegShift;
|
||||
MTD_PROGREGION_CTRLMODE_VALID(mtd) = cfi->interleave * prinfo->ControlValid;
|
||||
MTD_PROGREGION_CTRLMODE_INVALID(mtd) = cfi->interleave * prinfo->ControlInvalid;
|
||||
mtd->flags &= ~MTD_BIT_WRITEABLE;
|
||||
printk(KERN_DEBUG "%s: program region size/ctrl_valid/ctrl_inval = %d/%d/%d\n",
|
||||
map->name, mtd->writesize,
|
||||
MTD_PROGREGION_CTRLMODE_VALID(mtd),
|
||||
MTD_PROGREGION_CTRLMODE_INVALID(mtd));
|
||||
cfi->interleave * prinfo->ControlValid,
|
||||
cfi->interleave * prinfo->ControlInvalid);
|
||||
}
|
||||
|
||||
/*
|
||||
|
||||
@@ -662,7 +662,7 @@ static int cfi_staa_write_buffers (struct mtd_info *mtd, loff_t to,
|
||||
* a small buffer for this.
|
||||
* XXX: If the buffer size is not a multiple of 2, this will break
|
||||
*/
|
||||
#define ECCBUF_SIZE (mtd->eccsize)
|
||||
#define ECCBUF_SIZE (mtd->writesize)
|
||||
#define ECCBUF_DIV(x) ((x) & ~(ECCBUF_SIZE - 1))
|
||||
#define ECCBUF_MOD(x) ((x) & (ECCBUF_SIZE - 1))
|
||||
static int
|
||||
|
||||
@@ -569,7 +569,6 @@ void DoC2k_init(struct mtd_info *mtd)
|
||||
|
||||
mtd->type = MTD_NANDFLASH;
|
||||
mtd->flags = MTD_CAP_NANDFLASH;
|
||||
mtd->ecctype = MTD_ECC_RS_DiskOnChip;
|
||||
mtd->size = 0;
|
||||
mtd->erasesize = 0;
|
||||
mtd->writesize = 512;
|
||||
|
||||
@@ -348,7 +348,6 @@ void DoCMil_init(struct mtd_info *mtd)
|
||||
|
||||
mtd->type = MTD_NANDFLASH;
|
||||
mtd->flags = MTD_CAP_NANDFLASH;
|
||||
mtd->ecctype = MTD_ECC_RS_DiskOnChip;
|
||||
mtd->size = 0;
|
||||
|
||||
/* FIXME: erase size is not always 8KiB */
|
||||
|
||||
@@ -472,7 +472,6 @@ void DoCMilPlus_init(struct mtd_info *mtd)
|
||||
|
||||
mtd->type = MTD_NANDFLASH;
|
||||
mtd->flags = MTD_CAP_NANDFLASH;
|
||||
mtd->ecctype = MTD_ECC_RS_DiskOnChip;
|
||||
mtd->size = 0;
|
||||
|
||||
mtd->erasesize = 0;
|
||||
|
||||
@@ -204,7 +204,7 @@ config MTD_ESB2ROM
|
||||
|
||||
config MTD_CK804XROM
|
||||
tristate "BIOS flash chip on Nvidia CK804"
|
||||
depends on X86 && MTD_JEDECPROBE
|
||||
depends on X86 && MTD_JEDECPROBE && PCI
|
||||
help
|
||||
Support for treating the BIOS flash chip on nvidia motherboards
|
||||
as an MTD device - with this you can reprogram your BIOS.
|
||||
|
||||
@@ -205,8 +205,8 @@ static int __devinit amd76xrom_init_one (struct pci_dev *pdev,
|
||||
(((unsigned long)(window->virt)) + offset);
|
||||
map->map.size = 0xffffffffUL - map_top + 1UL;
|
||||
/* Set the name of the map to the address I am trying */
|
||||
sprintf(map->map_name, "%s @%08lx",
|
||||
MOD_NAME, map->map.phys);
|
||||
sprintf(map->map_name, "%s @%08Lx",
|
||||
MOD_NAME, (unsigned long long)map->map.phys);
|
||||
|
||||
/* There is no generic VPP support */
|
||||
for(map->map.bankwidth = 32; map->map.bankwidth;
|
||||
|
||||
@@ -207,8 +207,8 @@ static int __devinit ck804xrom_init_one (struct pci_dev *pdev,
|
||||
(((unsigned long)(window->virt)) + offset);
|
||||
map->map.size = 0xffffffffUL - map_top + 1UL;
|
||||
/* Set the name of the map to the address I am trying */
|
||||
sprintf(map->map_name, "%s @%08lx",
|
||||
MOD_NAME, map->map.phys);
|
||||
sprintf(map->map_name, "%s @%08Lx",
|
||||
MOD_NAME, (unsigned long long)map->map.phys);
|
||||
|
||||
/* There is no generic VPP support */
|
||||
for(map->map.bankwidth = 32; map->map.bankwidth;
|
||||
@@ -327,7 +327,7 @@ static int __init init_ck804xrom(void)
|
||||
pdev = NULL;
|
||||
|
||||
for(id = ck804xrom_pci_tbl; id->vendor; id++) {
|
||||
pdev = pci_find_device(id->vendor, id->device, NULL);
|
||||
pdev = pci_get_device(id->vendor, id->device, NULL);
|
||||
if (pdev)
|
||||
break;
|
||||
}
|
||||
|
||||
@@ -289,8 +289,8 @@ static int __devinit esb2rom_init_one(struct pci_dev *pdev,
|
||||
(((unsigned long)(window->virt)) + offset);
|
||||
map->map.size = 0xffffffffUL - map_top + 1UL;
|
||||
/* Set the name of the map to the address I am trying */
|
||||
sprintf(map->map_name, "%s @%08lx",
|
||||
MOD_NAME, map->map.phys);
|
||||
sprintf(map->map_name, "%s @%08Lx",
|
||||
MOD_NAME, (unsigned long long)map->map.phys);
|
||||
|
||||
/* Firmware hubs only use vpp when being programmed
|
||||
* in a factory setting. So in-place programming
|
||||
|
||||
@@ -227,8 +227,8 @@ static int __devinit ichxrom_init_one (struct pci_dev *pdev,
|
||||
(((unsigned long)(window->virt)) + offset);
|
||||
map->map.size = 0xffffffffUL - map_top + 1UL;
|
||||
/* Set the name of the map to the address I am trying */
|
||||
sprintf(map->map_name, "%s @%08lx",
|
||||
MOD_NAME, map->map.phys);
|
||||
sprintf(map->map_name, "%s @%08Lx",
|
||||
MOD_NAME, (unsigned long long)map->map.phys);
|
||||
|
||||
/* Firmware hubs only use vpp when being programmed
|
||||
* in a factory setting. So in-place programming
|
||||
|
||||
@@ -94,7 +94,9 @@ static struct mtd_info *mymtd;
|
||||
|
||||
static int __init init_netsc520(void)
|
||||
{
|
||||
printk(KERN_NOTICE "NetSc520 flash device: 0x%lx at 0x%lx\n", netsc520_map.size, netsc520_map.phys);
|
||||
printk(KERN_NOTICE "NetSc520 flash device: 0x%Lx at 0x%Lx\n",
|
||||
(unsigned long long)netsc520_map.size,
|
||||
(unsigned long long)netsc520_map.phys);
|
||||
netsc520_map.virt = ioremap_nocache(netsc520_map.phys, netsc520_map.size);
|
||||
|
||||
if (!netsc520_map.virt) {
|
||||
|
||||
@@ -237,8 +237,9 @@ static int __init init_sc520cdp(void)
|
||||
#endif
|
||||
|
||||
for (i = 0; i < NUM_FLASH_BANKS; i++) {
|
||||
printk(KERN_NOTICE "SC520 CDP flash device: 0x%lx at 0x%lx\n",
|
||||
sc520cdp_map[i].size, sc520cdp_map[i].phys);
|
||||
printk(KERN_NOTICE "SC520 CDP flash device: 0x%Lx at 0x%Lx\n",
|
||||
(unsigned long long)sc520cdp_map[i].size,
|
||||
(unsigned long long)sc520cdp_map[i].phys);
|
||||
|
||||
sc520cdp_map[i].virt = ioremap_nocache(sc520cdp_map[i].phys, sc520cdp_map[i].size);
|
||||
|
||||
|
||||
@@ -419,8 +419,9 @@ static int mtd_ioctl(struct inode *inode, struct file *file,
|
||||
info.erasesize = mtd->erasesize;
|
||||
info.writesize = mtd->writesize;
|
||||
info.oobsize = mtd->oobsize;
|
||||
info.ecctype = mtd->ecctype;
|
||||
info.eccsize = mtd->eccsize;
|
||||
/* The below fields are obsolete */
|
||||
info.ecctype = -1;
|
||||
info.eccsize = 0;
|
||||
if (copy_to_user(argp, &info, sizeof(struct mtd_info_user)))
|
||||
return -EFAULT;
|
||||
break;
|
||||
|
||||
@@ -727,8 +727,6 @@ struct mtd_info *mtd_concat_create(struct mtd_info *subdev[], /* subdevices to c
|
||||
concat->mtd.erasesize = subdev[0]->erasesize;
|
||||
concat->mtd.writesize = subdev[0]->writesize;
|
||||
concat->mtd.oobsize = subdev[0]->oobsize;
|
||||
concat->mtd.ecctype = subdev[0]->ecctype;
|
||||
concat->mtd.eccsize = subdev[0]->eccsize;
|
||||
if (subdev[0]->writev)
|
||||
concat->mtd.writev = concat_writev;
|
||||
if (subdev[0]->read_oob)
|
||||
@@ -774,8 +772,6 @@ struct mtd_info *mtd_concat_create(struct mtd_info *subdev[], /* subdevices to c
|
||||
if (concat->mtd.writesize != subdev[i]->writesize ||
|
||||
concat->mtd.subpage_sft != subdev[i]->subpage_sft ||
|
||||
concat->mtd.oobsize != subdev[i]->oobsize ||
|
||||
concat->mtd.ecctype != subdev[i]->ecctype ||
|
||||
concat->mtd.eccsize != subdev[i]->eccsize ||
|
||||
!concat->mtd.read_oob != !subdev[i]->read_oob ||
|
||||
!concat->mtd.write_oob != !subdev[i]->write_oob) {
|
||||
kfree(concat);
|
||||
|
||||
@@ -338,8 +338,6 @@ int add_mtd_partitions(struct mtd_info *master,
|
||||
slave->mtd.size = parts[i].size;
|
||||
slave->mtd.writesize = master->writesize;
|
||||
slave->mtd.oobsize = master->oobsize;
|
||||
slave->mtd.ecctype = master->ecctype;
|
||||
slave->mtd.eccsize = master->eccsize;
|
||||
slave->mtd.subpage_sft = master->subpage_sft;
|
||||
|
||||
slave->mtd.name = parts[i].name;
|
||||
|
||||
@@ -126,10 +126,6 @@ config MTD_NAND_S3C2410_HWECC
|
||||
incorrect ECC generation, and if using these, the default of
|
||||
software ECC is preferable.
|
||||
|
||||
If you lay down a device with the hardware ECC, then you will
|
||||
currently not be able to switch to software, as there is no
|
||||
implementation for ECC method used by the S3C2410
|
||||
|
||||
config MTD_NAND_NDFC
|
||||
tristate "NDFC NanD Flash Controller"
|
||||
depends on MTD_NAND && 44x
|
||||
@@ -221,9 +217,17 @@ config MTD_NAND_SHARPSL
|
||||
tristate "Support for NAND Flash on Sharp SL Series (C7xx + others)"
|
||||
depends on MTD_NAND && ARCH_PXA
|
||||
|
||||
config MTD_NAND_BASLER_EXCITE
|
||||
tristate "Support for NAND Flash on Basler eXcite"
|
||||
depends on MTD_NAND && BASLER_EXCITE
|
||||
help
|
||||
This enables the driver for the NAND flash device found on the
|
||||
Basler eXcite Smart Camera. If built as a module, the driver
|
||||
will be named "excite_nandflash.ko".
|
||||
|
||||
config MTD_NAND_CAFE
|
||||
tristate "NAND support for OLPC CAFÉ chip"
|
||||
depends on PCI
|
||||
depends on MTD_NAND && PCI
|
||||
help
|
||||
Use NAND flash attached to the CAFÉ chip designed for the $100
|
||||
laptop.
|
||||
|
||||
@@ -24,6 +24,7 @@ obj-$(CONFIG_MTD_NAND_NANDSIM) += nandsim.o
|
||||
obj-$(CONFIG_MTD_NAND_CS553X) += cs553x_nand.o
|
||||
obj-$(CONFIG_MTD_NAND_NDFC) += ndfc.o
|
||||
obj-$(CONFIG_MTD_NAND_AT91) += at91_nand.o
|
||||
obj-$(CONFIG_MTD_NAND_BASLER_EXCITE) += excite_nandflash.o
|
||||
|
||||
nand-objs := nand_base.o nand_bbt.o
|
||||
cafe_nand-objs := cafe.o cafe_ecc.o
|
||||
|
||||
+36
-53
@@ -78,8 +78,9 @@ module_param(regdebug, int, 0644);
|
||||
static int checkecc = 1;
|
||||
module_param(checkecc, int, 0644);
|
||||
|
||||
static int slowtiming = 0;
|
||||
module_param(slowtiming, int, 0644);
|
||||
static int numtimings;
|
||||
static int timing[3];
|
||||
module_param_array(timing, int, &numtimings, 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)
|
||||
@@ -264,10 +265,10 @@ static void cafe_nand_cmdfunc(struct mtd_info *mtd, unsigned command,
|
||||
ndelay(100);
|
||||
|
||||
if (1) {
|
||||
int c = 500000;
|
||||
int c;
|
||||
uint32_t irqs;
|
||||
|
||||
while (c--) {
|
||||
for (c = 500000; c != 0; c--) {
|
||||
irqs = cafe_readl(cafe, NAND_IRQ);
|
||||
if (irqs & doneint)
|
||||
break;
|
||||
@@ -529,6 +530,7 @@ static int __devinit cafe_nand_probe(struct pci_dev *pdev,
|
||||
{
|
||||
struct mtd_info *mtd;
|
||||
struct cafe_priv *cafe;
|
||||
uint32_t timing1, timing2, timing3;
|
||||
uint32_t ctrl;
|
||||
int err = 0;
|
||||
|
||||
@@ -580,31 +582,45 @@ static int __devinit cafe_nand_probe(struct pci_dev *pdev,
|
||||
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) {
|
||||
timing1 = timing[0];
|
||||
timing2 = timing[1];
|
||||
timing3 = timing[2];
|
||||
cafe_dev_dbg(&cafe->pdev->dev, "Using provided timings (%08x %08x %08x)\n",
|
||||
timing1, timing2, timing3);
|
||||
} else {
|
||||
timing1 = cafe_readl(cafe, NAND_TIMING1);
|
||||
timing2 = cafe_readl(cafe, NAND_TIMING2);
|
||||
timing3 = cafe_readl(cafe, NAND_TIMING3);
|
||||
|
||||
if (timing1 | timing2 | timing3) {
|
||||
cafe_dev_dbg(&cafe->pdev->dev, "Timing registers already set (%08x %08x %08x)\n", timing1, timing2, timing3);
|
||||
} else {
|
||||
dev_warn(&cafe->pdev->dev, "Timing registers unset; using most conservative defaults\n");
|
||||
timing1 = timing2 = timing3 = 0xffffffff;
|
||||
}
|
||||
}
|
||||
|
||||
/* 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);
|
||||
cafe_writel(cafe, timing1, NAND_TIMING1);
|
||||
cafe_writel(cafe, timing2, NAND_TIMING2);
|
||||
cafe_writel(cafe, timing3, NAND_TIMING3);
|
||||
|
||||
/* Timings from Marvell's test code (not verified or calculated by us) */
|
||||
if (!slowtiming) {
|
||||
cafe_writel(cafe, 0x01010a0a, NAND_TIMING1);
|
||||
cafe_writel(cafe, 0x24121212, NAND_TIMING2);
|
||||
cafe_writel(cafe, 0x11000000, NAND_TIMING3);
|
||||
} else {
|
||||
cafe_writel(cafe, 0xffffffff, NAND_TIMING1);
|
||||
cafe_writel(cafe, 0xffffffff, NAND_TIMING2);
|
||||
cafe_writel(cafe, 0xffffffff, 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;
|
||||
}
|
||||
#if 1
|
||||
|
||||
/* Disable master reset, enable NAND clock */
|
||||
ctrl = cafe_readl(cafe, GLOBAL_CTRL);
|
||||
ctrl &= 0xffffeff0;
|
||||
@@ -631,32 +647,8 @@ static int __devinit cafe_nand_probe(struct pci_dev *pdev,
|
||||
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));
|
||||
#endif
|
||||
#if 1
|
||||
mtd->writesize=2048;
|
||||
mtd->oobsize = 0x40;
|
||||
memset(cafe->dmabuf, 0x5a, 2112);
|
||||
cafe->nand.cmdfunc(mtd, NAND_CMD_READID, 0, -1);
|
||||
cafe->nand.read_byte(mtd);
|
||||
cafe->nand.read_byte(mtd);
|
||||
cafe->nand.read_byte(mtd);
|
||||
cafe->nand.read_byte(mtd);
|
||||
cafe->nand.read_byte(mtd);
|
||||
#endif
|
||||
#if 0
|
||||
cafe->nand.cmdfunc(mtd, NAND_CMD_READ0, 0, 0);
|
||||
// nand_wait_ready(mtd);
|
||||
cafe->nand.read_byte(mtd);
|
||||
cafe->nand.read_byte(mtd);
|
||||
cafe->nand.read_byte(mtd);
|
||||
cafe->nand.read_byte(mtd);
|
||||
#endif
|
||||
#if 0
|
||||
writel(0x84600070, cafe->mmio);
|
||||
udelay(10);
|
||||
cafe_dev_dbg(&cafe->pdev->dev, "Status %x\n", cafe_readl(cafe, NAND_NONMEM));
|
||||
#endif
|
||||
/* Scan to find existance of the device */
|
||||
|
||||
/* Scan to find existence of the device */
|
||||
if (nand_scan_ident(mtd, 1)) {
|
||||
err = -ENXIO;
|
||||
goto out_irq;
|
||||
@@ -760,13 +752,4 @@ module_exit(cafe_nand_exit);
|
||||
|
||||
MODULE_LICENSE("GPL");
|
||||
MODULE_AUTHOR("David Woodhouse <dwmw2@infradead.org>");
|
||||
MODULE_DESCRIPTION("NAND flash driver for OLPC CAFE chip");
|
||||
|
||||
/* Correct ECC for 2048 bytes of 0xff:
|
||||
41 a0 71 65 54 27 f3 93 ec a9 be ed 0b a1 */
|
||||
|
||||
/* dwmw2's B-test board, in case of completely screwing it:
|
||||
Bad eraseblock 2394 at 0x12b40000
|
||||
Bad eraseblock 2627 at 0x14860000
|
||||
Bad eraseblock 3349 at 0x1a2a0000
|
||||
*/
|
||||
MODULE_DESCRIPTION("NAND flash driver for OLPC CAFÉ chip");
|
||||
|
||||
@@ -1045,7 +1045,7 @@ static unsigned short err_pos_lut[4096] = {
|
||||
|
||||
static unsigned short err_pos(unsigned short din)
|
||||
{
|
||||
BUG_ON(din > 4096);
|
||||
BUG_ON(din >= ARRAY_SIZE(err_pos_lut));
|
||||
return err_pos_lut[din];
|
||||
}
|
||||
static int chk_no_err_only(unsigned short *chk_syndrome_list, unsigned short *err_info)
|
||||
|
||||
@@ -0,0 +1,248 @@
|
||||
/*
|
||||
* Copyright (C) 2005 - 2007 by Basler Vision Technologies AG
|
||||
* Author: Thomas Koeller <thomas.koeller.qbaslerweb.com>
|
||||
* Original code by Thies Moeller <thies.moeller@baslerweb.com>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software
|
||||
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
*/
|
||||
|
||||
#include <linux/module.h>
|
||||
#include <linux/types.h>
|
||||
#include <linux/init.h>
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/string.h>
|
||||
#include <linux/ioport.h>
|
||||
#include <linux/platform_device.h>
|
||||
#include <linux/delay.h>
|
||||
#include <linux/err.h>
|
||||
#include <linux/kernel.h>
|
||||
|
||||
#include <linux/mtd/mtd.h>
|
||||
#include <linux/mtd/nand.h>
|
||||
#include <linux/mtd/nand_ecc.h>
|
||||
#include <linux/mtd/partitions.h>
|
||||
|
||||
#include <asm/io.h>
|
||||
#include <asm/rm9k-ocd.h>
|
||||
|
||||
#include <excite_nandflash.h>
|
||||
|
||||
#define EXCITE_NANDFLASH_VERSION "0.1"
|
||||
|
||||
/* I/O register offsets */
|
||||
#define EXCITE_NANDFLASH_DATA_BYTE 0x00
|
||||
#define EXCITE_NANDFLASH_STATUS_BYTE 0x0c
|
||||
#define EXCITE_NANDFLASH_ADDR_BYTE 0x10
|
||||
#define EXCITE_NANDFLASH_CMD_BYTE 0x14
|
||||
|
||||
/* prefix for debug output */
|
||||
static const char module_id[] = "excite_nandflash";
|
||||
|
||||
/*
|
||||
* partition definition
|
||||
*/
|
||||
static const struct mtd_partition partition_info[] = {
|
||||
{
|
||||
.name = "eXcite RootFS",
|
||||
.offset = 0,
|
||||
.size = MTDPART_SIZ_FULL
|
||||
}
|
||||
};
|
||||
|
||||
static inline const struct resource *
|
||||
excite_nand_get_resource(struct platform_device *d, unsigned long flags,
|
||||
const char *basename)
|
||||
{
|
||||
char buf[80];
|
||||
|
||||
if (snprintf(buf, sizeof buf, "%s_%u", basename, d->id) >= sizeof buf)
|
||||
return NULL;
|
||||
return platform_get_resource_byname(d, flags, buf);
|
||||
}
|
||||
|
||||
static inline void __iomem *
|
||||
excite_nand_map_regs(struct platform_device *d, const char *basename)
|
||||
{
|
||||
void *result = NULL;
|
||||
const struct resource *const r =
|
||||
excite_nand_get_resource(d, IORESOURCE_MEM, basename);
|
||||
|
||||
if (r)
|
||||
result = ioremap_nocache(r->start, r->end + 1 - r->start);
|
||||
return result;
|
||||
}
|
||||
|
||||
/* controller and mtd information */
|
||||
struct excite_nand_drvdata {
|
||||
struct mtd_info board_mtd;
|
||||
struct nand_chip board_chip;
|
||||
void __iomem *regs;
|
||||
void __iomem *tgt;
|
||||
};
|
||||
|
||||
/* Control function */
|
||||
static void excite_nand_control(struct mtd_info *mtd, int cmd,
|
||||
unsigned int ctrl)
|
||||
{
|
||||
struct excite_nand_drvdata * const d =
|
||||
container_of(mtd, struct excite_nand_drvdata, board_mtd);
|
||||
|
||||
switch (ctrl) {
|
||||
case NAND_CTRL_CHANGE | NAND_CTRL_CLE:
|
||||
d->tgt = d->regs + EXCITE_NANDFLASH_CMD_BYTE;
|
||||
break;
|
||||
case NAND_CTRL_CHANGE | NAND_CTRL_ALE:
|
||||
d->tgt = d->regs + EXCITE_NANDFLASH_ADDR_BYTE;
|
||||
break;
|
||||
case NAND_CTRL_CHANGE | NAND_NCE:
|
||||
d->tgt = d->regs + EXCITE_NANDFLASH_DATA_BYTE;
|
||||
break;
|
||||
}
|
||||
|
||||
if (cmd != NAND_CMD_NONE)
|
||||
__raw_writeb(cmd, d->tgt);
|
||||
}
|
||||
|
||||
/* Return 0 if flash is busy, 1 if ready */
|
||||
static int excite_nand_devready(struct mtd_info *mtd)
|
||||
{
|
||||
struct excite_nand_drvdata * const drvdata =
|
||||
container_of(mtd, struct excite_nand_drvdata, board_mtd);
|
||||
|
||||
return __raw_readb(drvdata->regs + EXCITE_NANDFLASH_STATUS_BYTE);
|
||||
}
|
||||
|
||||
/*
|
||||
* Called by device layer to remove the driver.
|
||||
* The binding to the mtd and all allocated
|
||||
* resources are released.
|
||||
*/
|
||||
static int __exit excite_nand_remove(struct device *dev)
|
||||
{
|
||||
struct excite_nand_drvdata * const this = dev_get_drvdata(dev);
|
||||
|
||||
dev_set_drvdata(dev, NULL);
|
||||
|
||||
if (unlikely(!this)) {
|
||||
printk(KERN_ERR "%s: called %s without private data!!",
|
||||
module_id, __func__);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
/* first thing we need to do is release our mtd
|
||||
* then go through freeing the resource used
|
||||
*/
|
||||
nand_release(&this->board_mtd);
|
||||
|
||||
/* free the common resources */
|
||||
iounmap(this->regs);
|
||||
kfree(this);
|
||||
|
||||
DEBUG(MTD_DEBUG_LEVEL1, "%s: removed\n", module_id);
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* Called by device layer when it finds a device matching
|
||||
* one our driver can handle. This code checks to see if
|
||||
* it can allocate all necessary resources then calls the
|
||||
* nand layer to look for devices.
|
||||
*/
|
||||
static int __init excite_nand_probe(struct device *dev)
|
||||
{
|
||||
struct platform_device * const pdev = to_platform_device(dev);
|
||||
struct excite_nand_drvdata *drvdata; /* private driver data */
|
||||
struct nand_chip *board_chip; /* private flash chip data */
|
||||
struct mtd_info *board_mtd; /* mtd info for this board */
|
||||
int scan_res;
|
||||
|
||||
drvdata = kzalloc(sizeof(*drvdata), GFP_KERNEL);
|
||||
if (unlikely(!drvdata)) {
|
||||
printk(KERN_ERR "%s: no memory for drvdata\n",
|
||||
module_id);
|
||||
return -ENOMEM;
|
||||
}
|
||||
|
||||
/* bind private data into driver */
|
||||
dev_set_drvdata(dev, drvdata);
|
||||
|
||||
/* allocate and map the resource */
|
||||
drvdata->regs =
|
||||
excite_nand_map_regs(pdev, EXCITE_NANDFLASH_RESOURCE_REGS);
|
||||
|
||||
if (unlikely(!drvdata->regs)) {
|
||||
printk(KERN_ERR "%s: cannot reserve register region\n",
|
||||
module_id);
|
||||
kfree(drvdata);
|
||||
return -ENXIO;
|
||||
}
|
||||
|
||||
drvdata->tgt = drvdata->regs + EXCITE_NANDFLASH_DATA_BYTE;
|
||||
|
||||
/* initialise our chip */
|
||||
board_chip = &drvdata->board_chip;
|
||||
board_chip->IO_ADDR_R = board_chip->IO_ADDR_W =
|
||||
drvdata->regs + EXCITE_NANDFLASH_DATA_BYTE;
|
||||
board_chip->cmd_ctrl = excite_nand_control;
|
||||
board_chip->dev_ready = excite_nand_devready;
|
||||
board_chip->chip_delay = 25;
|
||||
board_chip->ecc.mode = NAND_ECC_SOFT;
|
||||
|
||||
/* link chip to mtd */
|
||||
board_mtd = &drvdata->board_mtd;
|
||||
board_mtd->priv = board_chip;
|
||||
|
||||
DEBUG(MTD_DEBUG_LEVEL2, "%s: device scan\n", module_id);
|
||||
scan_res = nand_scan(&drvdata->board_mtd, 1);
|
||||
|
||||
if (likely(!scan_res)) {
|
||||
DEBUG(MTD_DEBUG_LEVEL2, "%s: register partitions\n", module_id);
|
||||
add_mtd_partitions(&drvdata->board_mtd, partition_info,
|
||||
sizeof partition_info / sizeof partition_info[0]);
|
||||
} else {
|
||||
iounmap(drvdata->regs);
|
||||
kfree(drvdata);
|
||||
printk(KERN_ERR "%s: device scan failed\n", module_id);
|
||||
return -EIO;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
static struct device_driver excite_nand_driver = {
|
||||
.name = "excite_nand",
|
||||
.bus = &platform_bus_type,
|
||||
.probe = excite_nand_probe,
|
||||
.remove = __exit_p(excite_nand_remove)
|
||||
};
|
||||
|
||||
static int __init excite_nand_init(void)
|
||||
{
|
||||
pr_info("Basler eXcite nand flash driver Version "
|
||||
EXCITE_NANDFLASH_VERSION "\n");
|
||||
return driver_register(&excite_nand_driver);
|
||||
}
|
||||
|
||||
static void __exit excite_nand_exit(void)
|
||||
{
|
||||
driver_unregister(&excite_nand_driver);
|
||||
}
|
||||
|
||||
module_init(excite_nand_init);
|
||||
module_exit(excite_nand_exit);
|
||||
|
||||
MODULE_AUTHOR("Thomas Koeller <thomas.koeller@baslerweb.com>");
|
||||
MODULE_DESCRIPTION("Basler eXcite NAND-Flash driver");
|
||||
MODULE_LICENSE("GPL");
|
||||
MODULE_VERSION(EXCITE_NANDFLASH_VERSION)
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user