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