diff options
author | Brian Norris <computersforpeace@gmail.com> | 2011-08-30 21:45:38 -0400 |
---|---|---|
committer | Artem Bityutskiy <artem.bityutskiy@intel.com> | 2011-09-11 08:13:38 -0400 |
commit | c46f6483d21e93400e4a110de7902830173d53b0 (patch) | |
tree | bac45575c58963f4bc35d13b188c8ff5a32c9823 | |
parent | e9195edc59f33e9cabdd32a2959e927806670f45 (diff) |
mtd: support reading OOB without ECC
This fixes issues with `nanddump -n' and the MEMREADOOB[64] ioctls on
hardware that performs error correction when reading only OOB data. A
driver for such hardware needs to know when we're doing a RAW vs. a
normal write, but mtd_do_read_oob does not pass such information to the
lower layers (e.g., NAND). We should pass MTD_OOB_RAW or MTD_OOB_PLACE
based on the MTD file mode.
For now, most drivers can get away with just setting:
chip->ecc.read_oob_raw = chip->ecc.read_oob
This is done by default; but for systems that behave as described above,
you must supply your own replacement function.
This was tested with nandsim as well as on actual SLC NAND.
Signed-off-by: Brian Norris <computersforpeace@gmail.com>
Cc: Jim Quinlan <jim2101024@gmail.com>
Signed-off-by: Artem Bityutskiy <artem.bityutskiy@intel.com>
-rw-r--r-- | drivers/mtd/mtdchar.c | 14 | ||||
-rw-r--r-- | drivers/mtd/nand/nand_base.c | 7 | ||||
-rw-r--r-- | include/linux/mtd/nand.h | 3 |
3 files changed, 17 insertions, 7 deletions
diff --git a/drivers/mtd/mtdchar.c b/drivers/mtd/mtdchar.c index bcb7f05fd27b..d0eaef67b9bb 100644 --- a/drivers/mtd/mtdchar.c +++ b/drivers/mtd/mtdchar.c | |||
@@ -435,9 +435,11 @@ static int mtd_do_writeoob(struct file *file, struct mtd_info *mtd, | |||
435 | return ret; | 435 | return ret; |
436 | } | 436 | } |
437 | 437 | ||
438 | static int mtd_do_readoob(struct mtd_info *mtd, uint64_t start, | 438 | static int mtd_do_readoob(struct file *file, struct mtd_info *mtd, |
439 | uint32_t length, void __user *ptr, uint32_t __user *retp) | 439 | uint64_t start, uint32_t length, void __user *ptr, |
440 | uint32_t __user *retp) | ||
440 | { | 441 | { |
442 | struct mtd_file_info *mfi = file->private_data; | ||
441 | struct mtd_oob_ops ops; | 443 | struct mtd_oob_ops ops; |
442 | int ret = 0; | 444 | int ret = 0; |
443 | 445 | ||
@@ -455,7 +457,7 @@ static int mtd_do_readoob(struct mtd_info *mtd, uint64_t start, | |||
455 | ops.ooblen = length; | 457 | ops.ooblen = length; |
456 | ops.ooboffs = start & (mtd->writesize - 1); | 458 | ops.ooboffs = start & (mtd->writesize - 1); |
457 | ops.datbuf = NULL; | 459 | ops.datbuf = NULL; |
458 | ops.mode = MTD_OOB_PLACE; | 460 | ops.mode = (mfi->mode == MTD_MODE_RAW) ? MTD_OOB_RAW : MTD_OOB_PLACE; |
459 | 461 | ||
460 | if (ops.ooboffs && ops.ooblen > (mtd->oobsize - ops.ooboffs)) | 462 | if (ops.ooboffs && ops.ooblen > (mtd->oobsize - ops.ooboffs)) |
461 | return -EINVAL; | 463 | return -EINVAL; |
@@ -716,7 +718,7 @@ static int mtd_ioctl(struct file *file, u_int cmd, u_long arg) | |||
716 | if (copy_from_user(&buf, argp, sizeof(buf))) | 718 | if (copy_from_user(&buf, argp, sizeof(buf))) |
717 | ret = -EFAULT; | 719 | ret = -EFAULT; |
718 | else | 720 | else |
719 | ret = mtd_do_readoob(mtd, buf.start, buf.length, | 721 | ret = mtd_do_readoob(file, mtd, buf.start, buf.length, |
720 | buf.ptr, &buf_user->start); | 722 | buf.ptr, &buf_user->start); |
721 | break; | 723 | break; |
722 | } | 724 | } |
@@ -743,7 +745,7 @@ static int mtd_ioctl(struct file *file, u_int cmd, u_long arg) | |||
743 | if (copy_from_user(&buf, argp, sizeof(buf))) | 745 | if (copy_from_user(&buf, argp, sizeof(buf))) |
744 | ret = -EFAULT; | 746 | ret = -EFAULT; |
745 | else | 747 | else |
746 | ret = mtd_do_readoob(mtd, buf.start, buf.length, | 748 | ret = mtd_do_readoob(file, mtd, buf.start, buf.length, |
747 | (void __user *)(uintptr_t)buf.usr_ptr, | 749 | (void __user *)(uintptr_t)buf.usr_ptr, |
748 | &buf_user->length); | 750 | &buf_user->length); |
749 | break; | 751 | break; |
@@ -1029,7 +1031,7 @@ static long mtd_compat_ioctl(struct file *file, unsigned int cmd, | |||
1029 | if (copy_from_user(&buf, argp, sizeof(buf))) | 1031 | if (copy_from_user(&buf, argp, sizeof(buf))) |
1030 | ret = -EFAULT; | 1032 | ret = -EFAULT; |
1031 | else | 1033 | else |
1032 | ret = mtd_do_readoob(mtd, buf.start, | 1034 | ret = mtd_do_readoob(file, mtd, buf.start, |
1033 | buf.length, compat_ptr(buf.ptr), | 1035 | buf.length, compat_ptr(buf.ptr), |
1034 | &buf_user->start); | 1036 | &buf_user->start); |
1035 | break; | 1037 | break; |
diff --git a/drivers/mtd/nand/nand_base.c b/drivers/mtd/nand/nand_base.c index b61a7c7bd097..ad40607f5f24 100644 --- a/drivers/mtd/nand/nand_base.c +++ b/drivers/mtd/nand/nand_base.c | |||
@@ -1787,7 +1787,10 @@ static int nand_do_read_oob(struct mtd_info *mtd, loff_t from, | |||
1787 | page = realpage & chip->pagemask; | 1787 | page = realpage & chip->pagemask; |
1788 | 1788 | ||
1789 | while (1) { | 1789 | while (1) { |
1790 | sndcmd = chip->ecc.read_oob(mtd, chip, page, sndcmd); | 1790 | if (ops->mode == MTD_OOB_RAW) |
1791 | sndcmd = chip->ecc.read_oob_raw(mtd, chip, page, sndcmd); | ||
1792 | else | ||
1793 | sndcmd = chip->ecc.read_oob(mtd, chip, page, sndcmd); | ||
1791 | 1794 | ||
1792 | len = min(len, readlen); | 1795 | len = min(len, readlen); |
1793 | buf = nand_transfer_oob(chip, buf, ops, len); | 1796 | buf = nand_transfer_oob(chip, buf, ops, len); |
@@ -3385,6 +3388,8 @@ int nand_scan_tail(struct mtd_info *mtd) | |||
3385 | } | 3388 | } |
3386 | 3389 | ||
3387 | /* For many systems, the standard OOB write also works for raw */ | 3390 | /* For many systems, the standard OOB write also works for raw */ |
3391 | if (!chip->ecc.read_oob_raw) | ||
3392 | chip->ecc.read_oob_raw = chip->ecc.read_oob; | ||
3388 | if (!chip->ecc.write_oob_raw) | 3393 | if (!chip->ecc.write_oob_raw) |
3389 | chip->ecc.write_oob_raw = chip->ecc.write_oob; | 3394 | chip->ecc.write_oob_raw = chip->ecc.write_oob; |
3390 | 3395 | ||
diff --git a/include/linux/mtd/nand.h b/include/linux/mtd/nand.h index c7113a9cd66d..0b3d464cba13 100644 --- a/include/linux/mtd/nand.h +++ b/include/linux/mtd/nand.h | |||
@@ -341,6 +341,7 @@ struct nand_hw_control { | |||
341 | * @write_page: function to write a page according to the ECC generator | 341 | * @write_page: function to write a page according to the ECC generator |
342 | * requirements. | 342 | * requirements. |
343 | * @write_oob_raw: function to write chip OOB data without ECC | 343 | * @write_oob_raw: function to write chip OOB data without ECC |
344 | * @read_oob_raw: function to read chip OOB data without ECC | ||
344 | * @read_oob: function to read chip OOB data | 345 | * @read_oob: function to read chip OOB data |
345 | * @write_oob: function to write chip OOB data | 346 | * @write_oob: function to write chip OOB data |
346 | */ | 347 | */ |
@@ -371,6 +372,8 @@ struct nand_ecc_ctrl { | |||
371 | const uint8_t *buf); | 372 | const uint8_t *buf); |
372 | int (*write_oob_raw)(struct mtd_info *mtd, struct nand_chip *chip, | 373 | int (*write_oob_raw)(struct mtd_info *mtd, struct nand_chip *chip, |
373 | int page); | 374 | int page); |
375 | int (*read_oob_raw)(struct mtd_info *mtd, struct nand_chip *chip, | ||
376 | int page, int sndcmd); | ||
374 | int (*read_oob)(struct mtd_info *mtd, struct nand_chip *chip, int page, | 377 | int (*read_oob)(struct mtd_info *mtd, struct nand_chip *chip, int page, |
375 | int sndcmd); | 378 | int sndcmd); |
376 | int (*write_oob)(struct mtd_info *mtd, struct nand_chip *chip, | 379 | int (*write_oob)(struct mtd_info *mtd, struct nand_chip *chip, |