uboot/common/env_mmc.c
<<
>>
Prefs
   1/*
   2 * (C) Copyright 2008-2011 Freescale Semiconductor, Inc.
   3 *
   4 * SPDX-License-Identifier:     GPL-2.0+
   5 */
   6
   7/* #define DEBUG */
   8
   9#include <common.h>
  10
  11#include <command.h>
  12#include <environment.h>
  13#include <linux/stddef.h>
  14#include <malloc.h>
  15#include <memalign.h>
  16#include <mmc.h>
  17#include <search.h>
  18#include <errno.h>
  19
  20#if defined(CONFIG_ENV_SIZE_REDUND) &&  \
  21        (CONFIG_ENV_SIZE_REDUND != CONFIG_ENV_SIZE)
  22#error CONFIG_ENV_SIZE_REDUND should be the same as CONFIG_ENV_SIZE
  23#endif
  24
  25char *env_name_spec = "MMC";
  26
  27#ifdef ENV_IS_EMBEDDED
  28env_t *env_ptr = &environment;
  29#else /* ! ENV_IS_EMBEDDED */
  30env_t *env_ptr;
  31#endif /* ENV_IS_EMBEDDED */
  32
  33DECLARE_GLOBAL_DATA_PTR;
  34
  35#if !defined(CONFIG_ENV_OFFSET)
  36#define CONFIG_ENV_OFFSET 0
  37#endif
  38
  39__weak int mmc_get_env_addr(struct mmc *mmc, int copy, u32 *env_addr)
  40{
  41        s64 offset;
  42
  43        offset = CONFIG_ENV_OFFSET;
  44#ifdef CONFIG_ENV_OFFSET_REDUND
  45        if (copy)
  46                offset = CONFIG_ENV_OFFSET_REDUND;
  47#endif
  48
  49        if (offset < 0)
  50                offset += mmc->capacity;
  51
  52        *env_addr = offset;
  53
  54        return 0;
  55}
  56
  57__weak int mmc_get_env_dev(void)
  58{
  59        return CONFIG_SYS_MMC_ENV_DEV;
  60}
  61
  62int env_init(void)
  63{
  64        /* use default */
  65        gd->env_addr    = (ulong)&default_environment[0];
  66        gd->env_valid   = 1;
  67
  68        return 0;
  69}
  70
  71#ifdef CONFIG_SYS_MMC_ENV_PART
  72__weak uint mmc_get_env_part(struct mmc *mmc)
  73{
  74        return CONFIG_SYS_MMC_ENV_PART;
  75}
  76
  77static unsigned char env_mmc_orig_hwpart;
  78
  79static int mmc_set_env_part(struct mmc *mmc)
  80{
  81        uint part = mmc_get_env_part(mmc);
  82        int dev = mmc_get_env_dev();
  83        int ret = 0;
  84
  85#ifdef CONFIG_SPL_BUILD
  86        dev = 0;
  87#endif
  88
  89        env_mmc_orig_hwpart = mmc_get_blk_desc(mmc)->hwpart;
  90        ret = blk_select_hwpart_devnum(IF_TYPE_MMC, dev, part);
  91        if (ret)
  92                puts("MMC partition switch failed\n");
  93
  94        return ret;
  95}
  96#else
  97static inline int mmc_set_env_part(struct mmc *mmc) {return 0; };
  98#endif
  99
 100static const char *init_mmc_for_env(struct mmc *mmc)
 101{
 102        if (!mmc)
 103                return "!No MMC card found";
 104
 105        if (mmc_init(mmc))
 106                return "!MMC init failed";
 107
 108        if (mmc_set_env_part(mmc))
 109                return "!MMC partition switch failed";
 110
 111        return NULL;
 112}
 113
 114static void fini_mmc_for_env(struct mmc *mmc)
 115{
 116#ifdef CONFIG_SYS_MMC_ENV_PART
 117        int dev = mmc_get_env_dev();
 118
 119#ifdef CONFIG_SPL_BUILD
 120        dev = 0;
 121#endif
 122        blk_select_hwpart_devnum(IF_TYPE_MMC, dev, env_mmc_orig_hwpart);
 123#endif
 124}
 125
 126#ifdef CONFIG_CMD_SAVEENV
 127static inline int write_env(struct mmc *mmc, unsigned long size,
 128                            unsigned long offset, const void *buffer)
 129{
 130        uint blk_start, blk_cnt, n;
 131        struct blk_desc *desc = mmc_get_blk_desc(mmc);
 132
 133        blk_start       = ALIGN(offset, mmc->write_bl_len) / mmc->write_bl_len;
 134        blk_cnt         = ALIGN(size, mmc->write_bl_len) / mmc->write_bl_len;
 135
 136        n = blk_dwrite(desc, blk_start, blk_cnt, (u_char *)buffer);
 137
 138        return (n == blk_cnt) ? 0 : -1;
 139}
 140
 141#ifdef CONFIG_ENV_OFFSET_REDUND
 142static unsigned char env_flags;
 143#endif
 144
 145int saveenv(void)
 146{
 147        ALLOC_CACHE_ALIGN_BUFFER(env_t, env_new, 1);
 148        int dev = mmc_get_env_dev();
 149        struct mmc *mmc = find_mmc_device(dev);
 150        u32     offset;
 151        int     ret, copy = 0;
 152        const char *errmsg;
 153
 154        errmsg = init_mmc_for_env(mmc);
 155        if (errmsg) {
 156                printf("%s\n", errmsg);
 157                return 1;
 158        }
 159
 160        ret = env_export(env_new);
 161        if (ret)
 162                goto fini;
 163
 164#ifdef CONFIG_ENV_OFFSET_REDUND
 165        env_new->flags  = ++env_flags; /* increase the serial */
 166
 167        if (gd->env_valid == 1)
 168                copy = 1;
 169#endif
 170
 171        if (mmc_get_env_addr(mmc, copy, &offset)) {
 172                ret = 1;
 173                goto fini;
 174        }
 175
 176        printf("Writing to %sMMC(%d)... ", copy ? "redundant " : "", dev);
 177        if (write_env(mmc, CONFIG_ENV_SIZE, offset, (u_char *)env_new)) {
 178                puts("failed\n");
 179                ret = 1;
 180                goto fini;
 181        }
 182
 183        puts("done\n");
 184        ret = 0;
 185
 186#ifdef CONFIG_ENV_OFFSET_REDUND
 187        gd->env_valid = gd->env_valid == 2 ? 1 : 2;
 188#endif
 189
 190fini:
 191        fini_mmc_for_env(mmc);
 192        return ret;
 193}
 194#endif /* CONFIG_CMD_SAVEENV */
 195
 196static inline int read_env(struct mmc *mmc, unsigned long size,
 197                           unsigned long offset, const void *buffer)
 198{
 199        uint blk_start, blk_cnt, n;
 200        struct blk_desc *desc = mmc_get_blk_desc(mmc);
 201
 202        blk_start       = ALIGN(offset, mmc->read_bl_len) / mmc->read_bl_len;
 203        blk_cnt         = ALIGN(size, mmc->read_bl_len) / mmc->read_bl_len;
 204
 205        n = blk_dread(desc, blk_start, blk_cnt, (uchar *)buffer);
 206
 207        return (n == blk_cnt) ? 0 : -1;
 208}
 209
 210#ifdef CONFIG_ENV_OFFSET_REDUND
 211void env_relocate_spec(void)
 212{
 213#if !defined(ENV_IS_EMBEDDED)
 214        struct mmc *mmc;
 215        u32 offset1, offset2;
 216        int read1_fail = 0, read2_fail = 0;
 217        int crc1_ok = 0, crc2_ok = 0;
 218        env_t *ep;
 219        int ret;
 220        int dev = mmc_get_env_dev();
 221        const char *errmsg = NULL;
 222
 223        ALLOC_CACHE_ALIGN_BUFFER(env_t, tmp_env1, 1);
 224        ALLOC_CACHE_ALIGN_BUFFER(env_t, tmp_env2, 1);
 225
 226#ifdef CONFIG_SPL_BUILD
 227        dev = 0;
 228#endif
 229
 230        mmc = find_mmc_device(dev);
 231
 232        errmsg = init_mmc_for_env(mmc);
 233        if (errmsg) {
 234                ret = 1;
 235                goto err;
 236        }
 237
 238        if (mmc_get_env_addr(mmc, 0, &offset1) ||
 239            mmc_get_env_addr(mmc, 1, &offset2)) {
 240                ret = 1;
 241                goto fini;
 242        }
 243
 244        read1_fail = read_env(mmc, CONFIG_ENV_SIZE, offset1, tmp_env1);
 245        read2_fail = read_env(mmc, CONFIG_ENV_SIZE, offset2, tmp_env2);
 246
 247        if (read1_fail && read2_fail)
 248                puts("*** Error - No Valid Environment Area found\n");
 249        else if (read1_fail || read2_fail)
 250                puts("*** Warning - some problems detected "
 251                     "reading environment; recovered successfully\n");
 252
 253        crc1_ok = !read1_fail &&
 254                (crc32(0, tmp_env1->data, ENV_SIZE) == tmp_env1->crc);
 255        crc2_ok = !read2_fail &&
 256                (crc32(0, tmp_env2->data, ENV_SIZE) == tmp_env2->crc);
 257
 258        if (!crc1_ok && !crc2_ok) {
 259                errmsg = "!bad CRC";
 260                ret = 1;
 261                goto fini;
 262        } else if (crc1_ok && !crc2_ok) {
 263                gd->env_valid = 1;
 264        } else if (!crc1_ok && crc2_ok) {
 265                gd->env_valid = 2;
 266        } else {
 267                /* both ok - check serial */
 268                if (tmp_env1->flags == 255 && tmp_env2->flags == 0)
 269                        gd->env_valid = 2;
 270                else if (tmp_env2->flags == 255 && tmp_env1->flags == 0)
 271                        gd->env_valid = 1;
 272                else if (tmp_env1->flags > tmp_env2->flags)
 273                        gd->env_valid = 1;
 274                else if (tmp_env2->flags > tmp_env1->flags)
 275                        gd->env_valid = 2;
 276                else /* flags are equal - almost impossible */
 277                        gd->env_valid = 1;
 278        }
 279
 280        free(env_ptr);
 281
 282        if (gd->env_valid == 1)
 283                ep = tmp_env1;
 284        else
 285                ep = tmp_env2;
 286
 287        env_flags = ep->flags;
 288        env_import((char *)ep, 0);
 289        ret = 0;
 290
 291fini:
 292        fini_mmc_for_env(mmc);
 293err:
 294        if (ret)
 295                set_default_env(errmsg);
 296#endif
 297}
 298#else /* ! CONFIG_ENV_OFFSET_REDUND */
 299void env_relocate_spec(void)
 300{
 301#if !defined(ENV_IS_EMBEDDED)
 302        ALLOC_CACHE_ALIGN_BUFFER(char, buf, CONFIG_ENV_SIZE);
 303        struct mmc *mmc;
 304        u32 offset;
 305        int ret;
 306        int dev = mmc_get_env_dev();
 307        const char *errmsg;
 308
 309#ifdef CONFIG_SPL_BUILD
 310        dev = 0;
 311#endif
 312
 313        mmc = find_mmc_device(dev);
 314
 315        errmsg = init_mmc_for_env(mmc);
 316        if (errmsg) {
 317                ret = 1;
 318                goto err;
 319        }
 320
 321        if (mmc_get_env_addr(mmc, 0, &offset)) {
 322                ret = 1;
 323                goto fini;
 324        }
 325
 326        if (read_env(mmc, CONFIG_ENV_SIZE, offset, buf)) {
 327                errmsg = "!read failed";
 328                ret = 1;
 329                goto fini;
 330        }
 331
 332        env_import(buf, 1);
 333        ret = 0;
 334
 335fini:
 336        fini_mmc_for_env(mmc);
 337err:
 338        if (ret)
 339                set_default_env(errmsg);
 340#endif
 341}
 342#endif /* CONFIG_ENV_OFFSET_REDUND */
 343