]>
Commit | Line | Data |
---|---|---|
13a5695b | 1 | /* |
ea882baf WD |
2 | * (C) Copyright 2000-2010 |
3 | * Wolfgang Denk, DENX Software Engineering, wd@denx.de. | |
4 | * | |
cc49cade SW |
5 | * (C) Copyright 2008 |
6 | * Stuart Wood, Lab X Technologies <stuart.wood@labxtechnologies.com> | |
7 | * | |
13a5695b WD |
8 | * (C) Copyright 2004 |
9 | * Jian Zhang, Texas Instruments, jzhang@ti.com. | |
13a5695b WD |
10 | * |
11 | * (C) Copyright 2001 Sysgo Real-Time Solutions, GmbH <www.elinos.com> | |
12 | * Andreas Heppel <aheppel@sysgo.de> | |
ea882baf | 13 | * |
13a5695b WD |
14 | * See file CREDITS for list of people who contributed to this |
15 | * project. | |
16 | * | |
17 | * This program is free software; you can redistribute it and/or | |
18 | * modify it under the terms of the GNU General Public License as | |
19 | * published by the Free Software Foundation; either version 2 of | |
20 | * the License, or (at your option) any later version. | |
21 | * | |
22 | * This program is distributed in the hope that it will be useful, | |
23 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | |
24 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | |
25 | * GNU General Public License for more details. | |
26 | * | |
27 | * You should have received a copy of the GNU General Public License | |
28 | * along with this program; if not, write to the Free Software | |
29 | * Foundation, Inc., 59 Temple Place, Suite 330, Boston, | |
30 | * MA 02111-1307 USA | |
31 | */ | |
32 | ||
ea882baf | 33 | #define DEBUG |
13a5695b WD |
34 | |
35 | #include <common.h> | |
13a5695b WD |
36 | #include <command.h> |
37 | #include <environment.h> | |
38 | #include <linux/stddef.h> | |
e443c944 | 39 | #include <malloc.h> |
addb2e16 | 40 | #include <nand.h> |
ea882baf WD |
41 | #include <search.h> |
42 | #include <errno.h> | |
13a5695b | 43 | |
bdab39d3 | 44 | #if defined(CONFIG_CMD_SAVEENV) && defined(CONFIG_CMD_NAND) |
13a5695b | 45 | #define CMD_SAVEENV |
0e8d1586 | 46 | #elif defined(CONFIG_ENV_OFFSET_REDUND) |
bdab39d3 | 47 | #error Cannot use CONFIG_ENV_OFFSET_REDUND without CONFIG_CMD_SAVEENV & CONFIG_CMD_NAND |
13a5695b WD |
48 | #endif |
49 | ||
0e8d1586 JCPV |
50 | #if defined(CONFIG_ENV_SIZE_REDUND) && (CONFIG_ENV_SIZE_REDUND != CONFIG_ENV_SIZE) |
51 | #error CONFIG_ENV_SIZE_REDUND should be the same as CONFIG_ENV_SIZE | |
13a5695b WD |
52 | #endif |
53 | ||
0e8d1586 JCPV |
54 | #ifndef CONFIG_ENV_RANGE |
55 | #define CONFIG_ENV_RANGE CONFIG_ENV_SIZE | |
cc49cade SW |
56 | #endif |
57 | ||
13a5695b WD |
58 | /* references to names in env_common.c */ |
59 | extern uchar default_environment[]; | |
13a5695b | 60 | |
ea882baf | 61 | char *env_name_spec = "NAND"; |
13a5695b WD |
62 | |
63 | ||
b74ab737 | 64 | #if defined(ENV_IS_EMBEDDED) |
13a5695b WD |
65 | extern uchar environment[]; |
66 | env_t *env_ptr = (env_t *)(&environment[0]); | |
b74ab737 GL |
67 | #elif defined(CONFIG_NAND_ENV_DST) |
68 | env_t *env_ptr = (env_t *)CONFIG_NAND_ENV_DST; | |
13a5695b | 69 | #else /* ! ENV_IS_EMBEDDED */ |
49822e23 | 70 | env_t *env_ptr = 0; |
13a5695b WD |
71 | #endif /* ENV_IS_EMBEDDED */ |
72 | ||
d87080b7 | 73 | DECLARE_GLOBAL_DATA_PTR; |
13a5695b WD |
74 | |
75 | uchar env_get_char_spec (int index) | |
76 | { | |
13a5695b WD |
77 | return ( *((uchar *)(gd->env_addr + index)) ); |
78 | } | |
79 | ||
ea882baf WD |
80 | /* |
81 | * This is called before nand_init() so we can't read NAND to | |
82 | * validate env data. | |
83 | * | |
84 | * Mark it OK for now. env_relocate() in env_common.c will call our | |
85 | * relocate function which does the real validation. | |
d12ae808 SR |
86 | * |
87 | * When using a NAND boot image (like sequoia_nand), the environment | |
ea882baf WD |
88 | * can be embedded or attached to the U-Boot image in NAND flash. |
89 | * This way the SPL loads not only the U-Boot image from NAND but | |
90 | * also the environment. | |
13a5695b WD |
91 | */ |
92 | int env_init(void) | |
93 | { | |
b74ab737 | 94 | #if defined(ENV_IS_EMBEDDED) || defined(CONFIG_NAND_ENV_DST) |
d12ae808 | 95 | int crc1_ok = 0, crc2_ok = 0; |
b74ab737 GL |
96 | env_t *tmp_env1; |
97 | ||
98 | #ifdef CONFIG_ENV_OFFSET_REDUND | |
99 | env_t *tmp_env2; | |
d12ae808 | 100 | |
0e8d1586 | 101 | tmp_env2 = (env_t *)((ulong)env_ptr + CONFIG_ENV_SIZE); |
b74ab737 GL |
102 | crc2_ok = (crc32(0, tmp_env2->data, ENV_SIZE) == tmp_env2->crc); |
103 | #endif | |
104 | ||
105 | tmp_env1 = env_ptr; | |
d12ae808 SR |
106 | |
107 | crc1_ok = (crc32(0, tmp_env1->data, ENV_SIZE) == tmp_env1->crc); | |
d12ae808 | 108 | |
b74ab737 GL |
109 | if (!crc1_ok && !crc2_ok) { |
110 | gd->env_addr = 0; | |
d12ae808 | 111 | gd->env_valid = 0; |
b74ab737 GL |
112 | |
113 | return 0; | |
114 | } else if (crc1_ok && !crc2_ok) { | |
d12ae808 | 115 | gd->env_valid = 1; |
b74ab737 GL |
116 | } |
117 | #ifdef CONFIG_ENV_OFFSET_REDUND | |
118 | else if (!crc1_ok && crc2_ok) { | |
d12ae808 | 119 | gd->env_valid = 2; |
b74ab737 | 120 | } else { |
d12ae808 SR |
121 | /* both ok - check serial */ |
122 | if(tmp_env1->flags == 255 && tmp_env2->flags == 0) | |
123 | gd->env_valid = 2; | |
124 | else if(tmp_env2->flags == 255 && tmp_env1->flags == 0) | |
125 | gd->env_valid = 1; | |
126 | else if(tmp_env1->flags > tmp_env2->flags) | |
127 | gd->env_valid = 1; | |
128 | else if(tmp_env2->flags > tmp_env1->flags) | |
129 | gd->env_valid = 2; | |
130 | else /* flags are equal - almost impossible */ | |
131 | gd->env_valid = 1; | |
132 | } | |
133 | ||
b74ab737 GL |
134 | if (gd->env_valid == 2) |
135 | env_ptr = tmp_env2; | |
136 | else | |
137 | #endif | |
d12ae808 SR |
138 | if (gd->env_valid == 1) |
139 | env_ptr = tmp_env1; | |
b74ab737 GL |
140 | |
141 | gd->env_addr = (ulong)env_ptr->data; | |
142 | ||
143 | #else /* ENV_IS_EMBEDDED || CONFIG_NAND_ENV_DST */ | |
e443c944 | 144 | gd->env_addr = (ulong)&default_environment[0]; |
13a5695b | 145 | gd->env_valid = 1; |
b74ab737 | 146 | #endif /* ENV_IS_EMBEDDED || CONFIG_NAND_ENV_DST */ |
13a5695b WD |
147 | |
148 | return (0); | |
149 | } | |
150 | ||
151 | #ifdef CMD_SAVEENV | |
addb2e16 BS |
152 | /* |
153 | * The legacy NAND code saved the environment in the first NAND device i.e., | |
154 | * nand_dev_desc + 0. This is also the behaviour using the new NAND code. | |
155 | */ | |
cc49cade SW |
156 | int writeenv(size_t offset, u_char *buf) |
157 | { | |
0e8d1586 | 158 | size_t end = offset + CONFIG_ENV_RANGE; |
cc49cade | 159 | size_t amount_saved = 0; |
c3db8c64 | 160 | size_t blocksize, len; |
cc49cade SW |
161 | |
162 | u_char *char_ptr; | |
163 | ||
164 | blocksize = nand_info[0].erasesize; | |
0e8d1586 | 165 | len = min(blocksize, CONFIG_ENV_SIZE); |
cc49cade | 166 | |
0e8d1586 | 167 | while (amount_saved < CONFIG_ENV_SIZE && offset < end) { |
cc49cade SW |
168 | if (nand_block_isbad(&nand_info[0], offset)) { |
169 | offset += blocksize; | |
170 | } else { | |
171 | char_ptr = &buf[amount_saved]; | |
c3db8c64 | 172 | if (nand_write(&nand_info[0], offset, &len, |
cc49cade SW |
173 | char_ptr)) |
174 | return 1; | |
175 | offset += blocksize; | |
c3db8c64 | 176 | amount_saved += len; |
cc49cade SW |
177 | } |
178 | } | |
0e8d1586 | 179 | if (amount_saved != CONFIG_ENV_SIZE) |
cc49cade SW |
180 | return 1; |
181 | ||
182 | return 0; | |
183 | } | |
0e8d1586 | 184 | #ifdef CONFIG_ENV_OFFSET_REDUND |
13a5695b WD |
185 | int saveenv(void) |
186 | { | |
ea882baf WD |
187 | env_t env_new; |
188 | ssize_t len; | |
189 | char *res; | |
190 | int ret = 0; | |
cc49cade | 191 | nand_erase_options_t nand_erase_options; |
e443c944 | 192 | |
0e8d1586 | 193 | nand_erase_options.length = CONFIG_ENV_RANGE; |
cc49cade SW |
194 | nand_erase_options.quiet = 0; |
195 | nand_erase_options.jffs2 = 0; | |
196 | nand_erase_options.scrub = 0; | |
197 | ||
0e8d1586 | 198 | if (CONFIG_ENV_RANGE < CONFIG_ENV_SIZE) |
cc49cade | 199 | return 1; |
ea882baf WD |
200 | |
201 | res = (char *)&env_new.data; | |
2eb1573f | 202 | len = hexport_r(&env_htab, '\0', &res, ENV_SIZE); |
ea882baf WD |
203 | if (len < 0) { |
204 | error("Cannot export environment: errno = %d\n", errno); | |
205 | return 1; | |
206 | } | |
207 | env_new.crc = crc32(0, env_new.data, ENV_SIZE); | |
920a5dd2 | 208 | ++env_new.flags; /* increase the serial */ |
ea882baf | 209 | |
e443c944 | 210 | if(gd->env_valid == 1) { |
ea882baf | 211 | puts("Erasing redundant NAND...\n"); |
0e8d1586 | 212 | nand_erase_options.offset = CONFIG_ENV_OFFSET_REDUND; |
cc49cade | 213 | if (nand_erase_opts(&nand_info[0], &nand_erase_options)) |
e443c944 | 214 | return 1; |
cc49cade | 215 | |
ea882baf WD |
216 | puts("Writing to redundant NAND... "); |
217 | ret = writeenv(CONFIG_ENV_OFFSET_REDUND, | |
218 | (u_char *)&env_new); | |
e443c944 | 219 | } else { |
ea882baf | 220 | puts("Erasing NAND...\n"); |
0e8d1586 | 221 | nand_erase_options.offset = CONFIG_ENV_OFFSET; |
cc49cade | 222 | if (nand_erase_opts(&nand_info[0], &nand_erase_options)) |
e443c944 MK |
223 | return 1; |
224 | ||
ea882baf WD |
225 | puts("Writing to NAND... "); |
226 | ret = writeenv(CONFIG_ENV_OFFSET, | |
227 | (u_char *)&env_new); | |
e443c944 | 228 | } |
cc49cade SW |
229 | if (ret) { |
230 | puts("FAILED!\n"); | |
e443c944 | 231 | return 1; |
cc49cade | 232 | } |
e443c944 | 233 | |
ea882baf WD |
234 | puts("done\n"); |
235 | ||
e443c944 | 236 | gd->env_valid = (gd->env_valid == 2 ? 1 : 2); |
ea882baf | 237 | |
e443c944 MK |
238 | return ret; |
239 | } | |
0e8d1586 | 240 | #else /* ! CONFIG_ENV_OFFSET_REDUND */ |
e443c944 MK |
241 | int saveenv(void) |
242 | { | |
d52fb7e3 | 243 | int ret = 0; |
ea882baf WD |
244 | env_t env_new; |
245 | ssize_t len; | |
246 | char *res; | |
9e4006bc | 247 | nand_erase_options_t nand_erase_options; |
e093a247 | 248 | |
0e8d1586 | 249 | nand_erase_options.length = CONFIG_ENV_RANGE; |
cc49cade SW |
250 | nand_erase_options.quiet = 0; |
251 | nand_erase_options.jffs2 = 0; | |
252 | nand_erase_options.scrub = 0; | |
0e8d1586 | 253 | nand_erase_options.offset = CONFIG_ENV_OFFSET; |
cc49cade | 254 | |
0e8d1586 | 255 | if (CONFIG_ENV_RANGE < CONFIG_ENV_SIZE) |
cc49cade | 256 | return 1; |
ea882baf WD |
257 | |
258 | res = (char *)&env_new.data; | |
2eb1573f | 259 | len = hexport_r(&env_htab, '\0', &res, ENV_SIZE); |
ea882baf WD |
260 | if (len < 0) { |
261 | error("Cannot export environment: errno = %d\n", errno); | |
262 | return 1; | |
263 | } | |
264 | env_new.crc = crc32(0, env_new.data, ENV_SIZE); | |
265 | ||
266 | puts("Erasing Nand...\n"); | |
cc49cade | 267 | if (nand_erase_opts(&nand_info[0], &nand_erase_options)) |
addb2e16 | 268 | return 1; |
13a5695b | 269 | |
ea882baf WD |
270 | puts("Writing to Nand... "); |
271 | if (writeenv(CONFIG_ENV_OFFSET, (u_char *)&env_new)) { | |
cc49cade | 272 | puts("FAILED!\n"); |
13a5695b | 273 | return 1; |
cc49cade | 274 | } |
13a5695b | 275 | |
ea882baf | 276 | puts("done\n"); |
addb2e16 | 277 | return ret; |
13a5695b | 278 | } |
0e8d1586 | 279 | #endif /* CONFIG_ENV_OFFSET_REDUND */ |
13a5695b WD |
280 | #endif /* CMD_SAVEENV */ |
281 | ||
ea882baf | 282 | int readenv(size_t offset, u_char * buf) |
cc49cade | 283 | { |
0e8d1586 | 284 | size_t end = offset + CONFIG_ENV_RANGE; |
cc49cade | 285 | size_t amount_loaded = 0; |
c3db8c64 | 286 | size_t blocksize, len; |
cc49cade SW |
287 | |
288 | u_char *char_ptr; | |
289 | ||
290 | blocksize = nand_info[0].erasesize; | |
962ad59e MF |
291 | if (!blocksize) |
292 | return 1; | |
0e8d1586 | 293 | len = min(blocksize, CONFIG_ENV_SIZE); |
cc49cade | 294 | |
0e8d1586 | 295 | while (amount_loaded < CONFIG_ENV_SIZE && offset < end) { |
cc49cade SW |
296 | if (nand_block_isbad(&nand_info[0], offset)) { |
297 | offset += blocksize; | |
298 | } else { | |
299 | char_ptr = &buf[amount_loaded]; | |
068a208b | 300 | if (nand_read_skip_bad(&nand_info[0], offset, &len, char_ptr)) |
cc49cade SW |
301 | return 1; |
302 | offset += blocksize; | |
c3db8c64 | 303 | amount_loaded += len; |
cc49cade SW |
304 | } |
305 | } | |
0e8d1586 | 306 | if (amount_loaded != CONFIG_ENV_SIZE) |
cc49cade SW |
307 | return 1; |
308 | ||
309 | return 0; | |
310 | } | |
311 | ||
c9f7351b BG |
312 | #ifdef CONFIG_ENV_OFFSET_OOB |
313 | int get_nand_env_oob(nand_info_t *nand, unsigned long *result) | |
314 | { | |
315 | struct mtd_oob_ops ops; | |
316 | uint32_t oob_buf[ENV_OFFSET_SIZE/sizeof(uint32_t)]; | |
317 | int ret; | |
318 | ||
319 | ops.datbuf = NULL; | |
320 | ops.mode = MTD_OOB_AUTO; | |
321 | ops.ooboffs = 0; | |
322 | ops.ooblen = ENV_OFFSET_SIZE; | |
323 | ops.oobbuf = (void *) oob_buf; | |
324 | ||
325 | ret = nand->read_oob(nand, ENV_OFFSET_SIZE, &ops); | |
53504a27 SW |
326 | if (ret) { |
327 | printf("error reading OOB block 0\n"); | |
328 | return ret; | |
329 | } | |
c9f7351b | 330 | |
53504a27 SW |
331 | if (oob_buf[0] == ENV_OOB_MARKER) { |
332 | *result = oob_buf[1] * nand->erasesize; | |
333 | } else if (oob_buf[0] == ENV_OOB_MARKER_OLD) { | |
334 | *result = oob_buf[1]; | |
c9f7351b | 335 | } else { |
53504a27 SW |
336 | printf("No dynamic environment marker in OOB block 0\n"); |
337 | return -ENOENT; | |
c9f7351b | 338 | } |
53504a27 SW |
339 | |
340 | return 0; | |
c9f7351b BG |
341 | } |
342 | #endif | |
343 | ||
0e8d1586 | 344 | #ifdef CONFIG_ENV_OFFSET_REDUND |
ea882baf | 345 | void env_relocate_spec(void) |
e443c944 MK |
346 | { |
347 | #if !defined(ENV_IS_EMBEDDED) | |
2770bcb2 | 348 | int crc1_ok = 0, crc2_ok = 0; |
ea882baf | 349 | env_t *ep, *tmp_env1, *tmp_env2; |
e443c944 | 350 | |
ea882baf WD |
351 | tmp_env1 = (env_t *)malloc(CONFIG_ENV_SIZE); |
352 | tmp_env2 = (env_t *)malloc(CONFIG_ENV_SIZE); | |
e443c944 | 353 | |
15b86c3d WD |
354 | if ((tmp_env1 == NULL) || (tmp_env2 == NULL)) { |
355 | puts("Can't allocate buffers for environment\n"); | |
ea882baf WD |
356 | free(tmp_env1); |
357 | free(tmp_env2); | |
358 | set_default_env("!malloc() failed"); | |
359 | return; | |
15b86c3d WD |
360 | } |
361 | ||
0e8d1586 | 362 | if (readenv(CONFIG_ENV_OFFSET, (u_char *) tmp_env1)) |
ea882baf WD |
363 | puts("No Valid Environment Area found\n"); |
364 | ||
0e8d1586 | 365 | if (readenv(CONFIG_ENV_OFFSET_REDUND, (u_char *) tmp_env2)) |
ea882baf | 366 | puts("No Valid Redundant Environment Area found\n"); |
e443c944 MK |
367 | |
368 | crc1_ok = (crc32(0, tmp_env1->data, ENV_SIZE) == tmp_env1->crc); | |
369 | crc2_ok = (crc32(0, tmp_env2->data, ENV_SIZE) == tmp_env2->crc); | |
370 | ||
ea882baf | 371 | if (!crc1_ok && !crc2_ok) { |
5a9427dc | 372 | free(tmp_env1); |
373 | free(tmp_env2); | |
ea882baf WD |
374 | set_default_env("!bad CRC"); |
375 | return; | |
376 | } else if (crc1_ok && !crc2_ok) { | |
e443c944 | 377 | gd->env_valid = 1; |
ea882baf | 378 | } else if (!crc1_ok && crc2_ok) { |
e443c944 | 379 | gd->env_valid = 2; |
ea882baf | 380 | } else { |
e443c944 | 381 | /* both ok - check serial */ |
ea882baf | 382 | if (tmp_env1->flags == 255 && tmp_env2->flags == 0) |
e443c944 | 383 | gd->env_valid = 2; |
ea882baf | 384 | else if (tmp_env2->flags == 255 && tmp_env1->flags == 0) |
e443c944 | 385 | gd->env_valid = 1; |
ea882baf | 386 | else if (tmp_env1->flags > tmp_env2->flags) |
e443c944 | 387 | gd->env_valid = 1; |
ea882baf | 388 | else if (tmp_env2->flags > tmp_env1->flags) |
e443c944 MK |
389 | gd->env_valid = 2; |
390 | else /* flags are equal - almost impossible */ | |
391 | gd->env_valid = 1; | |
13a5695b | 392 | |
e443c944 MK |
393 | } |
394 | ||
395 | free(env_ptr); | |
ea882baf WD |
396 | |
397 | if (gd->env_valid == 1) | |
398 | ep = tmp_env1; | |
399 | else | |
400 | ep = tmp_env2; | |
401 | ||
402 | env_import((char *)ep, 0); | |
403 | ||
404 | free(tmp_env1); | |
405 | free(tmp_env2); | |
e443c944 MK |
406 | |
407 | #endif /* ! ENV_IS_EMBEDDED */ | |
408 | } | |
0e8d1586 | 409 | #else /* ! CONFIG_ENV_OFFSET_REDUND */ |
addb2e16 | 410 | /* |
ea882baf WD |
411 | * The legacy NAND code saved the environment in the first NAND |
412 | * device i.e., nand_dev_desc + 0. This is also the behaviour using | |
413 | * the new NAND code. | |
addb2e16 | 414 | */ |
13a5695b WD |
415 | void env_relocate_spec (void) |
416 | { | |
417 | #if !defined(ENV_IS_EMBEDDED) | |
d52fb7e3 | 418 | int ret; |
ea882baf | 419 | char buf[CONFIG_ENV_SIZE]; |
13a5695b | 420 | |
c9f7351b BG |
421 | #if defined(CONFIG_ENV_OFFSET_OOB) |
422 | ret = get_nand_env_oob(&nand_info[0], &nand_env_oob_offset); | |
ea882baf WD |
423 | /* |
424 | * If unable to read environment offset from NAND OOB then fall through | |
c9f7351b BG |
425 | * to the normal environment reading code below |
426 | */ | |
ea882baf | 427 | if (!ret) { |
c9f7351b | 428 | printf("Found Environment offset in OOB..\n"); |
ea882baf WD |
429 | } else { |
430 | set_default_env("!no env offset in OOB"); | |
431 | return; | |
432 | } | |
c9f7351b BG |
433 | #endif |
434 | ||
ea882baf WD |
435 | ret = readenv(CONFIG_ENV_OFFSET, (u_char *)buf); |
436 | if (ret) { | |
437 | set_default_env("!readenv() failed"); | |
438 | return; | |
439 | } | |
13a5695b | 440 | |
ea882baf | 441 | env_import(buf, 1); |
13a5695b | 442 | #endif /* ! ENV_IS_EMBEDDED */ |
13a5695b | 443 | } |
0e8d1586 | 444 | #endif /* CONFIG_ENV_OFFSET_REDUND */ |