Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

[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>

+259 -98
+142 -58
drivers/mtd/mtdchar.c
··· 49 49 }; 50 50 51 51 /* 52 - * We use file->private_data to store a pointer to the MTDdevice. 53 - * Since alighment is at least 32 bits, we have 2 bits free for OTP 54 - * modes as well. 52 + * Data structure to hold the pointer to the mtd device as well 53 + * as mode information ofr various use cases. 55 54 */ 56 - 57 - #define TO_MTD(file) (struct mtd_info *)((long)((file)->private_data) & ~3L) 58 - 59 - #define MTD_MODE_OTP_FACT 1 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) 55 + struct mtd_file_info { 56 + struct mtd_info *mtd; 57 + enum mtd_file_modes mode; 58 + }; 66 59 67 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 65 switch (orig) { 72 66 case 0: ··· 91 97 int minor = iminor(inode); 92 98 int devnum = minor >> 1; 93 99 struct mtd_info *mtd; 100 + struct mtd_file_info *mfi; 94 101 95 102 DEBUG(MTD_DEBUG_LEVEL0, "MTD_open\n"); 96 103 ··· 112 117 return -ENODEV; 113 118 } 114 119 115 - file->private_data = mtd; 116 - 117 120 /* You can't open it RW if it's not a writeable device */ 118 121 if ((file->f_mode & 2) && !(mtd->flags & MTD_WRITEABLE)) { 119 122 put_mtd_device(mtd); 120 123 return -EACCES; 121 124 } 125 + 126 + mfi = kzalloc(sizeof(*mfi), GFP_KERNEL); 127 + if (!mfi) { 128 + put_mtd_device(mtd); 129 + return -ENOMEM; 130 + } 131 + mfi->mtd = mtd; 132 + file->private_data = mfi; 122 133 123 134 return 0; 124 135 } /* mtd_open */ ··· 133 132 134 133 static int mtd_close(struct inode *inode, struct file *file) 135 134 { 136 - struct mtd_info *mtd; 135 + struct mtd_file_info *mfi = file->private_data; 136 + struct mtd_info *mtd = mfi->mtd; 137 137 138 138 DEBUG(MTD_DEBUG_LEVEL0, "MTD_close\n"); 139 - 140 - mtd = TO_MTD(file); 141 139 142 140 if (mtd->sync) 143 141 mtd->sync(mtd); 144 142 145 143 put_mtd_device(mtd); 144 + file->private_data = NULL; 145 + kfree(mfi); 146 146 147 147 return 0; 148 148 } /* mtd_close */ ··· 155 153 156 154 static ssize_t mtd_read(struct file *file, char __user *buf, size_t count,loff_t *ppos) 157 155 { 158 - struct mtd_info *mtd = TO_MTD(file); 156 + struct mtd_file_info *mfi = file->private_data; 157 + struct mtd_info *mtd = mfi->mtd; 159 158 size_t retlen=0; 160 159 size_t total_retlen=0; 161 160 int ret=0; ··· 189 186 else 190 187 len = count; 191 188 192 - switch (MTD_MODE(file)) { 193 - case MTD_MODE_OTP_FACT: 189 + switch (mfi->mode) { 190 + case MTD_MODE_OTP_FACTORY: 194 191 ret = mtd->read_fact_prot_reg(mtd, *ppos, len, &retlen, kbuf); 195 192 break; 196 193 case MTD_MODE_OTP_USER: 197 194 ret = mtd->read_user_prot_reg(mtd, *ppos, len, &retlen, kbuf); 198 195 break; 196 + case MTD_MODE_RAW: 197 + { 198 + struct mtd_oob_ops ops; 199 + 200 + ops.mode = MTD_OOB_RAW; 201 + ops.datbuf = kbuf; 202 + ops.oobbuf = NULL; 203 + ops.len = len; 204 + 205 + ret = mtd->read_oob(mtd, *ppos, &ops); 206 + retlen = ops.retlen; 207 + break; 208 + } 199 209 default: 200 210 ret = mtd->read(mtd, *ppos, len, &retlen, kbuf); 201 211 } ··· 248 232 249 233 static ssize_t mtd_write(struct file *file, const char __user *buf, size_t count,loff_t *ppos) 250 234 { 251 - struct mtd_info *mtd = TO_MTD(file); 235 + struct mtd_file_info *mfi = file->private_data; 236 + struct mtd_info *mtd = mfi->mtd; 252 237 char *kbuf; 253 238 size_t retlen; 254 239 size_t total_retlen=0; ··· 287 270 return -EFAULT; 288 271 } 289 272 290 - switch (MTD_MODE(file)) { 291 - case MTD_MODE_OTP_FACT: 273 + switch (mfi->mode) { 274 + case MTD_MODE_OTP_FACTORY: 292 275 ret = -EROFS; 293 276 break; 294 277 case MTD_MODE_OTP_USER: ··· 298 281 } 299 282 ret = mtd->write_user_prot_reg(mtd, *ppos, len, &retlen, kbuf); 300 283 break; 284 + 285 + case MTD_MODE_RAW: 286 + { 287 + struct mtd_oob_ops ops; 288 + 289 + ops.mode = MTD_OOB_RAW; 290 + ops.datbuf = kbuf; 291 + ops.oobbuf = NULL; 292 + ops.len = len; 293 + 294 + ret = mtd->write_oob(mtd, *ppos, &ops); 295 + retlen = ops.retlen; 296 + break; 297 + } 298 + 301 299 default: 302 300 ret = (*(mtd->write))(mtd, *ppos, len, &retlen, kbuf); 303 301 } ··· 342 310 wake_up((wait_queue_head_t *)instr->priv); 343 311 } 344 312 313 + #if defined(CONFIG_MTD_OTP) || defined(CONFIG_MTD_ONENAND_OTP) 314 + static int otp_select_filemode(struct mtd_file_info *mfi, int mode) 315 + { 316 + struct mtd_info *mtd = mfi->mtd; 317 + int ret = 0; 318 + 319 + switch (mode) { 320 + case MTD_OTP_FACTORY: 321 + if (!mtd->read_fact_prot_reg) 322 + ret = -EOPNOTSUPP; 323 + else 324 + mfi->mode = MTD_MODE_OTP_FACTORY; 325 + break; 326 + case MTD_OTP_USER: 327 + if (!mtd->read_fact_prot_reg) 328 + ret = -EOPNOTSUPP; 329 + else 330 + mfi->mode = MTD_MODE_OTP_USER; 331 + break; 332 + default: 333 + ret = -EINVAL; 334 + case MTD_OTP_OFF: 335 + break; 336 + } 337 + return ret; 338 + } 339 + #else 340 + # define otp_select_filemode(f,m) -EOPNOTSUPP 341 + #endif 342 + 345 343 static int mtd_ioctl(struct inode *inode, struct file *file, 346 344 u_int cmd, u_long arg) 347 345 { 348 - struct mtd_info *mtd = TO_MTD(file); 346 + struct mtd_file_info *mfi = file->private_data; 347 + struct mtd_info *mtd = mfi->mtd; 349 348 void __user *argp = (void __user *)arg; 350 349 int ret = 0; 351 350 u_long size; ··· 617 554 break; 618 555 } 619 556 620 - case ECCGETLAYOUT: 621 - 622 - if (!mtd->ecclayout) 623 - return -EOPNOTSUPP; 624 - 625 - if (copy_to_user(argp, &mtd->ecclayout, 626 - sizeof(struct nand_ecclayout))) 627 - return -EFAULT; 628 - break; 629 - 630 557 case MEMGETBADBLOCK: 631 558 { 632 559 loff_t offs; ··· 649 596 int mode; 650 597 if (copy_from_user(&mode, argp, sizeof(int))) 651 598 return -EFAULT; 652 - SET_MTD_MODE(file, 0); 653 - switch (mode) { 654 - case MTD_OTP_FACTORY: 655 - if (!mtd->read_fact_prot_reg) 656 - ret = -EOPNOTSUPP; 657 - else 658 - SET_MTD_MODE(file, MTD_MODE_OTP_FACT); 659 - break; 660 - case MTD_OTP_USER: 661 - if (!mtd->read_fact_prot_reg) 662 - ret = -EOPNOTSUPP; 663 - else 664 - SET_MTD_MODE(file, MTD_MODE_OTP_USER); 665 - break; 666 - default: 667 - ret = -EINVAL; 668 - case MTD_OTP_OFF: 669 - break; 670 - } 599 + 600 + mfi->mode = MTD_MODE_NORMAL; 601 + 602 + ret = otp_select_filemode(mfi, mode); 603 + 671 604 file->f_pos = 0; 672 605 break; 673 606 } ··· 665 626 if (!buf) 666 627 return -ENOMEM; 667 628 ret = -EOPNOTSUPP; 668 - switch (MTD_MODE(file)) { 669 - case MTD_MODE_OTP_FACT: 629 + switch (mfi->mode) { 630 + case MTD_MODE_OTP_FACTORY: 670 631 if (mtd->get_fact_prot_info) 671 632 ret = mtd->get_fact_prot_info(mtd, buf, 4096); 672 633 break; 673 634 case MTD_MODE_OTP_USER: 674 635 if (mtd->get_user_prot_info) 675 636 ret = mtd->get_user_prot_info(mtd, buf, 4096); 637 + break; 638 + default: 676 639 break; 677 640 } 678 641 if (ret >= 0) { ··· 694 653 { 695 654 struct otp_info info; 696 655 697 - if (MTD_MODE(file) != MTD_MODE_OTP_USER) 656 + if (mfi->mode != MTD_MODE_OTP_USER) 698 657 return -EINVAL; 699 658 if (copy_from_user(&info, argp, sizeof(info))) 700 659 return -EFAULT; ··· 704 663 break; 705 664 } 706 665 #endif 666 + 667 + case ECCGETLAYOUT: 668 + { 669 + if (!mtd->ecclayout) 670 + return -EOPNOTSUPP; 671 + 672 + if (copy_to_user(argp, &mtd->ecclayout, 673 + sizeof(struct nand_ecclayout))) 674 + return -EFAULT; 675 + break; 676 + } 677 + 678 + case ECCGETSTATS: 679 + { 680 + if (copy_to_user(argp, &mtd->ecc_stats, 681 + sizeof(struct mtd_ecc_stats))) 682 + return -EFAULT; 683 + break; 684 + } 685 + 686 + case MTDFILEMODE: 687 + { 688 + mfi->mode = 0; 689 + 690 + switch(arg) { 691 + case MTD_MODE_OTP_FACTORY: 692 + case MTD_MODE_OTP_USER: 693 + ret = otp_select_filemode(mfi, arg); 694 + break; 695 + 696 + case MTD_MODE_RAW: 697 + if (!mtd->read_oob || !mtd->write_oob) 698 + return -EOPNOTSUPP; 699 + mfi->mode = arg; 700 + 701 + case MTD_MODE_NORMAL: 702 + break; 703 + default: 704 + ret = -EINVAL; 705 + } 706 + file->f_pos = 0; 707 + break; 708 + } 707 709 708 710 default: 709 711 ret = -ENOTTY;
+35 -16
drivers/mtd/mtdconcat.c
··· 56 56 size_t * retlen, u_char * buf) 57 57 { 58 58 struct mtd_concat *concat = CONCAT(mtd); 59 - int ret = 0, err = -EINVAL; 59 + int ret = 0, err; 60 60 int i; 61 61 62 62 *retlen = 0; ··· 80 80 81 81 err = subdev->read(subdev, from, size, &retsize, buf); 82 82 83 - if (err && (err != -EBADMSG) && (err != -EUCLEAN)) 84 - break; 85 - 86 83 /* Save information about bitflips! */ 87 - if (err) { 88 - if (err == -EBADMSG) 84 + if (unlikely(err)) { 85 + if (err == -EBADMSG) { 86 + mtd->ecc_stats.failed++; 89 87 ret = err; 90 - else if (!ret) 91 - 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 97 *retlen += retsize; 96 98 len -= size; 97 99 if (len == 0) 98 - break; 100 + return ret; 99 101 100 - err = -EINVAL; 101 102 buf += size; 102 103 from = 0; 103 104 } 104 - return err ? err : ret; 105 + return -EINVAL; 105 106 } 106 107 107 108 static int ··· 245 244 { 246 245 struct mtd_concat *concat = CONCAT(mtd); 247 246 struct mtd_oob_ops devops = *ops; 248 - int i, err; 247 + int i, err, ret = 0; 249 248 250 249 ops->retlen = 0; 251 250 ··· 263 262 264 263 err = subdev->read_oob(subdev, from, &devops); 265 264 ops->retlen += devops.retlen; 266 - if (err) 267 - return err; 265 + 266 + /* Save information about bitflips! */ 267 + if (unlikely(err)) { 268 + if (err == -EBADMSG) { 269 + mtd->ecc_stats.failed++; 270 + ret = err; 271 + } else if (err == -EUCLEAN) { 272 + mtd->ecc_stats.corrected++; 273 + /* Do not overwrite -EBADMSG !! */ 274 + if (!ret) 275 + ret = err; 276 + } else 277 + return err; 278 + } 268 279 269 280 devops.len = ops->len - ops->retlen; 270 281 if (!devops.len) 271 - return 0; 282 + return ret; 272 283 273 284 if (devops.datbuf) 274 285 devops.datbuf += devops.retlen; ··· 668 655 } 669 656 670 657 err = subdev->block_markbad(subdev, ofs); 658 + if (!err) 659 + mtd->ecc_stats.badblocks++; 671 660 break; 672 661 } 673 662 ··· 732 717 if (subdev[0]->block_markbad) 733 718 concat->mtd.block_markbad = concat_block_markbad; 734 719 720 + concat->mtd.ecc_stats.badblocks = subdev[0]->ecc_stats.badblocks; 721 + 735 722 concat->subdev[0] = subdev[0]; 736 723 737 724 for (i = 1; i < num_devs; i++) { ··· 761 744 subdev[i]->flags & MTD_WRITEABLE; 762 745 } 763 746 concat->mtd.size += subdev[i]->size; 747 + concat->mtd.ecc_stats.badblocks += 748 + subdev[i]->ecc_stats.badblocks; 764 749 if (concat->mtd.writesize != subdev[i]->writesize || 765 750 concat->mtd.oobsize != subdev[i]->oobsize || 766 751 concat->mtd.ecctype != subdev[i]->ecctype ||
+36 -3
drivers/mtd/mtdpart.c
··· 51 51 size_t *retlen, u_char *buf) 52 52 { 53 53 struct mtd_part *part = PART(mtd); 54 + int res; 55 + 54 56 if (from >= mtd->size) 55 57 len = 0; 56 58 else if (from + len > mtd->size) 57 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 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 62 71 static int part_point (struct mtd_info *mtd, loff_t from, size_t len, ··· 91 82 struct mtd_oob_ops *ops) 92 83 { 93 84 struct mtd_part *part = PART(mtd); 85 + int res; 94 86 95 87 if (from >= mtd->size) 96 88 return -EINVAL; 97 89 if (from + ops->len > mtd->size) 98 90 return -EINVAL; 99 - return part->master->read_oob(part->master, from + part->offset, ops); 91 + res = part->master->read_oob(part->master, from + part->offset, ops); 92 + 93 + if (unlikely(res)) { 94 + if (res == -EUCLEAN) 95 + mtd->ecc_stats.corrected++; 96 + if (res == -EBADMSG) 97 + mtd->ecc_stats.failed++; 98 + } 99 + return res; 100 100 } 101 101 102 102 static int part_read_user_prot_reg (struct mtd_info *mtd, loff_t from, size_t len, ··· 264 246 static int part_block_markbad (struct mtd_info *mtd, loff_t ofs) 265 247 { 266 248 struct mtd_part *part = PART(mtd); 249 + int res; 250 + 267 251 if (!(mtd->flags & MTD_WRITEABLE)) 268 252 return -EROFS; 269 253 if (ofs >= mtd->size) 270 254 return -EINVAL; 271 255 ofs += part->offset; 272 - return part->master->block_markbad(part->master, ofs); 256 + res = part->master->block_markbad(part->master, ofs); 257 + if (!res) 258 + mtd->ecc_stats.badblocks++; 259 + return res; 273 260 } 274 261 275 262 /* ··· 459 436 } 460 437 461 438 slave->mtd.ecclayout = master->ecclayout; 439 + if (master->block_isbad) { 440 + uint32_t offs = 0; 441 + 442 + while(offs < slave->mtd.size) { 443 + if (master->block_isbad(master, 444 + offs + slave->offset)) 445 + slave->mtd.ecc_stats.badblocks++; 446 + offs += slave->mtd.erasesize; 447 + } 448 + } 462 449 463 450 if(parts[i].mtdp) 464 451 { /* store the object pointer (caller may or may not register it */
+16 -10
drivers/mtd/nand/nand_base.c
··· 347 347 { 348 348 struct nand_chip *chip = mtd->priv; 349 349 uint8_t buf[2] = { 0, 0 }; 350 - int block; 350 + int block, ret; 351 351 352 352 /* Get block number */ 353 353 block = ((int)ofs) >> chip->bbt_erase_shift; ··· 356 356 357 357 /* Do we have a flash based bad block table ? */ 358 358 if (chip->options & NAND_USE_FLASH_BBT) 359 - return nand_update_bbt(mtd, ofs); 359 + ret = nand_update_bbt(mtd, ofs); 360 + else { 361 + /* We write two bytes, so we dont have to mess with 16 bit 362 + * access 363 + */ 364 + ofs += mtd->oobsize; 365 + chip->ops.len = 2; 366 + chip->ops.datbuf = NULL; 367 + chip->ops.oobbuf = buf; 368 + chip->ops.ooboffs = chip->badblockpos & ~0x01; 360 369 361 - /* We write two bytes, so we dont have to mess with 16 bit access */ 362 - ofs += mtd->oobsize; 363 - chip->ops.len = 2; 364 - chip->ops.datbuf = NULL; 365 - chip->ops.oobbuf = buf; 366 - chip->ops.ooboffs = chip->badblockpos & ~0x01; 367 - 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 /**
+3
drivers/mtd/nand/nand_bbt.c
··· 176 176 printk(KERN_DEBUG "nand_read_bbt: Reserved block at 0x%08x\n", 177 177 ((offs << 2) + (act >> 1)) << this->bbt_erase_shift); 178 178 this->bbt[offs + (act >> 3)] |= 0x2 << (act & 0x06); 179 + mtd->ecc_stats.bbtblocks++; 179 180 continue; 180 181 } 181 182 /* Leave it for now, if its matured we can move this ··· 188 187 this->bbt[offs + (act >> 3)] |= 0x3 << (act & 0x06); 189 188 else 190 189 this->bbt[offs + (act >> 3)] |= 0x1 << (act & 0x06); 190 + mtd->ecc_stats.badblocks++; 191 191 } 192 192 } 193 193 totlen -= len; ··· 433 431 this->bbt[i >> 3] |= 0x03 << (i & 0x6); 434 432 printk(KERN_WARNING "Bad eraseblock %d at 0x%08x\n", 435 433 i >> 1, (unsigned int)from); 434 + mtd->ecc_stats.badblocks++; 436 435 } 437 436 438 437 i += 2;
-11
include/linux/mtd/mtd.h
··· 56 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 - */ 65 - struct mtd_ecc_stats { 66 - unsigned long corrected; 67 - unsigned long failed; 68 - }; 69 - 70 59 /* 71 60 * oob operation modes 72 61 *
+27
include/mtd/mtd-abi.h
··· 99 99 #define OTPGETREGIONINFO _IOW('M', 15, struct otp_info) 100 100 #define OTPLOCK _IOR('M', 16, struct otp_info) 101 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 106 * Obsolete legacy interface. Keep it in order not to break userspace ··· 128 126 uint32_t eccpos[64]; 129 127 uint32_t oobavail; 130 128 struct nand_oobfree oobfree[MTD_MAX_OOBFREE_ENTRIES]; 129 + }; 130 + 131 + /** 132 + * struct mtd_ecc_stats - error correction status 133 + * 134 + * @corrected: number of corrected bits 135 + * @failed: number of uncorrectable errors 136 + * @badblocks: number of bad blocks in this partition 137 + * @bbtblocks: number of blocks reserved for bad block tables 138 + */ 139 + struct mtd_ecc_stats { 140 + uint32_t corrected; 141 + uint32_t failed; 142 + uint32_t badblocks; 143 + uint32_t bbtblocks; 144 + }; 145 + 146 + /* 147 + * Read/write file modes for access to MTD 148 + */ 149 + enum mtd_file_modes { 150 + MTD_MODE_NORMAL = MTD_OTP_OFF, 151 + MTD_MODE_OTP_FACTORY = MTD_OTP_FACTORY, 152 + MTD_MODE_OTP_USER = MTD_OTP_USER, 153 + MTD_MODE_RAW, 131 154 }; 132 155 133 156 #endif /* __MTD_ABI_H__ */