From c9f7351b5bb70d292f6b0baaf0e21366e0b0b163 Mon Sep 17 00:00:00 2001 From: Ben Gardiner Date: Mon, 5 Jul 2010 13:27:07 -0400 Subject: NAND: environment offset in OOB (CONFIG_ENV_OFFSET_OOB) This is a re-submission of the patch by Harald Welte with minor modifications for rebase and changes as suggested by Scott Wood [1] [2]. This patch enables the environment partition to have a run-time dynamic location (offset) in the NAND flash. The reason for this is simply that all NAND flashes have factory-default bad blocks, and a fixed compile time offset would mean that sometimes the environment partition would live inside factory bad blocks. Since the number of factory default blocks can be quite high (easily 1.3MBytes in current standard components), it is not economic to keep that many spare blocks inside the environment partition. With this patch and CONFIG_ENV_OFFSET_OOB enabled, the location of the environment partition is stored in the out-of-band (OOB) data of the first block in flash. Since the first block is where most systems boot from, the vendors guarantee that the first block is not a factory default block. This patch introduces the 'nand env.oob' command, which can be called from the u-boot command line. 'nand env.oob get' reads the address of the environment partition from the OOB data, 'nand env.oob set {offset,partition-name}' allows the setting of the marker by specifying a numeric offset or a partition name. [1] http://article.gmane.org/gmane.comp.boot-loaders.u-boot/43916 [2] http://article.gmane.org/gmane.comp.boot-loaders.u-boot/79195 Signed-off-by: Ben Gardiner Acked-by: Harald Welte --- common/cmd_nand.c | 107 +++++++++++++++++++++++++++++++++++++++++++++++++++++- common/env_nand.c | 46 +++++++++++++++++++++++ 2 files changed, 152 insertions(+), 1 deletion(-) (limited to 'common') diff --git a/common/cmd_nand.c b/common/cmd_nand.c index ea80555..a4c67c1 100644 --- a/common/cmd_nand.c +++ b/common/cmd_nand.c @@ -4,6 +4,10 @@ * (c) 1999 Machine Vision Holdings, Inc. * (c) 1999, 2000 David Woodhouse * + * Ported 'dynenv' to 'nand env.oob' command + * (C) 2010 Nanometrics, Inc. + * 'dynenv' -- Dynamic environment offset in NAND OOB + * (C) Copyright 2006-2007 OpenMoko, Inc. * Added 16-bit nand support * (C) 2004 Texas Instruments */ @@ -193,6 +197,90 @@ static void do_nand_status(nand_info_t *nand) } #endif +#ifdef CONFIG_ENV_OFFSET_OOB +unsigned long nand_env_oob_offset; + +int do_nand_env_oob(cmd_tbl_t *cmdtp, nand_info_t *nand, + int argc, char * const argv[]) +{ + int ret; + uint32_t oob_buf[ENV_OFFSET_SIZE/sizeof(uint32_t)]; + + char *cmd = argv[1]; + + if (!strcmp(cmd, "get")) { + ret = get_nand_env_oob(nand, &nand_env_oob_offset); + if (!ret) + printf("0x%08lx\n", nand_env_oob_offset); + else + return 1; + } else if (!strcmp(cmd, "set")) { + ulong addr; + size_t dummy_size; + struct mtd_oob_ops ops; + + if (argc < 3) + goto usage; + + if (arg_off_size(argc-2, argv + 2, nand, &addr, + &dummy_size) < 0) { + printf("Offset or partition name expected\n"); + return 1; + } + + if (nand->oobavail < ENV_OFFSET_SIZE) { + printf("Insufficient available OOB bytes: %d OOB bytes" + " available but %d required for env.oob support\n", + nand->oobavail, + ENV_OFFSET_SIZE); + return 1; + } + + if ((addr & (nand->erasesize - 1)) != 0) { + printf("Environment offset must be block-aligned\n"); + return 1; + } + + ops.datbuf = NULL; + ops.mode = MTD_OOB_AUTO; + ops.ooboffs = 0; + ops.ooblen = ENV_OFFSET_SIZE; + ops.oobbuf = (void *) oob_buf; + + oob_buf[0] = ENV_OOB_MARKER; + oob_buf[1] = addr / nand->erasesize; + + ret = nand->write_oob(nand, ENV_OFFSET_SIZE, &ops); + if (!ret) { + ret = get_nand_env_oob(nand, &nand_env_oob_offset); + if (ret) { + printf("Error reading env offset in OOB\n"); + return ret; + } + + if (addr != nand_env_oob_offset) { + printf("Verification of env offset in OOB " + "failed: 0x%08lx expected but got " + "0x%08lx\n", addr, nand_env_oob_offset); + return 1; + } + } else { + printf("Error writing OOB block 0\n"); + return ret; + } + } else { + goto usage; + } + + return ret; + +usage: + cmd_usage(cmdtp); + return 1; +} + +#endif + static void nand_print_info(int idx) { nand_info_t *nand = &nand_info[idx]; @@ -272,9 +360,19 @@ int do_nand(cmd_tbl_t * cmdtp, int flag, int argc, char * const argv[]) strncmp(cmd, "read", 4) != 0 && strncmp(cmd, "write", 5) != 0 && strcmp(cmd, "scrub") != 0 && strcmp(cmd, "markbad") != 0 && strcmp(cmd, "biterr") != 0 && - strcmp(cmd, "lock") != 0 && strcmp(cmd, "unlock") != 0 ) + strcmp(cmd, "lock") != 0 && strcmp(cmd, "unlock") != 0 +#ifdef CONFIG_ENV_OFFSET_OOB + && strcmp(cmd, "env.oob") != 0 +#endif + ) goto usage; +#ifdef CONFIG_ENV_OFFSET_OOB + /* this command operates only on the first nand device */ + if (strcmp(cmd, "env.oob") == 0) + return do_nand_env_oob(cmdtp, &nand_info[0], argc - 1, argv + 1); +#endif + /* 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) { @@ -502,6 +600,13 @@ U_BOOT_CMD(nand, CONFIG_SYS_MAXARGS, 1, do_nand, " bring nand to lock state or display locked pages\n" "nand unlock [offset] [size] - unlock section" #endif +#ifdef CONFIG_ENV_OFFSET_OOB + "\n" + "nand env.oob - environment offset in OOB of block 0 of" + " first device.\n" + "nand env.oob set off|partition - set enviromnent offset\n" + "nand env.oob get - get environment offset" +#endif ); static int nand_load_image(cmd_tbl_t *cmdtp, nand_info_t *nand, diff --git a/common/env_nand.c b/common/env_nand.c index 50bc111..47d9848 100644 --- a/common/env_nand.c +++ b/common/env_nand.c @@ -38,6 +38,7 @@ #include #include #include +#include #if defined(CONFIG_CMD_SAVEENV) && defined(CONFIG_CMD_NAND) #define CMD_SAVEENV @@ -284,6 +285,40 @@ int readenv (size_t offset, u_char * buf) return 0; } +#ifdef CONFIG_ENV_OFFSET_OOB +int get_nand_env_oob(nand_info_t *nand, unsigned long *result) +{ + struct mtd_oob_ops ops; + uint32_t oob_buf[ENV_OFFSET_SIZE/sizeof(uint32_t)]; + int ret; + + ops.datbuf = NULL; + ops.mode = MTD_OOB_AUTO; + ops.ooboffs = 0; + ops.ooblen = ENV_OFFSET_SIZE; + ops.oobbuf = (void *) oob_buf; + + ret = nand->read_oob(nand, ENV_OFFSET_SIZE, &ops); + + if (!ret) { + if (oob_buf[0] == ENV_OOB_MARKER) { + *result = oob_buf[1] * nand->erasesize; + } else if (oob_buf[0] == ENV_OOB_MARKER_OLD) { + *result = oob_buf[1]; + } else { + printf("No dynamic environment marker in OOB block 0" + "\n"); + ret = -ENOENT; + goto fail; + } + } else { + printf("error reading OOB block 0\n"); + } +fail: + return ret; +} +#endif + #ifdef CONFIG_ENV_OFFSET_REDUND void env_relocate_spec (void) { @@ -353,6 +388,17 @@ void env_relocate_spec (void) #if !defined(ENV_IS_EMBEDDED) int ret; +#if defined(CONFIG_ENV_OFFSET_OOB) + ret = get_nand_env_oob(&nand_info[0], &nand_env_oob_offset); + /* If unable to read environment offset from NAND OOB then fall through + * to the normal environment reading code below + */ + if (!ret) + printf("Found Environment offset in OOB..\n"); + else + return use_default(); +#endif + ret = readenv(CONFIG_ENV_OFFSET, (u_char *) env_ptr); if (ret) return use_default(); -- cgit v1.1 From 53504a278900939e197d8c35164c27ffee861691 Mon Sep 17 00:00:00 2001 From: Scott Wood Date: Mon, 12 Jul 2010 18:17:40 -0500 Subject: NAND: formatting cleanups from env.oob support Change if (ok) { bunch of stuff } else { error } to if (error) { get out } proceed with bunch of stuff Plus a few whitespace cleanups. Signed-off-by: Scott Wood --- common/cmd_nand.c | 54 ++++++++++++++++++++++++++++-------------------------- common/env_nand.c | 26 ++++++++++++-------------- 2 files changed, 40 insertions(+), 40 deletions(-) (limited to 'common') diff --git a/common/cmd_nand.c b/common/cmd_nand.c index a4c67c1..84b6272 100644 --- a/common/cmd_nand.c +++ b/common/cmd_nand.c @@ -201,7 +201,7 @@ static void do_nand_status(nand_info_t *nand) unsigned long nand_env_oob_offset; int do_nand_env_oob(cmd_tbl_t *cmdtp, nand_info_t *nand, - int argc, char * const argv[]) + int argc, char * const argv[]) { int ret; uint32_t oob_buf[ENV_OFFSET_SIZE/sizeof(uint32_t)]; @@ -210,10 +210,10 @@ int do_nand_env_oob(cmd_tbl_t *cmdtp, nand_info_t *nand, if (!strcmp(cmd, "get")) { ret = get_nand_env_oob(nand, &nand_env_oob_offset); - if (!ret) - printf("0x%08lx\n", nand_env_oob_offset); - else + if (ret) return 1; + + printf("0x%08lx\n", nand_env_oob_offset); } else if (!strcmp(cmd, "set")) { ulong addr; size_t dummy_size; @@ -222,17 +222,17 @@ int do_nand_env_oob(cmd_tbl_t *cmdtp, nand_info_t *nand, if (argc < 3) goto usage; - if (arg_off_size(argc-2, argv + 2, nand, &addr, - &dummy_size) < 0) { + if (arg_off_size(argc - 2, argv + 2, nand, &addr, + &dummy_size) < 0) { printf("Offset or partition name expected\n"); return 1; } if (nand->oobavail < ENV_OFFSET_SIZE) { - printf("Insufficient available OOB bytes: %d OOB bytes" - " available but %d required for env.oob support\n", - nand->oobavail, - ENV_OFFSET_SIZE); + printf("Insufficient available OOB bytes:\n" + "%d OOB bytes available but %d required for " + "env.oob support\n", + nand->oobavail, ENV_OFFSET_SIZE); return 1; } @@ -251,23 +251,23 @@ int do_nand_env_oob(cmd_tbl_t *cmdtp, nand_info_t *nand, oob_buf[1] = addr / nand->erasesize; ret = nand->write_oob(nand, ENV_OFFSET_SIZE, &ops); - if (!ret) { - ret = get_nand_env_oob(nand, &nand_env_oob_offset); - if (ret) { - printf("Error reading env offset in OOB\n"); - return ret; - } - - if (addr != nand_env_oob_offset) { - printf("Verification of env offset in OOB " - "failed: 0x%08lx expected but got " - "0x%08lx\n", addr, nand_env_oob_offset); - return 1; - } - } else { + if (ret) { printf("Error writing OOB block 0\n"); return ret; } + + ret = get_nand_env_oob(nand, &nand_env_oob_offset); + if (ret) { + printf("Error reading env offset in OOB\n"); + return ret; + } + + if (addr != nand_env_oob_offset) { + printf("Verification of env offset in OOB failed: " + "0x%08lx expected but got 0x%08lx\n", + addr, nand_env_oob_offset); + return 1; + } } else { goto usage; } @@ -369,8 +369,10 @@ int do_nand(cmd_tbl_t * cmdtp, int flag, int argc, char * const argv[]) #ifdef CONFIG_ENV_OFFSET_OOB /* this command operates only on the first nand device */ - if (strcmp(cmd, "env.oob") == 0) - return do_nand_env_oob(cmdtp, &nand_info[0], argc - 1, argv + 1); + if (strcmp(cmd, "env.oob") == 0) { + return do_nand_env_oob(cmdtp, &nand_info[0], + argc - 1, argv + 1); + } #endif /* the following commands operate on the current device */ diff --git a/common/env_nand.c b/common/env_nand.c index 47d9848..a5e1038 100644 --- a/common/env_nand.c +++ b/common/env_nand.c @@ -299,23 +299,21 @@ int get_nand_env_oob(nand_info_t *nand, unsigned long *result) ops.oobbuf = (void *) oob_buf; ret = nand->read_oob(nand, ENV_OFFSET_SIZE, &ops); + if (ret) { + printf("error reading OOB block 0\n"); + return ret; + } - if (!ret) { - if (oob_buf[0] == ENV_OOB_MARKER) { - *result = oob_buf[1] * nand->erasesize; - } else if (oob_buf[0] == ENV_OOB_MARKER_OLD) { - *result = oob_buf[1]; - } else { - printf("No dynamic environment marker in OOB block 0" - "\n"); - ret = -ENOENT; - goto fail; - } + if (oob_buf[0] == ENV_OOB_MARKER) { + *result = oob_buf[1] * nand->erasesize; + } else if (oob_buf[0] == ENV_OOB_MARKER_OLD) { + *result = oob_buf[1]; } else { - printf("error reading OOB block 0\n"); + printf("No dynamic environment marker in OOB block 0\n"); + return -ENOENT; } -fail: - return ret; + + return 0; } #endif -- cgit v1.1