u-boot/common/env_mmc.c
Jean-Jacques Hiblot 687d207374 env_mmc: Allow SPL to use any MMC device to load/save the environment
SPL has been restricted to use only dev 0 based on the assumption that only
one MMC device is registered. This is not always the case and many
platforms now register several devices as expected by the spl mmc boot code
For those platform SPL_ENV_SUPPORT is broken if dev is forced to 0.

A word of warning: this commit may break SPL_ENV_SUPPORT on platforms that
do not register the same MMC controllers in SPL and in u-boot (mostly iMX6
based platforms). Fortunately none of those activate SPL_ENV_SUPPORT in
their default configuration.

Signed-off-by: Jean-Jacques Hiblot <jjhiblot@ti.com>
2017-04-08 09:26:44 -04:00

327 lines
6.5 KiB
C

/*
* (C) Copyright 2008-2011 Freescale Semiconductor, Inc.
*
* SPDX-License-Identifier: GPL-2.0+
*/
/* #define DEBUG */
#include <common.h>
#include <command.h>
#include <environment.h>
#include <linux/stddef.h>
#include <malloc.h>
#include <memalign.h>
#include <mmc.h>
#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
env_t *env_ptr = &environment;
#else /* ! ENV_IS_EMBEDDED */
env_t *env_ptr;
#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)
{
s64 offset;
offset = CONFIG_ENV_OFFSET;
#ifdef CONFIG_ENV_OFFSET_REDUND
if (copy)
offset = CONFIG_ENV_OFFSET_REDUND;
#endif
if (offset < 0)
offset += mmc->capacity;
*env_addr = offset;
return 0;
}
__weak int mmc_get_env_dev(void)
{
return CONFIG_SYS_MMC_ENV_DEV;
}
int env_init(void)
{
/* use default */
gd->env_addr = (ulong)&default_environment[0];
gd->env_valid = 1;
return 0;
}
#ifdef CONFIG_SYS_MMC_ENV_PART
__weak uint mmc_get_env_part(struct mmc *mmc)
{
return CONFIG_SYS_MMC_ENV_PART;
}
static unsigned char env_mmc_orig_hwpart;
static int mmc_set_env_part(struct mmc *mmc)
{
uint part = mmc_get_env_part(mmc);
int dev = mmc_get_env_dev();
int ret = 0;
env_mmc_orig_hwpart = mmc_get_blk_desc(mmc)->hwpart;
ret = blk_select_hwpart_devnum(IF_TYPE_MMC, dev, part);
if (ret)
puts("MMC partition switch failed\n");
return ret;
}
#else
static inline int mmc_set_env_part(struct mmc *mmc) {return 0; };
#endif
static const char *init_mmc_for_env(struct mmc *mmc)
{
if (!mmc)
return "!No MMC card found";
if (mmc_init(mmc))
return "!MMC init failed";
if (mmc_set_env_part(mmc))
return "!MMC partition switch failed";
return NULL;
}
static void fini_mmc_for_env(struct mmc *mmc)
{
#ifdef CONFIG_SYS_MMC_ENV_PART
int dev = mmc_get_env_dev();
blk_select_hwpart_devnum(IF_TYPE_MMC, dev, env_mmc_orig_hwpart);
#endif
}
#ifdef CONFIG_CMD_SAVEENV
static inline int write_env(struct mmc *mmc, unsigned long size,
unsigned long offset, const void *buffer)
{
uint blk_start, blk_cnt, n;
struct blk_desc *desc = mmc_get_blk_desc(mmc);
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 = blk_dwrite(desc, blk_start, blk_cnt, (u_char *)buffer);
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);
int dev = mmc_get_env_dev();
struct mmc *mmc = find_mmc_device(dev);
u32 offset;
int ret, copy = 0;
const char *errmsg;
errmsg = init_mmc_for_env(mmc);
if (errmsg) {
printf("%s\n", errmsg);
return 1;
}
ret = env_export(env_new);
if (ret)
goto fini;
#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 " : "", dev);
if (write_env(mmc, CONFIG_ENV_SIZE, offset, (u_char *)env_new)) {
puts("failed\n");
ret = 1;
goto fini;
}
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;
}
#endif /* CONFIG_CMD_SAVEENV */
static inline int read_env(struct mmc *mmc, unsigned long size,
unsigned long offset, const void *buffer)
{
uint blk_start, blk_cnt, n;
struct blk_desc *desc = mmc_get_blk_desc(mmc);
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 = blk_dread(desc, blk_start, blk_cnt, (uchar *)buffer);
return (n == blk_cnt) ? 0 : -1;
}
#ifdef CONFIG_ENV_OFFSET_REDUND
void env_relocate_spec(void)
{
#if !defined(ENV_IS_EMBEDDED)
struct mmc *mmc;
u32 offset1, offset2;
int read1_fail = 0, read2_fail = 0;
int crc1_ok = 0, crc2_ok = 0;
env_t *ep;
int ret;
int dev = mmc_get_env_dev();
const char *errmsg = NULL;
ALLOC_CACHE_ALIGN_BUFFER(env_t, tmp_env1, 1);
ALLOC_CACHE_ALIGN_BUFFER(env_t, tmp_env2, 1);
mmc = find_mmc_device(dev);
errmsg = init_mmc_for_env(mmc);
if (errmsg) {
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) {
errmsg = "!bad CRC";
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(errmsg);
#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;
u32 offset;
int ret;
int dev = mmc_get_env_dev();
const char *errmsg;
mmc = find_mmc_device(dev);
errmsg = init_mmc_for_env(mmc);
if (errmsg) {
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)) {
errmsg = "!read failed";
ret = 1;
goto fini;
}
env_import(buf, 1);
ret = 0;
fini:
fini_mmc_for_env(mmc);
err:
if (ret)
set_default_env(errmsg);
#endif
}
#endif /* CONFIG_ENV_OFFSET_REDUND */