diff options
author | Thomas Gleixner <tglx@cruncher.tec.linutronix.de> | 2006-05-29 18:37:34 -0400 |
---|---|---|
committer | Thomas Gleixner <tglx@cruncher.tec.linutronix.de> | 2006-05-29 18:37:34 -0400 |
commit | f1a28c02843efcfcc41982149880bac3ac180234 (patch) | |
tree | b15ca1a140e463ef3cde6b9a8591e7be172ee1f1 /drivers/mtd/mtdconcat.c | |
parent | 9a1fcdfd4bee27c418424cac47abf7c049541297 (diff) |
[MTD] NAND Expose the new raw mode function and status info to userspace
The raw read/write access to NAND (without ECC) has been changed in the
NAND rework. Expose the new way - setting the file mode via ioctl - to
userspace. Also allow to read out the ecc statistics information so userspace
tools can see that bitflips happened and whether errors where correctable
or not. Also expose the number of bad blocks for the partition, so nandwrite
can check if the data fits into the parition before writing to it.
Signed-off-by: Thomas Gleixner <tglx@linutronix.de>
Diffstat (limited to 'drivers/mtd/mtdconcat.c')
-rw-r--r-- | drivers/mtd/mtdconcat.c | 51 |
1 files changed, 35 insertions, 16 deletions
diff --git a/drivers/mtd/mtdconcat.c b/drivers/mtd/mtdconcat.c index 3c8d5e6fa010..1fea631b5852 100644 --- a/drivers/mtd/mtdconcat.c +++ b/drivers/mtd/mtdconcat.c | |||
@@ -56,7 +56,7 @@ concat_read(struct mtd_info *mtd, loff_t from, size_t len, | |||
56 | size_t * retlen, u_char * buf) | 56 | size_t * retlen, u_char * buf) |
57 | { | 57 | { |
58 | struct mtd_concat *concat = CONCAT(mtd); | 58 | struct mtd_concat *concat = CONCAT(mtd); |
59 | int ret = 0, err = -EINVAL; | 59 | int ret = 0, err; |
60 | int i; | 60 | int i; |
61 | 61 | ||
62 | *retlen = 0; | 62 | *retlen = 0; |
@@ -80,28 +80,29 @@ concat_read(struct mtd_info *mtd, loff_t from, size_t len, | |||
80 | 80 | ||
81 | err = subdev->read(subdev, from, size, &retsize, buf); | 81 | err = subdev->read(subdev, from, size, &retsize, buf); |
82 | 82 | ||
83 | if (err && (err != -EBADMSG) && (err != -EUCLEAN)) | ||
84 | break; | ||
85 | |||
86 | /* Save information about bitflips! */ | 83 | /* Save information about bitflips! */ |
87 | if (err) { | 84 | if (unlikely(err)) { |
88 | if (err == -EBADMSG) | 85 | if (err == -EBADMSG) { |
89 | ret = err; | 86 | mtd->ecc_stats.failed++; |
90 | else if (!ret) | ||
91 | ret = err; | 87 | ret = err; |
92 | err = 0; | 88 | } else if (err == -EUCLEAN) { |
89 | mtd->ecc_stats.corrected++; | ||
90 | /* Do not overwrite -EBADMSG !! */ | ||
91 | if (!ret) | ||
92 | ret = err; | ||
93 | } else | ||
94 | return err; | ||
93 | } | 95 | } |
94 | 96 | ||
95 | *retlen += retsize; | 97 | *retlen += retsize; |
96 | len -= size; | 98 | len -= size; |
97 | if (len == 0) | 99 | if (len == 0) |
98 | break; | 100 | return ret; |
99 | 101 | ||
100 | err = -EINVAL; | ||
101 | buf += size; | 102 | buf += size; |
102 | from = 0; | 103 | from = 0; |
103 | } | 104 | } |
104 | return err ? err : ret; | 105 | return -EINVAL; |
105 | } | 106 | } |
106 | 107 | ||
107 | static int | 108 | static int |
@@ -244,7 +245,7 @@ concat_read_oob(struct mtd_info *mtd, loff_t from, struct mtd_oob_ops *ops) | |||
244 | { | 245 | { |
245 | struct mtd_concat *concat = CONCAT(mtd); | 246 | struct mtd_concat *concat = CONCAT(mtd); |
246 | struct mtd_oob_ops devops = *ops; | 247 | struct mtd_oob_ops devops = *ops; |
247 | int i, err; | 248 | int i, err, ret = 0; |
248 | 249 | ||
249 | ops->retlen = 0; | 250 | ops->retlen = 0; |
250 | 251 | ||
@@ -262,12 +263,24 @@ concat_read_oob(struct mtd_info *mtd, loff_t from, struct mtd_oob_ops *ops) | |||
262 | 263 | ||
263 | err = subdev->read_oob(subdev, from, &devops); | 264 | err = subdev->read_oob(subdev, from, &devops); |
264 | ops->retlen += devops.retlen; | 265 | ops->retlen += devops.retlen; |
265 | if (err) | 266 | |
266 | return err; | 267 | /* Save information about bitflips! */ |
268 | if (unlikely(err)) { | ||
269 | if (err == -EBADMSG) { | ||
270 | mtd->ecc_stats.failed++; | ||
271 | ret = err; | ||
272 | } else if (err == -EUCLEAN) { | ||
273 | mtd->ecc_stats.corrected++; | ||
274 | /* Do not overwrite -EBADMSG !! */ | ||
275 | if (!ret) | ||
276 | ret = err; | ||
277 | } else | ||
278 | return err; | ||
279 | } | ||
267 | 280 | ||
268 | devops.len = ops->len - ops->retlen; | 281 | devops.len = ops->len - ops->retlen; |
269 | if (!devops.len) | 282 | if (!devops.len) |
270 | return 0; | 283 | return ret; |
271 | 284 | ||
272 | if (devops.datbuf) | 285 | if (devops.datbuf) |
273 | devops.datbuf += devops.retlen; | 286 | devops.datbuf += devops.retlen; |
@@ -655,6 +668,8 @@ static int concat_block_markbad(struct mtd_info *mtd, loff_t ofs) | |||
655 | } | 668 | } |
656 | 669 | ||
657 | err = subdev->block_markbad(subdev, ofs); | 670 | err = subdev->block_markbad(subdev, ofs); |
671 | if (!err) | ||
672 | mtd->ecc_stats.badblocks++; | ||
658 | break; | 673 | break; |
659 | } | 674 | } |
660 | 675 | ||
@@ -717,6 +732,8 @@ struct mtd_info *mtd_concat_create(struct mtd_info *subdev[], /* subdevices to c | |||
717 | if (subdev[0]->block_markbad) | 732 | if (subdev[0]->block_markbad) |
718 | concat->mtd.block_markbad = concat_block_markbad; | 733 | concat->mtd.block_markbad = concat_block_markbad; |
719 | 734 | ||
735 | concat->mtd.ecc_stats.badblocks = subdev[0]->ecc_stats.badblocks; | ||
736 | |||
720 | concat->subdev[0] = subdev[0]; | 737 | concat->subdev[0] = subdev[0]; |
721 | 738 | ||
722 | for (i = 1; i < num_devs; i++) { | 739 | for (i = 1; i < num_devs; i++) { |
@@ -744,6 +761,8 @@ struct mtd_info *mtd_concat_create(struct mtd_info *subdev[], /* subdevices to c | |||
744 | subdev[i]->flags & MTD_WRITEABLE; | 761 | subdev[i]->flags & MTD_WRITEABLE; |
745 | } | 762 | } |
746 | concat->mtd.size += subdev[i]->size; | 763 | concat->mtd.size += subdev[i]->size; |
764 | concat->mtd.ecc_stats.badblocks += | ||
765 | subdev[i]->ecc_stats.badblocks; | ||
747 | if (concat->mtd.writesize != subdev[i]->writesize || | 766 | if (concat->mtd.writesize != subdev[i]->writesize || |
748 | concat->mtd.oobsize != subdev[i]->oobsize || | 767 | concat->mtd.oobsize != subdev[i]->oobsize || |
749 | concat->mtd.ecctype != subdev[i]->ecctype || | 768 | concat->mtd.ecctype != subdev[i]->ecctype || |