From 2255b2d2044d434463eb2661e18018e50f1643d9 Mon Sep 17 00:00:00 2001 From: Stefan Roese Date: Tue, 10 Oct 2006 12:36:02 +0200 Subject: * Several improvements to the new NAND subsystem: - JFFS2 related commands implemented in mtd-utils style - Support for bad blocks - Bad block testing commands - NAND lock commands Please take a look at doc/README.nand for more details Patch by Guido Classen, 10 Oct 2006 --- drivers/nand/nand_util.c | 869 +++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 869 insertions(+) create mode 100644 drivers/nand/nand_util.c (limited to 'drivers/nand/nand_util.c') diff --git a/drivers/nand/nand_util.c b/drivers/nand/nand_util.c new file mode 100644 index 0000000000..5a8e58a741 --- /dev/null +++ b/drivers/nand/nand_util.c @@ -0,0 +1,869 @@ +/* + * drivers/nand/nand_util.c + * + * Copyright (C) 2006 by Weiss-Electronic GmbH. + * All rights reserved. + * + * @author: Guido Classen + * @descr: NAND Flash support + * @references: borrowed heavily from Linux mtd-utils code: + * flash_eraseall.c by Arcom Control System Ltd + * nandwrite.c by Steven J. Hill (sjhill@realitydiluted.com) + * and Thomas Gleixner (tglx@linutronix.de) + * + * See file CREDITS for list of people who contributed to this + * project. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License version + * 2 as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, + * MA 02111-1307 USA + * + */ + +#include + +#if (CONFIG_COMMANDS & CFG_CMD_NAND) && !defined(CFG_NAND_LEGACY) + +#include +#include +#include + +#include +#include + +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) +#define cpu_to_je32(x) (x) + +/*****************************************************************************/ +static int nand_block_bad_scrub(struct mtd_info *mtd, loff_t ofs, int getchip) +{ + return 0; +} + +/** + * nand_erase_opts: - erase NAND flash with support for various options + * (jffs2 formating) + * + * @param meminfo NAND device to erase + * @param opts options, @see struct nand_erase_options + * @return 0 in case of success + * + * This code is ported from flash_eraseall.c from Linux mtd utils by + * Arcom Control System Ltd. + */ +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; + + memset(&erase, 0, sizeof(erase)); + + erase.mtd = meminfo; + erase.len = meminfo->erasesize; + if (opts->offset == 0 && opts->length == 0) { + /* erase complete chip */ + erase.addr = 0; + erase_length = meminfo->size; + } else { + /* erase specified region */ + 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)); + } + + /* scrub option allows to erase badblock. To prevent internal + * check from erase() method, set block check method to dummy + * and disable bad block table while erasing. + */ + if (opts->scrub) { + struct nand_chip *priv_nand = meminfo->priv; + + nand_block_bad_old = priv_nand->block_bad; + priv_nand->block_bad = nand_block_bad_scrub; + /* we don't need the bad block table anymore... + * after scrub, there are no bad blocks left! + */ + if (priv_nand->bbt) { + kfree(priv_nand->bbt); + } + priv_nand->bbt = NULL; + } + + for (; + erase.addr < opts->offset + erase_length; + erase.addr += meminfo->erasesize) { + + WATCHDOG_RESET (); + + if (!opts->scrub && bbtest) { + int ret = meminfo->block_isbad(meminfo, erase.addr); + if (ret > 0) { + if (!opts->quiet) + printf("\rSkipping bad block at " + "0x%08x " + " \n", + erase.addr); + continue; + + } else if (ret < 0) { + printf("\n%s: MTD get bad block failed: %d\n", + mtd_device, + ret); + return -1; + } + } + + result = meminfo->erase(meminfo, &erase); + if (result != 0) { + printf("\n%s: MTD Erase failure: %d\n", + mtd_device, result); + continue; + } + + /* 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); + } + } + + if (!opts->quiet) { + int percent = (int) + ((unsigned long long) + (erase.addr+meminfo->erasesize-opts->offset) + * 100 / erase_length); + + /* output progress message only at whole percent + * steps to reduce the number of messages printed + * on (slow) serial consoles + */ + if (percent != percent_complete) { + percent_complete = percent; + + printf("\rErasing at 0x%x -- %3d%% complete.", + erase.addr, percent); + + if (opts->jffs2 && result == 0) + printf(" Cleanmarker written at 0x%x.", + erase.addr); + } + } + } + if (!opts->quiet) + printf("\n"); + + if (nand_block_bad_old) { + struct nand_chip *priv_nand = meminfo->priv; + + priv_nand->block_bad = nand_block_bad_old; + priv_nand->scan_bbt(meminfo); + } + + return 0; +} + +#define MAX_PAGE_SIZE 2048 +#define MAX_OOB_SIZE 64 + +/* + * buffer array used for writing data + */ +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 = { + .useecc = MTD_NANDECC_OFF, +}; + +static struct nand_oobinfo jffs2_oobinfo = { + .useecc = MTD_NANDECC_PLACE, + .eccbytes = 6, + .eccpos = { 0, 1, 2, 3, 6, 7 } +}; + +static struct nand_oobinfo yaffs_oobinfo = { + .useecc = MTD_NANDECC_PLACE, + .eccbytes = 6, + .eccpos = { 8, 9, 10, 13, 14, 15} +}; + +static struct nand_oobinfo autoplace_oobinfo = { + .useecc = MTD_NANDECC_AUTOPLACE +}; + +/** + * 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) { + int percent = (int) + ((unsigned long long) + (opts->length-imglen) * 100 + / opts->length); + /* 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%x " + "-- %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) { + int percent = (int) + ((unsigned long long) + (opts->length-imglen) * 100 + / opts->length); + /* 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%x " + "-- %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; +} + +/****************************************************************************** + * Support for locking / unlocking operations of some NAND devices + *****************************************************************************/ + +#define NAND_CMD_LOCK 0x2a +#define NAND_CMD_LOCK_TIGHT 0x2c +#define NAND_CMD_UNLOCK1 0x23 +#define NAND_CMD_UNLOCK2 0x24 +#define NAND_CMD_LOCK_STATUS 0x7a + +/** + * nand_lock: Set all pages of NAND flash chip to the LOCK or LOCK-TIGHT + * state + * + * @param meminfo nand mtd instance + * @param tight bring device in lock tight mode + * + * @return 0 on success, -1 in case of error + * + * The lock / lock-tight command only applies to the whole chip. To get some + * parts of the chip lock and others unlocked use the following sequence: + * + * - Lock all pages of the chip using nand_lock(mtd, 0) (or the lockpre pin) + * - Call nand_unlock() once for each consecutive area to be unlocked + * - If desired: Bring the chip to the lock-tight state using nand_lock(mtd, 1) + * + * If the device is in lock-tight state software can't change the + * current active lock/unlock state of all pages. nand_lock() / nand_unlock() + * calls will fail. It is only posible to leave lock-tight state by + * an hardware signal (low pulse on _WP pin) or by power down. + */ +int nand_lock(nand_info_t *meminfo, int tight) +{ + int ret = 0; + int status; + struct nand_chip *this = meminfo->priv; + + /* select the NAND device */ + this->select_chip(meminfo, 0); + + this->cmdfunc(meminfo, + (tight ? NAND_CMD_LOCK_TIGHT : NAND_CMD_LOCK), + -1, -1); + + /* call wait ready function */ + status = this->waitfunc(meminfo, this, FL_WRITING); + + /* see if device thinks it succeeded */ + if (status & 0x01) { + ret = -1; + } + + /* de-select the NAND device */ + this->select_chip(meminfo, -1); + return ret; +} + +/** + * nand_get_lock_status: - query current lock state from one page of NAND + * flash + * + * @param meminfo nand mtd instance + * @param offset page address to query (muss be page aligned!) + * + * @return -1 in case of error + * >0 lock status: + * bitfield with the following combinations: + * NAND_LOCK_STATUS_TIGHT: page in tight state + * NAND_LOCK_STATUS_LOCK: page locked + * NAND_LOCK_STATUS_UNLOCK: page unlocked + * + */ +int nand_get_lock_status(nand_info_t *meminfo, ulong offset) +{ + int ret = 0; + int chipnr; + int page; + struct nand_chip *this = meminfo->priv; + + /* select the NAND device */ + chipnr = (int)(offset >> this->chip_shift); + this->select_chip(meminfo, chipnr); + + + if ((offset & (meminfo->oobblock - 1)) != 0) { + printf ("nand_get_lock_status: " + "Start address must be beginning of " + "nand page!\n"); + ret = -1; + goto out; + } + + /* check the Lock Status */ + page = (int)(offset >> this->page_shift); + this->cmdfunc(meminfo, NAND_CMD_LOCK_STATUS, -1, page & this->pagemask); + + ret = this->read_byte(meminfo) & (NAND_LOCK_STATUS_TIGHT + | NAND_LOCK_STATUS_LOCK + | NAND_LOCK_STATUS_UNLOCK); + + out: + /* de-select the NAND device */ + this->select_chip(meminfo, -1); + return ret; +} + +/** + * nand_unlock: - Unlock area of NAND pages + * only one consecutive area can be unlocked at one time! + * + * @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) + * + * @return 0 on success, -1 in case of error + */ +int nand_unlock(nand_info_t *meminfo, ulong start, ulong length) +{ + int ret = 0; + int chipnr; + int status; + int page; + struct nand_chip *this = meminfo->priv; + printf ("nand_unlock: start: %08x, length: %d!\n", + (int)start, (int)length); + + /* select the NAND device */ + chipnr = (int)(start >> this->chip_shift); + this->select_chip(meminfo, chipnr); + + /* check the WP bit */ + this->cmdfunc(meminfo, NAND_CMD_STATUS, -1, -1); + if ((this->read_byte(meminfo) & 0x80) == 0) { + printf ("nand_unlock: Device is write protected!\n"); + ret = -1; + goto out; + } + + if ((start & (meminfo->oobblock - 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) { + printf ("nand_unlock: Length must be a multiple of nand page " + "size!\n"); + ret = -1; + goto out; + } + + /* submit address of first page to unlock */ + page = (int)(start >> this->page_shift); + this->cmdfunc(meminfo, NAND_CMD_UNLOCK1, -1, page & this->pagemask); + + /* submit ADDRESS of LAST page to unlock */ + page += (int)(length >> this->page_shift) - 1; + this->cmdfunc(meminfo, NAND_CMD_UNLOCK2, -1, page & this->pagemask); + + /* call wait ready function */ + status = this->waitfunc(meminfo, this, FL_WRITING); + /* see if device thinks it succeeded */ + if (status & 0x01) { + /* there was an error */ + ret = -1; + goto out; + } + + out: + /* de-select the NAND device */ + this->select_chip(meminfo, -1); + return ret; +} + +#endif /* (CONFIG_COMMANDS & CFG_CMD_NAND) && !defined(CFG_NAND_LEGACY) */ -- cgit v1.2.3 From 87621bc24789e024c62a8a12bebd592943cdfb02 Mon Sep 17 00:00:00 2001 From: Wolfgang Denk Date: Thu, 12 Oct 2006 11:43:47 +0200 Subject: Fix spelling; minor code cleanup. --- CHANGELOG | 2 ++ common/cmd_nand.c | 2 +- drivers/nand/nand_util.c | 6 +++--- 3 files changed, 6 insertions(+), 4 deletions(-) (limited to 'drivers/nand/nand_util.c') diff --git a/CHANGELOG b/CHANGELOG index 68aa65ae3c..166cfa3a4e 100644 --- a/CHANGELOG +++ b/CHANGELOG @@ -2,6 +2,8 @@ Changes since U-Boot 1.1.4: ====================================================================== +* Fix spelling; minor code cleanup. + * Fix JFFS2 compilation problem Patch by Stefan Roese, 12 Oct 2006 diff --git a/common/cmd_nand.c b/common/cmd_nand.c index fdbe7c78a6..7042a9c226 100644 --- a/common/cmd_nand.c +++ b/common/cmd_nand.c @@ -251,7 +251,7 @@ int do_nand(cmd_tbl_t * cmdtp, int flag, int argc, char *argv[]) "Use this command only for testing purposes " "if you\n" " " - "are shure of what you are doing!\n" + "are sure of what you are doing!\n" "\nReally scrub this NAND flash? \n" ); diff --git a/drivers/nand/nand_util.c b/drivers/nand/nand_util.c index 5a8e58a741..7570210d5c 100644 --- a/drivers/nand/nand_util.c +++ b/drivers/nand/nand_util.c @@ -170,9 +170,9 @@ int nand_erase_opts(nand_info_t *meminfo, const nand_erase_options_t *opts) if (ret > 0) { if (!opts->quiet) printf("\rSkipping bad block at " - "0x%08x " - " \n", - erase.addr); + "0x%08x " + " \n", + erase.addr); continue; } else if (ret < 0) { -- cgit v1.2.3 From 856f054410cef52d868feb330168b2a4c4091328 Mon Sep 17 00:00:00 2001 From: Stefan Roese Date: Sat, 28 Oct 2006 15:55:52 +0200 Subject: [PATCH] NAND: Partition name support added to NAND subsystem chpart, nboot and NAND subsystem related commands now accept also partition name to specify offset. Signed-off-by: Ladislav Michl Signed-off-by: Stefan Roese --- common/cmd_jffs2.c | 14 +- common/cmd_nand.c | 346 ++++++++++++++++++++++++++--------------------- doc/README.nand | 29 ++-- drivers/nand/nand_util.c | 11 +- 4 files changed, 226 insertions(+), 174 deletions(-) (limited to 'drivers/nand/nand_util.c') diff --git a/common/cmd_jffs2.c b/common/cmd_jffs2.c index b5fd41724f..7fd1fa33da 100644 --- a/common/cmd_jffs2.c +++ b/common/cmd_jffs2.c @@ -1300,7 +1300,7 @@ static void list_partitions(void) * Given partition identifier in form of , find * corresponding device and verify partition number. * - * @param id string describing device and partition + * @param id string describing device and partition or partition name * @param dev pointer to the requested device (output) * @param part_num verified partition number (output) * @param part pointer to requested partition (output) @@ -1309,11 +1309,23 @@ static void list_partitions(void) int find_dev_and_part(const char *id, struct mtd_device **dev, u8 *part_num, struct part_info **part) { + struct list_head *dentry, *pentry; u8 type, dnum, pnum; const char *p; DEBUGF("--- find_dev_and_part ---\nid = %s\n", id); + list_for_each(dentry, &devices) { + *part_num = 0; + *dev = list_entry(dentry, struct mtd_device, link); + list_for_each(pentry, &(*dev)->parts) { + *part = list_entry(pentry, struct part_info, link); + if (strcmp((*part)->name, id) == 0) + return 0; + (*part_num)++; + } + } + p = id; *dev = NULL; *part = NULL; diff --git a/common/cmd_nand.c b/common/cmd_nand.c index 4fb3b65960..7286726f1c 100644 --- a/common/cmd_nand.c +++ b/common/cmd_nand.c @@ -36,6 +36,15 @@ #include #include +#if (CONFIG_COMMANDS & CFG_CMD_JFFS2) && defined(CONFIG_JFFS2_CMDLINE) + +/* parition handling routines */ +int mtdparts_init(void); +int id_parse(const char *id, const char **ret_id, u8 *dev_type, u8 *dev_num); +int find_dev_and_part(const char *id, struct mtd_device **dev, + u8 *part_num, struct part_info **part); +#endif + extern nand_info_t nand_info[]; /* info for NAND chips */ static int nand_dump_oob(nand_info_t *nand, ulong off) @@ -83,50 +92,75 @@ static int nand_dump(nand_info_t *nand, ulong off) /* ------------------------------------------------------------------------- */ -static void -arg_off_size(int argc, char *argv[], ulong *off, ulong *size, ulong totsize) +static inline int str2long(char *p, ulong *num) { - *off = 0; - *size = 0; + char *endptr; -#if defined(CONFIG_JFFS2_NAND) && defined(CFG_JFFS_CUSTOM_PART) - if (argc >= 1 && strcmp(argv[0], "partition") == 0) { - int part_num; - struct part_info *part; - const char *partstr; - - if (argc >= 2) - partstr = argv[1]; - else - partstr = getenv("partition"); - - if (partstr) - part_num = (int)simple_strtoul(partstr, NULL, 10); - else - part_num = 0; + *num = simple_strtoul(p, &endptr, 16); + return (*p != '\0' && *endptr == '\0') ? 1 : 0; +} - part = jffs2_part_info(part_num); - if (part == NULL) { - printf("\nInvalid partition %d\n", part_num); - return; +static int +arg_off_size(int argc, char *argv[], nand_info_t *nand, ulong *off, ulong *size) +{ + int idx = nand_curr_device; +#if (CONFIG_COMMANDS & CFG_CMD_JFFS2) && defined(CONFIG_JFFS2_CMDLINE) + struct mtd_device *dev; + struct part_info *part; + u8 pnum; + + if (argc >= 1 && !(str2long(argv[0], off))) { + if ((mtdparts_init() == 0) && + (find_dev_and_part(argv[0], &dev, &pnum, &part) == 0)) { + if (dev->id->type != MTD_DEV_TYPE_NAND) { + puts("not a NAND device\n"); + return -1; + } + *off = part->offset; + if (argc >= 2) { + if (!(str2long(argv[1], size))) { + printf("'%s' is not a number\n", argv[1]); + return -1; + } + if (*size > part->size) + *size = part->size; + } else { + *size = part->size; + } + idx = dev->id->num; + *nand = nand_info[idx]; + goto out; } - *size = part->size; - *off = (ulong)part->offset; - } else + } #endif - { - if (argc >= 1) - *off = (ulong)simple_strtoul(argv[0], NULL, 16); - else - *off = 0; - if (argc >= 2) - *size = (ulong)simple_strtoul(argv[1], NULL, 16); - else - *size = totsize - *off; + if (argc >= 1) { + if (!(str2long(argv[0], off))) { + printf("'%s' is not a number\n", argv[0]); + return -1; + } + } else { + *off = 0; + } + if (argc >= 2) { + if (!(str2long(argv[1], size))) { + printf("'%s' is not a number\n", argv[1]); + return -1; + } + } else { + *size = nand->size - *off; } +#if (CONFIG_COMMANDS & CFG_CMD_JFFS2) && defined(CONFIG_JFFS2_CMDLINE) +out: +#endif + printf("device %d ", idx); + if (*size == nand->size) + puts("whole chip\n"); + else + printf("offset 0x%x, size 0x%x\n", *off, *size); + return 0; } int do_nand(cmd_tbl_t * cmdtp, int flag, int argc, char *argv[]) @@ -213,35 +247,22 @@ int do_nand(cmd_tbl_t * cmdtp, int flag, int argc, char *argv[]) return 0; } + /* + * Syntax is: + * 0 1 2 3 4 + * nand erase [clean] [off size] + */ if (strcmp(cmd, "erase") == 0 || strcmp(cmd, "scrub") == 0) { nand_erase_options_t opts; - int clean = argc >= 3 && !strcmp("clean", argv[2]); - int rest_argc = argc - 2; - char **rest_argv = argv + 2; + /* "clean" at index 2 means request to write cleanmarker */ + int clean = argc > 2 && !strcmp("clean", argv[2]); + int o = clean ? 3 : 2; int scrub = !strcmp(cmd, "scrub"); - if (clean) { - rest_argc--; - rest_argv++; - } - - if (rest_argc == 0) { - - printf("\nNAND %s: device %d whole chip\n", - cmd, - nand_curr_device); - - off = size = 0; - } else { - arg_off_size(rest_argc, rest_argv, &off, &size, - nand->size); - - if (off == 0 && size == 0) - return 1; - - printf("\nNAND %s: device %d offset 0x%x, size 0x%x\n", - cmd, nand_curr_device, off, size); - } + printf("\nNAND %s: ", scrub ? "scrub" : "erase"); + /* skip first two or three arguments, look for offset and size */ + if (arg_off_size(argc - o, argv + o, nand, &off, &size) != 0) + return 1; memset(&opts, 0, sizeof(opts)); opts.offset = off; @@ -250,23 +271,22 @@ int do_nand(cmd_tbl_t * cmdtp, int flag, int argc, char *argv[]) opts.quiet = quiet; if (scrub) { - printf("Warning: " - "scrub option will erase all factory set " - "bad blocks!\n" - " " - "There is no reliable way to recover them.\n" - " " - "Use this command only for testing purposes " - "if you\n" - " " - "are sure of what you are doing!\n" - "\nReally scrub this NAND flash? \n" - ); + puts("Warning: " + "scrub option will erase all factory set " + "bad blocks!\n" + " " + "There is no reliable way to recover them.\n" + " " + "Use this command only for testing purposes " + "if you\n" + " " + "are sure of what you are doing!\n" + "\nReally scrub this NAND flash? \n"); if (getc() == 'y' && getc() == '\r') { opts.scrub = 1; } else { - printf("scrub aborted\n"); + puts("scrub aborted\n"); return -1; } } @@ -301,13 +321,10 @@ int do_nand(cmd_tbl_t * cmdtp, int flag, int argc, char *argv[]) addr = (ulong)simple_strtoul(argv[2], NULL, 16); - arg_off_size(argc - 3, argv + 3, &off, &size, nand->size); - if (off == 0 && size == 0) - return 1; - read = strncmp(cmd, "read", 4) == 0; /* 1 = read, 0 = write */ - printf("\nNAND %s: device %d offset %u, size %u ... ", - read ? "read" : "write", nand_curr_device, off, size); + 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 && @@ -334,15 +351,13 @@ int do_nand(cmd_tbl_t * cmdtp, int flag, int argc, char *argv[]) opts.quiet = quiet; ret = nand_write_opts(nand, &opts); } - printf("%s\n", ret ? "ERROR" : "OK"); - return ret == 0 ? 0 : 1; + } else { + if (read) + ret = nand_read(nand, off, &size, (u_char *)addr); + else + ret = nand_write(nand, off, &size, (u_char *)addr); } - if (read) - ret = nand_read(nand, off, &size, (u_char *)addr); - else - ret = nand_write(nand, off, &size, (u_char *)addr); - printf(" %d bytes %s: %s\n", size, read ? "read" : "written", ret ? "ERROR" : "OK"); @@ -412,9 +427,9 @@ int do_nand(cmd_tbl_t * cmdtp, int flag, int argc, char *argv[]) } } else { if (!nand_lock(nand, tight)) { - printf ("NAND flash successfully locked\n"); + puts("NAND flash successfully locked\n"); } else { - printf ("Error locking NAND flash. \n"); + puts("Error locking NAND flash\n"); return 1; } } @@ -422,19 +437,14 @@ int do_nand(cmd_tbl_t * cmdtp, int flag, int argc, char *argv[]) } if (strcmp(cmd, "unlock") == 0) { - if (argc == 2) { - off = 0; - size = nand->size; - } else { - arg_off_size(argc - 2, argv + 2, &off, &size, - nand->size); - } + if (arg_off_size(argc - 2, argv + 2, nand, &off, &size) < 0) + return 1; if (!nand_unlock(nand, off, size)) { - printf("NAND flash successfully unlocked\n"); + puts("NAND flash successfully unlocked\n"); } else { - printf("Error unlocking NAND flash. " - "Write and erase will probably fail\n"); + puts("Error unlocking NAND flash, " + "write and erase will probably fail\n"); return 1; } return 0; @@ -449,8 +459,8 @@ U_BOOT_CMD(nand, 5, 1, do_nand, "nand - NAND sub-system\n", "info - show available NAND devices\n" "nand device [dev] - show or set current device\n" - "nand read[.jffs2] - addr off size\n" - "nand write[.jffs2] - addr off size - read/write `size' bytes starting\n" + "nand read[.jffs2] - addr off|partition size\n" + "nand write[.jffs2] - addr off|partiton size - read/write `size' bytes starting\n" " at offset `off' to/from memory address `addr'\n" "nand erase [clean] [off size] - erase `size' bytes from\n" " offset `off' (entire device if not specified)\n" @@ -462,62 +472,20 @@ U_BOOT_CMD(nand, 5, 1, do_nand, "nand lock [tight] [status] - bring nand to lock state or display locked pages\n" "nand unlock [offset] [size] - unlock section\n"); -int do_nandboot(cmd_tbl_t * cmdtp, int flag, int argc, char *argv[]) +static int nand_load_image(cmd_tbl_t *cmdtp, nand_info_t *nand, + ulong offset, ulong addr, char *cmd) { - char *boot_device = NULL; - char *ep; - int dev; int r; - ulong addr, cnt, offset = 0; + char *ep; + ulong cnt; image_header_t *hdr; - nand_info_t *nand; - switch (argc) { - case 1: - addr = CFG_LOAD_ADDR; - boot_device = getenv("bootdevice"); - break; - case 2: - addr = simple_strtoul(argv[1], NULL, 16); - boot_device = getenv("bootdevice"); - break; - case 3: - addr = simple_strtoul(argv[1], NULL, 16); - boot_device = argv[2]; - break; - case 4: - addr = simple_strtoul(argv[1], NULL, 16); - boot_device = argv[2]; - offset = simple_strtoul(argv[3], NULL, 16); - break; - default: - printf("Usage:\n%s\n", cmdtp->usage); - SHOW_BOOT_PROGRESS(-1); - return 1; - } - - if (!boot_device) { - puts("\n** No boot device **\n"); - SHOW_BOOT_PROGRESS(-1); - return 1; - } - - dev = simple_strtoul(boot_device, &ep, 16); - - if (dev < 0 || dev >= CFG_MAX_NAND_DEVICE || !nand_info[dev].name) { - printf("\n** Device %d not available\n", dev); - SHOW_BOOT_PROGRESS(-1); - return 1; - } - - nand = &nand_info[dev]; - printf("\nLoading from device %d: %s (offset 0x%lx)\n", - dev, nand->name, offset); + printf("\nLoading from %s, offset 0x%lx\n", nand->name, offset); cnt = nand->oobblock; r = nand_read(nand, offset, &cnt, (u_char *) addr); if (r) { - printf("** Read error on %d\n", dev); + puts("** Read error\n"); SHOW_BOOT_PROGRESS(-1); return 1; } @@ -536,7 +504,7 @@ int do_nandboot(cmd_tbl_t * cmdtp, int flag, int argc, char *argv[]) r = nand_read(nand, offset, &cnt, (u_char *) addr); if (r) { - printf("** Read error on %d\n", dev); + puts("** Read error\n"); SHOW_BOOT_PROGRESS(-1); return 1; } @@ -550,7 +518,7 @@ int do_nandboot(cmd_tbl_t * cmdtp, int flag, int argc, char *argv[]) char *local_args[2]; extern int do_bootm(cmd_tbl_t *, int, int, char *[]); - local_args[0] = argv[0]; + local_args[0] = cmd; local_args[1] = NULL; printf("Automatic boot of image at addr 0x%08lx ...\n", addr); @@ -561,9 +529,83 @@ int do_nandboot(cmd_tbl_t * cmdtp, int flag, int argc, char *argv[]) return 0; } -U_BOOT_CMD(nboot, 4, 1, do_nandboot, - "nboot - boot from NAND device\n", "loadAddr dev\n"); +int do_nandboot(cmd_tbl_t * cmdtp, int flag, int argc, char *argv[]) +{ + char *boot_device = NULL; + int idx; + ulong addr, offset = 0; +#if (CONFIG_COMMANDS & CFG_CMD_JFFS2) && defined(CONFIG_JFFS2_CMDLINE) + struct mtd_device *dev; + struct part_info *part; + u8 pnum; + + if (argc >= 2) { + char *p = (argc == 2) ? argv[1] : argv[2]; + if (!(str2long(p, &addr)) && (mtdparts_init() == 0) && + (find_dev_and_part(p, &dev, &pnum, &part) == 0)) { + if (dev->id->type != MTD_DEV_TYPE_NAND) { + puts("Not a NAND device\n"); + return 1; + } + if (argc > 3) + goto usage; + if (argc == 3) + addr = simple_strtoul(argv[2], NULL, 16); + else + addr = CFG_LOAD_ADDR; + return nand_load_image(cmdtp, &nand_info[dev->id->num], + part->offset, addr, argv[0]); + } + } +#endif + switch (argc) { + case 1: + addr = CFG_LOAD_ADDR; + boot_device = getenv("bootdevice"); + break; + case 2: + addr = simple_strtoul(argv[1], NULL, 16); + boot_device = getenv("bootdevice"); + break; + case 3: + addr = simple_strtoul(argv[1], NULL, 16); + boot_device = argv[2]; + break; + case 4: + addr = simple_strtoul(argv[1], NULL, 16); + boot_device = argv[2]; + offset = simple_strtoul(argv[3], NULL, 16); + break; + default: +#if (CONFIG_COMMANDS & CFG_CMD_JFFS2) && defined(CONFIG_JFFS2_CMDLINE) +usage: +#endif + printf("Usage:\n%s\n", cmdtp->usage); + SHOW_BOOT_PROGRESS(-1); + return 1; + } + + if (!boot_device) { + puts("\n** No boot device **\n"); + SHOW_BOOT_PROGRESS(-1); + return 1; + } + + idx = simple_strtoul(boot_device, NULL, 16); + + if (idx < 0 || idx >= CFG_MAX_NAND_DEVICE || !nand_info[idx].name) { + printf("\n** Device %d not available\n", idx); + SHOW_BOOT_PROGRESS(-1); + return 1; + } + + return nand_load_image(cmdtp, &nand_info[idx], offset, addr, argv[0]); +} + +U_BOOT_CMD(nboot, 4, 1, do_nandboot, + "nboot - boot from NAND device\n", + "[partition] | [[[loadAddr] dev] offset]\n"); #endif /* (CONFIG_COMMANDS & CFG_CMD_NAND) */ diff --git a/doc/README.nand b/doc/README.nand index 5279a4f2bc..b5171f4d40 100644 --- a/doc/README.nand +++ b/doc/README.nand @@ -34,14 +34,19 @@ Commands: nand device num Make device `num' the current device and print information about it. - nand erase off size - nand erase clean [off size] - Erase `size' bytes starting at offset `off'. Only complete erase - blocks can be erased. + nand erase off|partition size + nand erase clean [off|partition size] + Erase `size' bytes starting at offset `off'. Alternatively partition + name can be specified, in this case size will be eventually limited + to not exceed partition size (this behaviour applies also to read + and write commands). Only complete erase blocks can be erased. + + If `erase' is specified without an offset or size, the entire flash + is erased. If `erase' is specified with partition but without an + size, the entire partition is erased. If `clean' is specified, a JFFS2-style clean marker is written to - each block after it is erased. If `clean' is specified without an - offset or size, the entire flash is erased. + each block after it is erased. This command will not erase blocks that are marked bad. There is a debug option in cmd_nand.c to allow bad blocks to be erased. @@ -51,28 +56,28 @@ Commands: nand info Print information about all of the NAND devices found. - nand read addr ofs size + nand read addr ofs|partition size Read `size' bytes from `ofs' in NAND flash to `addr'. If a page cannot be read because it is marked bad or an uncorrectable data error is found the command stops with an error. - nand read.jffs2 addr ofs size + nand read.jffs2 addr ofs|partition size Like `read', but the data for blocks that are marked bad is read as 0xff. This gives a readable JFFS2 image that can be processed by the JFFS2 commands such as ls and fsload. - nand read.oob addr ofs size + nand read.oob addr ofs|partition size Read `size' bytes from the out-of-band data area corresponding to `ofs' in NAND flash to `addr'. This is limited to the 16 bytes of data for one 512-byte page or 2 256-byte pages. There is no check for bad blocks or ECC errors. - nand write addr ofs size + nand write addr ofs|partition size Write `size' bytes from `addr' to `ofs' in NAND flash. If a page cannot be written because it is marked bad or the write fails the command stops with an error. - nand write.jffs2 addr ofs size + nand write.jffs2 addr ofs|partition size Like `write', but blocks that are marked bad are skipped and the is written to the next block instead. This allows writing writing a JFFS2 image, as long as the image is short enough to fit even @@ -80,7 +85,7 @@ Commands: produced by mkfs.jffs2 should work well, but loading an image copied from another flash is going to be trouble if there are any bad blocks. - nand write.oob addr ofs size + nand write.oob addr ofs|partition size Write `size' bytes from `addr' to the out-of-band data area corresponding to `ofs' in NAND flash. This is limited to the 16 bytes of data for one 512-byte page or 2 256-byte pages. There is no check diff --git a/drivers/nand/nand_util.c b/drivers/nand/nand_util.c index 7570210d5c..10bf03659e 100644 --- a/drivers/nand/nand_util.c +++ b/drivers/nand/nand_util.c @@ -83,15 +83,8 @@ int nand_erase_opts(nand_info_t *meminfo, const nand_erase_options_t *opts) erase.mtd = meminfo; erase.len = meminfo->erasesize; - if (opts->offset == 0 && opts->length == 0) { - /* erase complete chip */ - erase.addr = 0; - erase_length = meminfo->size; - } else { - /* erase specified region */ - erase.addr = opts->offset; - erase_length = opts->length; - } + erase.addr = opts->offset; + erase_length = opts->length; isNAND = meminfo->type == MTD_NANDFLASH ? 1 : 0; -- cgit v1.2.3