diff options
Diffstat (limited to 'common/env_mmc.c')
| -rw-r--r-- | common/env_mmc.c | 132 | 
1 files changed, 123 insertions, 9 deletions
| diff --git a/common/env_mmc.c b/common/env_mmc.c index 02bd5aed1..9ca098fa6 100644 --- a/common/env_mmc.c +++ b/common/env_mmc.c @@ -32,6 +32,11 @@  #include <search.h>  #include <errno.h> +#if defined(CONFIG_ENV_SIZE_REDUND) &&  \ +	(CONFIG_ENV_SIZE_REDUND != CONFIG_ENV_SIZE) +#error CONFIG_ENV_SIZE_REDUND should be the same as CONFIG_ENV_SIZE +#endif +  char *env_name_spec = "MMC";  #ifdef ENV_IS_EMBEDDED @@ -46,9 +51,13 @@ DECLARE_GLOBAL_DATA_PTR;  #define CONFIG_ENV_OFFSET 0  #endif -__weak int mmc_get_env_addr(struct mmc *mmc, u32 *env_addr) +__weak int mmc_get_env_addr(struct mmc *mmc, int copy, u32 *env_addr)  {  	*env_addr = CONFIG_ENV_OFFSET; +#ifdef CONFIG_ENV_OFFSET_REDUND +	if (copy) +		*env_addr = CONFIG_ENV_OFFSET_REDUND; +#endif  	return 0;  } @@ -110,6 +119,10 @@ static inline int write_env(struct mmc *mmc, unsigned long size,  	return (n == blk_cnt) ? 0 : -1;  } +#ifdef CONFIG_ENV_OFFSET_REDUND +static unsigned char env_flags; +#endif +  int saveenv(void)  {  	ALLOC_CACHE_ALIGN_BUFFER(env_t, env_new, 1); @@ -117,16 +130,11 @@ int saveenv(void)  	char	*res;  	struct mmc *mmc = find_mmc_device(CONFIG_SYS_MMC_ENV_DEV);  	u32	offset; -	int	ret; +	int	ret, copy = 0;  	if (init_mmc_for_env(mmc))  		return 1; -	if (mmc_get_env_addr(mmc, &offset)) { -		ret = 1; -		goto fini; -	} -  	res = (char *)&env_new->data;  	len = hexport_r(&env_htab, '\0', 0, &res, ENV_SIZE, 0, NULL);  	if (len < 0) { @@ -136,7 +144,21 @@ int saveenv(void)  	}  	env_new->crc = crc32(0, &env_new->data[0], ENV_SIZE); -	printf("Writing to MMC(%d)... ", CONFIG_SYS_MMC_ENV_DEV); + +#ifdef CONFIG_ENV_OFFSET_REDUND +	env_new->flags	= ++env_flags; /* increase the serial */ + +	if (gd->env_valid == 1) +		copy = 1; +#endif + +	if (mmc_get_env_addr(mmc, copy, &offset)) { +		ret = 1; +		goto fini; +	} + +	printf("Writing to %sMMC(%d)... ", copy ? "redundant " : "", +	       CONFIG_SYS_MMC_ENV_DEV);  	if (write_env(mmc, CONFIG_ENV_SIZE, offset, (u_char *)env_new)) {  		puts("failed\n");  		ret = 1; @@ -146,6 +168,10 @@ int saveenv(void)  	puts("done\n");  	ret = 0; +#ifdef CONFIG_ENV_OFFSET_REDUND +	gd->env_valid = gd->env_valid == 2 ? 1 : 2; +#endif +  fini:  	fini_mmc_for_env(mmc);  	return ret; @@ -166,6 +192,93 @@ static inline int read_env(struct mmc *mmc, unsigned long size,  	return (n == blk_cnt) ? 0 : -1;  } +#ifdef CONFIG_ENV_OFFSET_REDUND +void env_relocate_spec(void) +{ +#if !defined(ENV_IS_EMBEDDED) +	struct mmc *mmc = find_mmc_device(CONFIG_SYS_MMC_ENV_DEV); +	u32 offset1, offset2; +	int read1_fail = 0, read2_fail = 0; +	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); +	if (tmp_env1 == NULL || tmp_env2 == NULL) { +		puts("Can't allocate buffers for environment\n"); +		ret = 1; +		goto err; +	} + +	if (init_mmc_for_env(mmc)) { +		ret = 1; +		goto err; +	} + +	if (mmc_get_env_addr(mmc, 0, &offset1) || +	    mmc_get_env_addr(mmc, 1, &offset2)) { +		ret = 1; +		goto fini; +	} + +	read1_fail = read_env(mmc, CONFIG_ENV_SIZE, offset1, tmp_env1); +	read2_fail = read_env(mmc, CONFIG_ENV_SIZE, offset2, tmp_env2); + +	if (read1_fail && read2_fail) +		puts("*** Error - No Valid Environment Area found\n"); +	else if (read1_fail || read2_fail) +		puts("*** Warning - some problems detected " +		     "reading environment; recovered successfully\n"); + +	crc1_ok = !read1_fail && +		(crc32(0, tmp_env1->data, ENV_SIZE) == tmp_env1->crc); +	crc2_ok = !read2_fail && +		(crc32(0, tmp_env2->data, ENV_SIZE) == tmp_env2->crc); + +	if (!crc1_ok && !crc2_ok) { +		ret = 1; +		goto fini; +	} 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) +		ep = tmp_env1; +	else +		ep = tmp_env2; + +	env_flags = ep->flags; +	env_import((char *)ep, 0); +	ret = 0; + +fini: +	fini_mmc_for_env(mmc); +err: +	if (ret) +		set_default_env(NULL); + +	free(tmp_env1); +	free(tmp_env2); +#endif +} +#else /* ! CONFIG_ENV_OFFSET_REDUND */  void env_relocate_spec(void)  {  #if !defined(ENV_IS_EMBEDDED) @@ -179,7 +292,7 @@ void env_relocate_spec(void)  		goto err;  	} -	if (mmc_get_env_addr(mmc, &offset)) { +	if (mmc_get_env_addr(mmc, 0, &offset)) {  		ret = 1;  		goto fini;  	} @@ -199,3 +312,4 @@ err:  		set_default_env(NULL);  #endif  } +#endif /* CONFIG_ENV_OFFSET_REDUND */ |