Newer
Older
* (C) Copyright 2008-2011 Freescale Semiconductor, Inc.
*
* See file CREDITS for list of people who contributed to this
* project.
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License as
* published by the Free Software Foundation; either version 2 of
* the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston,
* MA 02111-1307 USA
*/
/* #define DEBUG */
#include <common.h>
#include <command.h>
#include <environment.h>
#include <linux/stddef.h>
#include <malloc.h>
#include <mmc.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
env_t *env_ptr = &environment;
#endif /* ENV_IS_EMBEDDED */
DECLARE_GLOBAL_DATA_PTR;
#if !defined(CONFIG_ENV_OFFSET)
#define CONFIG_ENV_OFFSET 0
#endif
__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;
}
gd->env_addr = (ulong)&default_environment[0];
gd->env_valid = 1;
static int init_mmc_for_env(struct mmc *mmc)
{
if (!mmc) {
puts("No MMC card found\n");
return -1;
}
if (mmc_init(mmc)) {
puts("MMC init failed\n");
#ifdef CONFIG_SYS_MMC_ENV_PART
if (CONFIG_SYS_MMC_ENV_PART != mmc->part_num) {
if (mmc_switch_part(CONFIG_SYS_MMC_ENV_DEV,
CONFIG_SYS_MMC_ENV_PART)) {
puts("MMC partition switch failed\n");
return -1;
}
}
#endif
static void fini_mmc_for_env(struct mmc *mmc)
{
#ifdef CONFIG_SYS_MMC_ENV_PART
if (CONFIG_SYS_MMC_ENV_PART != mmc->part_num)
mmc_switch_part(CONFIG_SYS_MMC_ENV_DEV,
mmc->part_num);
#endif
}
static inline int write_env(struct mmc *mmc, unsigned long size,
unsigned long offset, const void *buffer)
blk_start = ALIGN(offset, mmc->write_bl_len) / mmc->write_bl_len;
blk_cnt = ALIGN(size, mmc->write_bl_len) / mmc->write_bl_len;
n = mmc->block_dev.block_write(CONFIG_SYS_MMC_ENV_DEV, blk_start,
blk_cnt, (u_char *)buffer);
return (n == blk_cnt) ? 0 : -1;
}
#ifdef CONFIG_ENV_OFFSET_REDUND
static unsigned char env_flags;
#endif
ALLOC_CACHE_ALIGN_BUFFER(env_t, env_new, 1);
struct mmc *mmc = find_mmc_device(CONFIG_SYS_MMC_ENV_DEV);
if (init_mmc_for_env(mmc))
return 1;
res = (char *)&env_new->data;
len = hexport_r(&env_htab, '\0', 0, &res, ENV_SIZE, 0, NULL);
if (len < 0) {
error("Cannot export environment: errno = %d\n", errno);
ret = 1;
goto fini;
env_new->crc = crc32(0, &env_new->data[0], ENV_SIZE);
#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)) {
ret = 1;
goto fini;
#ifdef CONFIG_ENV_OFFSET_REDUND
gd->env_valid = gd->env_valid == 2 ? 1 : 2;
#endif
fini:
fini_mmc_for_env(mmc);
return ret;
static inline int read_env(struct mmc *mmc, unsigned long size,
unsigned long offset, const void *buffer)
blk_start = ALIGN(offset, mmc->read_bl_len) / mmc->read_bl_len;
blk_cnt = ALIGN(size, mmc->read_bl_len) / mmc->read_bl_len;
n = mmc->block_dev.block_read(CONFIG_SYS_MMC_ENV_DEV, blk_start,
blk_cnt, (uchar *)buffer);
return (n == blk_cnt) ? 0 : -1;
}
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
#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)
ALLOC_CACHE_ALIGN_BUFFER(char, buf, CONFIG_ENV_SIZE);
struct mmc *mmc = find_mmc_device(CONFIG_SYS_MMC_ENV_DEV);
if (init_mmc_for_env(mmc)) {
ret = 1;
goto err;
}
if (mmc_get_env_addr(mmc, 0, &offset)) {
ret = 1;
goto fini;
}
if (read_env(mmc, CONFIG_ENV_SIZE, offset, buf)) {
ret = 1;
goto fini;
}
env_import(buf, 1);
ret = 0;
fini:
fini_mmc_for_env(mmc);
err:
if (ret)
set_default_env(NULL);
#endif /* CONFIG_ENV_OFFSET_REDUND */