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