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