diff options
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 | }; |