]>
Commit | Line | Data |
---|---|---|
13a5695b | 1 | /* |
cc49cade SW |
2 | * (C) Copyright 2008 |
3 | * Stuart Wood, Lab X Technologies <[email protected]> | |
4 | * | |
13a5695b WD |
5 | * (C) Copyright 2004 |
6 | * Jian Zhang, Texas Instruments, [email protected]. | |
7 | ||
d12ae808 | 8 | * (C) Copyright 2000-2006 |
13a5695b WD |
9 | * Wolfgang Denk, DENX Software Engineering, [email protected]. |
10 | * | |
11 | * (C) Copyright 2001 Sysgo Real-Time Solutions, GmbH <www.elinos.com> | |
12 | * Andreas Heppel <[email protected]> | |
13 | ||
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 | ||
33 | /* #define DEBUG */ | |
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> |
c9f7351b | 41 | #include <asm/errno.h> |
13a5695b | 42 | |
bdab39d3 | 43 | #if defined(CONFIG_CMD_SAVEENV) && defined(CONFIG_CMD_NAND) |
13a5695b | 44 | #define CMD_SAVEENV |
0e8d1586 | 45 | #elif defined(CONFIG_ENV_OFFSET_REDUND) |
bdab39d3 | 46 | #error Cannot use CONFIG_ENV_OFFSET_REDUND without CONFIG_CMD_SAVEENV & CONFIG_CMD_NAND |
13a5695b WD |
47 | #endif |
48 | ||
0e8d1586 JCPV |
49 | #if defined(CONFIG_ENV_SIZE_REDUND) && (CONFIG_ENV_SIZE_REDUND != CONFIG_ENV_SIZE) |
50 | #error CONFIG_ENV_SIZE_REDUND should be the same as CONFIG_ENV_SIZE | |
13a5695b WD |
51 | #endif |
52 | ||
0e8d1586 JCPV |
53 | #ifndef CONFIG_ENV_RANGE |
54 | #define CONFIG_ENV_RANGE CONFIG_ENV_SIZE | |
cc49cade SW |
55 | #endif |
56 | ||
13a5695b WD |
57 | /* references to names in env_common.c */ |
58 | extern uchar default_environment[]; | |
13a5695b WD |
59 | |
60 | char * env_name_spec = "NAND"; | |
61 | ||
62 | ||
b74ab737 | 63 | #if defined(ENV_IS_EMBEDDED) |
13a5695b WD |
64 | extern uchar environment[]; |
65 | env_t *env_ptr = (env_t *)(&environment[0]); | |
b74ab737 GL |
66 | #elif defined(CONFIG_NAND_ENV_DST) |
67 | env_t *env_ptr = (env_t *)CONFIG_NAND_ENV_DST; | |
13a5695b | 68 | #else /* ! ENV_IS_EMBEDDED */ |
49822e23 | 69 | env_t *env_ptr = 0; |
13a5695b WD |
70 | #endif /* ENV_IS_EMBEDDED */ |
71 | ||
72 | ||
73 | /* local functions */ | |
d12ae808 | 74 | #if !defined(ENV_IS_EMBEDDED) |
13a5695b | 75 | static void use_default(void); |
d12ae808 | 76 | #endif |
13a5695b | 77 | |
d87080b7 | 78 | DECLARE_GLOBAL_DATA_PTR; |
13a5695b WD |
79 | |
80 | uchar env_get_char_spec (int index) | |
81 | { | |
13a5695b WD |
82 | return ( *((uchar *)(gd->env_addr + index)) ); |
83 | } | |
84 | ||
85 | ||
86 | /* this is called before nand_init() | |
87 | * so we can't read Nand to validate env data. | |
88 | * Mark it OK for now. env_relocate() in env_common.c | |
99c2b434 MZ |
89 | * will call our relocate function which does the real |
90 | * validation. | |
d12ae808 SR |
91 | * |
92 | * When using a NAND boot image (like sequoia_nand), the environment | |
93 | * can be embedded or attached to the U-Boot image in NAND flash. This way | |
94 | * the SPL loads not only the U-Boot image from NAND but also the | |
95 | * environment. | |
13a5695b WD |
96 | */ |
97 | int env_init(void) | |
98 | { | |
b74ab737 | 99 | #if defined(ENV_IS_EMBEDDED) || defined(CONFIG_NAND_ENV_DST) |
d12ae808 | 100 | int crc1_ok = 0, crc2_ok = 0; |
b74ab737 GL |
101 | env_t *tmp_env1; |
102 | ||
103 | #ifdef CONFIG_ENV_OFFSET_REDUND | |
104 | env_t *tmp_env2; | |
d12ae808 | 105 | |
0e8d1586 | 106 | tmp_env2 = (env_t *)((ulong)env_ptr + CONFIG_ENV_SIZE); |
b74ab737 GL |
107 | crc2_ok = (crc32(0, tmp_env2->data, ENV_SIZE) == tmp_env2->crc); |
108 | #endif | |
109 | ||
110 | tmp_env1 = env_ptr; | |
d12ae808 SR |
111 | |
112 | crc1_ok = (crc32(0, tmp_env1->data, ENV_SIZE) == tmp_env1->crc); | |
d12ae808 | 113 | |
b74ab737 GL |
114 | if (!crc1_ok && !crc2_ok) { |
115 | gd->env_addr = 0; | |
d12ae808 | 116 | gd->env_valid = 0; |
b74ab737 GL |
117 | |
118 | return 0; | |
119 | } else if (crc1_ok && !crc2_ok) { | |
d12ae808 | 120 | gd->env_valid = 1; |
b74ab737 GL |
121 | } |
122 | #ifdef CONFIG_ENV_OFFSET_REDUND | |
123 | else if (!crc1_ok && crc2_ok) { | |
d12ae808 | 124 | gd->env_valid = 2; |
b74ab737 | 125 | } else { |
d12ae808 SR |
126 | /* both ok - check serial */ |
127 | if(tmp_env1->flags == 255 && tmp_env2->flags == 0) | |
128 | gd->env_valid = 2; | |
129 | else if(tmp_env2->flags == 255 && tmp_env1->flags == 0) | |
130 | gd->env_valid = 1; | |
131 | else if(tmp_env1->flags > tmp_env2->flags) | |
132 | gd->env_valid = 1; | |
133 | else if(tmp_env2->flags > tmp_env1->flags) | |
134 | gd->env_valid = 2; | |
135 | else /* flags are equal - almost impossible */ | |
136 | gd->env_valid = 1; | |
137 | } | |
138 | ||
b74ab737 GL |
139 | if (gd->env_valid == 2) |
140 | env_ptr = tmp_env2; | |
141 | else | |
142 | #endif | |
d12ae808 SR |
143 | if (gd->env_valid == 1) |
144 | env_ptr = tmp_env1; | |
b74ab737 GL |
145 | |
146 | gd->env_addr = (ulong)env_ptr->data; | |
147 | ||
148 | #else /* ENV_IS_EMBEDDED || CONFIG_NAND_ENV_DST */ | |
e443c944 | 149 | gd->env_addr = (ulong)&default_environment[0]; |
13a5695b | 150 | gd->env_valid = 1; |
b74ab737 | 151 | #endif /* ENV_IS_EMBEDDED || CONFIG_NAND_ENV_DST */ |
13a5695b WD |
152 | |
153 | return (0); | |
154 | } | |
155 | ||
156 | #ifdef CMD_SAVEENV | |
addb2e16 BS |
157 | /* |
158 | * The legacy NAND code saved the environment in the first NAND device i.e., | |
159 | * nand_dev_desc + 0. This is also the behaviour using the new NAND code. | |
160 | */ | |
cc49cade SW |
161 | int writeenv(size_t offset, u_char *buf) |
162 | { | |
0e8d1586 | 163 | size_t end = offset + CONFIG_ENV_RANGE; |
cc49cade | 164 | size_t amount_saved = 0; |
c3db8c64 | 165 | size_t blocksize, len; |
cc49cade SW |
166 | |
167 | u_char *char_ptr; | |
168 | ||
169 | blocksize = nand_info[0].erasesize; | |
0e8d1586 | 170 | len = min(blocksize, CONFIG_ENV_SIZE); |
cc49cade | 171 | |
0e8d1586 | 172 | while (amount_saved < CONFIG_ENV_SIZE && offset < end) { |
cc49cade SW |
173 | if (nand_block_isbad(&nand_info[0], offset)) { |
174 | offset += blocksize; | |
175 | } else { | |
176 | char_ptr = &buf[amount_saved]; | |
c3db8c64 | 177 | if (nand_write(&nand_info[0], offset, &len, |
cc49cade SW |
178 | char_ptr)) |
179 | return 1; | |
180 | offset += blocksize; | |
c3db8c64 | 181 | amount_saved += len; |
cc49cade SW |
182 | } |
183 | } | |
0e8d1586 | 184 | if (amount_saved != CONFIG_ENV_SIZE) |
cc49cade SW |
185 | return 1; |
186 | ||
187 | return 0; | |
188 | } | |
0e8d1586 | 189 | #ifdef CONFIG_ENV_OFFSET_REDUND |
13a5695b WD |
190 | int saveenv(void) |
191 | { | |
2770bcb2 | 192 | int ret = 0; |
cc49cade | 193 | nand_erase_options_t nand_erase_options; |
e443c944 | 194 | |
e443c944 | 195 | env_ptr->flags++; |
e443c944 | 196 | |
0e8d1586 | 197 | nand_erase_options.length = CONFIG_ENV_RANGE; |
cc49cade SW |
198 | nand_erase_options.quiet = 0; |
199 | nand_erase_options.jffs2 = 0; | |
200 | nand_erase_options.scrub = 0; | |
201 | ||
0e8d1586 | 202 | if (CONFIG_ENV_RANGE < CONFIG_ENV_SIZE) |
cc49cade | 203 | return 1; |
e443c944 | 204 | if(gd->env_valid == 1) { |
cc49cade | 205 | puts ("Erasing redundant Nand...\n"); |
0e8d1586 | 206 | nand_erase_options.offset = CONFIG_ENV_OFFSET_REDUND; |
cc49cade | 207 | if (nand_erase_opts(&nand_info[0], &nand_erase_options)) |
e443c944 | 208 | return 1; |
cc49cade | 209 | |
e443c944 | 210 | puts ("Writing to redundant Nand... "); |
0e8d1586 | 211 | ret = writeenv(CONFIG_ENV_OFFSET_REDUND, (u_char *) env_ptr); |
e443c944 | 212 | } else { |
cc49cade | 213 | puts ("Erasing Nand...\n"); |
0e8d1586 | 214 | nand_erase_options.offset = CONFIG_ENV_OFFSET; |
cc49cade | 215 | if (nand_erase_opts(&nand_info[0], &nand_erase_options)) |
e443c944 MK |
216 | return 1; |
217 | ||
218 | puts ("Writing to Nand... "); | |
0e8d1586 | 219 | ret = writeenv(CONFIG_ENV_OFFSET, (u_char *) env_ptr); |
e443c944 | 220 | } |
cc49cade SW |
221 | if (ret) { |
222 | puts("FAILED!\n"); | |
e443c944 | 223 | return 1; |
cc49cade | 224 | } |
e443c944 MK |
225 | |
226 | puts ("done\n"); | |
227 | gd->env_valid = (gd->env_valid == 2 ? 1 : 2); | |
228 | return ret; | |
229 | } | |
0e8d1586 | 230 | #else /* ! CONFIG_ENV_OFFSET_REDUND */ |
e443c944 MK |
231 | int saveenv(void) |
232 | { | |
d52fb7e3 | 233 | int ret = 0; |
9e4006bc | 234 | nand_erase_options_t nand_erase_options; |
e093a247 | 235 | |
0e8d1586 | 236 | nand_erase_options.length = CONFIG_ENV_RANGE; |
cc49cade SW |
237 | nand_erase_options.quiet = 0; |
238 | nand_erase_options.jffs2 = 0; | |
239 | nand_erase_options.scrub = 0; | |
0e8d1586 | 240 | nand_erase_options.offset = CONFIG_ENV_OFFSET; |
cc49cade | 241 | |
0e8d1586 | 242 | if (CONFIG_ENV_RANGE < CONFIG_ENV_SIZE) |
cc49cade SW |
243 | return 1; |
244 | puts ("Erasing Nand...\n"); | |
245 | if (nand_erase_opts(&nand_info[0], &nand_erase_options)) | |
addb2e16 | 246 | return 1; |
13a5695b WD |
247 | |
248 | puts ("Writing to Nand... "); | |
0e8d1586 | 249 | if (writeenv(CONFIG_ENV_OFFSET, (u_char *) env_ptr)) { |
cc49cade | 250 | puts("FAILED!\n"); |
13a5695b | 251 | return 1; |
cc49cade | 252 | } |
13a5695b | 253 | |
addb2e16 BS |
254 | puts ("done\n"); |
255 | return ret; | |
13a5695b | 256 | } |
0e8d1586 | 257 | #endif /* CONFIG_ENV_OFFSET_REDUND */ |
13a5695b WD |
258 | #endif /* CMD_SAVEENV */ |
259 | ||
cc49cade SW |
260 | int readenv (size_t offset, u_char * buf) |
261 | { | |
0e8d1586 | 262 | size_t end = offset + CONFIG_ENV_RANGE; |
cc49cade | 263 | size_t amount_loaded = 0; |
c3db8c64 | 264 | size_t blocksize, len; |
cc49cade SW |
265 | |
266 | u_char *char_ptr; | |
267 | ||
268 | blocksize = nand_info[0].erasesize; | |
0e8d1586 | 269 | len = min(blocksize, CONFIG_ENV_SIZE); |
cc49cade | 270 | |
0e8d1586 | 271 | while (amount_loaded < CONFIG_ENV_SIZE && offset < end) { |
cc49cade SW |
272 | if (nand_block_isbad(&nand_info[0], offset)) { |
273 | offset += blocksize; | |
274 | } else { | |
275 | char_ptr = &buf[amount_loaded]; | |
c3db8c64 | 276 | if (nand_read(&nand_info[0], offset, &len, char_ptr)) |
cc49cade SW |
277 | return 1; |
278 | offset += blocksize; | |
c3db8c64 | 279 | amount_loaded += len; |
cc49cade SW |
280 | } |
281 | } | |
0e8d1586 | 282 | if (amount_loaded != CONFIG_ENV_SIZE) |
cc49cade SW |
283 | return 1; |
284 | ||
285 | return 0; | |
286 | } | |
287 | ||
c9f7351b BG |
288 | #ifdef CONFIG_ENV_OFFSET_OOB |
289 | int get_nand_env_oob(nand_info_t *nand, unsigned long *result) | |
290 | { | |
291 | struct mtd_oob_ops ops; | |
292 | uint32_t oob_buf[ENV_OFFSET_SIZE/sizeof(uint32_t)]; | |
293 | int ret; | |
294 | ||
295 | ops.datbuf = NULL; | |
296 | ops.mode = MTD_OOB_AUTO; | |
297 | ops.ooboffs = 0; | |
298 | ops.ooblen = ENV_OFFSET_SIZE; | |
299 | ops.oobbuf = (void *) oob_buf; | |
300 | ||
301 | ret = nand->read_oob(nand, ENV_OFFSET_SIZE, &ops); | |
302 | ||
303 | if (!ret) { | |
304 | if (oob_buf[0] == ENV_OOB_MARKER) { | |
305 | *result = oob_buf[1] * nand->erasesize; | |
306 | } else if (oob_buf[0] == ENV_OOB_MARKER_OLD) { | |
307 | *result = oob_buf[1]; | |
308 | } else { | |
309 | printf("No dynamic environment marker in OOB block 0" | |
310 | "\n"); | |
311 | ret = -ENOENT; | |
312 | goto fail; | |
313 | } | |
314 | } else { | |
315 | printf("error reading OOB block 0\n"); | |
316 | } | |
317 | fail: | |
318 | return ret; | |
319 | } | |
320 | #endif | |
321 | ||
0e8d1586 | 322 | #ifdef CONFIG_ENV_OFFSET_REDUND |
e443c944 MK |
323 | void env_relocate_spec (void) |
324 | { | |
325 | #if !defined(ENV_IS_EMBEDDED) | |
2770bcb2 | 326 | int crc1_ok = 0, crc2_ok = 0; |
e443c944 MK |
327 | env_t *tmp_env1, *tmp_env2; |
328 | ||
0e8d1586 JCPV |
329 | tmp_env1 = (env_t *) malloc(CONFIG_ENV_SIZE); |
330 | tmp_env2 = (env_t *) malloc(CONFIG_ENV_SIZE); | |
e443c944 | 331 | |
15b86c3d WD |
332 | if ((tmp_env1 == NULL) || (tmp_env2 == NULL)) { |
333 | puts("Can't allocate buffers for environment\n"); | |
334 | free (tmp_env1); | |
335 | free (tmp_env2); | |
336 | return use_default(); | |
337 | } | |
338 | ||
0e8d1586 | 339 | if (readenv(CONFIG_ENV_OFFSET, (u_char *) tmp_env1)) |
cc49cade | 340 | puts("No Valid Environment Area Found\n"); |
0e8d1586 | 341 | if (readenv(CONFIG_ENV_OFFSET_REDUND, (u_char *) tmp_env2)) |
cc49cade | 342 | puts("No Valid Reundant Environment Area Found\n"); |
e443c944 MK |
343 | |
344 | crc1_ok = (crc32(0, tmp_env1->data, ENV_SIZE) == tmp_env1->crc); | |
345 | crc2_ok = (crc32(0, tmp_env2->data, ENV_SIZE) == tmp_env2->crc); | |
346 | ||
5a9427dc | 347 | if(!crc1_ok && !crc2_ok) { |
348 | free(tmp_env1); | |
349 | free(tmp_env2); | |
e443c944 | 350 | return use_default(); |
5a9427dc | 351 | } else if(crc1_ok && !crc2_ok) |
e443c944 MK |
352 | gd->env_valid = 1; |
353 | else if(!crc1_ok && crc2_ok) | |
354 | gd->env_valid = 2; | |
355 | else { | |
356 | /* both ok - check serial */ | |
357 | if(tmp_env1->flags == 255 && tmp_env2->flags == 0) | |
358 | gd->env_valid = 2; | |
359 | else if(tmp_env2->flags == 255 && tmp_env1->flags == 0) | |
360 | gd->env_valid = 1; | |
361 | else if(tmp_env1->flags > tmp_env2->flags) | |
362 | gd->env_valid = 1; | |
363 | else if(tmp_env2->flags > tmp_env1->flags) | |
364 | gd->env_valid = 2; | |
365 | else /* flags are equal - almost impossible */ | |
366 | gd->env_valid = 1; | |
13a5695b | 367 | |
e443c944 MK |
368 | } |
369 | ||
370 | free(env_ptr); | |
371 | if(gd->env_valid == 1) { | |
372 | env_ptr = tmp_env1; | |
373 | free(tmp_env2); | |
374 | } else { | |
375 | env_ptr = tmp_env2; | |
376 | free(tmp_env1); | |
377 | } | |
378 | ||
379 | #endif /* ! ENV_IS_EMBEDDED */ | |
380 | } | |
0e8d1586 | 381 | #else /* ! CONFIG_ENV_OFFSET_REDUND */ |
addb2e16 BS |
382 | /* |
383 | * The legacy NAND code saved the environment in the first NAND device i.e., | |
384 | * nand_dev_desc + 0. This is also the behaviour using the new NAND code. | |
385 | */ | |
13a5695b WD |
386 | void env_relocate_spec (void) |
387 | { | |
388 | #if !defined(ENV_IS_EMBEDDED) | |
d52fb7e3 | 389 | int ret; |
13a5695b | 390 | |
c9f7351b BG |
391 | #if defined(CONFIG_ENV_OFFSET_OOB) |
392 | ret = get_nand_env_oob(&nand_info[0], &nand_env_oob_offset); | |
393 | /* If unable to read environment offset from NAND OOB then fall through | |
394 | * to the normal environment reading code below | |
395 | */ | |
396 | if (!ret) | |
397 | printf("Found Environment offset in OOB..\n"); | |
398 | else | |
399 | return use_default(); | |
400 | #endif | |
401 | ||
0e8d1586 | 402 | ret = readenv(CONFIG_ENV_OFFSET, (u_char *) env_ptr); |
c3db8c64 | 403 | if (ret) |
13a5695b WD |
404 | return use_default(); |
405 | ||
406 | if (crc32(0, env_ptr->data, ENV_SIZE) != env_ptr->crc) | |
407 | return use_default(); | |
408 | #endif /* ! ENV_IS_EMBEDDED */ | |
13a5695b | 409 | } |
0e8d1586 | 410 | #endif /* CONFIG_ENV_OFFSET_REDUND */ |
13a5695b | 411 | |
d12ae808 | 412 | #if !defined(ENV_IS_EMBEDDED) |
13a5695b WD |
413 | static void use_default() |
414 | { | |
13a5695b | 415 | puts ("*** Warning - bad CRC or NAND, using default environment\n\n"); |
5bb12dbd | 416 | set_default_env(); |
13a5695b | 417 | } |
d12ae808 | 418 | #endif |