diff options
author | David Ungar <david.ungar@timesys.com> | 2010-10-11 14:22:33 -0400 |
---|---|---|
committer | David Ungar <david.ungar@timesys.com> | 2010-10-11 14:22:33 -0400 |
commit | c455ad7d7ceff60f0850697dc1949218972db3c8 (patch) | |
tree | be7b0391c166c296c231f5000ad3156960f178af | |
parent | b52e80bcc86390b6d2f09cec70a1a4bc5e970cb9 (diff) |
yaffs support
-rw-r--r-- | board/omap3/logic/logic.c | 3 | ||||
-rw-r--r-- | common/cmd_nand.c | 15 | ||||
-rw-r--r-- | cpu/arm_cortexa8/omap3/board.c | 20 | ||||
-rw-r--r-- | drivers/mtd/nand/nand_util.c | 284 | ||||
-rw-r--r-- | drivers/mtd/nand/omap_gpmc.c | 18 | ||||
-rw-r--r-- | include/configs/omap3_logic.h | 6 | ||||
-rw-r--r-- | lib_arm/board.c | 3 |
7 files changed, 339 insertions, 10 deletions
diff --git a/board/omap3/logic/logic.c b/board/omap3/logic/logic.c index 95d609d4bd..1169d548f0 100644 --- a/board/omap3/logic/logic.c +++ b/board/omap3/logic/logic.c @@ -136,6 +136,9 @@ int misc_init_r(void) gd->bd->bi_arch_number = logic_identify(); + /* Switch to Hardware ECC mode */ + omap_nand_switch_ecc(1); + dieid_num_r(); return 0; diff --git a/common/cmd_nand.c b/common/cmd_nand.c index 158a55fa70..d71fc49077 100644 --- a/common/cmd_nand.c +++ b/common/cmd_nand.c @@ -389,6 +389,19 @@ int do_nand(cmd_tbl_t * cmdtp, int flag, int argc, char *argv[]) else ret = nand_write_skip_bad(nand, off, &size, (u_char *)addr); + } else if (s != NULL && !strcmp(s, ".yaffs")) { + nand_write_options_t opts; + memset(&opts, 0, sizeof(opts)); + opts.buffer = (u_char *) addr; + opts.length = size; + opts.offset = off; + opts.pad = 0; + opts.blockalign = 1; + opts.quiet = quiet; + opts.writeoob = 1; + opts.autoplace = 1; + opts.forceyaffs = 1; + nand_write_opts(nand, &opts); } else if (!strcmp(s, ".oob")) { /* out-of-band data */ mtd_oob_ops_t ops = { @@ -494,6 +507,8 @@ U_BOOT_CMD(nand, CONFIG_SYS_MAXARGS, 1, do_nand, "nand write - addr off|partition size\n" " read/write 'size' bytes starting at offset 'off'\n" " to/from memory address 'addr', skipping bad blocks.\n" + "nand write[.yaffs] - addr off size - write `size' byte yaffs2 image\n" + " at offset `off' from memory address `addr'\n" "nand erase [clean] [off size] - erase 'size' bytes from\n" " offset 'off' (entire device if not specified)\n" "nand bad - show bad blocks\n" diff --git a/cpu/arm_cortexa8/omap3/board.c b/cpu/arm_cortexa8/omap3/board.c index 59336d7a0d..1c29e2143e 100644 --- a/cpu/arm_cortexa8/omap3/board.c +++ b/cpu/arm_cortexa8/omap3/board.c @@ -36,6 +36,7 @@ #include <asm/io.h> #include <asm/arch/sys_proto.h> #include <asm/arch/mem.h> +#include <nand.h> #include <asm/cache.h> extern omap3_sysinfo sysinfo; @@ -317,6 +318,25 @@ void abort(void) *****************************************************************************/ static int do_switch_ecc(cmd_tbl_t * cmdtp, int flag, int argc, char *argv[]) { + struct mtd_info *mtd; + struct nand_chip *nand; + + /* the following commands operate on the current device */ + if (nand_curr_device < 0 || nand_curr_device >= CONFIG_SYS_MAX_NAND_DEVICE || + !nand_info[nand_curr_device].name) { + puts("\nno devices available\n"); + return 1; + } + mtd = &nand_info[nand_curr_device]; + nand = mtd->priv; + + if (argc == 1) { + if (nand->ecc.mode == NAND_ECC_SOFT) + printf("Software ECC\n"); + else + printf("Hardware ECC\n"); + return 0; + } if (argc != 2) goto usage; if (strncmp(argv[1], "hw", 2) == 0) diff --git a/drivers/mtd/nand/nand_util.c b/drivers/mtd/nand/nand_util.c index 694ead68a1..c866c1210c 100644 --- a/drivers/mtd/nand/nand_util.c +++ b/drivers/mtd/nand/nand_util.c @@ -207,7 +207,6 @@ int nand_erase_opts(nand_info_t *meminfo, const nand_erase_options_t *opts) } /* XXX U-BOOT XXX */ -#if 0 #define MAX_PAGE_SIZE 2048 #define MAX_OOB_SIZE 64 @@ -220,26 +219,48 @@ static unsigned char oob_buf[MAX_OOB_SIZE]; /* OOB layouts to pass into the kernel as default */ static struct nand_ecclayout none_ecclayout = { - .useecc = MTD_NANDECC_OFF, + .eccbytes = 0, }; static struct nand_ecclayout jffs2_ecclayout = { - .useecc = MTD_NANDECC_PLACE, .eccbytes = 6, - .eccpos = { 0, 1, 2, 3, 6, 7 } + .eccpos = { 0, 1, 2, 3, 6, 7 }, + .oobfree = { { 8, 8 } }, + .oobavail = 8, }; static struct nand_ecclayout yaffs_ecclayout = { - .useecc = MTD_NANDECC_PLACE, .eccbytes = 6, - .eccpos = { 8, 9, 10, 13, 14, 15} + .eccpos = { 8, 9, 10, 13, 14, 15}, + .oobfree = { {0, 4}, {6, 2}, {11, 2} }, + .oobavail = 8, }; -static struct nand_ecclayout autoplace_ecclayout = { - .useecc = MTD_NANDECC_AUTOPLACE +#ifdef CONFIG_OMAP3_LOGIC +/* ecclayout for OMAP3_LOGIC, using omap HW ecc */ +static struct nand_ecclayout yaffs2_ecclayout = { + .eccbytes = 12, + .eccpos = { 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13}, + .oobfree = { { 14, 50} }, + .oobavail = 50, +}; +#else +/* ecclayout on chips that page_size = 2K, byte 0,1 is bad block marker */ +static struct nand_ecclayout yaffs2_ecclayout = { + .eccbytes = 24, + .eccpos = { + 40, 41, 42, 43, 44, 45, 46, 47, + 48, 49, 50, 51, 52, 53, 54, 55, + 56, 57, 58, 59, 60, 61, 62, 63}, + .oobfree = { {2, 38} }, + .oobavail = 38, }; #endif +static struct nand_ecclayout autoplace_ecclayout = { + .oobavail = 38, +}; + /* XXX U-BOOT XXX */ #ifdef CONFIG_CMD_NAND_LOCK_UNLOCK @@ -537,6 +558,253 @@ int nand_write_skip_bad(nand_info_t *nand, loff_t offset, size_t *length, } /** + * 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 ecclayout_changed = 0; + int percent_complete = -1; + struct nand_ecclayout *old_ecclayout; + struct nand_chip *chip = meminfo->priv; + 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 ecclayout */ + old_ecclayout = meminfo->ecclayout; + + /* write without ecc? */ + if (opts->noecc) { + meminfo->ecclayout = &none_ecclayout; + ecclayout_changed = 1; + } + + /* autoplace ECC? */ + if (opts->autoplace && (chip->ecc.mode != MTD_NANDECC_AUTOPLACE)) { + + meminfo->ecclayout = &autoplace_ecclayout, + ecclayout_changed = 1; + } + + /* force OOB layout for jffs2 or yaffs? */ + if (opts->forcejffs2 || opts->forceyaffs) { + struct nand_ecclayout *oobsel = + opts->forcejffs2 ? &jffs2_ecclayout : &yaffs2_ecclayout; + + if (meminfo->oobsize == 8) { + if (opts->forceyaffs) { + printf("YAFSS cannot operate on " + "256 Byte page size\n"); + goto restoreoob; + } + } + + meminfo->ecclayout = oobsel; + ecclayout_changed = 1; + } + + /* 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, (unsigned int)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; + + struct mtd_oob_ops ops = { + .mode = MTD_OOB_AUTO, + .len = meminfo->writesize, + .retlen = 0, + .ooblen = meminfo->ecclayout->oobavail, + .oobretlen = 0, + .ooboffs = 0, + .datbuf = NULL, + .oobbuf = oob_buf, + }; + + result = meminfo->write_oob(meminfo, mtdoffset, &ops); + + 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; + +#if 0 + int percent = (int) + ((unsigned long long) + (opts->length-imglen) * 100 + / opts->length); +#endif + /* 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 (ecclayout_changed) + meminfo->ecclayout = old_ecclayout; + + if (imglen > 0) { + printf("Data did not fit into device(imglen %d), due to bad blocks\n", imglen); + return -1; + } + + /* return happy */ + return 0; +} + +/** * nand_read_skip_bad: * * Read image from NAND flash. diff --git a/drivers/mtd/nand/omap_gpmc.c b/drivers/mtd/nand/omap_gpmc.c index 99b9cef17c..5624b06677 100644 --- a/drivers/mtd/nand/omap_gpmc.c +++ b/drivers/mtd/nand/omap_gpmc.c @@ -148,7 +148,9 @@ static int omap_correct_data(struct mtd_info *mtd, uint8_t *dat, */ if ((orig_ecc == 0x0FFF0FFF) && (new_ecc == 0x00000000)) return 0; +#if 0 printf("Error: Bad compare! failed\n"); +#endif /* detected 2 bit error */ return -1; } @@ -267,12 +269,12 @@ void omap_nand_switch_ecc(int32_t hardware) nand->ecc.correct = omap_correct_data; nand->ecc.calculate = omap_calculate_ecc; omap_hwecc_init(nand); - printf("HW ECC selected\n"); + printf("NAND: HW ECC selected\n"); } else { nand->ecc.mode = NAND_ECC_SOFT; /* Use mtd default settings */ nand->ecc.layout = NULL; - printf("SW ECC selected\n"); + printf("NAND: SW ECC selected\n"); } /* Update NAND handling after ECC mode switch */ @@ -296,6 +298,15 @@ void omap_nand_switch_ecc(int32_t hardware) * nand_scan about special functionality. See the defines for further * explanation */ + +static uint8_t omap3_lv_som_scan_ff_pattern[] = { 0xff, 0xff }; +static struct nand_bbt_descr omap3_lv_som_largepage_memorybased = { + .options = 0, + .offs = 0, + .len = 2, + .pattern = omap3_lv_som_scan_ff_pattern +}; + int board_nand_init(struct nand_chip *nand) { int32_t gpmc_config = 0; @@ -340,5 +351,8 @@ int board_nand_init(struct nand_chip *nand) /* Default ECC mode */ nand->ecc.mode = NAND_ECC_SOFT; + /* Use our specific bad-block definition (first byte only) */ + nand->badblock_pattern = &omap3_lv_som_largepage_memorybased; + return 0; } diff --git a/include/configs/omap3_logic.h b/include/configs/omap3_logic.h index 97f7a9bf82..f12e9a0c74 100644 --- a/include/configs/omap3_logic.h +++ b/include/configs/omap3_logic.h @@ -36,6 +36,7 @@ #define CONFIG_OMAP34XX 1 /* which is a 34XX */ #define CONFIG_OMAP3430 1 /* which is in a 3430 */ #define CONFIG_OMAP3_LV_SOM 1 /* working with LV_SOM */ +#define CONFIG_OMAP3_LOGIC 1 /* working with LV_SOM/Torpedo */ #include <asm/arch/cpu.h> /* get chip and board defs */ #include <asm/arch/omap3.h> @@ -327,6 +328,11 @@ #define CONFIG_ENV_OFFSET boot_flash_off #define CONFIG_ENV_ADDR SMNAND_ENV_OFFSET +#if 0 +#define CONFIG_MTD_DEBUG 1 +#define CONFIG_MTD_DEBUG_VERBOSE 2 // Loud MTD debug messages +#endif + /*----------------------------------------------------------------------- * CFI FLASH driver setup */ diff --git a/lib_arm/board.c b/lib_arm/board.c index a44d308f67..f984f6d60d 100644 --- a/lib_arm/board.c +++ b/lib_arm/board.c @@ -364,6 +364,9 @@ void start_armboot (void) puts ("NAND: "); nand_init(); /* go init the NAND */ #endif +#if defined(CONFIG_OMAP3_LOGIC) + omap_nand_switch_ecc(1); /* switch to HW ECC mode */ +#endif #if defined(CONFIG_CMD_ONENAND) onenand_init(); |