From cfa460adfdefcc30d104e1a9ee44994ee349bb7b Mon Sep 17 00:00:00 2001 From: William Juul Date: Wed, 31 Oct 2007 13:53:06 +0100 Subject: Update MTD to that of Linux 2.6.22.1 A lot changed in the Linux MTD code, since it was last ported from Linux to U-Boot. This patch takes U-Boot NAND support to the level of Linux 2.6.22.1 and will enable support for very large NAND devices (4KB pages) and ease the compatibility between U-Boot and Linux filesystems. This patch is tested on two custom boards with PPC and ARM processors running YAFFS in U-Boot and Linux using gcc-4.1.2 cross compilers. MAKEALL ppc/arm has some issues: * DOC/OneNand/nand_spl is not building (I have not tried porting these parts, and since I do not have any HW and I am not familiar with this code/HW I think its best left to someone else.) Except for the issues mentioned above, I have ported all drivers necessary to run MAKEALL ppc/arm without errors and warnings. Many drivers were trivial to port, but some were not so trivial. The following drivers must be examined carefully and maybe rewritten to some degree: cpu/ppc4xx/ndfc.c cpu/arm926ejs/davinci/nand.c board/delta/nand.c board/zylonite/nand.c Signed-off-by: William Juul Signed-off-by: Stig Olsen Signed-off-by: Scott Wood --- drivers/mtd/nand/nand_util.c | 359 +++++++++++++++++++++++++++++-------------- 1 file changed, 245 insertions(+), 114 deletions(-) (limited to 'drivers/mtd/nand/nand_util.c') diff --git a/drivers/mtd/nand/nand_util.c b/drivers/mtd/nand/nand_util.c index 828cc338a..78e70cc80 100644 --- a/drivers/mtd/nand/nand_util.c +++ b/drivers/mtd/nand/nand_util.c @@ -39,6 +39,9 @@ #include #include + +#include +#include #include #include @@ -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 @@ -163,7 +128,7 @@ int nand_erase_opts(nand_info_t *meminfo, const nand_erase_options_t *opts) for (; erase.addr < opts->offset + erase_length; erase.addr += meminfo->erasesize) { - + WATCHDOG_RESET (); if (!opts->scrub && bbtest) { @@ -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,26 +227,189 @@ 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_fill_oob - [Internal] Transfer client buffer to oob + * @chip: nand chip structure + * @oob: oob data buffer + * @ops: oob ops structure + * + * Copied from nand_base.c + */ +static uint8_t *nand_fill_oob(struct nand_chip *chip, uint8_t *oob, + struct mtd_oob_ops *ops) +{ + size_t len = ops->ooblen; + + switch(ops->mode) { + + case MTD_OOB_PLACE: + case MTD_OOB_RAW: + memcpy(chip->oob_poi + ops->ooboffs, oob, len); + return oob + len; + + case MTD_OOB_AUTO: { + struct nand_oobfree *free = chip->ecc.layout->oobfree; + uint32_t boffs = 0, woffs = ops->ooboffs; + size_t bytes = 0; + + for(; free->length && len; free++, len -= bytes) { + /* Write request not from offset 0 ? */ + if (unlikely(woffs)) { + if (woffs >= free->length) { + woffs -= free->length; + continue; + } + boffs = free->offset + woffs; + bytes = min_t(size_t, len, + (free->length - woffs)); + woffs = 0; + } else { + bytes = min_t(size_t, len, free->length); + boffs = free->offset; + } + memcpy(chip->oob_poi + boffs, oob, bytes); + oob += bytes; + } + return oob; + } + default: + BUG(); + } + return NULL; +} + +#define NOTALIGNED(x) (x & (chip->subpagesize - 1)) != 0 + + +/* copied from nand_base.c: nand_do_write_ops() + * Only very small changes + */ +int nand_write_opts(nand_info_t *mtd, loff_t to, mtd_oob_ops_t *ops) +{ + int chipnr, realpage, page, blockmask, column; + struct nand_chip *chip = mtd->priv; + uint32_t writelen = ops->len; + uint8_t *oob = ops->oobbuf; + uint8_t *buf = ops->datbuf; + int ret, subpage; + + ops->retlen = 0; + if (!writelen) + return 0; + + printk("nand_write_opts: to: 0x%08x, ops->len: 0x%08x\n", to, ops->len); + + /* reject writes, which are not page aligned */ + if (NOTALIGNED(to) || NOTALIGNED(ops->len)) { + printk(KERN_NOTICE "nand_write: " + "Attempt to write not page aligned data\n"); + return -EINVAL; + } + + column = to & (mtd->writesize - 1); + subpage = column || (writelen & (mtd->writesize - 1)); + + if (subpage && oob) { + printk(KERN_NOTICE "nand_write: " + "Attempt to write oob to subpage\n"); + return -EINVAL; + } + + chipnr = (int)(to >> chip->chip_shift); + chip->select_chip(mtd, chipnr); + + /* XXX U-BOOT XXX */ +#if 0 + /* Check, if it is write protected */ + if (nand_check_wp(mtd)) + return -EIO; +#endif + + realpage = (int)(to >> chip->page_shift); + page = realpage & chip->pagemask; + blockmask = (1 << (chip->phys_erase_shift - chip->page_shift)) - 1; + + /* Invalidate the page cache, when we write to the cached page */ + if (to <= (chip->pagebuf << chip->page_shift) && + (chip->pagebuf << chip->page_shift) < (to + ops->len)) + chip->pagebuf = -1; + + /* If we're not given explicit OOB data, let it be 0xFF */ + if (likely(!oob)) { + printf("!oob, writing %d bytes with 0xff to chip->oob_poi (0x%08x)\n", mtd->oobsize, chip->oob_poi); + memset(chip->oob_poi, 0xff, mtd->oobsize); + } + + while(1) { + int bytes = mtd->writesize; + int cached = writelen > bytes && page != blockmask; + uint8_t *wbuf = buf; + + /* Partial page write ? */ + if (unlikely(column || writelen < (mtd->writesize - 1))) { + cached = 0; + bytes = min_t(int, bytes - column, (int) writelen); + chip->pagebuf = -1; + memset(chip->buffers->databuf, 0xff, mtd->writesize); + memcpy(&chip->buffers->databuf[column], buf, bytes); + wbuf = chip->buffers->databuf; + } + + if (unlikely(oob)) + oob = nand_fill_oob(chip, oob, ops); + + ret = chip->write_page(mtd, chip, wbuf, page, cached, + (ops->mode == MTD_OOB_RAW)); + if (ret) + break; + + writelen -= bytes; + if (!writelen) + break; + + column = 0; + buf += bytes; + realpage++; + + page = realpage & chip->pagemask; + /* Check, if we cross a chip boundary */ + if (!page) { + chipnr++; + chip->select_chip(mtd, -1); + chip->select_chip(mtd, chipnr); + } + } + + ops->retlen = ops->len - writelen; + if (unlikely(oob)) + ops->oobretlen = ops->ooblen; + return ret; +} + +/* XXX U-BOOT XXX */ +#if 0 /** * nand_write_opts: - write image to NAND flash with support for various options * @@ -301,9 +428,9 @@ int nand_write_opts(nand_info_t *meminfo, const nand_write_options_t *opts) int blockstart = -1; loff_t offs; int readlen; - int oobinfochanged = 0; + int ecclayoutchanged = 0; int percent_complete = -1; - struct nand_oobinfo old_oobinfo; + struct nand_ecclayout old_ecclayout; ulong mtdoffset = opts->offset; ulong erasesize_blockalign; u_char *buffer = opts->buffer; @@ -324,35 +451,35 @@ int nand_write_opts(nand_info_t *meminfo, const nand_write_options_t *opts) } /* 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)) { + if (!(meminfo->oobsize == 16 && meminfo->writesize == 512) + && !(meminfo->oobsize == 8 && meminfo->writesize == 256) + && !(meminfo->oobsize == 64 && meminfo->writesize == 2048)) { printf("Unknown flash (not normal NAND)\n"); return -1; } /* read the current oob info */ - memcpy(&old_oobinfo, &meminfo->oobinfo, sizeof(old_oobinfo)); + memcpy(&old_ecclayout, &meminfo->ecclayout, sizeof(old_ecclayout)); /* write without ecc? */ if (opts->noecc) { - memcpy(&meminfo->oobinfo, &none_oobinfo, - sizeof(meminfo->oobinfo)); - oobinfochanged = 1; + memcpy(&meminfo->ecclayout, &none_ecclayout, + sizeof(meminfo->ecclayout)); + ecclayoutchanged = 1; } /* autoplace ECC? */ - if (opts->autoplace && (old_oobinfo.useecc != MTD_NANDECC_AUTOPLACE)) { + if (opts->autoplace && (old_ecclayout.useecc != MTD_NANDECC_AUTOPLACE)) { - memcpy(&meminfo->oobinfo, &autoplace_oobinfo, - sizeof(meminfo->oobinfo)); - oobinfochanged = 1; + memcpy(&meminfo->ecclayout, &autoplace_ecclayout, + sizeof(meminfo->ecclayout)); + ecclayoutchanged = 1; } /* force OOB layout for jffs2 or yaffs? */ if (opts->forcejffs2 || opts->forceyaffs) { - struct nand_oobinfo *oobsel = - opts->forcejffs2 ? &jffs2_oobinfo : &yaffs_oobinfo; + struct nand_ecclayout *oobsel = + opts->forcejffs2 ? &jffs2_ecclayout : &yaffs_ecclayout; if (meminfo->oobsize == 8) { if (opts->forceyaffs) { @@ -361,15 +488,15 @@ int nand_write_opts(nand_info_t *meminfo, const nand_write_options_t *opts) goto restoreoob; } /* Adjust number of ecc bytes */ - jffs2_oobinfo.eccbytes = 3; + jffs2_ecclayout.eccbytes = 3; } - memcpy(&meminfo->oobinfo, oobsel, sizeof(meminfo->oobinfo)); + memcpy(&meminfo->ecclayout, oobsel, sizeof(meminfo->ecclayout)); } /* get image length */ imglen = opts->length; - pagelen = meminfo->oobblock + pagelen = meminfo->writesize + ((opts->writeoob != 0) ? meminfo->oobsize : 0); /* check, if file is pagealigned */ @@ -379,11 +506,11 @@ int nand_write_opts(nand_info_t *meminfo, const nand_write_options_t *opts) } /* check, if length fits into device */ - if (((imglen / pagelen) * meminfo->oobblock) + if (((imglen / pagelen) * meminfo->writesize) > (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); + imglen, pagelen, meminfo->writesize, meminfo->size); printf("Input block does not fit into device\n"); goto restoreoob; } @@ -437,11 +564,11 @@ int nand_write_opts(nand_info_t *meminfo, const nand_write_options_t *opts) } while (offs < blockstart + erasesize_blockalign); } - readlen = meminfo->oobblock; + readlen = meminfo->writesize; if (opts->pad && (imglen < readlen)) { readlen = imglen; memset(data_buf + readlen, 0xff, - meminfo->oobblock - readlen); + meminfo->writesize - readlen); } /* read page data from input memory buffer */ @@ -474,7 +601,7 @@ int nand_write_opts(nand_info_t *meminfo, const nand_write_options_t *opts) /* write out the page data */ result = meminfo->write(meminfo, mtdoffset, - meminfo->oobblock, + meminfo->writesize, &written, (unsigned char *) &data_buf); @@ -505,16 +632,16 @@ int nand_write_opts(nand_info_t *meminfo, const nand_write_options_t *opts) } } - mtdoffset += meminfo->oobblock; + mtdoffset += meminfo->writesize; } if (!opts->quiet) printf("\n"); restoreoob: - if (oobinfochanged) { - memcpy(&meminfo->oobinfo, &old_oobinfo, - sizeof(meminfo->oobinfo)); + if (ecclayoutchanged) { + memcpy(&meminfo->ecclayout, &old_ecclayout, + sizeof(meminfo->ecclayout)); } if (imglen > 0) { @@ -548,22 +675,22 @@ int nand_read_opts(nand_info_t *meminfo, const nand_read_options_t *opts) 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)) { + if (!(meminfo->oobsize == 16 && meminfo->writesize == 512) + && !(meminfo->oobsize == 8 && meminfo->writesize == 256) + && !(meminfo->oobsize == 64 && meminfo->writesize == 2048)) { printf("Unknown flash (not normal NAND)\n"); return -1; } - pagelen = meminfo->oobblock + pagelen = meminfo->writesize + ((opts->readoob != 0) ? meminfo->oobsize : 0); /* check, if length is not larger than device */ - if (((imglen / pagelen) * meminfo->oobblock) + if (((imglen / pagelen) * meminfo->writesize) > (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); + imglen, pagelen, meminfo->writesize, meminfo->size); printf("Input block is larger than device\n"); return -1; } @@ -621,7 +748,7 @@ int nand_read_opts(nand_info_t *meminfo, const nand_read_options_t *opts) /* read page data to memory buffer */ result = meminfo->read(meminfo, mtdoffset, - meminfo->oobblock, + meminfo->writesize, &readlen, (unsigned char *) &data_buf); @@ -685,7 +812,7 @@ int nand_read_opts(nand_info_t *meminfo, const nand_read_options_t *opts) } } - mtdoffset += meminfo->oobblock; + mtdoffset += meminfo->writesize; } if (!opts->quiet) @@ -699,7 +826,10 @@ int nand_read_opts(nand_info_t *meminfo, const nand_read_options_t *opts) /* return happy */ return 0; } +#endif +/* XXX U-BOOT XXX */ +#if 0 /****************************************************************************** * Support for locking / unlocking operations of some NAND devices *****************************************************************************/ @@ -784,7 +914,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 +943,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 +969,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 +1005,6 @@ int nand_unlock(nand_info_t *meminfo, ulong start, ulong length) this->select_chip(meminfo, -1); return ret; } +#endif #endif -- cgit v1.2.3-70-g09d2 From 4cbb651b29cb64d378a06729970e1e153bb605b1 Mon Sep 17 00:00:00 2001 From: William Juul Date: Thu, 8 Nov 2007 10:39:53 +0100 Subject: Remove white space at end. Signed-off-by: William Juul Signed-off-by: Scott Wood --- board/bf537-stamp/nand.c | 4 ++-- board/dave/PPChameleonEVB/nand.c | 2 +- board/nc650/nand.c | 2 +- board/netstar/nand.c | 2 +- board/prodrive/alpr/nand.c | 2 +- board/sc3/sc3nand.c | 4 ++-- common/cmd_nand.c | 2 +- cpu/arm926ejs/davinci/nand.c | 2 +- drivers/mtd/nand/nand_base.c | 4 ++-- drivers/mtd/nand/nand_util.c | 10 +++++----- include/linux/mtd/nand.h | 2 +- 11 files changed, 18 insertions(+), 18 deletions(-) (limited to 'drivers/mtd/nand/nand_util.c') diff --git a/board/bf537-stamp/nand.c b/board/bf537-stamp/nand.c index bdf1d6ee4..d90bdd071 100644 --- a/board/bf537-stamp/nand.c +++ b/board/bf537-stamp/nand.c @@ -50,7 +50,7 @@ static void bfin_hwcontrol(struct mtd_info *mtd, int cmd, unsigned int ctrl) if( ctrl & NAND_ALE ) IO_ADDR_W = CFG_NAND_BASE + BFIN_NAND_ALE; else - IO_ADDR_W = CFG_NAND_BASE; + IO_ADDR_W = CFG_NAND_BASE; this->IO_ADDR_W = (void __iomem *) IO_ADDR_W; } this->IO_ADDR_R = this->IO_ADDR_W; @@ -59,7 +59,7 @@ static void bfin_hwcontrol(struct mtd_info *mtd, int cmd, unsigned int ctrl) SSYNC(); if (cmd != NAND_CMD_NONE) - writeb(cmd, this->IO_ADDR_W); + writeb(cmd, this->IO_ADDR_W); } int bfin_device_ready(struct mtd_info *mtd) diff --git a/board/dave/PPChameleonEVB/nand.c b/board/dave/PPChameleonEVB/nand.c index 4bc4257c8..ea6400c04 100644 --- a/board/dave/PPChameleonEVB/nand.c +++ b/board/dave/PPChameleonEVB/nand.c @@ -52,7 +52,7 @@ static void ppchameleonevb_hwcontrol(struct mtd_info *mtd, int cmd, unsigned int } if (cmd != NAND_CMD_NONE) - writeb(cmd, this->IO_ADDR_W); + writeb(cmd, this->IO_ADDR_W); } diff --git a/board/nc650/nand.c b/board/nc650/nand.c index faec6053f..f59f5e2c9 100644 --- a/board/nc650/nand.c +++ b/board/nc650/nand.c @@ -48,7 +48,7 @@ static void nc650_hwcontrol(struct mtd_info *mtd, int cmd, unsigned int ctrl) } if (cmd != NAND_CMD_NONE) - writeb(cmd, this->IO_ADDR_W); + writeb(cmd, this->IO_ADDR_W); } #elif defined(CONFIG_IDS852_REV2) /* diff --git a/board/netstar/nand.c b/board/netstar/nand.c index 302d78efe..961c92112 100644 --- a/board/netstar/nand.c +++ b/board/netstar/nand.c @@ -46,7 +46,7 @@ static void netstar_nand_hwcontrol(struct mtd_info *mtd, int cmd, unsigned int c IO_ADDR_W |= MASK_ALE; } this->IO_ADDR_W = (void __iomem *) IO_ADDR_W; - + if (cmd != NAND_CMD_NONE) writeb(cmd, this->IO_ADDR_W); } diff --git a/board/prodrive/alpr/nand.c b/board/prodrive/alpr/nand.c index 3224d3dd6..e1495feea 100644 --- a/board/prodrive/alpr/nand.c +++ b/board/prodrive/alpr/nand.c @@ -57,7 +57,7 @@ static struct alpr_ndfc_regs *alpr_ndfc = NULL; * There are 2 NAND devices on the board, a Hynix HY27US08561A (1 GByte). */ static void alpr_nand_hwcontrol(struct mtd_info *mtd, int cmd, unsigned int ctrl) -{ +{ struct nand_chip *this = mtd->priv; if (ctrl & NAND_CTRL_CHANGE) { diff --git a/board/sc3/sc3nand.c b/board/sc3/sc3nand.c index 2f2e74589..8ead7c850 100644 --- a/board/sc3/sc3nand.c +++ b/board/sc3/sc3nand.c @@ -46,7 +46,7 @@ static void sc3_nand_hwcontrol(struct mtd_info *mtd, int cmd, unsigned int ctrl) if ( ctrl & NAND_CLE ) set_bit (SC3_NAND_CLE, sc3_control_base); else - clear_bit (SC3_NAND_CLE, sc3_control_base); + clear_bit (SC3_NAND_CLE, sc3_control_base); if ( ctrl & NAND_ALE ) set_bit (SC3_NAND_ALE, sc3_control_base); else @@ -54,7 +54,7 @@ static void sc3_nand_hwcontrol(struct mtd_info *mtd, int cmd, unsigned int ctrl) if ( ctrl & NAND_NCE ) set_bit (SC3_NAND_CE, sc3_control_base); else - clear_bit (SC3_NAND_CE, sc3_control_base); + clear_bit (SC3_NAND_CE, sc3_control_base); } if (cmd != NAND_CMD_NONE) diff --git a/common/cmd_nand.c b/common/cmd_nand.c index 3e76d8207..f825234a7 100644 --- a/common/cmd_nand.c +++ b/common/cmd_nand.c @@ -328,7 +328,7 @@ int do_nand(cmd_tbl_t * cmdtp, int flag, int argc, char *argv[]) printf("\nNAND %s: ", read ? "read" : "write"); if (arg_off_size(argc - 3, argv + 3, nand, &off, &size) != 0) return 1; - + s = strchr(cmd, '.'); if (s != NULL && (!strcmp(s, ".jffs2") || !strcmp(s, ".e") || !strcmp(s, ".i"))) { diff --git a/cpu/arm926ejs/davinci/nand.c b/cpu/arm926ejs/davinci/nand.c index 43041b635..196fc146b 100644 --- a/cpu/arm926ejs/davinci/nand.c +++ b/cpu/arm926ejs/davinci/nand.c @@ -376,7 +376,7 @@ int board_nand_init(struct nand_chip *nand) // nand->autooob = &davinci_nand_oobinfo; nand->ecc.calculate = nand_davinci_calculate_ecc; nand->ecc.correct = nand_davinci_correct_data; - nand->ecc.hwctl = nand_davinci_enable_hwecc; + nand->ecc.hwctl = nand_davinci_enable_hwecc; #else nand->ecc.mode = NAND_ECC_SOFT; #endif diff --git a/drivers/mtd/nand/nand_base.c b/drivers/mtd/nand/nand_base.c index aeb179731..e21a18baa 100644 --- a/drivers/mtd/nand/nand_base.c +++ b/drivers/mtd/nand/nand_base.c @@ -2080,7 +2080,7 @@ int nand_erase_nand(struct mtd_info *mtd, struct erase_info *instr, /* Calculate pages in each block */ pages_per_block = 1 << (chip->phys_erase_shift - chip->page_shift); - + /* Select the NAND device */ chip->select_chip(mtd, chipnr); @@ -2748,7 +2748,7 @@ int nand_scan(struct mtd_info *mtd, int maxchips) BUG(); } #endif - + ret = nand_scan_ident(mtd, maxchips); if (!ret) ret = nand_scan_tail(mtd); diff --git a/drivers/mtd/nand/nand_util.c b/drivers/mtd/nand/nand_util.c index 78e70cc80..9fe486698 100644 --- a/drivers/mtd/nand/nand_util.c +++ b/drivers/mtd/nand/nand_util.c @@ -128,7 +128,7 @@ int nand_erase_opts(nand_info_t *meminfo, const nand_erase_options_t *opts) for (; erase.addr < opts->offset + erase_length; erase.addr += meminfo->erasesize) { - + WATCHDOG_RESET (); if (!opts->scrub && bbtest) { @@ -163,7 +163,7 @@ int nand_erase_opts(nand_info_t *meminfo, const nand_erase_options_t *opts) 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); @@ -254,7 +254,7 @@ static struct nand_ecclayout autoplace_ecclayout = { * @chip: nand chip structure * @oob: oob data buffer * @ops: oob ops structure - * + * * Copied from nand_base.c */ static uint8_t *nand_fill_oob(struct nand_chip *chip, uint8_t *oob, @@ -314,13 +314,13 @@ int nand_write_opts(nand_info_t *mtd, loff_t to, mtd_oob_ops_t *ops) uint8_t *oob = ops->oobbuf; uint8_t *buf = ops->datbuf; int ret, subpage; - + ops->retlen = 0; if (!writelen) return 0; printk("nand_write_opts: to: 0x%08x, ops->len: 0x%08x\n", to, ops->len); - + /* reject writes, which are not page aligned */ if (NOTALIGNED(to) || NOTALIGNED(ops->len)) { printk(KERN_NOTICE "nand_write: " diff --git a/include/linux/mtd/nand.h b/include/linux/mtd/nand.h index db8bd7ba2..2984f5f28 100644 --- a/include/linux/mtd/nand.h +++ b/include/linux/mtd/nand.h @@ -421,7 +421,7 @@ struct nand_chip { struct nand_ecc_ctrl ecc; struct nand_buffers *buffers; - + struct nand_hw_control hwcontrol; struct mtd_oob_ops ops; -- cgit v1.2.3-70-g09d2 From dfbf617ff055e4216f78d358b0867c548916d14b Mon Sep 17 00:00:00 2001 From: Scott Wood Date: Thu, 12 Jun 2008 13:20:16 -0500 Subject: NAND read/write fix Implement block-skipping read/write, based on a patch from Morten Ebbell Hestens . Signed-off-by: Morten Ebbell Hestnes Signed-off-by: Scott Wood --- common/cmd_nand.c | 36 +- drivers/mtd/nand/nand_util.c | 762 ++++++++++--------------------------------- include/nand.h | 7 +- 3 files changed, 194 insertions(+), 611 deletions(-) (limited to 'drivers/mtd/nand/nand_util.c') diff --git a/common/cmd_nand.c b/common/cmd_nand.c index 2c421e948..520c15217 100644 --- a/common/cmd_nand.c +++ b/common/cmd_nand.c @@ -74,7 +74,8 @@ static int nand_dump(nand_info_t *nand, ulong off, int only_oob) printf("\t%02x %02x %02x %02x %02x %02x %02x %02x" " %02x %02x %02x %02x %02x %02x %02x %02x\n", p[0], p[1], p[2], p[3], p[4], p[5], p[6], p[7], - p[8], p[9], p[10], p[11], p[12], p[13], p[14], p[15]); + p[8], p[9], p[10], p[11], p[12], p[13], p[14], + p[15]); p += 16; } puts("OOB:\n"); @@ -317,7 +318,6 @@ int do_nand(cmd_tbl_t * cmdtp, int flag, int argc, char *argv[]) } - /* read write */ if (strncmp(cmd, "read", 4) == 0 || strncmp(cmd, "write", 5) == 0) { int read; @@ -334,31 +334,12 @@ int do_nand(cmd_tbl_t * cmdtp, int flag, int argc, char *argv[]) s = strchr(cmd, '.'); if (!s || !strcmp(s, ".jffs2") || !strcmp(s, ".e") || !strcmp(s, ".i")) { - if (read) { - /* read */ - nand_read_options_t opts; - memset(&opts, 0, sizeof(opts)); - opts.buffer = (u_char*) addr; - opts.length = size; - opts.offset = off; - opts.quiet = quiet; -/* - * ! BROKEN ! - * - * TODO: Function must be implemented - * - * ret = nand_read_opts(nand, &opts); - */ - } else { - /* write */ - mtd_oob_ops_t opts; - memset(&opts, 0, sizeof(opts)); - opts.datbuf = (u_char*) addr; - opts.len = size; - opts.ooblen = 64; - opts.mode = MTD_OOB_AUTO; - ret = nand_write_opts(nand, off, &opts); - } + if (read) + ret = nand_read_skip_bad(nand, off, &size, + (u_char *)addr); + else + ret = nand_write_skip_bad(nand, off, &size, + (u_char *)addr); } else if (s != NULL && !strcmp(s, ".oob")) { /* out-of-band data */ mtd_oob_ops_t ops = { @@ -396,6 +377,7 @@ int do_nand(cmd_tbl_t * cmdtp, int flag, int argc, char *argv[]) } return 1; } + if (strcmp(cmd, "biterr") == 0) { /* todo */ return 1; diff --git a/drivers/mtd/nand/nand_util.c b/drivers/mtd/nand/nand_util.c index 9fe486698..02fe914db 100644 --- a/drivers/mtd/nand/nand_util.c +++ b/drivers/mtd/nand/nand_util.c @@ -248,586 +248,6 @@ static struct nand_ecclayout autoplace_ecclayout = { }; #endif - -/** - * nand_fill_oob - [Internal] Transfer client buffer to oob - * @chip: nand chip structure - * @oob: oob data buffer - * @ops: oob ops structure - * - * Copied from nand_base.c - */ -static uint8_t *nand_fill_oob(struct nand_chip *chip, uint8_t *oob, - struct mtd_oob_ops *ops) -{ - size_t len = ops->ooblen; - - switch(ops->mode) { - - case MTD_OOB_PLACE: - case MTD_OOB_RAW: - memcpy(chip->oob_poi + ops->ooboffs, oob, len); - return oob + len; - - case MTD_OOB_AUTO: { - struct nand_oobfree *free = chip->ecc.layout->oobfree; - uint32_t boffs = 0, woffs = ops->ooboffs; - size_t bytes = 0; - - for(; free->length && len; free++, len -= bytes) { - /* Write request not from offset 0 ? */ - if (unlikely(woffs)) { - if (woffs >= free->length) { - woffs -= free->length; - continue; - } - boffs = free->offset + woffs; - bytes = min_t(size_t, len, - (free->length - woffs)); - woffs = 0; - } else { - bytes = min_t(size_t, len, free->length); - boffs = free->offset; - } - memcpy(chip->oob_poi + boffs, oob, bytes); - oob += bytes; - } - return oob; - } - default: - BUG(); - } - return NULL; -} - -#define NOTALIGNED(x) (x & (chip->subpagesize - 1)) != 0 - - -/* copied from nand_base.c: nand_do_write_ops() - * Only very small changes - */ -int nand_write_opts(nand_info_t *mtd, loff_t to, mtd_oob_ops_t *ops) -{ - int chipnr, realpage, page, blockmask, column; - struct nand_chip *chip = mtd->priv; - uint32_t writelen = ops->len; - uint8_t *oob = ops->oobbuf; - uint8_t *buf = ops->datbuf; - int ret, subpage; - - ops->retlen = 0; - if (!writelen) - return 0; - - printk("nand_write_opts: to: 0x%08x, ops->len: 0x%08x\n", to, ops->len); - - /* reject writes, which are not page aligned */ - if (NOTALIGNED(to) || NOTALIGNED(ops->len)) { - printk(KERN_NOTICE "nand_write: " - "Attempt to write not page aligned data\n"); - return -EINVAL; - } - - column = to & (mtd->writesize - 1); - subpage = column || (writelen & (mtd->writesize - 1)); - - if (subpage && oob) { - printk(KERN_NOTICE "nand_write: " - "Attempt to write oob to subpage\n"); - return -EINVAL; - } - - chipnr = (int)(to >> chip->chip_shift); - chip->select_chip(mtd, chipnr); - - /* XXX U-BOOT XXX */ -#if 0 - /* Check, if it is write protected */ - if (nand_check_wp(mtd)) - return -EIO; -#endif - - realpage = (int)(to >> chip->page_shift); - page = realpage & chip->pagemask; - blockmask = (1 << (chip->phys_erase_shift - chip->page_shift)) - 1; - - /* Invalidate the page cache, when we write to the cached page */ - if (to <= (chip->pagebuf << chip->page_shift) && - (chip->pagebuf << chip->page_shift) < (to + ops->len)) - chip->pagebuf = -1; - - /* If we're not given explicit OOB data, let it be 0xFF */ - if (likely(!oob)) { - printf("!oob, writing %d bytes with 0xff to chip->oob_poi (0x%08x)\n", mtd->oobsize, chip->oob_poi); - memset(chip->oob_poi, 0xff, mtd->oobsize); - } - - while(1) { - int bytes = mtd->writesize; - int cached = writelen > bytes && page != blockmask; - uint8_t *wbuf = buf; - - /* Partial page write ? */ - if (unlikely(column || writelen < (mtd->writesize - 1))) { - cached = 0; - bytes = min_t(int, bytes - column, (int) writelen); - chip->pagebuf = -1; - memset(chip->buffers->databuf, 0xff, mtd->writesize); - memcpy(&chip->buffers->databuf[column], buf, bytes); - wbuf = chip->buffers->databuf; - } - - if (unlikely(oob)) - oob = nand_fill_oob(chip, oob, ops); - - ret = chip->write_page(mtd, chip, wbuf, page, cached, - (ops->mode == MTD_OOB_RAW)); - if (ret) - break; - - writelen -= bytes; - if (!writelen) - break; - - column = 0; - buf += bytes; - realpage++; - - page = realpage & chip->pagemask; - /* Check, if we cross a chip boundary */ - if (!page) { - chipnr++; - chip->select_chip(mtd, -1); - chip->select_chip(mtd, chipnr); - } - } - - ops->retlen = ops->len - writelen; - if (unlikely(oob)) - ops->oobretlen = ops->ooblen; - return ret; -} - -/* XXX U-BOOT XXX */ -#if 0 -/** - * 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 ecclayoutchanged = 0; - int percent_complete = -1; - struct nand_ecclayout old_ecclayout; - 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->writesize == 512) - && !(meminfo->oobsize == 8 && meminfo->writesize == 256) - && !(meminfo->oobsize == 64 && meminfo->writesize == 2048)) { - printf("Unknown flash (not normal NAND)\n"); - return -1; - } - - /* read the current oob info */ - memcpy(&old_ecclayout, &meminfo->ecclayout, sizeof(old_ecclayout)); - - /* write without ecc? */ - if (opts->noecc) { - memcpy(&meminfo->ecclayout, &none_ecclayout, - sizeof(meminfo->ecclayout)); - ecclayoutchanged = 1; - } - - /* autoplace ECC? */ - if (opts->autoplace && (old_ecclayout.useecc != MTD_NANDECC_AUTOPLACE)) { - - memcpy(&meminfo->ecclayout, &autoplace_ecclayout, - sizeof(meminfo->ecclayout)); - ecclayoutchanged = 1; - } - - /* force OOB layout for jffs2 or yaffs? */ - if (opts->forcejffs2 || opts->forceyaffs) { - struct nand_ecclayout *oobsel = - opts->forcejffs2 ? &jffs2_ecclayout : &yaffs_ecclayout; - - 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_ecclayout.eccbytes = 3; - } - - memcpy(&meminfo->ecclayout, oobsel, sizeof(meminfo->ecclayout)); - } - - /* get image length */ - imglen = opts->length; - pagelen = meminfo->writesize - + ((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->writesize) - > (meminfo->size - opts->offset)) { - printf("Image %d bytes, NAND page %d bytes, " - "OOB area %u bytes, device size %u bytes\n", - imglen, pagelen, meminfo->writesize, 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->writesize; - if (opts->pad && (imglen < readlen)) { - readlen = imglen; - memset(data_buf + readlen, 0xff, - meminfo->writesize - 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->writesize, - &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->writesize; - } - - if (!opts->quiet) - printf("\n"); - -restoreoob: - if (ecclayoutchanged) { - memcpy(&meminfo->ecclayout, &old_ecclayout, - sizeof(meminfo->ecclayout)); - } - - 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->writesize == 512) - && !(meminfo->oobsize == 8 && meminfo->writesize == 256) - && !(meminfo->oobsize == 64 && meminfo->writesize == 2048)) { - printf("Unknown flash (not normal NAND)\n"); - return -1; - } - - pagelen = meminfo->writesize - + ((opts->readoob != 0) ? meminfo->oobsize : 0); - - /* check, if length is not larger than device */ - if (((imglen / pagelen) * meminfo->writesize) - > (meminfo->size - opts->offset)) { - printf("Image %d bytes, NAND page %d bytes, " - "OOB area %u bytes, device size %u bytes\n", - imglen, pagelen, meminfo->writesize, 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->writesize, - &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->writesize; - } - - 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; -} -#endif - /* XXX U-BOOT XXX */ #if 0 /****************************************************************************** @@ -1007,4 +427,184 @@ int nand_unlock(nand_info_t *meminfo, ulong start, ulong length) } #endif -#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) */ diff --git a/include/nand.h b/include/nand.h index 83d597dc7..d5af4324c 100644 --- a/include/nand.h +++ b/include/nand.h @@ -108,9 +108,10 @@ struct nand_erase_options { typedef struct nand_erase_options nand_erase_options_t; -int nand_write_opts(nand_info_t *mtd, loff_t to, mtd_oob_ops_t *ops); - -int nand_read_opts(nand_info_t *meminfo, const nand_read_options_t *opts); +int nand_read_skip_bad(nand_info_t *nand, size_t offset, size_t *length, + u_char *buffer); +int nand_write_skip_bad(nand_info_t *nand, size_t offset, size_t *length, + u_char *buffer); int nand_erase_opts(nand_info_t *meminfo, const nand_erase_options_t *opts); #define NAND_LOCK_STATUS_TIGHT 0x01 -- cgit v1.2.3-70-g09d2