|
@@ -50,8 +50,8 @@
|
|
|
#include <nand.h>
|
|
|
#include <jffs2/jffs2.h>
|
|
|
|
|
|
-typedef struct erase_info erase_info_t;
|
|
|
-typedef struct mtd_info mtd_info_t;
|
|
|
+typedef struct erase_info erase_info_t;
|
|
|
+typedef struct mtd_info mtd_info_t;
|
|
|
|
|
|
/* support only for native endian JFFS2 */
|
|
|
#define cpu_to_je16(x) (x)
|
|
@@ -59,7 +59,7 @@ typedef struct mtd_info mtd_info_t;
|
|
|
|
|
|
/**
|
|
|
* nand_erase_opts: - erase NAND flash with support for various options
|
|
|
- * (jffs2 formating)
|
|
|
+ * (jffs2 formatting)
|
|
|
*
|
|
|
* @param meminfo NAND device to erase
|
|
|
* @param opts options, @see struct nand_erase_options
|
|
@@ -80,8 +80,8 @@ int nand_erase_opts(nand_info_t *meminfo, const nand_erase_options_t *opts)
|
|
|
struct mtd_oob_ops oob_opts;
|
|
|
struct nand_chip *chip = meminfo->priv;
|
|
|
|
|
|
- if ((opts->offset & (meminfo->writesize - 1)) != 0) {
|
|
|
- printf("Attempt to erase non page aligned data\n");
|
|
|
+ if ((opts->offset & (meminfo->erasesize - 1)) != 0) {
|
|
|
+ printf("Attempt to erase non block-aligned data\n");
|
|
|
return -1;
|
|
|
}
|
|
|
|
|
@@ -94,8 +94,8 @@ int nand_erase_opts(nand_info_t *meminfo, const nand_erase_options_t *opts)
|
|
|
erase_length = lldiv(opts->length + meminfo->erasesize - 1,
|
|
|
meminfo->erasesize);
|
|
|
|
|
|
- cleanmarker.magic = cpu_to_je16 (JFFS2_MAGIC_BITMASK);
|
|
|
- cleanmarker.nodetype = cpu_to_je16 (JFFS2_NODETYPE_CLEANMARKER);
|
|
|
+ cleanmarker.magic = cpu_to_je16(JFFS2_MAGIC_BITMASK);
|
|
|
+ cleanmarker.nodetype = cpu_to_je16(JFFS2_NODETYPE_CLEANMARKER);
|
|
|
cleanmarker.totlen = cpu_to_je32(8);
|
|
|
|
|
|
/* scrub option allows to erase badblock. To prevent internal
|
|
@@ -118,7 +118,7 @@ int nand_erase_opts(nand_info_t *meminfo, const nand_erase_options_t *opts)
|
|
|
erased_length < erase_length;
|
|
|
erase.addr += meminfo->erasesize) {
|
|
|
|
|
|
- WATCHDOG_RESET ();
|
|
|
+ WATCHDOG_RESET();
|
|
|
|
|
|
if (!opts->scrub && bbtest) {
|
|
|
int ret = meminfo->block_isbad(meminfo, erase.addr);
|
|
@@ -259,7 +259,7 @@ int nand_lock(struct mtd_info *mtd, int tight)
|
|
|
* flash
|
|
|
*
|
|
|
* @param mtd nand mtd instance
|
|
|
- * @param offset page address to query (muss be page aligned!)
|
|
|
+ * @param offset page address to query (must be page-aligned!)
|
|
|
*
|
|
|
* @return -1 in case of error
|
|
|
* >0 lock status:
|
|
@@ -281,7 +281,7 @@ int nand_get_lock_status(struct mtd_info *mtd, loff_t offset)
|
|
|
|
|
|
|
|
|
if ((offset & (mtd->writesize - 1)) != 0) {
|
|
|
- printf ("nand_get_lock_status: "
|
|
|
+ printf("nand_get_lock_status: "
|
|
|
"Start address must be beginning of "
|
|
|
"nand page!\n");
|
|
|
ret = -1;
|
|
@@ -332,20 +332,20 @@ int nand_unlock(struct mtd_info *mtd, loff_t start, size_t length,
|
|
|
/* check the WP bit */
|
|
|
chip->cmdfunc(mtd, NAND_CMD_STATUS, -1, -1);
|
|
|
if (!(chip->read_byte(mtd) & NAND_STATUS_WP)) {
|
|
|
- printf ("nand_unlock: Device is write protected!\n");
|
|
|
+ printf("nand_unlock: Device is write protected!\n");
|
|
|
ret = -1;
|
|
|
goto out;
|
|
|
}
|
|
|
|
|
|
if ((start & (mtd->erasesize - 1)) != 0) {
|
|
|
- printf ("nand_unlock: Start address must be beginning of "
|
|
|
+ printf("nand_unlock: Start address must be beginning of "
|
|
|
"nand block!\n");
|
|
|
ret = -1;
|
|
|
goto out;
|
|
|
}
|
|
|
|
|
|
if (length == 0 || (length & (mtd->erasesize - 1)) != 0) {
|
|
|
- printf ("nand_unlock: Length must be a multiple of nand block "
|
|
|
+ printf("nand_unlock: Length must be a multiple of nand block "
|
|
|
"size %08x!\n", mtd->erasesize);
|
|
|
ret = -1;
|
|
|
goto out;
|
|
@@ -485,7 +485,7 @@ int nand_write_skip_bad(nand_info_t *nand, loff_t offset, size_t *length,
|
|
|
pages = nand->erasesize / nand->writesize;
|
|
|
blocksize = (pages * nand->oobsize) + nand->erasesize;
|
|
|
if (*length % (nand->writesize + nand->oobsize)) {
|
|
|
- printf ("Attempt to write incomplete page"
|
|
|
+ printf("Attempt to write incomplete page"
|
|
|
" in yaffs mode\n");
|
|
|
return -EINVAL;
|
|
|
}
|
|
@@ -507,25 +507,25 @@ int nand_write_skip_bad(nand_info_t *nand, loff_t offset, size_t *length,
|
|
|
* partition boundary). So don't try to handle that.
|
|
|
*/
|
|
|
if ((offset & (nand->writesize - 1)) != 0) {
|
|
|
- printf ("Attempt to write non page aligned data\n");
|
|
|
+ printf("Attempt to write non page-aligned data\n");
|
|
|
*length = 0;
|
|
|
return -EINVAL;
|
|
|
}
|
|
|
|
|
|
need_skip = check_skip_len(nand, offset, *length);
|
|
|
if (need_skip < 0) {
|
|
|
- printf ("Attempt to write outside the flash area\n");
|
|
|
+ printf("Attempt to write outside the flash area\n");
|
|
|
*length = 0;
|
|
|
return -EINVAL;
|
|
|
}
|
|
|
|
|
|
if (!need_skip && !(flags & WITH_DROP_FFS)) {
|
|
|
- rval = nand_write (nand, offset, length, buffer);
|
|
|
+ rval = nand_write(nand, offset, length, buffer);
|
|
|
if (rval == 0)
|
|
|
return 0;
|
|
|
|
|
|
*length = 0;
|
|
|
- printf ("NAND write to offset %llx failed %d\n",
|
|
|
+ printf("NAND write to offset %llx failed %d\n",
|
|
|
offset, rval);
|
|
|
return rval;
|
|
|
}
|
|
@@ -534,10 +534,10 @@ int nand_write_skip_bad(nand_info_t *nand, loff_t offset, size_t *length,
|
|
|
size_t block_offset = offset & (nand->erasesize - 1);
|
|
|
size_t write_size, truncated_write_size;
|
|
|
|
|
|
- WATCHDOG_RESET ();
|
|
|
+ WATCHDOG_RESET();
|
|
|
|
|
|
- if (nand_block_isbad (nand, offset & ~(nand->erasesize - 1))) {
|
|
|
- printf ("Skip bad block 0x%08llx\n",
|
|
|
+ if (nand_block_isbad(nand, offset & ~(nand->erasesize - 1))) {
|
|
|
+ printf("Skip bad block 0x%08llx\n",
|
|
|
offset & ~(nand->erasesize - 1));
|
|
|
offset += nand->erasesize - block_offset;
|
|
|
continue;
|
|
@@ -592,7 +592,7 @@ int nand_write_skip_bad(nand_info_t *nand, loff_t offset, size_t *length,
|
|
|
}
|
|
|
|
|
|
if (rval != 0) {
|
|
|
- printf ("NAND write to offset %llx failed %d\n",
|
|
|
+ printf("NAND write to offset %llx failed %d\n",
|
|
|
offset, rval);
|
|
|
*length -= left_to_write;
|
|
|
return rval;
|
|
@@ -608,13 +608,13 @@ int nand_write_skip_bad(nand_info_t *nand, loff_t offset, size_t *length,
|
|
|
* nand_read_skip_bad:
|
|
|
*
|
|
|
* Read image from NAND flash.
|
|
|
- * Blocks that are marked bad are skipped and the next block is readen
|
|
|
+ * Blocks that are marked bad are skipped and the next block is read
|
|
|
* instead as long as the image is short enough to fit even after skipping the
|
|
|
* bad blocks.
|
|
|
*
|
|
|
* @param nand NAND device
|
|
|
* @param offset offset in flash
|
|
|
- * @param length buffer length, on return holds remaining bytes to read
|
|
|
+ * @param length buffer length, on return holds number of read bytes
|
|
|
* @param buffer buffer to write to
|
|
|
* @return 0 in case of success
|
|
|
*/
|
|
@@ -627,25 +627,25 @@ int nand_read_skip_bad(nand_info_t *nand, loff_t offset, size_t *length,
|
|
|
int need_skip;
|
|
|
|
|
|
if ((offset & (nand->writesize - 1)) != 0) {
|
|
|
- printf ("Attempt to read non page aligned data\n");
|
|
|
+ printf("Attempt to read non page-aligned data\n");
|
|
|
*length = 0;
|
|
|
return -EINVAL;
|
|
|
}
|
|
|
|
|
|
need_skip = check_skip_len(nand, offset, *length);
|
|
|
if (need_skip < 0) {
|
|
|
- printf ("Attempt to read outside the flash area\n");
|
|
|
+ printf("Attempt to read outside the flash area\n");
|
|
|
*length = 0;
|
|
|
return -EINVAL;
|
|
|
}
|
|
|
|
|
|
if (!need_skip) {
|
|
|
- rval = nand_read (nand, offset, length, buffer);
|
|
|
+ rval = nand_read(nand, offset, length, buffer);
|
|
|
if (!rval || rval == -EUCLEAN)
|
|
|
return 0;
|
|
|
|
|
|
*length = 0;
|
|
|
- printf ("NAND read from offset %llx failed %d\n",
|
|
|
+ printf("NAND read from offset %llx failed %d\n",
|
|
|
offset, rval);
|
|
|
return rval;
|
|
|
}
|
|
@@ -654,10 +654,10 @@ int nand_read_skip_bad(nand_info_t *nand, loff_t offset, size_t *length,
|
|
|
size_t block_offset = offset & (nand->erasesize - 1);
|
|
|
size_t read_length;
|
|
|
|
|
|
- WATCHDOG_RESET ();
|
|
|
+ WATCHDOG_RESET();
|
|
|
|
|
|
- if (nand_block_isbad (nand, offset & ~(nand->erasesize - 1))) {
|
|
|
- printf ("Skipping bad block 0x%08llx\n",
|
|
|
+ if (nand_block_isbad(nand, offset & ~(nand->erasesize - 1))) {
|
|
|
+ printf("Skipping bad block 0x%08llx\n",
|
|
|
offset & ~(nand->erasesize - 1));
|
|
|
offset += nand->erasesize - block_offset;
|
|
|
continue;
|
|
@@ -668,9 +668,9 @@ int nand_read_skip_bad(nand_info_t *nand, loff_t offset, size_t *length,
|
|
|
else
|
|
|
read_length = nand->erasesize - block_offset;
|
|
|
|
|
|
- rval = nand_read (nand, offset, &read_length, p_buffer);
|
|
|
+ rval = nand_read(nand, offset, &read_length, p_buffer);
|
|
|
if (rval && rval != -EUCLEAN) {
|
|
|
- printf ("NAND read from offset %llx failed %d\n",
|
|
|
+ printf("NAND read from offset %llx failed %d\n",
|
|
|
offset, rval);
|
|
|
*length -= left_to_read;
|
|
|
return rval;
|
|
@@ -683,3 +683,125 @@ int nand_read_skip_bad(nand_info_t *nand, loff_t offset, size_t *length,
|
|
|
|
|
|
return 0;
|
|
|
}
|
|
|
+
|
|
|
+#ifdef CONFIG_CMD_NAND_TORTURE
|
|
|
+
|
|
|
+/**
|
|
|
+ * check_pattern:
|
|
|
+ *
|
|
|
+ * Check if buffer contains only a certain byte pattern.
|
|
|
+ *
|
|
|
+ * @param buf buffer to check
|
|
|
+ * @param patt the pattern to check
|
|
|
+ * @param size buffer size in bytes
|
|
|
+ * @return 1 if there are only patt bytes in buf
|
|
|
+ * 0 if something else was found
|
|
|
+ */
|
|
|
+static int check_pattern(const u_char *buf, u_char patt, int size)
|
|
|
+{
|
|
|
+ int i;
|
|
|
+
|
|
|
+ for (i = 0; i < size; i++)
|
|
|
+ if (buf[i] != patt)
|
|
|
+ return 0;
|
|
|
+ return 1;
|
|
|
+}
|
|
|
+
|
|
|
+/**
|
|
|
+ * nand_torture:
|
|
|
+ *
|
|
|
+ * Torture a block of NAND flash.
|
|
|
+ * This is useful to determine if a block that caused a write error is still
|
|
|
+ * good or should be marked as bad.
|
|
|
+ *
|
|
|
+ * @param nand NAND device
|
|
|
+ * @param offset offset in flash
|
|
|
+ * @return 0 if the block is still good
|
|
|
+ */
|
|
|
+int nand_torture(nand_info_t *nand, loff_t offset)
|
|
|
+{
|
|
|
+ u_char patterns[] = {0xa5, 0x5a, 0x00};
|
|
|
+ struct erase_info instr = {
|
|
|
+ .mtd = nand,
|
|
|
+ .addr = offset,
|
|
|
+ .len = nand->erasesize,
|
|
|
+ };
|
|
|
+ size_t retlen;
|
|
|
+ int err, ret = -1, i, patt_count;
|
|
|
+ u_char *buf;
|
|
|
+
|
|
|
+ if ((offset & (nand->erasesize - 1)) != 0) {
|
|
|
+ puts("Attempt to torture a block at a non block-aligned offset\n");
|
|
|
+ return -EINVAL;
|
|
|
+ }
|
|
|
+
|
|
|
+ if (offset + nand->erasesize > nand->size) {
|
|
|
+ puts("Attempt to torture a block outside the flash area\n");
|
|
|
+ return -EINVAL;
|
|
|
+ }
|
|
|
+
|
|
|
+ patt_count = ARRAY_SIZE(patterns);
|
|
|
+
|
|
|
+ buf = malloc(nand->erasesize);
|
|
|
+ if (buf == NULL) {
|
|
|
+ puts("Out of memory for erase block buffer\n");
|
|
|
+ return -ENOMEM;
|
|
|
+ }
|
|
|
+
|
|
|
+ for (i = 0; i < patt_count; i++) {
|
|
|
+ err = nand->erase(nand, &instr);
|
|
|
+ if (err) {
|
|
|
+ printf("%s: erase() failed for block at 0x%llx: %d\n",
|
|
|
+ nand->name, instr.addr, err);
|
|
|
+ goto out;
|
|
|
+ }
|
|
|
+
|
|
|
+ /* Make sure the block contains only 0xff bytes */
|
|
|
+ err = nand->read(nand, offset, nand->erasesize, &retlen, buf);
|
|
|
+ if ((err && err != -EUCLEAN) || retlen != nand->erasesize) {
|
|
|
+ printf("%s: read() failed for block at 0x%llx: %d\n",
|
|
|
+ nand->name, instr.addr, err);
|
|
|
+ goto out;
|
|
|
+ }
|
|
|
+
|
|
|
+ err = check_pattern(buf, 0xff, nand->erasesize);
|
|
|
+ if (!err) {
|
|
|
+ printf("Erased block at 0x%llx, but a non-0xff byte was found\n",
|
|
|
+ offset);
|
|
|
+ ret = -EIO;
|
|
|
+ goto out;
|
|
|
+ }
|
|
|
+
|
|
|
+ /* Write a pattern and check it */
|
|
|
+ memset(buf, patterns[i], nand->erasesize);
|
|
|
+ err = nand->write(nand, offset, nand->erasesize, &retlen, buf);
|
|
|
+ if (err || retlen != nand->erasesize) {
|
|
|
+ printf("%s: write() failed for block at 0x%llx: %d\n",
|
|
|
+ nand->name, instr.addr, err);
|
|
|
+ goto out;
|
|
|
+ }
|
|
|
+
|
|
|
+ err = nand->read(nand, offset, nand->erasesize, &retlen, buf);
|
|
|
+ if ((err && err != -EUCLEAN) || retlen != nand->erasesize) {
|
|
|
+ printf("%s: read() failed for block at 0x%llx: %d\n",
|
|
|
+ nand->name, instr.addr, err);
|
|
|
+ goto out;
|
|
|
+ }
|
|
|
+
|
|
|
+ err = check_pattern(buf, patterns[i], nand->erasesize);
|
|
|
+ if (!err) {
|
|
|
+ printf("Pattern 0x%.2x checking failed for block at "
|
|
|
+ "0x%llx\n", patterns[i], offset);
|
|
|
+ ret = -EIO;
|
|
|
+ goto out;
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ ret = 0;
|
|
|
+
|
|
|
+out:
|
|
|
+ free(buf);
|
|
|
+ return ret;
|
|
|
+}
|
|
|
+
|
|
|
+#endif
|