From 89edb1f57951f3f8a883ab8f3bbc7daa94f08285 Mon Sep 17 00:00:00 2001 From: Thomas Gleixner Date: Tue, 30 May 2006 00:41:57 +0200 Subject: Update mtd-abi.h and use new NAND ECC functionality The NAND rework exposes more information to userspace and has a different mechanism to read raw FLASH contents without ECC. Update nanddump and nandwrite. Use the new ECC statistics ioctl to inform the user about corrected and uncorrectable bitflips. Signed-off-by: Thomas Gleixner --- include/mtd/mtd-abi.h | 27 +++++++++++++ nanddump.c | 98 ++++++++++++++++++++++++++++++++++------------ nandwrite.c | 105 ++++++++++++++++++++++++++++++-------------------- 3 files changed, 164 insertions(+), 66 deletions(-) diff --git a/include/mtd/mtd-abi.h b/include/mtd/mtd-abi.h index 54c673f..c11a589 100644 --- a/include/mtd/mtd-abi.h +++ b/include/mtd/mtd-abi.h @@ -99,6 +99,8 @@ struct otp_info { #define OTPGETREGIONINFO _IOW('M', 15, struct otp_info) #define OTPLOCK _IOR('M', 16, struct otp_info) #define ECCGETLAYOUT _IOR('M', 17, struct nand_ecclayout) +#define ECCGETSTATS _IOR('M', 18, struct mtd_ecc_stats) +#define MTDFILEMODE _IO('M', 19) /* * Obsolete legacy interface. Keep it in order not to break userspace @@ -128,4 +130,29 @@ struct nand_ecclayout { struct nand_oobfree oobfree[MTD_MAX_OOBFREE_ENTRIES]; }; +/** + * struct mtd_ecc_stats - error correction status + * + * @corrected: number of corrected bits + * @failed: number of uncorrectable errors + * @badblocks: number of bad blocks in this partition + * @bbtblocks: number of blocks reserved for bad block tables + */ +struct mtd_ecc_stats { + uint32_t corrected; + uint32_t failed; + uint32_t badblocks; + uint32_t bbtblocks; +}; + +/* + * Read/write file modes for access to MTD + */ +enum mtd_file_modes { + MTD_MODE_NORMAL = MTD_OTP_OFF, + MTD_MODE_OTP_FACTORY = MTD_OTP_FACTORY, + MTD_MODE_OTP_USER = MTD_OTP_USER, + MTD_MODE_RAW, +}; + #endif /* __MTD_ABI_H__ */ diff --git a/nanddump.c b/nanddump.c index a03c79a..b2323f6 100644 --- a/nanddump.c +++ b/nanddump.c @@ -17,6 +17,7 @@ #define _GNU_SOURCE #include +#include #include #include #include @@ -42,8 +43,8 @@ void display_help (void) printf("Usage: nanddump [OPTIONS] MTD-device\n" "Dumps the contents of a nand mtd partition.\n" "\n" - " --help display this help and exit\n" - " --version output version information and exit\n" + " --help display this help and exit\n" + " --version output version information and exit\n" "-f file --file=file dump to file\n" "-i --ignoreerrors ignore errors\n" "-l length --length=length length\n" @@ -70,10 +71,10 @@ void display_version (void) // Option variables -int ignoreerrors; // ignore errors -int pretty_print; // print nice in ascii +int ignoreerrors; // ignore errors +int pretty_print; // print nice in ascii int noecc; // don't error correct -int omitoob; // omit oob data +int omitoob; // omit oob data unsigned long start_addr; // start address unsigned long length; // dump length char *mtddev; // mtd device name @@ -174,9 +175,11 @@ int main(int argc, char **argv) mtd_info_t meminfo; char pretty_buf[80]; int oobinfochanged = 0 ; - struct nand_oobinfo old_oobinfo ; + struct nand_oobinfo old_oobinfo; + struct mtd_ecc_stats stat1, stat2; + int eccstats = 0; - process_options(argc, argv); + process_options(argc, argv); /* Open MTD device */ if ((fd = open(mtddev, O_RDONLY)) == -1) { @@ -203,24 +206,48 @@ int main(int argc, char **argv) oob.length = meminfo.oobsize; if (noecc) { - if (ioctl (fd, MEMGETOOBSEL, &old_oobinfo) != 0) { - perror ("MEMGETOOBSEL"); - close (fd); - exit (1); - } - if (ioctl (fd, MEMSETOOBSEL, &none_oobinfo) != 0) { - perror ("MEMSETOOBSEL"); + switch (ioctl(fd, MTDFILEMODE, (void *) MTD_MODE_RAW)) { + + case -ENOTTY: + if (ioctl (fd, MEMGETOOBSEL, &old_oobinfo) != 0) { + perror ("MEMGETOOBSEL"); + close (fd); + exit (1); + } + if (ioctl (fd, MEMSETOOBSEL, &none_oobinfo) != 0) { + perror ("MEMSETOOBSEL"); + close (fd); + exit (1); + } + oobinfochanged = 1; + break; + + case 0: + oobinfochanged = 2; + break; + default: + perror ("MTDFILEMODE"); close (fd); exit (1); } - oobinfochanged = 1 ; + } else { + + /* check if we can read ecc stats */ + if (!ioctl(fd, ECCGETSTATS, &stat1)) { + eccstats = 1; + fprintf(stderr, "ECC failed: %d\n", stat1.failed); + fprintf(stderr, "ECC corrected: %d\n", stat1.corrected); + fprintf(stderr, "Number of bad blocks: %d\n", stat1.badblocks); + fprintf(stderr, "Number of bbt blocks: %d\n", stat1.bbtblocks); + } else + perror("No ECC status information available"); } - - /* Open output file for writing. If file name is "-", write to standard output. */ + /* Open output file for writing. If file name is "-", write to standard + * output. */ if (!dumpfile) { ofd = STDOUT_FILENO; - } else if ((ofd = open(dumpfile, O_WRONLY | O_TRUNC | O_CREAT, 0644)) == -1) { + } else if ((ofd = open(dumpfile, O_WRONLY | O_TRUNC | O_CREAT, 0644))== -1) { perror ("open outfile"); close(fd); exit(1); @@ -230,14 +257,16 @@ int main(int argc, char **argv) if (length) end_addr = start_addr + length; if (!length || end_addr > meminfo.size) - end_addr = meminfo.size; + end_addr = meminfo.size; bs = meminfo.writesize; /* Print informative message */ - fprintf(stderr, "Block size %u, page size %u, OOB size %u\n", meminfo.erasesize, meminfo.writesize, meminfo.oobsize); - fprintf(stderr, "Dumping data starting at 0x%08x and ending at 0x%08x...\n", - (unsigned int) start_addr, (unsigned int) end_addr); + fprintf(stderr, "Block size %u, page size %u, OOB size %u\n", + meminfo.erasesize, meminfo.writesize, meminfo.oobsize); + fprintf(stderr, + "Dumping data starting at 0x%08x and ending at 0x%08x...\n", + (unsigned int) start_addr, (unsigned int) end_addr); /* Dump the flash contents */ for (ofs = start_addr; ofs < end_addr ; ofs+=bs) { @@ -263,6 +292,23 @@ int main(int argc, char **argv) } } + /* ECC stats available ? */ + if (eccstats) { + if (ioctl(fd, ECCGETSTATS, &stat2)) { + perror("ioctl(ECCGETSTATS)"); + goto closeall; + } + if (stat1.failed != stat2.failed) + fprintf(stderr, "ECC: %d uncorrectable bitflip(s)" + " at offset 0x%08lx\n", + stat2.failed - stat1.failed, ofs); + if (stat1.corrected != stat2.corrected) + fprintf(stderr, "ECC: %d corrected bitflip(s) at" + " offset 0x%08lx\n", + stat2.corrected - stat1.corrected, ofs); + stat1 = stat2; + } + /* Write out page data */ if (pretty_print) { for (i = 0; i < bs; i += 16) { @@ -283,6 +329,8 @@ int main(int argc, char **argv) } else write(ofd, readbuf, bs); + + if (omitoob) continue; @@ -325,7 +373,7 @@ int main(int argc, char **argv) } /* reset oobinfo */ - if (oobinfochanged) { + if (oobinfochanged == 1) { if (ioctl (fd, MEMSETOOBSEL, &old_oobinfo) != 0) { perror ("MEMSETOOBSEL"); close(fd); @@ -341,7 +389,8 @@ int main(int argc, char **argv) return 0; closeall: - if (oobinfochanged) { + /* The new mode change is per file descriptor ! */ + if (oobinfochanged == 1) { if (ioctl (fd, MEMSETOOBSEL, &old_oobinfo) != 0) { perror ("MEMSETOOBSEL"); } @@ -349,5 +398,4 @@ int main(int argc, char **argv) close(fd); close(ofd); exit(1); - } diff --git a/nandwrite.c b/nandwrite.c index 10489b7..037474a 100644 --- a/nandwrite.c +++ b/nandwrite.c @@ -2,7 +2,7 @@ * nandwrite.c * * Copyright (C) 2000 Steven J. Hill (sjhill@realitydiluted.com) - * 2003 Thomas Gleixner (tglx@linutronix.de) + * 2003 Thomas Gleixner (tglx@linutronix.de) * * $Id: nandwrite.c,v 1.32 2005/11/07 11:15:13 gleixner Exp $ * @@ -23,6 +23,7 @@ #define _GNU_SOURCE #include +#include #include #include #include @@ -76,18 +77,18 @@ void display_help (void) printf("Usage: nandwrite [OPTION] MTD_DEVICE INPUTFILE\n" "Writes to the specified MTD device.\n" "\n" - " -a, --autoplace Use auto oob layout\n" - " -j, --jffs2 force jffs2 oob layout (legacy support)\n" - " -y, --yaffs force yaffs oob layout (legacy support)\n" + " -a, --autoplace Use auto oob layout\n" + " -j, --jffs2 force jffs2 oob layout (legacy support)\n" + " -y, --yaffs force yaffs oob layout (legacy support)\n" " -f, --forcelegacy force legacy support on autoplacement enabled mtd device\n" " -n, --noecc write without ecc\n" - " -o, --oob image contains oob data\n" + " -o, --oob image contains oob data\n" " -s addr, --start=addr set start address (default is 0)\n" " -p, --pad pad to page size\n" " -b, --blockalign=1|2|4 set multiple of eraseblocks to align to\n" - " -q, --quiet don't display progress messages\n" - " --help display this help and exit\n" - " --version output version information and exit\n"); + " -q, --quiet don't display progress messages\n" + " --help display this help and exit\n" + " --version output version information and exit\n"); exit(0); } @@ -106,9 +107,9 @@ void display_version (void) exit(0); } -char *mtd_device, *img; -int mtdoffset = 0; -int quiet = 0; +char *mtd_device, *img; +int mtdoffset = 0; +int quiet = 0; int writeoob = 0; int autoplace = 0; int forcejffs2 = 0; @@ -129,7 +130,7 @@ void process_options (int argc, char *argv[]) {"help", no_argument, 0, 0}, {"version", no_argument, 0, 0}, {"autoplace", no_argument, 0, 'a'}, - {"blockalign", required_argument, 0, 'b'}, + {"blockalign", required_argument, 0, 'b'}, {"forcelegacy", no_argument, 0, 'f'}, {"jffs2", no_argument, 0, 'j'}, {"noecc", no_argument, 0, 'n'}, @@ -236,8 +237,9 @@ int main(int argc, char **argv) exit(1); } - /* Set erasesize to specified number of blocks - to match jffs2 (virtual) block size */ - meminfo.erasesize *= blockalign; + /* Set erasesize to specified number of blocks - to match jffs2 + * (virtual) block size */ + meminfo.erasesize *= blockalign; /* Make sure device page sizes are valid */ if (!(meminfo.oobsize == 16 && meminfo.writesize == 512) && @@ -248,32 +250,51 @@ int main(int argc, char **argv) exit(1); } - /* Read the current oob info */ - if (ioctl (fd, MEMGETOOBSEL, &old_oobinfo) != 0) { - perror ("MEMGETOOBSEL"); - close (fd); - exit (1); - } - - // write without ecc ? - if (noecc) { - if (ioctl (fd, MEMSETOOBSEL, &none_oobinfo) != 0) { - perror ("MEMSETOOBSEL"); + if (autoplace) { + /* Read the current oob info */ + if (ioctl (fd, MEMGETOOBSEL, &old_oobinfo) != 0) { + perror ("MEMGETOOBSEL"); close (fd); exit (1); } - oobinfochanged = 1; + + // autoplace ECC ? + if (autoplace && (old_oobinfo.useecc != MTD_NANDECC_AUTOPLACE)) { + + if (ioctl (fd, MEMSETOOBSEL, &autoplace_oobinfo) != 0) { + perror ("MEMSETOOBSEL"); + close (fd); + exit (1); + } + oobinfochanged = 1; + } } - // autoplace ECC ? - if (autoplace && (old_oobinfo.useecc != MTD_NANDECC_AUTOPLACE)) { + if (noecc) { + switch (ioctl(fd, MTDFILEMODE, (void *) MTD_MODE_RAW)) { - if (ioctl (fd, MEMSETOOBSEL, &autoplace_oobinfo) != 0) { - perror ("MEMSETOOBSEL"); + case -ENOTTY: + if (ioctl (fd, MEMGETOOBSEL, &old_oobinfo) != 0) { + perror ("MEMGETOOBSEL"); + close (fd); + exit (1); + } + if (ioctl (fd, MEMSETOOBSEL, &none_oobinfo) != 0) { + perror ("MEMSETOOBSEL"); + close (fd); + exit (1); + } + oobinfochanged = 1; + break; + + case 0: + oobinfochanged = 2; + break; + default: + perror ("MTDFILEMODE"); close (fd); exit (1); } - oobinfochanged = 1; } /* @@ -292,7 +313,7 @@ int main(int argc, char **argv) goto restoreoob; } if (meminfo.oobsize == 8) { - if (forceyaffs) { + if (forceyaffs) { fprintf (stderr, "YAFSS cannot operate on 256 Byte page size"); goto restoreoob; } @@ -316,7 +337,7 @@ int main(int argc, char **argv) } // get image length - imglen = lseek(ifd, 0, SEEK_END); + imglen = lseek(ifd, 0, SEEK_END); lseek (ifd, 0, SEEK_SET); pagelen = meminfo.writesize + ((writeoob == 1) ? meminfo.oobsize : 0); @@ -346,26 +367,28 @@ int main(int argc, char **argv) while (blockstart != (mtdoffset & (~meminfo.erasesize + 1))) { blockstart = mtdoffset & (~meminfo.erasesize + 1); offs = blockstart; - baderaseblock = 0; + baderaseblock = 0; if (!quiet) fprintf (stdout, "Writing data to block %x\n", blockstart); - /* Check all the blocks in an erase block for bad blocks */ + /* Check all the blocks in an erase block for bad blocks */ do { - if ((ret = ioctl(fd, MEMGETBADBLOCK, &offs)) < 0) { + if ((ret = ioctl(fd, MEMGETBADBLOCK, &offs)) < 0) { perror("ioctl(MEMGETBADBLOCK)"); goto closeall; } if (ret == 1) { baderaseblock = 1; - if (!quiet) - fprintf (stderr, "Bad block at %x, %u block(s) from %x will be skipped\n", (int) offs, blockalign, blockstart); - } + if (!quiet) + fprintf (stderr, "Bad block at %x, %u block(s) " + "from %x will be skipped\n", + (int) offs, blockalign, blockstart); + } if (baderaseblock) { mtdoffset = blockstart + meminfo.erasesize; } - offs += meminfo.erasesize / blockalign ; + offs += meminfo.erasesize / blockalign ; } while ( offs < blockstart + meminfo.erasesize ); } @@ -440,7 +463,7 @@ int main(int argc, char **argv) close(ifd); restoreoob: - if (oobinfochanged) { + if (oobinfochanged == 1) { if (ioctl (fd, MEMSETOOBSEL, &old_oobinfo) != 0) { perror ("MEMSETOOBSEL"); close (fd); -- cgit v1.2.3