Merge branch 'master' of git://git.denx.de/u-boot-sunxi
[platform/kernel/u-boot.git] / common / env_mmc.c
1 /*
2  * (C) Copyright 2008-2011 Freescale Semiconductor, Inc.
3  *
4  * SPDX-License-Identifier:     GPL-2.0+
5  */
6
7 /* #define DEBUG */
8
9 #include <common.h>
10
11 #include <command.h>
12 #include <environment.h>
13 #include <linux/stddef.h>
14 #include <malloc.h>
15 #include <memalign.h>
16 #include <mmc.h>
17 #include <search.h>
18 #include <errno.h>
19
20 #if defined(CONFIG_ENV_SIZE_REDUND) &&  \
21         (CONFIG_ENV_SIZE_REDUND != CONFIG_ENV_SIZE)
22 #error CONFIG_ENV_SIZE_REDUND should be the same as CONFIG_ENV_SIZE
23 #endif
24
25 char *env_name_spec = "MMC";
26
27 #ifdef ENV_IS_EMBEDDED
28 env_t *env_ptr = &environment;
29 #else /* ! ENV_IS_EMBEDDED */
30 env_t *env_ptr;
31 #endif /* ENV_IS_EMBEDDED */
32
33 DECLARE_GLOBAL_DATA_PTR;
34
35 #if !defined(CONFIG_ENV_OFFSET)
36 #define CONFIG_ENV_OFFSET 0
37 #endif
38
39 __weak int mmc_get_env_addr(struct mmc *mmc, int copy, u32 *env_addr)
40 {
41         s64 offset;
42
43         offset = CONFIG_ENV_OFFSET;
44 #ifdef CONFIG_ENV_OFFSET_REDUND
45         if (copy)
46                 offset = CONFIG_ENV_OFFSET_REDUND;
47 #endif
48
49         if (offset < 0)
50                 offset += mmc->capacity;
51
52         *env_addr = offset;
53
54         return 0;
55 }
56
57 __weak int mmc_get_env_dev(void)
58 {
59         return CONFIG_SYS_MMC_ENV_DEV;
60 }
61
62 int env_init(void)
63 {
64         /* use default */
65         gd->env_addr    = (ulong)&default_environment[0];
66         gd->env_valid   = 1;
67
68         return 0;
69 }
70
71 #ifdef CONFIG_SYS_MMC_ENV_PART
72 __weak uint mmc_get_env_part(struct mmc *mmc)
73 {
74         return CONFIG_SYS_MMC_ENV_PART;
75 }
76
77 static unsigned char env_mmc_orig_hwpart;
78
79 static int mmc_set_env_part(struct mmc *mmc)
80 {
81         uint part = mmc_get_env_part(mmc);
82         int dev = mmc_get_env_dev();
83         int ret = 0;
84
85         env_mmc_orig_hwpart = mmc_get_blk_desc(mmc)->hwpart;
86         ret = blk_select_hwpart_devnum(IF_TYPE_MMC, dev, part);
87         if (ret)
88                 puts("MMC partition switch failed\n");
89
90         return ret;
91 }
92 #else
93 static inline int mmc_set_env_part(struct mmc *mmc) {return 0; };
94 #endif
95
96 static const char *init_mmc_for_env(struct mmc *mmc)
97 {
98         if (!mmc)
99                 return "!No MMC card found";
100
101         if (mmc_init(mmc))
102                 return "!MMC init failed";
103
104         if (mmc_set_env_part(mmc))
105                 return "!MMC partition switch failed";
106
107         return NULL;
108 }
109
110 static void fini_mmc_for_env(struct mmc *mmc)
111 {
112 #ifdef CONFIG_SYS_MMC_ENV_PART
113         int dev = mmc_get_env_dev();
114
115         blk_select_hwpart_devnum(IF_TYPE_MMC, dev, env_mmc_orig_hwpart);
116 #endif
117 }
118
119 #ifdef CONFIG_CMD_SAVEENV
120 static inline int write_env(struct mmc *mmc, unsigned long size,
121                             unsigned long offset, const void *buffer)
122 {
123         uint blk_start, blk_cnt, n;
124         struct blk_desc *desc = mmc_get_blk_desc(mmc);
125
126         blk_start       = ALIGN(offset, mmc->write_bl_len) / mmc->write_bl_len;
127         blk_cnt         = ALIGN(size, mmc->write_bl_len) / mmc->write_bl_len;
128
129         n = blk_dwrite(desc, blk_start, blk_cnt, (u_char *)buffer);
130
131         return (n == blk_cnt) ? 0 : -1;
132 }
133
134 #ifdef CONFIG_ENV_OFFSET_REDUND
135 static unsigned char env_flags;
136 #endif
137
138 int saveenv(void)
139 {
140         ALLOC_CACHE_ALIGN_BUFFER(env_t, env_new, 1);
141         int dev = mmc_get_env_dev();
142         struct mmc *mmc = find_mmc_device(dev);
143         u32     offset;
144         int     ret, copy = 0;
145         const char *errmsg;
146
147         errmsg = init_mmc_for_env(mmc);
148         if (errmsg) {
149                 printf("%s\n", errmsg);
150                 return 1;
151         }
152
153         ret = env_export(env_new);
154         if (ret)
155                 goto fini;
156
157 #ifdef CONFIG_ENV_OFFSET_REDUND
158         env_new->flags  = ++env_flags; /* increase the serial */
159
160         if (gd->env_valid == 1)
161                 copy = 1;
162 #endif
163
164         if (mmc_get_env_addr(mmc, copy, &offset)) {
165                 ret = 1;
166                 goto fini;
167         }
168
169         printf("Writing to %sMMC(%d)... ", copy ? "redundant " : "", dev);
170         if (write_env(mmc, CONFIG_ENV_SIZE, offset, (u_char *)env_new)) {
171                 puts("failed\n");
172                 ret = 1;
173                 goto fini;
174         }
175
176         puts("done\n");
177         ret = 0;
178
179 #ifdef CONFIG_ENV_OFFSET_REDUND
180         gd->env_valid = gd->env_valid == 2 ? 1 : 2;
181 #endif
182
183 fini:
184         fini_mmc_for_env(mmc);
185         return ret;
186 }
187 #endif /* CONFIG_CMD_SAVEENV */
188
189 static inline int read_env(struct mmc *mmc, unsigned long size,
190                            unsigned long offset, const void *buffer)
191 {
192         uint blk_start, blk_cnt, n;
193         struct blk_desc *desc = mmc_get_blk_desc(mmc);
194
195         blk_start       = ALIGN(offset, mmc->read_bl_len) / mmc->read_bl_len;
196         blk_cnt         = ALIGN(size, mmc->read_bl_len) / mmc->read_bl_len;
197
198         n = blk_dread(desc, blk_start, blk_cnt, (uchar *)buffer);
199
200         return (n == blk_cnt) ? 0 : -1;
201 }
202
203 #ifdef CONFIG_ENV_OFFSET_REDUND
204 void env_relocate_spec(void)
205 {
206 #if !defined(ENV_IS_EMBEDDED)
207         struct mmc *mmc;
208         u32 offset1, offset2;
209         int read1_fail = 0, read2_fail = 0;
210         int crc1_ok = 0, crc2_ok = 0;
211         env_t *ep;
212         int ret;
213         int dev = mmc_get_env_dev();
214         const char *errmsg = NULL;
215
216         ALLOC_CACHE_ALIGN_BUFFER(env_t, tmp_env1, 1);
217         ALLOC_CACHE_ALIGN_BUFFER(env_t, tmp_env2, 1);
218
219         mmc = find_mmc_device(dev);
220
221         errmsg = init_mmc_for_env(mmc);
222         if (errmsg) {
223                 ret = 1;
224                 goto err;
225         }
226
227         if (mmc_get_env_addr(mmc, 0, &offset1) ||
228             mmc_get_env_addr(mmc, 1, &offset2)) {
229                 ret = 1;
230                 goto fini;
231         }
232
233         read1_fail = read_env(mmc, CONFIG_ENV_SIZE, offset1, tmp_env1);
234         read2_fail = read_env(mmc, CONFIG_ENV_SIZE, offset2, tmp_env2);
235
236         if (read1_fail && read2_fail)
237                 puts("*** Error - No Valid Environment Area found\n");
238         else if (read1_fail || read2_fail)
239                 puts("*** Warning - some problems detected "
240                      "reading environment; recovered successfully\n");
241
242         crc1_ok = !read1_fail &&
243                 (crc32(0, tmp_env1->data, ENV_SIZE) == tmp_env1->crc);
244         crc2_ok = !read2_fail &&
245                 (crc32(0, tmp_env2->data, ENV_SIZE) == tmp_env2->crc);
246
247         if (!crc1_ok && !crc2_ok) {
248                 errmsg = "!bad CRC";
249                 ret = 1;
250                 goto fini;
251         } else if (crc1_ok && !crc2_ok) {
252                 gd->env_valid = 1;
253         } else if (!crc1_ok && crc2_ok) {
254                 gd->env_valid = 2;
255         } else {
256                 /* both ok - check serial */
257                 if (tmp_env1->flags == 255 && tmp_env2->flags == 0)
258                         gd->env_valid = 2;
259                 else if (tmp_env2->flags == 255 && tmp_env1->flags == 0)
260                         gd->env_valid = 1;
261                 else if (tmp_env1->flags > tmp_env2->flags)
262                         gd->env_valid = 1;
263                 else if (tmp_env2->flags > tmp_env1->flags)
264                         gd->env_valid = 2;
265                 else /* flags are equal - almost impossible */
266                         gd->env_valid = 1;
267         }
268
269         free(env_ptr);
270
271         if (gd->env_valid == 1)
272                 ep = tmp_env1;
273         else
274                 ep = tmp_env2;
275
276         env_flags = ep->flags;
277         env_import((char *)ep, 0);
278         ret = 0;
279
280 fini:
281         fini_mmc_for_env(mmc);
282 err:
283         if (ret)
284                 set_default_env(errmsg);
285 #endif
286 }
287 #else /* ! CONFIG_ENV_OFFSET_REDUND */
288 void env_relocate_spec(void)
289 {
290 #if !defined(ENV_IS_EMBEDDED)
291         ALLOC_CACHE_ALIGN_BUFFER(char, buf, CONFIG_ENV_SIZE);
292         struct mmc *mmc;
293         u32 offset;
294         int ret;
295         int dev = mmc_get_env_dev();
296         const char *errmsg;
297
298         mmc = find_mmc_device(dev);
299
300         errmsg = init_mmc_for_env(mmc);
301         if (errmsg) {
302                 ret = 1;
303                 goto err;
304         }
305
306         if (mmc_get_env_addr(mmc, 0, &offset)) {
307                 ret = 1;
308                 goto fini;
309         }
310
311         if (read_env(mmc, CONFIG_ENV_SIZE, offset, buf)) {
312                 errmsg = "!read failed";
313                 ret = 1;
314                 goto fini;
315         }
316
317         env_import(buf, 1);
318         ret = 0;
319
320 fini:
321         fini_mmc_for_env(mmc);
322 err:
323         if (ret)
324                 set_default_env(errmsg);
325 #endif
326 }
327 #endif /* CONFIG_ENV_OFFSET_REDUND */