From e443c944cf4050daffb46d4788446d6c2df8ac6c Mon Sep 17 00:00:00 2001 From: Markus Klotzbuecher Date: Mon, 20 Mar 2006 18:02:44 +0100 Subject: [PATCH] Support for redundant environment in NAND Flash. --- README | 11 ++++ common/env_nand.c | 115 +++++++++++++++++++++++++++++++++++----- include/configs/delta.h | 1 + include/environment.h | 12 +++++ 4 files changed, 126 insertions(+), 13 deletions(-) diff --git a/README b/README index 6f6100822..44421497b 100644 --- a/README +++ b/README @@ -1946,6 +1946,17 @@ to save the current settings. These two #defines specify the offset and size of the environment area within the first NAND device. + - CFG_ENV_OFFSET_REDUND + + This setting describes a second storage area of CFG_ENV_SIZE + size used to hold a redundant copy of the environment data, + so that there is a valid backup copy in case there is a + power failure during a "saveenv" operation. + + Note: CFG_ENV_OFFSET and CFG_ENV_OFFSET_REDUND must be aligned + to a block boundary, and CFG_ENV_SIZE must be a multiple of + the NAND devices block size. + - CFG_SPI_INIT_OFFSET Defines offset to the initial SPI buffer area in DPRAM. The diff --git a/common/env_nand.c b/common/env_nand.c index 4ae68c5a2..2e1bfa666 100644 --- a/common/env_nand.c +++ b/common/env_nand.c @@ -36,21 +36,19 @@ #include #include #include +#include #include #if ((CONFIG_COMMANDS&(CFG_CMD_ENV|CFG_CMD_NAND)) == (CFG_CMD_ENV|CFG_CMD_NAND)) #define CMD_SAVEENV +#elif defined(CFG_ENV_OFFSET_REDUND) +#error Cannot use CFG_ENV_OFFSET_REDUND without CFG_CMD_ENV & CFG_CMD_NAND #endif -#if defined(CFG_ENV_SIZE_REDUND) -#error CFG_ENV_SIZE_REDUND not supported yet +#if defined(CFG_ENV_SIZE_REDUND) && (CFG_ENV_SIZE_REDUND != CFG_ENV_SIZE) +#error CFG_ENV_SIZE_REDUND should be the same as CFG_ENV_SIZE #endif -#if defined(CFG_ENV_ADDR_REDUND) -#error CFG_ENV_ADDR_REDUND and CFG_ENV_IS_IN_NAND not supported yet -#endif - - #ifdef CONFIG_INFERNO #error CONFIG_INFERNO not supported yet #endif @@ -99,7 +97,7 @@ int env_init(void) { DECLARE_GLOBAL_DATA_PTR; - gd->env_addr = (ulong)&default_environment[0]; + gd->env_addr = (ulong)&default_environment[0]; gd->env_valid = 1; return (0); @@ -110,9 +108,45 @@ 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. */ +#ifdef CFG_ENV_OFFSET_REDUND int saveenv(void) { - int total, ret = 0; + int total, ret = 0; + + DECLARE_GLOBAL_DATA_PTR; + + env_ptr->flags++; + total = CFG_ENV_SIZE; + + if(gd->env_valid == 1) { + puts ("Erasing redundant Nand..."); + if (nand_erase(&nand_info[0], + CFG_ENV_OFFSET_REDUND, CFG_ENV_SIZE)) + return 1; + puts ("Writing to redundant Nand... "); + ret = nand_write(&nand_info[0], CFG_ENV_OFFSET_REDUND, &total, + (u_char*) env_ptr); + } else { + puts ("Erasing Nand..."); + if (nand_erase(&nand_info[0], + CFG_ENV_OFFSET, CFG_ENV_SIZE)) + return 1; + + puts ("Writing to Nand... "); + ret = nand_write(&nand_info[0], CFG_ENV_OFFSET, &total, + (u_char*) env_ptr); + } + if (ret || total != CFG_ENV_SIZE) + return 1; + + puts ("done\n"); + gd->env_valid = (gd->env_valid == 2 ? 1 : 2); + return ret; +} +#else /* ! CFG_ENV_OFFSET_REDUND */ +int saveenv(void) +{ + int total, ret = 0; puts ("Erasing Nand..."); if (nand_erase(&nand_info[0], CFG_ENV_OFFSET, CFG_ENV_SIZE)) @@ -128,9 +162,64 @@ int saveenv(void) puts ("done\n"); return ret; } +#endif /* CFG_ENV_OFFSET_REDUND */ #endif /* CMD_SAVEENV */ +#ifdef CFG_ENV_OFFSET_REDUND +void env_relocate_spec (void) +{ +#if !defined(ENV_IS_EMBEDDED) + int crc1_ok = 0, crc2_ok = 0, total; + env_t *tmp_env1, *tmp_env2; + DECLARE_GLOBAL_DATA_PTR; + + total = CFG_ENV_SIZE; + + tmp_env1 = (env_t *) malloc(CFG_ENV_SIZE); + tmp_env2 = (env_t *) malloc(CFG_ENV_SIZE); + + nand_read(&nand_info[0], CFG_ENV_OFFSET, &total, + (u_char*) tmp_env1); + nand_read(&nand_info[0], CFG_ENV_OFFSET_REDUND, &total, + (u_char*) tmp_env2); + + crc1_ok = (crc32(0, tmp_env1->data, ENV_SIZE) == tmp_env1->crc); + crc2_ok = (crc32(0, tmp_env2->data, ENV_SIZE) == tmp_env2->crc); + + if(!crc1_ok && !crc2_ok) + return use_default(); + else if(crc1_ok && !crc2_ok) + gd->env_valid = 1; + else if(!crc1_ok && crc2_ok) + gd->env_valid = 2; + else { + /* both ok - check serial */ + if(tmp_env1->flags == 255 && tmp_env2->flags == 0) + gd->env_valid = 2; + else if(tmp_env2->flags == 255 && tmp_env1->flags == 0) + gd->env_valid = 1; + else if(tmp_env1->flags > tmp_env2->flags) + gd->env_valid = 1; + else if(tmp_env2->flags > tmp_env1->flags) + gd->env_valid = 2; + else /* flags are equal - almost impossible */ + gd->env_valid = 1; + + } + + free(env_ptr); + if(gd->env_valid == 1) { + env_ptr = tmp_env1; + free(tmp_env2); + } else { + env_ptr = tmp_env2; + free(tmp_env1); + } + +#endif /* ! ENV_IS_EMBEDDED */ +} +#else /* ! CFG_ENV_OFFSET_REDUND */ /* * 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. @@ -143,14 +232,14 @@ void env_relocate_spec (void) total = CFG_ENV_SIZE; ret = nand_read(&nand_info[0], CFG_ENV_OFFSET, &total, (u_char*) env_ptr); - if (ret || total != CFG_ENV_SIZE) + if (ret || total != CFG_ENV_SIZE) return use_default(); if (crc32(0, env_ptr->data, ENV_SIZE) != env_ptr->crc) return use_default(); #endif /* ! ENV_IS_EMBEDDED */ - } +#endif /* CFG_ENV_OFFSET_REDUND */ static void use_default() { @@ -158,7 +247,7 @@ static void use_default() puts ("*** Warning - bad CRC or NAND, using default environment\n\n"); - if (default_environment_size > CFG_ENV_SIZE){ + if (default_environment_size > CFG_ENV_SIZE){ puts ("*** Error - default environment is too large\n\n"); return; } @@ -168,7 +257,7 @@ static void use_default() default_environment, default_environment_size); env_ptr->crc = crc32(0, env_ptr->data, ENV_SIZE); - gd->env_valid = 1; + gd->env_valid = 1; } diff --git a/include/configs/delta.h b/include/configs/delta.h index b42a7e2c1..f230b0385 100644 --- a/include/configs/delta.h +++ b/include/configs/delta.h @@ -282,6 +282,7 @@ /* #define CFG_ENV_IS_NOWHERE */ #define CFG_ENV_IS_IN_NAND 1 #define CFG_ENV_OFFSET 0x40000 +#define CFG_ENV_OFFSET_REDUND 0x44000 #define CFG_ENV_SIZE 0x4000 #endif /* __CONFIG_H */ diff --git a/include/environment.h b/include/environment.h index bb109649f..422f80089 100644 --- a/include/environment.h +++ b/include/environment.h @@ -69,6 +69,18 @@ # endif #endif /* CFG_ENV_IS_IN_FLASH */ +#if defined(CFG_ENV_IS_IN_NAND) +# ifndef CFG_ENV_OFFSET +# error "Need to define CFG_ENV_OFFSET when using CFG_ENV_IS_IN_NAND" +# endif +# ifndef CFG_ENV_SIZE +# error "Need to define CFG_ENV_SIZE when using CFG_ENV_IS_IN_NAND" +# endif +# ifdef CFG_ENV_OFFSET_REDUND +# define CFG_REDUNDAND_ENVIRONMENT +# endif +#endif /* CFG_ENV_IS_IN_NAND */ + #ifdef CFG_REDUNDAND_ENVIRONMENT # define ENV_HEADER_SIZE (sizeof(unsigned long) + 1)