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)
57 static int mmc_env_partition_by_name(struct blk_desc *desc, const char *str,
58 struct disk_partition *info)
63 ret = part_get_info(desc, i, info);
67 if (!strncmp((const char *)info->name, str, sizeof(info->name)))
72 static int mmc_env_partition_by_guid(struct blk_desc *desc, struct disk_partition *info)
74 const efi_guid_t env_guid = PARTITION_U_BOOT_ENVIRONMENT;
79 ret = part_get_info(desc, i, info);
83 uuid_str_to_bin(disk_partition_type_guid(info), type_guid.b, UUID_STR_FORMAT_GUID);
84 if (!memcmp(&env_guid, &type_guid, sizeof(efi_guid_t)))
90 static inline int mmc_offset_try_partition(const char *str, int copy, s64 *val)
92 struct disk_partition info;
93 struct blk_desc *desc;
97 snprintf(dev_str, sizeof(dev_str), "%d", mmc_get_env_dev());
98 ret = blk_get_device_by_str("mmc", dev_str, &desc);
103 ret = mmc_env_partition_by_name(desc, str, &info);
104 } else if (IS_ENABLED(CONFIG_PARTITION_TYPE_GUID) && !str) {
105 ret = mmc_env_partition_by_guid(desc, &info);
110 /* round up to info.blksz */
111 len = DIV_ROUND_UP(CONFIG_ENV_SIZE, info.blksz);
113 /* use the top of the partion for the environment */
114 *val = (info.start + info.size - (1 + copy) * len) * info.blksz;
119 static inline s64 mmc_offset(struct mmc *mmc, int copy)
122 const char *offset_redund;
123 const char *partition;
126 .offset_redund = "u-boot,mmc-env-offset-redundant",
127 .partition = "u-boot,mmc-env-partition",
128 .offset = "u-boot,mmc-env-offset",
130 s64 val = 0, defvalue;
131 const char *propname;
136 #if defined(CONFIG_SYS_MMC_ENV_PART)
137 hwpart = mmc_get_env_part(mmc);
140 #if defined(CONFIG_ENV_MMC_PARTITION)
141 str = CONFIG_ENV_MMC_PARTITION;
143 /* look for the partition in mmc CONFIG_SYS_MMC_ENV_DEV */
144 str = ofnode_conf_read_str(dt_prop.partition);
148 /* try to place the environment at end of the partition */
149 err = mmc_offset_try_partition(str, copy, &val);
152 debug("env partition '%s' not found (%d)", str, err);
155 /* try the GPT partition with "U-Boot ENV" TYPE GUID */
156 if (IS_ENABLED(CONFIG_PARTITION_TYPE_GUID) && hwpart == 0) {
157 err = mmc_offset_try_partition(NULL, copy, &val);
162 defvalue = ENV_MMC_OFFSET;
163 propname = dt_prop.offset;
165 if (IS_ENABLED(CONFIG_SYS_REDUNDAND_ENVIRONMENT) && copy) {
166 defvalue = ENV_MMC_OFFSET_REDUND;
167 propname = dt_prop.offset_redund;
170 return ofnode_conf_read_int(propname, defvalue);
173 static inline s64 mmc_offset(struct mmc *mmc, int copy)
175 s64 offset = ENV_MMC_OFFSET;
177 if (IS_ENABLED(CONFIG_SYS_REDUNDAND_ENVIRONMENT) && copy)
178 offset = ENV_MMC_OFFSET_REDUND;
184 __weak int mmc_get_env_addr(struct mmc *mmc, int copy, u32 *env_addr)
186 s64 offset = mmc_offset(mmc, copy);
188 if (offset == ENV_MMC_INVALID_OFFSET) {
189 printf("Invalid ENV offset in MMC, copy=%d\n", copy);
194 offset += mmc->capacity;
201 #ifdef CONFIG_SYS_MMC_ENV_PART
202 __weak uint mmc_get_env_part(struct mmc *mmc)
204 return CONFIG_SYS_MMC_ENV_PART;
207 static unsigned char env_mmc_orig_hwpart;
209 static int mmc_set_env_part(struct mmc *mmc, uint part)
211 int dev = mmc_get_env_dev();
214 ret = blk_select_hwpart_devnum(UCLASS_MMC, dev, part);
216 puts("MMC partition switch failed\n");
221 static bool mmc_set_env_part_init(struct mmc *mmc)
223 env_mmc_orig_hwpart = mmc_get_blk_desc(mmc)->hwpart;
224 if (mmc_set_env_part(mmc, mmc_get_env_part(mmc)))
230 static int mmc_set_env_part_restore(struct mmc *mmc)
232 return mmc_set_env_part(mmc, env_mmc_orig_hwpart);
235 static inline int mmc_set_env_part(struct mmc *mmc, uint part) {return 0; };
236 static bool mmc_set_env_part_init(struct mmc *mmc) {return true; }
237 static inline int mmc_set_env_part_restore(struct mmc *mmc) {return 0; };
240 static const char *init_mmc_for_env(struct mmc *mmc)
243 return "No MMC card found";
245 #if CONFIG_IS_ENABLED(BLK)
248 if (blk_get_from_parent(mmc->dev, &dev))
249 return "No block device";
252 return "MMC init failed";
254 if (!mmc_set_env_part_init(mmc))
255 return "MMC partition switch failed";
260 static void fini_mmc_for_env(struct mmc *mmc)
262 mmc_set_env_part_restore(mmc);
265 #if defined(CONFIG_CMD_SAVEENV) && !defined(CONFIG_SPL_BUILD)
266 static inline int write_env(struct mmc *mmc, unsigned long size,
267 unsigned long offset, const void *buffer)
269 uint blk_start, blk_cnt, n;
270 struct blk_desc *desc = mmc_get_blk_desc(mmc);
272 blk_start = ALIGN(offset, mmc->write_bl_len) / mmc->write_bl_len;
273 blk_cnt = ALIGN(size, mmc->write_bl_len) / mmc->write_bl_len;
275 n = blk_dwrite(desc, blk_start, blk_cnt, (u_char *)buffer);
277 return (n == blk_cnt) ? 0 : -1;
280 static int env_mmc_save(void)
282 ALLOC_CACHE_ALIGN_BUFFER(env_t, env_new, 1);
283 int dev = mmc_get_env_dev();
284 struct mmc *mmc = find_mmc_device(dev);
289 errmsg = init_mmc_for_env(mmc);
291 printf("%s\n", errmsg);
295 ret = env_export(env_new);
299 if (IS_ENABLED(CONFIG_SYS_REDUNDAND_ENVIRONMENT)) {
300 if (gd->env_valid == ENV_VALID)
303 if (IS_ENABLED(ENV_MMC_HWPART_REDUND)) {
304 ret = mmc_set_env_part(mmc, copy + 1);
310 if (mmc_get_env_addr(mmc, copy, &offset)) {
315 printf("Writing to %sMMC(%d)... ", copy ? "redundant " : "", dev);
316 if (write_env(mmc, CONFIG_ENV_SIZE, offset, (u_char *)env_new)) {
324 if (IS_ENABLED(CONFIG_SYS_REDUNDAND_ENVIRONMENT))
325 gd->env_valid = gd->env_valid == ENV_REDUND ? ENV_VALID : ENV_REDUND;
328 fini_mmc_for_env(mmc);
333 static inline int erase_env(struct mmc *mmc, unsigned long size,
334 unsigned long offset)
336 uint blk_start, blk_cnt, n;
337 struct blk_desc *desc = mmc_get_blk_desc(mmc);
340 erase_size = mmc->erase_grp_size * desc->blksz;
341 blk_start = ALIGN_DOWN(offset, erase_size) / desc->blksz;
342 blk_cnt = ALIGN(size, erase_size) / desc->blksz;
344 n = blk_derase(desc, blk_start, blk_cnt);
345 printf("%d blocks erased at 0x%x: %s\n", n, blk_start,
346 (n == blk_cnt) ? "OK" : "ERROR");
348 return (n == blk_cnt) ? 0 : 1;
351 static int env_mmc_erase(void)
353 int dev = mmc_get_env_dev();
354 struct mmc *mmc = find_mmc_device(dev);
359 errmsg = init_mmc_for_env(mmc);
361 printf("%s\n", errmsg);
365 if (mmc_get_env_addr(mmc, copy, &offset)) {
366 ret = CMD_RET_FAILURE;
371 ret = erase_env(mmc, CONFIG_ENV_SIZE, offset);
373 if (IS_ENABLED(CONFIG_SYS_REDUNDAND_ENVIRONMENT)) {
376 if (IS_ENABLED(ENV_MMC_HWPART_REDUND)) {
377 ret = mmc_set_env_part(mmc, copy + 1);
382 if (mmc_get_env_addr(mmc, copy, &offset)) {
383 ret = CMD_RET_FAILURE;
387 ret |= erase_env(mmc, CONFIG_ENV_SIZE, offset);
391 fini_mmc_for_env(mmc);
394 #endif /* CONFIG_CMD_SAVEENV && !CONFIG_SPL_BUILD */
396 static inline int read_env(struct mmc *mmc, unsigned long size,
397 unsigned long offset, const void *buffer)
399 uint blk_start, blk_cnt, n;
400 struct blk_desc *desc = mmc_get_blk_desc(mmc);
402 blk_start = ALIGN(offset, mmc->read_bl_len) / mmc->read_bl_len;
403 blk_cnt = ALIGN(size, mmc->read_bl_len) / mmc->read_bl_len;
405 n = blk_dread(desc, blk_start, blk_cnt, (uchar *)buffer);
407 return (n == blk_cnt) ? 0 : -1;
410 #if defined(ENV_IS_EMBEDDED)
411 static int env_mmc_load(void)
415 #elif defined(CONFIG_SYS_REDUNDAND_ENVIRONMENT)
416 static int env_mmc_load(void)
419 u32 offset1, offset2;
420 int read1_fail = 0, read2_fail = 0;
422 int dev = mmc_get_env_dev();
423 const char *errmsg = NULL;
425 ALLOC_CACHE_ALIGN_BUFFER(env_t, tmp_env1, 1);
426 ALLOC_CACHE_ALIGN_BUFFER(env_t, tmp_env2, 1);
428 mmc_initialize(NULL);
430 mmc = find_mmc_device(dev);
432 errmsg = init_mmc_for_env(mmc);
438 if (mmc_get_env_addr(mmc, 0, &offset1) ||
439 mmc_get_env_addr(mmc, 1, &offset2)) {
444 if (IS_ENABLED(ENV_MMC_HWPART_REDUND)) {
445 ret = mmc_set_env_part(mmc, 1);
450 read1_fail = read_env(mmc, CONFIG_ENV_SIZE, offset1, tmp_env1);
452 if (IS_ENABLED(ENV_MMC_HWPART_REDUND)) {
453 ret = mmc_set_env_part(mmc, 2);
458 read2_fail = read_env(mmc, CONFIG_ENV_SIZE, offset2, tmp_env2);
460 ret = env_import_redund((char *)tmp_env1, read1_fail, (char *)tmp_env2,
461 read2_fail, H_EXTERNAL);
462 printf("Reading from %sMMC(%d)... ", gd->env_valid == ENV_REDUND ? "redundant " : "", dev);
465 fini_mmc_for_env(mmc);
468 env_set_default(errmsg, 0);
472 #else /* ! CONFIG_SYS_REDUNDAND_ENVIRONMENT */
473 static int env_mmc_load(void)
475 ALLOC_CACHE_ALIGN_BUFFER(char, buf, CONFIG_ENV_SIZE);
479 int dev = mmc_get_env_dev();
483 mmc = find_mmc_device(dev);
485 errmsg = init_mmc_for_env(mmc);
491 if (mmc_get_env_addr(mmc, 0, &offset)) {
496 if (read_env(mmc, CONFIG_ENV_SIZE, offset, buf)) {
497 errmsg = "!read failed";
502 printf("Reading from MMC(%d)... ", dev);
504 ret = env_import(buf, 1, H_EXTERNAL);
507 gd->env_addr = (ulong)&ep->data;
511 fini_mmc_for_env(mmc);
514 env_set_default(errmsg, 0);
518 #endif /* CONFIG_SYS_REDUNDAND_ENVIRONMENT */
520 U_BOOT_ENV_LOCATION(mmc) = {
521 .location = ENVL_MMC,
523 .load = env_mmc_load,
524 #if defined(CONFIG_CMD_SAVEENV) && !defined(CONFIG_SPL_BUILD)
525 .save = env_save_ptr(env_mmc_save),
526 .erase = ENV_ERASE_PTR(env_mmc_erase)