aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-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__ */