blob: 46ffa6386d6d2cc5a3853b05a5342defab481c20 [file] [log] [blame]
Tom Rini10e47792018-05-06 17:58:06 -04001// SPDX-License-Identifier: GPL-2.0+
Terry Lvb4eceac2010-05-17 10:57:01 +08002/*
Mingkai Hua55fce32011-01-24 17:09:55 +00003 * (C) Copyright 2008-2011 Freescale Semiconductor, Inc.
Terry Lvb4eceac2010-05-17 10:57:01 +08004 */
5
6/* #define DEBUG */
7
Simon Glass3ba929a2020-10-30 21:38:53 -06008#include <asm/global_data.h>
Terry Lvb4eceac2010-05-17 10:57:01 +08009
10#include <command.h>
Simon Glass97385862019-08-01 09:47:00 -060011#include <env.h>
Simon Glass9d1f6192019-08-02 09:44:25 -060012#include <env_internal.h>
Philipp Tomsich8ab507b2017-05-16 00:16:31 +020013#include <fdtdec.h>
Terry Lvb4eceac2010-05-17 10:57:01 +080014#include <linux/stddef.h>
15#include <malloc.h>
Simon Glass2dd337a2015-09-02 17:24:58 -060016#include <memalign.h>
Terry Lvb4eceac2010-05-17 10:57:01 +080017#include <mmc.h>
Jorge Ramirez-Ortiz03ab46c2017-11-06 14:16:37 +010018#include <part.h>
Lei Wen9f84f212010-11-10 07:39:23 +080019#include <search.h>
Lei Wenff833ed2010-10-13 11:07:21 +080020#include <errno.h>
Simon Glass0034d962021-08-07 07:24:01 -060021#include <dm/ofnode.h>
Terry Lvb4eceac2010-05-17 10:57:01 +080022
Patrick Delaunayf080c4c2022-11-10 11:48:58 +010023#define ENV_MMC_INVALID_OFFSET ((s64)-1)
24
Patrick Delaunay41e143f2022-11-10 11:49:01 +010025#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
Patrick Delaunayf080c4c2022-11-10 11:48:58 +010031/* Default ENV offset when not defined in Device Tree */
Michael Walle21205d52025-06-05 09:46:10 +020032#if !defined(CONFIG_ENV_OFFSET_RELATIVE_END)
Patrick Delaunayf080c4c2022-11-10 11:48:58 +010033#define ENV_MMC_OFFSET CONFIG_ENV_OFFSET
Michael Walle21205d52025-06-05 09:46:10 +020034#else
35#define ENV_MMC_OFFSET (-(CONFIG_ENV_OFFSET))
36#endif
Patrick Delaunayf080c4c2022-11-10 11:48:58 +010037
38#if defined(CONFIG_ENV_OFFSET_REDUND)
Michael Walle21205d52025-06-05 09:46:10 +020039#if !defined(CONFIG_ENV_OFFSET_REDUND_RELATIVE_END)
Patrick Delaunayf080c4c2022-11-10 11:48:58 +010040#define ENV_MMC_OFFSET_REDUND CONFIG_ENV_OFFSET_REDUND
41#else
Michael Walle21205d52025-06-05 09:46:10 +020042#define ENV_MMC_OFFSET_REDUND (-(CONFIG_ENV_OFFSET_REDUND))
43#endif
44#else
Patrick Delaunayf080c4c2022-11-10 11:48:58 +010045#define ENV_MMC_OFFSET_REDUND ENV_MMC_INVALID_OFFSET
46#endif
Patrick Delaunay41e143f2022-11-10 11:49:01 +010047#endif
Patrick Delaunayf080c4c2022-11-10 11:48:58 +010048
Terry Lvb4eceac2010-05-17 10:57:01 +080049DECLARE_GLOBAL_DATA_PTR;
50
Philipp Tomsich8ab507b2017-05-16 00:16:31 +020051#if CONFIG_IS_ENABLED(OF_CONTROL)
Rasmus Villemoesfd2b9d32024-09-12 15:41:39 +020052
53static int mmc_env_partition_by_name(struct blk_desc *desc, const char *str,
54 struct disk_partition *info)
55{
56 int i, ret;
57
58 for (i = 1;; i++) {
59 ret = part_get_info(desc, i, info);
60 if (ret < 0)
61 return ret;
62
63 if (!strncmp((const char *)info->name, str, sizeof(info->name)))
64 return 0;
65 }
66}
67
Rasmus Villemoes98836e42024-09-12 15:41:41 +020068/*
69 * Look for one or two partitions with the U-Boot environment GUID.
70 *
71 * If *copy is 0, return the first such partition.
72 *
73 * If *copy is 1 on entry and two partitions are found, return the
74 * second partition and set *copy = 0.
75 *
76 * If *copy is 1 on entry and only one partition is found, return that
77 * partition, leaving *copy unmodified.
78 */
79static int mmc_env_partition_by_guid(struct blk_desc *desc, struct disk_partition *info,
80 int *copy)
Rasmus Villemoesfd2b9d32024-09-12 15:41:39 +020081{
82 const efi_guid_t env_guid = PARTITION_U_BOOT_ENVIRONMENT;
83 efi_guid_t type_guid;
Rasmus Villemoes98836e42024-09-12 15:41:41 +020084 int i, ret, found = 0;
85 struct disk_partition dp;
Rasmus Villemoesfd2b9d32024-09-12 15:41:39 +020086
87 for (i = 1;; i++) {
Rasmus Villemoes98836e42024-09-12 15:41:41 +020088 ret = part_get_info(desc, i, &dp);
Rasmus Villemoesfd2b9d32024-09-12 15:41:39 +020089 if (ret < 0)
Rasmus Villemoes98836e42024-09-12 15:41:41 +020090 break;
Rasmus Villemoesfd2b9d32024-09-12 15:41:39 +020091
Rasmus Villemoes98836e42024-09-12 15:41:41 +020092 uuid_str_to_bin(disk_partition_type_guid(&dp), type_guid.b, UUID_STR_FORMAT_GUID);
93 if (!memcmp(&env_guid, &type_guid, sizeof(efi_guid_t))) {
94 memcpy(info, &dp, sizeof(dp));
95 /* If *copy is 0, we are only looking for the first partition. */
96 if (*copy == 0)
97 return 0;
98 /* This was the second such partition. */
99 if (found) {
100 *copy = 0;
101 return 0;
102 }
103 found = 1;
104 }
Rasmus Villemoesfd2b9d32024-09-12 15:41:39 +0200105 }
Rasmus Villemoes98836e42024-09-12 15:41:41 +0200106
107 /* The loop ended after finding at most one matching partition. */
108 if (found)
109 ret = 0;
110 return ret;
Rasmus Villemoesfd2b9d32024-09-12 15:41:39 +0200111}
112
113
Patrick Delaunayeff81952020-06-15 10:38:57 +0200114static inline int mmc_offset_try_partition(const char *str, int copy, s64 *val)
Jorge Ramirez-Ortiz03ab46c2017-11-06 14:16:37 +0100115{
Simon Glassc1c4a8f2020-05-10 11:39:57 -0600116 struct disk_partition info;
Jorge Ramirez-Ortiz03ab46c2017-11-06 14:16:37 +0100117 struct blk_desc *desc;
Rasmus Villemoes59624042024-09-12 15:41:40 +0200118 lbaint_t len;
119 int ret;
Patrick Delaunay530cb452020-06-15 10:38:55 +0200120 char dev_str[4];
Jorge Ramirez-Ortiz03ab46c2017-11-06 14:16:37 +0100121
Patrick Delaunay530cb452020-06-15 10:38:55 +0200122 snprintf(dev_str, sizeof(dev_str), "%d", mmc_get_env_dev());
123 ret = blk_get_device_by_str("mmc", dev_str, &desc);
Jorge Ramirez-Ortiz03ab46c2017-11-06 14:16:37 +0100124 if (ret < 0)
125 return (ret);
126
Rasmus Villemoesfd2b9d32024-09-12 15:41:39 +0200127 if (str) {
128 ret = mmc_env_partition_by_name(desc, str, &info);
129 } else if (IS_ENABLED(CONFIG_PARTITION_TYPE_GUID) && !str) {
Rasmus Villemoes98836e42024-09-12 15:41:41 +0200130 ret = mmc_env_partition_by_guid(desc, &info, &copy);
Jorge Ramirez-Ortiz03ab46c2017-11-06 14:16:37 +0100131 }
Rasmus Villemoesfd2b9d32024-09-12 15:41:39 +0200132 if (ret < 0)
133 return ret;
Jorge Ramirez-Ortiz03ab46c2017-11-06 14:16:37 +0100134
135 /* round up to info.blksz */
Patrick Delaunay66fba3a2020-06-15 10:38:56 +0200136 len = DIV_ROUND_UP(CONFIG_ENV_SIZE, info.blksz);
Jorge Ramirez-Ortiz03ab46c2017-11-06 14:16:37 +0100137
Rasmus Villemoes59624042024-09-12 15:41:40 +0200138 if ((1 + copy) * len > info.size) {
139 printf("Partition '%s' [0x"LBAF"; 0x"LBAF"] too small for %senvironment, required size 0x"LBAF" blocks\n",
140 (const char*)info.name, info.start, info.size,
141 copy ? "two copies of " : "", (1 + copy)*len);
142 return -ENOSPC;
143 }
144
Jorge Ramirez-Ortiz03ab46c2017-11-06 14:16:37 +0100145 /* use the top of the partion for the environment */
Patrick Delaunayeff81952020-06-15 10:38:57 +0200146 *val = (info.start + info.size - (1 + copy) * len) * info.blksz;
Jorge Ramirez-Ortiz03ab46c2017-11-06 14:16:37 +0100147
148 return 0;
149}
150
Marek Vasut1f2473a2023-02-09 13:30:10 +0100151static inline s64 mmc_offset(struct mmc *mmc, int copy)
Philipp Tomsich8ab507b2017-05-16 00:16:31 +0200152{
Jorge Ramirez-Ortiz03ab46c2017-11-06 14:16:37 +0100153 const struct {
154 const char *offset_redund;
155 const char *partition;
156 const char *offset;
157 } dt_prop = {
158 .offset_redund = "u-boot,mmc-env-offset-redundant",
159 .partition = "u-boot,mmc-env-partition",
160 .offset = "u-boot,mmc-env-offset",
161 };
Philipp Tomsich745d8de2017-11-21 23:29:40 +0100162 s64 val = 0, defvalue;
Jorge Ramirez-Ortiz03ab46c2017-11-06 14:16:37 +0100163 const char *propname;
164 const char *str;
Marek Vasut1f2473a2023-02-09 13:30:10 +0100165 int hwpart = 0;
Jorge Ramirez-Ortiz03ab46c2017-11-06 14:16:37 +0100166 int err;
167
Marek Vasut02edd0d2025-06-09 21:26:40 +0200168#if defined(CONFIG_ENV_MMC_EMMC_HW_PARTITION)
Mattijs Korpershoek06f6c9f2024-07-19 17:38:54 +0200169 hwpart = mmc_get_env_part(mmc);
170#endif
Marek Vasut1f2473a2023-02-09 13:30:10 +0100171
Marek Vasut369a4702025-06-09 21:26:41 +0200172#if defined(CONFIG_ENV_MMC_SW_PARTITION)
173 str = CONFIG_ENV_MMC_SW_PARTITION;
Emmanuel Di Fedeb6601dd2023-06-14 12:05:28 +0200174#else
Marek Vasut30cf71d2025-06-09 21:26:39 +0200175 /* look for the partition in mmc CONFIG_ENV_MMC_DEVICE_INDEX */
Simon Glass0034d962021-08-07 07:24:01 -0600176 str = ofnode_conf_read_str(dt_prop.partition);
Emmanuel Di Fedeb6601dd2023-06-14 12:05:28 +0200177#endif
178
Jorge Ramirez-Ortiz03ab46c2017-11-06 14:16:37 +0100179 if (str) {
180 /* try to place the environment at end of the partition */
Patrick Delaunayeff81952020-06-15 10:38:57 +0200181 err = mmc_offset_try_partition(str, copy, &val);
Jorge Ramirez-Ortiz03ab46c2017-11-06 14:16:37 +0100182 if (!err)
183 return val;
Patrick Delaunay01b07982022-11-10 11:49:04 +0100184 debug("env partition '%s' not found (%d)", str, err);
Jorge Ramirez-Ortiz03ab46c2017-11-06 14:16:37 +0100185 }
186
Patrick Delaunayeb787872022-11-10 11:49:03 +0100187 /* try the GPT partition with "U-Boot ENV" TYPE GUID */
Marek Vasut1f2473a2023-02-09 13:30:10 +0100188 if (IS_ENABLED(CONFIG_PARTITION_TYPE_GUID) && hwpart == 0) {
Patrick Delaunayeb787872022-11-10 11:49:03 +0100189 err = mmc_offset_try_partition(NULL, copy, &val);
190 if (!err)
191 return val;
192 }
193
Patrick Delaunayf080c4c2022-11-10 11:48:58 +0100194 defvalue = ENV_MMC_OFFSET;
Jorge Ramirez-Ortiz03ab46c2017-11-06 14:16:37 +0100195 propname = dt_prop.offset;
Philipp Tomsich8ab507b2017-05-16 00:16:31 +0200196
Marek Vasutc3bb7412025-06-09 21:26:37 +0200197 if (IS_ENABLED(CONFIG_ENV_REDUNDANT) && copy) {
Patrick Delaunayf080c4c2022-11-10 11:48:58 +0100198 defvalue = ENV_MMC_OFFSET_REDUND;
Jorge Ramirez-Ortiz03ab46c2017-11-06 14:16:37 +0100199 propname = dt_prop.offset_redund;
Philipp Tomsich8ab507b2017-05-16 00:16:31 +0200200 }
Patrick Delaunay036a3582022-11-10 11:48:59 +0100201
Simon Glass0034d962021-08-07 07:24:01 -0600202 return ofnode_conf_read_int(propname, defvalue);
Philipp Tomsich8ab507b2017-05-16 00:16:31 +0200203}
204#else
Marek Vasut1f2473a2023-02-09 13:30:10 +0100205static inline s64 mmc_offset(struct mmc *mmc, int copy)
Mingkai Hua55fce32011-01-24 17:09:55 +0000206{
Patrick Delaunayf080c4c2022-11-10 11:48:58 +0100207 s64 offset = ENV_MMC_OFFSET;
Stephen Warren24dc2032013-06-11 15:14:02 -0600208
Marek Vasutc3bb7412025-06-09 21:26:37 +0200209 if (IS_ENABLED(CONFIG_ENV_REDUNDANT) && copy)
Patrick Delaunayf080c4c2022-11-10 11:48:58 +0100210 offset = ENV_MMC_OFFSET_REDUND;
Patrick Delaunay036a3582022-11-10 11:48:59 +0100211
Philipp Tomsich8ab507b2017-05-16 00:16:31 +0200212 return offset;
213}
214#endif
215
Marek Vasute4766fa2025-02-21 19:47:24 +0100216static bool mmc_env_is_redundant_in_both_boot_hwparts(struct mmc *mmc)
Marek Vasutf1a41982025-02-21 19:47:23 +0100217{
218 /*
219 * In case the environment is redundant, stored in eMMC hardware boot
220 * partition and the environment and redundant environment offsets are
221 * identical, store the environment and redundant environment in both
222 * eMMC boot partitions, one copy in each.
223 */
Marek Vasutc3bb7412025-06-09 21:26:37 +0200224 if (!IS_ENABLED(CONFIG_ENV_REDUNDANT))
Marek Vasutf1a41982025-02-21 19:47:23 +0100225 return false;
226
Marek Vasut02edd0d2025-06-09 21:26:40 +0200227 if (CONFIG_ENV_MMC_EMMC_HW_PARTITION != 1)
Marek Vasutf1a41982025-02-21 19:47:23 +0100228 return false;
229
230 return mmc_offset(mmc, 0) == mmc_offset(mmc, 1);
231}
232
Philipp Tomsich8ab507b2017-05-16 00:16:31 +0200233__weak int mmc_get_env_addr(struct mmc *mmc, int copy, u32 *env_addr)
234{
Marek Vasut1f2473a2023-02-09 13:30:10 +0100235 s64 offset = mmc_offset(mmc, copy);
Stephen Warren24dc2032013-06-11 15:14:02 -0600236
Patrick Delaunayf080c4c2022-11-10 11:48:58 +0100237 if (offset == ENV_MMC_INVALID_OFFSET) {
238 printf("Invalid ENV offset in MMC, copy=%d\n", copy);
239 return -ENOENT;
240 }
241
Stephen Warren24dc2032013-06-11 15:14:02 -0600242 if (offset < 0)
243 offset += mmc->capacity;
244
245 *env_addr = offset;
246
Mingkai Hua55fce32011-01-24 17:09:55 +0000247 return 0;
248}
Mingkai Hua55fce32011-01-24 17:09:55 +0000249
Marek Vasut02edd0d2025-06-09 21:26:40 +0200250#ifdef CONFIG_ENV_MMC_EMMC_HW_PARTITION
Dmitry Lifshitz97d2fb92014-07-30 13:19:06 +0300251__weak uint mmc_get_env_part(struct mmc *mmc)
252{
Marek Vasut02edd0d2025-06-09 21:26:40 +0200253 return CONFIG_ENV_MMC_EMMC_HW_PARTITION;
Dmitry Lifshitz97d2fb92014-07-30 13:19:06 +0300254}
255
Stephen Warren1e0f92a2015-12-07 11:38:49 -0700256static unsigned char env_mmc_orig_hwpart;
257
Marek Vasut509f36e2021-10-17 19:23:36 +0200258static int mmc_set_env_part(struct mmc *mmc, uint part)
Dmitry Lifshitz97d2fb92014-07-30 13:19:06 +0300259{
Clemens Gruberf7b61442016-01-20 15:43:37 +0100260 int dev = mmc_get_env_dev();
Dmitry Lifshitz97d2fb92014-07-30 13:19:06 +0300261 int ret = 0;
Tom Rinic929f822014-03-28 12:03:34 -0400262
Simon Glassdbfa32c2022-08-11 19:34:59 -0600263 ret = blk_select_hwpart_devnum(UCLASS_MMC, dev, part);
Stephen Warren1e0f92a2015-12-07 11:38:49 -0700264 if (ret)
265 puts("MMC partition switch failed\n");
Dmitry Lifshitz97d2fb92014-07-30 13:19:06 +0300266
267 return ret;
268}
Patrick Delaunay036a3582022-11-10 11:48:59 +0100269
270static bool mmc_set_env_part_init(struct mmc *mmc)
271{
272 env_mmc_orig_hwpart = mmc_get_blk_desc(mmc)->hwpart;
273 if (mmc_set_env_part(mmc, mmc_get_env_part(mmc)))
274 return false;
275
276 return true;
277}
278
279static int mmc_set_env_part_restore(struct mmc *mmc)
280{
281 return mmc_set_env_part(mmc, env_mmc_orig_hwpart);
282}
Dmitry Lifshitz97d2fb92014-07-30 13:19:06 +0300283#else
Marek Vasut509f36e2021-10-17 19:23:36 +0200284static inline int mmc_set_env_part(struct mmc *mmc, uint part) {return 0; };
Patrick Delaunay036a3582022-11-10 11:48:59 +0100285static bool mmc_set_env_part_init(struct mmc *mmc) {return true; }
286static inline int mmc_set_env_part_restore(struct mmc *mmc) {return 0; };
Tom Rinic929f822014-03-28 12:03:34 -0400287#endif
288
Tim Harveyeda75c32015-05-08 14:52:09 -0700289static const char *init_mmc_for_env(struct mmc *mmc)
Dmitry Lifshitz97d2fb92014-07-30 13:19:06 +0300290{
Tim Harveyeda75c32015-05-08 14:52:09 -0700291 if (!mmc)
Yaniv Levinskyb2bb9bc2018-06-24 19:16:57 +0300292 return "No MMC card found";
Terry Lvb4eceac2010-05-17 10:57:01 +0800293
Sjoerd Simons5e4546d2018-03-22 22:53:50 +0100294#if CONFIG_IS_ENABLED(BLK)
Simon Glass1d9907c2017-05-27 11:37:18 -0600295 struct udevice *dev;
296
297 if (blk_get_from_parent(mmc->dev, &dev))
Yaniv Levinskyb2bb9bc2018-06-24 19:16:57 +0300298 return "No block device";
Simon Glass1d9907c2017-05-27 11:37:18 -0600299#else
Tim Harveyeda75c32015-05-08 14:52:09 -0700300 if (mmc_init(mmc))
Yaniv Levinskyb2bb9bc2018-06-24 19:16:57 +0300301 return "MMC init failed";
Simon Glass86d78c72017-04-23 20:02:04 -0600302#endif
Patrick Delaunay036a3582022-11-10 11:48:59 +0100303 if (!mmc_set_env_part_init(mmc))
Yaniv Levinskyb2bb9bc2018-06-24 19:16:57 +0300304 return "MMC partition switch failed";
Tim Harveyeda75c32015-05-08 14:52:09 -0700305
306 return NULL;
Terry Lvb4eceac2010-05-17 10:57:01 +0800307}
308
Stephen Warren27ee0f32012-07-30 10:55:44 +0000309static void fini_mmc_for_env(struct mmc *mmc)
310{
Patrick Delaunay036a3582022-11-10 11:48:59 +0100311 mmc_set_env_part_restore(mmc);
Stephen Warren27ee0f32012-07-30 10:55:44 +0000312}
313
Simon Glass0e84d962024-09-29 19:49:50 -0600314#if defined(CONFIG_CMD_SAVEENV) && !defined(CONFIG_XPL_BUILD)
Igor Grinbergecbfb622011-11-07 01:14:05 +0000315static inline int write_env(struct mmc *mmc, unsigned long size,
316 unsigned long offset, const void *buffer)
Terry Lvb4eceac2010-05-17 10:57:01 +0800317{
318 uint blk_start, blk_cnt, n;
Simon Glassda898922016-05-14 14:03:03 -0600319 struct blk_desc *desc = mmc_get_blk_desc(mmc);
Terry Lvb4eceac2010-05-17 10:57:01 +0800320
Igor Grinbergecbfb622011-11-07 01:14:05 +0000321 blk_start = ALIGN(offset, mmc->write_bl_len) / mmc->write_bl_len;
322 blk_cnt = ALIGN(size, mmc->write_bl_len) / mmc->write_bl_len;
Terry Lvb4eceac2010-05-17 10:57:01 +0800323
Simon Glassda898922016-05-14 14:03:03 -0600324 n = blk_dwrite(desc, blk_start, blk_cnt, (u_char *)buffer);
Terry Lvb4eceac2010-05-17 10:57:01 +0800325
326 return (n == blk_cnt) ? 0 : -1;
327}
328
Simon Glass082af922017-08-03 12:22:01 -0600329static int env_mmc_save(void)
Terry Lvb4eceac2010-05-17 10:57:01 +0800330{
Tom Rinia840b8e2013-04-05 14:55:21 -0400331 ALLOC_CACHE_ALIGN_BUFFER(env_t, env_new, 1);
Clemens Gruberf7b61442016-01-20 15:43:37 +0100332 int dev = mmc_get_env_dev();
333 struct mmc *mmc = find_mmc_device(dev);
Igor Grinbergecbfb622011-11-07 01:14:05 +0000334 u32 offset;
Michael Heimpolda1d25a32013-04-10 10:36:19 +0000335 int ret, copy = 0;
Tim Harveyeda75c32015-05-08 14:52:09 -0700336 const char *errmsg;
Terry Lvb4eceac2010-05-17 10:57:01 +0800337
Tim Harveyeda75c32015-05-08 14:52:09 -0700338 errmsg = init_mmc_for_env(mmc);
339 if (errmsg) {
340 printf("%s\n", errmsg);
Terry Lvb4eceac2010-05-17 10:57:01 +0800341 return 1;
Tim Harveyeda75c32015-05-08 14:52:09 -0700342 }
Terry Lvb4eceac2010-05-17 10:57:01 +0800343
Marek Vasutd73c1292014-03-05 19:59:50 +0100344 ret = env_export(env_new);
345 if (ret)
Stephen Warren27ee0f32012-07-30 10:55:44 +0000346 goto fini;
Michael Heimpolda1d25a32013-04-10 10:36:19 +0000347
Marek Vasutc3bb7412025-06-09 21:26:37 +0200348 if (IS_ENABLED(CONFIG_ENV_REDUNDANT)) {
Patrick Delaunay036a3582022-11-10 11:48:59 +0100349 if (gd->env_valid == ENV_VALID)
350 copy = 1;
Marek Vasut509f36e2021-10-17 19:23:36 +0200351
Marek Vasutf1a41982025-02-21 19:47:23 +0100352 if (mmc_env_is_redundant_in_both_boot_hwparts(mmc)) {
Patrick Delaunay036a3582022-11-10 11:48:59 +0100353 ret = mmc_set_env_part(mmc, copy + 1);
354 if (ret)
355 goto fini;
356 }
Ye Li45d13872023-01-31 14:41:58 +0800357 }
Michael Heimpolda1d25a32013-04-10 10:36:19 +0000358
Ye Li45d13872023-01-31 14:41:58 +0800359 if (mmc_get_env_addr(mmc, copy, &offset)) {
360 ret = 1;
361 goto fini;
Michael Heimpolda1d25a32013-04-10 10:36:19 +0000362 }
363
Clemens Gruberf7b61442016-01-20 15:43:37 +0100364 printf("Writing to %sMMC(%d)... ", copy ? "redundant " : "", dev);
Stephen Warren42a3c442012-05-24 11:38:33 +0000365 if (write_env(mmc, CONFIG_ENV_SIZE, offset, (u_char *)env_new)) {
Terry Lvb4eceac2010-05-17 10:57:01 +0800366 puts("failed\n");
Stephen Warren27ee0f32012-07-30 10:55:44 +0000367 ret = 1;
368 goto fini;
Terry Lvb4eceac2010-05-17 10:57:01 +0800369 }
370
Stephen Warren27ee0f32012-07-30 10:55:44 +0000371 ret = 0;
372
Marek Vasutc3bb7412025-06-09 21:26:37 +0200373 if (IS_ENABLED(CONFIG_ENV_REDUNDANT))
Patrick Delaunay036a3582022-11-10 11:48:59 +0100374 gd->env_valid = gd->env_valid == ENV_REDUND ? ENV_VALID : ENV_REDUND;
Michael Heimpolda1d25a32013-04-10 10:36:19 +0000375
Stephen Warren27ee0f32012-07-30 10:55:44 +0000376fini:
377 fini_mmc_for_env(mmc);
Patrick Delaunay036a3582022-11-10 11:48:59 +0100378
Stephen Warren27ee0f32012-07-30 10:55:44 +0000379 return ret;
Terry Lvb4eceac2010-05-17 10:57:01 +0800380}
Frank Wunderlich81f087e2019-06-29 11:36:20 +0200381
Frank Wunderlich81f087e2019-06-29 11:36:20 +0200382static inline int erase_env(struct mmc *mmc, unsigned long size,
383 unsigned long offset)
384{
385 uint blk_start, blk_cnt, n;
386 struct blk_desc *desc = mmc_get_blk_desc(mmc);
Patrick Delaunayab5ea4d2022-02-15 16:23:23 +0100387 u32 erase_size;
Frank Wunderlich81f087e2019-06-29 11:36:20 +0200388
Patrick Delaunayab5ea4d2022-02-15 16:23:23 +0100389 erase_size = mmc->erase_grp_size * desc->blksz;
390 blk_start = ALIGN_DOWN(offset, erase_size) / desc->blksz;
391 blk_cnt = ALIGN(size, erase_size) / desc->blksz;
Frank Wunderlich81f087e2019-06-29 11:36:20 +0200392
393 n = blk_derase(desc, blk_start, blk_cnt);
Patrick Delaunayab5ea4d2022-02-15 16:23:23 +0100394 printf("%d blocks erased at 0x%x: %s\n", n, blk_start,
395 (n == blk_cnt) ? "OK" : "ERROR");
Frank Wunderlich81f087e2019-06-29 11:36:20 +0200396
397 return (n == blk_cnt) ? 0 : 1;
398}
399
400static int env_mmc_erase(void)
401{
402 int dev = mmc_get_env_dev();
403 struct mmc *mmc = find_mmc_device(dev);
404 int ret, copy = 0;
405 u32 offset;
406 const char *errmsg;
407
408 errmsg = init_mmc_for_env(mmc);
409 if (errmsg) {
410 printf("%s\n", errmsg);
411 return 1;
412 }
413
Marek Vasutd07a6e32021-10-06 18:29:53 +0200414 if (mmc_get_env_addr(mmc, copy, &offset)) {
415 ret = CMD_RET_FAILURE;
416 goto fini;
417 }
Frank Wunderlich81f087e2019-06-29 11:36:20 +0200418
Patrick Delaunayab5ea4d2022-02-15 16:23:23 +0100419 printf("\n");
Frank Wunderlich81f087e2019-06-29 11:36:20 +0200420 ret = erase_env(mmc, CONFIG_ENV_SIZE, offset);
421
Marek Vasutc3bb7412025-06-09 21:26:37 +0200422 if (IS_ENABLED(CONFIG_ENV_REDUNDANT)) {
Patrick Delaunay036a3582022-11-10 11:48:59 +0100423 copy = 1;
Frank Wunderlich81f087e2019-06-29 11:36:20 +0200424
Marek Vasutf1a41982025-02-21 19:47:23 +0100425 if (mmc_env_is_redundant_in_both_boot_hwparts(mmc)) {
Patrick Delaunay036a3582022-11-10 11:48:59 +0100426 ret = mmc_set_env_part(mmc, copy + 1);
427 if (ret)
428 goto fini;
429 }
Marek Vasut509f36e2021-10-17 19:23:36 +0200430
Patrick Delaunay036a3582022-11-10 11:48:59 +0100431 if (mmc_get_env_addr(mmc, copy, &offset)) {
432 ret = CMD_RET_FAILURE;
433 goto fini;
434 }
Frank Wunderlich81f087e2019-06-29 11:36:20 +0200435
Patrick Delaunay036a3582022-11-10 11:48:59 +0100436 ret |= erase_env(mmc, CONFIG_ENV_SIZE, offset);
437 }
Frank Wunderlich81f087e2019-06-29 11:36:20 +0200438
Marek Vasutd07a6e32021-10-06 18:29:53 +0200439fini:
440 fini_mmc_for_env(mmc);
Frank Wunderlich81f087e2019-06-29 11:36:20 +0200441 return ret;
442}
Simon Glass0e84d962024-09-29 19:49:50 -0600443#endif /* CONFIG_CMD_SAVEENV && !CONFIG_XPL_BUILD */
Terry Lvb4eceac2010-05-17 10:57:01 +0800444
Igor Grinbergecbfb622011-11-07 01:14:05 +0000445static inline int read_env(struct mmc *mmc, unsigned long size,
446 unsigned long offset, const void *buffer)
Terry Lvb4eceac2010-05-17 10:57:01 +0800447{
448 uint blk_start, blk_cnt, n;
Simon Glassda898922016-05-14 14:03:03 -0600449 struct blk_desc *desc = mmc_get_blk_desc(mmc);
Terry Lvb4eceac2010-05-17 10:57:01 +0800450
Igor Grinbergecbfb622011-11-07 01:14:05 +0000451 blk_start = ALIGN(offset, mmc->read_bl_len) / mmc->read_bl_len;
452 blk_cnt = ALIGN(size, mmc->read_bl_len) / mmc->read_bl_len;
Terry Lvb4eceac2010-05-17 10:57:01 +0800453
Simon Glassda898922016-05-14 14:03:03 -0600454 n = blk_dread(desc, blk_start, blk_cnt, (uchar *)buffer);
Terry Lvb4eceac2010-05-17 10:57:01 +0800455
456 return (n == blk_cnt) ? 0 : -1;
457}
458
Marek Vasute4766fa2025-02-21 19:47:24 +0100459static int env_mmc_load_redundant(void)
Patrick Delaunayd948e712022-11-10 11:49:00 +0100460{
Tom Rinic929f822014-03-28 12:03:34 -0400461 struct mmc *mmc;
Michael Heimpolda1d25a32013-04-10 10:36:19 +0000462 u32 offset1, offset2;
463 int read1_fail = 0, read2_fail = 0;
Michael Heimpolda1d25a32013-04-10 10:36:19 +0000464 int ret;
Clemens Gruberf7b61442016-01-20 15:43:37 +0100465 int dev = mmc_get_env_dev();
Tim Harveyeda75c32015-05-08 14:52:09 -0700466 const char *errmsg = NULL;
Michael Heimpolda1d25a32013-04-10 10:36:19 +0000467
Markus Niebel6ef41fd2013-10-04 15:48:03 +0200468 ALLOC_CACHE_ALIGN_BUFFER(env_t, tmp_env1, 1);
469 ALLOC_CACHE_ALIGN_BUFFER(env_t, tmp_env2, 1);
470
Faiz Abbasaae518f2018-02-12 19:24:31 +0530471 mmc_initialize(NULL);
472
Tom Rinic929f822014-03-28 12:03:34 -0400473 mmc = find_mmc_device(dev);
474
Tim Harveyeda75c32015-05-08 14:52:09 -0700475 errmsg = init_mmc_for_env(mmc);
476 if (errmsg) {
Simon Glass99778492017-08-03 12:22:17 -0600477 ret = -EIO;
Michael Heimpolda1d25a32013-04-10 10:36:19 +0000478 goto err;
479 }
480
481 if (mmc_get_env_addr(mmc, 0, &offset1) ||
482 mmc_get_env_addr(mmc, 1, &offset2)) {
Simon Glass99778492017-08-03 12:22:17 -0600483 ret = -EIO;
Michael Heimpolda1d25a32013-04-10 10:36:19 +0000484 goto fini;
485 }
486
Marek Vasutf1a41982025-02-21 19:47:23 +0100487 if (mmc_env_is_redundant_in_both_boot_hwparts(mmc)) {
Patrick Delaunay036a3582022-11-10 11:48:59 +0100488 ret = mmc_set_env_part(mmc, 1);
489 if (ret)
490 goto fini;
491 }
Marek Vasut509f36e2021-10-17 19:23:36 +0200492
Michael Heimpolda1d25a32013-04-10 10:36:19 +0000493 read1_fail = read_env(mmc, CONFIG_ENV_SIZE, offset1, tmp_env1);
Marek Vasut509f36e2021-10-17 19:23:36 +0200494
Marek Vasutf1a41982025-02-21 19:47:23 +0100495 if (mmc_env_is_redundant_in_both_boot_hwparts(mmc)) {
Patrick Delaunay036a3582022-11-10 11:48:59 +0100496 ret = mmc_set_env_part(mmc, 2);
497 if (ret)
498 goto fini;
499 }
Marek Vasut509f36e2021-10-17 19:23:36 +0200500
Michael Heimpolda1d25a32013-04-10 10:36:19 +0000501 read2_fail = read_env(mmc, CONFIG_ENV_SIZE, offset2, tmp_env2);
502
Simon Goldschmidte07096e2018-01-31 14:47:11 +0100503 ret = env_import_redund((char *)tmp_env1, read1_fail, (char *)tmp_env2,
Marek Vasutdfe4b7e2020-07-07 20:51:35 +0200504 read2_fail, H_EXTERNAL);
Quentin Schulzad652262024-04-15 14:43:57 +0200505 printf("Reading from %sMMC(%d)... ", gd->env_valid == ENV_REDUND ? "redundant " : "", dev);
Michael Heimpolda1d25a32013-04-10 10:36:19 +0000506
507fini:
508 fini_mmc_for_env(mmc);
509err:
510 if (ret)
Simon Glass97385862019-08-01 09:47:00 -0600511 env_set_default(errmsg, 0);
Simon Glass99778492017-08-03 12:22:17 -0600512
Simon Glass99778492017-08-03 12:22:17 -0600513 return ret;
Michael Heimpolda1d25a32013-04-10 10:36:19 +0000514}
Marek Vasute4766fa2025-02-21 19:47:24 +0100515
516static int env_mmc_load_singular(void)
Terry Lvb4eceac2010-05-17 10:57:01 +0800517{
Tom Rinia840b8e2013-04-05 14:55:21 -0400518 ALLOC_CACHE_ALIGN_BUFFER(char, buf, CONFIG_ENV_SIZE);
Tom Rinic929f822014-03-28 12:03:34 -0400519 struct mmc *mmc;
Mingkai Hua55fce32011-01-24 17:09:55 +0000520 u32 offset;
Stephen Warren27ee0f32012-07-30 10:55:44 +0000521 int ret;
Clemens Gruberf7b61442016-01-20 15:43:37 +0100522 int dev = mmc_get_env_dev();
Tim Harveyeda75c32015-05-08 14:52:09 -0700523 const char *errmsg;
Pankit Gargc2342f62019-11-19 09:49:31 +0000524 env_t *ep = NULL;
Tom Rinic929f822014-03-28 12:03:34 -0400525
Tom Rinic929f822014-03-28 12:03:34 -0400526 mmc = find_mmc_device(dev);
Stephen Warren27ee0f32012-07-30 10:55:44 +0000527
Tim Harveyeda75c32015-05-08 14:52:09 -0700528 errmsg = init_mmc_for_env(mmc);
529 if (errmsg) {
Simon Glass99778492017-08-03 12:22:17 -0600530 ret = -EIO;
Stephen Warren27ee0f32012-07-30 10:55:44 +0000531 goto err;
532 }
Terry Lvb4eceac2010-05-17 10:57:01 +0800533
Michael Heimpolda1d25a32013-04-10 10:36:19 +0000534 if (mmc_get_env_addr(mmc, 0, &offset)) {
Simon Glass99778492017-08-03 12:22:17 -0600535 ret = -EIO;
Stephen Warren27ee0f32012-07-30 10:55:44 +0000536 goto fini;
537 }
Terry Lvb4eceac2010-05-17 10:57:01 +0800538
Tom Rinia840b8e2013-04-05 14:55:21 -0400539 if (read_env(mmc, CONFIG_ENV_SIZE, offset, buf)) {
Tim Harveyeda75c32015-05-08 14:52:09 -0700540 errmsg = "!read failed";
Simon Glass99778492017-08-03 12:22:17 -0600541 ret = -EIO;
Stephen Warren27ee0f32012-07-30 10:55:44 +0000542 goto fini;
543 }
Terry Lvb4eceac2010-05-17 10:57:01 +0800544
Quentin Schulzad652262024-04-15 14:43:57 +0200545 printf("Reading from MMC(%d)... ", dev);
546
Marek Vasutdfe4b7e2020-07-07 20:51:35 +0200547 ret = env_import(buf, 1, H_EXTERNAL);
Pankit Gargc2342f62019-11-19 09:49:31 +0000548 if (!ret) {
549 ep = (env_t *)buf;
550 gd->env_addr = (ulong)&ep->data;
551 }
Stephen Warren27ee0f32012-07-30 10:55:44 +0000552
553fini:
554 fini_mmc_for_env(mmc);
555err:
556 if (ret)
Simon Glass97385862019-08-01 09:47:00 -0600557 env_set_default(errmsg, 0);
Patrick Delaunayd948e712022-11-10 11:49:00 +0100558
Simon Glass99778492017-08-03 12:22:17 -0600559 return ret;
Terry Lvb4eceac2010-05-17 10:57:01 +0800560}
Marek Vasute4766fa2025-02-21 19:47:24 +0100561
562static int env_mmc_load(void)
563{
Tom Rini9081ec82025-04-01 10:21:25 -0600564 if (IS_ENABLED(ENV_IS_EMBEDDED))
Marek Vasute4766fa2025-02-21 19:47:24 +0100565 return 0;
Marek Vasutc3bb7412025-06-09 21:26:37 +0200566 else if (IS_ENABLED(CONFIG_ENV_REDUNDANT))
Marek Vasute4766fa2025-02-21 19:47:24 +0100567 return env_mmc_load_redundant();
568 else
569 return env_mmc_load_singular();
570}
Simon Glassc10a88e2017-08-03 12:21:58 -0600571
572U_BOOT_ENV_LOCATION(mmc) = {
573 .location = ENVL_MMC,
Simon Glassd8273ed2017-08-03 12:22:03 -0600574 ENV_NAME("MMC")
Simon Glass082af922017-08-03 12:22:01 -0600575 .load = env_mmc_load,
Simon Glass0e84d962024-09-29 19:49:50 -0600576#if defined(CONFIG_CMD_SAVEENV) && !defined(CONFIG_XPL_BUILD)
Simon Glass082af922017-08-03 12:22:01 -0600577 .save = env_save_ptr(env_mmc_save),
Patrick Delaunay50f6f112021-02-09 11:48:50 +0100578 .erase = ENV_ERASE_PTR(env_mmc_erase)
Simon Glassc10a88e2017-08-03 12:21:58 -0600579#endif
Simon Glassc10a88e2017-08-03 12:21:58 -0600580};