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