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