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