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