]>
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> |
13a5695b | 41 | |
bdab39d3 | 42 | #if defined(CONFIG_CMD_SAVEENV) && defined(CONFIG_CMD_NAND) |
13a5695b | 43 | #define CMD_SAVEENV |
0e8d1586 | 44 | #elif defined(CONFIG_ENV_OFFSET_REDUND) |
bdab39d3 | 45 | #error Cannot use CONFIG_ENV_OFFSET_REDUND without CONFIG_CMD_SAVEENV & CONFIG_CMD_NAND |
13a5695b WD |
46 | #endif |
47 | ||
0e8d1586 JCPV |
48 | #if defined(CONFIG_ENV_SIZE_REDUND) && (CONFIG_ENV_SIZE_REDUND != CONFIG_ENV_SIZE) |
49 | #error CONFIG_ENV_SIZE_REDUND should be the same as CONFIG_ENV_SIZE | |
13a5695b WD |
50 | #endif |
51 | ||
13a5695b WD |
52 | #ifdef CONFIG_INFERNO |
53 | #error CONFIG_INFERNO not supported yet | |
54 | #endif | |
55 | ||
0e8d1586 JCPV |
56 | #ifndef CONFIG_ENV_RANGE |
57 | #define CONFIG_ENV_RANGE CONFIG_ENV_SIZE | |
cc49cade SW |
58 | #endif |
59 | ||
13a5695b WD |
60 | /* references to names in env_common.c */ |
61 | extern uchar default_environment[]; | |
13a5695b WD |
62 | |
63 | char * env_name_spec = "NAND"; | |
64 | ||
65 | ||
b74ab737 | 66 | #if defined(ENV_IS_EMBEDDED) |
13a5695b WD |
67 | extern uchar environment[]; |
68 | env_t *env_ptr = (env_t *)(&environment[0]); | |
b74ab737 GL |
69 | #elif defined(CONFIG_NAND_ENV_DST) |
70 | env_t *env_ptr = (env_t *)CONFIG_NAND_ENV_DST; | |
13a5695b | 71 | #else /* ! ENV_IS_EMBEDDED */ |
49822e23 | 72 | env_t *env_ptr = 0; |
13a5695b WD |
73 | #endif /* ENV_IS_EMBEDDED */ |
74 | ||
75 | ||
76 | /* local functions */ | |
d12ae808 | 77 | #if !defined(ENV_IS_EMBEDDED) |
13a5695b | 78 | static void use_default(void); |
d12ae808 | 79 | #endif |
13a5695b | 80 | |
d87080b7 | 81 | DECLARE_GLOBAL_DATA_PTR; |
13a5695b WD |
82 | |
83 | uchar env_get_char_spec (int index) | |
84 | { | |
13a5695b WD |
85 | return ( *((uchar *)(gd->env_addr + index)) ); |
86 | } | |
87 | ||
88 | ||
89 | /* this is called before nand_init() | |
90 | * so we can't read Nand to validate env data. | |
91 | * Mark it OK for now. env_relocate() in env_common.c | |
99c2b434 MZ |
92 | * will call our relocate function which does the real |
93 | * validation. | |
d12ae808 SR |
94 | * |
95 | * When using a NAND boot image (like sequoia_nand), the environment | |
96 | * can be embedded or attached to the U-Boot image in NAND flash. This way | |
97 | * the SPL loads not only the U-Boot image from NAND but also the | |
98 | * environment. | |
13a5695b WD |
99 | */ |
100 | int env_init(void) | |
101 | { | |
b74ab737 | 102 | #if defined(ENV_IS_EMBEDDED) || defined(CONFIG_NAND_ENV_DST) |
d12ae808 | 103 | int crc1_ok = 0, crc2_ok = 0; |
b74ab737 GL |
104 | env_t *tmp_env1; |
105 | ||
106 | #ifdef CONFIG_ENV_OFFSET_REDUND | |
107 | env_t *tmp_env2; | |
d12ae808 | 108 | |
0e8d1586 | 109 | tmp_env2 = (env_t *)((ulong)env_ptr + CONFIG_ENV_SIZE); |
b74ab737 GL |
110 | crc2_ok = (crc32(0, tmp_env2->data, ENV_SIZE) == tmp_env2->crc); |
111 | #endif | |
112 | ||
113 | tmp_env1 = env_ptr; | |
d12ae808 SR |
114 | |
115 | crc1_ok = (crc32(0, tmp_env1->data, ENV_SIZE) == tmp_env1->crc); | |
d12ae808 | 116 | |
b74ab737 GL |
117 | if (!crc1_ok && !crc2_ok) { |
118 | gd->env_addr = 0; | |
d12ae808 | 119 | gd->env_valid = 0; |
b74ab737 GL |
120 | |
121 | return 0; | |
122 | } else if (crc1_ok && !crc2_ok) { | |
d12ae808 | 123 | gd->env_valid = 1; |
b74ab737 GL |
124 | } |
125 | #ifdef CONFIG_ENV_OFFSET_REDUND | |
126 | else if (!crc1_ok && crc2_ok) { | |
d12ae808 | 127 | gd->env_valid = 2; |
b74ab737 | 128 | } else { |
d12ae808 SR |
129 | /* both ok - check serial */ |
130 | if(tmp_env1->flags == 255 && tmp_env2->flags == 0) | |
131 | gd->env_valid = 2; | |
132 | else if(tmp_env2->flags == 255 && tmp_env1->flags == 0) | |
133 | gd->env_valid = 1; | |
134 | else if(tmp_env1->flags > tmp_env2->flags) | |
135 | gd->env_valid = 1; | |
136 | else if(tmp_env2->flags > tmp_env1->flags) | |
137 | gd->env_valid = 2; | |
138 | else /* flags are equal - almost impossible */ | |
139 | gd->env_valid = 1; | |
140 | } | |
141 | ||
b74ab737 GL |
142 | if (gd->env_valid == 2) |
143 | env_ptr = tmp_env2; | |
144 | else | |
145 | #endif | |
d12ae808 SR |
146 | if (gd->env_valid == 1) |
147 | env_ptr = tmp_env1; | |
b74ab737 GL |
148 | |
149 | gd->env_addr = (ulong)env_ptr->data; | |
150 | ||
151 | #else /* ENV_IS_EMBEDDED || CONFIG_NAND_ENV_DST */ | |
e443c944 | 152 | gd->env_addr = (ulong)&default_environment[0]; |
13a5695b | 153 | gd->env_valid = 1; |
b74ab737 | 154 | #endif /* ENV_IS_EMBEDDED || CONFIG_NAND_ENV_DST */ |
13a5695b WD |
155 | |
156 | return (0); | |
157 | } | |
158 | ||
159 | #ifdef CMD_SAVEENV | |
addb2e16 BS |
160 | /* |
161 | * The legacy NAND code saved the environment in the first NAND device i.e., | |
162 | * nand_dev_desc + 0. This is also the behaviour using the new NAND code. | |
163 | */ | |
cc49cade SW |
164 | int writeenv(size_t offset, u_char *buf) |
165 | { | |
0e8d1586 | 166 | size_t end = offset + CONFIG_ENV_RANGE; |
cc49cade | 167 | size_t amount_saved = 0; |
c3db8c64 | 168 | size_t blocksize, len; |
cc49cade SW |
169 | |
170 | u_char *char_ptr; | |
171 | ||
172 | blocksize = nand_info[0].erasesize; | |
0e8d1586 | 173 | len = min(blocksize, CONFIG_ENV_SIZE); |
cc49cade | 174 | |
0e8d1586 | 175 | while (amount_saved < CONFIG_ENV_SIZE && offset < end) { |
cc49cade SW |
176 | if (nand_block_isbad(&nand_info[0], offset)) { |
177 | offset += blocksize; | |
178 | } else { | |
179 | char_ptr = &buf[amount_saved]; | |
c3db8c64 | 180 | if (nand_write(&nand_info[0], offset, &len, |
cc49cade SW |
181 | char_ptr)) |
182 | return 1; | |
183 | offset += blocksize; | |
c3db8c64 | 184 | amount_saved += len; |
cc49cade SW |
185 | } |
186 | } | |
0e8d1586 | 187 | if (amount_saved != CONFIG_ENV_SIZE) |
cc49cade SW |
188 | return 1; |
189 | ||
190 | return 0; | |
191 | } | |
0e8d1586 | 192 | #ifdef CONFIG_ENV_OFFSET_REDUND |
13a5695b WD |
193 | int saveenv(void) |
194 | { | |
2770bcb2 | 195 | int ret = 0; |
cc49cade | 196 | nand_erase_options_t nand_erase_options; |
e443c944 | 197 | |
e443c944 | 198 | env_ptr->flags++; |
e443c944 | 199 | |
0e8d1586 | 200 | nand_erase_options.length = CONFIG_ENV_RANGE; |
cc49cade SW |
201 | nand_erase_options.quiet = 0; |
202 | nand_erase_options.jffs2 = 0; | |
203 | nand_erase_options.scrub = 0; | |
204 | ||
0e8d1586 | 205 | if (CONFIG_ENV_RANGE < CONFIG_ENV_SIZE) |
cc49cade | 206 | return 1; |
e443c944 | 207 | if(gd->env_valid == 1) { |
cc49cade | 208 | puts ("Erasing redundant Nand...\n"); |
0e8d1586 | 209 | nand_erase_options.offset = CONFIG_ENV_OFFSET_REDUND; |
cc49cade | 210 | if (nand_erase_opts(&nand_info[0], &nand_erase_options)) |
e443c944 | 211 | return 1; |
cc49cade | 212 | |
e443c944 | 213 | puts ("Writing to redundant Nand... "); |
0e8d1586 | 214 | ret = writeenv(CONFIG_ENV_OFFSET_REDUND, (u_char *) env_ptr); |
e443c944 | 215 | } else { |
cc49cade | 216 | puts ("Erasing Nand...\n"); |
0e8d1586 | 217 | nand_erase_options.offset = CONFIG_ENV_OFFSET; |
cc49cade | 218 | if (nand_erase_opts(&nand_info[0], &nand_erase_options)) |
e443c944 MK |
219 | return 1; |
220 | ||
221 | puts ("Writing to Nand... "); | |
0e8d1586 | 222 | ret = writeenv(CONFIG_ENV_OFFSET, (u_char *) env_ptr); |
e443c944 | 223 | } |
cc49cade SW |
224 | if (ret) { |
225 | puts("FAILED!\n"); | |
e443c944 | 226 | return 1; |
cc49cade | 227 | } |
e443c944 MK |
228 | |
229 | puts ("done\n"); | |
230 | gd->env_valid = (gd->env_valid == 2 ? 1 : 2); | |
231 | return ret; | |
232 | } | |
0e8d1586 | 233 | #else /* ! CONFIG_ENV_OFFSET_REDUND */ |
e443c944 MK |
234 | int saveenv(void) |
235 | { | |
d52fb7e3 | 236 | int ret = 0; |
9e4006bc | 237 | nand_erase_options_t nand_erase_options; |
e093a247 | 238 | |
0e8d1586 | 239 | nand_erase_options.length = CONFIG_ENV_RANGE; |
cc49cade SW |
240 | nand_erase_options.quiet = 0; |
241 | nand_erase_options.jffs2 = 0; | |
242 | nand_erase_options.scrub = 0; | |
0e8d1586 | 243 | nand_erase_options.offset = CONFIG_ENV_OFFSET; |
cc49cade | 244 | |
0e8d1586 | 245 | if (CONFIG_ENV_RANGE < CONFIG_ENV_SIZE) |
cc49cade SW |
246 | return 1; |
247 | puts ("Erasing Nand...\n"); | |
248 | if (nand_erase_opts(&nand_info[0], &nand_erase_options)) | |
addb2e16 | 249 | return 1; |
13a5695b WD |
250 | |
251 | puts ("Writing to Nand... "); | |
0e8d1586 | 252 | if (writeenv(CONFIG_ENV_OFFSET, (u_char *) env_ptr)) { |
cc49cade | 253 | puts("FAILED!\n"); |
13a5695b | 254 | return 1; |
cc49cade | 255 | } |
13a5695b | 256 | |
addb2e16 BS |
257 | puts ("done\n"); |
258 | return ret; | |
13a5695b | 259 | } |
0e8d1586 | 260 | #endif /* CONFIG_ENV_OFFSET_REDUND */ |
13a5695b WD |
261 | #endif /* CMD_SAVEENV */ |
262 | ||
cc49cade SW |
263 | int readenv (size_t offset, u_char * buf) |
264 | { | |
0e8d1586 | 265 | size_t end = offset + CONFIG_ENV_RANGE; |
cc49cade | 266 | size_t amount_loaded = 0; |
c3db8c64 | 267 | size_t blocksize, len; |
cc49cade SW |
268 | |
269 | u_char *char_ptr; | |
270 | ||
271 | blocksize = nand_info[0].erasesize; | |
0e8d1586 | 272 | len = min(blocksize, CONFIG_ENV_SIZE); |
cc49cade | 273 | |
0e8d1586 | 274 | while (amount_loaded < CONFIG_ENV_SIZE && offset < end) { |
cc49cade SW |
275 | if (nand_block_isbad(&nand_info[0], offset)) { |
276 | offset += blocksize; | |
277 | } else { | |
278 | char_ptr = &buf[amount_loaded]; | |
c3db8c64 | 279 | if (nand_read(&nand_info[0], offset, &len, char_ptr)) |
cc49cade SW |
280 | return 1; |
281 | offset += blocksize; | |
c3db8c64 | 282 | amount_loaded += len; |
cc49cade SW |
283 | } |
284 | } | |
0e8d1586 | 285 | if (amount_loaded != CONFIG_ENV_SIZE) |
cc49cade SW |
286 | return 1; |
287 | ||
288 | return 0; | |
289 | } | |
290 | ||
0e8d1586 | 291 | #ifdef CONFIG_ENV_OFFSET_REDUND |
e443c944 MK |
292 | void env_relocate_spec (void) |
293 | { | |
294 | #if !defined(ENV_IS_EMBEDDED) | |
2770bcb2 | 295 | int crc1_ok = 0, crc2_ok = 0; |
e443c944 MK |
296 | env_t *tmp_env1, *tmp_env2; |
297 | ||
0e8d1586 JCPV |
298 | tmp_env1 = (env_t *) malloc(CONFIG_ENV_SIZE); |
299 | tmp_env2 = (env_t *) malloc(CONFIG_ENV_SIZE); | |
e443c944 | 300 | |
0e8d1586 | 301 | if (readenv(CONFIG_ENV_OFFSET, (u_char *) tmp_env1)) |
cc49cade | 302 | puts("No Valid Environment Area Found\n"); |
0e8d1586 | 303 | if (readenv(CONFIG_ENV_OFFSET_REDUND, (u_char *) tmp_env2)) |
cc49cade | 304 | puts("No Valid Reundant Environment Area Found\n"); |
e443c944 MK |
305 | |
306 | crc1_ok = (crc32(0, tmp_env1->data, ENV_SIZE) == tmp_env1->crc); | |
307 | crc2_ok = (crc32(0, tmp_env2->data, ENV_SIZE) == tmp_env2->crc); | |
308 | ||
5a9427dc | 309 | if(!crc1_ok && !crc2_ok) { |
310 | free(tmp_env1); | |
311 | free(tmp_env2); | |
e443c944 | 312 | return use_default(); |
5a9427dc | 313 | } else if(crc1_ok && !crc2_ok) |
e443c944 MK |
314 | gd->env_valid = 1; |
315 | else if(!crc1_ok && crc2_ok) | |
316 | gd->env_valid = 2; | |
317 | else { | |
318 | /* both ok - check serial */ | |
319 | if(tmp_env1->flags == 255 && tmp_env2->flags == 0) | |
320 | gd->env_valid = 2; | |
321 | else if(tmp_env2->flags == 255 && tmp_env1->flags == 0) | |
322 | gd->env_valid = 1; | |
323 | else if(tmp_env1->flags > tmp_env2->flags) | |
324 | gd->env_valid = 1; | |
325 | else if(tmp_env2->flags > tmp_env1->flags) | |
326 | gd->env_valid = 2; | |
327 | else /* flags are equal - almost impossible */ | |
328 | gd->env_valid = 1; | |
13a5695b | 329 | |
e443c944 MK |
330 | } |
331 | ||
332 | free(env_ptr); | |
333 | if(gd->env_valid == 1) { | |
334 | env_ptr = tmp_env1; | |
335 | free(tmp_env2); | |
336 | } else { | |
337 | env_ptr = tmp_env2; | |
338 | free(tmp_env1); | |
339 | } | |
340 | ||
341 | #endif /* ! ENV_IS_EMBEDDED */ | |
342 | } | |
0e8d1586 | 343 | #else /* ! CONFIG_ENV_OFFSET_REDUND */ |
addb2e16 BS |
344 | /* |
345 | * The legacy NAND code saved the environment in the first NAND device i.e., | |
346 | * nand_dev_desc + 0. This is also the behaviour using the new NAND code. | |
347 | */ | |
13a5695b WD |
348 | void env_relocate_spec (void) |
349 | { | |
350 | #if !defined(ENV_IS_EMBEDDED) | |
d52fb7e3 | 351 | int ret; |
13a5695b | 352 | |
0e8d1586 | 353 | ret = readenv(CONFIG_ENV_OFFSET, (u_char *) env_ptr); |
c3db8c64 | 354 | if (ret) |
13a5695b WD |
355 | return use_default(); |
356 | ||
357 | if (crc32(0, env_ptr->data, ENV_SIZE) != env_ptr->crc) | |
358 | return use_default(); | |
359 | #endif /* ! ENV_IS_EMBEDDED */ | |
13a5695b | 360 | } |
0e8d1586 | 361 | #endif /* CONFIG_ENV_OFFSET_REDUND */ |
13a5695b | 362 | |
d12ae808 | 363 | #if !defined(ENV_IS_EMBEDDED) |
13a5695b WD |
364 | static void use_default() |
365 | { | |
13a5695b | 366 | puts ("*** Warning - bad CRC or NAND, using default environment\n\n"); |
5bb12dbd | 367 | set_default_env(); |
13a5695b | 368 | } |
d12ae808 | 369 | #endif |