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