1 // SPDX-License-Identifier: GPL-2.0+
3 * (C) Copyright 2008-2011 Freescale Semiconductor, Inc.
8 #include <asm/global_data.h>
12 #include <env_internal.h>
14 #include <linux/stddef.h>
21 #include <dm/ofnode.h>
23 #define ENV_MMC_INVALID_OFFSET ((s64)-1)
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
31 /* Default ENV offset when not defined in Device Tree */
32 #define ENV_MMC_OFFSET CONFIG_ENV_OFFSET
34 #if defined(CONFIG_ENV_OFFSET_REDUND)
35 #define ENV_MMC_OFFSET_REDUND CONFIG_ENV_OFFSET_REDUND
37 #define ENV_MMC_OFFSET_REDUND ENV_MMC_INVALID_OFFSET
41 DECLARE_GLOBAL_DATA_PTR;
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.
49 #if (defined(CONFIG_SYS_REDUNDAND_ENVIRONMENT) && \
50 (CONFIG_SYS_MMC_ENV_PART == 1) && \
51 (CONFIG_ENV_OFFSET == CONFIG_ENV_OFFSET_REDUND))
52 #define ENV_MMC_HWPART_REDUND 1
55 #if CONFIG_IS_ENABLED(OF_CONTROL)
56 static inline int mmc_offset_try_partition(const char *str, int copy, s64 *val)
58 struct disk_partition info;
59 struct blk_desc *desc;
63 snprintf(dev_str, sizeof(dev_str), "%d", mmc_get_env_dev());
64 ret = blk_get_device_by_str("mmc", dev_str, &desc);
69 ret = part_get_info(desc, i, &info);
73 if (str && !strncmp((const char *)info.name, str, sizeof(info.name)))
75 #ifdef CONFIG_PARTITION_TYPE_GUID
77 const efi_guid_t env_guid = PARTITION_U_BOOT_ENVIRONMENT;
80 uuid_str_to_bin(info.type_guid, type_guid.b, UUID_STR_FORMAT_GUID);
81 if (!memcmp(&env_guid, &type_guid, sizeof(efi_guid_t)))
87 /* round up to info.blksz */
88 len = DIV_ROUND_UP(CONFIG_ENV_SIZE, info.blksz);
90 /* use the top of the partion for the environment */
91 *val = (info.start + info.size - (1 + copy) * len) * info.blksz;
96 static inline s64 mmc_offset(struct mmc *mmc, int copy)
99 const char *offset_redund;
100 const char *partition;
103 .offset_redund = "u-boot,mmc-env-offset-redundant",
104 .partition = "u-boot,mmc-env-partition",
105 .offset = "u-boot,mmc-env-offset",
107 s64 val = 0, defvalue;
108 const char *propname;
113 #if defined(CONFIG_SYS_MMC_ENV_PART)
114 hwpart = mmc_get_env_part(mmc);
117 #if defined(CONFIG_ENV_MMC_PARTITION)
118 str = CONFIG_ENV_MMC_PARTITION;
120 /* look for the partition in mmc CONFIG_SYS_MMC_ENV_DEV */
121 str = ofnode_conf_read_str(dt_prop.partition);
125 /* try to place the environment at end of the partition */
126 err = mmc_offset_try_partition(str, copy, &val);
129 debug("env partition '%s' not found (%d)", str, err);
132 /* try the GPT partition with "U-Boot ENV" TYPE GUID */
133 if (IS_ENABLED(CONFIG_PARTITION_TYPE_GUID) && hwpart == 0) {
134 err = mmc_offset_try_partition(NULL, copy, &val);
139 defvalue = ENV_MMC_OFFSET;
140 propname = dt_prop.offset;
142 if (IS_ENABLED(CONFIG_SYS_REDUNDAND_ENVIRONMENT) && copy) {
143 defvalue = ENV_MMC_OFFSET_REDUND;
144 propname = dt_prop.offset_redund;
147 return ofnode_conf_read_int(propname, defvalue);
150 static inline s64 mmc_offset(struct mmc *mmc, int copy)
152 s64 offset = ENV_MMC_OFFSET;
154 if (IS_ENABLED(CONFIG_SYS_REDUNDAND_ENVIRONMENT) && copy)
155 offset = ENV_MMC_OFFSET_REDUND;
161 __weak int mmc_get_env_addr(struct mmc *mmc, int copy, u32 *env_addr)
163 s64 offset = mmc_offset(mmc, copy);
165 if (offset == ENV_MMC_INVALID_OFFSET) {
166 printf("Invalid ENV offset in MMC, copy=%d\n", copy);
171 offset += mmc->capacity;
178 #ifdef CONFIG_SYS_MMC_ENV_PART
179 __weak uint mmc_get_env_part(struct mmc *mmc)
181 return CONFIG_SYS_MMC_ENV_PART;
184 static unsigned char env_mmc_orig_hwpart;
186 static int mmc_set_env_part(struct mmc *mmc, uint part)
188 int dev = mmc_get_env_dev();
191 ret = blk_select_hwpart_devnum(UCLASS_MMC, dev, part);
193 puts("MMC partition switch failed\n");
198 static bool mmc_set_env_part_init(struct mmc *mmc)
200 env_mmc_orig_hwpart = mmc_get_blk_desc(mmc)->hwpart;
201 if (mmc_set_env_part(mmc, mmc_get_env_part(mmc)))
207 static int mmc_set_env_part_restore(struct mmc *mmc)
209 return mmc_set_env_part(mmc, env_mmc_orig_hwpart);
212 static inline int mmc_set_env_part(struct mmc *mmc, uint part) {return 0; };
213 static bool mmc_set_env_part_init(struct mmc *mmc) {return true; }
214 static inline int mmc_set_env_part_restore(struct mmc *mmc) {return 0; };
217 static const char *init_mmc_for_env(struct mmc *mmc)
220 return "No MMC card found";
222 #if CONFIG_IS_ENABLED(BLK)
225 if (blk_get_from_parent(mmc->dev, &dev))
226 return "No block device";
229 return "MMC init failed";
231 if (!mmc_set_env_part_init(mmc))
232 return "MMC partition switch failed";
237 static void fini_mmc_for_env(struct mmc *mmc)
239 mmc_set_env_part_restore(mmc);
242 #if defined(CONFIG_CMD_SAVEENV) && !defined(CONFIG_SPL_BUILD)
243 static inline int write_env(struct mmc *mmc, unsigned long size,
244 unsigned long offset, const void *buffer)
246 uint blk_start, blk_cnt, n;
247 struct blk_desc *desc = mmc_get_blk_desc(mmc);
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;
252 n = blk_dwrite(desc, blk_start, blk_cnt, (u_char *)buffer);
254 return (n == blk_cnt) ? 0 : -1;
257 static int env_mmc_save(void)
259 ALLOC_CACHE_ALIGN_BUFFER(env_t, env_new, 1);
260 int dev = mmc_get_env_dev();
261 struct mmc *mmc = find_mmc_device(dev);
266 errmsg = init_mmc_for_env(mmc);
268 printf("%s\n", errmsg);
272 ret = env_export(env_new);
276 if (IS_ENABLED(CONFIG_SYS_REDUNDAND_ENVIRONMENT)) {
277 if (gd->env_valid == ENV_VALID)
280 if (IS_ENABLED(ENV_MMC_HWPART_REDUND)) {
281 ret = mmc_set_env_part(mmc, copy + 1);
287 if (mmc_get_env_addr(mmc, copy, &offset)) {
292 printf("Writing to %sMMC(%d)... ", copy ? "redundant " : "", dev);
293 if (write_env(mmc, CONFIG_ENV_SIZE, offset, (u_char *)env_new)) {
301 if (IS_ENABLED(CONFIG_SYS_REDUNDAND_ENVIRONMENT))
302 gd->env_valid = gd->env_valid == ENV_REDUND ? ENV_VALID : ENV_REDUND;
305 fini_mmc_for_env(mmc);
310 static inline int erase_env(struct mmc *mmc, unsigned long size,
311 unsigned long offset)
313 uint blk_start, blk_cnt, n;
314 struct blk_desc *desc = mmc_get_blk_desc(mmc);
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;
321 n = blk_derase(desc, blk_start, blk_cnt);
322 printf("%d blocks erased at 0x%x: %s\n", n, blk_start,
323 (n == blk_cnt) ? "OK" : "ERROR");
325 return (n == blk_cnt) ? 0 : 1;
328 static int env_mmc_erase(void)
330 int dev = mmc_get_env_dev();
331 struct mmc *mmc = find_mmc_device(dev);
336 errmsg = init_mmc_for_env(mmc);
338 printf("%s\n", errmsg);
342 if (mmc_get_env_addr(mmc, copy, &offset)) {
343 ret = CMD_RET_FAILURE;
348 ret = erase_env(mmc, CONFIG_ENV_SIZE, offset);
350 if (IS_ENABLED(CONFIG_SYS_REDUNDAND_ENVIRONMENT)) {
353 if (IS_ENABLED(ENV_MMC_HWPART_REDUND)) {
354 ret = mmc_set_env_part(mmc, copy + 1);
359 if (mmc_get_env_addr(mmc, copy, &offset)) {
360 ret = CMD_RET_FAILURE;
364 ret |= erase_env(mmc, CONFIG_ENV_SIZE, offset);
368 fini_mmc_for_env(mmc);
371 #endif /* CONFIG_CMD_SAVEENV && !CONFIG_SPL_BUILD */
373 static inline int read_env(struct mmc *mmc, unsigned long size,
374 unsigned long offset, const void *buffer)
376 uint blk_start, blk_cnt, n;
377 struct blk_desc *desc = mmc_get_blk_desc(mmc);
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;
382 n = blk_dread(desc, blk_start, blk_cnt, (uchar *)buffer);
384 return (n == blk_cnt) ? 0 : -1;
387 #if defined(ENV_IS_EMBEDDED)
388 static int env_mmc_load(void)
392 #elif defined(CONFIG_SYS_REDUNDAND_ENVIRONMENT)
393 static int env_mmc_load(void)
396 u32 offset1, offset2;
397 int read1_fail = 0, read2_fail = 0;
399 int dev = mmc_get_env_dev();
400 const char *errmsg = NULL;
402 ALLOC_CACHE_ALIGN_BUFFER(env_t, tmp_env1, 1);
403 ALLOC_CACHE_ALIGN_BUFFER(env_t, tmp_env2, 1);
405 mmc_initialize(NULL);
407 mmc = find_mmc_device(dev);
409 errmsg = init_mmc_for_env(mmc);
415 if (mmc_get_env_addr(mmc, 0, &offset1) ||
416 mmc_get_env_addr(mmc, 1, &offset2)) {
421 if (IS_ENABLED(ENV_MMC_HWPART_REDUND)) {
422 ret = mmc_set_env_part(mmc, 1);
427 read1_fail = read_env(mmc, CONFIG_ENV_SIZE, offset1, tmp_env1);
429 if (IS_ENABLED(ENV_MMC_HWPART_REDUND)) {
430 ret = mmc_set_env_part(mmc, 2);
435 read2_fail = read_env(mmc, CONFIG_ENV_SIZE, offset2, tmp_env2);
437 ret = env_import_redund((char *)tmp_env1, read1_fail, (char *)tmp_env2,
438 read2_fail, H_EXTERNAL);
439 printf("Reading from %sMMC(%d)... ", gd->env_valid == ENV_REDUND ? "redundant " : "", dev);
442 fini_mmc_for_env(mmc);
445 env_set_default(errmsg, 0);
449 #else /* ! CONFIG_SYS_REDUNDAND_ENVIRONMENT */
450 static int env_mmc_load(void)
452 ALLOC_CACHE_ALIGN_BUFFER(char, buf, CONFIG_ENV_SIZE);
456 int dev = mmc_get_env_dev();
460 mmc = find_mmc_device(dev);
462 errmsg = init_mmc_for_env(mmc);
468 if (mmc_get_env_addr(mmc, 0, &offset)) {
473 if (read_env(mmc, CONFIG_ENV_SIZE, offset, buf)) {
474 errmsg = "!read failed";
479 printf("Reading from MMC(%d)... ", dev);
481 ret = env_import(buf, 1, H_EXTERNAL);
484 gd->env_addr = (ulong)&ep->data;
488 fini_mmc_for_env(mmc);
491 env_set_default(errmsg, 0);
495 #endif /* CONFIG_SYS_REDUNDAND_ENVIRONMENT */
497 U_BOOT_ENV_LOCATION(mmc) = {
498 .location = ENVL_MMC,
500 .load = env_mmc_load,
501 #if defined(CONFIG_CMD_SAVEENV) && !defined(CONFIG_SPL_BUILD)
502 .save = env_save_ptr(env_mmc_save),
503 .erase = ENV_ERASE_PTR(env_mmc_erase)