summaryrefslogtreecommitdiff
path: root/drivers/mtd/nand/nand_util.c
diff options
context:
space:
mode:
authorStefan Roese <sr@denx.de>2008-08-13 06:47:12 +0200
committerStefan Roese <sr@denx.de>2008-08-13 06:47:12 +0200
commit5a7ddf4e1fb9347f783eb1473c30187d7a22bd81 (patch)
tree5e30fc06d7bbd5b382b1a7b89f57cd81a5246f32 /drivers/mtd/nand/nand_util.c
parent9939ffd5fbf1f5aff4d8172531d4fc33797c62c8 (diff)
parent8641ff266ae6638da201747c239fd39ba34c4958 (diff)
downloadu-boot-imx-5a7ddf4e1fb9347f783eb1473c30187d7a22bd81.zip
u-boot-imx-5a7ddf4e1fb9347f783eb1473c30187d7a22bd81.tar.gz
u-boot-imx-5a7ddf4e1fb9347f783eb1473c30187d7a22bd81.tar.bz2
Merge branch 'master' of /home/stefan/git/u-boot/u-boot
Diffstat (limited to 'drivers/mtd/nand/nand_util.c')
-rw-r--r--drivers/mtd/nand/nand_util.c723
1 files changed, 227 insertions, 496 deletions
diff --git a/drivers/mtd/nand/nand_util.c b/drivers/mtd/nand/nand_util.c
index 828cc33..02fe914 100644
--- a/drivers/mtd/nand/nand_util.c
+++ b/drivers/mtd/nand/nand_util.c
@@ -39,6 +39,9 @@
#include <malloc.h>
#include <div64.h>
+
+#include <asm/errno.h>
+#include <linux/mtd/mtd.h>
#include <nand.h>
#include <jffs2/jffs2.h>
@@ -69,71 +72,33 @@ static int nand_block_bad_scrub(struct mtd_info *mtd, loff_t ofs, int getchip)
int nand_erase_opts(nand_info_t *meminfo, const nand_erase_options_t *opts)
{
struct jffs2_unknown_node cleanmarker;
- int clmpos = 0;
- int clmlen = 8;
erase_info_t erase;
ulong erase_length;
- int isNAND;
int bbtest = 1;
int result;
int percent_complete = -1;
int (*nand_block_bad_old)(struct mtd_info *, loff_t, int) = NULL;
const char *mtd_device = meminfo->name;
+ struct mtd_oob_ops oob_opts;
+ struct nand_chip *chip = meminfo->priv;
+ uint8_t buf[64];
+ memset(buf, 0, sizeof(buf));
memset(&erase, 0, sizeof(erase));
+ memset(&oob_opts, 0, sizeof(oob_opts));
erase.mtd = meminfo;
erase.len = meminfo->erasesize;
erase.addr = opts->offset;
erase_length = opts->length;
- isNAND = meminfo->type == MTD_NANDFLASH ? 1 : 0;
-
- if (opts->jffs2) {
- cleanmarker.magic = cpu_to_je16 (JFFS2_MAGIC_BITMASK);
- cleanmarker.nodetype = cpu_to_je16 (JFFS2_NODETYPE_CLEANMARKER);
- if (isNAND) {
- struct nand_oobinfo *oobinfo = &meminfo->oobinfo;
-
- /* check for autoplacement */
- if (oobinfo->useecc == MTD_NANDECC_AUTOPLACE) {
- /* get the position of the free bytes */
- if (!oobinfo->oobfree[0][1]) {
- printf(" Eeep. Autoplacement selected "
- "and no empty space in oob\n");
- return -1;
- }
- clmpos = oobinfo->oobfree[0][0];
- clmlen = oobinfo->oobfree[0][1];
- if (clmlen > 8)
- clmlen = 8;
- } else {
- /* legacy mode */
- switch (meminfo->oobsize) {
- case 8:
- clmpos = 6;
- clmlen = 2;
- break;
- case 16:
- clmpos = 8;
- clmlen = 8;
- break;
- case 64:
- clmpos = 16;
- clmlen = 8;
- break;
- }
- }
- cleanmarker.totlen = cpu_to_je32(8);
- } else {
- cleanmarker.totlen =
- cpu_to_je32(sizeof(struct jffs2_unknown_node));
- }
- cleanmarker.hdr_crc = cpu_to_je32(
- crc32_no_comp(0, (unsigned char *) &cleanmarker,
- sizeof(struct jffs2_unknown_node) - 4));
- }
+ cleanmarker.magic = cpu_to_je16 (JFFS2_MAGIC_BITMASK);
+ cleanmarker.nodetype = cpu_to_je16 (JFFS2_NODETYPE_CLEANMARKER);
+ cleanmarker.totlen = cpu_to_je32(8);
+ cleanmarker.hdr_crc = cpu_to_je32(
+ crc32_no_comp(0, (unsigned char *) &cleanmarker,
+ sizeof(struct jffs2_unknown_node) - 4));
/* scrub option allows to erase badblock. To prevent internal
* check from erase() method, set block check method to dummy
@@ -194,25 +159,21 @@ int nand_erase_opts(nand_info_t *meminfo, const nand_erase_options_t *opts)
/* format for JFFS2 ? */
if (opts->jffs2) {
- /* write cleanmarker */
- if (isNAND) {
- size_t written;
- result = meminfo->write_oob(meminfo,
- erase.addr + clmpos,
- clmlen,
- &written,
- (unsigned char *)
- &cleanmarker);
- if (result != 0) {
- printf("\n%s: MTD writeoob failure: %d\n",
- mtd_device, result);
- continue;
- }
- } else {
- printf("\n%s: this erase routine only supports"
- " NAND devices!\n",
- mtd_device);
+ chip->ops.len = chip->ops.ooblen = 64;
+ chip->ops.datbuf = NULL;
+ chip->ops.oobbuf = buf;
+ chip->ops.ooboffs = chip->badblockpos & ~0x01;
+
+ result = meminfo->write_oob(meminfo,
+ erase.addr + meminfo->oobsize,
+ &chip->ops);
+ if (result != 0) {
+ printf("\n%s: MTD writeoob failure: %d\n",
+ mtd_device, result);
+ continue;
}
+ else
+ printf("%s: MTD writeoob at 0x%08x\n",mtd_device, erase.addr + meminfo->oobsize );
}
if (!opts->quiet) {
@@ -232,11 +193,11 @@ int nand_erase_opts(nand_info_t *meminfo, const nand_erase_options_t *opts)
percent_complete = percent;
printf("\rErasing at 0x%x -- %3d%% complete.",
- erase.addr, percent);
+ erase.addr, percent);
if (opts->jffs2 && result == 0)
- printf(" Cleanmarker written at 0x%x.",
- erase.addr);
+ printf(" Cleanmarker written at 0x%x.",
+ erase.addr);
}
}
}
@@ -253,6 +214,9 @@ int nand_erase_opts(nand_info_t *meminfo, const nand_erase_options_t *opts)
return 0;
}
+/* XXX U-BOOT XXX */
+#if 0
+
#define MAX_PAGE_SIZE 2048
#define MAX_OOB_SIZE 64
@@ -263,443 +227,29 @@ static unsigned char data_buf[MAX_PAGE_SIZE];
static unsigned char oob_buf[MAX_OOB_SIZE];
/* OOB layouts to pass into the kernel as default */
-static struct nand_oobinfo none_oobinfo = {
+static struct nand_ecclayout none_ecclayout = {
.useecc = MTD_NANDECC_OFF,
};
-static struct nand_oobinfo jffs2_oobinfo = {
+static struct nand_ecclayout jffs2_ecclayout = {
.useecc = MTD_NANDECC_PLACE,
.eccbytes = 6,
.eccpos = { 0, 1, 2, 3, 6, 7 }
};
-static struct nand_oobinfo yaffs_oobinfo = {
+static struct nand_ecclayout yaffs_ecclayout = {
.useecc = MTD_NANDECC_PLACE,
.eccbytes = 6,
.eccpos = { 8, 9, 10, 13, 14, 15}
};
-static struct nand_oobinfo autoplace_oobinfo = {
+static struct nand_ecclayout autoplace_ecclayout = {
.useecc = MTD_NANDECC_AUTOPLACE
};
+#endif
-/**
- * nand_write_opts: - write image to NAND flash with support for various options
- *
- * @param meminfo NAND device to erase
- * @param opts write options (@see nand_write_options)
- * @return 0 in case of success
- *
- * This code is ported from nandwrite.c from Linux mtd utils by
- * Steven J. Hill and Thomas Gleixner.
- */
-int nand_write_opts(nand_info_t *meminfo, const nand_write_options_t *opts)
-{
- int imglen = 0;
- int pagelen;
- int baderaseblock;
- int blockstart = -1;
- loff_t offs;
- int readlen;
- int oobinfochanged = 0;
- int percent_complete = -1;
- struct nand_oobinfo old_oobinfo;
- ulong mtdoffset = opts->offset;
- ulong erasesize_blockalign;
- u_char *buffer = opts->buffer;
- size_t written;
- int result;
-
- if (opts->pad && opts->writeoob) {
- printf("Can't pad when oob data is present.\n");
- return -1;
- }
-
- /* set erasesize to specified number of blocks - to match
- * jffs2 (virtual) block size */
- if (opts->blockalign == 0) {
- erasesize_blockalign = meminfo->erasesize;
- } else {
- erasesize_blockalign = meminfo->erasesize * opts->blockalign;
- }
-
- /* make sure device page sizes are valid */
- if (!(meminfo->oobsize == 16 && meminfo->oobblock == 512)
- && !(meminfo->oobsize == 8 && meminfo->oobblock == 256)
- && !(meminfo->oobsize == 64 && meminfo->oobblock == 2048)) {
- printf("Unknown flash (not normal NAND)\n");
- return -1;
- }
-
- /* read the current oob info */
- memcpy(&old_oobinfo, &meminfo->oobinfo, sizeof(old_oobinfo));
-
- /* write without ecc? */
- if (opts->noecc) {
- memcpy(&meminfo->oobinfo, &none_oobinfo,
- sizeof(meminfo->oobinfo));
- oobinfochanged = 1;
- }
-
- /* autoplace ECC? */
- if (opts->autoplace && (old_oobinfo.useecc != MTD_NANDECC_AUTOPLACE)) {
-
- memcpy(&meminfo->oobinfo, &autoplace_oobinfo,
- sizeof(meminfo->oobinfo));
- oobinfochanged = 1;
- }
-
- /* force OOB layout for jffs2 or yaffs? */
- if (opts->forcejffs2 || opts->forceyaffs) {
- struct nand_oobinfo *oobsel =
- opts->forcejffs2 ? &jffs2_oobinfo : &yaffs_oobinfo;
-
- if (meminfo->oobsize == 8) {
- if (opts->forceyaffs) {
- printf("YAFSS cannot operate on "
- "256 Byte page size\n");
- goto restoreoob;
- }
- /* Adjust number of ecc bytes */
- jffs2_oobinfo.eccbytes = 3;
- }
-
- memcpy(&meminfo->oobinfo, oobsel, sizeof(meminfo->oobinfo));
- }
-
- /* get image length */
- imglen = opts->length;
- pagelen = meminfo->oobblock
- + ((opts->writeoob != 0) ? meminfo->oobsize : 0);
-
- /* check, if file is pagealigned */
- if ((!opts->pad) && ((imglen % pagelen) != 0)) {
- printf("Input block length is not page aligned\n");
- goto restoreoob;
- }
-
- /* check, if length fits into device */
- if (((imglen / pagelen) * meminfo->oobblock)
- > (meminfo->size - opts->offset)) {
- printf("Image %d bytes, NAND page %d bytes, "
- "OOB area %u bytes, device size %u bytes\n",
- imglen, pagelen, meminfo->oobblock, meminfo->size);
- printf("Input block does not fit into device\n");
- goto restoreoob;
- }
-
- if (!opts->quiet)
- printf("\n");
-
- /* get data from input and write to the device */
- while (imglen && (mtdoffset < meminfo->size)) {
-
- WATCHDOG_RESET ();
-
- /*
- * new eraseblock, check for bad block(s). Stay in the
- * loop to be sure if the offset changes because of
- * a bad block, that the next block that will be
- * written to is also checked. Thus avoiding errors if
- * the block(s) after the skipped block(s) is also bad
- * (number of blocks depending on the blockalign
- */
- while (blockstart != (mtdoffset & (~erasesize_blockalign+1))) {
- blockstart = mtdoffset & (~erasesize_blockalign+1);
- offs = blockstart;
- baderaseblock = 0;
-
- /* check all the blocks in an erase block for
- * bad blocks */
- do {
- int ret = meminfo->block_isbad(meminfo, offs);
-
- if (ret < 0) {
- printf("Bad block check failed\n");
- goto restoreoob;
- }
- if (ret == 1) {
- baderaseblock = 1;
- if (!opts->quiet)
- printf("\rBad block at 0x%lx "
- "in erase block from "
- "0x%x will be skipped\n",
- (long) offs,
- blockstart);
- }
-
- if (baderaseblock) {
- mtdoffset = blockstart
- + erasesize_blockalign;
- }
- offs += erasesize_blockalign
- / opts->blockalign;
- } while (offs < blockstart + erasesize_blockalign);
- }
-
- readlen = meminfo->oobblock;
- if (opts->pad && (imglen < readlen)) {
- readlen = imglen;
- memset(data_buf + readlen, 0xff,
- meminfo->oobblock - readlen);
- }
-
- /* read page data from input memory buffer */
- memcpy(data_buf, buffer, readlen);
- buffer += readlen;
-
- if (opts->writeoob) {
- /* read OOB data from input memory block, exit
- * on failure */
- memcpy(oob_buf, buffer, meminfo->oobsize);
- buffer += meminfo->oobsize;
-
- /* write OOB data first, as ecc will be placed
- * in there*/
- result = meminfo->write_oob(meminfo,
- mtdoffset,
- meminfo->oobsize,
- &written,
- (unsigned char *)
- &oob_buf);
-
- if (result != 0) {
- printf("\nMTD writeoob failure: %d\n",
- result);
- goto restoreoob;
- }
- imglen -= meminfo->oobsize;
- }
-
- /* write out the page data */
- result = meminfo->write(meminfo,
- mtdoffset,
- meminfo->oobblock,
- &written,
- (unsigned char *) &data_buf);
-
- if (result != 0) {
- printf("writing NAND page at offset 0x%lx failed\n",
- mtdoffset);
- goto restoreoob;
- }
- imglen -= readlen;
-
- if (!opts->quiet) {
- unsigned long long n = (unsigned long long)
- (opts->length-imglen) * 100;
- int percent;
-
- do_div(n, opts->length);
- percent = (int)n;
-
- /* output progress message only at whole percent
- * steps to reduce the number of messages printed
- * on (slow) serial consoles
- */
- if (percent != percent_complete) {
- printf("\rWriting data at 0x%lx "
- "-- %3d%% complete.",
- mtdoffset, percent);
- percent_complete = percent;
- }
- }
-
- mtdoffset += meminfo->oobblock;
- }
-
- if (!opts->quiet)
- printf("\n");
-
-restoreoob:
- if (oobinfochanged) {
- memcpy(&meminfo->oobinfo, &old_oobinfo,
- sizeof(meminfo->oobinfo));
- }
-
- if (imglen > 0) {
- printf("Data did not fit into device, due to bad blocks\n");
- return -1;
- }
-
- /* return happy */
- return 0;
-}
-
-/**
- * nand_read_opts: - read image from NAND flash with support for various options
- *
- * @param meminfo NAND device to erase
- * @param opts read options (@see struct nand_read_options)
- * @return 0 in case of success
- *
- */
-int nand_read_opts(nand_info_t *meminfo, const nand_read_options_t *opts)
-{
- int imglen = opts->length;
- int pagelen;
- int baderaseblock;
- int blockstart = -1;
- int percent_complete = -1;
- loff_t offs;
- size_t readlen;
- ulong mtdoffset = opts->offset;
- u_char *buffer = opts->buffer;
- int result;
-
- /* make sure device page sizes are valid */
- if (!(meminfo->oobsize == 16 && meminfo->oobblock == 512)
- && !(meminfo->oobsize == 8 && meminfo->oobblock == 256)
- && !(meminfo->oobsize == 64 && meminfo->oobblock == 2048)) {
- printf("Unknown flash (not normal NAND)\n");
- return -1;
- }
-
- pagelen = meminfo->oobblock
- + ((opts->readoob != 0) ? meminfo->oobsize : 0);
-
- /* check, if length is not larger than device */
- if (((imglen / pagelen) * meminfo->oobblock)
- > (meminfo->size - opts->offset)) {
- printf("Image %d bytes, NAND page %d bytes, "
- "OOB area %u bytes, device size %u bytes\n",
- imglen, pagelen, meminfo->oobblock, meminfo->size);
- printf("Input block is larger than device\n");
- return -1;
- }
-
- if (!opts->quiet)
- printf("\n");
-
- /* get data from input and write to the device */
- while (imglen && (mtdoffset < meminfo->size)) {
-
- WATCHDOG_RESET ();
-
- /*
- * new eraseblock, check for bad block(s). Stay in the
- * loop to be sure if the offset changes because of
- * a bad block, that the next block that will be
- * written to is also checked. Thus avoiding errors if
- * the block(s) after the skipped block(s) is also bad
- * (number of blocks depending on the blockalign
- */
- while (blockstart != (mtdoffset & (~meminfo->erasesize+1))) {
- blockstart = mtdoffset & (~meminfo->erasesize+1);
- offs = blockstart;
- baderaseblock = 0;
-
- /* check all the blocks in an erase block for
- * bad blocks */
- do {
- int ret = meminfo->block_isbad(meminfo, offs);
-
- if (ret < 0) {
- printf("Bad block check failed\n");
- return -1;
- }
- if (ret == 1) {
- baderaseblock = 1;
- if (!opts->quiet)
- printf("\rBad block at 0x%lx "
- "in erase block from "
- "0x%x will be skipped\n",
- (long) offs,
- blockstart);
- }
-
- if (baderaseblock) {
- mtdoffset = blockstart
- + meminfo->erasesize;
- }
- offs += meminfo->erasesize;
-
- } while (offs < blockstart + meminfo->erasesize);
- }
-
-
- /* read page data to memory buffer */
- result = meminfo->read(meminfo,
- mtdoffset,
- meminfo->oobblock,
- &readlen,
- (unsigned char *) &data_buf);
-
- if (result != 0) {
- printf("reading NAND page at offset 0x%lx failed\n",
- mtdoffset);
- return -1;
- }
-
- if (imglen < readlen) {
- readlen = imglen;
- }
-
- memcpy(buffer, data_buf, readlen);
- buffer += readlen;
- imglen -= readlen;
-
- if (opts->readoob) {
- result = meminfo->read_oob(meminfo,
- mtdoffset,
- meminfo->oobsize,
- &readlen,
- (unsigned char *)
- &oob_buf);
-
- if (result != 0) {
- printf("\nMTD readoob failure: %d\n",
- result);
- return -1;
- }
-
-
- if (imglen < readlen) {
- readlen = imglen;
- }
-
- memcpy(buffer, oob_buf, readlen);
-
- buffer += readlen;
- imglen -= readlen;
- }
-
- if (!opts->quiet) {
- unsigned long long n = (unsigned long long)
- (opts->length-imglen) * 100;
- int percent;
-
- do_div(n, opts->length);
- percent = (int)n;
-
- /* output progress message only at whole percent
- * steps to reduce the number of messages printed
- * on (slow) serial consoles
- */
- if (percent != percent_complete) {
- if (!opts->quiet)
- printf("\rReading data from 0x%lx "
- "-- %3d%% complete.",
- mtdoffset, percent);
- percent_complete = percent;
- }
- }
-
- mtdoffset += meminfo->oobblock;
- }
-
- if (!opts->quiet)
- printf("\n");
-
- if (imglen > 0) {
- printf("Could not read entire image due to bad blocks\n");
- return -1;
- }
-
- /* return happy */
- return 0;
-}
-
+/* XXX U-BOOT XXX */
+#if 0
/******************************************************************************
* Support for locking / unlocking operations of some NAND devices
*****************************************************************************/
@@ -784,7 +334,7 @@ int nand_get_lock_status(nand_info_t *meminfo, ulong offset)
this->select_chip(meminfo, chipnr);
- if ((offset & (meminfo->oobblock - 1)) != 0) {
+ if ((offset & (meminfo->writesize - 1)) != 0) {
printf ("nand_get_lock_status: "
"Start address must be beginning of "
"nand page!\n");
@@ -813,7 +363,7 @@ int nand_get_lock_status(nand_info_t *meminfo, ulong offset)
* @param meminfo nand mtd instance
* @param start start byte address
* @param length number of bytes to unlock (must be a multiple of
- * page size nand->oobblock)
+ * page size nand->writesize)
*
* @return 0 on success, -1 in case of error
*/
@@ -839,14 +389,14 @@ int nand_unlock(nand_info_t *meminfo, ulong start, ulong length)
goto out;
}
- if ((start & (meminfo->oobblock - 1)) != 0) {
+ if ((start & (meminfo->writesize - 1)) != 0) {
printf ("nand_unlock: Start address must be beginning of "
"nand page!\n");
ret = -1;
goto out;
}
- if (length == 0 || (length & (meminfo->oobblock - 1)) != 0) {
+ if (length == 0 || (length & (meminfo->writesize - 1)) != 0) {
printf ("nand_unlock: Length must be a multiple of nand page "
"size!\n");
ret = -1;
@@ -875,5 +425,186 @@ int nand_unlock(nand_info_t *meminfo, ulong start, ulong length)
this->select_chip(meminfo, -1);
return ret;
}
-
#endif
+
+/**
+ * get_len_incl_bad
+ *
+ * Check if length including bad blocks fits into device.
+ *
+ * @param nand NAND device
+ * @param offset offset in flash
+ * @param length image length
+ * @return image length including bad blocks
+ */
+static size_t get_len_incl_bad (nand_info_t *nand, size_t offset,
+ const size_t length)
+{
+ size_t len_incl_bad = 0;
+ size_t len_excl_bad = 0;
+ size_t block_len;
+
+ while (len_excl_bad < length) {
+ block_len = nand->erasesize - (offset & (nand->erasesize - 1));
+
+ if (!nand_block_isbad (nand, offset & ~(nand->erasesize - 1)))
+ len_excl_bad += block_len;
+
+ len_incl_bad += block_len;
+ offset += block_len;
+
+ if ((offset + len_incl_bad) >= nand->size)
+ break;
+ }
+
+ return len_incl_bad;
+}
+
+/**
+ * nand_write_skip_bad:
+ *
+ * Write image to NAND flash.
+ * Blocks that are marked bad are skipped and the is written to the next
+ * block 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
+ * @param buf buffer to read from
+ * @return 0 in case of success
+ */
+int nand_write_skip_bad(nand_info_t *nand, size_t offset, size_t *length,
+ u_char *buffer)
+{
+ int rval;
+ size_t left_to_write = *length;
+ size_t len_incl_bad;
+ u_char *p_buffer = buffer;
+
+ /* Reject writes, which are not page aligned */
+ if ((offset & (nand->writesize - 1)) != 0 ||
+ (*length & (nand->writesize - 1)) != 0) {
+ printf ("Attempt to write non page aligned data\n");
+ return -EINVAL;
+ }
+
+ len_incl_bad = get_len_incl_bad (nand, offset, *length);
+
+ if ((offset + len_incl_bad) >= nand->size) {
+ printf ("Attempt to write outside the flash area\n");
+ return -EINVAL;
+ }
+
+ if (len_incl_bad == *length) {
+ rval = nand_write (nand, offset, length, buffer);
+ if (rval != 0) {
+ printf ("NAND write to offset %x failed %d\n",
+ offset, rval);
+ return rval;
+ }
+ }
+
+ while (left_to_write > 0) {
+ size_t block_offset = offset & (nand->erasesize - 1);
+ size_t write_size;
+
+ if (nand_block_isbad (nand, offset & ~(nand->erasesize - 1))) {
+ printf ("Skip bad block 0x%08x\n",
+ offset & ~(nand->erasesize - 1));
+ offset += nand->erasesize - block_offset;
+ continue;
+ }
+
+ if (left_to_write < (nand->erasesize - block_offset))
+ write_size = left_to_write;
+ else
+ write_size = nand->erasesize - block_offset;
+
+ rval = nand_write (nand, offset, &write_size, p_buffer);
+ if (rval != 0) {
+ printf ("NAND write to offset %x failed %d\n",
+ offset, rval);
+ *length -= left_to_write;
+ return rval;
+ }
+
+ left_to_write -= write_size;
+ offset += write_size;
+ p_buffer += write_size;
+ }
+
+ return 0;
+}
+
+/**
+ * nand_read_skip_bad:
+ *
+ * Read image from NAND flash.
+ * Blocks that are marked bad are skipped and the next block is readen
+ * 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 buffer buffer to write to
+ * @return 0 in case of success
+ */
+int nand_read_skip_bad(nand_info_t *nand, size_t offset, size_t *length,
+ u_char *buffer)
+{
+ int rval;
+ size_t left_to_read = *length;
+ size_t len_incl_bad;
+ u_char *p_buffer = buffer;
+
+ len_incl_bad = get_len_incl_bad (nand, offset, *length);
+
+ if ((offset + len_incl_bad) >= nand->size) {
+ printf ("Attempt to read outside the flash area\n");
+ return -EINVAL;
+ }
+
+ if (len_incl_bad == *length) {
+ rval = nand_read (nand, offset, length, buffer);
+ if (rval != 0) {
+ printf ("NAND read from offset %x failed %d\n",
+ offset, rval);
+ return rval;
+ }
+ }
+
+ while (left_to_read > 0) {
+ size_t block_offset = offset & (nand->erasesize - 1);
+ size_t read_length;
+
+ if (nand_block_isbad (nand, offset & ~(nand->erasesize - 1))) {
+ printf ("Skipping bad block 0x%08x\n",
+ offset & ~(nand->erasesize - 1));
+ offset += nand->erasesize - block_offset;
+ continue;
+ }
+
+ if (left_to_read < (nand->erasesize - block_offset))
+ read_length = left_to_read;
+ else
+ read_length = nand->erasesize - block_offset;
+
+ rval = nand_read (nand, offset, &read_length, p_buffer);
+ if (rval != 0) {
+ printf ("NAND read from offset %x failed %d\n",
+ offset, rval);
+ *length -= left_to_read;
+ return rval;
+ }
+
+ left_to_read -= read_length;
+ offset += read_length;
+ p_buffer += read_length;
+ }
+
+ return 0;
+}
+
+#endif /* defined(CONFIG_CMD_NAND) && !defined(CFG_NAND_LEGACY) */