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/mtdchar.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/mtdchar.c')
-rw-r--r-- | drivers/mtd/mtdchar.c | 200 |
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 | 55 | struct 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 | ||
67 | static loff_t mtd_lseek (struct file *file, loff_t offset, int orig) | 60 | static 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 | ||
133 | static int mtd_close(struct inode *inode, struct file *file) | 134 | static 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 | ||
154 | static ssize_t mtd_read(struct file *file, char __user *buf, size_t count,loff_t *ppos) | 156 | static 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 | ||
233 | static ssize_t mtd_write(struct file *file, const char __user *buf, size_t count,loff_t *ppos) | 249 | static 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) | ||
346 | static 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 | |||
313 | static int mtd_ioctl(struct inode *inode, struct file *file, | 375 | static 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 | } |