]> Git Repo - J-u-boot.git/blame - env/mmc.c
board: siemens: iot2050: Migrate to OF_UPSTREAM
[J-u-boot.git] / env / mmc.c
CommitLineData
83d290c5 1// SPDX-License-Identifier: GPL-2.0+
a8060359 2/*
97039ab9 3 * (C) Copyright 2008-2011 Freescale Semiconductor, Inc.
a8060359
TL
4 */
5
6/* #define DEBUG */
7
401d1c4f 8#include <asm/global_data.h>
a8060359
TL
9
10#include <command.h>
0ac7d722 11#include <env.h>
f3998fdc 12#include <env_internal.h>
f8b8a554 13#include <fdtdec.h>
a8060359
TL
14#include <linux/stddef.h>
15#include <malloc.h>
cf92e05c 16#include <memalign.h>
a8060359 17#include <mmc.h>
c9e87ba6 18#include <part.h>
6d1d51b3 19#include <search.h>
e79f4839 20#include <errno.h>
7de8bd03 21#include <dm/ofnode.h>
a8060359 22
f7e07a7e
PD
23#define ENV_MMC_INVALID_OFFSET ((s64)-1)
24
d2103e20
PD
25#if defined(CONFIG_ENV_MMC_USE_DT)
26/* ENV offset is invalid when not defined in Device Tree */
27#define ENV_MMC_OFFSET ENV_MMC_INVALID_OFFSET
28#define ENV_MMC_OFFSET_REDUND ENV_MMC_INVALID_OFFSET
29
30#else
f7e07a7e
PD
31/* Default ENV offset when not defined in Device Tree */
32#define ENV_MMC_OFFSET CONFIG_ENV_OFFSET
33
34#if defined(CONFIG_ENV_OFFSET_REDUND)
35#define ENV_MMC_OFFSET_REDUND CONFIG_ENV_OFFSET_REDUND
36#else
37#define ENV_MMC_OFFSET_REDUND ENV_MMC_INVALID_OFFSET
38#endif
d2103e20 39#endif
f7e07a7e 40
a8060359
TL
41DECLARE_GLOBAL_DATA_PTR;
42
d11d1bec
MV
43/*
44 * In case the environment is redundant, stored in eMMC hardware boot
45 * partition and the environment and redundant environment offsets are
46 * identical, store the environment and redundant environment in both
47 * eMMC boot partitions, one copy in each.
48 * */
49#if (defined(CONFIG_SYS_REDUNDAND_ENVIRONMENT) && \
50 (CONFIG_SYS_MMC_ENV_PART == 1) && \
51 (CONFIG_ENV_OFFSET == CONFIG_ENV_OFFSET_REDUND))
46c9016b 52#define ENV_MMC_HWPART_REDUND 1
d11d1bec
MV
53#endif
54
f8b8a554 55#if CONFIG_IS_ENABLED(OF_CONTROL)
d7c59bfc
RV
56
57static int mmc_env_partition_by_name(struct blk_desc *desc, const char *str,
58 struct disk_partition *info)
59{
60 int i, ret;
61
62 for (i = 1;; i++) {
63 ret = part_get_info(desc, i, info);
64 if (ret < 0)
65 return ret;
66
67 if (!strncmp((const char *)info->name, str, sizeof(info->name)))
68 return 0;
69 }
70}
71
c1131aca
RV
72/*
73 * Look for one or two partitions with the U-Boot environment GUID.
74 *
75 * If *copy is 0, return the first such partition.
76 *
77 * If *copy is 1 on entry and two partitions are found, return the
78 * second partition and set *copy = 0.
79 *
80 * If *copy is 1 on entry and only one partition is found, return that
81 * partition, leaving *copy unmodified.
82 */
83static int mmc_env_partition_by_guid(struct blk_desc *desc, struct disk_partition *info,
84 int *copy)
d7c59bfc
RV
85{
86 const efi_guid_t env_guid = PARTITION_U_BOOT_ENVIRONMENT;
87 efi_guid_t type_guid;
c1131aca
RV
88 int i, ret, found = 0;
89 struct disk_partition dp;
d7c59bfc
RV
90
91 for (i = 1;; i++) {
c1131aca 92 ret = part_get_info(desc, i, &dp);
d7c59bfc 93 if (ret < 0)
c1131aca
RV
94 break;
95
96 uuid_str_to_bin(disk_partition_type_guid(&dp), type_guid.b, UUID_STR_FORMAT_GUID);
97 if (!memcmp(&env_guid, &type_guid, sizeof(efi_guid_t))) {
98 memcpy(info, &dp, sizeof(dp));
99 /* If *copy is 0, we are only looking for the first partition. */
100 if (*copy == 0)
101 return 0;
102 /* This was the second such partition. */
103 if (found) {
104 *copy = 0;
105 return 0;
106 }
107 found = 1;
108 }
d7c59bfc 109 }
c1131aca
RV
110
111 /* The loop ended after finding at most one matching partition. */
112 if (found)
113 ret = 0;
114 return ret;
d7c59bfc
RV
115}
116
117
5d4f7b4e 118static inline int mmc_offset_try_partition(const char *str, int copy, s64 *val)
c9e87ba6 119{
0528979f 120 struct disk_partition info;
c9e87ba6 121 struct blk_desc *desc;
9402e3bb
RV
122 lbaint_t len;
123 int ret;
2b2f7275 124 char dev_str[4];
c9e87ba6 125
2b2f7275
PD
126 snprintf(dev_str, sizeof(dev_str), "%d", mmc_get_env_dev());
127 ret = blk_get_device_by_str("mmc", dev_str, &desc);
c9e87ba6
JRO
128 if (ret < 0)
129 return (ret);
130
d7c59bfc
RV
131 if (str) {
132 ret = mmc_env_partition_by_name(desc, str, &info);
133 } else if (IS_ENABLED(CONFIG_PARTITION_TYPE_GUID) && !str) {
c1131aca 134 ret = mmc_env_partition_by_guid(desc, &info, &copy);
c9e87ba6 135 }
d7c59bfc
RV
136 if (ret < 0)
137 return ret;
c9e87ba6
JRO
138
139 /* round up to info.blksz */
76b640c3 140 len = DIV_ROUND_UP(CONFIG_ENV_SIZE, info.blksz);
c9e87ba6 141
9402e3bb
RV
142 if ((1 + copy) * len > info.size) {
143 printf("Partition '%s' [0x"LBAF"; 0x"LBAF"] too small for %senvironment, required size 0x"LBAF" blocks\n",
144 (const char*)info.name, info.start, info.size,
145 copy ? "two copies of " : "", (1 + copy)*len);
146 return -ENOSPC;
147 }
148
c9e87ba6 149 /* use the top of the partion for the environment */
5d4f7b4e 150 *val = (info.start + info.size - (1 + copy) * len) * info.blksz;
c9e87ba6
JRO
151
152 return 0;
153}
154
5b4acb0f 155static inline s64 mmc_offset(struct mmc *mmc, int copy)
f8b8a554 156{
c9e87ba6
JRO
157 const struct {
158 const char *offset_redund;
159 const char *partition;
160 const char *offset;
161 } dt_prop = {
162 .offset_redund = "u-boot,mmc-env-offset-redundant",
163 .partition = "u-boot,mmc-env-partition",
164 .offset = "u-boot,mmc-env-offset",
165 };
fd374665 166 s64 val = 0, defvalue;
c9e87ba6
JRO
167 const char *propname;
168 const char *str;
5b4acb0f 169 int hwpart = 0;
c9e87ba6
JRO
170 int err;
171
b2345dbd
MK
172#if defined(CONFIG_SYS_MMC_ENV_PART)
173 hwpart = mmc_get_env_part(mmc);
174#endif
5b4acb0f 175
9e70676c
EDF
176#if defined(CONFIG_ENV_MMC_PARTITION)
177 str = CONFIG_ENV_MMC_PARTITION;
178#else
c9e87ba6 179 /* look for the partition in mmc CONFIG_SYS_MMC_ENV_DEV */
7de8bd03 180 str = ofnode_conf_read_str(dt_prop.partition);
9e70676c
EDF
181#endif
182
c9e87ba6
JRO
183 if (str) {
184 /* try to place the environment at end of the partition */
5d4f7b4e 185 err = mmc_offset_try_partition(str, copy, &val);
c9e87ba6
JRO
186 if (!err)
187 return val;
52e9aa3c 188 debug("env partition '%s' not found (%d)", str, err);
c9e87ba6
JRO
189 }
190
80105d8f 191 /* try the GPT partition with "U-Boot ENV" TYPE GUID */
5b4acb0f 192 if (IS_ENABLED(CONFIG_PARTITION_TYPE_GUID) && hwpart == 0) {
80105d8f
PD
193 err = mmc_offset_try_partition(NULL, copy, &val);
194 if (!err)
195 return val;
196 }
197
f7e07a7e 198 defvalue = ENV_MMC_OFFSET;
c9e87ba6 199 propname = dt_prop.offset;
f8b8a554 200
46c9016b 201 if (IS_ENABLED(CONFIG_SYS_REDUNDAND_ENVIRONMENT) && copy) {
f7e07a7e 202 defvalue = ENV_MMC_OFFSET_REDUND;
c9e87ba6 203 propname = dt_prop.offset_redund;
f8b8a554 204 }
46c9016b 205
7de8bd03 206 return ofnode_conf_read_int(propname, defvalue);
f8b8a554
PT
207}
208#else
5b4acb0f 209static inline s64 mmc_offset(struct mmc *mmc, int copy)
97039ab9 210{
f7e07a7e 211 s64 offset = ENV_MMC_OFFSET;
5c088ee8 212
46c9016b 213 if (IS_ENABLED(CONFIG_SYS_REDUNDAND_ENVIRONMENT) && copy)
f7e07a7e 214 offset = ENV_MMC_OFFSET_REDUND;
46c9016b 215
f8b8a554
PT
216 return offset;
217}
218#endif
219
220__weak int mmc_get_env_addr(struct mmc *mmc, int copy, u32 *env_addr)
221{
5b4acb0f 222 s64 offset = mmc_offset(mmc, copy);
5c088ee8 223
f7e07a7e
PD
224 if (offset == ENV_MMC_INVALID_OFFSET) {
225 printf("Invalid ENV offset in MMC, copy=%d\n", copy);
226 return -ENOENT;
227 }
228
5c088ee8
SW
229 if (offset < 0)
230 offset += mmc->capacity;
231
232 *env_addr = offset;
233
97039ab9
MH
234 return 0;
235}
97039ab9 236
b9c8ccab 237#ifdef CONFIG_SYS_MMC_ENV_PART
6e7b7df4
DL
238__weak uint mmc_get_env_part(struct mmc *mmc)
239{
240 return CONFIG_SYS_MMC_ENV_PART;
241}
242
873cc1d7
SW
243static unsigned char env_mmc_orig_hwpart;
244
d11d1bec 245static int mmc_set_env_part(struct mmc *mmc, uint part)
6e7b7df4 246{
e92029c0 247 int dev = mmc_get_env_dev();
6e7b7df4 248 int ret = 0;
b9c8ccab 249
e33a5c6b 250 ret = blk_select_hwpart_devnum(UCLASS_MMC, dev, part);
873cc1d7
SW
251 if (ret)
252 puts("MMC partition switch failed\n");
6e7b7df4
DL
253
254 return ret;
255}
46c9016b
PD
256
257static bool mmc_set_env_part_init(struct mmc *mmc)
258{
259 env_mmc_orig_hwpart = mmc_get_blk_desc(mmc)->hwpart;
260 if (mmc_set_env_part(mmc, mmc_get_env_part(mmc)))
261 return false;
262
263 return true;
264}
265
266static int mmc_set_env_part_restore(struct mmc *mmc)
267{
268 return mmc_set_env_part(mmc, env_mmc_orig_hwpart);
269}
6e7b7df4 270#else
d11d1bec 271static inline int mmc_set_env_part(struct mmc *mmc, uint part) {return 0; };
46c9016b
PD
272static bool mmc_set_env_part_init(struct mmc *mmc) {return true; }
273static inline int mmc_set_env_part_restore(struct mmc *mmc) {return 0; };
b9c8ccab
TR
274#endif
275
c75648d7 276static const char *init_mmc_for_env(struct mmc *mmc)
6e7b7df4 277{
c75648d7 278 if (!mmc)
c5d548a9 279 return "No MMC card found";
a8060359 280
d48b8d11 281#if CONFIG_IS_ENABLED(BLK)
01b73fe6
SG
282 struct udevice *dev;
283
284 if (blk_get_from_parent(mmc->dev, &dev))
c5d548a9 285 return "No block device";
01b73fe6 286#else
c75648d7 287 if (mmc_init(mmc))
c5d548a9 288 return "MMC init failed";
e7017a3c 289#endif
46c9016b 290 if (!mmc_set_env_part_init(mmc))
c5d548a9 291 return "MMC partition switch failed";
a8060359 292
c75648d7 293 return NULL;
a8060359
TL
294}
295
9404a5fc
SW
296static void fini_mmc_for_env(struct mmc *mmc)
297{
46c9016b 298 mmc_set_env_part_restore(mmc);
9404a5fc
SW
299}
300
1d6132e2 301#if defined(CONFIG_CMD_SAVEENV) && !defined(CONFIG_XPL_BUILD)
e8db8f71
IG
302static inline int write_env(struct mmc *mmc, unsigned long size,
303 unsigned long offset, const void *buffer)
a8060359
TL
304{
305 uint blk_start, blk_cnt, n;
5461acba 306 struct blk_desc *desc = mmc_get_blk_desc(mmc);
a8060359 307
e8db8f71
IG
308 blk_start = ALIGN(offset, mmc->write_bl_len) / mmc->write_bl_len;
309 blk_cnt = ALIGN(size, mmc->write_bl_len) / mmc->write_bl_len;
a8060359 310
5461acba 311 n = blk_dwrite(desc, blk_start, blk_cnt, (u_char *)buffer);
a8060359
TL
312
313 return (n == blk_cnt) ? 0 : -1;
314}
315
e5bce247 316static int env_mmc_save(void)
a8060359 317{
cd0f4fa1 318 ALLOC_CACHE_ALIGN_BUFFER(env_t, env_new, 1);
e92029c0
CG
319 int dev = mmc_get_env_dev();
320 struct mmc *mmc = find_mmc_device(dev);
e8db8f71 321 u32 offset;
d196bd88 322 int ret, copy = 0;
c75648d7 323 const char *errmsg;
a8060359 324
c75648d7
TH
325 errmsg = init_mmc_for_env(mmc);
326 if (errmsg) {
327 printf("%s\n", errmsg);
97039ab9 328 return 1;
c75648d7 329 }
97039ab9 330
7ce1526e
MV
331 ret = env_export(env_new);
332 if (ret)
9404a5fc 333 goto fini;
d196bd88 334
46c9016b
PD
335 if (IS_ENABLED(CONFIG_SYS_REDUNDAND_ENVIRONMENT)) {
336 if (gd->env_valid == ENV_VALID)
337 copy = 1;
d11d1bec 338
46c9016b
PD
339 if (IS_ENABLED(ENV_MMC_HWPART_REDUND)) {
340 ret = mmc_set_env_part(mmc, copy + 1);
341 if (ret)
342 goto fini;
343 }
ccd0542a 344 }
d196bd88 345
ccd0542a
YL
346 if (mmc_get_env_addr(mmc, copy, &offset)) {
347 ret = 1;
348 goto fini;
d196bd88
MH
349 }
350
e92029c0 351 printf("Writing to %sMMC(%d)... ", copy ? "redundant " : "", dev);
4036b630 352 if (write_env(mmc, CONFIG_ENV_SIZE, offset, (u_char *)env_new)) {
a8060359 353 puts("failed\n");
9404a5fc
SW
354 ret = 1;
355 goto fini;
a8060359
TL
356 }
357
9404a5fc
SW
358 ret = 0;
359
46c9016b
PD
360 if (IS_ENABLED(CONFIG_SYS_REDUNDAND_ENVIRONMENT))
361 gd->env_valid = gd->env_valid == ENV_REDUND ? ENV_VALID : ENV_REDUND;
d196bd88 362
9404a5fc
SW
363fini:
364 fini_mmc_for_env(mmc);
46c9016b 365
9404a5fc 366 return ret;
a8060359 367}
34853925 368
34853925
FW
369static inline int erase_env(struct mmc *mmc, unsigned long size,
370 unsigned long offset)
371{
372 uint blk_start, blk_cnt, n;
373 struct blk_desc *desc = mmc_get_blk_desc(mmc);
d7226704 374 u32 erase_size;
34853925 375
d7226704
PD
376 erase_size = mmc->erase_grp_size * desc->blksz;
377 blk_start = ALIGN_DOWN(offset, erase_size) / desc->blksz;
378 blk_cnt = ALIGN(size, erase_size) / desc->blksz;
34853925
FW
379
380 n = blk_derase(desc, blk_start, blk_cnt);
d7226704
PD
381 printf("%d blocks erased at 0x%x: %s\n", n, blk_start,
382 (n == blk_cnt) ? "OK" : "ERROR");
34853925
FW
383
384 return (n == blk_cnt) ? 0 : 1;
385}
386
387static int env_mmc_erase(void)
388{
389 int dev = mmc_get_env_dev();
390 struct mmc *mmc = find_mmc_device(dev);
391 int ret, copy = 0;
392 u32 offset;
393 const char *errmsg;
394
395 errmsg = init_mmc_for_env(mmc);
396 if (errmsg) {
397 printf("%s\n", errmsg);
398 return 1;
399 }
400
f47f87f2
MV
401 if (mmc_get_env_addr(mmc, copy, &offset)) {
402 ret = CMD_RET_FAILURE;
403 goto fini;
404 }
34853925 405
d7226704 406 printf("\n");
34853925
FW
407 ret = erase_env(mmc, CONFIG_ENV_SIZE, offset);
408
46c9016b
PD
409 if (IS_ENABLED(CONFIG_SYS_REDUNDAND_ENVIRONMENT)) {
410 copy = 1;
34853925 411
46c9016b
PD
412 if (IS_ENABLED(ENV_MMC_HWPART_REDUND)) {
413 ret = mmc_set_env_part(mmc, copy + 1);
414 if (ret)
415 goto fini;
416 }
d11d1bec 417
46c9016b
PD
418 if (mmc_get_env_addr(mmc, copy, &offset)) {
419 ret = CMD_RET_FAILURE;
420 goto fini;
421 }
34853925 422
46c9016b
PD
423 ret |= erase_env(mmc, CONFIG_ENV_SIZE, offset);
424 }
34853925 425
f47f87f2
MV
426fini:
427 fini_mmc_for_env(mmc);
34853925
FW
428 return ret;
429}
1d6132e2 430#endif /* CONFIG_CMD_SAVEENV && !CONFIG_XPL_BUILD */
a8060359 431
e8db8f71
IG
432static inline int read_env(struct mmc *mmc, unsigned long size,
433 unsigned long offset, const void *buffer)
a8060359
TL
434{
435 uint blk_start, blk_cnt, n;
5461acba 436 struct blk_desc *desc = mmc_get_blk_desc(mmc);
a8060359 437
e8db8f71
IG
438 blk_start = ALIGN(offset, mmc->read_bl_len) / mmc->read_bl_len;
439 blk_cnt = ALIGN(size, mmc->read_bl_len) / mmc->read_bl_len;
a8060359 440
5461acba 441 n = blk_dread(desc, blk_start, blk_cnt, (uchar *)buffer);
a8060359
TL
442
443 return (n == blk_cnt) ? 0 : -1;
444}
445
8566050e
PD
446#if defined(ENV_IS_EMBEDDED)
447static int env_mmc_load(void)
448{
449 return 0;
450}
451#elif defined(CONFIG_SYS_REDUNDAND_ENVIRONMENT)
c5951991 452static int env_mmc_load(void)
d196bd88 453{
b9c8ccab 454 struct mmc *mmc;
d196bd88
MH
455 u32 offset1, offset2;
456 int read1_fail = 0, read2_fail = 0;
d196bd88 457 int ret;
e92029c0 458 int dev = mmc_get_env_dev();
c75648d7 459 const char *errmsg = NULL;
d196bd88 460
452a2722
MN
461 ALLOC_CACHE_ALIGN_BUFFER(env_t, tmp_env1, 1);
462 ALLOC_CACHE_ALIGN_BUFFER(env_t, tmp_env2, 1);
463
26862b4a
FA
464 mmc_initialize(NULL);
465
b9c8ccab
TR
466 mmc = find_mmc_device(dev);
467
c75648d7
TH
468 errmsg = init_mmc_for_env(mmc);
469 if (errmsg) {
c5951991 470 ret = -EIO;
d196bd88
MH
471 goto err;
472 }
473
474 if (mmc_get_env_addr(mmc, 0, &offset1) ||
475 mmc_get_env_addr(mmc, 1, &offset2)) {
c5951991 476 ret = -EIO;
d196bd88
MH
477 goto fini;
478 }
479
46c9016b
PD
480 if (IS_ENABLED(ENV_MMC_HWPART_REDUND)) {
481 ret = mmc_set_env_part(mmc, 1);
482 if (ret)
483 goto fini;
484 }
d11d1bec 485
d196bd88 486 read1_fail = read_env(mmc, CONFIG_ENV_SIZE, offset1, tmp_env1);
d11d1bec 487
46c9016b
PD
488 if (IS_ENABLED(ENV_MMC_HWPART_REDUND)) {
489 ret = mmc_set_env_part(mmc, 2);
490 if (ret)
491 goto fini;
492 }
d11d1bec 493
d196bd88
MH
494 read2_fail = read_env(mmc, CONFIG_ENV_SIZE, offset2, tmp_env2);
495
31f044bd 496 ret = env_import_redund((char *)tmp_env1, read1_fail, (char *)tmp_env2,
890feeca 497 read2_fail, H_EXTERNAL);
97b34f6a 498 printf("Reading from %sMMC(%d)... ", gd->env_valid == ENV_REDUND ? "redundant " : "", dev);
d196bd88
MH
499
500fini:
501 fini_mmc_for_env(mmc);
502err:
503 if (ret)
0ac7d722 504 env_set_default(errmsg, 0);
c5951991 505
c5951991 506 return ret;
d196bd88 507}
46c9016b 508#else /* ! CONFIG_SYS_REDUNDAND_ENVIRONMENT */
c5951991 509static int env_mmc_load(void)
a8060359 510{
cd0f4fa1 511 ALLOC_CACHE_ALIGN_BUFFER(char, buf, CONFIG_ENV_SIZE);
b9c8ccab 512 struct mmc *mmc;
97039ab9 513 u32 offset;
9404a5fc 514 int ret;
e92029c0 515 int dev = mmc_get_env_dev();
c75648d7 516 const char *errmsg;
0536b440 517 env_t *ep = NULL;
b9c8ccab 518
b9c8ccab 519 mmc = find_mmc_device(dev);
a8060359 520
c75648d7
TH
521 errmsg = init_mmc_for_env(mmc);
522 if (errmsg) {
c5951991 523 ret = -EIO;
9404a5fc
SW
524 goto err;
525 }
a8060359 526
d196bd88 527 if (mmc_get_env_addr(mmc, 0, &offset)) {
c5951991 528 ret = -EIO;
9404a5fc
SW
529 goto fini;
530 }
531
cd0f4fa1 532 if (read_env(mmc, CONFIG_ENV_SIZE, offset, buf)) {
c75648d7 533 errmsg = "!read failed";
c5951991 534 ret = -EIO;
9404a5fc
SW
535 goto fini;
536 }
a8060359 537
97b34f6a
QS
538 printf("Reading from MMC(%d)... ", dev);
539
890feeca 540 ret = env_import(buf, 1, H_EXTERNAL);
0536b440
PG
541 if (!ret) {
542 ep = (env_t *)buf;
543 gd->env_addr = (ulong)&ep->data;
544 }
9404a5fc
SW
545
546fini:
547 fini_mmc_for_env(mmc);
548err:
549 if (ret)
0ac7d722 550 env_set_default(errmsg, 0);
8566050e 551
c5951991 552 return ret;
a8060359 553}
46c9016b 554#endif /* CONFIG_SYS_REDUNDAND_ENVIRONMENT */
4415f1d1
SG
555
556U_BOOT_ENV_LOCATION(mmc) = {
557 .location = ENVL_MMC,
ac358beb 558 ENV_NAME("MMC")
e5bce247 559 .load = env_mmc_load,
1d6132e2 560#if defined(CONFIG_CMD_SAVEENV) && !defined(CONFIG_XPL_BUILD)
e5bce247 561 .save = env_save_ptr(env_mmc_save),
1af031ac 562 .erase = ENV_ERASE_PTR(env_mmc_erase)
4415f1d1 563#endif
4415f1d1 564};
This page took 0.604621 seconds and 4 git commands to generate.