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:
Linus Torvalds 2007-02-19 13:34:11 -08:00
commit 4935361766
37 changed files with 1050 additions and 470 deletions

View file

@ -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,
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);
} }
/* /*

View file

@ -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

View file

@ -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;

View file

@ -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 */

View file

@ -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;

View file

@ -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.

View file

@ -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;

View file

@ -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)
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;
} }

View file

@ -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

View file

@ -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

View file

@ -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) {

View file

@ -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);

View 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;

View file

@ -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
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);

View file

@ -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;

View file

@ -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
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.

View file

@ -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

View file

@ -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,
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,
{ {
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,
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,
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);
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
*/

View file

@ -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)

View file

@ -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)

View file

@ -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,
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)
/* 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;

View file

@ -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];
/* we curently have no method for correcting the error */ 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);
return -1; 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;
byte |= (diff2 << 8) & 0x100;
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)
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
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
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,
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;

View file

@ -1,7 +1,7 @@
/* /*
* linux/drivers/mtd/onenand/onenand_base.c * linux/drivers/mtd/onenand/onenand_base.c
* *
* 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
@ -94,16 +94,9 @@ static void onenand_writew(unsigned short value, void __iomem *addr)
*/ */
static int onenand_block_address(struct onenand_chip *this, int block) static int onenand_block_address(struct onenand_chip *this, int block)
{ {
if (this->device_id & ONENAND_DEVICE_IS_DDP) { /* Device Flash Core select, NAND Flash Block Address */
/* Device Flash Core select, NAND Flash Block Address */ if (block & this->density_mask)
int dfs = 0; return ONENAND_DDP_CHIP1 | (block ^ this->density_mask);
if (block & this->density_mask)
dfs = 1;
return (dfs << ONENAND_DDP_SHIFT) |
(block & (this->density_mask - 1));
}
return block; return block;
} }
@ -118,17 +111,11 @@ static int onenand_block_address(struct onenand_chip *this, int block)
*/ */
static int onenand_bufferram_address(struct onenand_chip *this, int block) static int onenand_bufferram_address(struct onenand_chip *this, int block)
{ {
if (this->device_id & ONENAND_DEVICE_IS_DDP) { /* Device BufferRAM Select */
/* Device BufferRAM Select */ if (block & this->density_mask)
int dbs = 0; return ONENAND_DDP_CHIP1;
if (block & this->density_mask) return ONENAND_DDP_CHIP0;
dbs = 1;
return (dbs << ONENAND_DDP_SHIFT);
}
return 0;
} }
/** /**
@ -317,22 +304,25 @@ static int onenand_wait(struct mtd_info *mtd, int state)
ctrl = this->read_word(this->base + ONENAND_REG_CTRL_STATUS); ctrl = this->read_word(this->base + ONENAND_REG_CTRL_STATUS);
if (ctrl & ONENAND_CTRL_ERROR) { if (ctrl & ONENAND_CTRL_ERROR) {
DEBUG(MTD_DEBUG_LEVEL0, "onenand_wait: controller error = 0x%04x\n", ctrl); printk(KERN_ERR "onenand_wait: controller error = 0x%04x\n", ctrl);
if (ctrl & ONENAND_CTRL_LOCK) if (ctrl & ONENAND_CTRL_LOCK)
DEBUG(MTD_DEBUG_LEVEL0, "onenand_wait: it's locked error.\n"); printk(KERN_ERR "onenand_wait: it's locked error.\n");
return ctrl; return ctrl;
} }
if (interrupt & ONENAND_INT_READ) { if (interrupt & ONENAND_INT_READ) {
int ecc = this->read_word(this->base + ONENAND_REG_ECC_STATUS); int ecc = this->read_word(this->base + ONENAND_REG_ECC_STATUS);
if (ecc) { if (ecc) {
DEBUG(MTD_DEBUG_LEVEL0, "onenand_wait: ECC error = 0x%04x\n", ecc); printk(KERN_ERR "onenand_wait: ECC error = 0x%04x\n", ecc);
if (ecc & ONENAND_ECC_2BIT_ALL) { if (ecc & ONENAND_ECC_2BIT_ALL) {
mtd->ecc_stats.failed++; mtd->ecc_stats.failed++;
return ecc; return ecc;
} else if (ecc & ONENAND_ECC_1BIT_ALL) } else if (ecc & ONENAND_ECC_1BIT_ALL)
mtd->ecc_stats.corrected++; mtd->ecc_stats.corrected++;
} }
} else if (state == FL_READING) {
printk(KERN_ERR "onenand_wait: read timeout! ctrl=0x%04x intr=0x%04x\n", ctrl, interrupt);
return -EIO;
} }
return 0; return 0;
@ -587,22 +577,32 @@ static int onenand_write_bufferram(struct mtd_info *mtd, int area,
static int onenand_check_bufferram(struct mtd_info *mtd, loff_t addr) static int onenand_check_bufferram(struct mtd_info *mtd, loff_t addr)
{ {
struct onenand_chip *this = mtd->priv; struct onenand_chip *this = mtd->priv;
int block, page; int blockpage, found = 0;
int i; unsigned int i;
block = (int) (addr >> this->erase_shift); blockpage = (int) (addr >> this->page_shift);
page = (int) (addr >> this->page_shift);
page &= this->page_mask;
i = ONENAND_CURRENT_BUFFERRAM(this);
/* Is there valid data? */ /* Is there valid data? */
if (this->bufferram[i].block == block && i = ONENAND_CURRENT_BUFFERRAM(this);
this->bufferram[i].page == page && if (this->bufferram[i].blockpage == blockpage)
this->bufferram[i].valid) found = 1;
return 1; else {
/* Check another BufferRAM */
i = ONENAND_NEXT_BUFFERRAM(this);
if (this->bufferram[i].blockpage == blockpage) {
ONENAND_SET_NEXT_BUFFERRAM(this);
found = 1;
}
}
return 0; if (found && ONENAND_IS_DDP(this)) {
/* Select DataRAM for DDP */
int block = (int) (addr >> this->erase_shift);
int value = onenand_bufferram_address(this, block);
this->write_word(value, this->base + ONENAND_REG_START_ADDRESS2);
}
return found;
} }
/** /**
@ -613,31 +613,49 @@ static int onenand_check_bufferram(struct mtd_info *mtd, loff_t addr)
* *
* Update BufferRAM information * Update BufferRAM information
*/ */
static int onenand_update_bufferram(struct mtd_info *mtd, loff_t addr, static void onenand_update_bufferram(struct mtd_info *mtd, loff_t addr,
int valid) int valid)
{ {
struct onenand_chip *this = mtd->priv; struct onenand_chip *this = mtd->priv;
int block, page; int blockpage;
int i; unsigned int i;
block = (int) (addr >> this->erase_shift); blockpage = (int) (addr >> this->page_shift);
page = (int) (addr >> this->page_shift);
page &= this->page_mask;
/* Invalidate BufferRAM */ /* Invalidate another BufferRAM */
for (i = 0; i < MAX_BUFFERRAM; i++) { i = ONENAND_NEXT_BUFFERRAM(this);
if (this->bufferram[i].block == block && if (this->bufferram[i].blockpage == blockpage)
this->bufferram[i].page == page) this->bufferram[i].blockpage = -1;
this->bufferram[i].valid = 0;
}
/* Update BufferRAM */ /* Update BufferRAM */
i = ONENAND_CURRENT_BUFFERRAM(this); i = ONENAND_CURRENT_BUFFERRAM(this);
this->bufferram[i].block = block; if (valid)
this->bufferram[i].page = page; this->bufferram[i].blockpage = blockpage;
this->bufferram[i].valid = valid; else
this->bufferram[i].blockpage = -1;
}
return 0; /**
* onenand_invalidate_bufferram - [GENERIC] Invalidate BufferRAM information
* @param mtd MTD data structure
* @param addr start address to invalidate
* @param len length to invalidate
*
* Invalidate BufferRAM information
*/
static void onenand_invalidate_bufferram(struct mtd_info *mtd, loff_t addr,
unsigned int len)
{
struct onenand_chip *this = mtd->priv;
int i;
loff_t end_addr = addr + len;
/* Invalidate BufferRAM */
for (i = 0; i < MAX_BUFFERRAM; i++) {
loff_t buf_addr = this->bufferram[i].blockpage << this->page_shift;
if (buf_addr >= addr && buf_addr < end_addr)
this->bufferram[i].blockpage = -1;
}
} }
/** /**
@ -716,7 +734,7 @@ static int onenand_read(struct mtd_info *mtd, loff_t from, size_t len,
/* Do not allow reads past end of device */ /* Do not allow reads past end of device */
if ((from + len) > mtd->size) { if ((from + len) > mtd->size) {
DEBUG(MTD_DEBUG_LEVEL0, "onenand_read: Attempt read beyond end of device\n"); printk(KERN_ERR "onenand_read: Attempt read beyond end of device\n");
*retlen = 0; *retlen = 0;
return -EINVAL; return -EINVAL;
} }
@ -724,8 +742,6 @@ static int onenand_read(struct mtd_info *mtd, loff_t from, size_t len,
/* Grab the lock and see if the device is available */ /* Grab the lock and see if the device is available */
onenand_get_device(mtd, FL_READING); onenand_get_device(mtd, FL_READING);
/* TODO handling oob */
stats = mtd->ecc_stats; stats = mtd->ecc_stats;
/* Read-while-load method */ /* Read-while-load method */
@ -754,9 +770,9 @@ static int onenand_read(struct mtd_info *mtd, loff_t from, size_t len,
* Now we issued chip 1 read and pointed chip 1 * Now we issued chip 1 read and pointed chip 1
* bufferam so we have to point chip 0 bufferam. * bufferam so we have to point chip 0 bufferam.
*/ */
if (this->device_id & ONENAND_DEVICE_IS_DDP && if (ONENAND_IS_DDP(this) &&
unlikely(from == (this->chipsize >> 1))) { unlikely(from == (this->chipsize >> 1))) {
this->write_word(0, this->base + ONENAND_REG_START_ADDRESS2); this->write_word(ONENAND_DDP_CHIP0, this->base + ONENAND_REG_START_ADDRESS2);
boundary = 1; boundary = 1;
} else } else
boundary = 0; boundary = 0;
@ -770,7 +786,7 @@ static int onenand_read(struct mtd_info *mtd, loff_t from, size_t len,
break; break;
/* Set up for next read from bufferRAM */ /* Set up for next read from bufferRAM */
if (unlikely(boundary)) if (unlikely(boundary))
this->write_word(0x8000, this->base + ONENAND_REG_START_ADDRESS2); this->write_word(ONENAND_DDP_CHIP1, this->base + ONENAND_REG_START_ADDRESS2);
ONENAND_SET_NEXT_BUFFERRAM(this); ONENAND_SET_NEXT_BUFFERRAM(this);
buf += thislen; buf += thislen;
thislen = min_t(int, mtd->writesize, len - read); thislen = min_t(int, mtd->writesize, len - read);
@ -800,6 +816,44 @@ static int onenand_read(struct mtd_info *mtd, loff_t from, size_t len,
return mtd->ecc_stats.corrected - stats.corrected ? -EUCLEAN : 0; return mtd->ecc_stats.corrected - stats.corrected ? -EUCLEAN : 0;
} }
/**
* onenand_transfer_auto_oob - [Internal] oob auto-placement transfer
* @param mtd MTD device structure
* @param buf destination address
* @param column oob offset to read from
* @param thislen oob length to read
*/
static int onenand_transfer_auto_oob(struct mtd_info *mtd, uint8_t *buf, int column,
int thislen)
{
struct onenand_chip *this = mtd->priv;
struct nand_oobfree *free;
int readcol = column;
int readend = column + thislen;
int lastgap = 0;
uint8_t *oob_buf = this->page_buf + mtd->writesize;
for (free = this->ecclayout->oobfree; free->length; ++free) {
if (readcol >= lastgap)
readcol += free->offset - lastgap;
if (readend >= lastgap)
readend += free->offset - lastgap;
lastgap = free->offset + free->length;
}
this->read_bufferram(mtd, ONENAND_SPARERAM, oob_buf, 0, mtd->oobsize);
for (free = this->ecclayout->oobfree; free->length; ++free) {
int free_end = free->offset + free->length;
if (free->offset < readend && free_end > readcol) {
int st = max_t(int,free->offset,readcol);
int ed = min_t(int,free_end,readend);
int n = ed - st;
memcpy(buf, oob_buf + st, n);
buf += n;
}
}
return 0;
}
/** /**
* onenand_do_read_oob - [MTD Interface] OneNAND read out-of-band * onenand_do_read_oob - [MTD Interface] OneNAND read out-of-band
* @param mtd MTD device structure * @param mtd MTD device structure
@ -807,14 +861,15 @@ static int onenand_read(struct mtd_info *mtd, loff_t from, size_t len,
* @param len number of bytes to read * @param len number of bytes to read
* @param retlen pointer to variable to store the number of read bytes * @param retlen pointer to variable to store the number of read bytes
* @param buf the databuffer to put data * @param buf the databuffer to put data
* @param mode operation mode
* *
* OneNAND read out-of-band data from the spare area * OneNAND read out-of-band data from the spare area
*/ */
int onenand_do_read_oob(struct mtd_info *mtd, loff_t from, size_t len, static int onenand_do_read_oob(struct mtd_info *mtd, loff_t from, size_t len,
size_t *retlen, u_char *buf) size_t *retlen, u_char *buf, mtd_oob_mode_t mode)
{ {
struct onenand_chip *this = mtd->priv; struct onenand_chip *this = mtd->priv;
int read = 0, thislen, column; int read = 0, thislen, column, oobsize;
int ret = 0; int ret = 0;
DEBUG(MTD_DEBUG_LEVEL3, "onenand_read_oob: from = 0x%08x, len = %i\n", (unsigned int) from, (int) len); DEBUG(MTD_DEBUG_LEVEL3, "onenand_read_oob: from = 0x%08x, len = %i\n", (unsigned int) from, (int) len);
@ -822,21 +877,33 @@ int onenand_do_read_oob(struct mtd_info *mtd, loff_t from, size_t len,
/* Initialize return length value */ /* Initialize return length value */
*retlen = 0; *retlen = 0;
if (mode == MTD_OOB_AUTO)
oobsize = this->ecclayout->oobavail;
else
oobsize = mtd->oobsize;
column = from & (mtd->oobsize - 1);
if (unlikely(column >= oobsize)) {
printk(KERN_ERR "onenand_read_oob: Attempted to start read outside oob\n");
return -EINVAL;
}
/* Do not allow reads past end of device */ /* Do not allow reads past end of device */
if (unlikely((from + len) > mtd->size)) { if (unlikely(from >= mtd->size ||
DEBUG(MTD_DEBUG_LEVEL0, "onenand_read_oob: Attempt read beyond end of device\n"); column + len > ((mtd->size >> this->page_shift) -
(from >> this->page_shift)) * oobsize)) {
printk(KERN_ERR "onenand_read_oob: Attempted to read beyond end of device\n");
return -EINVAL; return -EINVAL;
} }
/* Grab the lock and see if the device is available */ /* Grab the lock and see if the device is available */
onenand_get_device(mtd, FL_READING); onenand_get_device(mtd, FL_READING);
column = from & (mtd->oobsize - 1);
while (read < len) { while (read < len) {
cond_resched(); cond_resched();
thislen = mtd->oobsize - column; thislen = oobsize - column;
thislen = min_t(int, thislen, len); thislen = min_t(int, thislen, len);
this->command(mtd, ONENAND_CMD_READOOB, from, mtd->oobsize); this->command(mtd, ONENAND_CMD_READOOB, from, mtd->oobsize);
@ -846,11 +913,14 @@ int onenand_do_read_oob(struct mtd_info *mtd, loff_t from, size_t len,
ret = this->wait(mtd, FL_READING); ret = this->wait(mtd, FL_READING);
/* First copy data and check return value for ECC handling */ /* First copy data and check return value for ECC handling */
this->read_bufferram(mtd, ONENAND_SPARERAM, buf, column, thislen); if (mode == MTD_OOB_AUTO)
onenand_transfer_auto_oob(mtd, buf, column, thislen);
else
this->read_bufferram(mtd, ONENAND_SPARERAM, buf, column, thislen);
if (ret) { if (ret) {
DEBUG(MTD_DEBUG_LEVEL0, "onenand_read_oob: read failed = 0x%x\n", ret); printk(KERN_ERR "onenand_read_oob: read failed = 0x%x\n", ret);
goto out; break;
} }
read += thislen; read += thislen;
@ -868,7 +938,6 @@ int onenand_do_read_oob(struct mtd_info *mtd, loff_t from, size_t len,
} }
} }
out:
/* Deselect and wake up anyone waiting on the device */ /* Deselect and wake up anyone waiting on the device */
onenand_release_device(mtd); onenand_release_device(mtd);
@ -885,10 +954,132 @@ out:
static int onenand_read_oob(struct mtd_info *mtd, loff_t from, static int onenand_read_oob(struct mtd_info *mtd, loff_t from,
struct mtd_oob_ops *ops) struct mtd_oob_ops *ops)
{ {
BUG_ON(ops->mode != MTD_OOB_PLACE); switch (ops->mode) {
case MTD_OOB_PLACE:
case MTD_OOB_AUTO:
break;
case MTD_OOB_RAW:
/* Not implemented yet */
default:
return -EINVAL;
}
return onenand_do_read_oob(mtd, from + ops->ooboffs, ops->ooblen, return onenand_do_read_oob(mtd, from + ops->ooboffs, ops->ooblen,
&ops->oobretlen, ops->oobbuf); &ops->oobretlen, ops->oobbuf, ops->mode);
}
/**
* onenand_bbt_wait - [DEFAULT] wait until the command is done
* @param mtd MTD device structure
* @param state state to select the max. timeout value
*
* Wait for command done.
*/
static int onenand_bbt_wait(struct mtd_info *mtd, int state)
{
struct onenand_chip *this = mtd->priv;
unsigned long timeout;
unsigned int interrupt;
unsigned int ctrl;
/* The 20 msec is enough */
timeout = jiffies + msecs_to_jiffies(20);
while (time_before(jiffies, timeout)) {
interrupt = this->read_word(this->base + ONENAND_REG_INTERRUPT);
if (interrupt & ONENAND_INT_MASTER)
break;
}
/* To get correct interrupt status in timeout case */
interrupt = this->read_word(this->base + ONENAND_REG_INTERRUPT);
ctrl = this->read_word(this->base + ONENAND_REG_CTRL_STATUS);
if (ctrl & ONENAND_CTRL_ERROR) {
printk(KERN_DEBUG "onenand_bbt_wait: controller error = 0x%04x\n", ctrl);
/* Initial bad block case */
if (ctrl & ONENAND_CTRL_LOAD)
return ONENAND_BBT_READ_ERROR;
return ONENAND_BBT_READ_FATAL_ERROR;
}
if (interrupt & ONENAND_INT_READ) {
int ecc = this->read_word(this->base + ONENAND_REG_ECC_STATUS);
if (ecc & ONENAND_ECC_2BIT_ALL)
return ONENAND_BBT_READ_ERROR;
} else {
printk(KERN_ERR "onenand_bbt_wait: read timeout!"
"ctrl=0x%04x intr=0x%04x\n", ctrl, interrupt);
return ONENAND_BBT_READ_FATAL_ERROR;
}
return 0;
}
/**
* onenand_bbt_read_oob - [MTD Interface] OneNAND read out-of-band for bbt scan
* @param mtd MTD device structure
* @param from offset to read from
* @param @ops oob operation description structure
*
* OneNAND read out-of-band data from the spare area for bbt scan
*/
int onenand_bbt_read_oob(struct mtd_info *mtd, loff_t from,
struct mtd_oob_ops *ops)
{
struct onenand_chip *this = mtd->priv;
int read = 0, thislen, column;
int ret = 0;
size_t len = ops->ooblen;
u_char *buf = ops->oobbuf;
DEBUG(MTD_DEBUG_LEVEL3, "onenand_bbt_read_oob: from = 0x%08x, len = %zi\n", (unsigned int) from, len);
/* Initialize return value */
ops->oobretlen = 0;
/* Do not allow reads past end of device */
if (unlikely((from + len) > mtd->size)) {
printk(KERN_ERR "onenand_bbt_read_oob: Attempt read beyond end of device\n");
return ONENAND_BBT_READ_FATAL_ERROR;
}
/* Grab the lock and see if the device is available */
onenand_get_device(mtd, FL_READING);
column = from & (mtd->oobsize - 1);
while (read < len) {
cond_resched();
thislen = mtd->oobsize - column;
thislen = min_t(int, thislen, len);
this->command(mtd, ONENAND_CMD_READOOB, from, mtd->oobsize);
onenand_update_bufferram(mtd, from, 0);
ret = onenand_bbt_wait(mtd, FL_READING);
if (ret)
break;
this->read_bufferram(mtd, ONENAND_SPARERAM, buf, column, thislen);
read += thislen;
if (read == len)
break;
buf += thislen;
/* Read more? */
if (read < len) {
/* Update Page size */
from += mtd->writesize;
column = 0;
}
}
/* Deselect and wake up anyone waiting on the device */
onenand_release_device(mtd);
ops->oobretlen = read;
return ret;
} }
#ifdef CONFIG_MTD_ONENAND_VERIFY_WRITE #ifdef CONFIG_MTD_ONENAND_VERIFY_WRITE
@ -897,14 +1088,12 @@ static int onenand_read_oob(struct mtd_info *mtd, loff_t from,
* @param mtd MTD device structure * @param mtd MTD device structure
* @param buf the databuffer to verify * @param buf the databuffer to verify
* @param to offset to read from * @param to offset to read from
* @param len number of bytes to read and compare
* *
*/ */
static int onenand_verify_oob(struct mtd_info *mtd, const u_char *buf, loff_t to, int len) static int onenand_verify_oob(struct mtd_info *mtd, const u_char *buf, loff_t to)
{ {
struct onenand_chip *this = mtd->priv; struct onenand_chip *this = mtd->priv;
char *readp = this->page_buf; char *readp = this->page_buf + mtd->writesize;
int column = to & (mtd->oobsize - 1);
int status, i; int status, i;
this->command(mtd, ONENAND_CMD_READOOB, to, mtd->oobsize); this->command(mtd, ONENAND_CMD_READOOB, to, mtd->oobsize);
@ -913,9 +1102,8 @@ static int onenand_verify_oob(struct mtd_info *mtd, const u_char *buf, loff_t to
if (status) if (status)
return status; return status;
this->read_bufferram(mtd, ONENAND_SPARERAM, readp, column, len); this->read_bufferram(mtd, ONENAND_SPARERAM, readp, 0, mtd->oobsize);
for(i = 0; i < mtd->oobsize; i++)
for(i = 0; i < len; i++)
if (buf[i] != 0xFF && buf[i] != readp[i]) if (buf[i] != 0xFF && buf[i] != readp[i])
return -EBADMSG; return -EBADMSG;
@ -923,41 +1111,51 @@ static int onenand_verify_oob(struct mtd_info *mtd, const u_char *buf, loff_t to
} }
/** /**
* onenand_verify_page - [GENERIC] verify the chip contents after a write * onenand_verify - [GENERIC] verify the chip contents after a write
* @param mtd MTD device structure * @param mtd MTD device structure
* @param buf the databuffer to verify * @param buf the databuffer to verify
* @param addr offset to read from
* @param len number of bytes to read and compare
* *
* Check DataRAM area directly
*/ */
static int onenand_verify_page(struct mtd_info *mtd, u_char *buf, loff_t addr) static int onenand_verify(struct mtd_info *mtd, const u_char *buf, loff_t addr, size_t len)
{ {
struct onenand_chip *this = mtd->priv; struct onenand_chip *this = mtd->priv;
void __iomem *dataram0, *dataram1; void __iomem *dataram;
int ret = 0; int ret = 0;
int thislen, column;
/* In partial page write, just skip it */ while (len != 0) {
if ((addr & (mtd->writesize - 1)) != 0) thislen = min_t(int, mtd->writesize, len);
return 0; column = addr & (mtd->writesize - 1);
if (column + thislen > mtd->writesize)
thislen = mtd->writesize - column;
this->command(mtd, ONENAND_CMD_READ, addr, mtd->writesize); this->command(mtd, ONENAND_CMD_READ, addr, mtd->writesize);
ret = this->wait(mtd, FL_READING); onenand_update_bufferram(mtd, addr, 0);
if (ret)
return ret;
onenand_update_bufferram(mtd, addr, 1); ret = this->wait(mtd, FL_READING);
if (ret)
return ret;
/* Check, if the two dataram areas are same */ onenand_update_bufferram(mtd, addr, 1);
dataram0 = this->base + ONENAND_DATARAM;
dataram1 = dataram0 + mtd->writesize;
if (memcmp(dataram0, dataram1, mtd->writesize)) dataram = this->base + ONENAND_DATARAM;
return -EBADMSG; dataram += onenand_bufferram_offset(mtd, ONENAND_DATARAM);
if (memcmp(buf, dataram + column, thislen))
return -EBADMSG;
len -= thislen;
buf += thislen;
addr += thislen;
}
return 0; return 0;
} }
#else #else
#define onenand_verify_page(...) (0) #define onenand_verify(...) (0)
#define onenand_verify_oob(...) (0) #define onenand_verify_oob(...) (0)
#endif #endif
@ -988,60 +1186,57 @@ static int onenand_write(struct mtd_info *mtd, loff_t to, size_t len,
/* Do not allow writes past end of device */ /* Do not allow writes past end of device */
if (unlikely((to + len) > mtd->size)) { if (unlikely((to + len) > mtd->size)) {
DEBUG(MTD_DEBUG_LEVEL0, "onenand_write: Attempt write to past end of device\n"); printk(KERN_ERR "onenand_write: Attempt write to past end of device\n");
return -EINVAL; return -EINVAL;
} }
/* Reject writes, which are not page aligned */ /* Reject writes, which are not page aligned */
if (unlikely(NOTALIGNED(to)) || unlikely(NOTALIGNED(len))) { if (unlikely(NOTALIGNED(to)) || unlikely(NOTALIGNED(len))) {
DEBUG(MTD_DEBUG_LEVEL0, "onenand_write: Attempt to write not page aligned data\n"); printk(KERN_ERR "onenand_write: Attempt to write not page aligned data\n");
return -EINVAL; return -EINVAL;
} }
column = to & (mtd->writesize - 1); column = to & (mtd->writesize - 1);
subpage = column || (len & (mtd->writesize - 1));
/* Grab the lock and see if the device is available */ /* Grab the lock and see if the device is available */
onenand_get_device(mtd, FL_WRITING); onenand_get_device(mtd, FL_WRITING);
/* Loop until all data write */ /* Loop until all data write */
while (written < len) { while (written < len) {
int bytes = mtd->writesize; int thislen = min_t(int, mtd->writesize - column, len - written);
int thislen = min_t(int, bytes, len - written);
u_char *wbuf = (u_char *) buf; u_char *wbuf = (u_char *) buf;
cond_resched(); cond_resched();
this->command(mtd, ONENAND_CMD_BUFFERRAM, to, bytes); this->command(mtd, ONENAND_CMD_BUFFERRAM, to, thislen);
/* Partial page write */ /* Partial page write */
subpage = thislen < mtd->writesize;
if (subpage) { if (subpage) {
bytes = min_t(int, bytes - column, (int) len);
memset(this->page_buf, 0xff, mtd->writesize); memset(this->page_buf, 0xff, mtd->writesize);
memcpy(this->page_buf + column, buf, bytes); memcpy(this->page_buf + column, buf, thislen);
wbuf = this->page_buf; wbuf = this->page_buf;
/* Even though partial write, we need page size */
thislen = mtd->writesize;
} }
this->write_bufferram(mtd, ONENAND_DATARAM, wbuf, 0, thislen); this->write_bufferram(mtd, ONENAND_DATARAM, wbuf, 0, mtd->writesize);
this->write_bufferram(mtd, ONENAND_SPARERAM, ffchars, 0, mtd->oobsize); this->write_bufferram(mtd, ONENAND_SPARERAM, ffchars, 0, mtd->oobsize);
this->command(mtd, ONENAND_CMD_PROG, to, mtd->writesize); this->command(mtd, ONENAND_CMD_PROG, to, mtd->writesize);
/* In partial page write we don't update bufferram */
onenand_update_bufferram(mtd, to, !subpage);
ret = this->wait(mtd, FL_WRITING); ret = this->wait(mtd, FL_WRITING);
/* In partial page write we don't update bufferram */
onenand_update_bufferram(mtd, to, !ret && !subpage);
if (ret) { if (ret) {
DEBUG(MTD_DEBUG_LEVEL0, "onenand_write: write filaed %d\n", ret); printk(KERN_ERR "onenand_write: write filaed %d\n", ret);
break; break;
} }
/* Only check verify write turn on */ /* Only check verify write turn on */
ret = onenand_verify_page(mtd, (u_char *) wbuf, to); ret = onenand_verify(mtd, (u_char *) wbuf, to, thislen);
if (ret) { if (ret) {
DEBUG(MTD_DEBUG_LEVEL0, "onenand_write: verify failed %d\n", ret); printk(KERN_ERR "onenand_write: verify failed %d\n", ret);
break; break;
} }
@ -1063,6 +1258,43 @@ static int onenand_write(struct mtd_info *mtd, loff_t to, size_t len,
return ret; return ret;
} }
/**
* onenand_fill_auto_oob - [Internal] oob auto-placement transfer
* @param mtd MTD device structure
* @param oob_buf oob buffer
* @param buf source address
* @param column oob offset to write to
* @param thislen oob length to write
*/
static int onenand_fill_auto_oob(struct mtd_info *mtd, u_char *oob_buf,
const u_char *buf, int column, int thislen)
{
struct onenand_chip *this = mtd->priv;
struct nand_oobfree *free;
int writecol = column;
int writeend = column + thislen;
int lastgap = 0;
for (free = this->ecclayout->oobfree; free->length; ++free) {
if (writecol >= lastgap)
writecol += free->offset - lastgap;
if (writeend >= lastgap)
writeend += free->offset - lastgap;
lastgap = free->offset + free->length;
}
for (free = this->ecclayout->oobfree; free->length; ++free) {
int free_end = free->offset + free->length;
if (free->offset < writeend && free_end > writecol) {
int st = max_t(int,free->offset,writecol);
int ed = min_t(int,free_end,writeend);
int n = ed - st;
memcpy(oob_buf + st, buf, n);
buf += n;
}
}
return 0;
}
/** /**
* onenand_do_write_oob - [Internal] OneNAND write out-of-band * onenand_do_write_oob - [Internal] OneNAND write out-of-band
* @param mtd MTD device structure * @param mtd MTD device structure
@ -1070,14 +1302,15 @@ static int onenand_write(struct mtd_info *mtd, loff_t to, size_t len,
* @param len number of bytes to write * @param len number of bytes to write
* @param retlen pointer to variable to store the number of written bytes * @param retlen pointer to variable to store the number of written bytes
* @param buf the data to write * @param buf the data to write
* @param mode operation mode
* *
* OneNAND write out-of-band * OneNAND write out-of-band
*/ */
static int onenand_do_write_oob(struct mtd_info *mtd, loff_t to, size_t len, static int onenand_do_write_oob(struct mtd_info *mtd, loff_t to, size_t len,
size_t *retlen, const u_char *buf) size_t *retlen, const u_char *buf, mtd_oob_mode_t mode)
{ {
struct onenand_chip *this = mtd->priv; struct onenand_chip *this = mtd->priv;
int column, ret = 0; int column, ret = 0, oobsize;
int written = 0; int written = 0;
DEBUG(MTD_DEBUG_LEVEL3, "onenand_write_oob: to = 0x%08x, len = %i\n", (unsigned int) to, (int) len); DEBUG(MTD_DEBUG_LEVEL3, "onenand_write_oob: to = 0x%08x, len = %i\n", (unsigned int) to, (int) len);
@ -1085,9 +1318,30 @@ static int onenand_do_write_oob(struct mtd_info *mtd, loff_t to, size_t len,
/* Initialize retlen, in case of early exit */ /* Initialize retlen, in case of early exit */
*retlen = 0; *retlen = 0;
/* Do not allow writes past end of device */ if (mode == MTD_OOB_AUTO)
if (unlikely((to + len) > mtd->size)) { oobsize = this->ecclayout->oobavail;
DEBUG(MTD_DEBUG_LEVEL0, "onenand_write_oob: Attempt write to past end of device\n"); else
oobsize = mtd->oobsize;
column = to & (mtd->oobsize - 1);
if (unlikely(column >= oobsize)) {
printk(KERN_ERR "onenand_write_oob: Attempted to start write outside oob\n");
return -EINVAL;
}
/* For compatibility with NAND: Do not allow write past end of page */
if (column + len > oobsize) {
printk(KERN_ERR "onenand_write_oob: "
"Attempt to write past end of page\n");
return -EINVAL;
}
/* Do not allow reads past end of device */
if (unlikely(to >= mtd->size ||
column + len > ((mtd->size >> this->page_shift) -
(to >> this->page_shift)) * oobsize)) {
printk(KERN_ERR "onenand_write_oob: Attempted to write past end of device\n");
return -EINVAL; return -EINVAL;
} }
@ -1096,18 +1350,19 @@ static int onenand_do_write_oob(struct mtd_info *mtd, loff_t to, size_t len,
/* Loop until all data write */ /* Loop until all data write */
while (written < len) { while (written < len) {
int thislen = min_t(int, mtd->oobsize, len - written); int thislen = min_t(int, oobsize, len - written);
cond_resched(); cond_resched();
column = to & (mtd->oobsize - 1);
this->command(mtd, ONENAND_CMD_BUFFERRAM, to, mtd->oobsize); this->command(mtd, ONENAND_CMD_BUFFERRAM, to, mtd->oobsize);
/* We send data to spare ram with oobsize /* We send data to spare ram with oobsize
* to prevent byte access */ * to prevent byte access */
memset(this->page_buf, 0xff, mtd->oobsize); memset(this->page_buf, 0xff, mtd->oobsize);
memcpy(this->page_buf + column, buf, thislen); if (mode == MTD_OOB_AUTO)
onenand_fill_auto_oob(mtd, this->page_buf, buf, column, thislen);
else
memcpy(this->page_buf + column, buf, thislen);
this->write_bufferram(mtd, ONENAND_SPARERAM, this->page_buf, 0, mtd->oobsize); this->write_bufferram(mtd, ONENAND_SPARERAM, this->page_buf, 0, mtd->oobsize);
this->command(mtd, ONENAND_CMD_PROGOOB, to, mtd->oobsize); this->command(mtd, ONENAND_CMD_PROGOOB, to, mtd->oobsize);
@ -1116,26 +1371,25 @@ static int onenand_do_write_oob(struct mtd_info *mtd, loff_t to, size_t len,
ret = this->wait(mtd, FL_WRITING); ret = this->wait(mtd, FL_WRITING);
if (ret) { if (ret) {
DEBUG(MTD_DEBUG_LEVEL0, "onenand_write_oob: write filaed %d\n", ret); printk(KERN_ERR "onenand_write_oob: write failed %d\n", ret);
goto out; break;
} }
ret = onenand_verify_oob(mtd, buf, to, thislen); ret = onenand_verify_oob(mtd, this->page_buf, to);
if (ret) { if (ret) {
DEBUG(MTD_DEBUG_LEVEL0, "onenand_write_oob: verify failed %d\n", ret); printk(KERN_ERR "onenand_write_oob: verify failed %d\n", ret);
goto out; break;
} }
written += thislen; written += thislen;
if (written == len) if (written == len)
break; break;
to += thislen; to += mtd->writesize;
buf += thislen; buf += thislen;
column = 0;
} }
out:
/* Deselect and wake up anyone waiting on the device */ /* Deselect and wake up anyone waiting on the device */
onenand_release_device(mtd); onenand_release_device(mtd);
@ -1153,10 +1407,17 @@ out:
static int onenand_write_oob(struct mtd_info *mtd, loff_t to, static int onenand_write_oob(struct mtd_info *mtd, loff_t to,
struct mtd_oob_ops *ops) struct mtd_oob_ops *ops)
{ {
BUG_ON(ops->mode != MTD_OOB_PLACE); switch (ops->mode) {
case MTD_OOB_PLACE:
case MTD_OOB_AUTO:
break;
case MTD_OOB_RAW:
/* Not implemented yet */
default:
return -EINVAL;
}
return onenand_do_write_oob(mtd, to + ops->ooboffs, ops->ooblen, return onenand_do_write_oob(mtd, to + ops->ooboffs, ops->ooblen,
&ops->oobretlen, ops->oobbuf); &ops->oobretlen, ops->oobbuf, ops->mode);
} }
/** /**
@ -1199,19 +1460,19 @@ static int onenand_erase(struct mtd_info *mtd, struct erase_info *instr)
/* Start address must align on block boundary */ /* Start address must align on block boundary */
if (unlikely(instr->addr & (block_size - 1))) { if (unlikely(instr->addr & (block_size - 1))) {
DEBUG(MTD_DEBUG_LEVEL0, "onenand_erase: Unaligned address\n"); printk(KERN_ERR "onenand_erase: Unaligned address\n");
return -EINVAL; return -EINVAL;
} }
/* Length must align on block boundary */ /* Length must align on block boundary */
if (unlikely(instr->len & (block_size - 1))) { if (unlikely(instr->len & (block_size - 1))) {
DEBUG(MTD_DEBUG_LEVEL0, "onenand_erase: Length not block aligned\n"); printk(KERN_ERR "onenand_erase: Length not block aligned\n");
return -EINVAL; return -EINVAL;
} }
/* Do not allow erase past end of device */ /* Do not allow erase past end of device */
if (unlikely((instr->len + instr->addr) > mtd->size)) { if (unlikely((instr->len + instr->addr) > mtd->size)) {
DEBUG(MTD_DEBUG_LEVEL0, "onenand_erase: Erase past end of device\n"); printk(KERN_ERR "onenand_erase: Erase past end of device\n");
return -EINVAL; return -EINVAL;
} }
@ -1238,10 +1499,12 @@ static int onenand_erase(struct mtd_info *mtd, struct erase_info *instr)
this->command(mtd, ONENAND_CMD_ERASE, addr, block_size); this->command(mtd, ONENAND_CMD_ERASE, addr, block_size);
onenand_invalidate_bufferram(mtd, addr, block_size);
ret = this->wait(mtd, FL_ERASING); ret = this->wait(mtd, FL_ERASING);
/* Check, if it is write protected */ /* Check, if it is write protected */
if (ret) { if (ret) {
DEBUG(MTD_DEBUG_LEVEL0, "onenand_erase: Failed erase, block %d\n", (unsigned) (addr >> this->erase_shift)); printk(KERN_ERR "onenand_erase: Failed erase, block %d\n", (unsigned) (addr >> this->erase_shift));
instr->state = MTD_ERASE_FAILED; instr->state = MTD_ERASE_FAILED;
instr->fail_addr = addr; instr->fail_addr = addr;
goto erase_exit; goto erase_exit;
@ -1322,7 +1585,7 @@ static int onenand_default_block_markbad(struct mtd_info *mtd, loff_t ofs)
/* We write two bytes, so we dont have to mess with 16 bit access */ /* We write two bytes, so we dont have to mess with 16 bit access */
ofs += mtd->oobsize + (bbm->badblockpos & ~0x01); ofs += mtd->oobsize + (bbm->badblockpos & ~0x01);
return onenand_do_write_oob(mtd, ofs , 2, &retlen, buf); return onenand_do_write_oob(mtd, ofs , 2, &retlen, buf, MTD_OOB_PLACE);
} }
/** /**
@ -1491,6 +1754,8 @@ static int onenand_unlock_all(struct mtd_info *mtd)
struct onenand_chip *this = mtd->priv; struct onenand_chip *this = mtd->priv;
if (this->options & ONENAND_HAS_UNLOCK_ALL) { if (this->options & ONENAND_HAS_UNLOCK_ALL) {
/* Set start block address */
this->write_word(0, this->base + ONENAND_REG_START_BLOCK_ADDRESS);
/* Write unlock command */ /* Write unlock command */
this->command(mtd, ONENAND_CMD_UNLOCK_ALL, 0, 0); this->command(mtd, ONENAND_CMD_UNLOCK_ALL, 0, 0);
@ -1503,13 +1768,10 @@ static int onenand_unlock_all(struct mtd_info *mtd)
continue; continue;
/* Workaround for all block unlock in DDP */ /* Workaround for all block unlock in DDP */
if (this->device_id & ONENAND_DEVICE_IS_DDP) { if (ONENAND_IS_DDP(this)) {
loff_t ofs;
size_t len;
/* 1st block on another chip */ /* 1st block on another chip */
ofs = this->chipsize >> 1; loff_t ofs = this->chipsize >> 1;
len = 1 << this->erase_shift; size_t len = mtd->erasesize;
onenand_unlock(mtd, ofs, len); onenand_unlock(mtd, ofs, len);
} }
@ -1617,7 +1879,7 @@ static int do_otp_lock(struct mtd_info *mtd, loff_t from, size_t len,
this->command(mtd, ONENAND_CMD_OTP_ACCESS, 0, 0); this->command(mtd, ONENAND_CMD_OTP_ACCESS, 0, 0);
this->wait(mtd, FL_OTPING); this->wait(mtd, FL_OTPING);
ret = onenand_do_write_oob(mtd, from, len, retlen, buf); ret = onenand_do_write_oob(mtd, from, len, retlen, buf, MTD_OOB_PLACE);
/* Exit OTP access mode */ /* Exit OTP access mode */
this->command(mtd, ONENAND_CMD_RESET, 0, 0); this->command(mtd, ONENAND_CMD_RESET, 0, 0);
@ -1823,12 +2085,13 @@ static int onenand_lock_user_prot_reg(struct mtd_info *mtd, loff_t from,
#endif /* CONFIG_MTD_ONENAND_OTP */ #endif /* CONFIG_MTD_ONENAND_OTP */
/** /**
* onenand_lock_scheme - Check and set OneNAND lock scheme * onenand_check_features - Check and set OneNAND features
* @param mtd MTD data structure * @param mtd MTD data structure
* *
* Check and set OneNAND lock scheme * Check and set OneNAND features
* - lock scheme
*/ */
static void onenand_lock_scheme(struct mtd_info *mtd) static void onenand_check_features(struct mtd_info *mtd)
{ {
struct onenand_chip *this = mtd->priv; struct onenand_chip *this = mtd->priv;
unsigned int density, process; unsigned int density, process;
@ -1961,26 +2224,28 @@ static int onenand_probe(struct mtd_info *mtd)
density = dev_id >> ONENAND_DEVICE_DENSITY_SHIFT; density = dev_id >> ONENAND_DEVICE_DENSITY_SHIFT;
this->chipsize = (16 << density) << 20; this->chipsize = (16 << density) << 20;
/* Set density mask. it is used for DDP */ /* Set density mask. it is used for DDP */
this->density_mask = (1 << (density + 6)); if (ONENAND_IS_DDP(this))
this->density_mask = (1 << (density + 6));
else
this->density_mask = 0;
/* OneNAND page size & block size */ /* OneNAND page size & block size */
/* The data buffer size is equal to page size */ /* The data buffer size is equal to page size */
mtd->writesize = this->read_word(this->base + ONENAND_REG_DATA_BUFFER_SIZE); mtd->writesize = this->read_word(this->base + ONENAND_REG_DATA_BUFFER_SIZE);
mtd->oobsize = mtd->writesize >> 5; mtd->oobsize = mtd->writesize >> 5;
/* Pagers per block is always 64 in OneNAND */ /* Pages per a block are always 64 in OneNAND */
mtd->erasesize = mtd->writesize << 6; mtd->erasesize = mtd->writesize << 6;
this->erase_shift = ffs(mtd->erasesize) - 1; this->erase_shift = ffs(mtd->erasesize) - 1;
this->page_shift = ffs(mtd->writesize) - 1; this->page_shift = ffs(mtd->writesize) - 1;
this->ppb_shift = (this->erase_shift - this->page_shift); this->page_mask = (1 << (this->erase_shift - this->page_shift)) - 1;
this->page_mask = (mtd->erasesize / mtd->writesize) - 1;
/* REVIST: Multichip handling */ /* REVIST: Multichip handling */
mtd->size = this->chipsize; mtd->size = this->chipsize;
/* Check OneNAND lock scheme */ /* Check OneNAND features */
onenand_lock_scheme(mtd); onenand_check_features(mtd);
return 0; return 0;
} }
@ -2021,6 +2286,7 @@ static void onenand_resume(struct mtd_info *mtd)
*/ */
int onenand_scan(struct mtd_info *mtd, int maxchips) int onenand_scan(struct mtd_info *mtd, int maxchips)
{ {
int i;
struct onenand_chip *this = mtd->priv; struct onenand_chip *this = mtd->priv;
if (!this->read_word) if (!this->read_word)
@ -2092,12 +2358,21 @@ int onenand_scan(struct mtd_info *mtd, int maxchips)
} }
this->subpagesize = mtd->writesize >> mtd->subpage_sft; this->subpagesize = mtd->writesize >> mtd->subpage_sft;
/*
* The number of bytes available for a client to place data into
* the out of band area
*/
this->ecclayout->oobavail = 0;
for (i = 0; this->ecclayout->oobfree[i].length; i++)
this->ecclayout->oobavail +=
this->ecclayout->oobfree[i].length;
mtd->ecclayout = this->ecclayout; mtd->ecclayout = this->ecclayout;
/* 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 = onenand_erase; mtd->erase = onenand_erase;
mtd->point = NULL; mtd->point = NULL;
mtd->unpoint = NULL; mtd->unpoint = NULL;
@ -2144,8 +2419,11 @@ void onenand_release(struct mtd_info *mtd)
del_mtd_device (mtd); del_mtd_device (mtd);
/* Free bad block table memory, if allocated */ /* Free bad block table memory, if allocated */
if (this->bbm) if (this->bbm) {
struct bbm_info *bbm = this->bbm;
kfree(bbm->bbt);
kfree(this->bbm); kfree(this->bbm);
}
/* Buffer allocated by onenand_scan */ /* Buffer allocated by onenand_scan */
if (this->options & ONENAND_PAGEBUF_ALLOC) if (this->options & ONENAND_PAGEBUF_ALLOC)
kfree(this->page_buf); kfree(this->page_buf);

View file

@ -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
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
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)
* 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)

View file

@ -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,
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) {

View file

@ -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;
} }

View file

@ -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 */

View file

@ -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;
} }
} }

View file

@ -957,43 +957,48 @@ exit:
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,
} }
} }
/* 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. */ ops.mode = MTD_OOB_AUTO;
if (c->mtd->block_isbad(c->mtd, offset)) { ops.ooblen = cmlen;
D1 (printk(KERN_WARNING "jffs2_check_nand_cleanmarker()"
": Bad block at %08x\n", jeb->offset));
return 2;
}
ops.ooblen = oobsize;
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 *
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)
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)

View file

@ -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

View file

@ -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;

View file

@ -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 {
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;

View file

@ -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

View file

@ -1,7 +1,7 @@
/* /*
* 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 {
/** /**
* 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 {
* 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 {
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 {
#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)

View file

@ -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 @@
#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)

View file

@ -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);

View file

@ -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 {
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;
}; };