summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorThomas Gleixner <tglx@cruncher.tec.linutronix.de>2006-05-30 00:41:57 +0200
committerThomas Gleixner <tglx@cruncher.tec.linutronix.de>2006-05-30 00:41:57 +0200
commit89edb1f57951f3f8a883ab8f3bbc7daa94f08285 (patch)
tree36265b5607dafec49d15bd54bd3a09a0eb8b73a2
parent01207e4c55264c8539b654edb66f7dbe01c9ed6a (diff)
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 <tglx@linutronix.de>
-rw-r--r--include/mtd/mtd-abi.h27
-rw-r--r--nanddump.c98
-rw-r--r--nandwrite.c105
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 <ctype.h>
+#include <errno.h>
#include <fcntl.h>
#include <stdio.h>
#include <stdlib.h>
@@ -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 <ctype.h>
+#include <errno.h>
#include <fcntl.h>
#include <stdio.h>
#include <stdlib.h>
@@ -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);