Commit 49353617 authored by Linus Torvalds's avatar Linus Torvalds

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
  ...
parents 2874b391 4f659923
...@@ -397,9 +397,23 @@ struct mtd_info *cfi_cmdset_0001(struct map_info *map, int primary) ...@@ -397,9 +397,23 @@ struct mtd_info *cfi_cmdset_0001(struct map_info *map, int primary)
cfi_fixup(mtd, fixup_table); cfi_fixup(mtd, fixup_table);
for (i=0; i< cfi->numchips; i++) { for (i=0; i< cfi->numchips; i++) {
cfi->chips[i].word_write_time = 1<<cfi->cfiq->WordWriteTimeoutTyp; if (cfi->cfiq->WordWriteTimeoutTyp)
cfi->chips[i].buffer_write_time = 1<<cfi->cfiq->BufWriteTimeoutTyp; cfi->chips[i].word_write_time =
cfi->chips[i].erase_time = 1000<<cfi->cfiq->BlockEraseTimeoutTyp; 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; cfi->chips[i].ref_point_counter = 0;
init_waitqueue_head(&(cfi->chips[i].wq)); init_waitqueue_head(&(cfi->chips[i].wq));
} }
...@@ -546,13 +560,11 @@ static int cfi_intelext_partition_fixup(struct mtd_info *mtd, ...@@ -546,13 +560,11 @@ static int cfi_intelext_partition_fixup(struct mtd_info *mtd,
struct cfi_intelext_programming_regioninfo *prinfo; struct cfi_intelext_programming_regioninfo *prinfo;
prinfo = (struct cfi_intelext_programming_regioninfo *)&extp->extra[offs]; prinfo = (struct cfi_intelext_programming_regioninfo *)&extp->extra[offs];
mtd->writesize = cfi->interleave << prinfo->ProgRegShift; 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; mtd->flags &= ~MTD_BIT_WRITEABLE;
printk(KERN_DEBUG "%s: program region size/ctrl_valid/ctrl_inval = %d/%d/%d\n", printk(KERN_DEBUG "%s: program region size/ctrl_valid/ctrl_inval = %d/%d/%d\n",
map->name, mtd->writesize, map->name, mtd->writesize,
MTD_PROGREGION_CTRLMODE_VALID(mtd), cfi->interleave * prinfo->ControlValid,
MTD_PROGREGION_CTRLMODE_INVALID(mtd)); cfi->interleave * prinfo->ControlInvalid);
} }
/* /*
......
...@@ -662,7 +662,7 @@ static int cfi_staa_write_buffers (struct mtd_info *mtd, loff_t to, ...@@ -662,7 +662,7 @@ static int cfi_staa_write_buffers (struct mtd_info *mtd, loff_t to,
* a small buffer for this. * a small buffer for this.
* XXX: If the buffer size is not a multiple of 2, this will break * 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_DIV(x) ((x) & ~(ECCBUF_SIZE - 1))
#define ECCBUF_MOD(x) ((x) & (ECCBUF_SIZE - 1)) #define ECCBUF_MOD(x) ((x) & (ECCBUF_SIZE - 1))
static int static int
......
...@@ -569,7 +569,6 @@ void DoC2k_init(struct mtd_info *mtd) ...@@ -569,7 +569,6 @@ void DoC2k_init(struct mtd_info *mtd)
mtd->type = MTD_NANDFLASH; mtd->type = MTD_NANDFLASH;
mtd->flags = MTD_CAP_NANDFLASH; mtd->flags = MTD_CAP_NANDFLASH;
mtd->ecctype = MTD_ECC_RS_DiskOnChip;
mtd->size = 0; mtd->size = 0;
mtd->erasesize = 0; mtd->erasesize = 0;
mtd->writesize = 512; mtd->writesize = 512;
......
...@@ -348,7 +348,6 @@ void DoCMil_init(struct mtd_info *mtd) ...@@ -348,7 +348,6 @@ void DoCMil_init(struct mtd_info *mtd)
mtd->type = MTD_NANDFLASH; mtd->type = MTD_NANDFLASH;
mtd->flags = MTD_CAP_NANDFLASH; mtd->flags = MTD_CAP_NANDFLASH;
mtd->ecctype = MTD_ECC_RS_DiskOnChip;
mtd->size = 0; mtd->size = 0;
/* FIXME: erase size is not always 8KiB */ /* FIXME: erase size is not always 8KiB */
......
...@@ -472,7 +472,6 @@ void DoCMilPlus_init(struct mtd_info *mtd) ...@@ -472,7 +472,6 @@ void DoCMilPlus_init(struct mtd_info *mtd)
mtd->type = MTD_NANDFLASH; mtd->type = MTD_NANDFLASH;
mtd->flags = MTD_CAP_NANDFLASH; mtd->flags = MTD_CAP_NANDFLASH;
mtd->ecctype = MTD_ECC_RS_DiskOnChip;
mtd->size = 0; mtd->size = 0;
mtd->erasesize = 0; mtd->erasesize = 0;
......
...@@ -204,7 +204,7 @@ config MTD_ESB2ROM ...@@ -204,7 +204,7 @@ config MTD_ESB2ROM
config MTD_CK804XROM config MTD_CK804XROM
tristate "BIOS flash chip on Nvidia CK804" tristate "BIOS flash chip on Nvidia CK804"
depends on X86 && MTD_JEDECPROBE depends on X86 && MTD_JEDECPROBE && PCI
help help
Support for treating the BIOS flash chip on nvidia motherboards Support for treating the BIOS flash chip on nvidia motherboards
as an MTD device - with this you can reprogram your BIOS. 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, ...@@ -205,8 +205,8 @@ static int __devinit amd76xrom_init_one (struct pci_dev *pdev,
(((unsigned long)(window->virt)) + offset); (((unsigned long)(window->virt)) + offset);
map->map.size = 0xffffffffUL - map_top + 1UL; map->map.size = 0xffffffffUL - map_top + 1UL;
/* Set the name of the map to the address I am trying */ /* Set the name of the map to the address I am trying */
sprintf(map->map_name, "%s @%08lx", sprintf(map->map_name, "%s @%08Lx",
MOD_NAME, map->map.phys); MOD_NAME, (unsigned long long)map->map.phys);
/* There is no generic VPP support */ /* There is no generic VPP support */
for(map->map.bankwidth = 32; map->map.bankwidth; for(map->map.bankwidth = 32; map->map.bankwidth;
......
...@@ -207,8 +207,8 @@ static int __devinit ck804xrom_init_one (struct pci_dev *pdev, ...@@ -207,8 +207,8 @@ static int __devinit ck804xrom_init_one (struct pci_dev *pdev,
(((unsigned long)(window->virt)) + offset); (((unsigned long)(window->virt)) + offset);
map->map.size = 0xffffffffUL - map_top + 1UL; map->map.size = 0xffffffffUL - map_top + 1UL;
/* Set the name of the map to the address I am trying */ /* Set the name of the map to the address I am trying */
sprintf(map->map_name, "%s @%08lx", sprintf(map->map_name, "%s @%08Lx",
MOD_NAME, map->map.phys); MOD_NAME, (unsigned long long)map->map.phys);
/* There is no generic VPP support */ /* There is no generic VPP support */
for(map->map.bankwidth = 32; map->map.bankwidth; for(map->map.bankwidth = 32; map->map.bankwidth;
...@@ -327,7 +327,7 @@ static int __init init_ck804xrom(void) ...@@ -327,7 +327,7 @@ static int __init init_ck804xrom(void)
pdev = NULL; pdev = NULL;
for(id = ck804xrom_pci_tbl; id->vendor; id++) { 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) if (pdev)
break; break;
} }
......
...@@ -289,8 +289,8 @@ static int __devinit esb2rom_init_one(struct pci_dev *pdev, ...@@ -289,8 +289,8 @@ static int __devinit esb2rom_init_one(struct pci_dev *pdev,
(((unsigned long)(window->virt)) + offset); (((unsigned long)(window->virt)) + offset);
map->map.size = 0xffffffffUL - map_top + 1UL; map->map.size = 0xffffffffUL - map_top + 1UL;
/* Set the name of the map to the address I am trying */ /* Set the name of the map to the address I am trying */
sprintf(map->map_name, "%s @%08lx", sprintf(map->map_name, "%s @%08Lx",
MOD_NAME, map->map.phys); MOD_NAME, (unsigned long long)map->map.phys);
/* Firmware hubs only use vpp when being programmed /* Firmware hubs only use vpp when being programmed
* in a factory setting. So in-place programming * in a factory setting. So in-place programming
......
...@@ -227,8 +227,8 @@ static int __devinit ichxrom_init_one (struct pci_dev *pdev, ...@@ -227,8 +227,8 @@ static int __devinit ichxrom_init_one (struct pci_dev *pdev,
(((unsigned long)(window->virt)) + offset); (((unsigned long)(window->virt)) + offset);
map->map.size = 0xffffffffUL - map_top + 1UL; map->map.size = 0xffffffffUL - map_top + 1UL;
/* Set the name of the map to the address I am trying */ /* Set the name of the map to the address I am trying */
sprintf(map->map_name, "%s @%08lx", sprintf(map->map_name, "%s @%08Lx",
MOD_NAME, map->map.phys); MOD_NAME, (unsigned long long)map->map.phys);
/* Firmware hubs only use vpp when being programmed /* Firmware hubs only use vpp when being programmed
* in a factory setting. So in-place programming * in a factory setting. So in-place programming
......
...@@ -94,7 +94,9 @@ static struct mtd_info *mymtd; ...@@ -94,7 +94,9 @@ static struct mtd_info *mymtd;
static int __init init_netsc520(void) 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); netsc520_map.virt = ioremap_nocache(netsc520_map.phys, netsc520_map.size);
if (!netsc520_map.virt) { if (!netsc520_map.virt) {
......
...@@ -237,8 +237,9 @@ static int __init init_sc520cdp(void) ...@@ -237,8 +237,9 @@ static int __init init_sc520cdp(void)
#endif #endif
for (i = 0; i < NUM_FLASH_BANKS; i++) { for (i = 0; i < NUM_FLASH_BANKS; i++) {
printk(KERN_NOTICE "SC520 CDP flash device: 0x%lx at 0x%lx\n", printk(KERN_NOTICE "SC520 CDP flash device: 0x%Lx at 0x%Lx\n",
sc520cdp_map[i].size, sc520cdp_map[i].phys); (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); 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, ...@@ -419,8 +419,9 @@ static int mtd_ioctl(struct inode *inode, struct file *file,
info.erasesize = mtd->erasesize; info.erasesize = mtd->erasesize;
info.writesize = mtd->writesize; info.writesize = mtd->writesize;
info.oobsize = mtd->oobsize; info.oobsize = mtd->oobsize;
info.ecctype = mtd->ecctype; /* The below fields are obsolete */
info.eccsize = mtd->eccsize; info.ecctype = -1;
info.eccsize = 0;
if (copy_to_user(argp, &info, sizeof(struct mtd_info_user))) if (copy_to_user(argp, &info, sizeof(struct mtd_info_user)))
return -EFAULT; return -EFAULT;
break; break;
......
...@@ -727,8 +727,6 @@ struct mtd_info *mtd_concat_create(struct mtd_info *subdev[], /* subdevices to c ...@@ -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.erasesize = subdev[0]->erasesize;
concat->mtd.writesize = subdev[0]->writesize; concat->mtd.writesize = subdev[0]->writesize;
concat->mtd.oobsize = subdev[0]->oobsize; concat->mtd.oobsize = subdev[0]->oobsize;
concat->mtd.ecctype = subdev[0]->ecctype;
concat->mtd.eccsize = subdev[0]->eccsize;
if (subdev[0]->writev) if (subdev[0]->writev)
concat->mtd.writev = concat_writev; concat->mtd.writev = concat_writev;
if (subdev[0]->read_oob) if (subdev[0]->read_oob)
...@@ -774,8 +772,6 @@ struct mtd_info *mtd_concat_create(struct mtd_info *subdev[], /* subdevices to c ...@@ -774,8 +772,6 @@ struct mtd_info *mtd_concat_create(struct mtd_info *subdev[], /* subdevices to c
if (concat->mtd.writesize != subdev[i]->writesize || if (concat->mtd.writesize != subdev[i]->writesize ||
concat->mtd.subpage_sft != subdev[i]->subpage_sft || concat->mtd.subpage_sft != subdev[i]->subpage_sft ||
concat->mtd.oobsize != subdev[i]->oobsize || 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.read_oob != !subdev[i]->read_oob ||
!concat->mtd.write_oob != !subdev[i]->write_oob) { !concat->mtd.write_oob != !subdev[i]->write_oob) {
kfree(concat); kfree(concat);
......
...@@ -338,8 +338,6 @@ int add_mtd_partitions(struct mtd_info *master, ...@@ -338,8 +338,6 @@ int add_mtd_partitions(struct mtd_info *master,
slave->mtd.size = parts[i].size; slave->mtd.size = parts[i].size;
slave->mtd.writesize = master->writesize; slave->mtd.writesize = master->writesize;
slave->mtd.oobsize = master->oobsize; slave->mtd.oobsize = master->oobsize;
slave->mtd.ecctype = master->ecctype;
slave->mtd.eccsize = master->eccsize;
slave->mtd.subpage_sft = master->subpage_sft; slave->mtd.subpage_sft = master->subpage_sft;
slave->mtd.name = parts[i].name; slave->mtd.name = parts[i].name;
......
...@@ -126,10 +126,6 @@ config MTD_NAND_S3C2410_HWECC ...@@ -126,10 +126,6 @@ config MTD_NAND_S3C2410_HWECC
incorrect ECC generation, and if using these, the default of incorrect ECC generation, and if using these, the default of
software ECC is preferable. 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 config MTD_NAND_NDFC
tristate "NDFC NanD Flash Controller" tristate "NDFC NanD Flash Controller"
depends on MTD_NAND && 44x depends on MTD_NAND && 44x
...@@ -221,9 +217,17 @@ config MTD_NAND_SHARPSL ...@@ -221,9 +217,17 @@ config MTD_NAND_SHARPSL
tristate "Support for NAND Flash on Sharp SL Series (C7xx + others)" tristate "Support for NAND Flash on Sharp SL Series (C7xx + others)"
depends on MTD_NAND && ARCH_PXA 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 config MTD_NAND_CAFE
tristate "NAND support for OLPC CAFÉ chip" tristate "NAND support for OLPC CAFÉ chip"
depends on PCI depends on MTD_NAND && PCI
help help
Use NAND flash attached to the CAFÉ chip designed for the $100 Use NAND flash attached to the CAFÉ chip designed for the $100
laptop. laptop.
......
...@@ -24,6 +24,7 @@ obj-$(CONFIG_MTD_NAND_NANDSIM) += nandsim.o ...@@ -24,6 +24,7 @@ obj-$(CONFIG_MTD_NAND_NANDSIM) += nandsim.o
obj-$(CONFIG_MTD_NAND_CS553X) += cs553x_nand.o obj-$(CONFIG_MTD_NAND_CS553X) += cs553x_nand.o
obj-$(CONFIG_MTD_NAND_NDFC) += ndfc.o obj-$(CONFIG_MTD_NAND_NDFC) += ndfc.o
obj-$(CONFIG_MTD_NAND_AT91) += at91_nand.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 nand-objs := nand_base.o nand_bbt.o
cafe_nand-objs := cafe.o cafe_ecc.o cafe_nand-objs := cafe.o cafe_ecc.o
...@@ -78,8 +78,9 @@ module_param(regdebug, int, 0644); ...@@ -78,8 +78,9 @@ module_param(regdebug, int, 0644);
static int checkecc = 1; static int checkecc = 1;
module_param(checkecc, int, 0644); module_param(checkecc, int, 0644);
static int slowtiming = 0; static int numtimings;
module_param(slowtiming, int, 0644); static int timing[3];
module_param_array(timing, int, &numtimings, 0644);
/* Hrm. Why isn't this already conditional on something in the struct device? */ /* 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) #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, ...@@ -264,10 +265,10 @@ static void cafe_nand_cmdfunc(struct mtd_info *mtd, unsigned command,
ndelay(100); ndelay(100);
if (1) { if (1) {
int c = 500000; int c;
uint32_t irqs; uint32_t irqs;
while (c--) { for (c = 500000; c != 0; c--) {
irqs = cafe_readl(cafe, NAND_IRQ); irqs = cafe_readl(cafe, NAND_IRQ);
if (irqs & doneint) if (irqs & doneint)
break; break;
...@@ -529,6 +530,7 @@ static int __devinit cafe_nand_probe(struct pci_dev *pdev, ...@@ -529,6 +530,7 @@ static int __devinit cafe_nand_probe(struct pci_dev *pdev,
{ {
struct mtd_info *mtd; struct mtd_info *mtd;
struct cafe_priv *cafe; struct cafe_priv *cafe;
uint32_t timing1, timing2, timing3;
uint32_t ctrl; uint32_t ctrl;
int err = 0; int err = 0;
...@@ -580,31 +582,45 @@ static int __devinit cafe_nand_probe(struct pci_dev *pdev, ...@@ -580,31 +582,45 @@ static int __devinit cafe_nand_probe(struct pci_dev *pdev,
cafe->nand.block_bad = cafe_nand_block_bad; 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 */ /* Start off by resetting the NAND controller completely */
cafe_writel(cafe, 1, NAND_RESET); cafe_writel(cafe, 1, NAND_RESET);
cafe_writel(cafe, 0, 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); cafe_writel(cafe, 0xffffffff, NAND_IRQ_MASK);
err = request_irq(pdev->irq, &cafe_nand_interrupt, IRQF_SHARED, err = request_irq(pdev->irq, &cafe_nand_interrupt, IRQF_SHARED,
"CAFE NAND", mtd); "CAFE NAND", mtd);
if (err) { if (err) {
dev_warn(&pdev->dev, "Could not register IRQ %d\n", pdev->irq); dev_warn(&pdev->dev, "Could not register IRQ %d\n", pdev->irq);
goto out_free_dma; goto out_free_dma;
} }
#if 1
/* Disable master reset, enable NAND clock */ /* Disable master reset, enable NAND clock */
ctrl = cafe_readl(cafe, GLOBAL_CTRL); ctrl = cafe_readl(cafe, GLOBAL_CTRL);
ctrl &= 0xffffeff0; ctrl &= 0xffffeff0;
...@@ -631,32 +647,8 @@ static int __devinit cafe_nand_probe(struct pci_dev *pdev, ...@@ -631,32 +647,8 @@ static int __devinit cafe_nand_probe(struct pci_dev *pdev,
cafe_writel(cafe, 0x80000007, GLOBAL_IRQ_MASK); cafe_writel(cafe, 0x80000007, GLOBAL_IRQ_MASK);
cafe_dev_dbg(&cafe->pdev->dev, "Control %x, IRQ mask %x\n", cafe_dev_dbg(&cafe->pdev->dev, "Control %x, IRQ mask %x\n",
cafe_readl(cafe, GLOBAL_CTRL), cafe_readl(cafe, GLOBAL_IRQ_MASK)); cafe_readl(cafe, GLOBAL_CTRL), cafe_readl(cafe, GLOBAL_IRQ_MASK));
#endif
#if 1 /* Scan to find existence of the device */
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 */
if (nand_scan_ident(mtd, 1)) { if (nand_scan_ident(mtd, 1)) {
err = -ENXIO; err = -ENXIO;
goto out_irq; goto out_irq;
...@@ -760,13 +752,4 @@ module_exit(cafe_nand_exit); ...@@ -760,13 +752,4 @@ module_exit(cafe_nand_exit);
MODULE_LICENSE("GPL"); MODULE_LICENSE("GPL");
MODULE_AUTHOR("David Woodhouse <dwmw2@infradead.org>"); MODULE_AUTHOR("David Woodhouse <dwmw2@infradead.org>");
MODULE_DESCRIPTION("NAND flash driver for OLPC CAFE chip"); MODULE_DESCRIPTION("NAND flash driver for OLPC CAFÉ 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
*/
...@@ -1045,7 +1045,7 @@ static unsigned short err_pos_lut[4096] = { ...@@ -1045,7 +1045,7 @@ static unsigned short err_pos_lut[4096] = {
static unsigned short err_pos(unsigned short din) 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]; return err_pos_lut[din];
} }
static int chk_no_err_only(unsigned short *chk_syndrome_list, unsigned short *err_info) static int chk_no_err_only(unsigned short *chk_syndrome_list, unsigned short *err_info)
......
/*
* 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)
...@@ -1272,10 +1272,25 @@ static int nand_do_read_oob(struct mtd_info *mtd, loff_t from, ...@@ -1272,10 +1272,25 @@ static int nand_do_read_oob(struct mtd_info *mtd, loff_t from,
DEBUG(MTD_DEBUG_LEVEL3, "nand_read_oob: from = 0x%08Lx, len = %i\n", DEBUG(MTD_DEBUG_LEVEL3, "nand_read_oob: from = 0x%08Lx, len = %i\n",
(unsigned long long)from, readlen); (unsigned long long)from, readlen);
if (ops->mode == MTD_OOB_RAW) if (ops->mode == MTD_OOB_AUTO)
len = mtd->oobsize;
else
len = chip->ecc.layout->oobavail; len = chip->ecc.layout->oobavail;
else
len = mtd->oobsize;
if (unlikely(ops->ooboffs >= len)) {
DEBUG(MTD_DEBUG_LEVEL0, "nand_read_oob: "
"Attempt to start read outside oob\n");
return -EINVAL;
}
/* Do not allow reads past end of device */
if (unlikely(from >= mtd->size ||
ops->ooboffs + readlen > ((mtd->size >> chip->page_shift) -
(from >> chip->page_shift)) * len)) {
DEBUG(MTD_DEBUG_LEVEL0, "nand_read_oob: "
"Attempt read beyond end of device\n");
return -EINVAL;
}
chipnr = (int)(from >> chip->chip_shift); chipnr = (int)(from >> chip->chip_shift);
chip->select_chip(mtd, chipnr); chip->select_chip(mtd, chipnr);
...@@ -1742,19 +1757,40 @@ static int nand_write(struct mtd_info *mtd, loff_t to, size_t len, ...@@ -1742,19 +1757,40 @@ static int nand_write(struct mtd_info *mtd, loff_t to, size_t len,
static int nand_do_write_oob(struct mtd_info *mtd, loff_t to, static int nand_do_write_oob(struct mtd_info *mtd, loff_t to,
struct mtd_oob_ops *ops) struct mtd_oob_ops *ops)
{ {
int chipnr, page, status; int chipnr, page, status, len;
struct nand_chip *chip = mtd->priv; struct nand_chip *chip = mtd->priv;
DEBUG(MTD_DEBUG_LEVEL3, "nand_write_oob: to = 0x%08x, len = %i\n", DEBUG(MTD_DEBUG_LEVEL3, "nand_write_oob: to = 0x%08x, len = %i\n",
(unsigned int)to, (int)ops->ooblen); (unsigned int)to, (int)ops->ooblen);
if (ops->mode == MTD_OOB_AUTO)
len = chip->ecc.layout->oobavail;
else
len = mtd->oobsize;
/* Do not allow write past end of page */ /* Do not allow write past end of page */
if ((ops->ooboffs + ops->ooblen) > mtd->oobsize) { if ((ops->ooboffs + ops->ooblen) > len) {
DEBUG(MTD_DEBUG_LEVEL0, "nand_write_oob: " DEBUG(MTD_DEBUG_LEVEL0, "nand_write_oob: "
"Attempt to write past end of page\n"); "Attempt to write past end of page\n");
return -EINVAL; return -EINVAL;
} }
if (unlikely(ops->ooboffs >= len)) {
DEBUG(MTD_DEBUG_LEVEL0, "nand_read_oob: "
"Attempt to start write outside oob\n");
return -EINVAL;
}
/* Do not allow reads past end of device */
if (unlikely(to >= mtd->size ||
ops->ooboffs + ops->ooblen >
((mtd->size >> chip->page_shift) -
(to >> chip->page_shift)) * len)) {
DEBUG(MTD_DEBUG_LEVEL0, "nand_read_oob: "
"Attempt write beyond end of device\n");
return -EINVAL;
}
chipnr = (int)(to >> chip->chip_shift); chipnr = (int)(to >> chip->chip_shift);
chip->select_chip(mtd, chipnr); chip->select_chip(mtd, chipnr);
...@@ -2530,7 +2566,6 @@ int nand_scan_tail(struct mtd_info *mtd) ...@@ -2530,7 +2566,6 @@ int nand_scan_tail(struct mtd_info *mtd)
/* Fill in remaining MTD driver data */ /* Fill in remaining MTD driver data */
mtd->type = MTD_NANDFLASH; mtd->type = MTD_NANDFLASH;
mtd->flags = MTD_CAP_NANDFLASH; mtd->flags = MTD_CAP_NANDFLASH;
mtd->ecctype = MTD_ECC_SW;
mtd->erase = nand_erase; mtd->erase = nand_erase;
mtd->point = NULL; mtd->point = NULL;
mtd->unpoint = NULL; mtd->unpoint = NULL;
......
...@@ -337,17 +337,69 @@ static int s3c2412_nand_devready(struct mtd_info *mtd) ...@@ -337,17 +337,69 @@ static int s3c2412_nand_devready(struct mtd_info *mtd)
static int s3c2410_nand_correct_data(struct mtd_info *mtd, u_char *dat, static int s3c2410_nand_correct_data(struct mtd_info *mtd, u_char *dat,
u_char *read_ecc, u_char *calc_ecc) u_char *read_ecc, u_char *calc_ecc)
{ {
pr_debug("s3c2410_nand_correct_data(%p,%p,%p,%p)\n", mtd, dat, read_ecc, calc_ecc); struct s3c2410_nand_info *info = s3c2410_nand_mtd_toinfo(mtd);
unsigned int diff0, diff1, diff2;
unsigned int bit, byte;
pr_debug("eccs: read %02x,%02x,%02x vs calc %02x,%02x,%02x\n", pr_debug("%s(%p,%p,%p,%p)\n", __func__, mtd, dat, read_ecc, calc_ecc);
read_ecc[0], read_ecc[1], read_ecc[2], calc_ecc[0], calc_ecc[1], calc_ecc[2]);
if (read_ecc[0] == calc_ecc[0] && read_ecc[1] == calc_ecc[1] && read_ecc[2] == calc_ecc[2]) diff0 = read_ecc[0] ^ calc_ecc[0];
return 0; diff1 = read_ecc[1] ^ calc_ecc[1];
diff2 = read_ecc[2] ^ calc_ecc[2];
pr_debug("%s: rd %02x%02x%02x calc %02x%02x%02x diff %02x%02x%02x\n",
__func__,
read_ecc[0], read_ecc[1], read_ecc[2],
calc_ecc[0], calc_ecc[1], calc_ecc[2],
diff0, diff1, diff2);
if (diff0 == 0 && diff1 == 0 && diff2 == 0)
return 0; /* ECC is ok */
/* Can we correct this ECC (ie, one row and column change).
* Note, this is similar to the 256 error code on smartmedia */
if (((diff0 ^ (diff0 >> 1)) & 0x55) == 0x55 &&
((diff1 ^ (diff1 >> 1)) & 0x55) == 0x55 &&
((diff2 ^ (diff2 >> 1)) & 0x55) == 0x55) {
/* calculate the bit position of the error */
bit = (diff2 >> 2) & 1;
bit |= (diff2 >> 3) & 2;
bit |= (diff2 >> 4) & 4;
/* calculate the byte position of the error */
byte = (diff1 << 1) & 0x80;
byte |= (diff1 << 2) & 0x40;
byte |= (diff1 << 3) & 0x20;
byte |= (diff1 << 4) & 0x10;
byte |= (diff0 >> 3) & 0x08;
byte |= (diff0 >> 2) & 0x04;
byte |= (diff0 >> 1) & 0x02;
byte |= (diff0 >> 0) & 0x01;
/* we curently have no method for correcting the error */ byte |= (diff2 << 8) & 0x100;
return -1; dev_dbg(info->device, "correcting error bit %d, byte %d\n",
bit, byte);
dat[byte] ^= (1 << bit);
return 1;
}
/* if there is only one bit difference in the ECC, then
* one of only a row or column parity has changed, which
* means the error is most probably in the ECC itself */
diff0 |= (diff1 << 8);
diff0 |= (diff2 << 16);
if ((diff0 & ~(1<<fls(diff0))) == 0)
return 1;
return 0;
} }
/* ECC functions /* ECC functions
...@@ -366,6 +418,15 @@ static void s3c2410_nand_enable_hwecc(struct mtd_info *mtd, int mode) ...@@ -366,6 +418,15 @@ static void s3c2410_nand_enable_hwecc(struct mtd_info *mtd, int mode)
writel(ctrl, info->regs + S3C2410_NFCONF); writel(ctrl, info->regs + S3C2410_NFCONF);
} }
static void s3c2412_nand_enable_hwecc(struct mtd_info *mtd, int mode)
{
struct s3c2410_nand_info *info = s3c2410_nand_mtd_toinfo(mtd);
unsigned long ctrl;
ctrl = readl(info->regs + S3C2440_NFCONT);
writel(ctrl | S3C2412_NFCONT_INIT_MAIN_ECC, info->regs + S3C2440_NFCONT);
}
static void s3c2440_nand_enable_hwecc(struct mtd_info *mtd, int mode) static void s3c2440_nand_enable_hwecc(struct mtd_info *mtd, int mode)
{ {
struct s3c2410_nand_info *info = s3c2410_nand_mtd_toinfo(mtd); struct s3c2410_nand_info *info = s3c2410_nand_mtd_toinfo(mtd);
...@@ -383,6 +444,21 @@ static int s3c2410_nand_calculate_ecc(struct mtd_info *mtd, const u_char *dat, u ...@@ -383,6 +444,21 @@ static int s3c2410_nand_calculate_ecc(struct mtd_info *mtd, const u_char *dat, u
ecc_code[1] = readb(info->regs + S3C2410_NFECC + 1); ecc_code[1] = readb(info->regs + S3C2410_NFECC + 1);
ecc_code[2] = readb(info->regs + S3C2410_NFECC + 2); ecc_code[2] = readb(info->regs + S3C2410_NFECC + 2);
pr_debug("%s: returning ecc %02x%02x%02x\n", __func__,
ecc_code[0], ecc_code[1], ecc_code[2]);
return 0;
}
static int s3c2412_nand_calculate_ecc(struct mtd_info *mtd, const u_char *dat, u_char *ecc_code)
{
struct s3c2410_nand_info *info = s3c2410_nand_mtd_toinfo(mtd);
unsigned long ecc = readl(info->regs + S3C2412_NFMECC0);
ecc_code[0] = ecc;
ecc_code[1] = ecc >> 8;
ecc_code[2] = ecc >> 16;
pr_debug("calculate_ecc: returning ecc %02x,%02x,%02x\n", ecc_code[0], ecc_code[1], ecc_code[2]); pr_debug("calculate_ecc: returning ecc %02x,%02x,%02x\n", ecc_code[0], ecc_code[1], ecc_code[2]);
return 0; return 0;
...@@ -397,7 +473,7 @@ static int s3c2440_nand_calculate_ecc(struct mtd_info *mtd, const u_char *dat, u ...@@ -397,7 +473,7 @@ static int s3c2440_nand_calculate_ecc(struct mtd_info *mtd, const u_char *dat, u
ecc_code[1] = ecc >> 8; ecc_code[1] = ecc >> 8;
ecc_code[2] = ecc >> 16; ecc_code[2] = ecc >> 16;
pr_debug("calculate_ecc: returning ecc %02x,%02x,%02x\n", ecc_code[0], ecc_code[1], ecc_code[2]); pr_debug("%s: returning ecc %06x\n", __func__, ecc);
return 0; return 0;
} }
...@@ -565,6 +641,10 @@ static void s3c2410_nand_init_chip(struct s3c2410_nand_info *info, ...@@ -565,6 +641,10 @@ static void s3c2410_nand_init_chip(struct s3c2410_nand_info *info,
break; break;
case TYPE_S3C2412: case TYPE_S3C2412:
chip->ecc.hwctl = s3c2412_nand_enable_hwecc;
chip->ecc.calculate = s3c2412_nand_calculate_ecc;
break;
case TYPE_S3C2440: case TYPE_S3C2440:
chip->ecc.hwctl = s3c2440_nand_enable_hwecc; chip->ecc.hwctl = s3c2440_nand_enable_hwecc;
chip->ecc.calculate = s3c2440_nand_calculate_ecc; chip->ecc.calculate = s3c2440_nand_calculate_ecc;
......
This diff is collapsed.
...@@ -17,8 +17,8 @@ ...@@ -17,8 +17,8 @@
#include <linux/mtd/onenand.h> #include <linux/mtd/onenand.h>
#include <linux/mtd/compatmac.h> #include <linux/mtd/compatmac.h>
extern int onenand_do_read_oob(struct mtd_info *mtd, loff_t from, size_t len, extern int onenand_bbt_read_oob(struct mtd_info *mtd, loff_t from,
size_t *retlen, u_char *buf); struct mtd_oob_ops *ops);
/** /**
* check_short_pattern - [GENERIC] check if a pattern is in the buffer * check_short_pattern - [GENERIC] check if a pattern is in the buffer
...@@ -65,10 +65,11 @@ static int create_bbt(struct mtd_info *mtd, uint8_t *buf, struct nand_bbt_descr ...@@ -65,10 +65,11 @@ static int create_bbt(struct mtd_info *mtd, uint8_t *buf, struct nand_bbt_descr
int startblock; int startblock;
loff_t from; loff_t from;
size_t readlen, ooblen; size_t readlen, ooblen;
struct mtd_oob_ops ops;
printk(KERN_INFO "Scanning device for bad blocks\n"); printk(KERN_INFO "Scanning device for bad blocks\n");
len = 1; len = 2;
/* We need only read few bytes from the OOB area */ /* We need only read few bytes from the OOB area */
scanlen = ooblen = 0; scanlen = ooblen = 0;
...@@ -82,22 +83,24 @@ static int create_bbt(struct mtd_info *mtd, uint8_t *buf, struct nand_bbt_descr ...@@ -82,22 +83,24 @@ static int create_bbt(struct mtd_info *mtd, uint8_t *buf, struct nand_bbt_descr
startblock = 0; startblock = 0;
from = 0; from = 0;
ops.mode = MTD_OOB_PLACE;
ops.ooblen = readlen;
ops.oobbuf = buf;
ops.len = ops.ooboffs = ops.retlen = ops.oobretlen = 0;
for (i = startblock; i < numblocks; ) { for (i = startblock; i < numblocks; ) {
int ret; int ret;
for (j = 0; j < len; j++) { for (j = 0; j < len; j++) {
size_t retlen;
/* No need to read pages fully, /* No need to read pages fully,
* just read required OOB bytes */ * just read required OOB bytes */
ret = onenand_do_read_oob(mtd, from + j * mtd->writesize + bd->offs, ret = onenand_bbt_read_oob(mtd, from + j * mtd->writesize + bd->offs, &ops);
readlen, &retlen, &buf[0]);
/* If it is a initial bad block, just ignore it */ /* If it is a initial bad block, just ignore it */
if (ret && !(ret & ONENAND_CTRL_LOAD)) if (ret == ONENAND_BBT_READ_FATAL_ERROR)
return ret; return -EIO;
if (check_short_pattern(&buf[j * scanlen], scanlen, mtd->writesize, bd)) { if (ret || check_short_pattern(&buf[j * scanlen], scanlen, mtd->writesize, bd)) {
bbm->bbt[i >> 3] |= 0x03 << (i & 0x6); bbm->bbt[i >> 3] |= 0x03 << (i & 0x6);
printk(KERN_WARNING "Bad eraseblock %d at 0x%08x\n", printk(KERN_WARNING "Bad eraseblock %d at 0x%08x\n",
i >> 1, (unsigned int) from); i >> 1, (unsigned int) from);
...@@ -168,8 +171,8 @@ static int onenand_isbad_bbt(struct mtd_info *mtd, loff_t offs, int allowbbt) ...@@ -168,8 +171,8 @@ static int onenand_isbad_bbt(struct mtd_info *mtd, loff_t offs, int allowbbt)
* marked good / bad blocks and writes the bad block table(s) to * marked good / bad blocks and writes the bad block table(s) to
* the selected place. * the selected place.
* *
* The bad block table memory is allocated here. It must be freed * The bad block table memory is allocated here. It is freed
* by calling the onenand_free_bbt function. * by the onenand_release function.
* *
*/ */
int onenand_scan_bbt(struct mtd_info *mtd, struct nand_bbt_descr *bd) int onenand_scan_bbt(struct mtd_info *mtd, struct nand_bbt_descr *bd)
......
...@@ -94,8 +94,19 @@ static int parse_redboot_partitions(struct mtd_info *master, ...@@ -94,8 +94,19 @@ static int parse_redboot_partitions(struct mtd_info *master,
* (NOTE: this is 'size' not 'data_length'; size is * (NOTE: this is 'size' not 'data_length'; size is
* the full size of the entry.) * the full size of the entry.)
*/ */
if (swab32(buf[i].size) == master->erasesize) {
/* RedBoot can combine the FIS directory and
config partitions into a single eraseblock;
we assume wrong-endian if either the swapped
'size' matches the eraseblock size precisely,
or if the swapped size actually fits in an
eraseblock while the unswapped size doesn't. */
if (swab32(buf[i].size) == master->erasesize ||
(buf[i].size > master->erasesize
&& swab32(buf[i].size) < master->erasesize)) {
int j; int j;
/* Update numslots based on actual FIS directory size */
numslots = swab32(buf[i].size) / sizeof (struct fis_image_desc);
for (j = 0; j < numslots; ++j) { for (j = 0; j < numslots; ++j) {
/* A single 0xff denotes a deleted entry. /* A single 0xff denotes a deleted entry.
...@@ -120,11 +131,11 @@ static int parse_redboot_partitions(struct mtd_info *master, ...@@ -120,11 +131,11 @@ static int parse_redboot_partitions(struct mtd_info *master,
swab32s(&buf[j].desc_cksum); swab32s(&buf[j].desc_cksum);
swab32s(&buf[j].file_cksum); swab32s(&buf[j].file_cksum);
} }
} else if (buf[i].size < master->erasesize) {
/* Update numslots based on actual FIS directory size */
numslots = buf[i].size / sizeof(struct fis_image_desc);
} }
break; break;
} else {
/* re-calculate of real numslots */
numslots = buf[i].size / sizeof(struct fis_image_desc);
} }
} }
if (i == numslots) { if (i == numslots) {
......
...@@ -348,23 +348,27 @@ int jffs2_do_mount_fs(struct jffs2_sb_info *c) ...@@ -348,23 +348,27 @@ int jffs2_do_mount_fs(struct jffs2_sb_info *c)
ret = jffs2_sum_init(c); ret = jffs2_sum_init(c);
if (ret) if (ret)
return ret; goto out_free;
if (jffs2_build_filesystem(c)) { if (jffs2_build_filesystem(c)) {
dbg_fsbuild("build_fs failed\n"); dbg_fsbuild("build_fs failed\n");
jffs2_free_ino_caches(c); jffs2_free_ino_caches(c);
jffs2_free_raw_node_refs(c); jffs2_free_raw_node_refs(c);
#ifndef __ECOS ret = -EIO;
if (jffs2_blocks_use_vmalloc(c)) goto out_free;
vfree(c->blocks);
else
#endif
kfree(c->blocks);
return -EIO;
} }
jffs2_calc_trigger_levels(c); jffs2_calc_trigger_levels(c);
return 0; return 0;
out_free:
#ifndef __ECOS
if (jffs2_blocks_use_vmalloc(c))
vfree(c->blocks);
else
#endif
kfree(c->blocks);
return ret;
} }
...@@ -98,20 +98,14 @@ struct jffs2_sb_info { ...@@ -98,20 +98,14 @@ struct jffs2_sb_info {
uint32_t wbuf_pagesize; /* 0 for NOR and other flashes with no wbuf */ uint32_t wbuf_pagesize; /* 0 for NOR and other flashes with no wbuf */
#ifdef CONFIG_JFFS2_FS_WRITEBUFFER #ifdef CONFIG_JFFS2_FS_WRITEBUFFER
/* Write-behind buffer for NAND flash */ unsigned char *wbuf; /* Write-behind buffer for NAND flash */
unsigned char *wbuf;
unsigned char *oobbuf;
uint32_t wbuf_ofs; uint32_t wbuf_ofs;
uint32_t wbuf_len; uint32_t wbuf_len;
struct jffs2_inodirty *wbuf_inodes; struct jffs2_inodirty *wbuf_inodes;
struct rw_semaphore wbuf_sem; /* Protects the write buffer */ struct rw_semaphore wbuf_sem; /* Protects the write buffer */
/* Information about out-of-band area usage... */ unsigned char *oobbuf;
struct nand_ecclayout *ecclayout; int oobavail; /* How many bytes are available for JFFS2 in OOB */
uint32_t badblock_pos;
uint32_t fsdata_pos;
uint32_t fsdata_len;
#endif #endif
struct jffs2_summary *summary; /* Summary information */ struct jffs2_summary *summary; /* Summary information */
......
...@@ -450,16 +450,20 @@ static int jffs2_scan_eraseblock (struct jffs2_sb_info *c, struct jffs2_eraseblo ...@@ -450,16 +450,20 @@ static int jffs2_scan_eraseblock (struct jffs2_sb_info *c, struct jffs2_eraseblo
#ifdef CONFIG_JFFS2_FS_WRITEBUFFER #ifdef CONFIG_JFFS2_FS_WRITEBUFFER
if (jffs2_cleanmarker_oob(c)) { if (jffs2_cleanmarker_oob(c)) {
int ret = jffs2_check_nand_cleanmarker(c, jeb); int ret;
if (c->mtd->block_isbad(c->mtd, jeb->offset))
return BLK_STATE_BADBLOCK;
ret = jffs2_check_nand_cleanmarker(c, jeb);
D2(printk(KERN_NOTICE "jffs_check_nand_cleanmarker returned %d\n",ret)); D2(printk(KERN_NOTICE "jffs_check_nand_cleanmarker returned %d\n",ret));
/* Even if it's not found, we still scan to see /* Even if it's not found, we still scan to see
if the block is empty. We use this information if the block is empty. We use this information
to decide whether to erase it or not. */ to decide whether to erase it or not. */
switch (ret) { switch (ret) {
case 0: cleanmarkerfound = 1; break; case 0: cleanmarkerfound = 1; break;
case 1: break; case 1: break;
case 2: return BLK_STATE_BADBLOCK;
case 3: return BLK_STATE_ALLDIRTY; /* Block has failed to erase min. once */
default: return ret; default: return ret;
} }
} }
......
...@@ -957,43 +957,48 @@ int jffs2_flash_read(struct jffs2_sb_info *c, loff_t ofs, size_t len, size_t *re ...@@ -957,43 +957,48 @@ int jffs2_flash_read(struct jffs2_sb_info *c, loff_t ofs, size_t len, size_t *re
return ret; return ret;
} }
#define NR_OOB_SCAN_PAGES 4 #define NR_OOB_SCAN_PAGES 4
/* For historical reasons we use only 12 bytes for OOB clean marker */
#define OOB_CM_SIZE 12
static const struct jffs2_unknown_node oob_cleanmarker =
{
.magic = cpu_to_je16(JFFS2_MAGIC_BITMASK),
.nodetype = cpu_to_je16(JFFS2_NODETYPE_CLEANMARKER),
.totlen = cpu_to_je32(8)
};
/* /*
* Check, if the out of band area is empty * Check, if the out of band area is empty. This function knows about the clean
* marker and if it is present in OOB, treats the OOB as empty anyway.
*/ */
int jffs2_check_oob_empty(struct jffs2_sb_info *c, int jffs2_check_oob_empty(struct jffs2_sb_info *c,
struct jffs2_eraseblock *jeb, int mode) struct jffs2_eraseblock *jeb, int mode)
{ {
int i, page, ret; int i, ret;
int oobsize = c->mtd->oobsize; int cmlen = min_t(int, c->oobavail, OOB_CM_SIZE);
struct mtd_oob_ops ops; struct mtd_oob_ops ops;
ops.ooblen = NR_OOB_SCAN_PAGES * oobsize; ops.mode = MTD_OOB_AUTO;
ops.ooblen = NR_OOB_SCAN_PAGES * c->oobavail;
ops.oobbuf = c->oobbuf; ops.oobbuf = c->oobbuf;
ops.ooboffs = 0; ops.len = ops.ooboffs = ops.retlen = ops.oobretlen = 0;
ops.datbuf = NULL; ops.datbuf = NULL;
ops.mode = MTD_OOB_PLACE;
ret = c->mtd->read_oob(c->mtd, jeb->offset, &ops); ret = c->mtd->read_oob(c->mtd, jeb->offset, &ops);
if (ret) { if (ret || ops.oobretlen != ops.ooblen) {
D1(printk(KERN_WARNING "jffs2_check_oob_empty(): Read OOB " printk(KERN_ERR "cannot read OOB for EB at %08x, requested %zd"
"failed %d for block at %08x\n", ret, jeb->offset)); " bytes, read %zd bytes, error %d\n",
jeb->offset, ops.ooblen, ops.oobretlen, ret);
if (!ret)
ret = -EIO;
return ret; return ret;
} }
if (ops.oobretlen < ops.ooblen) { for(i = 0; i < ops.ooblen; i++) {
D1(printk(KERN_WARNING "jffs2_check_oob_empty(): Read OOB " if (mode && i < cmlen)
"returned short read (%zd bytes not %d) for block " /* Yeah, we know about the cleanmarker */
"at %08x\n", ops.oobretlen, ops.ooblen, jeb->offset));
return -EIO;
}
/* Special check for first page */
for(i = 0; i < oobsize ; i++) {
/* Yeah, we know about the cleanmarker. */
if (mode && i >= c->fsdata_pos &&
i < c->fsdata_pos + c->fsdata_len)
continue; continue;
if (ops.oobbuf[i] != 0xFF) { if (ops.oobbuf[i] != 0xFF) {
...@@ -1003,111 +1008,63 @@ int jffs2_check_oob_empty(struct jffs2_sb_info *c, ...@@ -1003,111 +1008,63 @@ int jffs2_check_oob_empty(struct jffs2_sb_info *c,
} }
} }
/* we know, we are aligned :) */
for (page = oobsize; page < ops.ooblen; page += sizeof(long)) {
long dat = *(long *)(&ops.oobbuf[page]);
if(dat != -1)
return 1;
}
return 0; return 0;
} }
/* /*
* Scan for a valid cleanmarker and for bad blocks * Check for a valid cleanmarker.
* Returns: 0 if a valid cleanmarker was found
* 1 if no cleanmarker was found
* negative error code if an error occurred
*/ */
int jffs2_check_nand_cleanmarker (struct jffs2_sb_info *c, int jffs2_check_nand_cleanmarker(struct jffs2_sb_info *c,
struct jffs2_eraseblock *jeb) struct jffs2_eraseblock *jeb)
{ {
struct jffs2_unknown_node n;
struct mtd_oob_ops ops; struct mtd_oob_ops ops;
int oobsize = c->mtd->oobsize; int ret, cmlen = min_t(int, c->oobavail, OOB_CM_SIZE);
unsigned char *p,*b;
int i, ret;
size_t offset = jeb->offset;
/* Check first if the block is bad. */
if (c->mtd->block_isbad(c->mtd, offset)) {
D1 (printk(KERN_WARNING "jffs2_check_nand_cleanmarker()"
": Bad block at %08x\n", jeb->offset));
return 2;
}
ops.ooblen = oobsize; ops.mode = MTD_OOB_AUTO;
ops.ooblen = cmlen;
ops.oobbuf = c->oobbuf; ops.oobbuf = c->oobbuf;
ops.ooboffs = 0; ops.len = ops.ooboffs = ops.retlen = ops.oobretlen = 0;
ops.datbuf = NULL; ops.datbuf = NULL;
ops.mode = MTD_OOB_PLACE;
ret = c->mtd->read_oob(c->mtd, offset, &ops); ret = c->mtd->read_oob(c->mtd, jeb->offset, &ops);
if (ret) { if (ret || ops.oobretlen != ops.ooblen) {
D1 (printk(KERN_WARNING "jffs2_check_nand_cleanmarker(): " printk(KERN_ERR "cannot read OOB for EB at %08x, requested %zd"
"Read OOB failed %d for block at %08x\n", " bytes, read %zd bytes, error %d\n",
ret, jeb->offset)); jeb->offset, ops.ooblen, ops.oobretlen, ret);
if (!ret)
ret = -EIO;
return ret; return ret;
} }
if (ops.oobretlen < ops.ooblen) { return !!memcmp(&oob_cleanmarker, c->oobbuf, cmlen);
D1 (printk (KERN_WARNING "jffs2_check_nand_cleanmarker(): "
"Read OOB return short read (%zd bytes not %d) "
"for block at %08x\n", ops.oobretlen, ops.ooblen,
jeb->offset));
return -EIO;
}
n.magic = cpu_to_je16 (JFFS2_MAGIC_BITMASK);
n.nodetype = cpu_to_je16 (JFFS2_NODETYPE_CLEANMARKER);
n.totlen = cpu_to_je32 (8);
p = (unsigned char *) &n;
b = c->oobbuf + c->fsdata_pos;
for (i = c->fsdata_len; i; i--) {
if (*b++ != *p++)
ret = 1;
}
D1(if (ret == 1) {
printk(KERN_WARNING "jffs2_check_nand_cleanmarker(): "
"Cleanmarker node not detected in block at %08x\n",
offset);
printk(KERN_WARNING "OOB at %08zx was ", offset);
for (i=0; i < oobsize; i++)
printk("%02x ", c->oobbuf[i]);
printk("\n");
});
return ret;
} }
int jffs2_write_nand_cleanmarker(struct jffs2_sb_info *c, int jffs2_write_nand_cleanmarker(struct jffs2_sb_info *c,
struct jffs2_eraseblock *jeb) struct jffs2_eraseblock *jeb)
{ {
struct jffs2_unknown_node n; int ret;
int ret;
struct mtd_oob_ops ops; struct mtd_oob_ops ops;
int cmlen = min_t(int, c->oobavail, OOB_CM_SIZE);
n.magic = cpu_to_je16(JFFS2_MAGIC_BITMASK); ops.mode = MTD_OOB_AUTO;
n.nodetype = cpu_to_je16(JFFS2_NODETYPE_CLEANMARKER); ops.ooblen = cmlen;
n.totlen = cpu_to_je32(8); ops.oobbuf = (uint8_t *)&oob_cleanmarker;
ops.len = ops.ooboffs = ops.retlen = ops.oobretlen = 0;
ops.ooblen = c->fsdata_len;
ops.oobbuf = (uint8_t *)&n;
ops.ooboffs = c->fsdata_pos;
ops.datbuf = NULL; ops.datbuf = NULL;
ops.mode = MTD_OOB_PLACE;
ret = c->mtd->write_oob(c->mtd, jeb->offset, &ops); ret = c->mtd->write_oob(c->mtd, jeb->offset, &ops);
if (ret || ops.oobretlen != ops.ooblen) {
if (ret) { printk(KERN_ERR "cannot write OOB for EB at %08x, requested %zd"
D1(printk(KERN_WARNING "jffs2_write_nand_cleanmarker(): " " bytes, read %zd bytes, error %d\n",
"Write failed for block at %08x: error %d\n", jeb->offset, ops.ooblen, ops.oobretlen, ret);
jeb->offset, ret)); if (!ret)
ret = -EIO;
return ret; return ret;
} }
if (ops.oobretlen != ops.ooblen) {
D1(printk(KERN_WARNING "jffs2_write_nand_cleanmarker(): "
"Short write for block at %08x: %zd not %d\n",
jeb->offset, ops.oobretlen, ops.ooblen));
return -EIO;
}
return 0; return 0;
} }
...@@ -1140,41 +1097,24 @@ int jffs2_write_nand_badblock(struct jffs2_sb_info *c, struct jffs2_eraseblock * ...@@ -1140,41 +1097,24 @@ int jffs2_write_nand_badblock(struct jffs2_sb_info *c, struct jffs2_eraseblock *
return 1; return 1;
} }
static int jffs2_nand_set_oobinfo(struct jffs2_sb_info *c) int jffs2_nand_flash_setup(struct jffs2_sb_info *c)
{ {
struct nand_ecclayout *oinfo = c->mtd->ecclayout; struct nand_ecclayout *oinfo = c->mtd->ecclayout;
/* Do this only, if we have an oob buffer */
if (!c->mtd->oobsize) if (!c->mtd->oobsize)
return 0; return 0;
/* Cleanmarker is out-of-band, so inline size zero */ /* Cleanmarker is out-of-band, so inline size zero */
c->cleanmarker_size = 0; c->cleanmarker_size = 0;
/* Should we use autoplacement ? */ if (!oinfo || oinfo->oobavail == 0) {
if (!oinfo) { printk(KERN_ERR "inconsistent device description\n");
D1(printk(KERN_DEBUG "JFFS2 on NAND. No autoplacment info found\n"));
return -EINVAL; return -EINVAL;
} }
D1(printk(KERN_DEBUG "JFFS2 using autoplace on NAND\n")); D1(printk(KERN_DEBUG "JFFS2 using OOB on NAND\n"));
/* Get the position of the free bytes */
if (!oinfo->oobfree[0].length) {
printk (KERN_WARNING "jffs2_nand_set_oobinfo(): Eeep."
" Autoplacement selected and no empty space in oob\n");
return -ENOSPC;
}
c->fsdata_pos = oinfo->oobfree[0].offset;
c->fsdata_len = oinfo->oobfree[0].length;
if (c->fsdata_len > 8)
c->fsdata_len = 8;
return 0; c->oobavail = oinfo->oobavail;
}
int jffs2_nand_flash_setup(struct jffs2_sb_info *c)
{
int res;
/* Initialise write buffer */ /* Initialise write buffer */
init_rwsem(&c->wbuf_sem); init_rwsem(&c->wbuf_sem);
...@@ -1185,22 +1125,13 @@ int jffs2_nand_flash_setup(struct jffs2_sb_info *c) ...@@ -1185,22 +1125,13 @@ int jffs2_nand_flash_setup(struct jffs2_sb_info *c)
if (!c->wbuf) if (!c->wbuf)
return -ENOMEM; return -ENOMEM;
c->oobbuf = kmalloc(NR_OOB_SCAN_PAGES * c->mtd->oobsize, GFP_KERNEL); c->oobbuf = kmalloc(NR_OOB_SCAN_PAGES * c->oobavail, GFP_KERNEL);
if (!c->oobbuf) if (!c->oobbuf) {
return -ENOMEM;
res = jffs2_nand_set_oobinfo(c);
#ifdef BREAKME
if (!brokenbuf)
brokenbuf = kmalloc(c->wbuf_pagesize, GFP_KERNEL);
if (!brokenbuf) {
kfree(c->wbuf); kfree(c->wbuf);
return -ENOMEM; return -ENOMEM;
} }
memset(brokenbuf, 0xdb, c->wbuf_pagesize);
#endif return 0;
return res;
} }
void jffs2_nand_flash_cleanup(struct jffs2_sb_info *c) void jffs2_nand_flash_cleanup(struct jffs2_sb_info *c)
......
...@@ -92,6 +92,13 @@ struct nand_bbt_descr { ...@@ -92,6 +92,13 @@ struct nand_bbt_descr {
*/ */
#define ONENAND_BADBLOCK_POS 0 #define ONENAND_BADBLOCK_POS 0
/*
* Bad block scanning errors
*/
#define ONENAND_BBT_READ_ERROR 1
#define ONENAND_BBT_READ_ECC_ERROR 2
#define ONENAND_BBT_READ_FATAL_ERROR 4
/** /**
* struct bbm_info - [GENERIC] Bad Block Table data structure * struct bbm_info - [GENERIC] Bad Block Table data structure
* @bbt_erase_shift: [INTERN] number of address bits in a bbt entry * @bbt_erase_shift: [INTERN] number of address bits in a bbt entry
......
...@@ -183,7 +183,7 @@ typedef union { ...@@ -183,7 +183,7 @@ typedef union {
struct map_info { struct map_info {
char *name; char *name;
unsigned long size; unsigned long size;
unsigned long phys; resource_size_t phys;
#define NO_XIP (-1UL) #define NO_XIP (-1UL)
void __iomem *virt; void __iomem *virt;
......
...@@ -85,6 +85,10 @@ typedef enum { ...@@ -85,6 +85,10 @@ typedef enum {
* mode = MTD_OOB_PLACE) * mode = MTD_OOB_PLACE)
* @datbuf: data buffer - if NULL only oob data are read/written * @datbuf: data buffer - if NULL only oob data are read/written
* @oobbuf: oob data buffer * @oobbuf: oob data buffer
*
* Note, it is allowed to read more then one OOB area at one go, but not write.
* The interface assumes that the OOB write requests program only one page's
* OOB area.
*/ */
struct mtd_oob_ops { struct mtd_oob_ops {
mtd_oob_mode_t mode; mtd_oob_mode_t mode;
...@@ -117,18 +121,6 @@ struct mtd_info { ...@@ -117,18 +121,6 @@ struct mtd_info {
u_int32_t writesize; u_int32_t writesize;
u_int32_t oobsize; // Amount of OOB data per block (e.g. 16) u_int32_t oobsize; // Amount of OOB data per block (e.g. 16)
u_int32_t ecctype;
u_int32_t eccsize;
/*
* Reuse some of the above unused fields in the case of NOR flash
* with configurable programming regions to avoid modifying the
* user visible structure layout/size. Only valid when the
* MTD_PROGRAM_REGIONS flag is set.
* (Maybe we should have an union for those?)
*/
#define MTD_PROGREGION_CTRLMODE_VALID(mtd) (mtd)->oobsize
#define MTD_PROGREGION_CTRLMODE_INVALID(mtd) (mtd)->ecctype
// Kernel-only stuff starts here. // Kernel-only stuff starts here.
char *name; char *name;
......
...@@ -343,6 +343,7 @@ struct nand_buffers { ...@@ -343,6 +343,7 @@ struct nand_buffers {
* @options: [BOARDSPECIFIC] various chip options. They can partly be set to inform nand_scan about * @options: [BOARDSPECIFIC] various chip options. They can partly be set to inform nand_scan about
* special functionality. See the defines for further explanation * special functionality. See the defines for further explanation
* @badblockpos: [INTERN] position of the bad block marker in the oob area * @badblockpos: [INTERN] position of the bad block marker in the oob area
* @cellinfo: [INTERN] MLC/multichip data from chip ident
* @numchips: [INTERN] number of physical chips * @numchips: [INTERN] number of physical chips
* @chipsize: [INTERN] the size of one chip for multichip arrays * @chipsize: [INTERN] the size of one chip for multichip arrays
* @pagemask: [INTERN] page number mask = number of (pages / chip) - 1 * @pagemask: [INTERN] page number mask = number of (pages / chip) - 1
......
/* /*
* linux/include/linux/mtd/onenand.h * linux/include/linux/mtd/onenand.h
* *
* Copyright (C) 2005-2006 Samsung Electronics * Copyright (C) 2005-2007 Samsung Electronics
* Kyungmin Park <kyungmin.park@samsung.com> * Kyungmin Park <kyungmin.park@samsung.com>
* *
* This program is free software; you can redistribute it and/or modify * This program is free software; you can redistribute it and/or modify
...@@ -42,14 +42,10 @@ typedef enum { ...@@ -42,14 +42,10 @@ typedef enum {
/** /**
* struct onenand_bufferram - OneNAND BufferRAM Data * struct onenand_bufferram - OneNAND BufferRAM Data
* @block: block address in BufferRAM * @blockpage: block & page address in BufferRAM
* @page: page address in BufferRAM
* @valid: valid flag
*/ */
struct onenand_bufferram { struct onenand_bufferram {
int block; int blockpage;
int page;
int valid;
}; };
/** /**
...@@ -63,7 +59,6 @@ struct onenand_bufferram { ...@@ -63,7 +59,6 @@ struct onenand_bufferram {
* partly be set to inform onenand_scan about * partly be set to inform onenand_scan about
* @erase_shift: [INTERN] number of address bits in a block * @erase_shift: [INTERN] number of address bits in a block
* @page_shift: [INTERN] number of address bits in a page * @page_shift: [INTERN] number of address bits in a page
* @ppb_shift: [INTERN] number of address bits in a pages per block
* @page_mask: [INTERN] a page per block mask * @page_mask: [INTERN] a page per block mask
* @bufferram_index: [INTERN] BufferRAM index * @bufferram_index: [INTERN] BufferRAM index
* @bufferram: [INTERN] BufferRAM info * @bufferram: [INTERN] BufferRAM info
...@@ -103,7 +98,6 @@ struct onenand_chip { ...@@ -103,7 +98,6 @@ struct onenand_chip {
unsigned int erase_shift; unsigned int erase_shift;
unsigned int page_shift; unsigned int page_shift;
unsigned int ppb_shift; /* Pages per block shift */
unsigned int page_mask; unsigned int page_mask;
unsigned int bufferram_index; unsigned int bufferram_index;
...@@ -150,6 +144,9 @@ struct onenand_chip { ...@@ -150,6 +144,9 @@ struct onenand_chip {
#define ONENAND_SET_SYS_CFG1(v, this) \ #define ONENAND_SET_SYS_CFG1(v, this) \
(this->write_word(v, this->base + ONENAND_REG_SYS_CFG1)) (this->write_word(v, this->base + ONENAND_REG_SYS_CFG1))
#define ONENAND_IS_DDP(this) \
(this->device_id & ONENAND_DEVICE_IS_DDP)
/* Check byte access in OneNAND */ /* Check byte access in OneNAND */
#define ONENAND_CHECK_BYTE_ACCESS(addr) (addr & 0x1) #define ONENAND_CHECK_BYTE_ACCESS(addr) (addr & 0x1)
......
...@@ -3,7 +3,8 @@ ...@@ -3,7 +3,8 @@
* *
* OneNAND Register header file * OneNAND Register header file
* *
* Copyright (C) 2005-2006 Samsung Electronics * Copyright (C) 2005-2007 Samsung Electronics
* Kyungmin Park <kyungmin.park@samsung.com>
* *
* This program is free software; you can redistribute it and/or modify * This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 as * it under the terms of the GNU General Public License version 2 as
...@@ -80,9 +81,11 @@ ...@@ -80,9 +81,11 @@
#define ONENAND_VERSION_PROCESS_SHIFT (8) #define ONENAND_VERSION_PROCESS_SHIFT (8)
/* /*
* Start Address 1 F100h (R/W) * Start Address 1 F100h (R/W) & Start Address 2 F101h (R/W)
*/ */
#define ONENAND_DDP_SHIFT (15) #define ONENAND_DDP_SHIFT (15)
#define ONENAND_DDP_CHIP0 (0)
#define ONENAND_DDP_CHIP1 (1 << ONENAND_DDP_SHIFT)
/* /*
* Start Address 8 F107h (R/W) * Start Address 8 F107h (R/W)
......
...@@ -18,9 +18,10 @@ ...@@ -18,9 +18,10 @@
#define __LINUX_MTD_PHYSMAP__ #define __LINUX_MTD_PHYSMAP__
#include <linux/mtd/mtd.h> #include <linux/mtd/mtd.h>
#include <linux/mtd/map.h>
#include <linux/mtd/partitions.h> #include <linux/mtd/partitions.h>
struct map_info;
struct physmap_flash_data { struct physmap_flash_data {
unsigned int width; unsigned int width;
void (*set_vpp)(struct map_info *, int); void (*set_vpp)(struct map_info *, int);
......
...@@ -36,12 +36,6 @@ struct mtd_oob_buf { ...@@ -36,12 +36,6 @@ struct mtd_oob_buf {
#define MTD_CAP_NORFLASH (MTD_WRITEABLE | MTD_BIT_WRITEABLE) #define MTD_CAP_NORFLASH (MTD_WRITEABLE | MTD_BIT_WRITEABLE)
#define MTD_CAP_NANDFLASH (MTD_WRITEABLE) #define MTD_CAP_NANDFLASH (MTD_WRITEABLE)
// Types of automatic ECC/Checksum available
#define MTD_ECC_NONE 0 // No automatic ECC available
#define MTD_ECC_RS_DiskOnChip 1 // Automatic ECC on DiskOnChip
#define MTD_ECC_SW 2 // SW ECC for Toshiba & Samsung devices
/* ECC byte placement */ /* ECC byte placement */
#define MTD_NANDECC_OFF 0 // Switch off ECC (Not recommended) #define MTD_NANDECC_OFF 0 // Switch off ECC (Not recommended)
#define MTD_NANDECC_PLACE 1 // Use the given placement in the structure (YAFFS1 legacy mode) #define MTD_NANDECC_PLACE 1 // Use the given placement in the structure (YAFFS1 legacy mode)
...@@ -61,6 +55,8 @@ struct mtd_info_user { ...@@ -61,6 +55,8 @@ struct mtd_info_user {
uint32_t erasesize; uint32_t erasesize;
uint32_t writesize; uint32_t writesize;
uint32_t oobsize; // Amount of OOB data per block (e.g. 16) uint32_t oobsize; // Amount of OOB data per block (e.g. 16)
/* The below two fields are obsolete and broken, do not use them
* (TODO: remove at some point) */
uint32_t ecctype; uint32_t ecctype;
uint32_t eccsize; uint32_t eccsize;
}; };
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment