]> Git Repo - J-u-boot.git/blob - env/mmc.c
db2d35e9bd4cf1aaa23fc6d381275dedda9a730f
[J-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
57 static int mmc_env_partition_by_name(struct blk_desc *desc, const char *str,
58                                      struct disk_partition *info)
59 {
60         int i, ret;
61
62         for (i = 1;; i++) {
63                 ret = part_get_info(desc, i, info);
64                 if (ret < 0)
65                         return ret;
66
67                 if (!strncmp((const char *)info->name, str, sizeof(info->name)))
68                         return 0;
69         }
70 }
71
72 static int mmc_env_partition_by_guid(struct blk_desc *desc, struct disk_partition *info)
73 {
74         const efi_guid_t env_guid = PARTITION_U_BOOT_ENVIRONMENT;
75         efi_guid_t type_guid;
76         int i, ret;
77
78         for (i = 1;; i++) {
79                 ret = part_get_info(desc, i, info);
80                 if (ret < 0)
81                         return ret;
82
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)))
85                         return 0;
86         }
87 }
88
89
90 static inline int mmc_offset_try_partition(const char *str, int copy, s64 *val)
91 {
92         struct disk_partition info;
93         struct blk_desc *desc;
94         int len, ret;
95         char dev_str[4];
96
97         snprintf(dev_str, sizeof(dev_str), "%d", mmc_get_env_dev());
98         ret = blk_get_device_by_str("mmc", dev_str, &desc);
99         if (ret < 0)
100                 return (ret);
101
102         if (str) {
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);
106         }
107         if (ret < 0)
108                 return ret;
109
110         /* round up to info.blksz */
111         len = DIV_ROUND_UP(CONFIG_ENV_SIZE, info.blksz);
112
113         /* use the top of the partion for the environment */
114         *val = (info.start + info.size - (1 + copy) * len) * info.blksz;
115
116         return 0;
117 }
118
119 static inline s64 mmc_offset(struct mmc *mmc, int copy)
120 {
121         const struct {
122                 const char *offset_redund;
123                 const char *partition;
124                 const char *offset;
125         } dt_prop = {
126                 .offset_redund = "u-boot,mmc-env-offset-redundant",
127                 .partition = "u-boot,mmc-env-partition",
128                 .offset = "u-boot,mmc-env-offset",
129         };
130         s64 val = 0, defvalue;
131         const char *propname;
132         const char *str;
133         int hwpart = 0;
134         int err;
135
136 #if defined(CONFIG_SYS_MMC_ENV_PART)
137         hwpart = mmc_get_env_part(mmc);
138 #endif
139
140 #if defined(CONFIG_ENV_MMC_PARTITION)
141         str = CONFIG_ENV_MMC_PARTITION;
142 #else
143         /* look for the partition in mmc CONFIG_SYS_MMC_ENV_DEV */
144         str = ofnode_conf_read_str(dt_prop.partition);
145 #endif
146
147         if (str) {
148                 /* try to place the environment at end of the partition */
149                 err = mmc_offset_try_partition(str, copy, &val);
150                 if (!err)
151                         return val;
152                 debug("env partition '%s' not found (%d)", str, err);
153         }
154
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);
158                 if (!err)
159                         return val;
160         }
161
162         defvalue = ENV_MMC_OFFSET;
163         propname = dt_prop.offset;
164
165         if (IS_ENABLED(CONFIG_SYS_REDUNDAND_ENVIRONMENT) && copy) {
166                 defvalue = ENV_MMC_OFFSET_REDUND;
167                 propname = dt_prop.offset_redund;
168         }
169
170         return ofnode_conf_read_int(propname, defvalue);
171 }
172 #else
173 static inline s64 mmc_offset(struct mmc *mmc, int copy)
174 {
175         s64 offset = ENV_MMC_OFFSET;
176
177         if (IS_ENABLED(CONFIG_SYS_REDUNDAND_ENVIRONMENT) && copy)
178                 offset = ENV_MMC_OFFSET_REDUND;
179
180         return offset;
181 }
182 #endif
183
184 __weak int mmc_get_env_addr(struct mmc *mmc, int copy, u32 *env_addr)
185 {
186         s64 offset = mmc_offset(mmc, copy);
187
188         if (offset == ENV_MMC_INVALID_OFFSET) {
189                 printf("Invalid ENV offset in MMC, copy=%d\n", copy);
190                 return -ENOENT;
191         }
192
193         if (offset < 0)
194                 offset += mmc->capacity;
195
196         *env_addr = offset;
197
198         return 0;
199 }
200
201 #ifdef CONFIG_SYS_MMC_ENV_PART
202 __weak uint mmc_get_env_part(struct mmc *mmc)
203 {
204         return CONFIG_SYS_MMC_ENV_PART;
205 }
206
207 static unsigned char env_mmc_orig_hwpart;
208
209 static int mmc_set_env_part(struct mmc *mmc, uint part)
210 {
211         int dev = mmc_get_env_dev();
212         int ret = 0;
213
214         ret = blk_select_hwpart_devnum(UCLASS_MMC, dev, part);
215         if (ret)
216                 puts("MMC partition switch failed\n");
217
218         return ret;
219 }
220
221 static bool mmc_set_env_part_init(struct mmc *mmc)
222 {
223         env_mmc_orig_hwpart = mmc_get_blk_desc(mmc)->hwpart;
224         if (mmc_set_env_part(mmc, mmc_get_env_part(mmc)))
225                 return false;
226
227         return true;
228 }
229
230 static int mmc_set_env_part_restore(struct mmc *mmc)
231 {
232         return mmc_set_env_part(mmc, env_mmc_orig_hwpart);
233 }
234 #else
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; };
238 #endif
239
240 static const char *init_mmc_for_env(struct mmc *mmc)
241 {
242         if (!mmc)
243                 return "No MMC card found";
244
245 #if CONFIG_IS_ENABLED(BLK)
246         struct udevice *dev;
247
248         if (blk_get_from_parent(mmc->dev, &dev))
249                 return "No block device";
250 #else
251         if (mmc_init(mmc))
252                 return "MMC init failed";
253 #endif
254         if (!mmc_set_env_part_init(mmc))
255                 return "MMC partition switch failed";
256
257         return NULL;
258 }
259
260 static void fini_mmc_for_env(struct mmc *mmc)
261 {
262         mmc_set_env_part_restore(mmc);
263 }
264
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)
268 {
269         uint blk_start, blk_cnt, n;
270         struct blk_desc *desc = mmc_get_blk_desc(mmc);
271
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;
274
275         n = blk_dwrite(desc, blk_start, blk_cnt, (u_char *)buffer);
276
277         return (n == blk_cnt) ? 0 : -1;
278 }
279
280 static int env_mmc_save(void)
281 {
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);
285         u32     offset;
286         int     ret, copy = 0;
287         const char *errmsg;
288
289         errmsg = init_mmc_for_env(mmc);
290         if (errmsg) {
291                 printf("%s\n", errmsg);
292                 return 1;
293         }
294
295         ret = env_export(env_new);
296         if (ret)
297                 goto fini;
298
299         if (IS_ENABLED(CONFIG_SYS_REDUNDAND_ENVIRONMENT)) {
300                 if (gd->env_valid == ENV_VALID)
301                         copy = 1;
302
303                 if (IS_ENABLED(ENV_MMC_HWPART_REDUND)) {
304                         ret = mmc_set_env_part(mmc, copy + 1);
305                         if (ret)
306                                 goto fini;
307                 }
308         }
309
310         if (mmc_get_env_addr(mmc, copy, &offset)) {
311                 ret = 1;
312                 goto fini;
313         }
314
315         printf("Writing to %sMMC(%d)... ", copy ? "redundant " : "", dev);
316         if (write_env(mmc, CONFIG_ENV_SIZE, offset, (u_char *)env_new)) {
317                 puts("failed\n");
318                 ret = 1;
319                 goto fini;
320         }
321
322         ret = 0;
323
324         if (IS_ENABLED(CONFIG_SYS_REDUNDAND_ENVIRONMENT))
325                 gd->env_valid = gd->env_valid == ENV_REDUND ? ENV_VALID : ENV_REDUND;
326
327 fini:
328         fini_mmc_for_env(mmc);
329
330         return ret;
331 }
332
333 static inline int erase_env(struct mmc *mmc, unsigned long size,
334                             unsigned long offset)
335 {
336         uint blk_start, blk_cnt, n;
337         struct blk_desc *desc = mmc_get_blk_desc(mmc);
338         u32 erase_size;
339
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;
343
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");
347
348         return (n == blk_cnt) ? 0 : 1;
349 }
350
351 static int env_mmc_erase(void)
352 {
353         int dev = mmc_get_env_dev();
354         struct mmc *mmc = find_mmc_device(dev);
355         int     ret, copy = 0;
356         u32     offset;
357         const char *errmsg;
358
359         errmsg = init_mmc_for_env(mmc);
360         if (errmsg) {
361                 printf("%s\n", errmsg);
362                 return 1;
363         }
364
365         if (mmc_get_env_addr(mmc, copy, &offset)) {
366                 ret = CMD_RET_FAILURE;
367                 goto fini;
368         }
369
370         printf("\n");
371         ret = erase_env(mmc, CONFIG_ENV_SIZE, offset);
372
373         if (IS_ENABLED(CONFIG_SYS_REDUNDAND_ENVIRONMENT)) {
374                 copy = 1;
375
376                 if (IS_ENABLED(ENV_MMC_HWPART_REDUND)) {
377                         ret = mmc_set_env_part(mmc, copy + 1);
378                         if (ret)
379                                 goto fini;
380                 }
381
382                 if (mmc_get_env_addr(mmc, copy, &offset)) {
383                         ret = CMD_RET_FAILURE;
384                         goto fini;
385                 }
386
387                 ret |= erase_env(mmc, CONFIG_ENV_SIZE, offset);
388         }
389
390 fini:
391         fini_mmc_for_env(mmc);
392         return ret;
393 }
394 #endif /* CONFIG_CMD_SAVEENV && !CONFIG_SPL_BUILD */
395
396 static inline int read_env(struct mmc *mmc, unsigned long size,
397                            unsigned long offset, const void *buffer)
398 {
399         uint blk_start, blk_cnt, n;
400         struct blk_desc *desc = mmc_get_blk_desc(mmc);
401
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;
404
405         n = blk_dread(desc, blk_start, blk_cnt, (uchar *)buffer);
406
407         return (n == blk_cnt) ? 0 : -1;
408 }
409
410 #if defined(ENV_IS_EMBEDDED)
411 static int env_mmc_load(void)
412 {
413         return 0;
414 }
415 #elif defined(CONFIG_SYS_REDUNDAND_ENVIRONMENT)
416 static int env_mmc_load(void)
417 {
418         struct mmc *mmc;
419         u32 offset1, offset2;
420         int read1_fail = 0, read2_fail = 0;
421         int ret;
422         int dev = mmc_get_env_dev();
423         const char *errmsg = NULL;
424
425         ALLOC_CACHE_ALIGN_BUFFER(env_t, tmp_env1, 1);
426         ALLOC_CACHE_ALIGN_BUFFER(env_t, tmp_env2, 1);
427
428         mmc_initialize(NULL);
429
430         mmc = find_mmc_device(dev);
431
432         errmsg = init_mmc_for_env(mmc);
433         if (errmsg) {
434                 ret = -EIO;
435                 goto err;
436         }
437
438         if (mmc_get_env_addr(mmc, 0, &offset1) ||
439             mmc_get_env_addr(mmc, 1, &offset2)) {
440                 ret = -EIO;
441                 goto fini;
442         }
443
444         if (IS_ENABLED(ENV_MMC_HWPART_REDUND)) {
445                 ret = mmc_set_env_part(mmc, 1);
446                 if (ret)
447                         goto fini;
448         }
449
450         read1_fail = read_env(mmc, CONFIG_ENV_SIZE, offset1, tmp_env1);
451
452         if (IS_ENABLED(ENV_MMC_HWPART_REDUND)) {
453                 ret = mmc_set_env_part(mmc, 2);
454                 if (ret)
455                         goto fini;
456         }
457
458         read2_fail = read_env(mmc, CONFIG_ENV_SIZE, offset2, tmp_env2);
459
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);
463
464 fini:
465         fini_mmc_for_env(mmc);
466 err:
467         if (ret)
468                 env_set_default(errmsg, 0);
469
470         return ret;
471 }
472 #else /* ! CONFIG_SYS_REDUNDAND_ENVIRONMENT */
473 static int env_mmc_load(void)
474 {
475         ALLOC_CACHE_ALIGN_BUFFER(char, buf, CONFIG_ENV_SIZE);
476         struct mmc *mmc;
477         u32 offset;
478         int ret;
479         int dev = mmc_get_env_dev();
480         const char *errmsg;
481         env_t *ep = NULL;
482
483         mmc = find_mmc_device(dev);
484
485         errmsg = init_mmc_for_env(mmc);
486         if (errmsg) {
487                 ret = -EIO;
488                 goto err;
489         }
490
491         if (mmc_get_env_addr(mmc, 0, &offset)) {
492                 ret = -EIO;
493                 goto fini;
494         }
495
496         if (read_env(mmc, CONFIG_ENV_SIZE, offset, buf)) {
497                 errmsg = "!read failed";
498                 ret = -EIO;
499                 goto fini;
500         }
501
502         printf("Reading from MMC(%d)... ", dev);
503
504         ret = env_import(buf, 1, H_EXTERNAL);
505         if (!ret) {
506                 ep = (env_t *)buf;
507                 gd->env_addr = (ulong)&ep->data;
508         }
509
510 fini:
511         fini_mmc_for_env(mmc);
512 err:
513         if (ret)
514                 env_set_default(errmsg, 0);
515
516         return ret;
517 }
518 #endif /* CONFIG_SYS_REDUNDAND_ENVIRONMENT */
519
520 U_BOOT_ENV_LOCATION(mmc) = {
521         .location       = ENVL_MMC,
522         ENV_NAME("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)
527 #endif
528 };
This page took 0.043805 seconds and 2 git commands to generate.