summaryrefslogtreecommitdiff
path: root/common/env_nand.c
diff options
context:
space:
mode:
Diffstat (limited to 'common/env_nand.c')
-rw-r--r--common/env_nand.c161
1 files changed, 141 insertions, 20 deletions
diff --git a/common/env_nand.c b/common/env_nand.c
index 14446a6a579..facb4b3016f 100644
--- a/common/env_nand.c
+++ b/common/env_nand.c
@@ -153,30 +153,64 @@ int env_init(void)
* The legacy NAND code saved the environment in the first NAND device i.e.,
* nand_dev_desc + 0. This is also the behaviour using the new NAND code.
*/
-int writeenv(size_t offset, u_char *buf)
+static int writeenv(size_t offset, u_char *buf)
{
size_t end = offset + CONFIG_ENV_RANGE;
size_t amount_saved = 0;
size_t blocksize, len;
-
+ int ret, ncopies = 0;
u_char *char_ptr;
+#if defined(CONFIG_NAND_MULTIPLE_ECC)
+ int current_ecc_method;
+#endif
+
+#if defined(CONFIG_NAND_MULTIPLE_ECC)
+ nand_switch_ecc_default(&current_ecc_method);
+#endif
blocksize = nand_info[0].erasesize;
len = min(blocksize, CONFIG_ENV_SIZE);
- while (amount_saved < CONFIG_ENV_SIZE && offset < end) {
+ while (offset < end) {
if (nand_block_isbad(&nand_info[0], offset)) {
offset += blocksize;
} else {
char_ptr = &buf[amount_saved];
- if (nand_write(&nand_info[0], offset, &len,
- char_ptr))
- return 1;
- offset += blocksize;
- amount_saved += len;
+ /* Data written can not straddle a block in case
+ one of the two blocks goes bad */
+ if ((offset & ~(blocksize - 1)) != ((offset + len - 1) & ~(blocksize - 1)))
+ offset = (offset + blocksize - 1) & ~(blocksize-1);
+
+ if (offset >= end)
+ break;
+
+ ret = nand_write(&nand_info[0], offset, &len,
+ char_ptr);
+
+ if (ret) {
+ /* Need to move offset forward by len,
+ * then to the start of the next block,
+ * reset amount_amount saved to start
+ * at the beginning of the environment */
+ offset += len;
+ offset = (offset + blocksize - 1) & ~(blocksize - 1);
+ amount_saved = 0;
+ } else {
+ amount_saved += len;
+ if (amount_saved >= CONFIG_ENV_SIZE) {
+ ncopies++;
+ amount_saved = 0;
+ }
+ }
+ offset += len;
}
}
- if (amount_saved != CONFIG_ENV_SIZE)
+
+#if defined(CONFIG_NAND_MULTIPLE_ECC)
+ nand_switch_ecc_method(current_ecc_method);
+#endif
+
+ if (!ncopies)
return 1;
return 0;
@@ -211,8 +245,9 @@ int saveenv(void)
if(gd->env_valid == 1) {
puts("Erasing redundant NAND...\n");
nand_erase_options.offset = CONFIG_ENV_OFFSET_REDUND;
- if (nand_erase_opts(&nand_info[0], &nand_erase_options))
+ if (nand_erase_opts(&nand_info[0], &nand_erase_options)) {
return 1;
+ }
puts("Writing to redundant NAND... ");
ret = writeenv(CONFIG_ENV_OFFSET_REDUND,
@@ -220,8 +255,9 @@ int saveenv(void)
} else {
puts("Erasing NAND...\n");
nand_erase_options.offset = CONFIG_ENV_OFFSET;
- if (nand_erase_opts(&nand_info[0], &nand_erase_options))
+ if (nand_erase_opts(&nand_info[0], &nand_erase_options)) {
return 1;
+ }
puts("Writing to NAND... ");
ret = writeenv(CONFIG_ENV_OFFSET,
@@ -263,8 +299,9 @@ int saveenv(void)
env_new.crc = crc32(0, env_new.data, ENV_SIZE);
puts("Erasing Nand...\n");
- if (nand_erase_opts(&nand_info[0], &nand_erase_options))
+ if (nand_erase_opts(&nand_info[0], &nand_erase_options)) {
return 1;
+ }
puts("Writing to Nand... ");
if (writeenv(CONFIG_ENV_OFFSET, (u_char *)&env_new)) {
@@ -273,40 +310,123 @@ int saveenv(void)
}
puts("done\n");
+
return ret;
}
#endif /* CONFIG_ENV_OFFSET_REDUND */
#endif /* CMD_SAVEENV */
-int readenv(size_t offset, u_char * buf)
+#ifdef CONFIG_ENV_OFFSET_REDUND
+static int readenv(size_t offset, u_char * buf)
{
size_t end = offset + CONFIG_ENV_RANGE;
size_t amount_loaded = 0;
size_t blocksize, len;
-
u_char *char_ptr;
+#if defined(CONFIG_NAND_MULTIPLE_ECC)
+ int current_ecc_method;
+#endif
blocksize = nand_info[0].erasesize;
if (!blocksize)
return 1;
len = min(blocksize, CONFIG_ENV_SIZE);
+#if defined(CONFIG_NAND_MULTIPLE_ECC)
+ nand_switch_ecc_default(&current_ecc_method);
+#endif
+
while (amount_loaded < CONFIG_ENV_SIZE && offset < end) {
if (nand_block_isbad(&nand_info[0], offset)) {
offset += blocksize;
} else {
char_ptr = &buf[amount_loaded];
- if (nand_read_skip_bad(&nand_info[0], offset, &len, char_ptr))
+ if (nand_read_skip_bad(&nand_info[0], offset, &len, char_ptr)) {
+#if defined(CONFIG_NAND_MULTIPLE_ECC)
+ nand_switch_ecc_method(current_ecc_method);
+#endif
+
return 1;
+ }
offset += blocksize;
amount_loaded += len;
}
}
+#if defined(CONFIG_NAND_MULTIPLE_ECC)
+ nand_switch_ecc_method(current_ecc_method);
+#endif
if (amount_loaded != CONFIG_ENV_SIZE)
return 1;
return 0;
}
+#else /* ! CONFIG_ENV_OFFSET_REDUND */
+/* Read the environment, checking along the way. return 0 if valid
+ * environment or no environment is found, negative on error */
+static int readenv(size_t offset, u_char * buf)
+{
+ size_t end = offset + CONFIG_ENV_RANGE;
+ size_t amount_loaded = 0;
+ size_t blocksize, len;
+ u_char *char_ptr;
+ int ret;
+#if defined(CONFIG_NAND_MULTIPLE_ECC)
+ int current_ecc_method;
+#endif
+
+ blocksize = nand_info[0].erasesize;
+ if (!blocksize)
+ return -1;
+ len = min(blocksize, CONFIG_ENV_SIZE);
+
+#if defined(CONFIG_NAND_MULTIPLE_ECC)
+ nand_switch_ecc_default(&current_ecc_method);
+#endif
+
+ while (offset < end) {
+ if (nand_block_isbad(&nand_info[0], offset)) {
+ offset += blocksize;
+ } else {
+ char_ptr = &buf[amount_loaded];
+ /* Data read can not straddle a block in case
+ one of the two blocks goes bad */
+ if ((offset & ~(blocksize - 1)) != ((offset + len - 1) & ~(blocksize - 1)))
+ offset = (offset + blocksize - 1) & ~(blocksize-1);
+ if (offset >= end)
+ break;
+
+ ret = nand_read(&nand_info[0], offset, &len, char_ptr);
+
+ if (ret) {
+ /* Need to move offset forward by len,
+ * then to the start of the next block,
+ * reset amount_amount saved to start
+ * at the beginning of the environment */
+ offset += len;
+ offset = (offset + blocksize - 1) & ~(blocksize - 1);
+ amount_loaded = 0;
+ } else {
+ amount_loaded += len;
+ if (amount_loaded >= CONFIG_ENV_SIZE) {
+ if (env_check_valid(buf))
+ goto found;
+ amount_loaded = 0;
+ }
+ }
+ offset += len;
+ }
+ }
+
+found:
+#if defined(CONFIG_NAND_MULTIPLE_ECC)
+ nand_switch_ecc_method(current_ecc_method);
+#endif
+ if (amount_loaded != CONFIG_ENV_SIZE)
+ return 0;
+
+ return 1;
+}
+#endif
#ifdef CONFIG_ENV_OFFSET_OOB
int get_nand_env_oob(nand_info_t *nand, unsigned long *result)
@@ -346,6 +466,7 @@ void env_relocate_spec(void)
#if !defined(ENV_IS_EMBEDDED)
int crc1_ok = 0, crc2_ok = 0;
env_t *ep, *tmp_env1, *tmp_env2;
+ int ret;
tmp_env1 = (env_t *)malloc(CONFIG_ENV_SIZE);
tmp_env2 = (env_t *)malloc(CONFIG_ENV_SIZE);
@@ -358,11 +479,11 @@ void env_relocate_spec(void)
return;
}
- if (readenv(CONFIG_ENV_OFFSET, (u_char *) tmp_env1))
- puts("No Valid Environment Area found\n");
+ ret = readenv(CONFIG_ENV_OFFSET, (u_char *) tmp_env1);
+ printf("%sValid Environment Area found\n", ret ? "No ":"");
- if (readenv(CONFIG_ENV_OFFSET_REDUND, (u_char *) tmp_env2))
- puts("No Valid Redundant Environment Area found\n");
+ ret = readenv(CONFIG_ENV_OFFSET_REDUND, (u_char *) tmp_env2);
+ printf("%sValid Redundant Environment Area found\n", ret ? "No ":"");
crc1_ok = (crc32(0, tmp_env1->data, ENV_SIZE) == tmp_env1->crc);
crc2_ok = (crc32(0, tmp_env2->data, ENV_SIZE) == tmp_env2->crc);
@@ -433,7 +554,7 @@ void env_relocate_spec (void)
#endif
ret = readenv(CONFIG_ENV_OFFSET, (u_char *)buf);
- if (ret) {
+ if (ret < 0) {
set_default_env("!readenv() failed");
return;
}