uboot/env/mmc.c
<<
>>
Prefs
   1// SPDX-License-Identifier: GPL-2.0+
   2/*
   3 * (C) Copyright 2008-2011 Freescale Semiconductor, Inc.
   4 */
   5
   6/* #define DEBUG */
   7
   8#include <common.h>
   9
  10#include <command.h>
  11#include <env.h>
  12#include <env_internal.h>
  13#include <fdtdec.h>
  14#include <linux/stddef.h>
  15#include <malloc.h>
  16#include <memalign.h>
  17#include <mmc.h>
  18#include <part.h>
  19#include <search.h>
  20#include <errno.h>
  21
  22#define __STR(X) #X
  23#define STR(X) __STR(X)
  24
  25#if defined(CONFIG_ENV_SIZE_REDUND) &&  \
  26        (CONFIG_ENV_SIZE_REDUND != CONFIG_ENV_SIZE)
  27#error CONFIG_ENV_SIZE_REDUND should be the same as CONFIG_ENV_SIZE
  28#endif
  29
  30DECLARE_GLOBAL_DATA_PTR;
  31
  32#if !defined(CONFIG_ENV_OFFSET)
  33#define CONFIG_ENV_OFFSET 0
  34#endif
  35
  36#if CONFIG_IS_ENABLED(OF_CONTROL)
  37static inline int mmc_offset_try_partition(const char *str, s64 *val)
  38{
  39        disk_partition_t info;
  40        struct blk_desc *desc;
  41        int len, i, ret;
  42
  43        ret = blk_get_device_by_str("mmc", STR(CONFIG_SYS_MMC_ENV_DEV), &desc);
  44        if (ret < 0)
  45                return (ret);
  46
  47        for (i = 1;;i++) {
  48                ret = part_get_info(desc, i, &info);
  49                if (ret < 0)
  50                        return ret;
  51
  52                if (!strncmp((const char *)info.name, str, sizeof(str)))
  53                        break;
  54        }
  55
  56        /* round up to info.blksz */
  57        len = (CONFIG_ENV_SIZE + info.blksz - 1) & ~(info.blksz - 1);
  58
  59        /* use the top of the partion for the environment */
  60        *val = (info.start + info.size - 1) - len / info.blksz;
  61
  62        return 0;
  63}
  64
  65static inline s64 mmc_offset(int copy)
  66{
  67        const struct {
  68                const char *offset_redund;
  69                const char *partition;
  70                const char *offset;
  71        } dt_prop = {
  72                .offset_redund = "u-boot,mmc-env-offset-redundant",
  73                .partition = "u-boot,mmc-env-partition",
  74                .offset = "u-boot,mmc-env-offset",
  75        };
  76        s64 val = 0, defvalue;
  77        const char *propname;
  78        const char *str;
  79        int err;
  80
  81        /* look for the partition in mmc CONFIG_SYS_MMC_ENV_DEV */
  82        str = fdtdec_get_config_string(gd->fdt_blob, dt_prop.partition);
  83        if (str) {
  84                /* try to place the environment at end of the partition */
  85                err = mmc_offset_try_partition(str, &val);
  86                if (!err)
  87                        return val;
  88        }
  89
  90        defvalue = CONFIG_ENV_OFFSET;
  91        propname = dt_prop.offset;
  92
  93#if defined(CONFIG_ENV_OFFSET_REDUND)
  94        if (copy) {
  95                defvalue = CONFIG_ENV_OFFSET_REDUND;
  96                propname = dt_prop.offset_redund;
  97        }
  98#endif
  99        return fdtdec_get_config_int(gd->fdt_blob, propname, defvalue);
 100}
 101#else
 102static inline s64 mmc_offset(int copy)
 103{
 104        s64 offset = CONFIG_ENV_OFFSET;
 105
 106#if defined(CONFIG_ENV_OFFSET_REDUND)
 107        if (copy)
 108                offset = CONFIG_ENV_OFFSET_REDUND;
 109#endif
 110        return offset;
 111}
 112#endif
 113
 114__weak int mmc_get_env_addr(struct mmc *mmc, int copy, u32 *env_addr)
 115{
 116        s64 offset = mmc_offset(copy);
 117
 118        if (offset < 0)
 119                offset += mmc->capacity;
 120
 121        *env_addr = offset;
 122
 123        return 0;
 124}
 125
 126__weak int mmc_get_env_dev(void)
 127{
 128        return CONFIG_SYS_MMC_ENV_DEV;
 129}
 130
 131#ifdef CONFIG_SYS_MMC_ENV_PART
 132__weak uint mmc_get_env_part(struct mmc *mmc)
 133{
 134        return CONFIG_SYS_MMC_ENV_PART;
 135}
 136
 137static unsigned char env_mmc_orig_hwpart;
 138
 139static int mmc_set_env_part(struct mmc *mmc)
 140{
 141        uint part = mmc_get_env_part(mmc);
 142        int dev = mmc_get_env_dev();
 143        int ret = 0;
 144
 145        env_mmc_orig_hwpart = mmc_get_blk_desc(mmc)->hwpart;
 146        ret = blk_select_hwpart_devnum(IF_TYPE_MMC, dev, part);
 147        if (ret)
 148                puts("MMC partition switch failed\n");
 149
 150        return ret;
 151}
 152#else
 153static inline int mmc_set_env_part(struct mmc *mmc) {return 0; };
 154#endif
 155
 156static const char *init_mmc_for_env(struct mmc *mmc)
 157{
 158        if (!mmc)
 159                return "No MMC card found";
 160
 161#if CONFIG_IS_ENABLED(BLK)
 162        struct udevice *dev;
 163
 164        if (blk_get_from_parent(mmc->dev, &dev))
 165                return "No block device";
 166#else
 167        if (mmc_init(mmc))
 168                return "MMC init failed";
 169#endif
 170        if (mmc_set_env_part(mmc))
 171                return "MMC partition switch failed";
 172
 173        return NULL;
 174}
 175
 176static void fini_mmc_for_env(struct mmc *mmc)
 177{
 178#ifdef CONFIG_SYS_MMC_ENV_PART
 179        int dev = mmc_get_env_dev();
 180
 181        blk_select_hwpart_devnum(IF_TYPE_MMC, dev, env_mmc_orig_hwpart);
 182#endif
 183}
 184
 185#if defined(CONFIG_CMD_SAVEENV) && !defined(CONFIG_SPL_BUILD)
 186static inline int write_env(struct mmc *mmc, unsigned long size,
 187                            unsigned long offset, const void *buffer)
 188{
 189        uint blk_start, blk_cnt, n;
 190        struct blk_desc *desc = mmc_get_blk_desc(mmc);
 191
 192        blk_start       = ALIGN(offset, mmc->write_bl_len) / mmc->write_bl_len;
 193        blk_cnt         = ALIGN(size, mmc->write_bl_len) / mmc->write_bl_len;
 194
 195        n = blk_dwrite(desc, blk_start, blk_cnt, (u_char *)buffer);
 196
 197        return (n == blk_cnt) ? 0 : -1;
 198}
 199
 200static int env_mmc_save(void)
 201{
 202        ALLOC_CACHE_ALIGN_BUFFER(env_t, env_new, 1);
 203        int dev = mmc_get_env_dev();
 204        struct mmc *mmc = find_mmc_device(dev);
 205        u32     offset;
 206        int     ret, copy = 0;
 207        const char *errmsg;
 208
 209        errmsg = init_mmc_for_env(mmc);
 210        if (errmsg) {
 211                printf("%s\n", errmsg);
 212                return 1;
 213        }
 214
 215        ret = env_export(env_new);
 216        if (ret)
 217                goto fini;
 218
 219#ifdef CONFIG_ENV_OFFSET_REDUND
 220        if (gd->env_valid == ENV_VALID)
 221                copy = 1;
 222#endif
 223
 224        if (mmc_get_env_addr(mmc, copy, &offset)) {
 225                ret = 1;
 226                goto fini;
 227        }
 228
 229        printf("Writing to %sMMC(%d)... ", copy ? "redundant " : "", dev);
 230        if (write_env(mmc, CONFIG_ENV_SIZE, offset, (u_char *)env_new)) {
 231                puts("failed\n");
 232                ret = 1;
 233                goto fini;
 234        }
 235
 236        ret = 0;
 237
 238#ifdef CONFIG_ENV_OFFSET_REDUND
 239        gd->env_valid = gd->env_valid == ENV_REDUND ? ENV_VALID : ENV_REDUND;
 240#endif
 241
 242fini:
 243        fini_mmc_for_env(mmc);
 244        return ret;
 245}
 246
 247#if defined(CONFIG_CMD_ERASEENV)
 248static inline int erase_env(struct mmc *mmc, unsigned long size,
 249                            unsigned long offset)
 250{
 251        uint blk_start, blk_cnt, n;
 252        struct blk_desc *desc = mmc_get_blk_desc(mmc);
 253
 254        blk_start       = ALIGN(offset, mmc->write_bl_len) / mmc->write_bl_len;
 255        blk_cnt         = ALIGN(size, mmc->write_bl_len) / mmc->write_bl_len;
 256
 257        n = blk_derase(desc, blk_start, blk_cnt);
 258        printf("%d blocks erased: %s\n", n, (n == blk_cnt) ? "OK" : "ERROR");
 259
 260        return (n == blk_cnt) ? 0 : 1;
 261}
 262
 263static int env_mmc_erase(void)
 264{
 265        int dev = mmc_get_env_dev();
 266        struct mmc *mmc = find_mmc_device(dev);
 267        int     ret, copy = 0;
 268        u32     offset;
 269        const char *errmsg;
 270
 271        errmsg = init_mmc_for_env(mmc);
 272        if (errmsg) {
 273                printf("%s\n", errmsg);
 274                return 1;
 275        }
 276
 277        if (mmc_get_env_addr(mmc, copy, &offset))
 278                return CMD_RET_FAILURE;
 279
 280        ret = erase_env(mmc, CONFIG_ENV_SIZE, offset);
 281
 282#ifdef CONFIG_ENV_OFFSET_REDUND
 283        copy = 1;
 284
 285        if (mmc_get_env_addr(mmc, copy, &offset))
 286                return CMD_RET_FAILURE;
 287
 288        ret |= erase_env(mmc, CONFIG_ENV_SIZE, offset);
 289#endif
 290
 291        return ret;
 292}
 293#endif /* CONFIG_CMD_ERASEENV */
 294#endif /* CONFIG_CMD_SAVEENV && !CONFIG_SPL_BUILD */
 295
 296static inline int read_env(struct mmc *mmc, unsigned long size,
 297                           unsigned long offset, const void *buffer)
 298{
 299        uint blk_start, blk_cnt, n;
 300        struct blk_desc *desc = mmc_get_blk_desc(mmc);
 301
 302        blk_start       = ALIGN(offset, mmc->read_bl_len) / mmc->read_bl_len;
 303        blk_cnt         = ALIGN(size, mmc->read_bl_len) / mmc->read_bl_len;
 304
 305        n = blk_dread(desc, blk_start, blk_cnt, (uchar *)buffer);
 306
 307        return (n == blk_cnt) ? 0 : -1;
 308}
 309
 310#ifdef CONFIG_ENV_OFFSET_REDUND
 311static int env_mmc_load(void)
 312{
 313#if !defined(ENV_IS_EMBEDDED)
 314        struct mmc *mmc;
 315        u32 offset1, offset2;
 316        int read1_fail = 0, read2_fail = 0;
 317        int ret;
 318        int dev = mmc_get_env_dev();
 319        const char *errmsg = NULL;
 320
 321        ALLOC_CACHE_ALIGN_BUFFER(env_t, tmp_env1, 1);
 322        ALLOC_CACHE_ALIGN_BUFFER(env_t, tmp_env2, 1);
 323
 324        mmc_initialize(NULL);
 325
 326        mmc = find_mmc_device(dev);
 327
 328        errmsg = init_mmc_for_env(mmc);
 329        if (errmsg) {
 330                ret = -EIO;
 331                goto err;
 332        }
 333
 334        if (mmc_get_env_addr(mmc, 0, &offset1) ||
 335            mmc_get_env_addr(mmc, 1, &offset2)) {
 336                ret = -EIO;
 337                goto fini;
 338        }
 339
 340        read1_fail = read_env(mmc, CONFIG_ENV_SIZE, offset1, tmp_env1);
 341        read2_fail = read_env(mmc, CONFIG_ENV_SIZE, offset2, tmp_env2);
 342
 343        ret = env_import_redund((char *)tmp_env1, read1_fail, (char *)tmp_env2,
 344                                read2_fail);
 345
 346fini:
 347        fini_mmc_for_env(mmc);
 348err:
 349        if (ret)
 350                env_set_default(errmsg, 0);
 351
 352#endif
 353        return ret;
 354}
 355#else /* ! CONFIG_ENV_OFFSET_REDUND */
 356static int env_mmc_load(void)
 357{
 358#if !defined(ENV_IS_EMBEDDED)
 359        ALLOC_CACHE_ALIGN_BUFFER(char, buf, CONFIG_ENV_SIZE);
 360        struct mmc *mmc;
 361        u32 offset;
 362        int ret;
 363        int dev = mmc_get_env_dev();
 364        const char *errmsg;
 365
 366        mmc = find_mmc_device(dev);
 367
 368        errmsg = init_mmc_for_env(mmc);
 369        if (errmsg) {
 370                ret = -EIO;
 371                goto err;
 372        }
 373
 374        if (mmc_get_env_addr(mmc, 0, &offset)) {
 375                ret = -EIO;
 376                goto fini;
 377        }
 378
 379        if (read_env(mmc, CONFIG_ENV_SIZE, offset, buf)) {
 380                errmsg = "!read failed";
 381                ret = -EIO;
 382                goto fini;
 383        }
 384
 385        ret = env_import(buf, 1);
 386
 387fini:
 388        fini_mmc_for_env(mmc);
 389err:
 390        if (ret)
 391                env_set_default(errmsg, 0);
 392#endif
 393        return ret;
 394}
 395#endif /* CONFIG_ENV_OFFSET_REDUND */
 396
 397U_BOOT_ENV_LOCATION(mmc) = {
 398        .location       = ENVL_MMC,
 399        ENV_NAME("MMC")
 400        .load           = env_mmc_load,
 401#ifndef CONFIG_SPL_BUILD
 402        .save           = env_save_ptr(env_mmc_save),
 403#if defined(CONFIG_CMD_ERASEENV)
 404        .erase          = env_mmc_erase,
 405#endif
 406#endif
 407};
 408