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