Merge branch '2023-05-11-CONFIG_IS_ENABLED-vs-IS_ENABLED-cleanups' into next
[platform/kernel/u-boot.git] / env / mmc.c
1 // SPDX-License-Identifier: GPL-2.0+
2 /*
3  * (C) Copyright 2008-2011 Freescale Semiconductor, Inc.
4  */
5
6 /* #define DEBUG */
7
8 #include <common.h>
9 #include <asm/global_data.h>
10
11 #include <command.h>
12 #include <env.h>
13 #include <env_internal.h>
14 #include <fdtdec.h>
15 #include <linux/stddef.h>
16 #include <malloc.h>
17 #include <memalign.h>
18 #include <mmc.h>
19 #include <part.h>
20 #include <search.h>
21 #include <errno.h>
22 #include <dm/ofnode.h>
23
24 #define ENV_MMC_INVALID_OFFSET ((s64)-1)
25
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
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
40 #endif
41
42 DECLARE_GLOBAL_DATA_PTR;
43
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))
53 #define ENV_MMC_HWPART_REDUND   1
54 #endif
55
56 #if CONFIG_IS_ENABLED(OF_CONTROL)
57 static inline int mmc_offset_try_partition(const char *str, int copy, s64 *val)
58 {
59         struct disk_partition info;
60         struct blk_desc *desc;
61         int len, i, ret;
62         char dev_str[4];
63
64         snprintf(dev_str, sizeof(dev_str), "%d", mmc_get_env_dev());
65         ret = blk_get_device_by_str("mmc", dev_str, &desc);
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
74                 if (str && !strncmp((const char *)info.name, str, sizeof(info.name)))
75                         break;
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
86         }
87
88         /* round up to info.blksz */
89         len = DIV_ROUND_UP(CONFIG_ENV_SIZE, info.blksz);
90
91         /* use the top of the partion for the environment */
92         *val = (info.start + info.size - (1 + copy) * len) * info.blksz;
93
94         return 0;
95 }
96
97 static inline s64 mmc_offset(struct mmc *mmc, int copy)
98 {
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         };
108         s64 val = 0, defvalue;
109         const char *propname;
110         const char *str;
111         int hwpart = 0;
112         int err;
113
114         if (IS_ENABLED(CONFIG_SYS_MMC_ENV_PART))
115                 hwpart = mmc_get_env_part(mmc);
116
117         /* look for the partition in mmc CONFIG_SYS_MMC_ENV_DEV */
118         str = ofnode_conf_read_str(dt_prop.partition);
119         if (str) {
120                 /* try to place the environment at end of the partition */
121                 err = mmc_offset_try_partition(str, copy, &val);
122                 if (!err)
123                         return val;
124                 debug("env partition '%s' not found (%d)", str, err);
125         }
126
127         /* try the GPT partition with "U-Boot ENV" TYPE GUID */
128         if (IS_ENABLED(CONFIG_PARTITION_TYPE_GUID) && hwpart == 0) {
129                 err = mmc_offset_try_partition(NULL, copy, &val);
130                 if (!err)
131                         return val;
132         }
133
134         defvalue = ENV_MMC_OFFSET;
135         propname = dt_prop.offset;
136
137         if (IS_ENABLED(CONFIG_SYS_REDUNDAND_ENVIRONMENT) && copy) {
138                 defvalue = ENV_MMC_OFFSET_REDUND;
139                 propname = dt_prop.offset_redund;
140         }
141
142         return ofnode_conf_read_int(propname, defvalue);
143 }
144 #else
145 static inline s64 mmc_offset(struct mmc *mmc, int copy)
146 {
147         s64 offset = ENV_MMC_OFFSET;
148
149         if (IS_ENABLED(CONFIG_SYS_REDUNDAND_ENVIRONMENT) && copy)
150                 offset = ENV_MMC_OFFSET_REDUND;
151
152         return offset;
153 }
154 #endif
155
156 __weak int mmc_get_env_addr(struct mmc *mmc, int copy, u32 *env_addr)
157 {
158         s64 offset = mmc_offset(mmc, copy);
159
160         if (offset == ENV_MMC_INVALID_OFFSET) {
161                 printf("Invalid ENV offset in MMC, copy=%d\n", copy);
162                 return -ENOENT;
163         }
164
165         if (offset < 0)
166                 offset += mmc->capacity;
167
168         *env_addr = offset;
169
170         return 0;
171 }
172
173 #ifdef CONFIG_SYS_MMC_ENV_PART
174 __weak uint mmc_get_env_part(struct mmc *mmc)
175 {
176         return CONFIG_SYS_MMC_ENV_PART;
177 }
178
179 static unsigned char env_mmc_orig_hwpart;
180
181 static int mmc_set_env_part(struct mmc *mmc, uint part)
182 {
183         int dev = mmc_get_env_dev();
184         int ret = 0;
185
186         ret = blk_select_hwpart_devnum(UCLASS_MMC, dev, part);
187         if (ret)
188                 puts("MMC partition switch failed\n");
189
190         return ret;
191 }
192
193 static bool mmc_set_env_part_init(struct mmc *mmc)
194 {
195         env_mmc_orig_hwpart = mmc_get_blk_desc(mmc)->hwpart;
196         if (mmc_set_env_part(mmc, mmc_get_env_part(mmc)))
197                 return false;
198
199         return true;
200 }
201
202 static int mmc_set_env_part_restore(struct mmc *mmc)
203 {
204         return mmc_set_env_part(mmc, env_mmc_orig_hwpart);
205 }
206 #else
207 static inline int mmc_set_env_part(struct mmc *mmc, uint part) {return 0; };
208 static bool mmc_set_env_part_init(struct mmc *mmc) {return true; }
209 static inline int mmc_set_env_part_restore(struct mmc *mmc) {return 0; };
210 #endif
211
212 static const char *init_mmc_for_env(struct mmc *mmc)
213 {
214         if (!mmc)
215                 return "No MMC card found";
216
217 #if CONFIG_IS_ENABLED(BLK)
218         struct udevice *dev;
219
220         if (blk_get_from_parent(mmc->dev, &dev))
221                 return "No block device";
222 #else
223         if (mmc_init(mmc))
224                 return "MMC init failed";
225 #endif
226         if (!mmc_set_env_part_init(mmc))
227                 return "MMC partition switch failed";
228
229         return NULL;
230 }
231
232 static void fini_mmc_for_env(struct mmc *mmc)
233 {
234         mmc_set_env_part_restore(mmc);
235 }
236
237 #if defined(CONFIG_CMD_SAVEENV) && !defined(CONFIG_SPL_BUILD)
238 static inline int write_env(struct mmc *mmc, unsigned long size,
239                             unsigned long offset, const void *buffer)
240 {
241         uint blk_start, blk_cnt, n;
242         struct blk_desc *desc = mmc_get_blk_desc(mmc);
243
244         blk_start       = ALIGN(offset, mmc->write_bl_len) / mmc->write_bl_len;
245         blk_cnt         = ALIGN(size, mmc->write_bl_len) / mmc->write_bl_len;
246
247         n = blk_dwrite(desc, blk_start, blk_cnt, (u_char *)buffer);
248
249         return (n == blk_cnt) ? 0 : -1;
250 }
251
252 static int env_mmc_save(void)
253 {
254         ALLOC_CACHE_ALIGN_BUFFER(env_t, env_new, 1);
255         int dev = mmc_get_env_dev();
256         struct mmc *mmc = find_mmc_device(dev);
257         u32     offset;
258         int     ret, copy = 0;
259         const char *errmsg;
260
261         errmsg = init_mmc_for_env(mmc);
262         if (errmsg) {
263                 printf("%s\n", errmsg);
264                 return 1;
265         }
266
267         ret = env_export(env_new);
268         if (ret)
269                 goto fini;
270
271         if (IS_ENABLED(CONFIG_SYS_REDUNDAND_ENVIRONMENT)) {
272                 if (gd->env_valid == ENV_VALID)
273                         copy = 1;
274
275                 if (IS_ENABLED(ENV_MMC_HWPART_REDUND)) {
276                         ret = mmc_set_env_part(mmc, copy + 1);
277                         if (ret)
278                                 goto fini;
279                 }
280         }
281
282         if (mmc_get_env_addr(mmc, copy, &offset)) {
283                 ret = 1;
284                 goto fini;
285         }
286
287         printf("Writing to %sMMC(%d)... ", copy ? "redundant " : "", dev);
288         if (write_env(mmc, CONFIG_ENV_SIZE, offset, (u_char *)env_new)) {
289                 puts("failed\n");
290                 ret = 1;
291                 goto fini;
292         }
293
294         ret = 0;
295
296         if (IS_ENABLED(CONFIG_SYS_REDUNDAND_ENVIRONMENT))
297                 gd->env_valid = gd->env_valid == ENV_REDUND ? ENV_VALID : ENV_REDUND;
298
299 fini:
300         fini_mmc_for_env(mmc);
301
302         return ret;
303 }
304
305 static inline int erase_env(struct mmc *mmc, unsigned long size,
306                             unsigned long offset)
307 {
308         uint blk_start, blk_cnt, n;
309         struct blk_desc *desc = mmc_get_blk_desc(mmc);
310         u32 erase_size;
311
312         erase_size = mmc->erase_grp_size * desc->blksz;
313         blk_start = ALIGN_DOWN(offset, erase_size) / desc->blksz;
314         blk_cnt = ALIGN(size, erase_size) / desc->blksz;
315
316         n = blk_derase(desc, blk_start, blk_cnt);
317         printf("%d blocks erased at 0x%x: %s\n", n, blk_start,
318                (n == blk_cnt) ? "OK" : "ERROR");
319
320         return (n == blk_cnt) ? 0 : 1;
321 }
322
323 static int env_mmc_erase(void)
324 {
325         int dev = mmc_get_env_dev();
326         struct mmc *mmc = find_mmc_device(dev);
327         int     ret, copy = 0;
328         u32     offset;
329         const char *errmsg;
330
331         errmsg = init_mmc_for_env(mmc);
332         if (errmsg) {
333                 printf("%s\n", errmsg);
334                 return 1;
335         }
336
337         if (mmc_get_env_addr(mmc, copy, &offset)) {
338                 ret = CMD_RET_FAILURE;
339                 goto fini;
340         }
341
342         printf("\n");
343         ret = erase_env(mmc, CONFIG_ENV_SIZE, offset);
344
345         if (IS_ENABLED(CONFIG_SYS_REDUNDAND_ENVIRONMENT)) {
346                 copy = 1;
347
348                 if (IS_ENABLED(ENV_MMC_HWPART_REDUND)) {
349                         ret = mmc_set_env_part(mmc, copy + 1);
350                         if (ret)
351                                 goto fini;
352                 }
353
354                 if (mmc_get_env_addr(mmc, copy, &offset)) {
355                         ret = CMD_RET_FAILURE;
356                         goto fini;
357                 }
358
359                 ret |= erase_env(mmc, CONFIG_ENV_SIZE, offset);
360         }
361
362 fini:
363         fini_mmc_for_env(mmc);
364         return ret;
365 }
366 #endif /* CONFIG_CMD_SAVEENV && !CONFIG_SPL_BUILD */
367
368 static inline int read_env(struct mmc *mmc, unsigned long size,
369                            unsigned long offset, const void *buffer)
370 {
371         uint blk_start, blk_cnt, n;
372         struct blk_desc *desc = mmc_get_blk_desc(mmc);
373
374         blk_start       = ALIGN(offset, mmc->read_bl_len) / mmc->read_bl_len;
375         blk_cnt         = ALIGN(size, mmc->read_bl_len) / mmc->read_bl_len;
376
377         n = blk_dread(desc, blk_start, blk_cnt, (uchar *)buffer);
378
379         return (n == blk_cnt) ? 0 : -1;
380 }
381
382 #if defined(ENV_IS_EMBEDDED)
383 static int env_mmc_load(void)
384 {
385         return 0;
386 }
387 #elif defined(CONFIG_SYS_REDUNDAND_ENVIRONMENT)
388 static int env_mmc_load(void)
389 {
390         struct mmc *mmc;
391         u32 offset1, offset2;
392         int read1_fail = 0, read2_fail = 0;
393         int ret;
394         int dev = mmc_get_env_dev();
395         const char *errmsg = NULL;
396
397         ALLOC_CACHE_ALIGN_BUFFER(env_t, tmp_env1, 1);
398         ALLOC_CACHE_ALIGN_BUFFER(env_t, tmp_env2, 1);
399
400         mmc_initialize(NULL);
401
402         mmc = find_mmc_device(dev);
403
404         errmsg = init_mmc_for_env(mmc);
405         if (errmsg) {
406                 ret = -EIO;
407                 goto err;
408         }
409
410         if (mmc_get_env_addr(mmc, 0, &offset1) ||
411             mmc_get_env_addr(mmc, 1, &offset2)) {
412                 ret = -EIO;
413                 goto fini;
414         }
415
416         if (IS_ENABLED(ENV_MMC_HWPART_REDUND)) {
417                 ret = mmc_set_env_part(mmc, 1);
418                 if (ret)
419                         goto fini;
420         }
421
422         read1_fail = read_env(mmc, CONFIG_ENV_SIZE, offset1, tmp_env1);
423
424         if (IS_ENABLED(ENV_MMC_HWPART_REDUND)) {
425                 ret = mmc_set_env_part(mmc, 2);
426                 if (ret)
427                         goto fini;
428         }
429
430         read2_fail = read_env(mmc, CONFIG_ENV_SIZE, offset2, tmp_env2);
431
432         ret = env_import_redund((char *)tmp_env1, read1_fail, (char *)tmp_env2,
433                                 read2_fail, H_EXTERNAL);
434
435 fini:
436         fini_mmc_for_env(mmc);
437 err:
438         if (ret)
439                 env_set_default(errmsg, 0);
440
441         return ret;
442 }
443 #else /* ! CONFIG_SYS_REDUNDAND_ENVIRONMENT */
444 static int env_mmc_load(void)
445 {
446         ALLOC_CACHE_ALIGN_BUFFER(char, buf, CONFIG_ENV_SIZE);
447         struct mmc *mmc;
448         u32 offset;
449         int ret;
450         int dev = mmc_get_env_dev();
451         const char *errmsg;
452         env_t *ep = NULL;
453
454         mmc = find_mmc_device(dev);
455
456         errmsg = init_mmc_for_env(mmc);
457         if (errmsg) {
458                 ret = -EIO;
459                 goto err;
460         }
461
462         if (mmc_get_env_addr(mmc, 0, &offset)) {
463                 ret = -EIO;
464                 goto fini;
465         }
466
467         if (read_env(mmc, CONFIG_ENV_SIZE, offset, buf)) {
468                 errmsg = "!read failed";
469                 ret = -EIO;
470                 goto fini;
471         }
472
473         ret = env_import(buf, 1, H_EXTERNAL);
474         if (!ret) {
475                 ep = (env_t *)buf;
476                 gd->env_addr = (ulong)&ep->data;
477         }
478
479 fini:
480         fini_mmc_for_env(mmc);
481 err:
482         if (ret)
483                 env_set_default(errmsg, 0);
484
485         return ret;
486 }
487 #endif /* CONFIG_SYS_REDUNDAND_ENVIRONMENT */
488
489 U_BOOT_ENV_LOCATION(mmc) = {
490         .location       = ENVL_MMC,
491         ENV_NAME("MMC")
492         .load           = env_mmc_load,
493 #ifndef CONFIG_SPL_BUILD
494         .save           = env_save_ptr(env_mmc_save),
495         .erase          = ENV_ERASE_PTR(env_mmc_erase)
496 #endif
497 };