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