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