blob: f47bd777256f0bdee6252d69eaa940010620f1d0 [file] [log] [blame]
Terry Lva8060352010-05-17 10:57:01 +08001/*
Mingkai Hu97039ab2011-01-24 17:09:55 +00002 * (C) Copyright 2008-2011 Freescale Semiconductor, Inc.
Terry Lva8060352010-05-17 10:57:01 +08003 *
Wolfgang Denk3765b3e2013-10-07 13:07:26 +02004 * SPDX-License-Identifier: GPL-2.0+
Terry Lva8060352010-05-17 10:57:01 +08005 */
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>
Lei Wen6d1d51b2010-11-10 07:39:23 +080016#include <search.h>
Lei Wene79f4832010-10-13 11:07:21 +080017#include <errno.h>
Terry Lva8060352010-05-17 10:57:01 +080018
Michael Heimpoldd196bd82013-04-10 10:36:19 +000019#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
Terry Lva8060352010-05-17 10:57:01 +080024char *env_name_spec = "MMC";
25
26#ifdef ENV_IS_EMBEDDED
Igor Grinberg994bc672011-11-17 06:07:23 +000027env_t *env_ptr = &environment;
Terry Lva8060352010-05-17 10:57:01 +080028#else /* ! ENV_IS_EMBEDDED */
Igor Grinberge8db8f72011-11-07 01:14:05 +000029env_t *env_ptr;
Terry Lva8060352010-05-17 10:57:01 +080030#endif /* ENV_IS_EMBEDDED */
31
Terry Lva8060352010-05-17 10:57:01 +080032DECLARE_GLOBAL_DATA_PTR;
33
Mingkai Hu97039ab2011-01-24 17:09:55 +000034#if !defined(CONFIG_ENV_OFFSET)
35#define CONFIG_ENV_OFFSET 0
36#endif
37
Michael Heimpoldd196bd82013-04-10 10:36:19 +000038__weak int mmc_get_env_addr(struct mmc *mmc, int copy, u32 *env_addr)
Mingkai Hu97039ab2011-01-24 17:09:55 +000039{
Stephen Warren5c088ee2013-06-11 15:14:02 -060040 s64 offset;
41
42 offset = CONFIG_ENV_OFFSET;
Michael Heimpoldd196bd82013-04-10 10:36:19 +000043#ifdef CONFIG_ENV_OFFSET_REDUND
44 if (copy)
Stephen Warren5c088ee2013-06-11 15:14:02 -060045 offset = CONFIG_ENV_OFFSET_REDUND;
Michael Heimpoldd196bd82013-04-10 10:36:19 +000046#endif
Stephen Warren5c088ee2013-06-11 15:14:02 -060047
48 if (offset < 0)
49 offset += mmc->capacity;
50
51 *env_addr = offset;
52
Mingkai Hu97039ab2011-01-24 17:09:55 +000053 return 0;
54}
Mingkai Hu97039ab2011-01-24 17:09:55 +000055
Terry Lva8060352010-05-17 10:57:01 +080056int env_init(void)
57{
58 /* use default */
Igor Grinberge8db8f72011-11-07 01:14:05 +000059 gd->env_addr = (ulong)&default_environment[0];
60 gd->env_valid = 1;
Terry Lva8060352010-05-17 10:57:01 +080061
62 return 0;
63}
64
Igor Grinberge8db8f72011-11-07 01:14:05 +000065static int init_mmc_for_env(struct mmc *mmc)
Terry Lva8060352010-05-17 10:57:01 +080066{
Tom Rinib9c8cca2014-03-28 12:03:34 -040067#ifdef CONFIG_SYS_MMC_ENV_PART
68 int dev = CONFIG_SYS_MMC_ENV_DEV;
69
70#ifdef CONFIG_SPL_BUILD
71 dev = 0;
72#endif
73#endif
74
Terry Lva8060352010-05-17 10:57:01 +080075 if (!mmc) {
76 puts("No MMC card found\n");
77 return -1;
78 }
79
80 if (mmc_init(mmc)) {
81 puts("MMC init failed\n");
Igor Grinberge8db8f72011-11-07 01:14:05 +000082 return -1;
Terry Lva8060352010-05-17 10:57:01 +080083 }
84
Stephen Warren9404a5f2012-07-30 10:55:44 +000085#ifdef CONFIG_SYS_MMC_ENV_PART
86 if (CONFIG_SYS_MMC_ENV_PART != mmc->part_num) {
Tom Rinib9c8cca2014-03-28 12:03:34 -040087 if (mmc_switch_part(dev, CONFIG_SYS_MMC_ENV_PART)) {
Stephen Warren9404a5f2012-07-30 10:55:44 +000088 puts("MMC partition switch failed\n");
89 return -1;
90 }
91 }
92#endif
93
Terry Lva8060352010-05-17 10:57:01 +080094 return 0;
95}
96
Stephen Warren9404a5f2012-07-30 10:55:44 +000097static void fini_mmc_for_env(struct mmc *mmc)
98{
99#ifdef CONFIG_SYS_MMC_ENV_PART
Tom Rinib9c8cca2014-03-28 12:03:34 -0400100 int dev = CONFIG_SYS_MMC_ENV_DEV;
101
102#ifdef CONFIG_SPL_BUILD
103 dev = 0;
104#endif
Stephen Warren9404a5f2012-07-30 10:55:44 +0000105 if (CONFIG_SYS_MMC_ENV_PART != mmc->part_num)
Tom Rinib9c8cca2014-03-28 12:03:34 -0400106 mmc_switch_part(dev, mmc->part_num);
Stephen Warren9404a5f2012-07-30 10:55:44 +0000107#endif
108}
109
Terry Lva8060352010-05-17 10:57:01 +0800110#ifdef CONFIG_CMD_SAVEENV
Igor Grinberge8db8f72011-11-07 01:14:05 +0000111static inline int write_env(struct mmc *mmc, unsigned long size,
112 unsigned long offset, const void *buffer)
Terry Lva8060352010-05-17 10:57:01 +0800113{
114 uint blk_start, blk_cnt, n;
115
Igor Grinberge8db8f72011-11-07 01:14:05 +0000116 blk_start = ALIGN(offset, mmc->write_bl_len) / mmc->write_bl_len;
117 blk_cnt = ALIGN(size, mmc->write_bl_len) / mmc->write_bl_len;
Terry Lva8060352010-05-17 10:57:01 +0800118
119 n = mmc->block_dev.block_write(CONFIG_SYS_MMC_ENV_DEV, blk_start,
120 blk_cnt, (u_char *)buffer);
121
122 return (n == blk_cnt) ? 0 : -1;
123}
124
Michael Heimpoldd196bd82013-04-10 10:36:19 +0000125#ifdef CONFIG_ENV_OFFSET_REDUND
126static unsigned char env_flags;
127#endif
128
Terry Lva8060352010-05-17 10:57:01 +0800129int saveenv(void)
130{
Tom Rinicd0f4fa2013-04-05 14:55:21 -0400131 ALLOC_CACHE_ALIGN_BUFFER(env_t, env_new, 1);
Lei Wene79f4832010-10-13 11:07:21 +0800132 ssize_t len;
133 char *res;
Terry Lva8060352010-05-17 10:57:01 +0800134 struct mmc *mmc = find_mmc_device(CONFIG_SYS_MMC_ENV_DEV);
Igor Grinberge8db8f72011-11-07 01:14:05 +0000135 u32 offset;
Michael Heimpoldd196bd82013-04-10 10:36:19 +0000136 int ret, copy = 0;
Terry Lva8060352010-05-17 10:57:01 +0800137
Stephen Warren9404a5f2012-07-30 10:55:44 +0000138 if (init_mmc_for_env(mmc))
Mingkai Hu97039ab2011-01-24 17:09:55 +0000139 return 1;
140
Tom Rinicd0f4fa2013-04-05 14:55:21 -0400141 res = (char *)&env_new->data;
Joe Hershbergerbe112352012-12-11 22:16:23 -0600142 len = hexport_r(&env_htab, '\0', 0, &res, ENV_SIZE, 0, NULL);
Lei Wene79f4832010-10-13 11:07:21 +0800143 if (len < 0) {
144 error("Cannot export environment: errno = %d\n", errno);
Stephen Warren9404a5f2012-07-30 10:55:44 +0000145 ret = 1;
146 goto fini;
Lei Wene79f4832010-10-13 11:07:21 +0800147 }
Igor Grinberge8db8f72011-11-07 01:14:05 +0000148
Tom Rinicd0f4fa2013-04-05 14:55:21 -0400149 env_new->crc = crc32(0, &env_new->data[0], ENV_SIZE);
Michael Heimpoldd196bd82013-04-10 10:36:19 +0000150
151#ifdef CONFIG_ENV_OFFSET_REDUND
152 env_new->flags = ++env_flags; /* increase the serial */
153
154 if (gd->env_valid == 1)
155 copy = 1;
156#endif
157
158 if (mmc_get_env_addr(mmc, copy, &offset)) {
159 ret = 1;
160 goto fini;
161 }
162
163 printf("Writing to %sMMC(%d)... ", copy ? "redundant " : "",
164 CONFIG_SYS_MMC_ENV_DEV);
Stephen Warren4036b632012-05-24 11:38:33 +0000165 if (write_env(mmc, CONFIG_ENV_SIZE, offset, (u_char *)env_new)) {
Terry Lva8060352010-05-17 10:57:01 +0800166 puts("failed\n");
Stephen Warren9404a5f2012-07-30 10:55:44 +0000167 ret = 1;
168 goto fini;
Terry Lva8060352010-05-17 10:57:01 +0800169 }
170
171 puts("done\n");
Stephen Warren9404a5f2012-07-30 10:55:44 +0000172 ret = 0;
173
Michael Heimpoldd196bd82013-04-10 10:36:19 +0000174#ifdef CONFIG_ENV_OFFSET_REDUND
175 gd->env_valid = gd->env_valid == 2 ? 1 : 2;
176#endif
177
Stephen Warren9404a5f2012-07-30 10:55:44 +0000178fini:
179 fini_mmc_for_env(mmc);
180 return ret;
Terry Lva8060352010-05-17 10:57:01 +0800181}
182#endif /* CONFIG_CMD_SAVEENV */
183
Igor Grinberge8db8f72011-11-07 01:14:05 +0000184static inline int read_env(struct mmc *mmc, unsigned long size,
185 unsigned long offset, const void *buffer)
Terry Lva8060352010-05-17 10:57:01 +0800186{
187 uint blk_start, blk_cnt, n;
Tom Rinib9c8cca2014-03-28 12:03:34 -0400188 int dev = CONFIG_SYS_MMC_ENV_DEV;
189
190#ifdef CONFIG_SPL_BUILD
191 dev = 0;
192#endif
Terry Lva8060352010-05-17 10:57:01 +0800193
Igor Grinberge8db8f72011-11-07 01:14:05 +0000194 blk_start = ALIGN(offset, mmc->read_bl_len) / mmc->read_bl_len;
195 blk_cnt = ALIGN(size, mmc->read_bl_len) / mmc->read_bl_len;
Terry Lva8060352010-05-17 10:57:01 +0800196
Tom Rinib9c8cca2014-03-28 12:03:34 -0400197 n = mmc->block_dev.block_read(dev, blk_start, blk_cnt, (uchar *)buffer);
Terry Lva8060352010-05-17 10:57:01 +0800198
199 return (n == blk_cnt) ? 0 : -1;
200}
201
Michael Heimpoldd196bd82013-04-10 10:36:19 +0000202#ifdef CONFIG_ENV_OFFSET_REDUND
203void env_relocate_spec(void)
204{
205#if !defined(ENV_IS_EMBEDDED)
Tom Rinib9c8cca2014-03-28 12:03:34 -0400206 struct mmc *mmc;
Michael Heimpoldd196bd82013-04-10 10:36:19 +0000207 u32 offset1, offset2;
208 int read1_fail = 0, read2_fail = 0;
209 int crc1_ok = 0, crc2_ok = 0;
Markus Niebel452a2722013-10-04 15:48:03 +0200210 env_t *ep;
Michael Heimpoldd196bd82013-04-10 10:36:19 +0000211 int ret;
Tom Rinib9c8cca2014-03-28 12:03:34 -0400212 int dev = CONFIG_SYS_MMC_ENV_DEV;
Michael Heimpoldd196bd82013-04-10 10:36:19 +0000213
Markus Niebel452a2722013-10-04 15:48:03 +0200214 ALLOC_CACHE_ALIGN_BUFFER(env_t, tmp_env1, 1);
215 ALLOC_CACHE_ALIGN_BUFFER(env_t, tmp_env2, 1);
216
Tom Rinib9c8cca2014-03-28 12:03:34 -0400217#ifdef CONFIG_SPL_BUILD
218 dev = 0;
219#endif
220
221 mmc = find_mmc_device(dev);
222
Michael Heimpoldd196bd82013-04-10 10:36:19 +0000223 if (init_mmc_for_env(mmc)) {
224 ret = 1;
225 goto err;
226 }
227
228 if (mmc_get_env_addr(mmc, 0, &offset1) ||
229 mmc_get_env_addr(mmc, 1, &offset2)) {
230 ret = 1;
231 goto fini;
232 }
233
234 read1_fail = read_env(mmc, CONFIG_ENV_SIZE, offset1, tmp_env1);
235 read2_fail = read_env(mmc, CONFIG_ENV_SIZE, offset2, tmp_env2);
236
237 if (read1_fail && read2_fail)
238 puts("*** Error - No Valid Environment Area found\n");
239 else if (read1_fail || read2_fail)
240 puts("*** Warning - some problems detected "
241 "reading environment; recovered successfully\n");
242
243 crc1_ok = !read1_fail &&
244 (crc32(0, tmp_env1->data, ENV_SIZE) == tmp_env1->crc);
245 crc2_ok = !read2_fail &&
246 (crc32(0, tmp_env2->data, ENV_SIZE) == tmp_env2->crc);
247
248 if (!crc1_ok && !crc2_ok) {
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
280fini:
281 fini_mmc_for_env(mmc);
282err:
283 if (ret)
284 set_default_env(NULL);
285
Michael Heimpoldd196bd82013-04-10 10:36:19 +0000286#endif
287}
288#else /* ! CONFIG_ENV_OFFSET_REDUND */
Terry Lva8060352010-05-17 10:57:01 +0800289void env_relocate_spec(void)
290{
291#if !defined(ENV_IS_EMBEDDED)
Tom Rinicd0f4fa2013-04-05 14:55:21 -0400292 ALLOC_CACHE_ALIGN_BUFFER(char, buf, CONFIG_ENV_SIZE);
Tom Rinib9c8cca2014-03-28 12:03:34 -0400293 struct mmc *mmc;
Mingkai Hu97039ab2011-01-24 17:09:55 +0000294 u32 offset;
Stephen Warren9404a5f2012-07-30 10:55:44 +0000295 int ret;
Tom Rinib9c8cca2014-03-28 12:03:34 -0400296 int dev = CONFIG_SYS_MMC_ENV_DEV;
297
298#ifdef CONFIG_SPL_BUILD
299 dev = 0;
300#endif
301
302 mmc = find_mmc_device(dev);
Terry Lva8060352010-05-17 10:57:01 +0800303
Stephen Warren9404a5f2012-07-30 10:55:44 +0000304 if (init_mmc_for_env(mmc)) {
305 ret = 1;
306 goto err;
307 }
Terry Lva8060352010-05-17 10:57:01 +0800308
Michael Heimpoldd196bd82013-04-10 10:36:19 +0000309 if (mmc_get_env_addr(mmc, 0, &offset)) {
Stephen Warren9404a5f2012-07-30 10:55:44 +0000310 ret = 1;
311 goto fini;
312 }
313
Tom Rinicd0f4fa2013-04-05 14:55:21 -0400314 if (read_env(mmc, CONFIG_ENV_SIZE, offset, buf)) {
Stephen Warren9404a5f2012-07-30 10:55:44 +0000315 ret = 1;
316 goto fini;
317 }
Terry Lva8060352010-05-17 10:57:01 +0800318
Tom Rinicd0f4fa2013-04-05 14:55:21 -0400319 env_import(buf, 1);
Stephen Warren9404a5f2012-07-30 10:55:44 +0000320 ret = 0;
321
322fini:
323 fini_mmc_for_env(mmc);
324err:
325 if (ret)
326 set_default_env(NULL);
Terry Lva8060352010-05-17 10:57:01 +0800327#endif
328}
Michael Heimpoldd196bd82013-04-10 10:36:19 +0000329#endif /* CONFIG_ENV_OFFSET_REDUND */