diff options
Diffstat (limited to 'drivers/mtd')
-rw-r--r-- | drivers/mtd/ubi/build.c | 5 | ||||
-rw-r--r-- | drivers/mtd/ubi/io.c | 58 | ||||
-rw-r--r-- | drivers/mtd/ubi/ubi.h | 4 |
3 files changed, 64 insertions, 3 deletions
diff --git a/drivers/mtd/ubi/build.c b/drivers/mtd/ubi/build.c index db0b9cb64c6c..e1f7d0a78b9d 100644 --- a/drivers/mtd/ubi/build.c +++ b/drivers/mtd/ubi/build.c | |||
@@ -657,6 +657,11 @@ static int io_init(struct ubi_device *ubi) | |||
657 | if (ubi->mtd->block_isbad && ubi->mtd->block_markbad) | 657 | if (ubi->mtd->block_isbad && ubi->mtd->block_markbad) |
658 | ubi->bad_allowed = 1; | 658 | ubi->bad_allowed = 1; |
659 | 659 | ||
660 | if (ubi->mtd->type == MTD_NORFLASH) { | ||
661 | ubi_assert(ubi->mtd->writesize == 1); | ||
662 | ubi->nor_flash = 1; | ||
663 | } | ||
664 | |||
660 | ubi->min_io_size = ubi->mtd->writesize; | 665 | ubi->min_io_size = ubi->mtd->writesize; |
661 | ubi->hdrs_min_io_size = ubi->mtd->writesize >> ubi->mtd->subpage_sft; | 666 | ubi->hdrs_min_io_size = ubi->mtd->writesize >> ubi->mtd->subpage_sft; |
662 | 667 | ||
diff --git a/drivers/mtd/ubi/io.c b/drivers/mtd/ubi/io.c index 1ea14218de02..c3bd2e34c642 100644 --- a/drivers/mtd/ubi/io.c +++ b/drivers/mtd/ubi/io.c | |||
@@ -266,8 +266,8 @@ int ubi_io_write(struct ubi_device *ubi, const void *buf, int pnum, int offset, | |||
266 | addr = (loff_t)pnum * ubi->peb_size + offset; | 266 | addr = (loff_t)pnum * ubi->peb_size + offset; |
267 | err = ubi->mtd->write(ubi->mtd, addr, len, &written, buf); | 267 | err = ubi->mtd->write(ubi->mtd, addr, len, &written, buf); |
268 | if (err) { | 268 | if (err) { |
269 | ubi_err("error %d while writing %d bytes to PEB %d:%d, written" | 269 | ubi_err("error %d while writing %d bytes to PEB %d:%d, written " |
270 | " %zd bytes", err, len, pnum, offset, written); | 270 | "%zd bytes", err, len, pnum, offset, written); |
271 | ubi_dbg_dump_stack(); | 271 | ubi_dbg_dump_stack(); |
272 | } else | 272 | } else |
273 | ubi_assert(written == len); | 273 | ubi_assert(written == len); |
@@ -454,6 +454,54 @@ out: | |||
454 | } | 454 | } |
455 | 455 | ||
456 | /** | 456 | /** |
457 | * nor_erase_prepare - prepare a NOR flash PEB for erasure. | ||
458 | * @ubi: UBI device description object | ||
459 | * @pnum: physical eraseblock number to prepare | ||
460 | * | ||
461 | * NOR flash, or at least some of them, have peculiar embedded PEB erasure | ||
462 | * algorithm: the PEB is first filled with zeroes, then it is erased. And | ||
463 | * filling with zeroes starts from the end of the PEB. This was observed with | ||
464 | * Spansion S29GL512N NOR flash. | ||
465 | * | ||
466 | * This means that in case of a power cut we may end up with intact data at the | ||
467 | * beginning of the PEB, and all zeroes at the end of PEB. In other words, the | ||
468 | * EC and VID headers are OK, but a large chunk of data at the end of PEB is | ||
469 | * zeroed. This makes UBI mistakenly treat this PEB as used and associate it | ||
470 | * with an LEB, which leads to subsequent failures (e.g., UBIFS fails). | ||
471 | * | ||
472 | * This function is called before erasing NOR PEBs and it zeroes out EC and VID | ||
473 | * magic numbers in order to invalidate them and prevent the failures. Returns | ||
474 | * zero in case of success and a negative error code in case of failure. | ||
475 | */ | ||
476 | static int nor_erase_prepare(struct ubi_device *ubi, int pnum) | ||
477 | { | ||
478 | int err; | ||
479 | size_t written; | ||
480 | loff_t addr; | ||
481 | uint32_t data = 0; | ||
482 | |||
483 | addr = (loff_t)pnum * ubi->peb_size; | ||
484 | err = ubi->mtd->write(ubi->mtd, addr, 4, &written, &data); | ||
485 | if (err) { | ||
486 | ubi_err("error %d while writing 4 bytes to PEB %d:0, written " | ||
487 | "%zd bytes", err, pnum, 0, written); | ||
488 | ubi_dbg_dump_stack(); | ||
489 | return err; | ||
490 | } | ||
491 | |||
492 | addr += ubi->vid_hdr_aloffset; | ||
493 | err = ubi->mtd->write(ubi->mtd, addr, 4, &written, &data); | ||
494 | if (err) { | ||
495 | ubi_err("error %d while writing 4 bytes to PEB %d:%d, written " | ||
496 | "%zd bytes", err, pnum, ubi->vid_hdr_aloffset, written); | ||
497 | ubi_dbg_dump_stack(); | ||
498 | return err; | ||
499 | } | ||
500 | |||
501 | return 0; | ||
502 | } | ||
503 | |||
504 | /** | ||
457 | * ubi_io_sync_erase - synchronously erase a physical eraseblock. | 505 | * ubi_io_sync_erase - synchronously erase a physical eraseblock. |
458 | * @ubi: UBI device description object | 506 | * @ubi: UBI device description object |
459 | * @pnum: physical eraseblock number to erase | 507 | * @pnum: physical eraseblock number to erase |
@@ -484,6 +532,12 @@ int ubi_io_sync_erase(struct ubi_device *ubi, int pnum, int torture) | |||
484 | return -EROFS; | 532 | return -EROFS; |
485 | } | 533 | } |
486 | 534 | ||
535 | if (ubi->nor_flash) { | ||
536 | err = nor_erase_prepare(ubi, pnum); | ||
537 | if (err) | ||
538 | return err; | ||
539 | } | ||
540 | |||
487 | if (torture) { | 541 | if (torture) { |
488 | ret = torture_peb(ubi, pnum); | 542 | ret = torture_peb(ubi, pnum); |
489 | if (ret < 0) | 543 | if (ret < 0) |
diff --git a/drivers/mtd/ubi/ubi.h b/drivers/mtd/ubi/ubi.h index 64604e8809ec..6a5fe9633783 100644 --- a/drivers/mtd/ubi/ubi.h +++ b/drivers/mtd/ubi/ubi.h | |||
@@ -373,6 +373,7 @@ struct ubi_wl_entry; | |||
373 | * @vid_hdr_shift: contains @vid_hdr_offset - @vid_hdr_aloffset | 373 | * @vid_hdr_shift: contains @vid_hdr_offset - @vid_hdr_aloffset |
374 | * @bad_allowed: whether the MTD device admits of bad physical eraseblocks or | 374 | * @bad_allowed: whether the MTD device admits of bad physical eraseblocks or |
375 | * not | 375 | * not |
376 | * @nor_flash: non-zero if working on top of NOR flash | ||
376 | * @mtd: MTD device descriptor | 377 | * @mtd: MTD device descriptor |
377 | * | 378 | * |
378 | * @peb_buf1: a buffer of PEB size used for different purposes | 379 | * @peb_buf1: a buffer of PEB size used for different purposes |
@@ -454,7 +455,8 @@ struct ubi_device { | |||
454 | int vid_hdr_offset; | 455 | int vid_hdr_offset; |
455 | int vid_hdr_aloffset; | 456 | int vid_hdr_aloffset; |
456 | int vid_hdr_shift; | 457 | int vid_hdr_shift; |
457 | int bad_allowed; | 458 | unsigned int bad_allowed:1; |
459 | unsigned int nor_flash:1; | ||
458 | struct mtd_info *mtd; | 460 | struct mtd_info *mtd; |
459 | 461 | ||
460 | void *peb_buf1; | 462 | void *peb_buf1; |