diff options
| author | Linus Torvalds <torvalds@woody.linux-foundation.org> | 2007-02-19 16:34:11 -0500 |
|---|---|---|
| committer | Linus Torvalds <torvalds@woody.linux-foundation.org> | 2007-02-19 16:34:11 -0500 |
| commit | 4935361766cc73949fe032cd157d314f288922ba (patch) | |
| tree | 1584f81525ae05a04d515b13a4787cd8eed46029 | |
| parent | 2874b391bd78a5b8cb84be67297a345fbdec4ac8 (diff) | |
| parent | 4f65992381112acd7d2732665a9eae492c2c9de6 (diff) | |
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
...
37 files changed, 1048 insertions, 468 deletions
diff --git a/drivers/mtd/chips/cfi_cmdset_0001.c b/drivers/mtd/chips/cfi_cmdset_0001.c index f69184a92eb2..f334959a335b 100644 --- a/drivers/mtd/chips/cfi_cmdset_0001.c +++ b/drivers/mtd/chips/cfi_cmdset_0001.c | |||
| @@ -397,9 +397,23 @@ struct mtd_info *cfi_cmdset_0001(struct map_info *map, int primary) | |||
| 397 | cfi_fixup(mtd, fixup_table); | 397 | cfi_fixup(mtd, fixup_table); |
| 398 | 398 | ||
| 399 | for (i=0; i< cfi->numchips; i++) { | 399 | for (i=0; i< cfi->numchips; i++) { |
| 400 | cfi->chips[i].word_write_time = 1<<cfi->cfiq->WordWriteTimeoutTyp; | 400 | if (cfi->cfiq->WordWriteTimeoutTyp) |
| 401 | cfi->chips[i].buffer_write_time = 1<<cfi->cfiq->BufWriteTimeoutTyp; | 401 | cfi->chips[i].word_write_time = |
| 402 | cfi->chips[i].erase_time = 1000<<cfi->cfiq->BlockEraseTimeoutTyp; | 402 | 1<<cfi->cfiq->WordWriteTimeoutTyp; |
| 403 | else | ||
| 404 | cfi->chips[i].word_write_time = 50000; | ||
| 405 | |||
| 406 | if (cfi->cfiq->BufWriteTimeoutTyp) | ||
| 407 | cfi->chips[i].buffer_write_time = | ||
| 408 | 1<<cfi->cfiq->BufWriteTimeoutTyp; | ||
| 409 | /* No default; if it isn't specified, we won't use it */ | ||
| 410 | |||
| 411 | if (cfi->cfiq->BlockEraseTimeoutTyp) | ||
| 412 | cfi->chips[i].erase_time = | ||
| 413 | 1000<<cfi->cfiq->BlockEraseTimeoutTyp; | ||
| 414 | else | ||
| 415 | cfi->chips[i].erase_time = 2000000; | ||
| 416 | |||
| 403 | cfi->chips[i].ref_point_counter = 0; | 417 | cfi->chips[i].ref_point_counter = 0; |
| 404 | init_waitqueue_head(&(cfi->chips[i].wq)); | 418 | init_waitqueue_head(&(cfi->chips[i].wq)); |
| 405 | } | 419 | } |
| @@ -546,13 +560,11 @@ static int cfi_intelext_partition_fixup(struct mtd_info *mtd, | |||
| 546 | struct cfi_intelext_programming_regioninfo *prinfo; | 560 | struct cfi_intelext_programming_regioninfo *prinfo; |
| 547 | prinfo = (struct cfi_intelext_programming_regioninfo *)&extp->extra[offs]; | 561 | prinfo = (struct cfi_intelext_programming_regioninfo *)&extp->extra[offs]; |
| 548 | mtd->writesize = cfi->interleave << prinfo->ProgRegShift; | 562 | mtd->writesize = cfi->interleave << prinfo->ProgRegShift; |
| 549 | MTD_PROGREGION_CTRLMODE_VALID(mtd) = cfi->interleave * prinfo->ControlValid; | ||
| 550 | MTD_PROGREGION_CTRLMODE_INVALID(mtd) = cfi->interleave * prinfo->ControlInvalid; | ||
| 551 | mtd->flags &= ~MTD_BIT_WRITEABLE; | 563 | mtd->flags &= ~MTD_BIT_WRITEABLE; |
| 552 | printk(KERN_DEBUG "%s: program region size/ctrl_valid/ctrl_inval = %d/%d/%d\n", | 564 | printk(KERN_DEBUG "%s: program region size/ctrl_valid/ctrl_inval = %d/%d/%d\n", |
| 553 | map->name, mtd->writesize, | 565 | map->name, mtd->writesize, |
| 554 | MTD_PROGREGION_CTRLMODE_VALID(mtd), | 566 | cfi->interleave * prinfo->ControlValid, |
| 555 | MTD_PROGREGION_CTRLMODE_INVALID(mtd)); | 567 | cfi->interleave * prinfo->ControlInvalid); |
| 556 | } | 568 | } |
| 557 | 569 | ||
| 558 | /* | 570 | /* |
diff --git a/drivers/mtd/chips/cfi_cmdset_0020.c b/drivers/mtd/chips/cfi_cmdset_0020.c index d56849f5f107..69d49e0250a9 100644 --- a/drivers/mtd/chips/cfi_cmdset_0020.c +++ b/drivers/mtd/chips/cfi_cmdset_0020.c | |||
| @@ -662,7 +662,7 @@ static int cfi_staa_write_buffers (struct mtd_info *mtd, loff_t to, | |||
| 662 | * a small buffer for this. | 662 | * a small buffer for this. |
| 663 | * XXX: If the buffer size is not a multiple of 2, this will break | 663 | * XXX: If the buffer size is not a multiple of 2, this will break |
| 664 | */ | 664 | */ |
| 665 | #define ECCBUF_SIZE (mtd->eccsize) | 665 | #define ECCBUF_SIZE (mtd->writesize) |
| 666 | #define ECCBUF_DIV(x) ((x) & ~(ECCBUF_SIZE - 1)) | 666 | #define ECCBUF_DIV(x) ((x) & ~(ECCBUF_SIZE - 1)) |
| 667 | #define ECCBUF_MOD(x) ((x) & (ECCBUF_SIZE - 1)) | 667 | #define ECCBUF_MOD(x) ((x) & (ECCBUF_SIZE - 1)) |
| 668 | static int | 668 | static int |
diff --git a/drivers/mtd/devices/doc2000.c b/drivers/mtd/devices/doc2000.c index 603a7951ac9b..8a0c4dec6351 100644 --- a/drivers/mtd/devices/doc2000.c +++ b/drivers/mtd/devices/doc2000.c | |||
| @@ -569,7 +569,6 @@ void DoC2k_init(struct mtd_info *mtd) | |||
| 569 | 569 | ||
| 570 | mtd->type = MTD_NANDFLASH; | 570 | mtd->type = MTD_NANDFLASH; |
| 571 | mtd->flags = MTD_CAP_NANDFLASH; | 571 | mtd->flags = MTD_CAP_NANDFLASH; |
| 572 | mtd->ecctype = MTD_ECC_RS_DiskOnChip; | ||
| 573 | mtd->size = 0; | 572 | mtd->size = 0; |
| 574 | mtd->erasesize = 0; | 573 | mtd->erasesize = 0; |
| 575 | mtd->writesize = 512; | 574 | mtd->writesize = 512; |
diff --git a/drivers/mtd/devices/doc2001.c b/drivers/mtd/devices/doc2001.c index fe71a12c2627..6f368aec5d5d 100644 --- a/drivers/mtd/devices/doc2001.c +++ b/drivers/mtd/devices/doc2001.c | |||
| @@ -348,7 +348,6 @@ void DoCMil_init(struct mtd_info *mtd) | |||
| 348 | 348 | ||
| 349 | mtd->type = MTD_NANDFLASH; | 349 | mtd->type = MTD_NANDFLASH; |
| 350 | mtd->flags = MTD_CAP_NANDFLASH; | 350 | mtd->flags = MTD_CAP_NANDFLASH; |
| 351 | mtd->ecctype = MTD_ECC_RS_DiskOnChip; | ||
| 352 | mtd->size = 0; | 351 | mtd->size = 0; |
| 353 | 352 | ||
| 354 | /* FIXME: erase size is not always 8KiB */ | 353 | /* FIXME: erase size is not always 8KiB */ |
diff --git a/drivers/mtd/devices/doc2001plus.c b/drivers/mtd/devices/doc2001plus.c index ba4db686285a..88ba82df0fbb 100644 --- a/drivers/mtd/devices/doc2001plus.c +++ b/drivers/mtd/devices/doc2001plus.c | |||
| @@ -472,7 +472,6 @@ void DoCMilPlus_init(struct mtd_info *mtd) | |||
| 472 | 472 | ||
| 473 | mtd->type = MTD_NANDFLASH; | 473 | mtd->type = MTD_NANDFLASH; |
| 474 | mtd->flags = MTD_CAP_NANDFLASH; | 474 | mtd->flags = MTD_CAP_NANDFLASH; |
| 475 | mtd->ecctype = MTD_ECC_RS_DiskOnChip; | ||
| 476 | mtd->size = 0; | 475 | mtd->size = 0; |
| 477 | 476 | ||
| 478 | mtd->erasesize = 0; | 477 | mtd->erasesize = 0; |
diff --git a/drivers/mtd/maps/Kconfig b/drivers/mtd/maps/Kconfig index f457315579db..bbf0553bdb2e 100644 --- a/drivers/mtd/maps/Kconfig +++ b/drivers/mtd/maps/Kconfig | |||
| @@ -204,7 +204,7 @@ config MTD_ESB2ROM | |||
| 204 | 204 | ||
| 205 | config MTD_CK804XROM | 205 | config MTD_CK804XROM |
| 206 | tristate "BIOS flash chip on Nvidia CK804" | 206 | tristate "BIOS flash chip on Nvidia CK804" |
| 207 | depends on X86 && MTD_JEDECPROBE | 207 | depends on X86 && MTD_JEDECPROBE && PCI |
| 208 | help | 208 | help |
| 209 | Support for treating the BIOS flash chip on nvidia motherboards | 209 | Support for treating the BIOS flash chip on nvidia motherboards |
| 210 | as an MTD device - with this you can reprogram your BIOS. | 210 | as an MTD device - with this you can reprogram your BIOS. |
diff --git a/drivers/mtd/maps/amd76xrom.c b/drivers/mtd/maps/amd76xrom.c index 78b671172bb2..728aed6ad722 100644 --- a/drivers/mtd/maps/amd76xrom.c +++ b/drivers/mtd/maps/amd76xrom.c | |||
| @@ -205,8 +205,8 @@ static int __devinit amd76xrom_init_one (struct pci_dev *pdev, | |||
| 205 | (((unsigned long)(window->virt)) + offset); | 205 | (((unsigned long)(window->virt)) + offset); |
| 206 | map->map.size = 0xffffffffUL - map_top + 1UL; | 206 | map->map.size = 0xffffffffUL - map_top + 1UL; |
| 207 | /* Set the name of the map to the address I am trying */ | 207 | /* Set the name of the map to the address I am trying */ |
| 208 | sprintf(map->map_name, "%s @%08lx", | 208 | sprintf(map->map_name, "%s @%08Lx", |
| 209 | MOD_NAME, map->map.phys); | 209 | MOD_NAME, (unsigned long long)map->map.phys); |
| 210 | 210 | ||
| 211 | /* There is no generic VPP support */ | 211 | /* There is no generic VPP support */ |
| 212 | for(map->map.bankwidth = 32; map->map.bankwidth; | 212 | for(map->map.bankwidth = 32; map->map.bankwidth; |
diff --git a/drivers/mtd/maps/ck804xrom.c b/drivers/mtd/maps/ck804xrom.c index 238d42e88ec5..3d4a4d8ac789 100644 --- a/drivers/mtd/maps/ck804xrom.c +++ b/drivers/mtd/maps/ck804xrom.c | |||
| @@ -207,8 +207,8 @@ static int __devinit ck804xrom_init_one (struct pci_dev *pdev, | |||
| 207 | (((unsigned long)(window->virt)) + offset); | 207 | (((unsigned long)(window->virt)) + offset); |
| 208 | map->map.size = 0xffffffffUL - map_top + 1UL; | 208 | map->map.size = 0xffffffffUL - map_top + 1UL; |
| 209 | /* Set the name of the map to the address I am trying */ | 209 | /* Set the name of the map to the address I am trying */ |
| 210 | sprintf(map->map_name, "%s @%08lx", | 210 | sprintf(map->map_name, "%s @%08Lx", |
| 211 | MOD_NAME, map->map.phys); | 211 | MOD_NAME, (unsigned long long)map->map.phys); |
| 212 | 212 | ||
| 213 | /* There is no generic VPP support */ | 213 | /* There is no generic VPP support */ |
| 214 | for(map->map.bankwidth = 32; map->map.bankwidth; | 214 | for(map->map.bankwidth = 32; map->map.bankwidth; |
| @@ -327,7 +327,7 @@ static int __init init_ck804xrom(void) | |||
| 327 | pdev = NULL; | 327 | pdev = NULL; |
| 328 | 328 | ||
| 329 | for(id = ck804xrom_pci_tbl; id->vendor; id++) { | 329 | for(id = ck804xrom_pci_tbl; id->vendor; id++) { |
| 330 | pdev = pci_find_device(id->vendor, id->device, NULL); | 330 | pdev = pci_get_device(id->vendor, id->device, NULL); |
| 331 | if (pdev) | 331 | if (pdev) |
| 332 | break; | 332 | break; |
| 333 | } | 333 | } |
diff --git a/drivers/mtd/maps/esb2rom.c b/drivers/mtd/maps/esb2rom.c index a9d808a617c9..0bc013fd66a3 100644 --- a/drivers/mtd/maps/esb2rom.c +++ b/drivers/mtd/maps/esb2rom.c | |||
| @@ -289,8 +289,8 @@ static int __devinit esb2rom_init_one(struct pci_dev *pdev, | |||
| 289 | (((unsigned long)(window->virt)) + offset); | 289 | (((unsigned long)(window->virt)) + offset); |
| 290 | map->map.size = 0xffffffffUL - map_top + 1UL; | 290 | map->map.size = 0xffffffffUL - map_top + 1UL; |
| 291 | /* Set the name of the map to the address I am trying */ | 291 | /* Set the name of the map to the address I am trying */ |
| 292 | sprintf(map->map_name, "%s @%08lx", | 292 | sprintf(map->map_name, "%s @%08Lx", |
| 293 | MOD_NAME, map->map.phys); | 293 | MOD_NAME, (unsigned long long)map->map.phys); |
| 294 | 294 | ||
| 295 | /* Firmware hubs only use vpp when being programmed | 295 | /* Firmware hubs only use vpp when being programmed |
| 296 | * in a factory setting. So in-place programming | 296 | * in a factory setting. So in-place programming |
diff --git a/drivers/mtd/maps/ichxrom.c b/drivers/mtd/maps/ichxrom.c index 2bb3e63606e5..2c884c49e84a 100644 --- a/drivers/mtd/maps/ichxrom.c +++ b/drivers/mtd/maps/ichxrom.c | |||
| @@ -227,8 +227,8 @@ static int __devinit ichxrom_init_one (struct pci_dev *pdev, | |||
| 227 | (((unsigned long)(window->virt)) + offset); | 227 | (((unsigned long)(window->virt)) + offset); |
| 228 | map->map.size = 0xffffffffUL - map_top + 1UL; | 228 | map->map.size = 0xffffffffUL - map_top + 1UL; |
| 229 | /* Set the name of the map to the address I am trying */ | 229 | /* Set the name of the map to the address I am trying */ |
| 230 | sprintf(map->map_name, "%s @%08lx", | 230 | sprintf(map->map_name, "%s @%08Lx", |
| 231 | MOD_NAME, map->map.phys); | 231 | MOD_NAME, (unsigned long long)map->map.phys); |
| 232 | 232 | ||
| 233 | /* Firmware hubs only use vpp when being programmed | 233 | /* Firmware hubs only use vpp when being programmed |
| 234 | * in a factory setting. So in-place programming | 234 | * in a factory setting. So in-place programming |
diff --git a/drivers/mtd/maps/netsc520.c b/drivers/mtd/maps/netsc520.c index ed215470158b..95dcab2146ad 100644 --- a/drivers/mtd/maps/netsc520.c +++ b/drivers/mtd/maps/netsc520.c | |||
| @@ -94,7 +94,9 @@ static struct mtd_info *mymtd; | |||
| 94 | 94 | ||
| 95 | static int __init init_netsc520(void) | 95 | static int __init init_netsc520(void) |
| 96 | { | 96 | { |
| 97 | printk(KERN_NOTICE "NetSc520 flash device: 0x%lx at 0x%lx\n", netsc520_map.size, netsc520_map.phys); | 97 | printk(KERN_NOTICE "NetSc520 flash device: 0x%Lx at 0x%Lx\n", |
| 98 | (unsigned long long)netsc520_map.size, | ||
| 99 | (unsigned long long)netsc520_map.phys); | ||
| 98 | netsc520_map.virt = ioremap_nocache(netsc520_map.phys, netsc520_map.size); | 100 | netsc520_map.virt = ioremap_nocache(netsc520_map.phys, netsc520_map.size); |
| 99 | 101 | ||
| 100 | if (!netsc520_map.virt) { | 102 | if (!netsc520_map.virt) { |
diff --git a/drivers/mtd/maps/sc520cdp.c b/drivers/mtd/maps/sc520cdp.c index 9b50cfc355b1..4045e372b90d 100644 --- a/drivers/mtd/maps/sc520cdp.c +++ b/drivers/mtd/maps/sc520cdp.c | |||
| @@ -237,8 +237,9 @@ static int __init init_sc520cdp(void) | |||
| 237 | #endif | 237 | #endif |
| 238 | 238 | ||
| 239 | for (i = 0; i < NUM_FLASH_BANKS; i++) { | 239 | for (i = 0; i < NUM_FLASH_BANKS; i++) { |
| 240 | printk(KERN_NOTICE "SC520 CDP flash device: 0x%lx at 0x%lx\n", | 240 | printk(KERN_NOTICE "SC520 CDP flash device: 0x%Lx at 0x%Lx\n", |
| 241 | sc520cdp_map[i].size, sc520cdp_map[i].phys); | 241 | (unsigned long long)sc520cdp_map[i].size, |
| 242 | (unsigned long long)sc520cdp_map[i].phys); | ||
| 242 | 243 | ||
| 243 | sc520cdp_map[i].virt = ioremap_nocache(sc520cdp_map[i].phys, sc520cdp_map[i].size); | 244 | sc520cdp_map[i].virt = ioremap_nocache(sc520cdp_map[i].phys, sc520cdp_map[i].size); |
| 244 | 245 | ||
diff --git a/drivers/mtd/mtdchar.c b/drivers/mtd/mtdchar.c index 61a994ea8af1..1592eac64e57 100644 --- a/drivers/mtd/mtdchar.c +++ b/drivers/mtd/mtdchar.c | |||
| @@ -419,8 +419,9 @@ static int mtd_ioctl(struct inode *inode, struct file *file, | |||
| 419 | info.erasesize = mtd->erasesize; | 419 | info.erasesize = mtd->erasesize; |
| 420 | info.writesize = mtd->writesize; | 420 | info.writesize = mtd->writesize; |
| 421 | info.oobsize = mtd->oobsize; | 421 | info.oobsize = mtd->oobsize; |
| 422 | info.ecctype = mtd->ecctype; | 422 | /* The below fields are obsolete */ |
| 423 | info.eccsize = mtd->eccsize; | 423 | info.ecctype = -1; |
| 424 | info.eccsize = 0; | ||
| 424 | if (copy_to_user(argp, &info, sizeof(struct mtd_info_user))) | 425 | if (copy_to_user(argp, &info, sizeof(struct mtd_info_user))) |
| 425 | return -EFAULT; | 426 | return -EFAULT; |
| 426 | break; | 427 | break; |
diff --git a/drivers/mtd/mtdconcat.c b/drivers/mtd/mtdconcat.c index 06902683bc2a..880580c44e01 100644 --- a/drivers/mtd/mtdconcat.c +++ b/drivers/mtd/mtdconcat.c | |||
| @@ -727,8 +727,6 @@ struct mtd_info *mtd_concat_create(struct mtd_info *subdev[], /* subdevices to c | |||
| 727 | concat->mtd.erasesize = subdev[0]->erasesize; | 727 | concat->mtd.erasesize = subdev[0]->erasesize; |
| 728 | concat->mtd.writesize = subdev[0]->writesize; | 728 | concat->mtd.writesize = subdev[0]->writesize; |
| 729 | concat->mtd.oobsize = subdev[0]->oobsize; | 729 | concat->mtd.oobsize = subdev[0]->oobsize; |
| 730 | concat->mtd.ecctype = subdev[0]->ecctype; | ||
| 731 | concat->mtd.eccsize = subdev[0]->eccsize; | ||
| 732 | if (subdev[0]->writev) | 730 | if (subdev[0]->writev) |
| 733 | concat->mtd.writev = concat_writev; | 731 | concat->mtd.writev = concat_writev; |
| 734 | if (subdev[0]->read_oob) | 732 | if (subdev[0]->read_oob) |
| @@ -774,8 +772,6 @@ struct mtd_info *mtd_concat_create(struct mtd_info *subdev[], /* subdevices to c | |||
| 774 | if (concat->mtd.writesize != subdev[i]->writesize || | 772 | if (concat->mtd.writesize != subdev[i]->writesize || |
| 775 | concat->mtd.subpage_sft != subdev[i]->subpage_sft || | 773 | concat->mtd.subpage_sft != subdev[i]->subpage_sft || |
| 776 | concat->mtd.oobsize != subdev[i]->oobsize || | 774 | concat->mtd.oobsize != subdev[i]->oobsize || |
| 777 | concat->mtd.ecctype != subdev[i]->ecctype || | ||
| 778 | concat->mtd.eccsize != subdev[i]->eccsize || | ||
| 779 | !concat->mtd.read_oob != !subdev[i]->read_oob || | 775 | !concat->mtd.read_oob != !subdev[i]->read_oob || |
| 780 | !concat->mtd.write_oob != !subdev[i]->write_oob) { | 776 | !concat->mtd.write_oob != !subdev[i]->write_oob) { |
| 781 | kfree(concat); | 777 | kfree(concat); |
diff --git a/drivers/mtd/mtdpart.c b/drivers/mtd/mtdpart.c index bafd2fba87bd..633def3fb087 100644 --- a/drivers/mtd/mtdpart.c +++ b/drivers/mtd/mtdpart.c | |||
| @@ -338,8 +338,6 @@ int add_mtd_partitions(struct mtd_info *master, | |||
| 338 | slave->mtd.size = parts[i].size; | 338 | slave->mtd.size = parts[i].size; |
| 339 | slave->mtd.writesize = master->writesize; | 339 | slave->mtd.writesize = master->writesize; |
| 340 | slave->mtd.oobsize = master->oobsize; | 340 | slave->mtd.oobsize = master->oobsize; |
| 341 | slave->mtd.ecctype = master->ecctype; | ||
| 342 | slave->mtd.eccsize = master->eccsize; | ||
| 343 | slave->mtd.subpage_sft = master->subpage_sft; | 341 | slave->mtd.subpage_sft = master->subpage_sft; |
| 344 | 342 | ||
| 345 | slave->mtd.name = parts[i].name; | 343 | slave->mtd.name = parts[i].name; |
diff --git a/drivers/mtd/nand/Kconfig b/drivers/mtd/nand/Kconfig index 358f55a82dbe..2d12dcdd740c 100644 --- a/drivers/mtd/nand/Kconfig +++ b/drivers/mtd/nand/Kconfig | |||
| @@ -126,10 +126,6 @@ config MTD_NAND_S3C2410_HWECC | |||
| 126 | incorrect ECC generation, and if using these, the default of | 126 | incorrect ECC generation, and if using these, the default of |
| 127 | software ECC is preferable. | 127 | software ECC is preferable. |
| 128 | 128 | ||
| 129 | If you lay down a device with the hardware ECC, then you will | ||
| 130 | currently not be able to switch to software, as there is no | ||
| 131 | implementation for ECC method used by the S3C2410 | ||
| 132 | |||
| 133 | config MTD_NAND_NDFC | 129 | config MTD_NAND_NDFC |
| 134 | tristate "NDFC NanD Flash Controller" | 130 | tristate "NDFC NanD Flash Controller" |
| 135 | depends on MTD_NAND && 44x | 131 | depends on MTD_NAND && 44x |
| @@ -221,9 +217,17 @@ config MTD_NAND_SHARPSL | |||
| 221 | tristate "Support for NAND Flash on Sharp SL Series (C7xx + others)" | 217 | tristate "Support for NAND Flash on Sharp SL Series (C7xx + others)" |
| 222 | depends on MTD_NAND && ARCH_PXA | 218 | depends on MTD_NAND && ARCH_PXA |
| 223 | 219 | ||
| 220 | config MTD_NAND_BASLER_EXCITE | ||
| 221 | tristate "Support for NAND Flash on Basler eXcite" | ||
| 222 | depends on MTD_NAND && BASLER_EXCITE | ||
| 223 | help | ||
| 224 | This enables the driver for the NAND flash device found on the | ||
| 225 | Basler eXcite Smart Camera. If built as a module, the driver | ||
| 226 | will be named "excite_nandflash.ko". | ||
| 227 | |||
| 224 | config MTD_NAND_CAFE | 228 | config MTD_NAND_CAFE |
| 225 | tristate "NAND support for OLPC CAFÉ chip" | 229 | tristate "NAND support for OLPC CAFÉ chip" |
| 226 | depends on PCI | 230 | depends on MTD_NAND && PCI |
| 227 | help | 231 | help |
| 228 | Use NAND flash attached to the CAFÉ chip designed for the $100 | 232 | Use NAND flash attached to the CAFÉ chip designed for the $100 |
| 229 | laptop. | 233 | laptop. |
diff --git a/drivers/mtd/nand/Makefile b/drivers/mtd/nand/Makefile index f7a53f0b7017..80f1dfc77949 100644 --- a/drivers/mtd/nand/Makefile +++ b/drivers/mtd/nand/Makefile | |||
| @@ -24,6 +24,7 @@ obj-$(CONFIG_MTD_NAND_NANDSIM) += nandsim.o | |||
| 24 | obj-$(CONFIG_MTD_NAND_CS553X) += cs553x_nand.o | 24 | obj-$(CONFIG_MTD_NAND_CS553X) += cs553x_nand.o |
| 25 | obj-$(CONFIG_MTD_NAND_NDFC) += ndfc.o | 25 | obj-$(CONFIG_MTD_NAND_NDFC) += ndfc.o |
| 26 | obj-$(CONFIG_MTD_NAND_AT91) += at91_nand.o | 26 | obj-$(CONFIG_MTD_NAND_AT91) += at91_nand.o |
| 27 | obj-$(CONFIG_MTD_NAND_BASLER_EXCITE) += excite_nandflash.o | ||
| 27 | 28 | ||
| 28 | nand-objs := nand_base.o nand_bbt.o | 29 | nand-objs := nand_base.o nand_bbt.o |
| 29 | cafe_nand-objs := cafe.o cafe_ecc.o | 30 | cafe_nand-objs := cafe.o cafe_ecc.o |
diff --git a/drivers/mtd/nand/cafe.c b/drivers/mtd/nand/cafe.c index 08cb060dfa3d..fd6bb3ed40df 100644 --- a/drivers/mtd/nand/cafe.c +++ b/drivers/mtd/nand/cafe.c | |||
| @@ -78,8 +78,9 @@ module_param(regdebug, int, 0644); | |||
| 78 | static int checkecc = 1; | 78 | static int checkecc = 1; |
| 79 | module_param(checkecc, int, 0644); | 79 | module_param(checkecc, int, 0644); |
| 80 | 80 | ||
| 81 | static int slowtiming = 0; | 81 | static int numtimings; |
| 82 | module_param(slowtiming, int, 0644); | 82 | static int timing[3]; |
| 83 | module_param_array(timing, int, &numtimings, 0644); | ||
| 83 | 84 | ||
| 84 | /* Hrm. Why isn't this already conditional on something in the struct device? */ | 85 | /* Hrm. Why isn't this already conditional on something in the struct device? */ |
| 85 | #define cafe_dev_dbg(dev, args...) do { if (debug) dev_dbg(dev, ##args); } while(0) | 86 | #define cafe_dev_dbg(dev, args...) do { if (debug) dev_dbg(dev, ##args); } while(0) |
| @@ -264,10 +265,10 @@ static void cafe_nand_cmdfunc(struct mtd_info *mtd, unsigned command, | |||
| 264 | ndelay(100); | 265 | ndelay(100); |
| 265 | 266 | ||
| 266 | if (1) { | 267 | if (1) { |
| 267 | int c = 500000; | 268 | int c; |
| 268 | uint32_t irqs; | 269 | uint32_t irqs; |
| 269 | 270 | ||
| 270 | while (c--) { | 271 | for (c = 500000; c != 0; c--) { |
| 271 | irqs = cafe_readl(cafe, NAND_IRQ); | 272 | irqs = cafe_readl(cafe, NAND_IRQ); |
| 272 | if (irqs & doneint) | 273 | if (irqs & doneint) |
| 273 | break; | 274 | break; |
| @@ -529,6 +530,7 @@ static int __devinit cafe_nand_probe(struct pci_dev *pdev, | |||
| 529 | { | 530 | { |
| 530 | struct mtd_info *mtd; | 531 | struct mtd_info *mtd; |
| 531 | struct cafe_priv *cafe; | 532 | struct cafe_priv *cafe; |
| 533 | uint32_t timing1, timing2, timing3; | ||
| 532 | uint32_t ctrl; | 534 | uint32_t ctrl; |
| 533 | int err = 0; | 535 | int err = 0; |
| 534 | 536 | ||
| @@ -580,31 +582,45 @@ static int __devinit cafe_nand_probe(struct pci_dev *pdev, | |||
| 580 | cafe->nand.block_bad = cafe_nand_block_bad; | 582 | cafe->nand.block_bad = cafe_nand_block_bad; |
| 581 | } | 583 | } |
| 582 | 584 | ||
| 585 | if (numtimings && numtimings != 3) { | ||
| 586 | dev_warn(&cafe->pdev->dev, "%d timing register values ignored; precisely three are required\n", numtimings); | ||
| 587 | } | ||
| 588 | |||
| 589 | if (numtimings == 3) { | ||
| 590 | timing1 = timing[0]; | ||
| 591 | timing2 = timing[1]; | ||
| 592 | timing3 = timing[2]; | ||
| 593 | cafe_dev_dbg(&cafe->pdev->dev, "Using provided timings (%08x %08x %08x)\n", | ||
| 594 | timing1, timing2, timing3); | ||
| 595 | } else { | ||
| 596 | timing1 = cafe_readl(cafe, NAND_TIMING1); | ||
| 597 | timing2 = cafe_readl(cafe, NAND_TIMING2); | ||
| 598 | timing3 = cafe_readl(cafe, NAND_TIMING3); | ||
| 599 | |||
| 600 | if (timing1 | timing2 | timing3) { | ||
| 601 | cafe_dev_dbg(&cafe->pdev->dev, "Timing registers already set (%08x %08x %08x)\n", timing1, timing2, timing3); | ||
| 602 | } else { | ||
| 603 | dev_warn(&cafe->pdev->dev, "Timing registers unset; using most conservative defaults\n"); | ||
| 604 | timing1 = timing2 = timing3 = 0xffffffff; | ||
| 605 | } | ||
| 606 | } | ||
| 607 | |||
| 583 | /* Start off by resetting the NAND controller completely */ | 608 | /* Start off by resetting the NAND controller completely */ |
| 584 | cafe_writel(cafe, 1, NAND_RESET); | 609 | cafe_writel(cafe, 1, NAND_RESET); |
| 585 | cafe_writel(cafe, 0, NAND_RESET); | 610 | cafe_writel(cafe, 0, NAND_RESET); |
| 586 | 611 | ||
| 587 | cafe_writel(cafe, 0xffffffff, NAND_IRQ_MASK); | 612 | cafe_writel(cafe, timing1, NAND_TIMING1); |
| 613 | cafe_writel(cafe, timing2, NAND_TIMING2); | ||
| 614 | cafe_writel(cafe, timing3, NAND_TIMING3); | ||
| 588 | 615 | ||
| 589 | /* Timings from Marvell's test code (not verified or calculated by us) */ | ||
| 590 | if (!slowtiming) { | ||
| 591 | cafe_writel(cafe, 0x01010a0a, NAND_TIMING1); | ||
| 592 | cafe_writel(cafe, 0x24121212, NAND_TIMING2); | ||
| 593 | cafe_writel(cafe, 0x11000000, NAND_TIMING3); | ||
| 594 | } else { | ||
| 595 | cafe_writel(cafe, 0xffffffff, NAND_TIMING1); | ||
| 596 | cafe_writel(cafe, 0xffffffff, NAND_TIMING2); | ||
| 597 | cafe_writel(cafe, 0xffffffff, NAND_TIMING3); | ||
| 598 | } | ||
| 599 | cafe_writel(cafe, 0xffffffff, NAND_IRQ_MASK); | 616 | cafe_writel(cafe, 0xffffffff, NAND_IRQ_MASK); |
| 600 | err = request_irq(pdev->irq, &cafe_nand_interrupt, IRQF_SHARED, | 617 | err = request_irq(pdev->irq, &cafe_nand_interrupt, IRQF_SHARED, |
| 601 | "CAFE NAND", mtd); | 618 | "CAFE NAND", mtd); |
| 602 | if (err) { | 619 | if (err) { |
| 603 | dev_warn(&pdev->dev, "Could not register IRQ %d\n", pdev->irq); | 620 | dev_warn(&pdev->dev, "Could not register IRQ %d\n", pdev->irq); |
| 604 | |||
| 605 | goto out_free_dma; | 621 | goto out_free_dma; |
| 606 | } | 622 | } |
| 607 | #if 1 | 623 | |
| 608 | /* Disable master reset, enable NAND clock */ | 624 | /* Disable master reset, enable NAND clock */ |
| 609 | ctrl = cafe_readl(cafe, GLOBAL_CTRL); | 625 | ctrl = cafe_readl(cafe, GLOBAL_CTRL); |
| 610 | ctrl &= 0xffffeff0; | 626 | ctrl &= 0xffffeff0; |
| @@ -631,32 +647,8 @@ static int __devinit cafe_nand_probe(struct pci_dev *pdev, | |||
| 631 | cafe_writel(cafe, 0x80000007, GLOBAL_IRQ_MASK); | 647 | cafe_writel(cafe, 0x80000007, GLOBAL_IRQ_MASK); |
| 632 | cafe_dev_dbg(&cafe->pdev->dev, "Control %x, IRQ mask %x\n", | 648 | cafe_dev_dbg(&cafe->pdev->dev, "Control %x, IRQ mask %x\n", |
| 633 | cafe_readl(cafe, GLOBAL_CTRL), cafe_readl(cafe, GLOBAL_IRQ_MASK)); | 649 | cafe_readl(cafe, GLOBAL_CTRL), cafe_readl(cafe, GLOBAL_IRQ_MASK)); |
| 634 | #endif | 650 | |
| 635 | #if 1 | 651 | /* Scan to find existence of the device */ |
| 636 | mtd->writesize=2048; | ||
| 637 | mtd->oobsize = 0x40; | ||
| 638 | memset(cafe->dmabuf, 0x5a, 2112); | ||
| 639 | cafe->nand.cmdfunc(mtd, NAND_CMD_READID, 0, -1); | ||
| 640 | cafe->nand.read_byte(mtd); | ||
| 641 | cafe->nand.read_byte(mtd); | ||
| 642 | cafe->nand.read_byte(mtd); | ||
| 643 | cafe->nand.read_byte(mtd); | ||
| 644 | cafe->nand.read_byte(mtd); | ||
| 645 | #endif | ||
| 646 | #if 0 | ||
| 647 | cafe->nand.cmdfunc(mtd, NAND_CMD_READ0, 0, 0); | ||
| 648 | // nand_wait_ready(mtd); | ||
| 649 | cafe->nand.read_byte(mtd); | ||
| 650 | cafe->nand.read_byte(mtd); | ||
| 651 | cafe->nand.read_byte(mtd); | ||
| 652 | cafe->nand.read_byte(mtd); | ||
| 653 | #endif | ||
| 654 | #if 0 | ||
| 655 | writel(0x84600070, cafe->mmio); | ||
| 656 | udelay(10); | ||
| 657 | cafe_dev_dbg(&cafe->pdev->dev, "Status %x\n", cafe_readl(cafe, NAND_NONMEM)); | ||
| 658 | #endif | ||
| 659 | /* Scan to find existance of the device */ | ||
| 660 | if (nand_scan_ident(mtd, 1)) { | 652 | if (nand_scan_ident(mtd, 1)) { |
| 661 | err = -ENXIO; | 653 | err = -ENXIO; |
| 662 | goto out_irq; | 654 | goto out_irq; |
| @@ -760,13 +752,4 @@ module_exit(cafe_nand_exit); | |||
| 760 | 752 | ||
| 761 | MODULE_LICENSE("GPL"); | 753 | MODULE_LICENSE("GPL"); |
| 762 | MODULE_AUTHOR("David Woodhouse <dwmw2@infradead.org>"); | 754 | MODULE_AUTHOR("David Woodhouse <dwmw2@infradead.org>"); |
| 763 | MODULE_DESCRIPTION("NAND flash driver for OLPC CAFE chip"); | 755 | MODULE_DESCRIPTION("NAND flash driver for OLPC CAFÉ chip"); |
| 764 | |||
| 765 | /* Correct ECC for 2048 bytes of 0xff: | ||
| 766 | 41 a0 71 65 54 27 f3 93 ec a9 be ed 0b a1 */ | ||
| 767 | |||
| 768 | /* dwmw2's B-test board, in case of completely screwing it: | ||
| 769 | Bad eraseblock 2394 at 0x12b40000 | ||
| 770 | Bad eraseblock 2627 at 0x14860000 | ||
| 771 | Bad eraseblock 3349 at 0x1a2a0000 | ||
| 772 | */ | ||
diff --git a/drivers/mtd/nand/cafe_ecc.c b/drivers/mtd/nand/cafe_ecc.c index 1b9fa05a4474..ea5c8491d2c5 100644 --- a/drivers/mtd/nand/cafe_ecc.c +++ b/drivers/mtd/nand/cafe_ecc.c | |||
| @@ -1045,7 +1045,7 @@ static unsigned short err_pos_lut[4096] = { | |||
| 1045 | 1045 | ||
| 1046 | static unsigned short err_pos(unsigned short din) | 1046 | static unsigned short err_pos(unsigned short din) |
| 1047 | { | 1047 | { |
| 1048 | BUG_ON(din > 4096); | 1048 | BUG_ON(din >= ARRAY_SIZE(err_pos_lut)); |
| 1049 | return err_pos_lut[din]; | 1049 | return err_pos_lut[din]; |
| 1050 | } | 1050 | } |
| 1051 | static int chk_no_err_only(unsigned short *chk_syndrome_list, unsigned short *err_info) | 1051 | static int chk_no_err_only(unsigned short *chk_syndrome_list, unsigned short *err_info) |
diff --git a/drivers/mtd/nand/excite_nandflash.c b/drivers/mtd/nand/excite_nandflash.c new file mode 100644 index 000000000000..7e9afc4c7757 --- /dev/null +++ b/drivers/mtd/nand/excite_nandflash.c | |||
| @@ -0,0 +1,248 @@ | |||
| 1 | /* | ||
| 2 | * Copyright (C) 2005 - 2007 by Basler Vision Technologies AG | ||
| 3 | * Author: Thomas Koeller <thomas.koeller.qbaslerweb.com> | ||
| 4 | * Original code by Thies Moeller <thies.moeller@baslerweb.com> | ||
| 5 | * | ||
| 6 | * This program is free software; you can redistribute it and/or modify | ||
| 7 | * it under the terms of the GNU General Public License as published by | ||
| 8 | * the Free Software Foundation; either version 2 of the License, or | ||
| 9 | * (at your option) any later version. | ||
| 10 | * | ||
| 11 | * This program is distributed in the hope that it will be useful, | ||
| 12 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
| 13 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
| 14 | * GNU General Public License for more details. | ||
| 15 | * | ||
| 16 | * You should have received a copy of the GNU General Public License | ||
| 17 | * along with this program; if not, write to the Free Software | ||
| 18 | * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA | ||
| 19 | */ | ||
| 20 | |||
| 21 | #include <linux/module.h> | ||
| 22 | #include <linux/types.h> | ||
| 23 | #include <linux/init.h> | ||
| 24 | #include <linux/kernel.h> | ||
| 25 | #include <linux/string.h> | ||
| 26 | #include <linux/ioport.h> | ||
| 27 | #include <linux/platform_device.h> | ||
| 28 | #include <linux/delay.h> | ||
| 29 | #include <linux/err.h> | ||
| 30 | #include <linux/kernel.h> | ||
| 31 | |||
| 32 | #include <linux/mtd/mtd.h> | ||
| 33 | #include <linux/mtd/nand.h> | ||
| 34 | #include <linux/mtd/nand_ecc.h> | ||
| 35 | #include <linux/mtd/partitions.h> | ||
| 36 | |||
| 37 | #include <asm/io.h> | ||
| 38 | #include <asm/rm9k-ocd.h> | ||
| 39 | |||
| 40 | #include <excite_nandflash.h> | ||
| 41 | |||
| 42 | #define EXCITE_NANDFLASH_VERSION "0.1" | ||
| 43 | |||
| 44 | /* I/O register offsets */ | ||
| 45 | #define EXCITE_NANDFLASH_DATA_BYTE 0x00 | ||
| 46 | #define EXCITE_NANDFLASH_STATUS_BYTE 0x0c | ||
| 47 | #define EXCITE_NANDFLASH_ADDR_BYTE 0x10 | ||
| 48 | #define EXCITE_NANDFLASH_CMD_BYTE 0x14 | ||
| 49 | |||
| 50 | /* prefix for debug output */ | ||
| 51 | static const char module_id[] = "excite_nandflash"; | ||
| 52 | |||
| 53 | /* | ||
| 54 | * partition definition | ||
| 55 | */ | ||
| 56 | static const struct mtd_partition partition_info[] = { | ||
| 57 | { | ||
| 58 | .name = "eXcite RootFS", | ||
| 59 | .offset = 0, | ||
| 60 | .size = MTDPART_SIZ_FULL | ||
| 61 | } | ||
| 62 | }; | ||
| 63 | |||
| 64 | static inline const struct resource * | ||
| 65 | excite_nand_get_resource(struct platform_device *d, unsigned long flags, | ||
| 66 | const char *basename) | ||
| 67 | { | ||
| 68 | char buf[80]; | ||
| 69 | |||
| 70 | if (snprintf(buf, sizeof buf, "%s_%u", basename, d->id) >= sizeof buf) | ||
| 71 | return NULL; | ||
| 72 | return platform_get_resource_byname(d, flags, buf); | ||
| 73 | } | ||
| 74 | |||
| 75 | static inline void __iomem * | ||
| 76 | excite_nand_map_regs(struct platform_device *d, const char *basename) | ||
| 77 | { | ||
| 78 | void *result = NULL; | ||
| 79 | const struct resource *const r = | ||
| 80 | excite_nand_get_resource(d, IORESOURCE_MEM, basename); | ||
| 81 | |||
| 82 | if (r) | ||
| 83 | result = ioremap_nocache(r->start, r->end + 1 - r->start); | ||
| 84 | return result; | ||
| 85 | } | ||
| 86 | |||
| 87 | /* controller and mtd information */ | ||
| 88 | struct excite_nand_drvdata { | ||
| 89 | struct mtd_info board_mtd; | ||
| 90 | struct nand_chip board_chip; | ||
| 91 | void __iomem *regs; | ||
| 92 | void __iomem *tgt; | ||
| 93 | }; | ||
| 94 | |||
| 95 | /* Control function */ | ||
| 96 | static void excite_nand_control(struct mtd_info *mtd, int cmd, | ||
| 97 | unsigned int ctrl) | ||
| 98 | { | ||
| 99 | struct excite_nand_drvdata * const d = | ||
| 100 | container_of(mtd, struct excite_nand_drvdata, board_mtd); | ||
| 101 | |||
| 102 | switch (ctrl) { | ||
| 103 | case NAND_CTRL_CHANGE | NAND_CTRL_CLE: | ||
| 104 | d->tgt = d->regs + EXCITE_NANDFLASH_CMD_BYTE; | ||
| 105 | break; | ||
| 106 | case NAND_CTRL_CHANGE | NAND_CTRL_ALE: | ||
| 107 | d->tgt = d->regs + EXCITE_NANDFLASH_ADDR_BYTE; | ||
| 108 | break; | ||
| 109 | case NAND_CTRL_CHANGE | NAND_NCE: | ||
| 110 | d->tgt = d->regs + EXCITE_NANDFLASH_DATA_BYTE; | ||
| 111 | break; | ||
| 112 | } | ||
| 113 | |||
| 114 | if (cmd != NAND_CMD_NONE) | ||
| 115 | __raw_writeb(cmd, d->tgt); | ||
| 116 | } | ||
| 117 | |||
| 118 | /* Return 0 if flash is busy, 1 if ready */ | ||
| 119 | static int excite_nand_devready(struct mtd_info *mtd) | ||
| 120 | { | ||
| 121 | struct excite_nand_drvdata * const drvdata = | ||
| 122 | container_of(mtd, struct excite_nand_drvdata, board_mtd); | ||
| 123 | |||
| 124 | return __raw_readb(drvdata->regs + EXCITE_NANDFLASH_STATUS_BYTE); | ||
| 125 | } | ||
| 126 | |||
| 127 | /* | ||
| 128 | * Called by device layer to remove the driver. | ||
| 129 | * The binding to the mtd and all allocated | ||
| 130 | * resources are released. | ||
| 131 | */ | ||
| 132 | static int __exit excite_nand_remove(struct device *dev) | ||
| 133 | { | ||
| 134 | struct excite_nand_drvdata * const this = dev_get_drvdata(dev); | ||
| 135 | |||
| 136 | dev_set_drvdata(dev, NULL); | ||
| 137 | |||
| 138 | if (unlikely(!this)) { | ||
| 139 | printk(KERN_ERR "%s: called %s without private data!!", | ||
| 140 | module_id, __func__); | ||
| 141 | return -EINVAL; | ||
| 142 | } | ||
| 143 | |||
| 144 | /* first thing we need to do is release our mtd | ||
| 145 | * then go through freeing the resource used | ||
| 146 | */ | ||
| 147 | nand_release(&this->board_mtd); | ||
| 148 | |||
| 149 | /* free the common resources */ | ||
| 150 | iounmap(this->regs); | ||
| 151 | kfree(this); | ||
| 152 | |||
| 153 | DEBUG(MTD_DEBUG_LEVEL1, "%s: removed\n", module_id); | ||
| 154 | return 0; | ||
| 155 | } | ||
| 156 | |||
| 157 | /* | ||
| 158 | * Called by device layer when it finds a device matching | ||
| 159 | * one our driver can handle. This code checks to see if | ||
| 160 | * it can allocate all necessary resources then calls the | ||
| 161 | * nand layer to look for devices. | ||
| 162 | */ | ||
| 163 | static int __init excite_nand_probe(struct device *dev) | ||
| 164 | { | ||
| 165 | struct platform_device * const pdev = to_platform_device(dev); | ||
| 166 | struct excite_nand_drvdata *drvdata; /* private driver data */ | ||
| 167 | struct nand_chip *board_chip; /* private flash chip data */ | ||
| 168 | struct mtd_info *board_mtd; /* mtd info for this board */ | ||
| 169 | int scan_res; | ||
| 170 | |||
| 171 | drvdata = kzalloc(sizeof(*drvdata), GFP_KERNEL); | ||
| 172 | if (unlikely(!drvdata)) { | ||
| 173 | printk(KERN_ERR "%s: no memory for drvdata\n", | ||
| 174 | module_id); | ||
| 175 | return -ENOMEM; | ||
| 176 | } | ||
| 177 | |||
| 178 | /* bind private data into driver */ | ||
| 179 | dev_set_drvdata(dev, drvdata); | ||
| 180 | |||
| 181 | /* allocate and map the resource */ | ||
| 182 | drvdata->regs = | ||
| 183 | excite_nand_map_regs(pdev, EXCITE_NANDFLASH_RESOURCE_REGS); | ||
| 184 | |||
| 185 | if (unlikely(!drvdata->regs)) { | ||
| 186 | printk(KERN_ERR "%s: cannot reserve register region\n", | ||
| 187 | module_id); | ||
| 188 | kfree(drvdata); | ||
| 189 | return -ENXIO; | ||
| 190 | } | ||
| 191 | |||
| 192 | drvdata->tgt = drvdata->regs + EXCITE_NANDFLASH_DATA_BYTE; | ||
| 193 | |||
| 194 | /* initialise our chip */ | ||
| 195 | board_chip = &drvdata->board_chip; | ||
| 196 | board_chip->IO_ADDR_R = board_chip->IO_ADDR_W = | ||
| 197 | drvdata->regs + EXCITE_NANDFLASH_DATA_BYTE; | ||
| 198 | board_chip->cmd_ctrl = excite_nand_control; | ||
| 199 | board_chip->dev_ready = excite_nand_devready; | ||
| 200 | board_chip->chip_delay = 25; | ||
| 201 | board_chip->ecc.mode = NAND_ECC_SOFT; | ||
| 202 | |||
| 203 | /* link chip to mtd */ | ||
| 204 | board_mtd = &drvdata->board_mtd; | ||
| 205 | board_mtd->priv = board_chip; | ||
| 206 | |||
| 207 | DEBUG(MTD_DEBUG_LEVEL2, "%s: device scan\n", module_id); | ||
| 208 | scan_res = nand_scan(&drvdata->board_mtd, 1); | ||
| 209 | |||
| 210 | if (likely(!scan_res)) { | ||
| 211 | DEBUG(MTD_DEBUG_LEVEL2, "%s: register partitions\n", module_id); | ||
| 212 | add_mtd_partitions(&drvdata->board_mtd, partition_info, | ||
| 213 | sizeof partition_info / sizeof partition_info[0]); | ||
| 214 | } else { | ||
| 215 | iounmap(drvdata->regs); | ||
| 216 | kfree(drvdata); | ||
| 217 | printk(KERN_ERR "%s: device scan failed\n", module_id); | ||
| 218 | return -EIO; | ||
| 219 | } | ||
| 220 | return 0; | ||
| 221 | } | ||
| 222 | |||
| 223 | static struct device_driver excite_nand_driver = { | ||
| 224 | .name = "excite_nand", | ||
| 225 | .bus = &platform_bus_type, | ||
| 226 | .probe = excite_nand_probe, | ||
| 227 | .remove = __exit_p(excite_nand_remove) | ||
| 228 | }; | ||
| 229 | |||
| 230 | static int __init excite_nand_init(void) | ||
| 231 | { | ||
| 232 | pr_info("Basler eXcite nand flash driver Version " | ||
| 233 | EXCITE_NANDFLASH_VERSION "\n"); | ||
| 234 | return driver_register(&excite_nand_driver); | ||
| 235 | } | ||
| 236 | |||
| 237 | static void __exit excite_nand_exit(void) | ||
| 238 | { | ||
| 239 | driver_unregister(&excite_nand_driver); | ||
| 240 | } | ||
| 241 | |||
| 242 | module_init(excite_nand_init); | ||
| 243 | module_exit(excite_nand_exit); | ||
| 244 | |||
| 245 | MODULE_AUTHOR("Thomas Koeller <thomas.koeller@baslerweb.com>"); | ||
| 246 | MODULE_DESCRIPTION("Basler eXcite NAND-Flash driver"); | ||
| 247 | MODULE_LICENSE("GPL"); | ||
| 248 | MODULE_VERSION(EXCITE_NANDFLASH_VERSION) | ||
diff --git a/drivers/mtd/nand/nand_base.c b/drivers/mtd/nand/nand_base.c index dfe56e03e48b..acaf97bc80d1 100644 --- a/drivers/mtd/nand/nand_base.c +++ b/drivers/mtd/nand/nand_base.c | |||
| @@ -1272,10 +1272,25 @@ static int nand_do_read_oob(struct mtd_info *mtd, loff_t from, | |||
| 1272 | DEBUG(MTD_DEBUG_LEVEL3, "nand_read_oob: from = 0x%08Lx, len = %i\n", | 1272 | DEBUG(MTD_DEBUG_LEVEL3, "nand_read_oob: from = 0x%08Lx, len = %i\n", |
| 1273 | (unsigned long long)from, readlen); | 1273 | (unsigned long long)from, readlen); |
| 1274 | 1274 | ||
| 1275 | if (ops->mode == MTD_OOB_RAW) | 1275 | if (ops->mode == MTD_OOB_AUTO) |
| 1276 | len = mtd->oobsize; | ||
| 1277 | else | ||
| 1278 | len = chip->ecc.layout->oobavail; | 1276 | len = chip->ecc.layout->oobavail; |
| 1277 | else | ||
| 1278 | len = mtd->oobsize; | ||
| 1279 | |||
| 1280 | if (unlikely(ops->ooboffs >= len)) { | ||
| 1281 | DEBUG(MTD_DEBUG_LEVEL0, "nand_read_oob: " | ||
| 1282 | "Attempt to start read outside oob\n"); | ||
| 1283 | return -EINVAL; | ||
| 1284 | } | ||
| 1285 | |||
| 1286 | /* Do not allow reads past end of device */ | ||
| 1287 | if (unlikely(from >= mtd->size || | ||
| 1288 | ops->ooboffs + readlen > ((mtd->size >> chip->page_shift) - | ||
| 1289 | (from >> chip->page_shift)) * len)) { | ||
| 1290 | DEBUG(MTD_DEBUG_LEVEL0, "nand_read_oob: " | ||
| 1291 | "Attempt read beyond end of device\n"); | ||
| 1292 | return -EINVAL; | ||
| 1293 | } | ||
| 1279 | 1294 | ||
| 1280 | chipnr = (int)(from >> chip->chip_shift); | 1295 | chipnr = (int)(from >> chip->chip_shift); |
| 1281 | chip->select_chip(mtd, chipnr); | 1296 | chip->select_chip(mtd, chipnr); |
| @@ -1742,19 +1757,40 @@ static int nand_write(struct mtd_info *mtd, loff_t to, size_t len, | |||
| 1742 | static int nand_do_write_oob(struct mtd_info *mtd, loff_t to, | 1757 | static int nand_do_write_oob(struct mtd_info *mtd, loff_t to, |
| 1743 | struct mtd_oob_ops *ops) | 1758 | struct mtd_oob_ops *ops) |
| 1744 | { | 1759 | { |
| 1745 | int chipnr, page, status; | 1760 | int chipnr, page, status, len; |
| 1746 | struct nand_chip *chip = mtd->priv; | 1761 | struct nand_chip *chip = mtd->priv; |
| 1747 | 1762 | ||
| 1748 | DEBUG(MTD_DEBUG_LEVEL3, "nand_write_oob: to = 0x%08x, len = %i\n", | 1763 | DEBUG(MTD_DEBUG_LEVEL3, "nand_write_oob: to = 0x%08x, len = %i\n", |
| 1749 | (unsigned int)to, (int)ops->ooblen); | 1764 | (unsigned int)to, (int)ops->ooblen); |
| 1750 | 1765 | ||
| 1766 | if (ops->mode == MTD_OOB_AUTO) | ||
| 1767 | len = chip->ecc.layout->oobavail; | ||
| 1768 | else | ||
| 1769 | len = mtd->oobsize; | ||
| 1770 | |||
| 1751 | /* Do not allow write past end of page */ | 1771 | /* Do not allow write past end of page */ |
| 1752 | if ((ops->ooboffs + ops->ooblen) > mtd->oobsize) { | 1772 | if ((ops->ooboffs + ops->ooblen) > len) { |
| 1753 | DEBUG(MTD_DEBUG_LEVEL0, "nand_write_oob: " | 1773 | DEBUG(MTD_DEBUG_LEVEL0, "nand_write_oob: " |
| 1754 | "Attempt to write past end of page\n"); | 1774 | "Attempt to write past end of page\n"); |
| 1755 | return -EINVAL; | 1775 | return -EINVAL; |
| 1756 | } | 1776 | } |
| 1757 | 1777 | ||
| 1778 | if (unlikely(ops->ooboffs >= len)) { | ||
| 1779 | DEBUG(MTD_DEBUG_LEVEL0, "nand_read_oob: " | ||
| 1780 | "Attempt to start write outside oob\n"); | ||
| 1781 | return -EINVAL; | ||
| 1782 | } | ||
| 1783 | |||
| 1784 | /* Do not allow reads past end of device */ | ||
| 1785 | if (unlikely(to >= mtd->size || | ||
| 1786 | ops->ooboffs + ops->ooblen > | ||
| 1787 | ((mtd->size >> chip->page_shift) - | ||
| 1788 | (to >> chip->page_shift)) * len)) { | ||
| 1789 | DEBUG(MTD_DEBUG_LEVEL0, "nand_read_oob: " | ||
| 1790 | "Attempt write beyond end of device\n"); | ||
| 1791 | return -EINVAL; | ||
| 1792 | } | ||
| 1793 | |||
| 1758 | chipnr = (int)(to >> chip->chip_shift); | 1794 | chipnr = (int)(to >> chip->chip_shift); |
| 1759 | chip->select_chip(mtd, chipnr); | 1795 | chip->select_chip(mtd, chipnr); |
| 1760 | 1796 | ||
| @@ -2530,7 +2566,6 @@ int nand_scan_tail(struct mtd_info *mtd) | |||
| 2530 | /* Fill in remaining MTD driver data */ | 2566 | /* Fill in remaining MTD driver data */ |
| 2531 | mtd->type = MTD_NANDFLASH; | 2567 | mtd->type = MTD_NANDFLASH; |
| 2532 | mtd->flags = MTD_CAP_NANDFLASH; | 2568 | mtd->flags = MTD_CAP_NANDFLASH; |
| 2533 | mtd->ecctype = MTD_ECC_SW; | ||
| 2534 | mtd->erase = nand_erase; | 2569 | mtd->erase = nand_erase; |
| 2535 | mtd->point = NULL; | 2570 | mtd->point = NULL; |
| 2536 | mtd->unpoint = NULL; | 2571 | mtd->unpoint = NULL; |
diff --git a/drivers/mtd/nand/s3c2410.c b/drivers/mtd/nand/s3c2410.c index 8b3203571eeb..0ddfd6de75c5 100644 --- a/drivers/mtd/nand/s3c2410.c +++ b/drivers/mtd/nand/s3c2410.c | |||
| @@ -337,17 +337,69 @@ static int s3c2412_nand_devready(struct mtd_info *mtd) | |||
| 337 | static int s3c2410_nand_correct_data(struct mtd_info *mtd, u_char *dat, | 337 | static int s3c2410_nand_correct_data(struct mtd_info *mtd, u_char *dat, |
| 338 | u_char *read_ecc, u_char *calc_ecc) | 338 | u_char *read_ecc, u_char *calc_ecc) |
| 339 | { | 339 | { |
| 340 | pr_debug("s3c2410_nand_correct_data(%p,%p,%p,%p)\n", mtd, dat, read_ecc, calc_ecc); | 340 | struct s3c2410_nand_info *info = s3c2410_nand_mtd_toinfo(mtd); |
| 341 | unsigned int diff0, diff1, diff2; | ||
| 342 | unsigned int bit, byte; | ||
| 341 | 343 | ||
| 342 | pr_debug("eccs: read %02x,%02x,%02x vs calc %02x,%02x,%02x\n", | 344 | pr_debug("%s(%p,%p,%p,%p)\n", __func__, mtd, dat, read_ecc, calc_ecc); |
| 343 | read_ecc[0], read_ecc[1], read_ecc[2], calc_ecc[0], calc_ecc[1], calc_ecc[2]); | ||
| 344 | 345 | ||
| 345 | if (read_ecc[0] == calc_ecc[0] && read_ecc[1] == calc_ecc[1] && read_ecc[2] == calc_ecc[2]) | 346 | diff0 = read_ecc[0] ^ calc_ecc[0]; |
| 346 | return 0; | 347 | diff1 = read_ecc[1] ^ calc_ecc[1]; |
| 348 | diff2 = read_ecc[2] ^ calc_ecc[2]; | ||
| 349 | |||
| 350 | pr_debug("%s: rd %02x%02x%02x calc %02x%02x%02x diff %02x%02x%02x\n", | ||
| 351 | __func__, | ||
| 352 | read_ecc[0], read_ecc[1], read_ecc[2], | ||
| 353 | calc_ecc[0], calc_ecc[1], calc_ecc[2], | ||
| 354 | diff0, diff1, diff2); | ||
| 355 | |||
| 356 | if (diff0 == 0 && diff1 == 0 && diff2 == 0) | ||
| 357 | return 0; /* ECC is ok */ | ||
| 358 | |||
| 359 | /* Can we correct this ECC (ie, one row and column change). | ||
| 360 | * Note, this is similar to the 256 error code on smartmedia */ | ||
| 361 | |||
| 362 | if (((diff0 ^ (diff0 >> 1)) & 0x55) == 0x55 && | ||
| 363 | ((diff1 ^ (diff1 >> 1)) & 0x55) == 0x55 && | ||
| 364 | ((diff2 ^ (diff2 >> 1)) & 0x55) == 0x55) { | ||
| 365 | /* calculate the bit position of the error */ | ||
| 366 | |||
| 367 | bit = (diff2 >> 2) & 1; | ||
| 368 | bit |= (diff2 >> 3) & 2; | ||
| 369 | bit |= (diff2 >> 4) & 4; | ||
| 370 | |||
| 371 | /* calculate the byte position of the error */ | ||
| 372 | |||
| 373 | byte = (diff1 << 1) & 0x80; | ||
| 374 | byte |= (diff1 << 2) & 0x40; | ||
| 375 | byte |= (diff1 << 3) & 0x20; | ||
| 376 | byte |= (diff1 << 4) & 0x10; | ||
| 377 | |||
| 378 | byte |= (diff0 >> 3) & 0x08; | ||
| 379 | byte |= (diff0 >> 2) & 0x04; | ||
| 380 | byte |= (diff0 >> 1) & 0x02; | ||
| 381 | byte |= (diff0 >> 0) & 0x01; | ||
| 347 | 382 | ||
| 348 | /* we curently have no method for correcting the error */ | 383 | byte |= (diff2 << 8) & 0x100; |
| 349 | 384 | ||
| 350 | return -1; | 385 | dev_dbg(info->device, "correcting error bit %d, byte %d\n", |
| 386 | bit, byte); | ||
| 387 | |||
| 388 | dat[byte] ^= (1 << bit); | ||
| 389 | return 1; | ||
| 390 | } | ||
| 391 | |||
| 392 | /* if there is only one bit difference in the ECC, then | ||
| 393 | * one of only a row or column parity has changed, which | ||
| 394 | * means the error is most probably in the ECC itself */ | ||
| 395 | |||
| 396 | diff0 |= (diff1 << 8); | ||
| 397 | diff0 |= (diff2 << 16); | ||
| 398 | |||
| 399 | if ((diff0 & ~(1<<fls(diff0))) == 0) | ||
| 400 | return 1; | ||
| 401 | |||
| 402 | return 0; | ||
| 351 | } | 403 | } |
| 352 | 404 | ||
| 353 | /* ECC functions | 405 | /* ECC functions |
| @@ -366,6 +418,15 @@ static void s3c2410_nand_enable_hwecc(struct mtd_info *mtd, int mode) | |||
| 366 | writel(ctrl, info->regs + S3C2410_NFCONF); | 418 | writel(ctrl, info->regs + S3C2410_NFCONF); |
| 367 | } | 419 | } |
| 368 | 420 | ||
| 421 | static void s3c2412_nand_enable_hwecc(struct mtd_info *mtd, int mode) | ||
| 422 | { | ||
| 423 | struct s3c2410_nand_info *info = s3c2410_nand_mtd_toinfo(mtd); | ||
| 424 | unsigned long ctrl; | ||
| 425 | |||
| 426 | ctrl = readl(info->regs + S3C2440_NFCONT); | ||
| 427 | writel(ctrl | S3C2412_NFCONT_INIT_MAIN_ECC, info->regs + S3C2440_NFCONT); | ||
| 428 | } | ||
| 429 | |||
| 369 | static void s3c2440_nand_enable_hwecc(struct mtd_info *mtd, int mode) | 430 | static void s3c2440_nand_enable_hwecc(struct mtd_info *mtd, int mode) |
| 370 | { | 431 | { |
| 371 | struct s3c2410_nand_info *info = s3c2410_nand_mtd_toinfo(mtd); | 432 | struct s3c2410_nand_info *info = s3c2410_nand_mtd_toinfo(mtd); |
| @@ -383,6 +444,21 @@ static int s3c2410_nand_calculate_ecc(struct mtd_info *mtd, const u_char *dat, u | |||
| 383 | ecc_code[1] = readb(info->regs + S3C2410_NFECC + 1); | 444 | ecc_code[1] = readb(info->regs + S3C2410_NFECC + 1); |
| 384 | ecc_code[2] = readb(info->regs + S3C2410_NFECC + 2); | 445 | ecc_code[2] = readb(info->regs + S3C2410_NFECC + 2); |
| 385 | 446 | ||
| 447 | pr_debug("%s: returning ecc %02x%02x%02x\n", __func__, | ||
| 448 | ecc_code[0], ecc_code[1], ecc_code[2]); | ||
| 449 | |||
| 450 | return 0; | ||
| 451 | } | ||
| 452 | |||
| 453 | static int s3c2412_nand_calculate_ecc(struct mtd_info *mtd, const u_char *dat, u_char *ecc_code) | ||
| 454 | { | ||
| 455 | struct s3c2410_nand_info *info = s3c2410_nand_mtd_toinfo(mtd); | ||
| 456 | unsigned long ecc = readl(info->regs + S3C2412_NFMECC0); | ||
| 457 | |||
| 458 | ecc_code[0] = ecc; | ||
| 459 | ecc_code[1] = ecc >> 8; | ||
| 460 | ecc_code[2] = ecc >> 16; | ||
| 461 | |||
| 386 | pr_debug("calculate_ecc: returning ecc %02x,%02x,%02x\n", ecc_code[0], ecc_code[1], ecc_code[2]); | 462 | pr_debug("calculate_ecc: returning ecc %02x,%02x,%02x\n", ecc_code[0], ecc_code[1], ecc_code[2]); |
| 387 | 463 | ||
| 388 | return 0; | 464 | return 0; |
| @@ -397,7 +473,7 @@ static int s3c2440_nand_calculate_ecc(struct mtd_info *mtd, const u_char *dat, u | |||
| 397 | ecc_code[1] = ecc >> 8; | 473 | ecc_code[1] = ecc >> 8; |
| 398 | ecc_code[2] = ecc >> 16; | 474 | ecc_code[2] = ecc >> 16; |
| 399 | 475 | ||
| 400 | pr_debug("calculate_ecc: returning ecc %02x,%02x,%02x\n", ecc_code[0], ecc_code[1], ecc_code[2]); | 476 | pr_debug("%s: returning ecc %06x\n", __func__, ecc); |
| 401 | 477 | ||
| 402 | return 0; | 478 | return 0; |
| 403 | } | 479 | } |
| @@ -565,6 +641,10 @@ static void s3c2410_nand_init_chip(struct s3c2410_nand_info *info, | |||
| 565 | break; | 641 | break; |
| 566 | 642 | ||
| 567 | case TYPE_S3C2412: | 643 | case TYPE_S3C2412: |
| 644 | chip->ecc.hwctl = s3c2412_nand_enable_hwecc; | ||
| 645 | chip->ecc.calculate = s3c2412_nand_calculate_ecc; | ||
| 646 | break; | ||
| 647 | |||
| 568 | case TYPE_S3C2440: | 648 | case TYPE_S3C2440: |
| 569 | chip->ecc.hwctl = s3c2440_nand_enable_hwecc; | 649 | chip->ecc.hwctl = s3c2440_nand_enable_hwecc; |
| 570 | chip->ecc.calculate = s3c2440_nand_calculate_ecc; | 650 | chip->ecc.calculate = s3c2440_nand_calculate_ecc; |
diff --git a/drivers/mtd/onenand/onenand_base.c b/drivers/mtd/onenand/onenand_base.c index 2da6bb26353e..7f1cb6e5dccb 100644 --- a/drivers/mtd/onenand/onenand_base.c +++ b/drivers/mtd/onenand/onenand_base.c | |||
| @@ -1,7 +1,7 @@ | |||
| 1 | /* | 1 | /* |
| 2 | * linux/drivers/mtd/onenand/onenand_base.c | 2 | * linux/drivers/mtd/onenand/onenand_base.c |
| 3 | * | 3 | * |
| 4 | * Copyright (C) 2005-2006 Samsung Electronics | 4 | * Copyright (C) 2005-2007 Samsung Electronics |
| 5 | * Kyungmin Park <kyungmin.park@samsung.com> | 5 | * Kyungmin Park <kyungmin.park@samsung.com> |
| 6 | * | 6 | * |
| 7 | * This program is free software; you can redistribute it and/or modify | 7 | * 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) | |||
| 94 | */ | 94 | */ |
| 95 | static int onenand_block_address(struct onenand_chip *this, int block) | 95 | static int onenand_block_address(struct onenand_chip *this, int block) |
| 96 | { | 96 | { |
| 97 | if (this->device_id & ONENAND_DEVICE_IS_DDP) { | 97 | /* Device Flash Core select, NAND Flash Block Address */ |
| 98 | /* Device Flash Core select, NAND Flash Block Address */ | 98 | if (block & this->density_mask) |
| 99 | int dfs = 0; | 99 | return ONENAND_DDP_CHIP1 | (block ^ this->density_mask); |
| 100 | |||
| 101 | if (block & this->density_mask) | ||
| 102 | dfs = 1; | ||
| 103 | |||
| 104 | return (dfs << ONENAND_DDP_SHIFT) | | ||
| 105 | (block & (this->density_mask - 1)); | ||
| 106 | } | ||
| 107 | 100 | ||
| 108 | return block; | 101 | return block; |
| 109 | } | 102 | } |
| @@ -118,17 +111,11 @@ static int onenand_block_address(struct onenand_chip *this, int block) | |||
| 118 | */ | 111 | */ |
| 119 | static int onenand_bufferram_address(struct onenand_chip *this, int block) | 112 | static int onenand_bufferram_address(struct onenand_chip *this, int block) |
| 120 | { | 113 | { |
| 121 | if (this->device_id & ONENAND_DEVICE_IS_DDP) { | 114 | /* Device BufferRAM Select */ |
| 122 | /* Device BufferRAM Select */ | 115 | if (block & this->density_mask) |
| 123 | int dbs = 0; | 116 | return ONENAND_DDP_CHIP1; |
| 124 | |||
| 125 | if (block & this->density_mask) | ||
| 126 | dbs = 1; | ||
| 127 | 117 | ||
| 128 | return (dbs << ONENAND_DDP_SHIFT); | 118 | return ONENAND_DDP_CHIP0; |
| 129 | } | ||
| 130 | |||
| 131 | return 0; | ||
| 132 | } | 119 | } |
| 133 | 120 | ||
| 134 | /** | 121 | /** |
| @@ -317,22 +304,25 @@ static int onenand_wait(struct mtd_info *mtd, int state) | |||
| 317 | ctrl = this->read_word(this->base + ONENAND_REG_CTRL_STATUS); | 304 | ctrl = this->read_word(this->base + ONENAND_REG_CTRL_STATUS); |
| 318 | 305 | ||
| 319 | if (ctrl & ONENAND_CTRL_ERROR) { | 306 | if (ctrl & ONENAND_CTRL_ERROR) { |
| 320 | DEBUG(MTD_DEBUG_LEVEL0, "onenand_wait: controller error = 0x%04x\n", ctrl); | 307 | printk(KERN_ERR "onenand_wait: controller error = 0x%04x\n", ctrl); |
| 321 | if (ctrl & ONENAND_CTRL_LOCK) | 308 | if (ctrl & ONENAND_CTRL_LOCK) |
| 322 | DEBUG(MTD_DEBUG_LEVEL0, "onenand_wait: it's locked error.\n"); | 309 | printk(KERN_ERR "onenand_wait: it's locked error.\n"); |
| 323 | return ctrl; | 310 | return ctrl; |
| 324 | } | 311 | } |
| 325 | 312 | ||
| 326 | if (interrupt & ONENAND_INT_READ) { | 313 | if (interrupt & ONENAND_INT_READ) { |
| 327 | int ecc = this->read_word(this->base + ONENAND_REG_ECC_STATUS); | 314 | int ecc = this->read_word(this->base + ONENAND_REG_ECC_STATUS); |
| 328 | if (ecc) { | 315 | if (ecc) { |
| 329 | DEBUG(MTD_DEBUG_LEVEL0, "onenand_wait: ECC error = 0x%04x\n", ecc); | 316 | printk(KERN_ERR "onenand_wait: ECC error = 0x%04x\n", ecc); |
| 330 | if (ecc & ONENAND_ECC_2BIT_ALL) { | 317 | if (ecc & ONENAND_ECC_2BIT_ALL) { |
| 331 | mtd->ecc_stats.failed++; | 318 | mtd->ecc_stats.failed++; |
| 332 | return ecc; | 319 | return ecc; |
| 333 | } else if (ecc & ONENAND_ECC_1BIT_ALL) | 320 | } else if (ecc & ONENAND_ECC_1BIT_ALL) |
| 334 | mtd->ecc_stats.corrected++; | 321 | mtd->ecc_stats.corrected++; |
| 335 | } | 322 | } |
| 323 | } else if (state == FL_READING) { | ||
| 324 | printk(KERN_ERR "onenand_wait: read timeout! ctrl=0x%04x intr=0x%04x\n", ctrl, interrupt); | ||
| 325 | return -EIO; | ||
| 336 | } | 326 | } |
| 337 | 327 | ||
| 338 | return 0; | 328 | return 0; |
| @@ -587,22 +577,32 @@ static int onenand_write_bufferram(struct mtd_info *mtd, int area, | |||
| 587 | static int onenand_check_bufferram(struct mtd_info *mtd, loff_t addr) | 577 | static int onenand_check_bufferram(struct mtd_info *mtd, loff_t addr) |
| 588 | { | 578 | { |
| 589 | struct onenand_chip *this = mtd->priv; | 579 | struct onenand_chip *this = mtd->priv; |
| 590 | int block, page; | 580 | int blockpage, found = 0; |
| 591 | int i; | 581 | unsigned int i; |
| 592 | 582 | ||
| 593 | block = (int) (addr >> this->erase_shift); | 583 | blockpage = (int) (addr >> this->page_shift); |
| 594 | page = (int) (addr >> this->page_shift); | ||
| 595 | page &= this->page_mask; | ||
| 596 | 584 | ||
| 585 | /* Is there valid data? */ | ||
| 597 | i = ONENAND_CURRENT_BUFFERRAM(this); | 586 | i = ONENAND_CURRENT_BUFFERRAM(this); |
| 587 | if (this->bufferram[i].blockpage == blockpage) | ||
| 588 | found = 1; | ||
| 589 | else { | ||
| 590 | /* Check another BufferRAM */ | ||
| 591 | i = ONENAND_NEXT_BUFFERRAM(this); | ||
| 592 | if (this->bufferram[i].blockpage == blockpage) { | ||
| 593 | ONENAND_SET_NEXT_BUFFERRAM(this); | ||
| 594 | found = 1; | ||
| 595 | } | ||
| 596 | } | ||
| 598 | 597 | ||
| 599 | /* Is there valid data? */ | 598 | if (found && ONENAND_IS_DDP(this)) { |
| 600 | if (this->bufferram[i].block == block && | 599 | /* Select DataRAM for DDP */ |
| 601 | this->bufferram[i].page == page && | 600 | int block = (int) (addr >> this->erase_shift); |
| 602 | this->bufferram[i].valid) | 601 | int value = onenand_bufferram_address(this, block); |
| 603 | return 1; | 602 | this->write_word(value, this->base + ONENAND_REG_START_ADDRESS2); |
| 603 | } | ||
| 604 | 604 | ||
| 605 | return 0; | 605 | return found; |
| 606 | } | 606 | } |
| 607 | 607 | ||
| 608 | /** | 608 | /** |
| @@ -613,31 +613,49 @@ static int onenand_check_bufferram(struct mtd_info *mtd, loff_t addr) | |||
| 613 | * | 613 | * |
| 614 | * Update BufferRAM information | 614 | * Update BufferRAM information |
| 615 | */ | 615 | */ |
| 616 | static int onenand_update_bufferram(struct mtd_info *mtd, loff_t addr, | 616 | static void onenand_update_bufferram(struct mtd_info *mtd, loff_t addr, |
| 617 | int valid) | 617 | int valid) |
| 618 | { | 618 | { |
| 619 | struct onenand_chip *this = mtd->priv; | 619 | struct onenand_chip *this = mtd->priv; |
| 620 | int block, page; | 620 | int blockpage; |
| 621 | int i; | 621 | unsigned int i; |
| 622 | 622 | ||
| 623 | block = (int) (addr >> this->erase_shift); | 623 | blockpage = (int) (addr >> this->page_shift); |
| 624 | page = (int) (addr >> this->page_shift); | ||
| 625 | page &= this->page_mask; | ||
| 626 | 624 | ||
| 627 | /* Invalidate BufferRAM */ | 625 | /* Invalidate another BufferRAM */ |
| 628 | for (i = 0; i < MAX_BUFFERRAM; i++) { | 626 | i = ONENAND_NEXT_BUFFERRAM(this); |
| 629 | if (this->bufferram[i].block == block && | 627 | if (this->bufferram[i].blockpage == blockpage) |
| 630 | this->bufferram[i].page == page) | 628 | this->bufferram[i].blockpage = -1; |
| 631 | this->bufferram[i].valid = 0; | ||
| 632 | } | ||
| 633 | 629 | ||
| 634 | /* Update BufferRAM */ | 630 | /* Update BufferRAM */ |
| 635 | i = ONENAND_CURRENT_BUFFERRAM(this); | 631 | i = ONENAND_CURRENT_BUFFERRAM(this); |
| 636 | this->bufferram[i].block = block; | 632 | if (valid) |
| 637 | this->bufferram[i].page = page; | 633 | this->bufferram[i].blockpage = blockpage; |
| 638 | this->bufferram[i].valid = valid; | 634 | else |
| 635 | this->bufferram[i].blockpage = -1; | ||
| 636 | } | ||
| 639 | 637 | ||
| 640 | return 0; | 638 | /** |
| 639 | * onenand_invalidate_bufferram - [GENERIC] Invalidate BufferRAM information | ||
| 640 | * @param mtd MTD data structure | ||
| 641 | * @param addr start address to invalidate | ||
| 642 | * @param len length to invalidate | ||
| 643 | * | ||
| 644 | * Invalidate BufferRAM information | ||
| 645 | */ | ||
| 646 | static void onenand_invalidate_bufferram(struct mtd_info *mtd, loff_t addr, | ||
| 647 | unsigned int len) | ||
| 648 | { | ||
| 649 | struct onenand_chip *this = mtd->priv; | ||
| 650 | int i; | ||
| 651 | loff_t end_addr = addr + len; | ||
| 652 | |||
| 653 | /* Invalidate BufferRAM */ | ||
| 654 | for (i = 0; i < MAX_BUFFERRAM; i++) { | ||
| 655 | loff_t buf_addr = this->bufferram[i].blockpage << this->page_shift; | ||
| 656 | if (buf_addr >= addr && buf_addr < end_addr) | ||
| 657 | this->bufferram[i].blockpage = -1; | ||
| 658 | } | ||
| 641 | } | 659 | } |
| 642 | 660 | ||
| 643 | /** | 661 | /** |
| @@ -716,7 +734,7 @@ static int onenand_read(struct mtd_info *mtd, loff_t from, size_t len, | |||
| 716 | 734 | ||
| 717 | /* Do not allow reads past end of device */ | 735 | /* Do not allow reads past end of device */ |
| 718 | if ((from + len) > mtd->size) { | 736 | if ((from + len) > mtd->size) { |
| 719 | DEBUG(MTD_DEBUG_LEVEL0, "onenand_read: Attempt read beyond end of device\n"); | 737 | printk(KERN_ERR "onenand_read: Attempt read beyond end of device\n"); |
| 720 | *retlen = 0; | 738 | *retlen = 0; |
| 721 | return -EINVAL; | 739 | return -EINVAL; |
| 722 | } | 740 | } |
| @@ -724,8 +742,6 @@ static int onenand_read(struct mtd_info *mtd, loff_t from, size_t len, | |||
| 724 | /* Grab the lock and see if the device is available */ | 742 | /* Grab the lock and see if the device is available */ |
| 725 | onenand_get_device(mtd, FL_READING); | 743 | onenand_get_device(mtd, FL_READING); |
| 726 | 744 | ||
| 727 | /* TODO handling oob */ | ||
| 728 | |||
| 729 | stats = mtd->ecc_stats; | 745 | stats = mtd->ecc_stats; |
| 730 | 746 | ||
| 731 | /* Read-while-load method */ | 747 | /* Read-while-load method */ |
| @@ -754,9 +770,9 @@ static int onenand_read(struct mtd_info *mtd, loff_t from, size_t len, | |||
| 754 | * Now we issued chip 1 read and pointed chip 1 | 770 | * Now we issued chip 1 read and pointed chip 1 |
| 755 | * bufferam so we have to point chip 0 bufferam. | 771 | * bufferam so we have to point chip 0 bufferam. |
| 756 | */ | 772 | */ |
| 757 | if (this->device_id & ONENAND_DEVICE_IS_DDP && | 773 | if (ONENAND_IS_DDP(this) && |
| 758 | unlikely(from == (this->chipsize >> 1))) { | 774 | unlikely(from == (this->chipsize >> 1))) { |
| 759 | this->write_word(0, this->base + ONENAND_REG_START_ADDRESS2); | 775 | this->write_word(ONENAND_DDP_CHIP0, this->base + ONENAND_REG_START_ADDRESS2); |
| 760 | boundary = 1; | 776 | boundary = 1; |
| 761 | } else | 777 | } else |
| 762 | boundary = 0; | 778 | boundary = 0; |
| @@ -770,7 +786,7 @@ static int onenand_read(struct mtd_info *mtd, loff_t from, size_t len, | |||
| 770 | break; | 786 | break; |
| 771 | /* Set up for next read from bufferRAM */ | 787 | /* Set up for next read from bufferRAM */ |
| 772 | if (unlikely(boundary)) | 788 | if (unlikely(boundary)) |
| 773 | this->write_word(0x8000, this->base + ONENAND_REG_START_ADDRESS2); | 789 | this->write_word(ONENAND_DDP_CHIP1, this->base + ONENAND_REG_START_ADDRESS2); |
| 774 | ONENAND_SET_NEXT_BUFFERRAM(this); | 790 | ONENAND_SET_NEXT_BUFFERRAM(this); |
| 775 | buf += thislen; | 791 | buf += thislen; |
| 776 | thislen = min_t(int, mtd->writesize, len - read); | 792 | thislen = min_t(int, mtd->writesize, len - read); |
| @@ -801,20 +817,59 @@ static int onenand_read(struct mtd_info *mtd, loff_t from, size_t len, | |||
| 801 | } | 817 | } |
| 802 | 818 | ||
| 803 | /** | 819 | /** |
| 820 | * onenand_transfer_auto_oob - [Internal] oob auto-placement transfer | ||
| 821 | * @param mtd MTD device structure | ||
| 822 | * @param buf destination address | ||
| 823 | * @param column oob offset to read from | ||
| 824 | * @param thislen oob length to read | ||
| 825 | */ | ||
| 826 | static int onenand_transfer_auto_oob(struct mtd_info *mtd, uint8_t *buf, int column, | ||
| 827 | int thislen) | ||
| 828 | { | ||
| 829 | struct onenand_chip *this = mtd->priv; | ||
| 830 | struct nand_oobfree *free; | ||
| 831 | int readcol = column; | ||
| 832 | int readend = column + thislen; | ||
| 833 | int lastgap = 0; | ||
| 834 | uint8_t *oob_buf = this->page_buf + mtd->writesize; | ||
| 835 | |||
| 836 | for (free = this->ecclayout->oobfree; free->length; ++free) { | ||
| 837 | if (readcol >= lastgap) | ||
| 838 | readcol += free->offset - lastgap; | ||
| 839 | if (readend >= lastgap) | ||
| 840 | readend += free->offset - lastgap; | ||
| 841 | lastgap = free->offset + free->length; | ||
| 842 | } | ||
| 843 | this->read_bufferram(mtd, ONENAND_SPARERAM, oob_buf, 0, mtd->oobsize); | ||
| 844 | for (free = this->ecclayout->oobfree; free->length; ++free) { | ||
| 845 | int free_end = free->offset + free->length; | ||
| 846 | if (free->offset < readend && free_end > readcol) { | ||
| 847 | int st = max_t(int,free->offset,readcol); | ||
| 848 | int ed = min_t(int,free_end,readend); | ||
| 849 | int n = ed - st; | ||
| 850 | memcpy(buf, oob_buf + st, n); | ||
| 851 | buf += n; | ||
| 852 | } | ||
| 853 | } | ||
| 854 | return 0; | ||
| 855 | } | ||
| 856 | |||
| 857 | /** | ||
| 804 | * onenand_do_read_oob - [MTD Interface] OneNAND read out-of-band | 858 | * onenand_do_read_oob - [MTD Interface] OneNAND read out-of-band |
| 805 | * @param mtd MTD device structure | 859 | * @param mtd MTD device structure |
| 806 | * @param from offset to read from | 860 | * @param from offset to read from |
| 807 | * @param len number of bytes to read | 861 | * @param len number of bytes to read |
| 808 | * @param retlen pointer to variable to store the number of read bytes | 862 | * @param retlen pointer to variable to store the number of read bytes |
| 809 | * @param buf the databuffer to put data | 863 | * @param buf the databuffer to put data |
| 864 | * @param mode operation mode | ||
| 810 | * | 865 | * |
| 811 | * OneNAND read out-of-band data from the spare area | 866 | * OneNAND read out-of-band data from the spare area |
| 812 | */ | 867 | */ |
| 813 | int onenand_do_read_oob(struct mtd_info *mtd, loff_t from, size_t len, | 868 | static int onenand_do_read_oob(struct mtd_info *mtd, loff_t from, size_t len, |
| 814 | size_t *retlen, u_char *buf) | 869 | size_t *retlen, u_char *buf, mtd_oob_mode_t mode) |
| 815 | { | 870 | { |
| 816 | struct onenand_chip *this = mtd->priv; | 871 | struct onenand_chip *this = mtd->priv; |
| 817 | int read = 0, thislen, column; | 872 | int read = 0, thislen, column, oobsize; |
| 818 | int ret = 0; | 873 | int ret = 0; |
| 819 | 874 | ||
| 820 | DEBUG(MTD_DEBUG_LEVEL3, "onenand_read_oob: from = 0x%08x, len = %i\n", (unsigned int) from, (int) len); | 875 | 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, | |||
| 822 | /* Initialize return length value */ | 877 | /* Initialize return length value */ |
| 823 | *retlen = 0; | 878 | *retlen = 0; |
| 824 | 879 | ||
| 880 | if (mode == MTD_OOB_AUTO) | ||
| 881 | oobsize = this->ecclayout->oobavail; | ||
| 882 | else | ||
| 883 | oobsize = mtd->oobsize; | ||
| 884 | |||
| 885 | column = from & (mtd->oobsize - 1); | ||
| 886 | |||
| 887 | if (unlikely(column >= oobsize)) { | ||
| 888 | printk(KERN_ERR "onenand_read_oob: Attempted to start read outside oob\n"); | ||
| 889 | return -EINVAL; | ||
| 890 | } | ||
| 891 | |||
| 825 | /* Do not allow reads past end of device */ | 892 | /* Do not allow reads past end of device */ |
| 826 | if (unlikely((from + len) > mtd->size)) { | 893 | if (unlikely(from >= mtd->size || |
| 827 | DEBUG(MTD_DEBUG_LEVEL0, "onenand_read_oob: Attempt read beyond end of device\n"); | 894 | column + len > ((mtd->size >> this->page_shift) - |
| 895 | (from >> this->page_shift)) * oobsize)) { | ||
| 896 | printk(KERN_ERR "onenand_read_oob: Attempted to read beyond end of device\n"); | ||
| 828 | return -EINVAL; | 897 | return -EINVAL; |
| 829 | } | 898 | } |
| 830 | 899 | ||
| 831 | /* Grab the lock and see if the device is available */ | 900 | /* Grab the lock and see if the device is available */ |
| 832 | onenand_get_device(mtd, FL_READING); | 901 | onenand_get_device(mtd, FL_READING); |
| 833 | 902 | ||
| 834 | column = from & (mtd->oobsize - 1); | ||
| 835 | |||
| 836 | while (read < len) { | 903 | while (read < len) { |
| 837 | cond_resched(); | 904 | cond_resched(); |
| 838 | 905 | ||
| 839 | thislen = mtd->oobsize - column; | 906 | thislen = oobsize - column; |
| 840 | thislen = min_t(int, thislen, len); | 907 | thislen = min_t(int, thislen, len); |
| 841 | 908 | ||
| 842 | this->command(mtd, ONENAND_CMD_READOOB, from, mtd->oobsize); | 909 | 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, | |||
| 846 | ret = this->wait(mtd, FL_READING); | 913 | ret = this->wait(mtd, FL_READING); |
| 847 | /* First copy data and check return value for ECC handling */ | 914 | /* First copy data and check return value for ECC handling */ |
| 848 | 915 | ||
| 849 | this->read_bufferram(mtd, ONENAND_SPARERAM, buf, column, thislen); | 916 | if (mode == MTD_OOB_AUTO) |
| 917 | onenand_transfer_auto_oob(mtd, buf, column, thislen); | ||
| 918 | else | ||
| 919 | this->read_bufferram(mtd, ONENAND_SPARERAM, buf, column, thislen); | ||
| 850 | 920 | ||
| 851 | if (ret) { | 921 | if (ret) { |
| 852 | DEBUG(MTD_DEBUG_LEVEL0, "onenand_read_oob: read failed = 0x%x\n", ret); | 922 | printk(KERN_ERR "onenand_read_oob: read failed = 0x%x\n", ret); |
| 853 | goto out; | 923 | break; |
| 854 | } | 924 | } |
| 855 | 925 | ||
| 856 | read += thislen; | 926 | read += thislen; |
| @@ -868,7 +938,6 @@ int onenand_do_read_oob(struct mtd_info *mtd, loff_t from, size_t len, | |||
| 868 | } | 938 | } |
| 869 | } | 939 | } |
| 870 | 940 | ||
| 871 | out: | ||
| 872 | /* Deselect and wake up anyone waiting on the device */ | 941 | /* Deselect and wake up anyone waiting on the device */ |
| 873 | onenand_release_device(mtd); | 942 | onenand_release_device(mtd); |
| 874 | 943 | ||
| @@ -885,10 +954,132 @@ out: | |||
| 885 | static int onenand_read_oob(struct mtd_info *mtd, loff_t from, | 954 | static int onenand_read_oob(struct mtd_info *mtd, loff_t from, |
| 886 | struct mtd_oob_ops *ops) | 955 | struct mtd_oob_ops *ops) |
| 887 | { | 956 | { |
| 888 | BUG_ON(ops->mode != MTD_OOB_PLACE); | 957 | switch (ops->mode) { |
| 889 | 958 | case MTD_OOB_PLACE: | |
| 959 | case MTD_OOB_AUTO: | ||
| 960 | break; | ||
| 961 | case MTD_OOB_RAW: | ||
| 962 | /* Not implemented yet */ | ||
| 963 | default: | ||
| 964 | return -EINVAL; | ||
| 965 | } | ||
| 890 | return onenand_do_read_oob(mtd, from + ops->ooboffs, ops->ooblen, | 966 | return onenand_do_read_oob(mtd, from + ops->ooboffs, ops->ooblen, |
| 891 | &ops->oobretlen, ops->oobbuf); | 967 | &ops->oobretlen, ops->oobbuf, ops->mode); |
| 968 | } | ||
| 969 | |||
| 970 | /** | ||
| 971 | * onenand_bbt_wait - [DEFAULT] wait until the command is done | ||
| 972 | * @param mtd MTD device structure | ||
| 973 | * @param state state to select the max. timeout value | ||
| 974 | * | ||
| 975 | * Wait for command done. | ||
| 976 | */ | ||
| 977 | static int onenand_bbt_wait(struct mtd_info *mtd, int state) | ||
| 978 | { | ||
| 979 | struct onenand_chip *this = mtd->priv; | ||
| 980 | unsigned long timeout; | ||
| 981 | unsigned int interrupt; | ||
| 982 | unsigned int ctrl; | ||
| 983 | |||
| 984 | /* The 20 msec is enough */ | ||
| 985 | timeout = jiffies + msecs_to_jiffies(20); | ||
| 986 | while (time_before(jiffies, timeout)) { | ||
| 987 | interrupt = this->read_word(this->base + ONENAND_REG_INTERRUPT); | ||
| 988 | if (interrupt & ONENAND_INT_MASTER) | ||
| 989 | break; | ||
| 990 | } | ||
| 991 | /* To get correct interrupt status in timeout case */ | ||
| 992 | interrupt = this->read_word(this->base + ONENAND_REG_INTERRUPT); | ||
| 993 | ctrl = this->read_word(this->base + ONENAND_REG_CTRL_STATUS); | ||
| 994 | |||
| 995 | if (ctrl & ONENAND_CTRL_ERROR) { | ||
| 996 | printk(KERN_DEBUG "onenand_bbt_wait: controller error = 0x%04x\n", ctrl); | ||
| 997 | /* Initial bad block case */ | ||
| 998 | if (ctrl & ONENAND_CTRL_LOAD) | ||
| 999 | return ONENAND_BBT_READ_ERROR; | ||
| 1000 | return ONENAND_BBT_READ_FATAL_ERROR; | ||
| 1001 | } | ||
| 1002 | |||
| 1003 | if (interrupt & ONENAND_INT_READ) { | ||
| 1004 | int ecc = this->read_word(this->base + ONENAND_REG_ECC_STATUS); | ||
| 1005 | if (ecc & ONENAND_ECC_2BIT_ALL) | ||
| 1006 | return ONENAND_BBT_READ_ERROR; | ||
| 1007 | } else { | ||
| 1008 | printk(KERN_ERR "onenand_bbt_wait: read timeout!" | ||
| 1009 | "ctrl=0x%04x intr=0x%04x\n", ctrl, interrupt); | ||
| 1010 | return ONENAND_BBT_READ_FATAL_ERROR; | ||
| 1011 | } | ||
| 1012 | |||
| 1013 | return 0; | ||
| 1014 | } | ||
| 1015 | |||
| 1016 | /** | ||
| 1017 | * onenand_bbt_read_oob - [MTD Interface] OneNAND read out-of-band for bbt scan | ||
| 1018 | * @param mtd MTD device structure | ||
| 1019 | * @param from offset to read from | ||
| 1020 | * @param @ops oob operation description structure | ||
| 1021 | * | ||
| 1022 | * OneNAND read out-of-band data from the spare area for bbt scan | ||
| 1023 | */ | ||
| 1024 | int onenand_bbt_read_oob(struct mtd_info *mtd, loff_t from, | ||
| 1025 | struct mtd_oob_ops *ops) | ||
| 1026 | { | ||
| 1027 | struct onenand_chip *this = mtd->priv; | ||
| 1028 | int read = 0, thislen, column; | ||
| 1029 | int ret = 0; | ||
| 1030 | size_t len = ops->ooblen; | ||
| 1031 | u_char *buf = ops->oobbuf; | ||
| 1032 | |||
| 1033 | DEBUG(MTD_DEBUG_LEVEL3, "onenand_bbt_read_oob: from = 0x%08x, len = %zi\n", (unsigned int) from, len); | ||
| 1034 | |||
| 1035 | /* Initialize return value */ | ||
| 1036 | ops->oobretlen = 0; | ||
| 1037 | |||
| 1038 | /* Do not allow reads past end of device */ | ||
| 1039 | if (unlikely((from + len) > mtd->size)) { | ||
| 1040 | printk(KERN_ERR "onenand_bbt_read_oob: Attempt read beyond end of device\n"); | ||
| 1041 | return ONENAND_BBT_READ_FATAL_ERROR; | ||
| 1042 | } | ||
| 1043 | |||
| 1044 | /* Grab the lock and see if the device is available */ | ||
| 1045 | onenand_get_device(mtd, FL_READING); | ||
| 1046 | |||
| 1047 | column = from & (mtd->oobsize - 1); | ||
| 1048 | |||
| 1049 | while (read < len) { | ||
| 1050 | cond_resched(); | ||
| 1051 | |||
| 1052 | thislen = mtd->oobsize - column; | ||
| 1053 | thislen = min_t(int, thislen, len); | ||
| 1054 | |||
| 1055 | this->command(mtd, ONENAND_CMD_READOOB, from, mtd->oobsize); | ||
| 1056 | |||
| 1057 | onenand_update_bufferram(mtd, from, 0); | ||
| 1058 | |||
| 1059 | ret = onenand_bbt_wait(mtd, FL_READING); | ||
| 1060 | if (ret) | ||
| 1061 | break; | ||
| 1062 | |||
| 1063 | this->read_bufferram(mtd, ONENAND_SPARERAM, buf, column, thislen); | ||
| 1064 | read += thislen; | ||
| 1065 | if (read == len) | ||
| 1066 | break; | ||
| 1067 | |||
| 1068 | buf += thislen; | ||
| 1069 | |||
| 1070 | /* Read more? */ | ||
| 1071 | if (read < len) { | ||
| 1072 | /* Update Page size */ | ||
| 1073 | from += mtd->writesize; | ||
| 1074 | column = 0; | ||
| 1075 | } | ||
| 1076 | } | ||
| 1077 | |||
| 1078 | /* Deselect and wake up anyone waiting on the device */ | ||
| 1079 | onenand_release_device(mtd); | ||
| 1080 | |||
| 1081 | ops->oobretlen = read; | ||
| 1082 | return ret; | ||
| 892 | } | 1083 | } |
| 893 | 1084 | ||
| 894 | #ifdef CONFIG_MTD_ONENAND_VERIFY_WRITE | 1085 | #ifdef CONFIG_MTD_ONENAND_VERIFY_WRITE |
| @@ -897,14 +1088,12 @@ static int onenand_read_oob(struct mtd_info *mtd, loff_t from, | |||
| 897 | * @param mtd MTD device structure | 1088 | * @param mtd MTD device structure |
| 898 | * @param buf the databuffer to verify | 1089 | * @param buf the databuffer to verify |
| 899 | * @param to offset to read from | 1090 | * @param to offset to read from |
| 900 | * @param len number of bytes to read and compare | ||
| 901 | * | 1091 | * |
| 902 | */ | 1092 | */ |
| 903 | static int onenand_verify_oob(struct mtd_info *mtd, const u_char *buf, loff_t to, int len) | 1093 | static int onenand_verify_oob(struct mtd_info *mtd, const u_char *buf, loff_t to) |
| 904 | { | 1094 | { |
| 905 | struct onenand_chip *this = mtd->priv; | 1095 | struct onenand_chip *this = mtd->priv; |
| 906 | char *readp = this->page_buf; | 1096 | char *readp = this->page_buf + mtd->writesize; |
| 907 | int column = to & (mtd->oobsize - 1); | ||
| 908 | int status, i; | 1097 | int status, i; |
| 909 | 1098 | ||
| 910 | this->command(mtd, ONENAND_CMD_READOOB, to, mtd->oobsize); | 1099 | 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 | |||
| 913 | if (status) | 1102 | if (status) |
| 914 | return status; | 1103 | return status; |
| 915 | 1104 | ||
| 916 | this->read_bufferram(mtd, ONENAND_SPARERAM, readp, column, len); | 1105 | this->read_bufferram(mtd, ONENAND_SPARERAM, readp, 0, mtd->oobsize); |
| 917 | 1106 | for(i = 0; i < mtd->oobsize; i++) | |
| 918 | for(i = 0; i < len; i++) | ||
| 919 | if (buf[i] != 0xFF && buf[i] != readp[i]) | 1107 | if (buf[i] != 0xFF && buf[i] != readp[i]) |
| 920 | return -EBADMSG; | 1108 | return -EBADMSG; |
| 921 | 1109 | ||
| @@ -923,41 +1111,51 @@ static int onenand_verify_oob(struct mtd_info *mtd, const u_char *buf, loff_t to | |||
| 923 | } | 1111 | } |
| 924 | 1112 | ||
| 925 | /** | 1113 | /** |
| 926 | * onenand_verify_page - [GENERIC] verify the chip contents after a write | 1114 | * onenand_verify - [GENERIC] verify the chip contents after a write |
| 927 | * @param mtd MTD device structure | 1115 | * @param mtd MTD device structure |
| 928 | * @param buf the databuffer to verify | 1116 | * @param buf the databuffer to verify |
| 1117 | * @param addr offset to read from | ||
| 1118 | * @param len number of bytes to read and compare | ||
| 929 | * | 1119 | * |
| 930 | * Check DataRAM area directly | ||
| 931 | */ | 1120 | */ |
| 932 | static int onenand_verify_page(struct mtd_info *mtd, u_char *buf, loff_t addr) | 1121 | static int onenand_verify(struct mtd_info *mtd, const u_char *buf, loff_t addr, size_t len) |
| 933 | { | 1122 | { |
| 934 | struct onenand_chip *this = mtd->priv; | 1123 | struct onenand_chip *this = mtd->priv; |
| 935 | void __iomem *dataram0, *dataram1; | 1124 | void __iomem *dataram; |
| 936 | int ret = 0; | 1125 | int ret = 0; |
| 1126 | int thislen, column; | ||
| 937 | 1127 | ||
| 938 | /* In partial page write, just skip it */ | 1128 | while (len != 0) { |
| 939 | if ((addr & (mtd->writesize - 1)) != 0) | 1129 | thislen = min_t(int, mtd->writesize, len); |
| 940 | return 0; | 1130 | column = addr & (mtd->writesize - 1); |
| 1131 | if (column + thislen > mtd->writesize) | ||
| 1132 | thislen = mtd->writesize - column; | ||
| 941 | 1133 | ||
| 942 | this->command(mtd, ONENAND_CMD_READ, addr, mtd->writesize); | 1134 | this->command(mtd, ONENAND_CMD_READ, addr, mtd->writesize); |
| 943 | 1135 | ||
| 944 | ret = this->wait(mtd, FL_READING); | 1136 | onenand_update_bufferram(mtd, addr, 0); |
| 945 | if (ret) | 1137 | |
| 946 | return ret; | 1138 | ret = this->wait(mtd, FL_READING); |
| 1139 | if (ret) | ||
| 1140 | return ret; | ||
| 947 | 1141 | ||
| 948 | onenand_update_bufferram(mtd, addr, 1); | 1142 | onenand_update_bufferram(mtd, addr, 1); |
| 949 | 1143 | ||
| 950 | /* Check, if the two dataram areas are same */ | 1144 | dataram = this->base + ONENAND_DATARAM; |
| 951 | dataram0 = this->base + ONENAND_DATARAM; | 1145 | dataram += onenand_bufferram_offset(mtd, ONENAND_DATARAM); |
| 952 | dataram1 = dataram0 + mtd->writesize; | ||
| 953 | 1146 | ||
| 954 | if (memcmp(dataram0, dataram1, mtd->writesize)) | 1147 | if (memcmp(buf, dataram + column, thislen)) |
| 955 | return -EBADMSG; | 1148 | return -EBADMSG; |
| 1149 | |||
| 1150 | len -= thislen; | ||
| 1151 | buf += thislen; | ||
| 1152 | addr += thislen; | ||
| 1153 | } | ||
| 956 | 1154 | ||
| 957 | return 0; | 1155 | return 0; |
| 958 | } | 1156 | } |
| 959 | #else | 1157 | #else |
| 960 | #define onenand_verify_page(...) (0) | 1158 | #define onenand_verify(...) (0) |
| 961 | #define onenand_verify_oob(...) (0) | 1159 | #define onenand_verify_oob(...) (0) |
| 962 | #endif | 1160 | #endif |
| 963 | 1161 | ||
| @@ -988,60 +1186,57 @@ static int onenand_write(struct mtd_info *mtd, loff_t to, size_t len, | |||
| 988 | 1186 | ||
| 989 | /* Do not allow writes past end of device */ | 1187 | /* Do not allow writes past end of device */ |
| 990 | if (unlikely((to + len) > mtd->size)) { | 1188 | if (unlikely((to + len) > mtd->size)) { |
| 991 | DEBUG(MTD_DEBUG_LEVEL0, "onenand_write: Attempt write to past end of device\n"); | 1189 | printk(KERN_ERR "onenand_write: Attempt write to past end of device\n"); |
| 992 | return -EINVAL; | 1190 | return -EINVAL; |
| 993 | } | 1191 | } |
| 994 | 1192 | ||
| 995 | /* Reject writes, which are not page aligned */ | 1193 | /* Reject writes, which are not page aligned */ |
| 996 | if (unlikely(NOTALIGNED(to)) || unlikely(NOTALIGNED(len))) { | 1194 | if (unlikely(NOTALIGNED(to)) || unlikely(NOTALIGNED(len))) { |
| 997 | DEBUG(MTD_DEBUG_LEVEL0, "onenand_write: Attempt to write not page aligned data\n"); | 1195 | printk(KERN_ERR "onenand_write: Attempt to write not page aligned data\n"); |
| 998 | return -EINVAL; | 1196 | return -EINVAL; |
| 999 | } | 1197 | } |
| 1000 | 1198 | ||
| 1001 | column = to & (mtd->writesize - 1); | 1199 | column = to & (mtd->writesize - 1); |
| 1002 | subpage = column || (len & (mtd->writesize - 1)); | ||
| 1003 | 1200 | ||
| 1004 | /* Grab the lock and see if the device is available */ | 1201 | /* Grab the lock and see if the device is available */ |
| 1005 | onenand_get_device(mtd, FL_WRITING); | 1202 | onenand_get_device(mtd, FL_WRITING); |
| 1006 | 1203 | ||
| 1007 | /* Loop until all data write */ | 1204 | /* Loop until all data write */ |
| 1008 | while (written < len) { | 1205 | while (written < len) { |
| 1009 | int bytes = mtd->writesize; | 1206 | int thislen = min_t(int, mtd->writesize - column, len - written); |
| 1010 | int thislen = min_t(int, bytes, len - written); | ||
| 1011 | u_char *wbuf = (u_char *) buf; | 1207 | u_char *wbuf = (u_char *) buf; |
| 1012 | 1208 | ||
| 1013 | cond_resched(); | 1209 | cond_resched(); |
| 1014 | 1210 | ||
| 1015 | this->command(mtd, ONENAND_CMD_BUFFERRAM, to, bytes); | 1211 | this->command(mtd, ONENAND_CMD_BUFFERRAM, to, thislen); |
| 1016 | 1212 | ||
| 1017 | /* Partial page write */ | 1213 | /* Partial page write */ |
| 1214 | subpage = thislen < mtd->writesize; | ||
| 1018 | if (subpage) { | 1215 | if (subpage) { |
| 1019 | bytes = min_t(int, bytes - column, (int) len); | ||
| 1020 | memset(this->page_buf, 0xff, mtd->writesize); | 1216 | memset(this->page_buf, 0xff, mtd->writesize); |
| 1021 | memcpy(this->page_buf + column, buf, bytes); | 1217 | memcpy(this->page_buf + column, buf, thislen); |
| 1022 | wbuf = this->page_buf; | 1218 | wbuf = this->page_buf; |
| 1023 | /* Even though partial write, we need page size */ | ||
| 1024 | thislen = mtd->writesize; | ||
| 1025 | } | 1219 | } |
| 1026 | 1220 | ||
| 1027 | this->write_bufferram(mtd, ONENAND_DATARAM, wbuf, 0, thislen); | 1221 | this->write_bufferram(mtd, ONENAND_DATARAM, wbuf, 0, mtd->writesize); |
| 1028 | this->write_bufferram(mtd, ONENAND_SPARERAM, ffchars, 0, mtd->oobsize); | 1222 | this->write_bufferram(mtd, ONENAND_SPARERAM, ffchars, 0, mtd->oobsize); |
| 1029 | 1223 | ||
| 1030 | this->command(mtd, ONENAND_CMD_PROG, to, mtd->writesize); | 1224 | this->command(mtd, ONENAND_CMD_PROG, to, mtd->writesize); |
| 1031 | 1225 | ||
| 1226 | ret = this->wait(mtd, FL_WRITING); | ||
| 1227 | |||
| 1032 | /* In partial page write we don't update bufferram */ | 1228 | /* In partial page write we don't update bufferram */ |
| 1033 | onenand_update_bufferram(mtd, to, !subpage); | 1229 | onenand_update_bufferram(mtd, to, !ret && !subpage); |
| 1034 | 1230 | ||
| 1035 | ret = this->wait(mtd, FL_WRITING); | ||
| 1036 | if (ret) { | 1231 | if (ret) { |
| 1037 | DEBUG(MTD_DEBUG_LEVEL0, "onenand_write: write filaed %d\n", ret); | 1232 | printk(KERN_ERR "onenand_write: write filaed %d\n", ret); |
| 1038 | break; | 1233 | break; |
| 1039 | } | 1234 | } |
| 1040 | 1235 | ||
| 1041 | /* Only check verify write turn on */ | 1236 | /* Only check verify write turn on */ |
| 1042 | ret = onenand_verify_page(mtd, (u_char *) wbuf, to); | 1237 | ret = onenand_verify(mtd, (u_char *) wbuf, to, thislen); |
| 1043 | if (ret) { | 1238 | if (ret) { |
| 1044 | DEBUG(MTD_DEBUG_LEVEL0, "onenand_write: verify failed %d\n", ret); | 1239 | printk(KERN_ERR "onenand_write: verify failed %d\n", ret); |
| 1045 | break; | 1240 | break; |
| 1046 | } | 1241 | } |
| 1047 | 1242 | ||
| @@ -1064,20 +1259,58 @@ static int onenand_write(struct mtd_info *mtd, loff_t to, size_t len, | |||
| 1064 | } | 1259 | } |
| 1065 | 1260 | ||
| 1066 | /** | 1261 | /** |
| 1262 | * onenand_fill_auto_oob - [Internal] oob auto-placement transfer | ||
| 1263 | * @param mtd MTD device structure | ||
| 1264 | * @param oob_buf oob buffer | ||
| 1265 | * @param buf source address | ||
| 1266 | * @param column oob offset to write to | ||
| 1267 | * @param thislen oob length to write | ||
| 1268 | */ | ||
| 1269 | static int onenand_fill_auto_oob(struct mtd_info *mtd, u_char *oob_buf, | ||
| 1270 | const u_char *buf, int column, int thislen) | ||
| 1271 | { | ||
| 1272 | struct onenand_chip *this = mtd->priv; | ||
| 1273 | struct nand_oobfree *free; | ||
| 1274 | int writecol = column; | ||
| 1275 | int writeend = column + thislen; | ||
| 1276 | int lastgap = 0; | ||
| 1277 | |||
| 1278 | for (free = this->ecclayout->oobfree; free->length; ++free) { | ||
| 1279 | if (writecol >= lastgap) | ||
| 1280 | writecol += free->offset - lastgap; | ||
| 1281 | if (writeend >= lastgap) | ||
| 1282 | writeend += free->offset - lastgap; | ||
| 1283 | lastgap = free->offset + free->length; | ||
| 1284 | } | ||
| 1285 | for (free = this->ecclayout->oobfree; free->length; ++free) { | ||
| 1286 | int free_end = free->offset + free->length; | ||
| 1287 | if (free->offset < writeend && free_end > writecol) { | ||
| 1288 | int st = max_t(int,free->offset,writecol); | ||
| 1289 | int ed = min_t(int,free_end,writeend); | ||
| 1290 | int n = ed - st; | ||
| 1291 | memcpy(oob_buf + st, buf, n); | ||
| 1292 | buf += n; | ||
| 1293 | } | ||
| 1294 | } | ||
| 1295 | return 0; | ||
| 1296 | } | ||
| 1297 | |||
| 1298 | /** | ||
| 1067 | * onenand_do_write_oob - [Internal] OneNAND write out-of-band | 1299 | * onenand_do_write_oob - [Internal] OneNAND write out-of-band |
| 1068 | * @param mtd MTD device structure | 1300 | * @param mtd MTD device structure |
| 1069 | * @param to offset to write to | 1301 | * @param to offset to write to |
| 1070 | * @param len number of bytes to write | 1302 | * @param len number of bytes to write |
| 1071 | * @param retlen pointer to variable to store the number of written bytes | 1303 | * @param retlen pointer to variable to store the number of written bytes |
| 1072 | * @param buf the data to write | 1304 | * @param buf the data to write |
| 1305 | * @param mode operation mode | ||
| 1073 | * | 1306 | * |
| 1074 | * OneNAND write out-of-band | 1307 | * OneNAND write out-of-band |
| 1075 | */ | 1308 | */ |
| 1076 | static int onenand_do_write_oob(struct mtd_info *mtd, loff_t to, size_t len, | 1309 | static int onenand_do_write_oob(struct mtd_info *mtd, loff_t to, size_t len, |
| 1077 | size_t *retlen, const u_char *buf) | 1310 | size_t *retlen, const u_char *buf, mtd_oob_mode_t mode) |
| 1078 | { | 1311 | { |
| 1079 | struct onenand_chip *this = mtd->priv; | 1312 | struct onenand_chip *this = mtd->priv; |
| 1080 | int column, ret = 0; | 1313 | int column, ret = 0, oobsize; |
| 1081 | int written = 0; | 1314 | int written = 0; |
| 1082 | 1315 | ||
| 1083 | DEBUG(MTD_DEBUG_LEVEL3, "onenand_write_oob: to = 0x%08x, len = %i\n", (unsigned int) to, (int) len); | 1316 | 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, | |||
| 1085 | /* Initialize retlen, in case of early exit */ | 1318 | /* Initialize retlen, in case of early exit */ |
| 1086 | *retlen = 0; | 1319 | *retlen = 0; |
| 1087 | 1320 | ||
| 1088 | /* Do not allow writes past end of device */ | 1321 | if (mode == MTD_OOB_AUTO) |
| 1089 | if (unlikely((to + len) > mtd->size)) { | 1322 | oobsize = this->ecclayout->oobavail; |
| 1090 | DEBUG(MTD_DEBUG_LEVEL0, "onenand_write_oob: Attempt write to past end of device\n"); | 1323 | else |
| 1324 | oobsize = mtd->oobsize; | ||
| 1325 | |||
| 1326 | column = to & (mtd->oobsize - 1); | ||
| 1327 | |||
| 1328 | if (unlikely(column >= oobsize)) { | ||
| 1329 | printk(KERN_ERR "onenand_write_oob: Attempted to start write outside oob\n"); | ||
| 1330 | return -EINVAL; | ||
| 1331 | } | ||
| 1332 | |||
| 1333 | /* For compatibility with NAND: Do not allow write past end of page */ | ||
| 1334 | if (column + len > oobsize) { | ||
| 1335 | printk(KERN_ERR "onenand_write_oob: " | ||
| 1336 | "Attempt to write past end of page\n"); | ||
| 1337 | return -EINVAL; | ||
| 1338 | } | ||
| 1339 | |||
| 1340 | /* Do not allow reads past end of device */ | ||
| 1341 | if (unlikely(to >= mtd->size || | ||
| 1342 | column + len > ((mtd->size >> this->page_shift) - | ||
| 1343 | (to >> this->page_shift)) * oobsize)) { | ||
| 1344 | printk(KERN_ERR "onenand_write_oob: Attempted to write past end of device\n"); | ||
| 1091 | return -EINVAL; | 1345 | return -EINVAL; |
| 1092 | } | 1346 | } |
| 1093 | 1347 | ||
| @@ -1096,18 +1350,19 @@ static int onenand_do_write_oob(struct mtd_info *mtd, loff_t to, size_t len, | |||
| 1096 | 1350 | ||
| 1097 | /* Loop until all data write */ | 1351 | /* Loop until all data write */ |
| 1098 | while (written < len) { | 1352 | while (written < len) { |
| 1099 | int thislen = min_t(int, mtd->oobsize, len - written); | 1353 | int thislen = min_t(int, oobsize, len - written); |
| 1100 | 1354 | ||
| 1101 | cond_resched(); | 1355 | cond_resched(); |
| 1102 | 1356 | ||
| 1103 | column = to & (mtd->oobsize - 1); | ||
| 1104 | |||
| 1105 | this->command(mtd, ONENAND_CMD_BUFFERRAM, to, mtd->oobsize); | 1357 | this->command(mtd, ONENAND_CMD_BUFFERRAM, to, mtd->oobsize); |
| 1106 | 1358 | ||
| 1107 | /* We send data to spare ram with oobsize | 1359 | /* We send data to spare ram with oobsize |
| 1108 | * to prevent byte access */ | 1360 | * to prevent byte access */ |
| 1109 | memset(this->page_buf, 0xff, mtd->oobsize); | 1361 | memset(this->page_buf, 0xff, mtd->oobsize); |
| 1110 | memcpy(this->page_buf + column, buf, thislen); | 1362 | if (mode == MTD_OOB_AUTO) |
| 1363 | onenand_fill_auto_oob(mtd, this->page_buf, buf, column, thislen); | ||
| 1364 | else | ||
| 1365 | memcpy(this->page_buf + column, buf, thislen); | ||
| 1111 | this->write_bufferram(mtd, ONENAND_SPARERAM, this->page_buf, 0, mtd->oobsize); | 1366 | this->write_bufferram(mtd, ONENAND_SPARERAM, this->page_buf, 0, mtd->oobsize); |
| 1112 | 1367 | ||
| 1113 | this->command(mtd, ONENAND_CMD_PROGOOB, to, mtd->oobsize); | 1368 | 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, | |||
| 1116 | 1371 | ||
| 1117 | ret = this->wait(mtd, FL_WRITING); | 1372 | ret = this->wait(mtd, FL_WRITING); |
| 1118 | if (ret) { | 1373 | if (ret) { |
| 1119 | DEBUG(MTD_DEBUG_LEVEL0, "onenand_write_oob: write filaed %d\n", ret); | 1374 | printk(KERN_ERR "onenand_write_oob: write failed %d\n", ret); |
| 1120 | goto out; | 1375 | break; |
| 1121 | } | 1376 | } |
| 1122 | 1377 | ||
| 1123 | ret = onenand_verify_oob(mtd, buf, to, thislen); | 1378 | ret = onenand_verify_oob(mtd, this->page_buf, to); |
| 1124 | if (ret) { | 1379 | if (ret) { |
| 1125 | DEBUG(MTD_DEBUG_LEVEL0, "onenand_write_oob: verify failed %d\n", ret); | 1380 | printk(KERN_ERR "onenand_write_oob: verify failed %d\n", ret); |
| 1126 | goto out; | 1381 | break; |
| 1127 | } | 1382 | } |
| 1128 | 1383 | ||
| 1129 | written += thislen; | 1384 | written += thislen; |
| 1130 | |||
| 1131 | if (written == len) | 1385 | if (written == len) |
| 1132 | break; | 1386 | break; |
| 1133 | 1387 | ||
| 1134 | to += thislen; | 1388 | to += mtd->writesize; |
| 1135 | buf += thislen; | 1389 | buf += thislen; |
| 1390 | column = 0; | ||
| 1136 | } | 1391 | } |
| 1137 | 1392 | ||
| 1138 | out: | ||
| 1139 | /* Deselect and wake up anyone waiting on the device */ | 1393 | /* Deselect and wake up anyone waiting on the device */ |
| 1140 | onenand_release_device(mtd); | 1394 | onenand_release_device(mtd); |
| 1141 | 1395 | ||
| @@ -1153,10 +1407,17 @@ out: | |||
| 1153 | static int onenand_write_oob(struct mtd_info *mtd, loff_t to, | 1407 | static int onenand_write_oob(struct mtd_info *mtd, loff_t to, |
| 1154 | struct mtd_oob_ops *ops) | 1408 | struct mtd_oob_ops *ops) |
| 1155 | { | 1409 | { |
| 1156 | BUG_ON(ops->mode != MTD_OOB_PLACE); | 1410 | switch (ops->mode) { |
| 1157 | 1411 | case MTD_OOB_PLACE: | |
| 1412 | case MTD_OOB_AUTO: | ||
| 1413 | break; | ||
| 1414 | case MTD_OOB_RAW: | ||
| 1415 | /* Not implemented yet */ | ||
| 1416 | default: | ||
| 1417 | return -EINVAL; | ||
| 1418 | } | ||
| 1158 | return onenand_do_write_oob(mtd, to + ops->ooboffs, ops->ooblen, | 1419 | return onenand_do_write_oob(mtd, to + ops->ooboffs, ops->ooblen, |
| 1159 | &ops->oobretlen, ops->oobbuf); | 1420 | &ops->oobretlen, ops->oobbuf, ops->mode); |
| 1160 | } | 1421 | } |
| 1161 | 1422 | ||
| 1162 | /** | 1423 | /** |
| @@ -1199,19 +1460,19 @@ static int onenand_erase(struct mtd_info *mtd, struct erase_info *instr) | |||
| 1199 | 1460 | ||
| 1200 | /* Start address must align on block boundary */ | 1461 | /* Start address must align on block boundary */ |
| 1201 | if (unlikely(instr->addr & (block_size - 1))) { | 1462 | if (unlikely(instr->addr & (block_size - 1))) { |
| 1202 | DEBUG(MTD_DEBUG_LEVEL0, "onenand_erase: Unaligned address\n"); | 1463 | printk(KERN_ERR "onenand_erase: Unaligned address\n"); |
| 1203 | return -EINVAL; | 1464 | return -EINVAL; |
| 1204 | } | 1465 | } |
| 1205 | 1466 | ||
| 1206 | /* Length must align on block boundary */ | 1467 | /* Length must align on block boundary */ |
| 1207 | if (unlikely(instr->len & (block_size - 1))) { | 1468 | if (unlikely(instr->len & (block_size - 1))) { |
| 1208 | DEBUG(MTD_DEBUG_LEVEL0, "onenand_erase: Length not block aligned\n"); | 1469 | printk(KERN_ERR "onenand_erase: Length not block aligned\n"); |
| 1209 | return -EINVAL; | 1470 | return -EINVAL; |
| 1210 | } | 1471 | } |
| 1211 | 1472 | ||
| 1212 | /* Do not allow erase past end of device */ | 1473 | /* Do not allow erase past end of device */ |
| 1213 | if (unlikely((instr->len + instr->addr) > mtd->size)) { | 1474 | if (unlikely((instr->len + instr->addr) > mtd->size)) { |
| 1214 | DEBUG(MTD_DEBUG_LEVEL0, "onenand_erase: Erase past end of device\n"); | 1475 | printk(KERN_ERR "onenand_erase: Erase past end of device\n"); |
| 1215 | return -EINVAL; | 1476 | return -EINVAL; |
| 1216 | } | 1477 | } |
| 1217 | 1478 | ||
| @@ -1238,10 +1499,12 @@ static int onenand_erase(struct mtd_info *mtd, struct erase_info *instr) | |||
| 1238 | 1499 | ||
| 1239 | this->command(mtd, ONENAND_CMD_ERASE, addr, block_size); | 1500 | this->command(mtd, ONENAND_CMD_ERASE, addr, block_size); |
| 1240 | 1501 | ||
| 1502 | onenand_invalidate_bufferram(mtd, addr, block_size); | ||
| 1503 | |||
| 1241 | ret = this->wait(mtd, FL_ERASING); | 1504 | ret = this->wait(mtd, FL_ERASING); |
| 1242 | /* Check, if it is write protected */ | 1505 | /* Check, if it is write protected */ |
| 1243 | if (ret) { | 1506 | if (ret) { |
| 1244 | DEBUG(MTD_DEBUG_LEVEL0, "onenand_erase: Failed erase, block %d\n", (unsigned) (addr >> this->erase_shift)); | 1507 | printk(KERN_ERR "onenand_erase: Failed erase, block %d\n", (unsigned) (addr >> this->erase_shift)); |
| 1245 | instr->state = MTD_ERASE_FAILED; | 1508 | instr->state = MTD_ERASE_FAILED; |
| 1246 | instr->fail_addr = addr; | 1509 | instr->fail_addr = addr; |
| 1247 | goto erase_exit; | 1510 | goto erase_exit; |
| @@ -1322,7 +1585,7 @@ static int onenand_default_block_markbad(struct mtd_info *mtd, loff_t ofs) | |||
| 1322 | 1585 | ||
| 1323 | /* We write two bytes, so we dont have to mess with 16 bit access */ | 1586 | /* We write two bytes, so we dont have to mess with 16 bit access */ |
| 1324 | ofs += mtd->oobsize + (bbm->badblockpos & ~0x01); | 1587 | ofs += mtd->oobsize + (bbm->badblockpos & ~0x01); |
| 1325 | return onenand_do_write_oob(mtd, ofs , 2, &retlen, buf); | 1588 | return onenand_do_write_oob(mtd, ofs , 2, &retlen, buf, MTD_OOB_PLACE); |
| 1326 | } | 1589 | } |
| 1327 | 1590 | ||
| 1328 | /** | 1591 | /** |
| @@ -1491,6 +1754,8 @@ static int onenand_unlock_all(struct mtd_info *mtd) | |||
| 1491 | struct onenand_chip *this = mtd->priv; | 1754 | struct onenand_chip *this = mtd->priv; |
| 1492 | 1755 | ||
| 1493 | if (this->options & ONENAND_HAS_UNLOCK_ALL) { | 1756 | if (this->options & ONENAND_HAS_UNLOCK_ALL) { |
| 1757 | /* Set start block address */ | ||
| 1758 | this->write_word(0, this->base + ONENAND_REG_START_BLOCK_ADDRESS); | ||
| 1494 | /* Write unlock command */ | 1759 | /* Write unlock command */ |
| 1495 | this->command(mtd, ONENAND_CMD_UNLOCK_ALL, 0, 0); | 1760 | this->command(mtd, ONENAND_CMD_UNLOCK_ALL, 0, 0); |
| 1496 | 1761 | ||
| @@ -1503,13 +1768,10 @@ static int onenand_unlock_all(struct mtd_info *mtd) | |||
| 1503 | continue; | 1768 | continue; |
| 1504 | 1769 | ||
| 1505 | /* Workaround for all block unlock in DDP */ | 1770 | /* Workaround for all block unlock in DDP */ |
| 1506 | if (this->device_id & ONENAND_DEVICE_IS_DDP) { | 1771 | if (ONENAND_IS_DDP(this)) { |
| 1507 | loff_t ofs; | ||
| 1508 | size_t len; | ||
| 1509 | |||
| 1510 | /* 1st block on another chip */ | 1772 | /* 1st block on another chip */ |
| 1511 | ofs = this->chipsize >> 1; | 1773 | loff_t ofs = this->chipsize >> 1; |
| 1512 | len = 1 << this->erase_shift; | 1774 | size_t len = mtd->erasesize; |
| 1513 | 1775 | ||
| 1514 | onenand_unlock(mtd, ofs, len); | 1776 | onenand_unlock(mtd, ofs, len); |
| 1515 | } | 1777 | } |
| @@ -1617,7 +1879,7 @@ static int do_otp_lock(struct mtd_info *mtd, loff_t from, size_t len, | |||
| 1617 | this->command(mtd, ONENAND_CMD_OTP_ACCESS, 0, 0); | 1879 | this->command(mtd, ONENAND_CMD_OTP_ACCESS, 0, 0); |
| 1618 | this->wait(mtd, FL_OTPING); | 1880 | this->wait(mtd, FL_OTPING); |
| 1619 | 1881 | ||
| 1620 | ret = onenand_do_write_oob(mtd, from, len, retlen, buf); | 1882 | ret = onenand_do_write_oob(mtd, from, len, retlen, buf, MTD_OOB_PLACE); |
| 1621 | 1883 | ||
| 1622 | /* Exit OTP access mode */ | 1884 | /* Exit OTP access mode */ |
| 1623 | this->command(mtd, ONENAND_CMD_RESET, 0, 0); | 1885 | 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, | |||
| 1823 | #endif /* CONFIG_MTD_ONENAND_OTP */ | 2085 | #endif /* CONFIG_MTD_ONENAND_OTP */ |
| 1824 | 2086 | ||
| 1825 | /** | 2087 | /** |
| 1826 | * onenand_lock_scheme - Check and set OneNAND lock scheme | 2088 | * onenand_check_features - Check and set OneNAND features |
| 1827 | * @param mtd MTD data structure | 2089 | * @param mtd MTD data structure |
| 1828 | * | 2090 | * |
| 1829 | * Check and set OneNAND lock scheme | 2091 | * Check and set OneNAND features |
| 2092 | * - lock scheme | ||
| 1830 | */ | 2093 | */ |
| 1831 | static void onenand_lock_scheme(struct mtd_info *mtd) | 2094 | static void onenand_check_features(struct mtd_info *mtd) |
| 1832 | { | 2095 | { |
| 1833 | struct onenand_chip *this = mtd->priv; | 2096 | struct onenand_chip *this = mtd->priv; |
| 1834 | unsigned int density, process; | 2097 | unsigned int density, process; |
| @@ -1961,26 +2224,28 @@ static int onenand_probe(struct mtd_info *mtd) | |||
| 1961 | density = dev_id >> ONENAND_DEVICE_DENSITY_SHIFT; | 2224 | density = dev_id >> ONENAND_DEVICE_DENSITY_SHIFT; |
| 1962 | this->chipsize = (16 << density) << 20; | 2225 | this->chipsize = (16 << density) << 20; |
| 1963 | /* Set density mask. it is used for DDP */ | 2226 | /* Set density mask. it is used for DDP */ |
| 1964 | this->density_mask = (1 << (density + 6)); | 2227 | if (ONENAND_IS_DDP(this)) |
| 2228 | this->density_mask = (1 << (density + 6)); | ||
| 2229 | else | ||
| 2230 | this->density_mask = 0; | ||
| 1965 | 2231 | ||
| 1966 | /* OneNAND page size & block size */ | 2232 | /* OneNAND page size & block size */ |
| 1967 | /* The data buffer size is equal to page size */ | 2233 | /* The data buffer size is equal to page size */ |
| 1968 | mtd->writesize = this->read_word(this->base + ONENAND_REG_DATA_BUFFER_SIZE); | 2234 | mtd->writesize = this->read_word(this->base + ONENAND_REG_DATA_BUFFER_SIZE); |
| 1969 | mtd->oobsize = mtd->writesize >> 5; | 2235 | mtd->oobsize = mtd->writesize >> 5; |
| 1970 | /* Pagers per block is always 64 in OneNAND */ | 2236 | /* Pages per a block are always 64 in OneNAND */ |
| 1971 | mtd->erasesize = mtd->writesize << 6; | 2237 | mtd->erasesize = mtd->writesize << 6; |
| 1972 | 2238 | ||
| 1973 | this->erase_shift = ffs(mtd->erasesize) - 1; | 2239 | this->erase_shift = ffs(mtd->erasesize) - 1; |
| 1974 | this->page_shift = ffs(mtd->writesize) - 1; | 2240 | this->page_shift = ffs(mtd->writesize) - 1; |
| 1975 | this->ppb_shift = (this->erase_shift - this->page_shift); | 2241 | this->page_mask = (1 << (this->erase_shift - this->page_shift)) - 1; |
| 1976 | this->page_mask = (mtd->erasesize / mtd->writesize) - 1; | ||
| 1977 | 2242 | ||
| 1978 | /* REVIST: Multichip handling */ | 2243 | /* REVIST: Multichip handling */ |
| 1979 | 2244 | ||
| 1980 | mtd->size = this->chipsize; | 2245 | mtd->size = this->chipsize; |
| 1981 | 2246 | ||
| 1982 | /* Check OneNAND lock scheme */ | 2247 | /* Check OneNAND features */ |
| 1983 | onenand_lock_scheme(mtd); | 2248 | onenand_check_features(mtd); |
| 1984 | 2249 | ||
| 1985 | return 0; | 2250 | return 0; |
| 1986 | } | 2251 | } |
| @@ -2021,6 +2286,7 @@ static void onenand_resume(struct mtd_info *mtd) | |||
| 2021 | */ | 2286 | */ |
| 2022 | int onenand_scan(struct mtd_info *mtd, int maxchips) | 2287 | int onenand_scan(struct mtd_info *mtd, int maxchips) |
| 2023 | { | 2288 | { |
| 2289 | int i; | ||
| 2024 | struct onenand_chip *this = mtd->priv; | 2290 | struct onenand_chip *this = mtd->priv; |
| 2025 | 2291 | ||
| 2026 | if (!this->read_word) | 2292 | if (!this->read_word) |
| @@ -2092,12 +2358,21 @@ int onenand_scan(struct mtd_info *mtd, int maxchips) | |||
| 2092 | } | 2358 | } |
| 2093 | 2359 | ||
| 2094 | this->subpagesize = mtd->writesize >> mtd->subpage_sft; | 2360 | this->subpagesize = mtd->writesize >> mtd->subpage_sft; |
| 2361 | |||
| 2362 | /* | ||
| 2363 | * The number of bytes available for a client to place data into | ||
| 2364 | * the out of band area | ||
| 2365 | */ | ||
| 2366 | this->ecclayout->oobavail = 0; | ||
| 2367 | for (i = 0; this->ecclayout->oobfree[i].length; i++) | ||
| 2368 | this->ecclayout->oobavail += | ||
| 2369 | this->ecclayout->oobfree[i].length; | ||
| 2370 | |||
| 2095 | mtd->ecclayout = this->ecclayout; | 2371 | mtd->ecclayout = this->ecclayout; |
| 2096 | 2372 | ||
| 2097 | /* Fill in remaining MTD driver data */ | 2373 | /* Fill in remaining MTD driver data */ |
| 2098 | mtd->type = MTD_NANDFLASH; | 2374 | mtd->type = MTD_NANDFLASH; |
| 2099 | mtd->flags = MTD_CAP_NANDFLASH; | 2375 | mtd->flags = MTD_CAP_NANDFLASH; |
| 2100 | mtd->ecctype = MTD_ECC_SW; | ||
| 2101 | mtd->erase = onenand_erase; | 2376 | mtd->erase = onenand_erase; |
| 2102 | mtd->point = NULL; | 2377 | mtd->point = NULL; |
| 2103 | mtd->unpoint = NULL; | 2378 | mtd->unpoint = NULL; |
| @@ -2144,8 +2419,11 @@ void onenand_release(struct mtd_info *mtd) | |||
| 2144 | del_mtd_device (mtd); | 2419 | del_mtd_device (mtd); |
| 2145 | 2420 | ||
| 2146 | /* Free bad block table memory, if allocated */ | 2421 | /* Free bad block table memory, if allocated */ |
| 2147 | if (this->bbm) | 2422 | if (this->bbm) { |
| 2423 | struct bbm_info *bbm = this->bbm; | ||
| 2424 | kfree(bbm->bbt); | ||
| 2148 | kfree(this->bbm); | 2425 | kfree(this->bbm); |
| 2426 | } | ||
| 2149 | /* Buffer allocated by onenand_scan */ | 2427 | /* Buffer allocated by onenand_scan */ |
| 2150 | if (this->options & ONENAND_PAGEBUF_ALLOC) | 2428 | if (this->options & ONENAND_PAGEBUF_ALLOC) |
| 2151 | kfree(this->page_buf); | 2429 | kfree(this->page_buf); |
diff --git a/drivers/mtd/onenand/onenand_bbt.c b/drivers/mtd/onenand/onenand_bbt.c index 98f8fd1c6375..aecdd50a1781 100644 --- a/drivers/mtd/onenand/onenand_bbt.c +++ b/drivers/mtd/onenand/onenand_bbt.c | |||
| @@ -17,8 +17,8 @@ | |||
| 17 | #include <linux/mtd/onenand.h> | 17 | #include <linux/mtd/onenand.h> |
| 18 | #include <linux/mtd/compatmac.h> | 18 | #include <linux/mtd/compatmac.h> |
| 19 | 19 | ||
| 20 | extern int onenand_do_read_oob(struct mtd_info *mtd, loff_t from, size_t len, | 20 | extern int onenand_bbt_read_oob(struct mtd_info *mtd, loff_t from, |
| 21 | size_t *retlen, u_char *buf); | 21 | struct mtd_oob_ops *ops); |
| 22 | 22 | ||
| 23 | /** | 23 | /** |
| 24 | * check_short_pattern - [GENERIC] check if a pattern is in the buffer | 24 | * check_short_pattern - [GENERIC] check if a pattern is in the buffer |
| @@ -65,10 +65,11 @@ static int create_bbt(struct mtd_info *mtd, uint8_t *buf, struct nand_bbt_descr | |||
| 65 | int startblock; | 65 | int startblock; |
| 66 | loff_t from; | 66 | loff_t from; |
| 67 | size_t readlen, ooblen; | 67 | size_t readlen, ooblen; |
| 68 | struct mtd_oob_ops ops; | ||
| 68 | 69 | ||
| 69 | printk(KERN_INFO "Scanning device for bad blocks\n"); | 70 | printk(KERN_INFO "Scanning device for bad blocks\n"); |
| 70 | 71 | ||
| 71 | len = 1; | 72 | len = 2; |
| 72 | 73 | ||
| 73 | /* We need only read few bytes from the OOB area */ | 74 | /* We need only read few bytes from the OOB area */ |
| 74 | scanlen = ooblen = 0; | 75 | scanlen = ooblen = 0; |
| @@ -82,22 +83,24 @@ static int create_bbt(struct mtd_info *mtd, uint8_t *buf, struct nand_bbt_descr | |||
| 82 | startblock = 0; | 83 | startblock = 0; |
| 83 | from = 0; | 84 | from = 0; |
| 84 | 85 | ||
| 86 | ops.mode = MTD_OOB_PLACE; | ||
| 87 | ops.ooblen = readlen; | ||
| 88 | ops.oobbuf = buf; | ||
| 89 | ops.len = ops.ooboffs = ops.retlen = ops.oobretlen = 0; | ||
| 90 | |||
| 85 | for (i = startblock; i < numblocks; ) { | 91 | for (i = startblock; i < numblocks; ) { |
| 86 | int ret; | 92 | int ret; |
| 87 | 93 | ||
| 88 | for (j = 0; j < len; j++) { | 94 | for (j = 0; j < len; j++) { |
| 89 | size_t retlen; | ||
| 90 | |||
| 91 | /* No need to read pages fully, | 95 | /* No need to read pages fully, |
| 92 | * just read required OOB bytes */ | 96 | * just read required OOB bytes */ |
| 93 | ret = onenand_do_read_oob(mtd, from + j * mtd->writesize + bd->offs, | 97 | ret = onenand_bbt_read_oob(mtd, from + j * mtd->writesize + bd->offs, &ops); |
| 94 | readlen, &retlen, &buf[0]); | ||
| 95 | 98 | ||
| 96 | /* If it is a initial bad block, just ignore it */ | 99 | /* If it is a initial bad block, just ignore it */ |
| 97 | if (ret && !(ret & ONENAND_CTRL_LOAD)) | 100 | if (ret == ONENAND_BBT_READ_FATAL_ERROR) |
| 98 | return ret; | 101 | return -EIO; |
| 99 | 102 | ||
| 100 | if (check_short_pattern(&buf[j * scanlen], scanlen, mtd->writesize, bd)) { | 103 | if (ret || check_short_pattern(&buf[j * scanlen], scanlen, mtd->writesize, bd)) { |
| 101 | bbm->bbt[i >> 3] |= 0x03 << (i & 0x6); | 104 | bbm->bbt[i >> 3] |= 0x03 << (i & 0x6); |
| 102 | printk(KERN_WARNING "Bad eraseblock %d at 0x%08x\n", | 105 | printk(KERN_WARNING "Bad eraseblock %d at 0x%08x\n", |
| 103 | i >> 1, (unsigned int) from); | 106 | i >> 1, (unsigned int) from); |
| @@ -168,8 +171,8 @@ static int onenand_isbad_bbt(struct mtd_info *mtd, loff_t offs, int allowbbt) | |||
| 168 | * marked good / bad blocks and writes the bad block table(s) to | 171 | * marked good / bad blocks and writes the bad block table(s) to |
| 169 | * the selected place. | 172 | * the selected place. |
| 170 | * | 173 | * |
| 171 | * The bad block table memory is allocated here. It must be freed | 174 | * The bad block table memory is allocated here. It is freed |
| 172 | * by calling the onenand_free_bbt function. | 175 | * by the onenand_release function. |
| 173 | * | 176 | * |
| 174 | */ | 177 | */ |
| 175 | int onenand_scan_bbt(struct mtd_info *mtd, struct nand_bbt_descr *bd) | 178 | int onenand_scan_bbt(struct mtd_info *mtd, struct nand_bbt_descr *bd) |
diff --git a/drivers/mtd/redboot.c b/drivers/mtd/redboot.c index 035cd9b0cc08..a61351f88ec0 100644 --- a/drivers/mtd/redboot.c +++ b/drivers/mtd/redboot.c | |||
| @@ -94,8 +94,19 @@ static int parse_redboot_partitions(struct mtd_info *master, | |||
| 94 | * (NOTE: this is 'size' not 'data_length'; size is | 94 | * (NOTE: this is 'size' not 'data_length'; size is |
| 95 | * the full size of the entry.) | 95 | * the full size of the entry.) |
| 96 | */ | 96 | */ |
| 97 | if (swab32(buf[i].size) == master->erasesize) { | 97 | |
| 98 | /* RedBoot can combine the FIS directory and | ||
| 99 | config partitions into a single eraseblock; | ||
| 100 | we assume wrong-endian if either the swapped | ||
| 101 | 'size' matches the eraseblock size precisely, | ||
| 102 | or if the swapped size actually fits in an | ||
| 103 | eraseblock while the unswapped size doesn't. */ | ||
| 104 | if (swab32(buf[i].size) == master->erasesize || | ||
| 105 | (buf[i].size > master->erasesize | ||
| 106 | && swab32(buf[i].size) < master->erasesize)) { | ||
| 98 | int j; | 107 | int j; |
| 108 | /* Update numslots based on actual FIS directory size */ | ||
| 109 | numslots = swab32(buf[i].size) / sizeof (struct fis_image_desc); | ||
| 99 | for (j = 0; j < numslots; ++j) { | 110 | for (j = 0; j < numslots; ++j) { |
| 100 | 111 | ||
| 101 | /* A single 0xff denotes a deleted entry. | 112 | /* A single 0xff denotes a deleted entry. |
| @@ -120,11 +131,11 @@ static int parse_redboot_partitions(struct mtd_info *master, | |||
| 120 | swab32s(&buf[j].desc_cksum); | 131 | swab32s(&buf[j].desc_cksum); |
| 121 | swab32s(&buf[j].file_cksum); | 132 | swab32s(&buf[j].file_cksum); |
| 122 | } | 133 | } |
| 134 | } else if (buf[i].size < master->erasesize) { | ||
| 135 | /* Update numslots based on actual FIS directory size */ | ||
| 136 | numslots = buf[i].size / sizeof(struct fis_image_desc); | ||
| 123 | } | 137 | } |
| 124 | break; | 138 | break; |
| 125 | } else { | ||
| 126 | /* re-calculate of real numslots */ | ||
| 127 | numslots = buf[i].size / sizeof(struct fis_image_desc); | ||
| 128 | } | 139 | } |
| 129 | } | 140 | } |
| 130 | if (i == numslots) { | 141 | if (i == numslots) { |
diff --git a/fs/jffs2/build.c b/fs/jffs2/build.c index 02826967ab58..07119c42a861 100644 --- a/fs/jffs2/build.c +++ b/fs/jffs2/build.c | |||
| @@ -348,23 +348,27 @@ int jffs2_do_mount_fs(struct jffs2_sb_info *c) | |||
| 348 | 348 | ||
| 349 | ret = jffs2_sum_init(c); | 349 | ret = jffs2_sum_init(c); |
| 350 | if (ret) | 350 | if (ret) |
| 351 | return ret; | 351 | goto out_free; |
| 352 | 352 | ||
| 353 | if (jffs2_build_filesystem(c)) { | 353 | if (jffs2_build_filesystem(c)) { |
| 354 | dbg_fsbuild("build_fs failed\n"); | 354 | dbg_fsbuild("build_fs failed\n"); |
| 355 | jffs2_free_ino_caches(c); | 355 | jffs2_free_ino_caches(c); |
| 356 | jffs2_free_raw_node_refs(c); | 356 | jffs2_free_raw_node_refs(c); |
| 357 | #ifndef __ECOS | 357 | ret = -EIO; |
| 358 | if (jffs2_blocks_use_vmalloc(c)) | 358 | goto out_free; |
| 359 | vfree(c->blocks); | ||
| 360 | else | ||
| 361 | #endif | ||
| 362 | kfree(c->blocks); | ||
| 363 | |||
| 364 | return -EIO; | ||
| 365 | } | 359 | } |
| 366 | 360 | ||
| 367 | jffs2_calc_trigger_levels(c); | 361 | jffs2_calc_trigger_levels(c); |
| 368 | 362 | ||
| 369 | return 0; | 363 | return 0; |
| 364 | |||
| 365 | out_free: | ||
| 366 | #ifndef __ECOS | ||
| 367 | if (jffs2_blocks_use_vmalloc(c)) | ||
| 368 | vfree(c->blocks); | ||
| 369 | else | ||
| 370 | #endif | ||
| 371 | kfree(c->blocks); | ||
| 372 | |||
| 373 | return ret; | ||
| 370 | } | 374 | } |
diff --git a/fs/jffs2/jffs2_fs_sb.h b/fs/jffs2/jffs2_fs_sb.h index b98594992eed..ea88f69af130 100644 --- a/fs/jffs2/jffs2_fs_sb.h +++ b/fs/jffs2/jffs2_fs_sb.h | |||
| @@ -98,20 +98,14 @@ struct jffs2_sb_info { | |||
| 98 | uint32_t wbuf_pagesize; /* 0 for NOR and other flashes with no wbuf */ | 98 | uint32_t wbuf_pagesize; /* 0 for NOR and other flashes with no wbuf */ |
| 99 | 99 | ||
| 100 | #ifdef CONFIG_JFFS2_FS_WRITEBUFFER | 100 | #ifdef CONFIG_JFFS2_FS_WRITEBUFFER |
| 101 | /* Write-behind buffer for NAND flash */ | 101 | unsigned char *wbuf; /* Write-behind buffer for NAND flash */ |
| 102 | unsigned char *wbuf; | ||
| 103 | unsigned char *oobbuf; | ||
| 104 | uint32_t wbuf_ofs; | 102 | uint32_t wbuf_ofs; |
| 105 | uint32_t wbuf_len; | 103 | uint32_t wbuf_len; |
| 106 | struct jffs2_inodirty *wbuf_inodes; | 104 | struct jffs2_inodirty *wbuf_inodes; |
| 107 | |||
| 108 | struct rw_semaphore wbuf_sem; /* Protects the write buffer */ | 105 | struct rw_semaphore wbuf_sem; /* Protects the write buffer */ |
| 109 | 106 | ||
| 110 | /* Information about out-of-band area usage... */ | 107 | unsigned char *oobbuf; |
| 111 | struct nand_ecclayout *ecclayout; | 108 | int oobavail; /* How many bytes are available for JFFS2 in OOB */ |
| 112 | uint32_t badblock_pos; | ||
| 113 | uint32_t fsdata_pos; | ||
| 114 | uint32_t fsdata_len; | ||
| 115 | #endif | 109 | #endif |
| 116 | 110 | ||
| 117 | struct jffs2_summary *summary; /* Summary information */ | 111 | struct jffs2_summary *summary; /* Summary information */ |
diff --git a/fs/jffs2/scan.c b/fs/jffs2/scan.c index 3af746eaff0e..31c1475d922a 100644 --- a/fs/jffs2/scan.c +++ b/fs/jffs2/scan.c | |||
| @@ -450,16 +450,20 @@ static int jffs2_scan_eraseblock (struct jffs2_sb_info *c, struct jffs2_eraseblo | |||
| 450 | 450 | ||
| 451 | #ifdef CONFIG_JFFS2_FS_WRITEBUFFER | 451 | #ifdef CONFIG_JFFS2_FS_WRITEBUFFER |
| 452 | if (jffs2_cleanmarker_oob(c)) { | 452 | if (jffs2_cleanmarker_oob(c)) { |
| 453 | int ret = jffs2_check_nand_cleanmarker(c, jeb); | 453 | int ret; |
| 454 | |||
| 455 | if (c->mtd->block_isbad(c->mtd, jeb->offset)) | ||
| 456 | return BLK_STATE_BADBLOCK; | ||
| 457 | |||
| 458 | ret = jffs2_check_nand_cleanmarker(c, jeb); | ||
| 454 | D2(printk(KERN_NOTICE "jffs_check_nand_cleanmarker returned %d\n",ret)); | 459 | D2(printk(KERN_NOTICE "jffs_check_nand_cleanmarker returned %d\n",ret)); |
| 460 | |||
| 455 | /* Even if it's not found, we still scan to see | 461 | /* Even if it's not found, we still scan to see |
| 456 | if the block is empty. We use this information | 462 | if the block is empty. We use this information |
| 457 | to decide whether to erase it or not. */ | 463 | to decide whether to erase it or not. */ |
| 458 | switch (ret) { | 464 | switch (ret) { |
| 459 | case 0: cleanmarkerfound = 1; break; | 465 | case 0: cleanmarkerfound = 1; break; |
| 460 | case 1: break; | 466 | case 1: break; |
| 461 | case 2: return BLK_STATE_BADBLOCK; | ||
| 462 | case 3: return BLK_STATE_ALLDIRTY; /* Block has failed to erase min. once */ | ||
| 463 | default: return ret; | 467 | default: return ret; |
| 464 | } | 468 | } |
| 465 | } | 469 | } |
diff --git a/fs/jffs2/wbuf.c b/fs/jffs2/wbuf.c index 9c99859f5edd..de718e3a1692 100644 --- a/fs/jffs2/wbuf.c +++ b/fs/jffs2/wbuf.c | |||
| @@ -957,43 +957,48 @@ exit: | |||
| 957 | return ret; | 957 | return ret; |
| 958 | } | 958 | } |
| 959 | 959 | ||
| 960 | #define NR_OOB_SCAN_PAGES 4 | 960 | #define NR_OOB_SCAN_PAGES 4 |
| 961 | |||
| 962 | /* For historical reasons we use only 12 bytes for OOB clean marker */ | ||
| 963 | #define OOB_CM_SIZE 12 | ||
| 964 | |||
| 965 | static const struct jffs2_unknown_node oob_cleanmarker = | ||
| 966 | { | ||
| 967 | .magic = cpu_to_je16(JFFS2_MAGIC_BITMASK), | ||
| 968 | .nodetype = cpu_to_je16(JFFS2_NODETYPE_CLEANMARKER), | ||
| 969 | .totlen = cpu_to_je32(8) | ||
| 970 | }; | ||
| 961 | 971 | ||
| 962 | /* | 972 | /* |
| 963 | * Check, if the out of band area is empty | 973 | * Check, if the out of band area is empty. This function knows about the clean |
| 974 | * marker and if it is present in OOB, treats the OOB as empty anyway. | ||
| 964 | */ | 975 | */ |
| 965 | int jffs2_check_oob_empty(struct jffs2_sb_info *c, | 976 | int jffs2_check_oob_empty(struct jffs2_sb_info *c, |
| 966 | struct jffs2_eraseblock *jeb, int mode) | 977 | struct jffs2_eraseblock *jeb, int mode) |
| 967 | { | 978 | { |
| 968 | int i, page, ret; | 979 | int i, ret; |
| 969 | int oobsize = c->mtd->oobsize; | 980 | int cmlen = min_t(int, c->oobavail, OOB_CM_SIZE); |
| 970 | struct mtd_oob_ops ops; | 981 | struct mtd_oob_ops ops; |
| 971 | 982 | ||
| 972 | ops.ooblen = NR_OOB_SCAN_PAGES * oobsize; | 983 | ops.mode = MTD_OOB_AUTO; |
| 984 | ops.ooblen = NR_OOB_SCAN_PAGES * c->oobavail; | ||
| 973 | ops.oobbuf = c->oobbuf; | 985 | ops.oobbuf = c->oobbuf; |
| 974 | ops.ooboffs = 0; | 986 | ops.len = ops.ooboffs = ops.retlen = ops.oobretlen = 0; |
| 975 | ops.datbuf = NULL; | 987 | ops.datbuf = NULL; |
| 976 | ops.mode = MTD_OOB_PLACE; | ||
| 977 | 988 | ||
| 978 | ret = c->mtd->read_oob(c->mtd, jeb->offset, &ops); | 989 | ret = c->mtd->read_oob(c->mtd, jeb->offset, &ops); |
| 979 | if (ret) { | 990 | if (ret || ops.oobretlen != ops.ooblen) { |
| 980 | D1(printk(KERN_WARNING "jffs2_check_oob_empty(): Read OOB " | 991 | printk(KERN_ERR "cannot read OOB for EB at %08x, requested %zd" |
| 981 | "failed %d for block at %08x\n", ret, jeb->offset)); | 992 | " bytes, read %zd bytes, error %d\n", |
| 993 | jeb->offset, ops.ooblen, ops.oobretlen, ret); | ||
| 994 | if (!ret) | ||
| 995 | ret = -EIO; | ||
| 982 | return ret; | 996 | return ret; |
| 983 | } | 997 | } |
| 984 | 998 | ||
| 985 | if (ops.oobretlen < ops.ooblen) { | 999 | for(i = 0; i < ops.ooblen; i++) { |
| 986 | D1(printk(KERN_WARNING "jffs2_check_oob_empty(): Read OOB " | 1000 | if (mode && i < cmlen) |
| 987 | "returned short read (%zd bytes not %d) for block " | 1001 | /* Yeah, we know about the cleanmarker */ |
| 988 | "at %08x\n", ops.oobretlen, ops.ooblen, jeb->offset)); | ||
| 989 | return -EIO; | ||
| 990 | } | ||
| 991 | |||
| 992 | /* Special check for first page */ | ||
| 993 | for(i = 0; i < oobsize ; i++) { | ||
| 994 | /* Yeah, we know about the cleanmarker. */ | ||
| 995 | if (mode && i >= c->fsdata_pos && | ||
| 996 | i < c->fsdata_pos + c->fsdata_len) | ||
| 997 | continue; | 1002 | continue; |
| 998 | 1003 | ||
| 999 | if (ops.oobbuf[i] != 0xFF) { | 1004 | if (ops.oobbuf[i] != 0xFF) { |
| @@ -1003,111 +1008,63 @@ int jffs2_check_oob_empty(struct jffs2_sb_info *c, | |||
| 1003 | } | 1008 | } |
| 1004 | } | 1009 | } |
| 1005 | 1010 | ||
| 1006 | /* we know, we are aligned :) */ | ||
| 1007 | for (page = oobsize; page < ops.ooblen; page += sizeof(long)) { | ||
| 1008 | long dat = *(long *)(&ops.oobbuf[page]); | ||
| 1009 | if(dat != -1) | ||
| 1010 | return 1; | ||
| 1011 | } | ||
| 1012 | return 0; | 1011 | return 0; |
| 1013 | } | 1012 | } |
| 1014 | 1013 | ||
| 1015 | /* | 1014 | /* |
| 1016 | * Scan for a valid cleanmarker and for bad blocks | 1015 | * Check for a valid cleanmarker. |
| 1016 | * Returns: 0 if a valid cleanmarker was found | ||
| 1017 | * 1 if no cleanmarker was found | ||
| 1018 | * negative error code if an error occurred | ||
| 1017 | */ | 1019 | */ |
| 1018 | int jffs2_check_nand_cleanmarker (struct jffs2_sb_info *c, | 1020 | int jffs2_check_nand_cleanmarker(struct jffs2_sb_info *c, |
| 1019 | struct jffs2_eraseblock *jeb) | 1021 | struct jffs2_eraseblock *jeb) |
| 1020 | { | 1022 | { |
| 1021 | struct jffs2_unknown_node n; | ||
| 1022 | struct mtd_oob_ops ops; | 1023 | struct mtd_oob_ops ops; |
| 1023 | int oobsize = c->mtd->oobsize; | 1024 | int ret, cmlen = min_t(int, c->oobavail, OOB_CM_SIZE); |
| 1024 | unsigned char *p,*b; | ||
| 1025 | int i, ret; | ||
| 1026 | size_t offset = jeb->offset; | ||
| 1027 | |||
| 1028 | /* Check first if the block is bad. */ | ||
| 1029 | if (c->mtd->block_isbad(c->mtd, offset)) { | ||
| 1030 | D1 (printk(KERN_WARNING "jffs2_check_nand_cleanmarker()" | ||
| 1031 | ": Bad block at %08x\n", jeb->offset)); | ||
| 1032 | return 2; | ||
| 1033 | } | ||
| 1034 | 1025 | ||
| 1035 | ops.ooblen = oobsize; | 1026 | ops.mode = MTD_OOB_AUTO; |
| 1027 | ops.ooblen = cmlen; | ||
| 1036 | ops.oobbuf = c->oobbuf; | 1028 | ops.oobbuf = c->oobbuf; |
| 1037 | ops.ooboffs = 0; | 1029 | ops.len = ops.ooboffs = ops.retlen = ops.oobretlen = 0; |
| 1038 | ops.datbuf = NULL; | 1030 | ops.datbuf = NULL; |
| 1039 | ops.mode = MTD_OOB_PLACE; | ||
| 1040 | 1031 | ||
| 1041 | ret = c->mtd->read_oob(c->mtd, offset, &ops); | 1032 | ret = c->mtd->read_oob(c->mtd, jeb->offset, &ops); |
| 1042 | if (ret) { | 1033 | if (ret || ops.oobretlen != ops.ooblen) { |
| 1043 | D1 (printk(KERN_WARNING "jffs2_check_nand_cleanmarker(): " | 1034 | printk(KERN_ERR "cannot read OOB for EB at %08x, requested %zd" |
| 1044 | "Read OOB failed %d for block at %08x\n", | 1035 | " bytes, read %zd bytes, error %d\n", |
| 1045 | ret, jeb->offset)); | 1036 | jeb->offset, ops.ooblen, ops.oobretlen, ret); |
| 1037 | if (!ret) | ||
| 1038 | ret = -EIO; | ||
| 1046 | return ret; | 1039 | return ret; |
| 1047 | } | 1040 | } |
| 1048 | 1041 | ||
| 1049 | if (ops.oobretlen < ops.ooblen) { | 1042 | return !!memcmp(&oob_cleanmarker, c->oobbuf, cmlen); |
| 1050 | D1 (printk (KERN_WARNING "jffs2_check_nand_cleanmarker(): " | ||
| 1051 | "Read OOB return short read (%zd bytes not %d) " | ||
| 1052 | "for block at %08x\n", ops.oobretlen, ops.ooblen, | ||
| 1053 | jeb->offset)); | ||
| 1054 | return -EIO; | ||
| 1055 | } | ||
| 1056 | |||
| 1057 | n.magic = cpu_to_je16 (JFFS2_MAGIC_BITMASK); | ||
| 1058 | n.nodetype = cpu_to_je16 (JFFS2_NODETYPE_CLEANMARKER); | ||
| 1059 | n.totlen = cpu_to_je32 (8); | ||
| 1060 | p = (unsigned char *) &n; | ||
| 1061 | b = c->oobbuf + c->fsdata_pos; | ||
| 1062 | |||
| 1063 | for (i = c->fsdata_len; i; i--) { | ||
| 1064 | if (*b++ != *p++) | ||
| 1065 | ret = 1; | ||
| 1066 | } | ||
| 1067 | |||
| 1068 | D1(if (ret == 1) { | ||
| 1069 | printk(KERN_WARNING "jffs2_check_nand_cleanmarker(): " | ||
| 1070 | "Cleanmarker node not detected in block at %08x\n", | ||
| 1071 | offset); | ||
| 1072 | printk(KERN_WARNING "OOB at %08zx was ", offset); | ||
| 1073 | for (i=0; i < oobsize; i++) | ||
| 1074 | printk("%02x ", c->oobbuf[i]); | ||
| 1075 | printk("\n"); | ||
| 1076 | }); | ||
| 1077 | return ret; | ||
| 1078 | } | 1043 | } |
| 1079 | 1044 | ||
| 1080 | int jffs2_write_nand_cleanmarker(struct jffs2_sb_info *c, | 1045 | int jffs2_write_nand_cleanmarker(struct jffs2_sb_info *c, |
| 1081 | struct jffs2_eraseblock *jeb) | 1046 | struct jffs2_eraseblock *jeb) |
| 1082 | { | 1047 | { |
| 1083 | struct jffs2_unknown_node n; | 1048 | int ret; |
| 1084 | int ret; | ||
| 1085 | struct mtd_oob_ops ops; | 1049 | struct mtd_oob_ops ops; |
| 1050 | int cmlen = min_t(int, c->oobavail, OOB_CM_SIZE); | ||
| 1086 | 1051 | ||
| 1087 | n.magic = cpu_to_je16(JFFS2_MAGIC_BITMASK); | 1052 | ops.mode = MTD_OOB_AUTO; |
| 1088 | n.nodetype = cpu_to_je16(JFFS2_NODETYPE_CLEANMARKER); | 1053 | ops.ooblen = cmlen; |
| 1089 | n.totlen = cpu_to_je32(8); | 1054 | ops.oobbuf = (uint8_t *)&oob_cleanmarker; |
| 1090 | 1055 | ops.len = ops.ooboffs = ops.retlen = ops.oobretlen = 0; | |
| 1091 | ops.ooblen = c->fsdata_len; | ||
| 1092 | ops.oobbuf = (uint8_t *)&n; | ||
| 1093 | ops.ooboffs = c->fsdata_pos; | ||
| 1094 | ops.datbuf = NULL; | 1056 | ops.datbuf = NULL; |
| 1095 | ops.mode = MTD_OOB_PLACE; | ||
| 1096 | 1057 | ||
| 1097 | ret = c->mtd->write_oob(c->mtd, jeb->offset, &ops); | 1058 | ret = c->mtd->write_oob(c->mtd, jeb->offset, &ops); |
| 1098 | 1059 | if (ret || ops.oobretlen != ops.ooblen) { | |
| 1099 | if (ret) { | 1060 | printk(KERN_ERR "cannot write OOB for EB at %08x, requested %zd" |
| 1100 | D1(printk(KERN_WARNING "jffs2_write_nand_cleanmarker(): " | 1061 | " bytes, read %zd bytes, error %d\n", |
| 1101 | "Write failed for block at %08x: error %d\n", | 1062 | jeb->offset, ops.ooblen, ops.oobretlen, ret); |
| 1102 | jeb->offset, ret)); | 1063 | if (!ret) |
| 1064 | ret = -EIO; | ||
| 1103 | return ret; | 1065 | return ret; |
| 1104 | } | 1066 | } |
| 1105 | if (ops.oobretlen != ops.ooblen) { | 1067 | |
| 1106 | D1(printk(KERN_WARNING "jffs2_write_nand_cleanmarker(): " | ||
| 1107 | "Short write for block at %08x: %zd not %d\n", | ||
| 1108 | jeb->offset, ops.oobretlen, ops.ooblen)); | ||
| 1109 | return -EIO; | ||
| 1110 | } | ||
| 1111 | return 0; | 1068 | return 0; |
| 1112 | } | 1069 | } |
| 1113 | 1070 | ||
| @@ -1140,41 +1097,24 @@ int jffs2_write_nand_badblock(struct jffs2_sb_info *c, struct jffs2_eraseblock * | |||
| 1140 | return 1; | 1097 | return 1; |
| 1141 | } | 1098 | } |
| 1142 | 1099 | ||
| 1143 | static int jffs2_nand_set_oobinfo(struct jffs2_sb_info *c) | 1100 | int jffs2_nand_flash_setup(struct jffs2_sb_info *c) |
| 1144 | { | 1101 | { |
| 1145 | struct nand_ecclayout *oinfo = c->mtd->ecclayout; | 1102 | struct nand_ecclayout *oinfo = c->mtd->ecclayout; |
| 1146 | 1103 | ||
| 1147 | /* Do this only, if we have an oob buffer */ | ||
| 1148 | if (!c->mtd->oobsize) | 1104 | if (!c->mtd->oobsize) |
| 1149 | return 0; | 1105 | return 0; |
| 1150 | 1106 | ||
| 1151 | /* Cleanmarker is out-of-band, so inline size zero */ | 1107 | /* Cleanmarker is out-of-band, so inline size zero */ |
| 1152 | c->cleanmarker_size = 0; | 1108 | c->cleanmarker_size = 0; |
| 1153 | 1109 | ||
| 1154 | /* Should we use autoplacement ? */ | 1110 | if (!oinfo || oinfo->oobavail == 0) { |
| 1155 | if (!oinfo) { | 1111 | printk(KERN_ERR "inconsistent device description\n"); |
| 1156 | D1(printk(KERN_DEBUG "JFFS2 on NAND. No autoplacment info found\n")); | ||
| 1157 | return -EINVAL; | 1112 | return -EINVAL; |
| 1158 | } | 1113 | } |
| 1159 | 1114 | ||
| 1160 | D1(printk(KERN_DEBUG "JFFS2 using autoplace on NAND\n")); | 1115 | D1(printk(KERN_DEBUG "JFFS2 using OOB on NAND\n")); |
| 1161 | /* Get the position of the free bytes */ | ||
| 1162 | if (!oinfo->oobfree[0].length) { | ||
| 1163 | printk (KERN_WARNING "jffs2_nand_set_oobinfo(): Eeep." | ||
| 1164 | " Autoplacement selected and no empty space in oob\n"); | ||
| 1165 | return -ENOSPC; | ||
| 1166 | } | ||
| 1167 | c->fsdata_pos = oinfo->oobfree[0].offset; | ||
| 1168 | c->fsdata_len = oinfo->oobfree[0].length; | ||
| 1169 | if (c->fsdata_len > 8) | ||
| 1170 | c->fsdata_len = 8; | ||
| 1171 | 1116 | ||
| 1172 | return 0; | 1117 | c->oobavail = oinfo->oobavail; |
| 1173 | } | ||
| 1174 | |||
| 1175 | int jffs2_nand_flash_setup(struct jffs2_sb_info *c) | ||
| 1176 | { | ||
| 1177 | int res; | ||
| 1178 | 1118 | ||
| 1179 | /* Initialise write buffer */ | 1119 | /* Initialise write buffer */ |
| 1180 | init_rwsem(&c->wbuf_sem); | 1120 | init_rwsem(&c->wbuf_sem); |
| @@ -1185,22 +1125,13 @@ int jffs2_nand_flash_setup(struct jffs2_sb_info *c) | |||
| 1185 | if (!c->wbuf) | 1125 | if (!c->wbuf) |
| 1186 | return -ENOMEM; | 1126 | return -ENOMEM; |
| 1187 | 1127 | ||
| 1188 | c->oobbuf = kmalloc(NR_OOB_SCAN_PAGES * c->mtd->oobsize, GFP_KERNEL); | 1128 | c->oobbuf = kmalloc(NR_OOB_SCAN_PAGES * c->oobavail, GFP_KERNEL); |
| 1189 | if (!c->oobbuf) | 1129 | if (!c->oobbuf) { |
| 1190 | return -ENOMEM; | ||
| 1191 | |||
| 1192 | res = jffs2_nand_set_oobinfo(c); | ||
| 1193 | |||
| 1194 | #ifdef BREAKME | ||
| 1195 | if (!brokenbuf) | ||
| 1196 | brokenbuf = kmalloc(c->wbuf_pagesize, GFP_KERNEL); | ||
| 1197 | if (!brokenbuf) { | ||
| 1198 | kfree(c->wbuf); | 1130 | kfree(c->wbuf); |
| 1199 | return -ENOMEM; | 1131 | return -ENOMEM; |
| 1200 | } | 1132 | } |
| 1201 | memset(brokenbuf, 0xdb, c->wbuf_pagesize); | 1133 | |
| 1202 | #endif | 1134 | return 0; |
| 1203 | return res; | ||
| 1204 | } | 1135 | } |
| 1205 | 1136 | ||
| 1206 | void jffs2_nand_flash_cleanup(struct jffs2_sb_info *c) | 1137 | void jffs2_nand_flash_cleanup(struct jffs2_sb_info *c) |
diff --git a/include/linux/mtd/bbm.h b/include/linux/mtd/bbm.h index 1221b7c44158..fff8c53e5434 100644 --- a/include/linux/mtd/bbm.h +++ b/include/linux/mtd/bbm.h | |||
| @@ -92,6 +92,13 @@ struct nand_bbt_descr { | |||
| 92 | */ | 92 | */ |
| 93 | #define ONENAND_BADBLOCK_POS 0 | 93 | #define ONENAND_BADBLOCK_POS 0 |
| 94 | 94 | ||
| 95 | /* | ||
| 96 | * Bad block scanning errors | ||
| 97 | */ | ||
| 98 | #define ONENAND_BBT_READ_ERROR 1 | ||
| 99 | #define ONENAND_BBT_READ_ECC_ERROR 2 | ||
| 100 | #define ONENAND_BBT_READ_FATAL_ERROR 4 | ||
| 101 | |||
| 95 | /** | 102 | /** |
| 96 | * struct bbm_info - [GENERIC] Bad Block Table data structure | 103 | * struct bbm_info - [GENERIC] Bad Block Table data structure |
| 97 | * @bbt_erase_shift: [INTERN] number of address bits in a bbt entry | 104 | * @bbt_erase_shift: [INTERN] number of address bits in a bbt entry |
diff --git a/include/linux/mtd/map.h b/include/linux/mtd/map.h index 28d461d862bd..81f3a314dd76 100644 --- a/include/linux/mtd/map.h +++ b/include/linux/mtd/map.h | |||
| @@ -183,7 +183,7 @@ typedef union { | |||
| 183 | struct map_info { | 183 | struct map_info { |
| 184 | char *name; | 184 | char *name; |
| 185 | unsigned long size; | 185 | unsigned long size; |
| 186 | unsigned long phys; | 186 | resource_size_t phys; |
| 187 | #define NO_XIP (-1UL) | 187 | #define NO_XIP (-1UL) |
| 188 | 188 | ||
| 189 | void __iomem *virt; | 189 | void __iomem *virt; |
diff --git a/include/linux/mtd/mtd.h b/include/linux/mtd/mtd.h index d644e57703ad..6a8570be331b 100644 --- a/include/linux/mtd/mtd.h +++ b/include/linux/mtd/mtd.h | |||
| @@ -85,6 +85,10 @@ typedef enum { | |||
| 85 | * mode = MTD_OOB_PLACE) | 85 | * mode = MTD_OOB_PLACE) |
| 86 | * @datbuf: data buffer - if NULL only oob data are read/written | 86 | * @datbuf: data buffer - if NULL only oob data are read/written |
| 87 | * @oobbuf: oob data buffer | 87 | * @oobbuf: oob data buffer |
| 88 | * | ||
| 89 | * Note, it is allowed to read more then one OOB area at one go, but not write. | ||
| 90 | * The interface assumes that the OOB write requests program only one page's | ||
| 91 | * OOB area. | ||
| 88 | */ | 92 | */ |
| 89 | struct mtd_oob_ops { | 93 | struct mtd_oob_ops { |
| 90 | mtd_oob_mode_t mode; | 94 | mtd_oob_mode_t mode; |
| @@ -117,18 +121,6 @@ struct mtd_info { | |||
| 117 | u_int32_t writesize; | 121 | u_int32_t writesize; |
| 118 | 122 | ||
| 119 | u_int32_t oobsize; // Amount of OOB data per block (e.g. 16) | 123 | u_int32_t oobsize; // Amount of OOB data per block (e.g. 16) |
| 120 | u_int32_t ecctype; | ||
| 121 | u_int32_t eccsize; | ||
| 122 | |||
| 123 | /* | ||
| 124 | * Reuse some of the above unused fields in the case of NOR flash | ||
| 125 | * with configurable programming regions to avoid modifying the | ||
| 126 | * user visible structure layout/size. Only valid when the | ||
| 127 | * MTD_PROGRAM_REGIONS flag is set. | ||
| 128 | * (Maybe we should have an union for those?) | ||
| 129 | */ | ||
| 130 | #define MTD_PROGREGION_CTRLMODE_VALID(mtd) (mtd)->oobsize | ||
| 131 | #define MTD_PROGREGION_CTRLMODE_INVALID(mtd) (mtd)->ecctype | ||
| 132 | 124 | ||
| 133 | // Kernel-only stuff starts here. | 125 | // Kernel-only stuff starts here. |
| 134 | char *name; | 126 | char *name; |
diff --git a/include/linux/mtd/nand.h b/include/linux/mtd/nand.h index 2071b02f0526..97523887fe5d 100644 --- a/include/linux/mtd/nand.h +++ b/include/linux/mtd/nand.h | |||
| @@ -343,6 +343,7 @@ struct nand_buffers { | |||
| 343 | * @options: [BOARDSPECIFIC] various chip options. They can partly be set to inform nand_scan about | 343 | * @options: [BOARDSPECIFIC] various chip options. They can partly be set to inform nand_scan about |
| 344 | * special functionality. See the defines for further explanation | 344 | * special functionality. See the defines for further explanation |
| 345 | * @badblockpos: [INTERN] position of the bad block marker in the oob area | 345 | * @badblockpos: [INTERN] position of the bad block marker in the oob area |
| 346 | * @cellinfo: [INTERN] MLC/multichip data from chip ident | ||
| 346 | * @numchips: [INTERN] number of physical chips | 347 | * @numchips: [INTERN] number of physical chips |
| 347 | * @chipsize: [INTERN] the size of one chip for multichip arrays | 348 | * @chipsize: [INTERN] the size of one chip for multichip arrays |
| 348 | * @pagemask: [INTERN] page number mask = number of (pages / chip) - 1 | 349 | * @pagemask: [INTERN] page number mask = number of (pages / chip) - 1 |
diff --git a/include/linux/mtd/onenand.h b/include/linux/mtd/onenand.h index f775a7af3890..d8af8a95e58d 100644 --- a/include/linux/mtd/onenand.h +++ b/include/linux/mtd/onenand.h | |||
| @@ -1,7 +1,7 @@ | |||
| 1 | /* | 1 | /* |
| 2 | * linux/include/linux/mtd/onenand.h | 2 | * linux/include/linux/mtd/onenand.h |
| 3 | * | 3 | * |
| 4 | * Copyright (C) 2005-2006 Samsung Electronics | 4 | * Copyright (C) 2005-2007 Samsung Electronics |
| 5 | * Kyungmin Park <kyungmin.park@samsung.com> | 5 | * Kyungmin Park <kyungmin.park@samsung.com> |
| 6 | * | 6 | * |
| 7 | * This program is free software; you can redistribute it and/or modify | 7 | * This program is free software; you can redistribute it and/or modify |
| @@ -42,14 +42,10 @@ typedef enum { | |||
| 42 | 42 | ||
| 43 | /** | 43 | /** |
| 44 | * struct onenand_bufferram - OneNAND BufferRAM Data | 44 | * struct onenand_bufferram - OneNAND BufferRAM Data |
| 45 | * @block: block address in BufferRAM | 45 | * @blockpage: block & page address in BufferRAM |
| 46 | * @page: page address in BufferRAM | ||
| 47 | * @valid: valid flag | ||
| 48 | */ | 46 | */ |
| 49 | struct onenand_bufferram { | 47 | struct onenand_bufferram { |
| 50 | int block; | 48 | int blockpage; |
| 51 | int page; | ||
| 52 | int valid; | ||
| 53 | }; | 49 | }; |
| 54 | 50 | ||
| 55 | /** | 51 | /** |
| @@ -63,7 +59,6 @@ struct onenand_bufferram { | |||
| 63 | * partly be set to inform onenand_scan about | 59 | * partly be set to inform onenand_scan about |
| 64 | * @erase_shift: [INTERN] number of address bits in a block | 60 | * @erase_shift: [INTERN] number of address bits in a block |
| 65 | * @page_shift: [INTERN] number of address bits in a page | 61 | * @page_shift: [INTERN] number of address bits in a page |
| 66 | * @ppb_shift: [INTERN] number of address bits in a pages per block | ||
| 67 | * @page_mask: [INTERN] a page per block mask | 62 | * @page_mask: [INTERN] a page per block mask |
| 68 | * @bufferram_index: [INTERN] BufferRAM index | 63 | * @bufferram_index: [INTERN] BufferRAM index |
| 69 | * @bufferram: [INTERN] BufferRAM info | 64 | * @bufferram: [INTERN] BufferRAM info |
| @@ -103,7 +98,6 @@ struct onenand_chip { | |||
| 103 | 98 | ||
| 104 | unsigned int erase_shift; | 99 | unsigned int erase_shift; |
| 105 | unsigned int page_shift; | 100 | unsigned int page_shift; |
| 106 | unsigned int ppb_shift; /* Pages per block shift */ | ||
| 107 | unsigned int page_mask; | 101 | unsigned int page_mask; |
| 108 | 102 | ||
| 109 | unsigned int bufferram_index; | 103 | unsigned int bufferram_index; |
| @@ -150,6 +144,9 @@ struct onenand_chip { | |||
| 150 | #define ONENAND_SET_SYS_CFG1(v, this) \ | 144 | #define ONENAND_SET_SYS_CFG1(v, this) \ |
| 151 | (this->write_word(v, this->base + ONENAND_REG_SYS_CFG1)) | 145 | (this->write_word(v, this->base + ONENAND_REG_SYS_CFG1)) |
| 152 | 146 | ||
| 147 | #define ONENAND_IS_DDP(this) \ | ||
| 148 | (this->device_id & ONENAND_DEVICE_IS_DDP) | ||
| 149 | |||
| 153 | /* Check byte access in OneNAND */ | 150 | /* Check byte access in OneNAND */ |
| 154 | #define ONENAND_CHECK_BYTE_ACCESS(addr) (addr & 0x1) | 151 | #define ONENAND_CHECK_BYTE_ACCESS(addr) (addr & 0x1) |
| 155 | 152 | ||
diff --git a/include/linux/mtd/onenand_regs.h b/include/linux/mtd/onenand_regs.h index e31c8f5d4271..af94719890e7 100644 --- a/include/linux/mtd/onenand_regs.h +++ b/include/linux/mtd/onenand_regs.h | |||
| @@ -3,7 +3,8 @@ | |||
| 3 | * | 3 | * |
| 4 | * OneNAND Register header file | 4 | * OneNAND Register header file |
| 5 | * | 5 | * |
| 6 | * Copyright (C) 2005-2006 Samsung Electronics | 6 | * Copyright (C) 2005-2007 Samsung Electronics |
| 7 | * Kyungmin Park <kyungmin.park@samsung.com> | ||
| 7 | * | 8 | * |
| 8 | * This program is free software; you can redistribute it and/or modify | 9 | * This program is free software; you can redistribute it and/or modify |
| 9 | * it under the terms of the GNU General Public License version 2 as | 10 | * it under the terms of the GNU General Public License version 2 as |
| @@ -80,9 +81,11 @@ | |||
| 80 | #define ONENAND_VERSION_PROCESS_SHIFT (8) | 81 | #define ONENAND_VERSION_PROCESS_SHIFT (8) |
| 81 | 82 | ||
| 82 | /* | 83 | /* |
| 83 | * Start Address 1 F100h (R/W) | 84 | * Start Address 1 F100h (R/W) & Start Address 2 F101h (R/W) |
| 84 | */ | 85 | */ |
| 85 | #define ONENAND_DDP_SHIFT (15) | 86 | #define ONENAND_DDP_SHIFT (15) |
| 87 | #define ONENAND_DDP_CHIP0 (0) | ||
| 88 | #define ONENAND_DDP_CHIP1 (1 << ONENAND_DDP_SHIFT) | ||
| 86 | 89 | ||
| 87 | /* | 90 | /* |
| 88 | * Start Address 8 F107h (R/W) | 91 | * Start Address 8 F107h (R/W) |
diff --git a/include/linux/mtd/physmap.h b/include/linux/mtd/physmap.h index 86831e3594f6..0dc07d5f3354 100644 --- a/include/linux/mtd/physmap.h +++ b/include/linux/mtd/physmap.h | |||
| @@ -18,9 +18,10 @@ | |||
| 18 | #define __LINUX_MTD_PHYSMAP__ | 18 | #define __LINUX_MTD_PHYSMAP__ |
| 19 | 19 | ||
| 20 | #include <linux/mtd/mtd.h> | 20 | #include <linux/mtd/mtd.h> |
| 21 | #include <linux/mtd/map.h> | ||
| 22 | #include <linux/mtd/partitions.h> | 21 | #include <linux/mtd/partitions.h> |
| 23 | 22 | ||
| 23 | struct map_info; | ||
| 24 | |||
| 24 | struct physmap_flash_data { | 25 | struct physmap_flash_data { |
| 25 | unsigned int width; | 26 | unsigned int width; |
| 26 | void (*set_vpp)(struct map_info *, int); | 27 | void (*set_vpp)(struct map_info *, int); |
diff --git a/include/mtd/mtd-abi.h b/include/mtd/mtd-abi.h index f913c30d7b89..8e501a75a764 100644 --- a/include/mtd/mtd-abi.h +++ b/include/mtd/mtd-abi.h | |||
| @@ -36,12 +36,6 @@ struct mtd_oob_buf { | |||
| 36 | #define MTD_CAP_NORFLASH (MTD_WRITEABLE | MTD_BIT_WRITEABLE) | 36 | #define MTD_CAP_NORFLASH (MTD_WRITEABLE | MTD_BIT_WRITEABLE) |
| 37 | #define MTD_CAP_NANDFLASH (MTD_WRITEABLE) | 37 | #define MTD_CAP_NANDFLASH (MTD_WRITEABLE) |
| 38 | 38 | ||
| 39 | |||
| 40 | // Types of automatic ECC/Checksum available | ||
| 41 | #define MTD_ECC_NONE 0 // No automatic ECC available | ||
| 42 | #define MTD_ECC_RS_DiskOnChip 1 // Automatic ECC on DiskOnChip | ||
| 43 | #define MTD_ECC_SW 2 // SW ECC for Toshiba & Samsung devices | ||
| 44 | |||
| 45 | /* ECC byte placement */ | 39 | /* ECC byte placement */ |
| 46 | #define MTD_NANDECC_OFF 0 // Switch off ECC (Not recommended) | 40 | #define MTD_NANDECC_OFF 0 // Switch off ECC (Not recommended) |
| 47 | #define MTD_NANDECC_PLACE 1 // Use the given placement in the structure (YAFFS1 legacy mode) | 41 | #define MTD_NANDECC_PLACE 1 // Use the given placement in the structure (YAFFS1 legacy mode) |
| @@ -61,6 +55,8 @@ struct mtd_info_user { | |||
| 61 | uint32_t erasesize; | 55 | uint32_t erasesize; |
| 62 | uint32_t writesize; | 56 | uint32_t writesize; |
| 63 | uint32_t oobsize; // Amount of OOB data per block (e.g. 16) | 57 | uint32_t oobsize; // Amount of OOB data per block (e.g. 16) |
| 58 | /* The below two fields are obsolete and broken, do not use them | ||
| 59 | * (TODO: remove at some point) */ | ||
| 64 | uint32_t ecctype; | 60 | uint32_t ecctype; |
| 65 | uint32_t eccsize; | 61 | uint32_t eccsize; |
| 66 | }; | 62 | }; |
