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