aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorThomas Gleixner <tglx@cruncher.tec.linutronix.de>2006-05-29 18:37:34 -0400
committerThomas Gleixner <tglx@cruncher.tec.linutronix.de>2006-05-29 18:37:34 -0400
commitf1a28c02843efcfcc41982149880bac3ac180234 (patch)
treeb15ca1a140e463ef3cde6b9a8591e7be172ee1f1
parent9a1fcdfd4bee27c418424cac47abf7c049541297 (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>
-rw-r--r--drivers/mtd/mtdchar.c200
-rw-r--r--drivers/mtd/mtdconcat.c51
-rw-r--r--drivers/mtd/mtdpart.c39
-rw-r--r--drivers/mtd/nand/nand_base.c26
-rw-r--r--drivers/mtd/nand/nand_bbt.c3
-rw-r--r--include/linux/mtd/mtd.h11
-rw-r--r--include/mtd/mtd-abi.h27
7 files changed, 259 insertions, 98 deletions
diff --git a/drivers/mtd/mtdchar.c b/drivers/mtd/mtdchar.c
index a48210d58b92..fdc535b22e39 100644
--- a/drivers/mtd/mtdchar.c
+++ b/drivers/mtd/mtdchar.c
@@ -49,24 +49,18 @@ static struct mtd_notifier notifier = {
49}; 49};
50 50
51/* 51/*
52 * We use file->private_data to store a pointer to the MTDdevice. 52 * Data structure to hold the pointer to the mtd device as well
53 * Since alighment is at least 32 bits, we have 2 bits free for OTP 53 * as mode information ofr various use cases.
54 * modes as well.
55 */ 54 */
56 55struct mtd_file_info {
57#define TO_MTD(file) (struct mtd_info *)((long)((file)->private_data) & ~3L) 56 struct mtd_info *mtd;
58 57 enum mtd_file_modes mode;
59#define MTD_MODE_OTP_FACT 1 58};
60#define MTD_MODE_OTP_USER 2
61#define MTD_MODE(file) ((long)((file)->private_data) & 3)
62
63#define SET_MTD_MODE(file, mode) \
64 do { long __p = (long)((file)->private_data); \
65 (file)->private_data = (void *)((__p & ~3L) | mode); } while (0)
66 59
67static loff_t mtd_lseek (struct file *file, loff_t offset, int orig) 60static loff_t mtd_lseek (struct file *file, loff_t offset, int orig)
68{ 61{
69 struct mtd_info *mtd = TO_MTD(file); 62 struct mtd_file_info *mfi = file->private_data;
63 struct mtd_info *mtd = mfi->mtd;
70 64
71 switch (orig) { 65 switch (orig) {
72 case 0: 66 case 0:
@@ -97,6 +91,7 @@ static int mtd_open(struct inode *inode, struct file *file)
97 int minor = iminor(inode); 91 int minor = iminor(inode);
98 int devnum = minor >> 1; 92 int devnum = minor >> 1;
99 struct mtd_info *mtd; 93 struct mtd_info *mtd;
94 struct mtd_file_info *mfi;
100 95
101 DEBUG(MTD_DEBUG_LEVEL0, "MTD_open\n"); 96 DEBUG(MTD_DEBUG_LEVEL0, "MTD_open\n");
102 97
@@ -117,14 +112,20 @@ static int mtd_open(struct inode *inode, struct file *file)
117 return -ENODEV; 112 return -ENODEV;
118 } 113 }
119 114
120 file->private_data = mtd;
121
122 /* You can't open it RW if it's not a writeable device */ 115 /* You can't open it RW if it's not a writeable device */
123 if ((file->f_mode & 2) && !(mtd->flags & MTD_WRITEABLE)) { 116 if ((file->f_mode & 2) && !(mtd->flags & MTD_WRITEABLE)) {
124 put_mtd_device(mtd); 117 put_mtd_device(mtd);
125 return -EACCES; 118 return -EACCES;
126 } 119 }
127 120
121 mfi = kzalloc(sizeof(*mfi), GFP_KERNEL);
122 if (!mfi) {
123 put_mtd_device(mtd);
124 return -ENOMEM;
125 }
126 mfi->mtd = mtd;
127 file->private_data = mfi;
128
128 return 0; 129 return 0;
129} /* mtd_open */ 130} /* mtd_open */
130 131
@@ -132,16 +133,17 @@ static int mtd_open(struct inode *inode, struct file *file)
132 133
133static int mtd_close(struct inode *inode, struct file *file) 134static int mtd_close(struct inode *inode, struct file *file)
134{ 135{
135 struct mtd_info *mtd; 136 struct mtd_file_info *mfi = file->private_data;
137 struct mtd_info *mtd = mfi->mtd;
136 138
137 DEBUG(MTD_DEBUG_LEVEL0, "MTD_close\n"); 139 DEBUG(MTD_DEBUG_LEVEL0, "MTD_close\n");
138 140
139 mtd = TO_MTD(file);
140
141 if (mtd->sync) 141 if (mtd->sync)
142 mtd->sync(mtd); 142 mtd->sync(mtd);
143 143
144 put_mtd_device(mtd); 144 put_mtd_device(mtd);
145 file->private_data = NULL;
146 kfree(mfi);
145 147
146 return 0; 148 return 0;
147} /* mtd_close */ 149} /* mtd_close */
@@ -153,7 +155,8 @@ static int mtd_close(struct inode *inode, struct file *file)
153 155
154static ssize_t mtd_read(struct file *file, char __user *buf, size_t count,loff_t *ppos) 156static ssize_t mtd_read(struct file *file, char __user *buf, size_t count,loff_t *ppos)
155{ 157{
156 struct mtd_info *mtd = TO_MTD(file); 158 struct mtd_file_info *mfi = file->private_data;
159 struct mtd_info *mtd = mfi->mtd;
157 size_t retlen=0; 160 size_t retlen=0;
158 size_t total_retlen=0; 161 size_t total_retlen=0;
159 int ret=0; 162 int ret=0;
@@ -186,13 +189,26 @@ static ssize_t mtd_read(struct file *file, char __user *buf, size_t count,loff_t
186 else 189 else
187 len = count; 190 len = count;
188 191
189 switch (MTD_MODE(file)) { 192 switch (mfi->mode) {
190 case MTD_MODE_OTP_FACT: 193 case MTD_MODE_OTP_FACTORY:
191 ret = mtd->read_fact_prot_reg(mtd, *ppos, len, &retlen, kbuf); 194 ret = mtd->read_fact_prot_reg(mtd, *ppos, len, &retlen, kbuf);
192 break; 195 break;
193 case MTD_MODE_OTP_USER: 196 case MTD_MODE_OTP_USER:
194 ret = mtd->read_user_prot_reg(mtd, *ppos, len, &retlen, kbuf); 197 ret = mtd->read_user_prot_reg(mtd, *ppos, len, &retlen, kbuf);
195 break; 198 break;
199 case MTD_MODE_RAW:
200 {
201 struct mtd_oob_ops ops;
202
203 ops.mode = MTD_OOB_RAW;
204 ops.datbuf = kbuf;
205 ops.oobbuf = NULL;
206 ops.len = len;
207
208 ret = mtd->read_oob(mtd, *ppos, &ops);
209 retlen = ops.retlen;
210 break;
211 }
196 default: 212 default:
197 ret = mtd->read(mtd, *ppos, len, &retlen, kbuf); 213 ret = mtd->read(mtd, *ppos, len, &retlen, kbuf);
198 } 214 }
@@ -232,7 +248,8 @@ static ssize_t mtd_read(struct file *file, char __user *buf, size_t count,loff_t
232 248
233static ssize_t mtd_write(struct file *file, const char __user *buf, size_t count,loff_t *ppos) 249static ssize_t mtd_write(struct file *file, const char __user *buf, size_t count,loff_t *ppos)
234{ 250{
235 struct mtd_info *mtd = TO_MTD(file); 251 struct mtd_file_info *mfi = file->private_data;
252 struct mtd_info *mtd = mfi->mtd;
236 char *kbuf; 253 char *kbuf;
237 size_t retlen; 254 size_t retlen;
238 size_t total_retlen=0; 255 size_t total_retlen=0;
@@ -270,8 +287,8 @@ static ssize_t mtd_write(struct file *file, const char __user *buf, size_t count
270 return -EFAULT; 287 return -EFAULT;
271 } 288 }
272 289
273 switch (MTD_MODE(file)) { 290 switch (mfi->mode) {
274 case MTD_MODE_OTP_FACT: 291 case MTD_MODE_OTP_FACTORY:
275 ret = -EROFS; 292 ret = -EROFS;
276 break; 293 break;
277 case MTD_MODE_OTP_USER: 294 case MTD_MODE_OTP_USER:
@@ -281,6 +298,21 @@ static ssize_t mtd_write(struct file *file, const char __user *buf, size_t count
281 } 298 }
282 ret = mtd->write_user_prot_reg(mtd, *ppos, len, &retlen, kbuf); 299 ret = mtd->write_user_prot_reg(mtd, *ppos, len, &retlen, kbuf);
283 break; 300 break;
301
302 case MTD_MODE_RAW:
303 {
304 struct mtd_oob_ops ops;
305
306 ops.mode = MTD_OOB_RAW;
307 ops.datbuf = kbuf;
308 ops.oobbuf = NULL;
309 ops.len = len;
310
311 ret = mtd->write_oob(mtd, *ppos, &ops);
312 retlen = ops.retlen;
313 break;
314 }
315
284 default: 316 default:
285 ret = (*(mtd->write))(mtd, *ppos, len, &retlen, kbuf); 317 ret = (*(mtd->write))(mtd, *ppos, len, &retlen, kbuf);
286 } 318 }
@@ -310,10 +342,41 @@ static void mtdchar_erase_callback (struct erase_info *instr)
310 wake_up((wait_queue_head_t *)instr->priv); 342 wake_up((wait_queue_head_t *)instr->priv);
311} 343}
312 344
345#if defined(CONFIG_MTD_OTP) || defined(CONFIG_MTD_ONENAND_OTP)
346static int otp_select_filemode(struct mtd_file_info *mfi, int mode)
347{
348 struct mtd_info *mtd = mfi->mtd;
349 int ret = 0;
350
351 switch (mode) {
352 case MTD_OTP_FACTORY:
353 if (!mtd->read_fact_prot_reg)
354 ret = -EOPNOTSUPP;
355 else
356 mfi->mode = MTD_MODE_OTP_FACTORY;
357 break;
358 case MTD_OTP_USER:
359 if (!mtd->read_fact_prot_reg)
360 ret = -EOPNOTSUPP;
361 else
362 mfi->mode = MTD_MODE_OTP_USER;
363 break;
364 default:
365 ret = -EINVAL;
366 case MTD_OTP_OFF:
367 break;
368 }
369 return ret;
370}
371#else
372# define otp_select_filemode(f,m) -EOPNOTSUPP
373#endif
374
313static int mtd_ioctl(struct inode *inode, struct file *file, 375static int mtd_ioctl(struct inode *inode, struct file *file,
314 u_int cmd, u_long arg) 376 u_int cmd, u_long arg)
315{ 377{
316 struct mtd_info *mtd = TO_MTD(file); 378 struct mtd_file_info *mfi = file->private_data;
379 struct mtd_info *mtd = mfi->mtd;
317 void __user *argp = (void __user *)arg; 380 void __user *argp = (void __user *)arg;
318 int ret = 0; 381 int ret = 0;
319 u_long size; 382 u_long size;
@@ -554,16 +617,6 @@ static int mtd_ioctl(struct inode *inode, struct file *file,
554 break; 617 break;
555 } 618 }
556 619
557 case ECCGETLAYOUT:
558
559 if (!mtd->ecclayout)
560 return -EOPNOTSUPP;
561
562 if (copy_to_user(argp, &mtd->ecclayout,
563 sizeof(struct nand_ecclayout)))
564 return -EFAULT;
565 break;
566
567 case MEMGETBADBLOCK: 620 case MEMGETBADBLOCK:
568 { 621 {
569 loff_t offs; 622 loff_t offs;
@@ -596,25 +649,11 @@ static int mtd_ioctl(struct inode *inode, struct file *file,
596 int mode; 649 int mode;
597 if (copy_from_user(&mode, argp, sizeof(int))) 650 if (copy_from_user(&mode, argp, sizeof(int)))
598 return -EFAULT; 651 return -EFAULT;
599 SET_MTD_MODE(file, 0); 652
600 switch (mode) { 653 mfi->mode = MTD_MODE_NORMAL;
601 case MTD_OTP_FACTORY: 654
602 if (!mtd->read_fact_prot_reg) 655 ret = otp_select_filemode(mfi, mode);
603 ret = -EOPNOTSUPP; 656
604 else
605 SET_MTD_MODE(file, MTD_MODE_OTP_FACT);
606 break;
607 case MTD_OTP_USER:
608 if (!mtd->read_fact_prot_reg)
609 ret = -EOPNOTSUPP;
610 else
611 SET_MTD_MODE(file, MTD_MODE_OTP_USER);
612 break;
613 default:
614 ret = -EINVAL;
615 case MTD_OTP_OFF:
616 break;
617 }
618 file->f_pos = 0; 657 file->f_pos = 0;
619 break; 658 break;
620 } 659 }
@@ -626,8 +665,8 @@ static int mtd_ioctl(struct inode *inode, struct file *file,
626 if (!buf) 665 if (!buf)
627 return -ENOMEM; 666 return -ENOMEM;
628 ret = -EOPNOTSUPP; 667 ret = -EOPNOTSUPP;
629 switch (MTD_MODE(file)) { 668 switch (mfi->mode) {
630 case MTD_MODE_OTP_FACT: 669 case MTD_MODE_OTP_FACTORY:
631 if (mtd->get_fact_prot_info) 670 if (mtd->get_fact_prot_info)
632 ret = mtd->get_fact_prot_info(mtd, buf, 4096); 671 ret = mtd->get_fact_prot_info(mtd, buf, 4096);
633 break; 672 break;
@@ -635,6 +674,8 @@ static int mtd_ioctl(struct inode *inode, struct file *file,
635 if (mtd->get_user_prot_info) 674 if (mtd->get_user_prot_info)
636 ret = mtd->get_user_prot_info(mtd, buf, 4096); 675 ret = mtd->get_user_prot_info(mtd, buf, 4096);
637 break; 676 break;
677 default:
678 break;
638 } 679 }
639 if (ret >= 0) { 680 if (ret >= 0) {
640 if (cmd == OTPGETREGIONCOUNT) { 681 if (cmd == OTPGETREGIONCOUNT) {
@@ -653,7 +694,7 @@ static int mtd_ioctl(struct inode *inode, struct file *file,
653 { 694 {
654 struct otp_info info; 695 struct otp_info info;
655 696
656 if (MTD_MODE(file) != MTD_MODE_OTP_USER) 697 if (mfi->mode != MTD_MODE_OTP_USER)
657 return -EINVAL; 698 return -EINVAL;
658 if (copy_from_user(&info, argp, sizeof(info))) 699 if (copy_from_user(&info, argp, sizeof(info)))
659 return -EFAULT; 700 return -EFAULT;
@@ -664,6 +705,49 @@ static int mtd_ioctl(struct inode *inode, struct file *file,
664 } 705 }
665#endif 706#endif
666 707
708 case ECCGETLAYOUT:
709 {
710 if (!mtd->ecclayout)
711 return -EOPNOTSUPP;
712
713 if (copy_to_user(argp, &mtd->ecclayout,
714 sizeof(struct nand_ecclayout)))
715 return -EFAULT;
716 break;
717 }
718
719 case ECCGETSTATS:
720 {
721 if (copy_to_user(argp, &mtd->ecc_stats,
722 sizeof(struct mtd_ecc_stats)))
723 return -EFAULT;
724 break;
725 }
726
727 case MTDFILEMODE:
728 {
729 mfi->mode = 0;
730
731 switch(arg) {
732 case MTD_MODE_OTP_FACTORY:
733 case MTD_MODE_OTP_USER:
734 ret = otp_select_filemode(mfi, arg);
735 break;
736
737 case MTD_MODE_RAW:
738 if (!mtd->read_oob || !mtd->write_oob)
739 return -EOPNOTSUPP;
740 mfi->mode = arg;
741
742 case MTD_MODE_NORMAL:
743 break;
744 default:
745 ret = -EINVAL;
746 }
747 file->f_pos = 0;
748 break;
749 }
750
667 default: 751 default:
668 ret = -ENOTTY; 752 ret = -ENOTTY;
669 } 753 }
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
107static int 108static 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 ||
diff --git a/drivers/mtd/mtdpart.c b/drivers/mtd/mtdpart.c
index f22aeccf01e7..77a7123a5c56 100644
--- a/drivers/mtd/mtdpart.c
+++ b/drivers/mtd/mtdpart.c
@@ -51,12 +51,21 @@ static int part_read (struct mtd_info *mtd, loff_t from, size_t len,
51 size_t *retlen, u_char *buf) 51 size_t *retlen, u_char *buf)
52{ 52{
53 struct mtd_part *part = PART(mtd); 53 struct mtd_part *part = PART(mtd);
54 int res;
55
54 if (from >= mtd->size) 56 if (from >= mtd->size)
55 len = 0; 57 len = 0;
56 else if (from + len > mtd->size) 58 else if (from + len > mtd->size)
57 len = mtd->size - from; 59 len = mtd->size - from;
58 return part->master->read (part->master, from + part->offset, 60 res = part->master->read (part->master, from + part->offset,
59 len, retlen, buf); 61 len, retlen, buf);
62 if (unlikely(res)) {
63 if (res == -EUCLEAN)
64 mtd->ecc_stats.corrected++;
65 if (res == -EBADMSG)
66 mtd->ecc_stats.failed++;
67 }
68 return res;
60} 69}
61 70
62static int part_point (struct mtd_info *mtd, loff_t from, size_t len, 71static int part_point (struct mtd_info *mtd, loff_t from, size_t len,
@@ -82,12 +91,21 @@ static int part_read_oob(struct mtd_info *mtd, loff_t from,
82 struct mtd_oob_ops *ops) 91 struct mtd_oob_ops *ops)
83{ 92{
84 struct mtd_part *part = PART(mtd); 93 struct mtd_part *part = PART(mtd);
94 int res;
85 95
86 if (from >= mtd->size) 96 if (from >= mtd->size)
87 return -EINVAL; 97 return -EINVAL;
88 if (from + ops->len > mtd->size) 98 if (from + ops->len > mtd->size)
89 return -EINVAL; 99 return -EINVAL;
90 return part->master->read_oob(part->master, from + part->offset, ops); 100 res = part->master->read_oob(part->master, from + part->offset, ops);
101
102 if (unlikely(res)) {
103 if (res == -EUCLEAN)
104 mtd->ecc_stats.corrected++;
105 if (res == -EBADMSG)
106 mtd->ecc_stats.failed++;
107 }
108 return res;
91} 109}
92 110
93static int part_read_user_prot_reg (struct mtd_info *mtd, loff_t from, size_t len, 111static int part_read_user_prot_reg (struct mtd_info *mtd, loff_t from, size_t len,
@@ -246,12 +264,17 @@ static int part_block_isbad (struct mtd_info *mtd, loff_t ofs)
246static int part_block_markbad (struct mtd_info *mtd, loff_t ofs) 264static int part_block_markbad (struct mtd_info *mtd, loff_t ofs)
247{ 265{
248 struct mtd_part *part = PART(mtd); 266 struct mtd_part *part = PART(mtd);
267 int res;
268
249 if (!(mtd->flags & MTD_WRITEABLE)) 269 if (!(mtd->flags & MTD_WRITEABLE))
250 return -EROFS; 270 return -EROFS;
251 if (ofs >= mtd->size) 271 if (ofs >= mtd->size)
252 return -EINVAL; 272 return -EINVAL;
253 ofs += part->offset; 273 ofs += part->offset;
254 return part->master->block_markbad(part->master, ofs); 274 res = part->master->block_markbad(part->master, ofs);
275 if (!res)
276 mtd->ecc_stats.badblocks++;
277 return res;
255} 278}
256 279
257/* 280/*
@@ -436,6 +459,16 @@ int add_mtd_partitions(struct mtd_info *master,
436 } 459 }
437 460
438 slave->mtd.ecclayout = master->ecclayout; 461 slave->mtd.ecclayout = master->ecclayout;
462 if (master->block_isbad) {
463 uint32_t offs = 0;
464
465 while(offs < slave->mtd.size) {
466 if (master->block_isbad(master,
467 offs + slave->offset))
468 slave->mtd.ecc_stats.badblocks++;
469 offs += slave->mtd.erasesize;
470 }
471 }
439 472
440 if(parts[i].mtdp) 473 if(parts[i].mtdp)
441 { /* store the object pointer (caller may or may not register it */ 474 { /* store the object pointer (caller may or may not register it */
diff --git a/drivers/mtd/nand/nand_base.c b/drivers/mtd/nand/nand_base.c
index 7a3a44907715..ea6d2c334aed 100644
--- a/drivers/mtd/nand/nand_base.c
+++ b/drivers/mtd/nand/nand_base.c
@@ -347,7 +347,7 @@ static int nand_default_block_markbad(struct mtd_info *mtd, loff_t ofs)
347{ 347{
348 struct nand_chip *chip = mtd->priv; 348 struct nand_chip *chip = mtd->priv;
349 uint8_t buf[2] = { 0, 0 }; 349 uint8_t buf[2] = { 0, 0 };
350 int block; 350 int block, ret;
351 351
352 /* Get block number */ 352 /* Get block number */
353 block = ((int)ofs) >> chip->bbt_erase_shift; 353 block = ((int)ofs) >> chip->bbt_erase_shift;
@@ -356,16 +356,22 @@ static int nand_default_block_markbad(struct mtd_info *mtd, loff_t ofs)
356 356
357 /* Do we have a flash based bad block table ? */ 357 /* Do we have a flash based bad block table ? */
358 if (chip->options & NAND_USE_FLASH_BBT) 358 if (chip->options & NAND_USE_FLASH_BBT)
359 return nand_update_bbt(mtd, ofs); 359 ret = nand_update_bbt(mtd, ofs);
360 360 else {
361 /* We write two bytes, so we dont have to mess with 16 bit access */ 361 /* We write two bytes, so we dont have to mess with 16 bit
362 ofs += mtd->oobsize; 362 * access
363 chip->ops.len = 2; 363 */
364 chip->ops.datbuf = NULL; 364 ofs += mtd->oobsize;
365 chip->ops.oobbuf = buf; 365 chip->ops.len = 2;
366 chip->ops.ooboffs = chip->badblockpos & ~0x01; 366 chip->ops.datbuf = NULL;
367 chip->ops.oobbuf = buf;
368 chip->ops.ooboffs = chip->badblockpos & ~0x01;
367 369
368 return nand_do_write_oob(mtd, ofs, &chip->ops); 370 ret = nand_do_write_oob(mtd, ofs, &chip->ops);
371 }
372 if (!ret)
373 mtd->ecc_stats.badblocks++;
374 return ret;
369} 375}
370 376
371/** 377/**
diff --git a/drivers/mtd/nand/nand_bbt.c b/drivers/mtd/nand/nand_bbt.c
index 480c3cbf9bf9..a612c4ea8194 100644
--- a/drivers/mtd/nand/nand_bbt.c
+++ b/drivers/mtd/nand/nand_bbt.c
@@ -176,6 +176,7 @@ static int read_bbt(struct mtd_info *mtd, uint8_t *buf, int page, int num,
176 printk(KERN_DEBUG "nand_read_bbt: Reserved block at 0x%08x\n", 176 printk(KERN_DEBUG "nand_read_bbt: Reserved block at 0x%08x\n",
177 ((offs << 2) + (act >> 1)) << this->bbt_erase_shift); 177 ((offs << 2) + (act >> 1)) << this->bbt_erase_shift);
178 this->bbt[offs + (act >> 3)] |= 0x2 << (act & 0x06); 178 this->bbt[offs + (act >> 3)] |= 0x2 << (act & 0x06);
179 mtd->ecc_stats.bbtblocks++;
179 continue; 180 continue;
180 } 181 }
181 /* Leave it for now, if its matured we can move this 182 /* Leave it for now, if its matured we can move this
@@ -187,6 +188,7 @@ static int read_bbt(struct mtd_info *mtd, uint8_t *buf, int page, int num,
187 this->bbt[offs + (act >> 3)] |= 0x3 << (act & 0x06); 188 this->bbt[offs + (act >> 3)] |= 0x3 << (act & 0x06);
188 else 189 else
189 this->bbt[offs + (act >> 3)] |= 0x1 << (act & 0x06); 190 this->bbt[offs + (act >> 3)] |= 0x1 << (act & 0x06);
191 mtd->ecc_stats.badblocks++;
190 } 192 }
191 } 193 }
192 totlen -= len; 194 totlen -= len;
@@ -431,6 +433,7 @@ static int create_bbt(struct mtd_info *mtd, uint8_t *buf,
431 this->bbt[i >> 3] |= 0x03 << (i & 0x6); 433 this->bbt[i >> 3] |= 0x03 << (i & 0x6);
432 printk(KERN_WARNING "Bad eraseblock %d at 0x%08x\n", 434 printk(KERN_WARNING "Bad eraseblock %d at 0x%08x\n",
433 i >> 1, (unsigned int)from); 435 i >> 1, (unsigned int)from);
436 mtd->ecc_stats.badblocks++;
434 } 437 }
435 438
436 i += 2; 439 i += 2;
diff --git a/include/linux/mtd/mtd.h b/include/linux/mtd/mtd.h
index e75bb584e80b..9536567d041b 100644
--- a/include/linux/mtd/mtd.h
+++ b/include/linux/mtd/mtd.h
@@ -56,17 +56,6 @@ struct mtd_erase_region_info {
56 u_int32_t numblocks; /* Number of blocks of erasesize in this region */ 56 u_int32_t numblocks; /* Number of blocks of erasesize in this region */
57}; 57};
58 58
59/**
60 * struct mtd_ecc_stats - error correction status
61 *
62 * @corrected: number of corrected bits
63 * @failed: number of uncorrectable errors
64 */
65struct mtd_ecc_stats {
66 unsigned long corrected;
67 unsigned long failed;
68};
69
70/* 59/*
71 * oob operation modes 60 * oob operation modes
72 * 61 *
diff --git a/include/mtd/mtd-abi.h b/include/mtd/mtd-abi.h
index 54c673f9648d..c11a589bdedf 100644
--- a/include/mtd/mtd-abi.h
+++ b/include/mtd/mtd-abi.h
@@ -99,6 +99,8 @@ struct otp_info {
99#define OTPGETREGIONINFO _IOW('M', 15, struct otp_info) 99#define OTPGETREGIONINFO _IOW('M', 15, struct otp_info)
100#define OTPLOCK _IOR('M', 16, struct otp_info) 100#define OTPLOCK _IOR('M', 16, struct otp_info)
101#define ECCGETLAYOUT _IOR('M', 17, struct nand_ecclayout) 101#define ECCGETLAYOUT _IOR('M', 17, struct nand_ecclayout)
102#define ECCGETSTATS _IOR('M', 18, struct mtd_ecc_stats)
103#define MTDFILEMODE _IO('M', 19)
102 104
103/* 105/*
104 * Obsolete legacy interface. Keep it in order not to break userspace 106 * Obsolete legacy interface. Keep it in order not to break userspace
@@ -128,4 +130,29 @@ struct nand_ecclayout {
128 struct nand_oobfree oobfree[MTD_MAX_OOBFREE_ENTRIES]; 130 struct nand_oobfree oobfree[MTD_MAX_OOBFREE_ENTRIES];
129}; 131};
130 132
133/**
134 * struct mtd_ecc_stats - error correction status
135 *
136 * @corrected: number of corrected bits
137 * @failed: number of uncorrectable errors
138 * @badblocks: number of bad blocks in this partition
139 * @bbtblocks: number of blocks reserved for bad block tables
140 */
141struct mtd_ecc_stats {
142 uint32_t corrected;
143 uint32_t failed;
144 uint32_t badblocks;
145 uint32_t bbtblocks;
146};
147
148/*
149 * Read/write file modes for access to MTD
150 */
151enum mtd_file_modes {
152 MTD_MODE_NORMAL = MTD_OTP_OFF,
153 MTD_MODE_OTP_FACTORY = MTD_OTP_FACTORY,
154 MTD_MODE_OTP_USER = MTD_OTP_USER,
155 MTD_MODE_RAW,
156};
157
131#endif /* __MTD_ABI_H__ */ 158#endif /* __MTD_ABI_H__ */