arm: Remove armadillo-800eva board
[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 __STR(X) #X
25 #define STR(X) __STR(X)
26
27 DECLARE_GLOBAL_DATA_PTR;
28
29 /*
30  * In case the environment is redundant, stored in eMMC hardware boot
31  * partition and the environment and redundant environment offsets are
32  * identical, store the environment and redundant environment in both
33  * eMMC boot partitions, one copy in each.
34  * */
35 #if (defined(CONFIG_SYS_REDUNDAND_ENVIRONMENT) && \
36      (CONFIG_SYS_MMC_ENV_PART == 1) && \
37      (CONFIG_ENV_OFFSET == CONFIG_ENV_OFFSET_REDUND))
38 #define ENV_MMC_HWPART_REDUND
39 #endif
40
41 #if CONFIG_IS_ENABLED(OF_CONTROL)
42 static inline int mmc_offset_try_partition(const char *str, int copy, s64 *val)
43 {
44         struct disk_partition info;
45         struct blk_desc *desc;
46         int len, i, ret;
47         char dev_str[4];
48
49         snprintf(dev_str, sizeof(dev_str), "%d", mmc_get_env_dev());
50         ret = blk_get_device_by_str("mmc", dev_str, &desc);
51         if (ret < 0)
52                 return (ret);
53
54         for (i = 1;;i++) {
55                 ret = part_get_info(desc, i, &info);
56                 if (ret < 0)
57                         return ret;
58
59                 if (!strncmp((const char *)info.name, str, sizeof(info.name)))
60                         break;
61         }
62
63         /* round up to info.blksz */
64         len = DIV_ROUND_UP(CONFIG_ENV_SIZE, info.blksz);
65
66         /* use the top of the partion for the environment */
67         *val = (info.start + info.size - (1 + copy) * len) * info.blksz;
68
69         return 0;
70 }
71
72 static inline s64 mmc_offset(int copy)
73 {
74         const struct {
75                 const char *offset_redund;
76                 const char *partition;
77                 const char *offset;
78         } dt_prop = {
79                 .offset_redund = "u-boot,mmc-env-offset-redundant",
80                 .partition = "u-boot,mmc-env-partition",
81                 .offset = "u-boot,mmc-env-offset",
82         };
83         s64 val = 0, defvalue;
84         const char *propname;
85         const char *str;
86         int err;
87
88         /* look for the partition in mmc CONFIG_SYS_MMC_ENV_DEV */
89         str = ofnode_conf_read_str(dt_prop.partition);
90         if (str) {
91                 /* try to place the environment at end of the partition */
92                 err = mmc_offset_try_partition(str, copy, &val);
93                 if (!err)
94                         return val;
95         }
96
97         defvalue = CONFIG_ENV_OFFSET;
98         propname = dt_prop.offset;
99
100 #if defined(CONFIG_ENV_OFFSET_REDUND)
101         if (copy) {
102                 defvalue = CONFIG_ENV_OFFSET_REDUND;
103                 propname = dt_prop.offset_redund;
104         }
105 #endif
106         return ofnode_conf_read_int(propname, defvalue);
107 }
108 #else
109 static inline s64 mmc_offset(int copy)
110 {
111         s64 offset = CONFIG_ENV_OFFSET;
112
113 #if defined(CONFIG_ENV_OFFSET_REDUND)
114         if (copy)
115                 offset = CONFIG_ENV_OFFSET_REDUND;
116 #endif
117         return offset;
118 }
119 #endif
120
121 __weak int mmc_get_env_addr(struct mmc *mmc, int copy, u32 *env_addr)
122 {
123         s64 offset = mmc_offset(copy);
124
125         if (offset < 0)
126                 offset += mmc->capacity;
127
128         *env_addr = offset;
129
130         return 0;
131 }
132
133 #ifdef CONFIG_SYS_MMC_ENV_PART
134 __weak uint mmc_get_env_part(struct mmc *mmc)
135 {
136         return CONFIG_SYS_MMC_ENV_PART;
137 }
138
139 static unsigned char env_mmc_orig_hwpart;
140
141 static int mmc_set_env_part(struct mmc *mmc, uint part)
142 {
143         int dev = mmc_get_env_dev();
144         int ret = 0;
145
146         ret = blk_select_hwpart_devnum(IF_TYPE_MMC, dev, part);
147         if (ret)
148                 puts("MMC partition switch failed\n");
149
150         return ret;
151 }
152 #else
153 static inline int mmc_set_env_part(struct mmc *mmc, uint part) {return 0; };
154 #endif
155
156 static const char *init_mmc_for_env(struct mmc *mmc)
157 {
158         if (!mmc)
159                 return "No MMC card found";
160
161 #if CONFIG_IS_ENABLED(BLK)
162         struct udevice *dev;
163
164         if (blk_get_from_parent(mmc->dev, &dev))
165                 return "No block device";
166 #else
167         if (mmc_init(mmc))
168                 return "MMC init failed";
169 #endif
170         env_mmc_orig_hwpart = mmc_get_blk_desc(mmc)->hwpart;
171         if (mmc_set_env_part(mmc, mmc_get_env_part(mmc)))
172                 return "MMC partition switch failed";
173
174         return NULL;
175 }
176
177 static void fini_mmc_for_env(struct mmc *mmc)
178 {
179 #ifdef CONFIG_SYS_MMC_ENV_PART
180         int dev = mmc_get_env_dev();
181
182         blk_select_hwpart_devnum(IF_TYPE_MMC, dev, env_mmc_orig_hwpart);
183 #endif
184 }
185
186 #if defined(CONFIG_CMD_SAVEENV) && !defined(CONFIG_SPL_BUILD)
187 static inline int write_env(struct mmc *mmc, unsigned long size,
188                             unsigned long offset, const void *buffer)
189 {
190         uint blk_start, blk_cnt, n;
191         struct blk_desc *desc = mmc_get_blk_desc(mmc);
192
193         blk_start       = ALIGN(offset, mmc->write_bl_len) / mmc->write_bl_len;
194         blk_cnt         = ALIGN(size, mmc->write_bl_len) / mmc->write_bl_len;
195
196         n = blk_dwrite(desc, blk_start, blk_cnt, (u_char *)buffer);
197
198         return (n == blk_cnt) ? 0 : -1;
199 }
200
201 static int env_mmc_save(void)
202 {
203         ALLOC_CACHE_ALIGN_BUFFER(env_t, env_new, 1);
204         int dev = mmc_get_env_dev();
205         struct mmc *mmc = find_mmc_device(dev);
206         u32     offset;
207         int     ret, copy = 0;
208         const char *errmsg;
209
210         errmsg = init_mmc_for_env(mmc);
211         if (errmsg) {
212                 printf("%s\n", errmsg);
213                 return 1;
214         }
215
216         ret = env_export(env_new);
217         if (ret)
218                 goto fini;
219
220 #ifdef CONFIG_ENV_OFFSET_REDUND
221         if (gd->env_valid == ENV_VALID)
222                 copy = 1;
223
224 #ifdef ENV_MMC_HWPART_REDUND
225         ret = mmc_set_env_part(mmc, copy + 1);
226         if (ret)
227                 goto fini;
228 #endif
229
230 #endif
231
232         if (mmc_get_env_addr(mmc, copy, &offset)) {
233                 ret = 1;
234                 goto fini;
235         }
236
237         printf("Writing to %sMMC(%d)... ", copy ? "redundant " : "", dev);
238         if (write_env(mmc, CONFIG_ENV_SIZE, offset, (u_char *)env_new)) {
239                 puts("failed\n");
240                 ret = 1;
241                 goto fini;
242         }
243
244         ret = 0;
245
246 #ifdef CONFIG_ENV_OFFSET_REDUND
247         gd->env_valid = gd->env_valid == ENV_REDUND ? ENV_VALID : ENV_REDUND;
248 #endif
249
250 fini:
251         fini_mmc_for_env(mmc);
252         return ret;
253 }
254
255 static inline int erase_env(struct mmc *mmc, unsigned long size,
256                             unsigned long offset)
257 {
258         uint blk_start, blk_cnt, n;
259         struct blk_desc *desc = mmc_get_blk_desc(mmc);
260         u32 erase_size;
261
262         erase_size = mmc->erase_grp_size * desc->blksz;
263         blk_start = ALIGN_DOWN(offset, erase_size) / desc->blksz;
264         blk_cnt = ALIGN(size, erase_size) / desc->blksz;
265
266         n = blk_derase(desc, blk_start, blk_cnt);
267         printf("%d blocks erased at 0x%x: %s\n", n, blk_start,
268                (n == blk_cnt) ? "OK" : "ERROR");
269
270         return (n == blk_cnt) ? 0 : 1;
271 }
272
273 static int env_mmc_erase(void)
274 {
275         int dev = mmc_get_env_dev();
276         struct mmc *mmc = find_mmc_device(dev);
277         int     ret, copy = 0;
278         u32     offset;
279         const char *errmsg;
280
281         errmsg = init_mmc_for_env(mmc);
282         if (errmsg) {
283                 printf("%s\n", errmsg);
284                 return 1;
285         }
286
287         if (mmc_get_env_addr(mmc, copy, &offset)) {
288                 ret = CMD_RET_FAILURE;
289                 goto fini;
290         }
291
292         printf("\n");
293         ret = erase_env(mmc, CONFIG_ENV_SIZE, offset);
294
295 #ifdef CONFIG_ENV_OFFSET_REDUND
296         copy = 1;
297
298 #ifdef ENV_MMC_HWPART_REDUND
299         ret = mmc_set_env_part(mmc, copy + 1);
300         if (ret)
301                 goto fini;
302 #endif
303
304         if (mmc_get_env_addr(mmc, copy, &offset)) {
305                 ret = CMD_RET_FAILURE;
306                 goto fini;
307         }
308
309         ret |= erase_env(mmc, CONFIG_ENV_SIZE, offset);
310 #endif
311
312 fini:
313         fini_mmc_for_env(mmc);
314         return ret;
315 }
316 #endif /* CONFIG_CMD_SAVEENV && !CONFIG_SPL_BUILD */
317
318 static inline int read_env(struct mmc *mmc, unsigned long size,
319                            unsigned long offset, const void *buffer)
320 {
321         uint blk_start, blk_cnt, n;
322         struct blk_desc *desc = mmc_get_blk_desc(mmc);
323
324         blk_start       = ALIGN(offset, mmc->read_bl_len) / mmc->read_bl_len;
325         blk_cnt         = ALIGN(size, mmc->read_bl_len) / mmc->read_bl_len;
326
327         n = blk_dread(desc, blk_start, blk_cnt, (uchar *)buffer);
328
329         return (n == blk_cnt) ? 0 : -1;
330 }
331
332 #ifdef CONFIG_ENV_OFFSET_REDUND
333 static int env_mmc_load(void)
334 {
335 #if !defined(ENV_IS_EMBEDDED)
336         struct mmc *mmc;
337         u32 offset1, offset2;
338         int read1_fail = 0, read2_fail = 0;
339         int ret;
340         int dev = mmc_get_env_dev();
341         const char *errmsg = NULL;
342
343         ALLOC_CACHE_ALIGN_BUFFER(env_t, tmp_env1, 1);
344         ALLOC_CACHE_ALIGN_BUFFER(env_t, tmp_env2, 1);
345
346         mmc_initialize(NULL);
347
348         mmc = find_mmc_device(dev);
349
350         errmsg = init_mmc_for_env(mmc);
351         if (errmsg) {
352                 ret = -EIO;
353                 goto err;
354         }
355
356         if (mmc_get_env_addr(mmc, 0, &offset1) ||
357             mmc_get_env_addr(mmc, 1, &offset2)) {
358                 ret = -EIO;
359                 goto fini;
360         }
361
362 #ifdef ENV_MMC_HWPART_REDUND
363         ret = mmc_set_env_part(mmc, 1);
364         if (ret)
365                 goto fini;
366 #endif
367
368         read1_fail = read_env(mmc, CONFIG_ENV_SIZE, offset1, tmp_env1);
369
370 #ifdef ENV_MMC_HWPART_REDUND
371         ret = mmc_set_env_part(mmc, 2);
372         if (ret)
373                 goto fini;
374 #endif
375
376         read2_fail = read_env(mmc, CONFIG_ENV_SIZE, offset2, tmp_env2);
377
378         ret = env_import_redund((char *)tmp_env1, read1_fail, (char *)tmp_env2,
379                                 read2_fail, H_EXTERNAL);
380
381 fini:
382         fini_mmc_for_env(mmc);
383 err:
384         if (ret)
385                 env_set_default(errmsg, 0);
386
387 #endif
388         return ret;
389 }
390 #else /* ! CONFIG_ENV_OFFSET_REDUND */
391 static int env_mmc_load(void)
392 {
393 #if !defined(ENV_IS_EMBEDDED)
394         ALLOC_CACHE_ALIGN_BUFFER(char, buf, CONFIG_ENV_SIZE);
395         struct mmc *mmc;
396         u32 offset;
397         int ret;
398         int dev = mmc_get_env_dev();
399         const char *errmsg;
400         env_t *ep = 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, &offset)) {
411                 ret = -EIO;
412                 goto fini;
413         }
414
415         if (read_env(mmc, CONFIG_ENV_SIZE, offset, buf)) {
416                 errmsg = "!read failed";
417                 ret = -EIO;
418                 goto fini;
419         }
420
421         ret = env_import(buf, 1, H_EXTERNAL);
422         if (!ret) {
423                 ep = (env_t *)buf;
424                 gd->env_addr = (ulong)&ep->data;
425         }
426
427 fini:
428         fini_mmc_for_env(mmc);
429 err:
430         if (ret)
431                 env_set_default(errmsg, 0);
432 #endif
433         return ret;
434 }
435 #endif /* CONFIG_ENV_OFFSET_REDUND */
436
437 U_BOOT_ENV_LOCATION(mmc) = {
438         .location       = ENVL_MMC,
439         ENV_NAME("MMC")
440         .load           = env_mmc_load,
441 #ifndef CONFIG_SPL_BUILD
442         .save           = env_save_ptr(env_mmc_save),
443         .erase          = ENV_ERASE_PTR(env_mmc_erase)
444 #endif
445 };