1 // SPDX-License-Identifier: GPL-2.0+
3 * (C) Copyright 2008-2011 Freescale Semiconductor, Inc.
9 #include <asm/global_data.h>
13 #include <env_internal.h>
15 #include <linux/stddef.h>
22 #include <dm/ofnode.h>
24 #define ENV_MMC_INVALID_OFFSET ((s64)-1)
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
32 /* Default ENV offset when not defined in Device Tree */
33 #define ENV_MMC_OFFSET CONFIG_ENV_OFFSET
35 #if defined(CONFIG_ENV_OFFSET_REDUND)
36 #define ENV_MMC_OFFSET_REDUND CONFIG_ENV_OFFSET_REDUND
38 #define ENV_MMC_OFFSET_REDUND ENV_MMC_INVALID_OFFSET
42 DECLARE_GLOBAL_DATA_PTR;
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.
50 #if (defined(CONFIG_SYS_REDUNDAND_ENVIRONMENT) && \
51 (CONFIG_SYS_MMC_ENV_PART == 1) && \
52 (CONFIG_ENV_OFFSET == CONFIG_ENV_OFFSET_REDUND))
53 #define ENV_MMC_HWPART_REDUND 1
56 #if CONFIG_IS_ENABLED(OF_CONTROL)
57 static inline int mmc_offset_try_partition(const char *str, int copy, s64 *val)
59 struct disk_partition info;
60 struct blk_desc *desc;
64 snprintf(dev_str, sizeof(dev_str), "%d", mmc_get_env_dev());
65 ret = blk_get_device_by_str("mmc", dev_str, &desc);
70 ret = part_get_info(desc, i, &info);
74 if (str && !strncmp((const char *)info.name, str, sizeof(info.name)))
76 #ifdef CONFIG_PARTITION_TYPE_GUID
78 const efi_guid_t env_guid = PARTITION_U_BOOT_ENVIRONMENT;
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)))
88 /* round up to info.blksz */
89 len = DIV_ROUND_UP(CONFIG_ENV_SIZE, info.blksz);
91 /* use the top of the partion for the environment */
92 *val = (info.start + info.size - (1 + copy) * len) * info.blksz;
97 static inline s64 mmc_offset(int copy)
100 const char *offset_redund;
101 const char *partition;
104 .offset_redund = "u-boot,mmc-env-offset-redundant",
105 .partition = "u-boot,mmc-env-partition",
106 .offset = "u-boot,mmc-env-offset",
108 s64 val = 0, defvalue;
109 const char *propname;
113 /* look for the partition in mmc CONFIG_SYS_MMC_ENV_DEV */
114 str = ofnode_conf_read_str(dt_prop.partition);
116 /* try to place the environment at end of the partition */
117 err = mmc_offset_try_partition(str, copy, &val);
120 debug("env partition '%s' not found (%d)", str, err);
123 /* try the GPT partition with "U-Boot ENV" TYPE GUID */
124 if (IS_ENABLED(CONFIG_PARTITION_TYPE_GUID)) {
125 err = mmc_offset_try_partition(NULL, copy, &val);
130 defvalue = ENV_MMC_OFFSET;
131 propname = dt_prop.offset;
133 if (IS_ENABLED(CONFIG_SYS_REDUNDAND_ENVIRONMENT) && copy) {
134 defvalue = ENV_MMC_OFFSET_REDUND;
135 propname = dt_prop.offset_redund;
138 return ofnode_conf_read_int(propname, defvalue);
141 static inline s64 mmc_offset(int copy)
143 s64 offset = ENV_MMC_OFFSET;
145 if (IS_ENABLED(CONFIG_SYS_REDUNDAND_ENVIRONMENT) && copy)
146 offset = ENV_MMC_OFFSET_REDUND;
152 __weak int mmc_get_env_addr(struct mmc *mmc, int copy, u32 *env_addr)
154 s64 offset = mmc_offset(copy);
156 if (offset == ENV_MMC_INVALID_OFFSET) {
157 printf("Invalid ENV offset in MMC, copy=%d\n", copy);
162 offset += mmc->capacity;
169 #ifdef CONFIG_SYS_MMC_ENV_PART
170 __weak uint mmc_get_env_part(struct mmc *mmc)
172 return CONFIG_SYS_MMC_ENV_PART;
175 static unsigned char env_mmc_orig_hwpart;
177 static int mmc_set_env_part(struct mmc *mmc, uint part)
179 int dev = mmc_get_env_dev();
182 ret = blk_select_hwpart_devnum(UCLASS_MMC, dev, part);
184 puts("MMC partition switch failed\n");
189 static bool mmc_set_env_part_init(struct mmc *mmc)
191 env_mmc_orig_hwpart = mmc_get_blk_desc(mmc)->hwpart;
192 if (mmc_set_env_part(mmc, mmc_get_env_part(mmc)))
198 static int mmc_set_env_part_restore(struct mmc *mmc)
200 return mmc_set_env_part(mmc, env_mmc_orig_hwpart);
203 static inline int mmc_set_env_part(struct mmc *mmc, uint part) {return 0; };
204 static bool mmc_set_env_part_init(struct mmc *mmc) {return true; }
205 static inline int mmc_set_env_part_restore(struct mmc *mmc) {return 0; };
208 static const char *init_mmc_for_env(struct mmc *mmc)
211 return "No MMC card found";
213 #if CONFIG_IS_ENABLED(BLK)
216 if (blk_get_from_parent(mmc->dev, &dev))
217 return "No block device";
220 return "MMC init failed";
222 if (!mmc_set_env_part_init(mmc))
223 return "MMC partition switch failed";
228 static void fini_mmc_for_env(struct mmc *mmc)
230 mmc_set_env_part_restore(mmc);
233 #if defined(CONFIG_CMD_SAVEENV) && !defined(CONFIG_SPL_BUILD)
234 static inline int write_env(struct mmc *mmc, unsigned long size,
235 unsigned long offset, const void *buffer)
237 uint blk_start, blk_cnt, n;
238 struct blk_desc *desc = mmc_get_blk_desc(mmc);
240 blk_start = ALIGN(offset, mmc->write_bl_len) / mmc->write_bl_len;
241 blk_cnt = ALIGN(size, mmc->write_bl_len) / mmc->write_bl_len;
243 n = blk_dwrite(desc, blk_start, blk_cnt, (u_char *)buffer);
245 return (n == blk_cnt) ? 0 : -1;
248 static int env_mmc_save(void)
250 ALLOC_CACHE_ALIGN_BUFFER(env_t, env_new, 1);
251 int dev = mmc_get_env_dev();
252 struct mmc *mmc = find_mmc_device(dev);
257 errmsg = init_mmc_for_env(mmc);
259 printf("%s\n", errmsg);
263 ret = env_export(env_new);
267 if (IS_ENABLED(CONFIG_SYS_REDUNDAND_ENVIRONMENT)) {
268 if (gd->env_valid == ENV_VALID)
271 if (IS_ENABLED(ENV_MMC_HWPART_REDUND)) {
272 ret = mmc_set_env_part(mmc, copy + 1);
277 if (mmc_get_env_addr(mmc, copy, &offset)) {
283 printf("Writing to %sMMC(%d)... ", copy ? "redundant " : "", dev);
284 if (write_env(mmc, CONFIG_ENV_SIZE, offset, (u_char *)env_new)) {
292 if (IS_ENABLED(CONFIG_SYS_REDUNDAND_ENVIRONMENT))
293 gd->env_valid = gd->env_valid == ENV_REDUND ? ENV_VALID : ENV_REDUND;
296 fini_mmc_for_env(mmc);
301 static inline int erase_env(struct mmc *mmc, unsigned long size,
302 unsigned long offset)
304 uint blk_start, blk_cnt, n;
305 struct blk_desc *desc = mmc_get_blk_desc(mmc);
308 erase_size = mmc->erase_grp_size * desc->blksz;
309 blk_start = ALIGN_DOWN(offset, erase_size) / desc->blksz;
310 blk_cnt = ALIGN(size, erase_size) / desc->blksz;
312 n = blk_derase(desc, blk_start, blk_cnt);
313 printf("%d blocks erased at 0x%x: %s\n", n, blk_start,
314 (n == blk_cnt) ? "OK" : "ERROR");
316 return (n == blk_cnt) ? 0 : 1;
319 static int env_mmc_erase(void)
321 int dev = mmc_get_env_dev();
322 struct mmc *mmc = find_mmc_device(dev);
327 errmsg = init_mmc_for_env(mmc);
329 printf("%s\n", errmsg);
333 if (mmc_get_env_addr(mmc, copy, &offset)) {
334 ret = CMD_RET_FAILURE;
339 ret = erase_env(mmc, CONFIG_ENV_SIZE, offset);
341 if (IS_ENABLED(CONFIG_SYS_REDUNDAND_ENVIRONMENT)) {
344 if (IS_ENABLED(ENV_MMC_HWPART_REDUND)) {
345 ret = mmc_set_env_part(mmc, copy + 1);
350 if (mmc_get_env_addr(mmc, copy, &offset)) {
351 ret = CMD_RET_FAILURE;
355 ret |= erase_env(mmc, CONFIG_ENV_SIZE, offset);
359 fini_mmc_for_env(mmc);
362 #endif /* CONFIG_CMD_SAVEENV && !CONFIG_SPL_BUILD */
364 static inline int read_env(struct mmc *mmc, unsigned long size,
365 unsigned long offset, const void *buffer)
367 uint blk_start, blk_cnt, n;
368 struct blk_desc *desc = mmc_get_blk_desc(mmc);
370 blk_start = ALIGN(offset, mmc->read_bl_len) / mmc->read_bl_len;
371 blk_cnt = ALIGN(size, mmc->read_bl_len) / mmc->read_bl_len;
373 n = blk_dread(desc, blk_start, blk_cnt, (uchar *)buffer);
375 return (n == blk_cnt) ? 0 : -1;
378 #if defined(ENV_IS_EMBEDDED)
379 static int env_mmc_load(void)
383 #elif defined(CONFIG_SYS_REDUNDAND_ENVIRONMENT)
384 static int env_mmc_load(void)
387 u32 offset1, offset2;
388 int read1_fail = 0, read2_fail = 0;
390 int dev = mmc_get_env_dev();
391 const char *errmsg = NULL;
393 ALLOC_CACHE_ALIGN_BUFFER(env_t, tmp_env1, 1);
394 ALLOC_CACHE_ALIGN_BUFFER(env_t, tmp_env2, 1);
396 mmc_initialize(NULL);
398 mmc = find_mmc_device(dev);
400 errmsg = init_mmc_for_env(mmc);
406 if (mmc_get_env_addr(mmc, 0, &offset1) ||
407 mmc_get_env_addr(mmc, 1, &offset2)) {
412 if (IS_ENABLED(ENV_MMC_HWPART_REDUND)) {
413 ret = mmc_set_env_part(mmc, 1);
418 read1_fail = read_env(mmc, CONFIG_ENV_SIZE, offset1, tmp_env1);
420 if (IS_ENABLED(ENV_MMC_HWPART_REDUND)) {
421 ret = mmc_set_env_part(mmc, 2);
426 read2_fail = read_env(mmc, CONFIG_ENV_SIZE, offset2, tmp_env2);
428 ret = env_import_redund((char *)tmp_env1, read1_fail, (char *)tmp_env2,
429 read2_fail, H_EXTERNAL);
432 fini_mmc_for_env(mmc);
435 env_set_default(errmsg, 0);
439 #else /* ! CONFIG_SYS_REDUNDAND_ENVIRONMENT */
440 static int env_mmc_load(void)
442 ALLOC_CACHE_ALIGN_BUFFER(char, buf, CONFIG_ENV_SIZE);
446 int dev = mmc_get_env_dev();
450 mmc = find_mmc_device(dev);
452 errmsg = init_mmc_for_env(mmc);
458 if (mmc_get_env_addr(mmc, 0, &offset)) {
463 if (read_env(mmc, CONFIG_ENV_SIZE, offset, buf)) {
464 errmsg = "!read failed";
469 ret = env_import(buf, 1, H_EXTERNAL);
472 gd->env_addr = (ulong)&ep->data;
476 fini_mmc_for_env(mmc);
479 env_set_default(errmsg, 0);
483 #endif /* CONFIG_SYS_REDUNDAND_ENVIRONMENT */
485 U_BOOT_ENV_LOCATION(mmc) = {
486 .location = ENVL_MMC,
488 .load = env_mmc_load,
489 #ifndef CONFIG_SPL_BUILD
490 .save = env_save_ptr(env_mmc_save),
491 .erase = ENV_ERASE_PTR(env_mmc_erase)