aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/mtd/mtdchar.c
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 /drivers/mtd/mtdchar.c
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>
Diffstat (limited to 'drivers/mtd/mtdchar.c')
-rw-r--r--drivers/mtd/mtdchar.c200
1 files changed, 142 insertions, 58 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 }