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