aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/mtd/mtdchar.c
diff options
context:
space:
mode:
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 }