summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorDavid Ungar <david.ungar@timesys.com>2010-10-11 14:22:33 -0400
committerDavid Ungar <david.ungar@timesys.com>2010-10-11 14:22:33 -0400
commitc455ad7d7ceff60f0850697dc1949218972db3c8 (patch)
treebe7b0391c166c296c231f5000ad3156960f178af
parentb52e80bcc86390b6d2f09cec70a1a4bc5e970cb9 (diff)
yaffs support
-rw-r--r--board/omap3/logic/logic.c3
-rw-r--r--common/cmd_nand.c15
-rw-r--r--cpu/arm_cortexa8/omap3/board.c20
-rw-r--r--drivers/mtd/nand/nand_util.c284
-rw-r--r--drivers/mtd/nand/omap_gpmc.c18
-rw-r--r--include/configs/omap3_logic.h6
-rw-r--r--lib_arm/board.c3
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();