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