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