Blame view
common/cmd_nand.c
23.4 KB
dc7c9a1a5
|
1 2 3 4 5 |
/* * Driver for NAND support, Rick Bronson * borrowed heavily from: * (c) 1999 Machine Vision Holdings, Inc. * (c) 1999, 2000 David Woodhouse <dwmw2@infradead.org> |
384cc6874
|
6 |
* |
c9f7351b5
|
7 8 9 10 |
* Ported 'dynenv' to 'nand env.oob' command * (C) 2010 Nanometrics, Inc. * 'dynenv' -- Dynamic environment offset in NAND OOB * (C) Copyright 2006-2007 OpenMoko, Inc. |
384cc6874
|
11 12 |
* Added 16-bit nand support * (C) 2004 Texas Instruments |
ea533c260
|
13 |
* |
418396e21
|
14 |
* Copyright 2010, 2012 Freescale Semiconductor |
ea533c260
|
15 16 17 18 19 |
* The portions of this file whose copyright is held by Freescale and which * are not considered a derived work of GPL v2-only code may be distributed * and/or modified under the terms of the GNU General Public License as * published by the Free Software Foundation; either version 2 of the * License, or (at your option) any later version. |
dc7c9a1a5
|
20 21 22 |
*/ #include <common.h> |
cfa460adf
|
23 |
#include <linux/mtd/mtd.h> |
addb2e165
|
24 25 26 27 |
#include <command.h> #include <watchdog.h> #include <malloc.h> #include <asm/byteorder.h> |
addb2e165
|
28 29 |
#include <jffs2/jffs2.h> #include <nand.h> |
0c8a84916
|
30 |
#if defined(CONFIG_CMD_MTDPARTS) |
856f05441
|
31 |
|
445093d17
|
32 |
/* partition handling routines */ |
856f05441
|
33 34 35 |
int mtdparts_init(void); int id_parse(const char *id, const char **ret_id, u8 *dev_type, u8 *dev_num); int find_dev_and_part(const char *id, struct mtd_device **dev, |
4b0708093
|
36 |
u8 *part_num, struct part_info **part); |
856f05441
|
37 |
#endif |
8c5659a6d
|
38 |
static int nand_dump(nand_info_t *nand, ulong off, int only_oob, int repeat) |
addb2e165
|
39 40 |
{ int i; |
9ad754fef
|
41 |
u_char *datbuf, *oobbuf, *p; |
8c5659a6d
|
42 |
static loff_t last; |
e40520b5b
|
43 |
int ret = 0; |
8c5659a6d
|
44 45 46 47 48 |
if (repeat) off = last + nand->writesize; last = off; |
addb2e165
|
49 |
|
62fd66f3d
|
50 |
datbuf = memalign(ARCH_DMA_MINALIGN, nand->writesize); |
e40520b5b
|
51 |
if (!datbuf) { |
addb2e165
|
52 53 54 55 |
puts("No memory for page buffer "); return 1; } |
e40520b5b
|
56 57 58 59 60 61 62 63 |
oobbuf = memalign(ARCH_DMA_MINALIGN, nand->oobsize); if (!oobbuf) { puts("No memory for page buffer "); ret = 1; goto free_dat; } |
cfa460adf
|
64 |
off &= ~(nand->writesize - 1); |
cfa460adf
|
65 |
loff_t addr = (loff_t) off; |
9ad754fef
|
66 67 68 |
struct mtd_oob_ops ops; memset(&ops, 0, sizeof(ops)); ops.datbuf = datbuf; |
cfdae12f3
|
69 |
ops.oobbuf = oobbuf; |
9ad754fef
|
70 71 |
ops.len = nand->writesize; ops.ooblen = nand->oobsize; |
dfe64e2c8
|
72 73 |
ops.mode = MTD_OPS_RAW; i = mtd_read_oob(nand, addr, &ops); |
addb2e165
|
74 |
if (i < 0) { |
e870690bd
|
75 76 |
printf("Error (%d) reading page %08lx ", i, off); |
e40520b5b
|
77 78 |
ret = 1; goto free_all; |
addb2e165
|
79 |
} |
e870690bd
|
80 81 |
printf("Page %08lx dump: ", off); |
4b0708093
|
82 |
|
7d25cd34e
|
83 84 85 86 87 |
if (!only_oob) { i = nand->writesize >> 4; p = datbuf; while (i--) { |
9ad754fef
|
88 89 90 91 |
printf("\t%02x %02x %02x %02x %02x %02x %02x %02x" " %02x %02x %02x %02x %02x %02x %02x %02x ", p[0], p[1], p[2], p[3], p[4], p[5], p[6], p[7], |
dfbf617ff
|
92 93 |
p[8], p[9], p[10], p[11], p[12], p[13], p[14], p[15]); |
7d25cd34e
|
94 95 |
p += 16; } |
addb2e165
|
96 |
} |
7d25cd34e
|
97 |
|
addb2e165
|
98 99 100 |
puts("OOB: "); i = nand->oobsize >> 3; |
cfdae12f3
|
101 |
p = oobbuf; |
addb2e165
|
102 |
while (i--) { |
cfa460adf
|
103 104 105 |
printf("\t%02x %02x %02x %02x %02x %02x %02x %02x ", p[0], p[1], p[2], p[3], p[4], p[5], p[6], p[7]); |
addb2e165
|
106 107 |
p += 8; } |
e40520b5b
|
108 109 |
free_all: |
9ad754fef
|
110 |
free(oobbuf); |
e40520b5b
|
111 112 |
free_dat: free(datbuf); |
addb2e165
|
113 |
|
e40520b5b
|
114 |
return ret; |
addb2e165
|
115 116 117 |
} /* ------------------------------------------------------------------------- */ |
ea533c260
|
118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 |
static int set_dev(int dev) { if (dev < 0 || dev >= CONFIG_SYS_MAX_NAND_DEVICE || !nand_info[dev].name) { puts("No such device "); return -1; } if (nand_curr_device == dev) return 0; printf("Device %d: %s", dev, nand_info[dev].name); puts("... is now current device "); nand_curr_device = dev; #ifdef CONFIG_SYS_NAND_SELECT_DEVICE board_nand_select_device(nand_info[dev].priv, dev); #endif return 0; } |
50657c273
|
141 142 143 |
#ifdef CONFIG_CMD_NAND_LOCK_UNLOCK static void print_status(ulong start, ulong end, ulong erasesize, int status) { |
e70bfa298
|
144 145 146 147 148 149 |
/* * Micron NAND flash (e.g. MT29F4G08ABADAH4) BLOCK LOCK READ STATUS is * not the same as others. Instead of bit 1 being lock, it is * #lock_tight. To make the driver support either format, ignore bit 1 * and use only bit 0 and bit 2. */ |
50657c273
|
150 151 152 153 154 155 |
printf("%08lx - %08lx: %08lx blocks %s%s%s ", start, end - 1, (end - start) / erasesize, ((status & NAND_LOCK_STATUS_TIGHT) ? "TIGHT " : ""), |
e70bfa298
|
156 |
(!(status & NAND_LOCK_STATUS_UNLOCK) ? "LOCK " : ""), |
50657c273
|
157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 |
((status & NAND_LOCK_STATUS_UNLOCK) ? "UNLOCK " : "")); } static void do_nand_status(nand_info_t *nand) { ulong block_start = 0; ulong off; int last_status = -1; struct nand_chip *nand_chip = nand->priv; /* check the WP bit */ nand_chip->cmdfunc(nand, NAND_CMD_STATUS, -1, -1); printf("device is %swrite protected ", (nand_chip->read_byte(nand) & 0x80 ? "NOT " : "")); for (off = 0; off < nand->size; off += nand->erasesize) { int s = nand_get_lock_status(nand, off); /* print message only if status has changed */ if (s != last_status && off != 0) { print_status(block_start, off, nand->erasesize, last_status); block_start = off; } last_status = s; } /* Print the last block info */ print_status(block_start, off, nand->erasesize, last_status); } #endif |
c9f7351b5
|
189 190 |
#ifdef CONFIG_ENV_OFFSET_OOB unsigned long nand_env_oob_offset; |
ea533c260
|
191 |
int do_nand_env_oob(cmd_tbl_t *cmdtp, int argc, char *const argv[]) |
c9f7351b5
|
192 193 194 |
{ int ret; uint32_t oob_buf[ENV_OFFSET_SIZE/sizeof(uint32_t)]; |
ea533c260
|
195 |
nand_info_t *nand = &nand_info[0]; |
c9f7351b5
|
196 |
char *cmd = argv[1]; |
ea533c260
|
197 198 199 200 201 202 203 |
if (CONFIG_SYS_MAX_NAND_DEVICE == 0 || !nand->name) { puts("no devices available "); return 1; } set_dev(0); |
c9f7351b5
|
204 205 |
if (!strcmp(cmd, "get")) { ret = get_nand_env_oob(nand, &nand_env_oob_offset); |
53504a278
|
206 |
if (ret) |
c9f7351b5
|
207 |
return 1; |
53504a278
|
208 209 210 |
printf("0x%08lx ", nand_env_oob_offset); |
c9f7351b5
|
211 |
} else if (!strcmp(cmd, "set")) { |
ea533c260
|
212 213 |
loff_t addr; loff_t maxsize; |
c9f7351b5
|
214 |
struct mtd_oob_ops ops; |
ea533c260
|
215 |
int idx = 0; |
c9f7351b5
|
216 217 218 |
if (argc < 3) goto usage; |
c39d6a0ea
|
219 |
/* We don't care about size, or maxsize. */ |
09c328075
|
220 221 222 223 224 225 226 |
if (mtd_arg_off(argv[2], &idx, &addr, &maxsize, &maxsize, MTD_DEV_TYPE_NAND, nand_info[idx].size)) { puts("Offset or partition name expected "); return 1; } if (set_dev(idx)) { |
ea533c260
|
227 228 229 230 231 232 233 234 |
puts("Offset or partition name expected "); return 1; } if (idx != 0) { puts("Partition not on first NAND device "); |
c9f7351b5
|
235 236 237 238 |
return 1; } if (nand->oobavail < ENV_OFFSET_SIZE) { |
53504a278
|
239 240 241 242 243 244 |
printf("Insufficient available OOB bytes: " "%d OOB bytes available but %d required for " "env.oob support ", nand->oobavail, ENV_OFFSET_SIZE); |
c9f7351b5
|
245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 |
return 1; } if ((addr & (nand->erasesize - 1)) != 0) { printf("Environment offset must be block-aligned "); return 1; } ops.datbuf = NULL; ops.mode = MTD_OOB_AUTO; ops.ooboffs = 0; ops.ooblen = ENV_OFFSET_SIZE; ops.oobbuf = (void *) oob_buf; oob_buf[0] = ENV_OOB_MARKER; oob_buf[1] = addr / nand->erasesize; ret = nand->write_oob(nand, ENV_OFFSET_SIZE, &ops); |
53504a278
|
264 |
if (ret) { |
c9f7351b5
|
265 266 267 268 |
printf("Error writing OOB block 0 "); return ret; } |
53504a278
|
269 270 271 272 273 274 275 276 277 278 |
ret = get_nand_env_oob(nand, &nand_env_oob_offset); if (ret) { printf("Error reading env offset in OOB "); return ret; } if (addr != nand_env_oob_offset) { printf("Verification of env offset in OOB failed: " |
ea533c260
|
279 280 281 |
"0x%08llx expected but got 0x%08lx ", (unsigned long long)addr, nand_env_oob_offset); |
53504a278
|
282 283 |
return 1; } |
c9f7351b5
|
284 285 286 287 288 289 290 |
} else { goto usage; } return ret; usage: |
4c12eeb8b
|
291 |
return CMD_RET_USAGE; |
c9f7351b5
|
292 293 294 |
} #endif |
ce80ddc18
|
295 |
static void nand_print_and_set_info(int idx) |
672ed2aee
|
296 297 298 |
{ nand_info_t *nand = &nand_info[idx]; struct nand_chip *chip = nand->priv; |
ce80ddc18
|
299 |
|
672ed2aee
|
300 301 302 303 304 305 |
printf("Device %d: ", idx); if (chip->numchips > 1) printf("%dx ", chip->numchips); printf("%s, sector size %u KiB ", nand->name, nand->erasesize >> 10); |
5db73feb1
|
306 307 308 309 310 311 312 313 314 315 316 317 |
printf(" Page size %8d b ", nand->writesize); printf(" OOB size %8d b ", nand->oobsize); printf(" Erase size %8d b ", nand->erasesize); printf(" subpagesize %8d b ", chip->subpagesize); printf(" options 0x%8x ", chip->options); printf(" bbt options 0x%8x ", chip->bbt_options); |
ce80ddc18
|
318 319 |
/* Set geometry info */ |
41ef372c1
|
320 321 322 |
setenv_hex("nand_writesize", nand->writesize); setenv_hex("nand_oobsize", nand->oobsize); setenv_hex("nand_erasesize", nand->erasesize); |
672ed2aee
|
323 |
} |
418396e21
|
324 325 326 327 |
static int raw_access(nand_info_t *nand, ulong addr, loff_t off, ulong count, int read) { int ret = 0; |
418396e21
|
328 329 330 331 332 333 334 335 |
while (count--) { /* Raw access */ mtd_oob_ops_t ops = { .datbuf = (u8 *)addr, .oobbuf = ((u8 *)addr) + nand->writesize, .len = nand->writesize, .ooblen = nand->oobsize, |
dfe64e2c8
|
336 |
.mode = MTD_OPS_RAW |
418396e21
|
337 |
}; |
6b94f118a
|
338 |
if (read) { |
dfe64e2c8
|
339 |
ret = mtd_read_oob(nand, off, &ops); |
6b94f118a
|
340 |
} else { |
dfe64e2c8
|
341 |
ret = mtd_write_oob(nand, off, &ops); |
6b94f118a
|
342 343 344 |
if (!ret) ret = nand_verify_page_oob(nand, &ops, off); } |
418396e21
|
345 346 347 348 349 350 351 352 353 354 355 356 357 358 |
if (ret) { printf("%s: error at offset %llx, ret %d ", __func__, (long long)off, ret); break; } addr += nand->writesize + nand->oobsize; off += nand->writesize; } return ret; } |
e834402fa
|
359 |
/* Adjust a chip/partition size down for bad blocks so we don't |
9b80aa8ec
|
360 |
* read/write past the end of a chip/partition by accident. |
e834402fa
|
361 362 363 364 365 366 367 368 369 370 371 372 373 374 375 376 377 378 379 380 381 382 383 |
*/ static void adjust_size_for_badblocks(loff_t *size, loff_t offset, int dev) { /* We grab the nand info object here fresh because this is usually * called after arg_off_size() which can change the value of dev. */ nand_info_t *nand = &nand_info[dev]; loff_t maxoffset = offset + *size; int badblocks = 0; /* count badblocks in NAND from offset to offset + size */ for (; offset < maxoffset; offset += nand->erasesize) { if (nand_block_isbad(nand, offset)) badblocks++; } /* adjust size if any bad blocks found */ if (badblocks) { *size -= badblocks * nand->erasesize; printf("size adjusted to 0x%llx (%d bad blocks) ", (unsigned long long)*size, badblocks); } } |
088f1b199
|
384 |
static int do_nand(cmd_tbl_t *cmdtp, int flag, int argc, char * const argv[]) |
addb2e165
|
385 |
{ |
ea533c260
|
386 387 |
int i, ret = 0; ulong addr; |
c39d6a0ea
|
388 |
loff_t off, size, maxsize; |
addb2e165
|
389 390 |
char *cmd, *s; nand_info_t *nand; |
6d0f6bcf3
|
391 392 |
#ifdef CONFIG_SYS_NAND_QUIET int quiet = CONFIG_SYS_NAND_QUIET; |
c750d2e66
|
393 |
#else |
2255b2d20
|
394 |
int quiet = 0; |
c750d2e66
|
395 |
#endif |
2255b2d20
|
396 |
const char *quiet_str = getenv("quiet"); |
ea533c260
|
397 |
int dev = nand_curr_device; |
8c5659a6d
|
398 |
int repeat = flag & CMD_FLAG_REPEAT; |
addb2e165
|
399 400 401 402 |
/* at least two arguments please */ if (argc < 2) goto usage; |
2255b2d20
|
403 404 |
if (quiet_str) quiet = simple_strtoul(quiet_str, NULL, 0) != 0; |
addb2e165
|
405 |
cmd = argv[1]; |
8c5659a6d
|
406 407 408 |
/* Only "dump" is repeatable. */ if (repeat && strcmp(cmd, "dump")) return 0; |
addb2e165
|
409 410 411 412 |
if (strcmp(cmd, "info") == 0) { putc(' '); |
6d0f6bcf3
|
413 |
for (i = 0; i < CONFIG_SYS_MAX_NAND_DEVICE; i++) { |
addb2e165
|
414 |
if (nand_info[i].name) |
ce80ddc18
|
415 |
nand_print_and_set_info(i); |
addb2e165
|
416 417 418 419 420 |
} return 0; } if (strcmp(cmd, "device") == 0) { |
addb2e165
|
421 |
if (argc < 3) { |
672ed2aee
|
422 423 |
putc(' '); |
ea533c260
|
424 |
if (dev < 0 || dev >= CONFIG_SYS_MAX_NAND_DEVICE) |
672ed2aee
|
425 426 |
puts("no devices available "); |
addb2e165
|
427 |
else |
ce80ddc18
|
428 |
nand_print_and_set_info(dev); |
addb2e165
|
429 430 |
return 0; } |
43a2b0e76
|
431 |
|
ea533c260
|
432 433 |
dev = (int)simple_strtoul(argv[2], NULL, 10); set_dev(dev); |
43a2b0e76
|
434 |
|
addb2e165
|
435 436 |
return 0; } |
c9f7351b5
|
437 438 |
#ifdef CONFIG_ENV_OFFSET_OOB /* this command operates only on the first nand device */ |
ea533c260
|
439 440 |
if (strcmp(cmd, "env.oob") == 0) return do_nand_env_oob(cmdtp, argc - 1, argv + 1); |
c9f7351b5
|
441 |
#endif |
ea533c260
|
442 443 444 445 446 447 448 449 |
/* The following commands operate on the current device, unless * overridden by a partition specifier. Note that if somehow the * current device is invalid, it will have to be changed to a valid * one before these commands can run, even if a partition specifier * for another device is to be used. */ if (dev < 0 || dev >= CONFIG_SYS_MAX_NAND_DEVICE || !nand_info[dev].name) { |
addb2e165
|
450 451 452 453 454 |
puts(" no devices available "); return 1; } |
ea533c260
|
455 |
nand = &nand_info[dev]; |
addb2e165
|
456 457 |
if (strcmp(cmd, "bad") == 0) { |
ea533c260
|
458 459 460 |
printf(" Device %d bad blocks: ", dev); |
addb2e165
|
461 462 |
for (off = 0; off < nand->size; off += nand->erasesize) if (nand_block_isbad(nand, off)) |
ea533c260
|
463 464 |
printf(" %08llx ", (unsigned long long)off); |
addb2e165
|
465 466 |
return 0; } |
856f05441
|
467 468 469 470 471 |
/* * Syntax is: * 0 1 2 3 4 * nand erase [clean] [off size] */ |
304863225
|
472 |
if (strncmp(cmd, "erase", 5) == 0 || strncmp(cmd, "scrub", 5) == 0) { |
2255b2d20
|
473 |
nand_erase_options_t opts; |
856f05441
|
474 |
/* "clean" at index 2 means request to write cleanmarker */ |
3043c045d
|
475 |
int clean = argc > 2 && !strcmp("clean", argv[2]); |
608998166
|
476 477 |
int scrub_yes = argc > 2 && !strcmp("-y", argv[2]); int o = (clean || scrub_yes) ? 3 : 2; |
304863225
|
478 |
int scrub = !strncmp(cmd, "scrub", 5); |
304863225
|
479 480 |
int spread = 0; int args = 2; |
608998166
|
481 482 483 484 485 486 487 488 489 490 491 492 493 494 495 496 |
const char *scrub_warn = "Warning: " "scrub option will erase all factory set bad blocks! " " " "There is no reliable way to recover them. " " " "Use this command only for testing purposes if you " " " "are sure of what you are doing! " " Really scrub this NAND flash? <y/N> "; |
304863225
|
497 498 499 500 501 |
if (cmd[5] != 0) { if (!strcmp(&cmd[5], ".spread")) { spread = 1; } else if (!strcmp(&cmd[5], ".part")) { |
304863225
|
502 503 |
args = 1; } else if (!strcmp(&cmd[5], ".chip")) { |
304863225
|
504 505 506 507 508 509 510 511 512 513 514 515 516 |
args = 0; } else { goto usage; } } /* * Don't allow missing arguments to cause full chip/partition * erases -- easy to do accidentally, e.g. with a misspelled * variable name. */ if (argc != o + args) goto usage; |
2255b2d20
|
517 |
|
304863225
|
518 519 |
printf(" NAND %s: ", cmd); |
856f05441
|
520 |
/* skip first two or three arguments, look for offset and size */ |
09c328075
|
521 522 523 524 525 526 |
if (mtd_arg_off_size(argc - o, argv + o, &dev, &off, &size, &maxsize, MTD_DEV_TYPE_NAND, nand_info[dev].size) != 0) return 1; if (set_dev(dev)) |
856f05441
|
527 |
return 1; |
2255b2d20
|
528 |
|
ea533c260
|
529 |
nand = &nand_info[dev]; |
2255b2d20
|
530 531 532 533 534 |
memset(&opts, 0, sizeof(opts)); opts.offset = off; opts.length = size; opts.jffs2 = clean; opts.quiet = quiet; |
304863225
|
535 |
opts.spread = spread; |
2255b2d20
|
536 537 |
if (scrub) { |
a5dffa4b6
|
538 |
if (scrub_yes) { |
608998166
|
539 |
opts.scrub = 1; |
a5dffa4b6
|
540 541 542 |
} else { puts(scrub_warn); if (confirm_yesno()) { |
6b94b4962
|
543 |
opts.scrub = 1; |
a5dffa4b6
|
544 |
} else { |
6b94b4962
|
545 546 |
puts("scrub aborted "); |
46aabcc44
|
547 |
return 1; |
6b94b4962
|
548 |
} |
2255b2d20
|
549 550 551 |
} } ret = nand_erase_opts(nand, &opts); |
addb2e165
|
552 553 554 555 556 557 558 559 560 |
printf("%s ", ret ? "ERROR" : "OK"); return ret == 0 ? 0 : 1; } if (strncmp(cmd, "dump", 4) == 0) { if (argc < 3) goto usage; |
addb2e165
|
561 |
off = (int)simple_strtoul(argv[2], NULL, 16); |
8c5659a6d
|
562 |
ret = nand_dump(nand, off, !strcmp(&cmd[4], ".oob"), repeat); |
addb2e165
|
563 564 |
return ret == 0 ? 1 : 0; |
addb2e165
|
565 |
} |
addb2e165
|
566 |
if (strncmp(cmd, "read", 4) == 0 || strncmp(cmd, "write", 5) == 0) { |
ea533c260
|
567 |
size_t rwsize; |
418396e21
|
568 |
ulong pagecount = 1; |
2255b2d20
|
569 |
int read; |
ced199dc8
|
570 |
int raw = 0; |
2255b2d20
|
571 |
|
addb2e165
|
572 573 |
if (argc < 4) goto usage; |
2255b2d20
|
574 |
|
addb2e165
|
575 |
addr = (ulong)simple_strtoul(argv[2], NULL, 16); |
2255b2d20
|
576 |
read = strncmp(cmd, "read", 4) == 0; /* 1 = read, 0 = write */ |
856f05441
|
577 578 |
printf(" NAND %s: ", read ? "read" : "write"); |
4cbb651b2
|
579 |
|
2255b2d20
|
580 |
s = strchr(cmd, '.'); |
418396e21
|
581 |
|
8d75c8964
|
582 |
if (s && !strcmp(s, ".raw")) { |
418396e21
|
583 |
raw = 1; |
09c328075
|
584 585 586 587 588 589 |
if (mtd_arg_off(argv[3], &dev, &off, &size, &maxsize, MTD_DEV_TYPE_NAND, nand_info[dev].size)) return 1; if (set_dev(dev)) |
418396e21
|
590 |
return 1; |
93d3232d9
|
591 |
nand = &nand_info[dev]; |
418396e21
|
592 593 594 595 596 597 598 599 600 601 602 603 604 605 |
if (argc > 4 && !str2long(argv[4], &pagecount)) { printf("'%s' is not a number ", argv[4]); return 1; } if (pagecount * nand->writesize > size) { puts("Size exceeds partition or device limit "); return -1; } rwsize = pagecount * (nand->writesize + nand->oobsize); } else { |
09c328075
|
606 607 608 609 610 611 612 |
if (mtd_arg_off_size(argc - 3, argv + 3, &dev, &off, &size, &maxsize, MTD_DEV_TYPE_NAND, nand_info[dev].size) != 0) return 1; if (set_dev(dev)) |
418396e21
|
613 |
return 1; |
e834402fa
|
614 615 616 |
/* size is unspecified */ if (argc < 5) adjust_size_for_badblocks(&size, off, dev); |
418396e21
|
617 618 |
rwsize = size; } |
93d3232d9
|
619 |
nand = &nand_info[dev]; |
984e03cdf
|
620 621 |
if (!s || !strcmp(s, ".jffs2") || !strcmp(s, ".e") || !strcmp(s, ".i")) { |
dfbf617ff
|
622 |
if (read) |
ea533c260
|
623 |
ret = nand_read_skip_bad(nand, off, &rwsize, |
c39d6a0ea
|
624 |
NULL, maxsize, |
4b0708093
|
625 |
(u_char *)addr); |
dfbf617ff
|
626 |
else |
ea533c260
|
627 |
ret = nand_write_skip_bad(nand, off, &rwsize, |
c39d6a0ea
|
628 |
NULL, maxsize, |
6b94f118a
|
629 630 |
(u_char *)addr, WITH_WR_VERIFY); |
c9494866d
|
631 632 633 634 635 636 637 |
#ifdef CONFIG_CMD_NAND_TRIMFFS } else if (!strcmp(s, ".trimffs")) { if (read) { printf("Unknown nand command suffix '%s' ", s); return 1; } |
c39d6a0ea
|
638 639 |
ret = nand_write_skip_bad(nand, off, &rwsize, NULL, maxsize, (u_char *)addr, |
6b94f118a
|
640 |
WITH_DROP_FFS | WITH_WR_VERIFY); |
c9494866d
|
641 |
#endif |
dfc99e143
|
642 |
} else if (!strcmp(s, ".oob")) { |
cfa460adf
|
643 644 645 |
/* out-of-band data */ mtd_oob_ops_t ops = { .oobbuf = (u8 *)addr, |
ea533c260
|
646 |
.ooblen = rwsize, |
dfe64e2c8
|
647 |
.mode = MTD_OPS_RAW |
cfa460adf
|
648 |
}; |
62d4f4365
|
649 |
if (read) |
dfe64e2c8
|
650 |
ret = mtd_read_oob(nand, off, &ops); |
62d4f4365
|
651 |
else |
dfe64e2c8
|
652 |
ret = mtd_write_oob(nand, off, &ops); |
418396e21
|
653 654 |
} else if (raw) { ret = raw_access(nand, addr, off, pagecount, read); |
856f05441
|
655 |
} else { |
984e03cdf
|
656 657 658 |
printf("Unknown nand command suffix '%s'. ", s); return 1; |
2255b2d20
|
659 |
} |
ea533c260
|
660 661 |
printf(" %zu bytes %s: %s ", rwsize, |
2255b2d20
|
662 |
read ? "read" : "written", ret ? "ERROR" : "OK"); |
addb2e165
|
663 664 665 |
return ret == 0 ? 0 : 1; } |
2255b2d20
|
666 |
|
3287f6d38
|
667 668 669 670 671 672 673 674 675 676 677 678 679 680 681 682 683 684 685 686 687 688 |
#ifdef CONFIG_CMD_NAND_TORTURE if (strcmp(cmd, "torture") == 0) { if (argc < 3) goto usage; if (!str2off(argv[2], &off)) { puts("Offset is not a valid number "); return 1; } printf(" NAND torture: device %d offset 0x%llx size 0x%x ", dev, off, nand->erasesize); ret = nand_torture(nand, off); printf(" %s ", ret ? "Failed" : "Passed"); return ret == 0 ? 0 : 1; } #endif |
2255b2d20
|
689 |
if (strcmp(cmd, "markbad") == 0) { |
8360b66ba
|
690 691 |
argc -= 2; argv += 2; |
2255b2d20
|
692 |
|
8360b66ba
|
693 694 695 696 697 |
if (argc <= 0) goto usage; while (argc > 0) { addr = simple_strtoul(*argv, NULL, 16); |
dfe64e2c8
|
698 |
if (mtd_block_markbad(nand, addr)) { |
8360b66ba
|
699 700 701 702 703 704 705 706 707 708 709 710 711 |
printf("block 0x%08lx NOT marked " "as bad! ERROR %d ", addr, ret); ret = 1; } else { printf("block 0x%08lx successfully " "marked as bad ", addr); } --argc; ++argv; |
2255b2d20
|
712 |
} |
8360b66ba
|
713 |
return ret; |
2255b2d20
|
714 |
} |
dfbf617ff
|
715 |
|
2255b2d20
|
716 717 718 719 |
if (strcmp(cmd, "biterr") == 0) { /* todo */ return 1; } |
50657c273
|
720 |
#ifdef CONFIG_CMD_NAND_LOCK_UNLOCK |
2255b2d20
|
721 |
if (strcmp(cmd, "lock") == 0) { |
50657c273
|
722 |
int tight = 0; |
2255b2d20
|
723 724 725 726 727 728 729 |
int status = 0; if (argc == 3) { if (!strcmp("tight", argv[2])) tight = 1; if (!strcmp("status", argv[2])) status = 1; } |
2255b2d20
|
730 |
if (status) { |
50657c273
|
731 |
do_nand_status(nand); |
cfa460adf
|
732 |
} else { |
5e1dae5c3
|
733 734 735 736 737 738 739 740 |
if (!nand_lock(nand, tight)) { puts("NAND flash successfully locked "); } else { puts("Error locking NAND flash "); return 1; } |
2255b2d20
|
741 742 743 |
} return 0; } |
eee623a50
|
744 745 746 747 748 749 750 |
if (strncmp(cmd, "unlock", 5) == 0) { int allexcept = 0; s = strchr(cmd, '.'); if (s && !strcmp(s, ".allexcept")) allexcept = 1; |
09c328075
|
751 752 753 754 755 756 |
if (mtd_arg_off_size(argc - 2, argv + 2, &dev, &off, &size, &maxsize, MTD_DEV_TYPE_NAND, nand_info[dev].size) < 0) return 1; if (set_dev(dev)) |
856f05441
|
757 |
return 1; |
2255b2d20
|
758 |
|
eee623a50
|
759 |
if (!nand_unlock(&nand_info[dev], off, size, allexcept)) { |
5e1dae5c3
|
760 761 762 763 |
puts("NAND flash successfully unlocked "); } else { puts("Error unlocking NAND flash, " |
3043c045d
|
764 765 |
"write and erase will probably fail "); |
5e1dae5c3
|
766 767 |
return 1; } |
2255b2d20
|
768 769 |
return 0; } |
50657c273
|
770 |
#endif |
2255b2d20
|
771 |
|
addb2e165
|
772 |
usage: |
4c12eeb8b
|
773 |
return CMD_RET_USAGE; |
addb2e165
|
774 |
} |
088f1b199
|
775 776 |
#ifdef CONFIG_SYS_LONGHELP static char nand_help_text[] = |
a89c33db9
|
777 778 779 780 781 782 783 784 785 786 787 788 |
"info - show available NAND devices " "nand device [dev] - show or set current device " "nand read - addr off|partition size " "nand write - addr off|partition size " " read/write 'size' bytes starting at offset 'off' " " to/from memory address 'addr', skipping bad blocks. " |
418396e21
|
789 790 791 792 793 794 |
"nand read.raw - addr off|partition [count] " "nand write.raw - addr off|partition [count] " " Use read.raw/write.raw to avoid ECC and access the flash as-is. " |
c9494866d
|
795 796 797 798 799 800 801 802 803 804 |
#ifdef CONFIG_CMD_NAND_TRIMFFS "nand write.trimffs - addr off|partition size " " write 'size' bytes starting at offset 'off' from memory address " " 'addr', skipping bad blocks and dropping any pages at the end " " of eraseblocks that contain only 0xFF " #endif |
eb3abce89
|
805 |
"nand erase[.spread] [clean] off size - erase 'size' bytes " |
304863225
|
806 807 808 809 810 811 812 813 814 815 |
"from offset 'off' " " With '.spread', erase enough for given file size, otherwise, " " 'size' includes skipped bad blocks. " "nand erase.part [clean] partition - erase entire mtd partition' " "nand erase.chip [clean] - erase entire chip' " |
a89c33db9
|
816 817 818 819 |
"nand bad - show bad blocks " "nand dump[.oob] off - dump page " |
3287f6d38
|
820 821 822 823 |
#ifdef CONFIG_CMD_NAND_TORTURE "nand torture off - torture block at offset " #endif |
608998166
|
824 825 |
"nand scrub [-y] off size | scrub.part partition | scrub.chip " |
304863225
|
826 827 |
" really clean NAND erasing bad blocks (UNSAFE) " |
a89c33db9
|
828 829 830 |
"nand markbad off [...] - mark bad block(s) at offset (UNSAFE) " "nand biterr off - make a bit error at offset (UNSAFE)" |
50657c273
|
831 |
#ifdef CONFIG_CMD_NAND_LOCK_UNLOCK |
a89c33db9
|
832 833 834 835 836 837 |
" " "nand lock [tight] [status] " " bring nand to lock state or display locked pages " |
eee623a50
|
838 |
"nand unlock[.allexcept] [offset] [size] - unlock section" |
50657c273
|
839 |
#endif |
c9f7351b5
|
840 841 842 843 844 845 846 847 848 849 |
#ifdef CONFIG_ENV_OFFSET_OOB " " "nand env.oob - environment offset in OOB of block 0 of" " first device. " "nand env.oob set off|partition - set enviromnent offset " "nand env.oob get - get environment offset" #endif |
088f1b199
|
850 851 852 853 854 855 |
""; #endif U_BOOT_CMD( nand, CONFIG_SYS_MAXARGS, 1, do_nand, "NAND sub-system", nand_help_text |
50657c273
|
856 |
); |
addb2e165
|
857 |
|
856f05441
|
858 |
static int nand_load_image(cmd_tbl_t *cmdtp, nand_info_t *nand, |
4b0708093
|
859 |
ulong offset, ulong addr, char *cmd) |
addb2e165
|
860 |
{ |
addb2e165
|
861 |
int r; |
67d668bf9
|
862 |
char *s; |
4ca79f477
|
863 |
size_t cnt; |
21d29f7f9
|
864 |
#if defined(CONFIG_IMAGE_FORMAT_LEGACY) |
addb2e165
|
865 |
image_header_t *hdr; |
21d29f7f9
|
866 |
#endif |
09475f752
|
867 |
#if defined(CONFIG_FIT) |
3bab76a26
|
868 |
const void *fit_hdr = NULL; |
09475f752
|
869 |
#endif |
10e038932
|
870 871 872 |
s = strchr(cmd, '.'); if (s != NULL && |
65d8bc94d
|
873 |
(strcmp(s, ".jffs2") && strcmp(s, ".e") && strcmp(s, ".i"))) { |
984e03cdf
|
874 875 |
printf("Unknown nand load suffix '%s' ", s); |
770605e4f
|
876 |
bootstage_error(BOOTSTAGE_ID_NAND_SUFFIX); |
984e03cdf
|
877 878 |
return 1; } |
addb2e165
|
879 |
|
856f05441
|
880 881 882 |
printf(" Loading from %s, offset 0x%lx ", nand->name, offset); |
addb2e165
|
883 |
|
cfa460adf
|
884 |
cnt = nand->writesize; |
c39d6a0ea
|
885 886 |
r = nand_read_skip_bad(nand, offset, &cnt, NULL, nand->size, (u_char *)addr); |
addb2e165
|
887 |
if (r) { |
856f05441
|
888 889 |
puts("** Read error "); |
770605e4f
|
890 |
bootstage_error(BOOTSTAGE_ID_NAND_HDR_READ); |
addb2e165
|
891 892 |
return 1; } |
770605e4f
|
893 |
bootstage_mark(BOOTSTAGE_ID_NAND_HDR_READ); |
addb2e165
|
894 |
|
9a4daad0a
|
895 |
switch (genimg_get_format ((void *)addr)) { |
21d29f7f9
|
896 |
#if defined(CONFIG_IMAGE_FORMAT_LEGACY) |
d5934ad77
|
897 898 |
case IMAGE_FORMAT_LEGACY: hdr = (image_header_t *)addr; |
770605e4f
|
899 |
bootstage_mark(BOOTSTAGE_ID_NAND_TYPE); |
d5934ad77
|
900 |
image_print_contents (hdr); |
addb2e165
|
901 |
|
d5934ad77
|
902 903 |
cnt = image_get_image_size (hdr); break; |
21d29f7f9
|
904 |
#endif |
d5934ad77
|
905 906 |
#if defined(CONFIG_FIT) case IMAGE_FORMAT_FIT: |
09475f752
|
907 |
fit_hdr = (const void *)addr; |
09475f752
|
908 909 910 911 912 |
puts ("Fit image detected... "); cnt = fit_get_size (fit_hdr); break; |
d5934ad77
|
913 914 |
#endif default: |
770605e4f
|
915 |
bootstage_error(BOOTSTAGE_ID_NAND_TYPE); |
d5934ad77
|
916 917 |
puts ("** Unknown image type "); |
addb2e165
|
918 919 |
return 1; } |
770605e4f
|
920 |
bootstage_mark(BOOTSTAGE_ID_NAND_TYPE); |
addb2e165
|
921 |
|
c39d6a0ea
|
922 923 |
r = nand_read_skip_bad(nand, offset, &cnt, NULL, nand->size, (u_char *)addr); |
addb2e165
|
924 |
if (r) { |
856f05441
|
925 926 |
puts("** Read error "); |
770605e4f
|
927 |
bootstage_error(BOOTSTAGE_ID_NAND_READ); |
addb2e165
|
928 929 |
return 1; } |
770605e4f
|
930 |
bootstage_mark(BOOTSTAGE_ID_NAND_READ); |
addb2e165
|
931 |
|
09475f752
|
932 933 |
#if defined(CONFIG_FIT) /* This cannot be done earlier, we need complete FIT image in RAM first */ |
3bab76a26
|
934 935 |
if (genimg_get_format ((void *)addr) == IMAGE_FORMAT_FIT) { if (!fit_check_format (fit_hdr)) { |
770605e4f
|
936 |
bootstage_error(BOOTSTAGE_ID_NAND_FIT_READ); |
3bab76a26
|
937 938 939 940 |
puts ("** Bad FIT image format "); return 1; } |
770605e4f
|
941 |
bootstage_mark(BOOTSTAGE_ID_NAND_FIT_READ_OK); |
3bab76a26
|
942 943 |
fit_print_contents (fit_hdr); } |
09475f752
|
944 |
#endif |
addb2e165
|
945 946 947 |
/* Loading ok, update default load address */ load_addr = addr; |
67d668bf9
|
948 |
return bootm_maybe_autostart(cmdtp, cmd); |
addb2e165
|
949 |
} |
088f1b199
|
950 951 |
static int do_nandboot(cmd_tbl_t *cmdtp, int flag, int argc, char * const argv[]) |
856f05441
|
952 953 954 955 |
{ char *boot_device = NULL; int idx; ulong addr, offset = 0; |
0c8a84916
|
956 |
#if defined(CONFIG_CMD_MTDPARTS) |
856f05441
|
957 958 959 960 961 962 963 964 965 966 967 968 969 970 971 972 |
struct mtd_device *dev; struct part_info *part; u8 pnum; if (argc >= 2) { char *p = (argc == 2) ? argv[1] : argv[2]; if (!(str2long(p, &addr)) && (mtdparts_init() == 0) && (find_dev_and_part(p, &dev, &pnum, &part) == 0)) { if (dev->id->type != MTD_DEV_TYPE_NAND) { puts("Not a NAND device "); return 1; } if (argc > 3) goto usage; if (argc == 3) |
10e038932
|
973 |
addr = simple_strtoul(argv[1], NULL, 16); |
856f05441
|
974 |
else |
6d0f6bcf3
|
975 |
addr = CONFIG_SYS_LOAD_ADDR; |
856f05441
|
976 |
return nand_load_image(cmdtp, &nand_info[dev->id->num], |
4b0708093
|
977 |
part->offset, addr, argv[0]); |
856f05441
|
978 979 980 |
} } #endif |
770605e4f
|
981 |
bootstage_mark(BOOTSTAGE_ID_NAND_PART); |
856f05441
|
982 983 |
switch (argc) { case 1: |
6d0f6bcf3
|
984 |
addr = CONFIG_SYS_LOAD_ADDR; |
856f05441
|
985 986 987 988 989 990 991 992 993 994 995 996 997 998 999 1000 |
boot_device = getenv("bootdevice"); break; case 2: addr = simple_strtoul(argv[1], NULL, 16); boot_device = getenv("bootdevice"); break; case 3: addr = simple_strtoul(argv[1], NULL, 16); boot_device = argv[2]; break; case 4: addr = simple_strtoul(argv[1], NULL, 16); boot_device = argv[2]; offset = simple_strtoul(argv[3], NULL, 16); break; default: |
0c8a84916
|
1001 |
#if defined(CONFIG_CMD_MTDPARTS) |
856f05441
|
1002 1003 |
usage: #endif |
770605e4f
|
1004 |
bootstage_error(BOOTSTAGE_ID_NAND_SUFFIX); |
4c12eeb8b
|
1005 |
return CMD_RET_USAGE; |
856f05441
|
1006 |
} |
770605e4f
|
1007 |
bootstage_mark(BOOTSTAGE_ID_NAND_SUFFIX); |
856f05441
|
1008 1009 1010 1011 1012 |
if (!boot_device) { puts(" ** No boot device ** "); |
770605e4f
|
1013 |
bootstage_error(BOOTSTAGE_ID_NAND_BOOT_DEVICE); |
856f05441
|
1014 1015 |
return 1; } |
770605e4f
|
1016 |
bootstage_mark(BOOTSTAGE_ID_NAND_BOOT_DEVICE); |
addb2e165
|
1017 |
|
856f05441
|
1018 |
idx = simple_strtoul(boot_device, NULL, 16); |
6d0f6bcf3
|
1019 |
if (idx < 0 || idx >= CONFIG_SYS_MAX_NAND_DEVICE || !nand_info[idx].name) { |
856f05441
|
1020 1021 1022 |
printf(" ** Device %d not available ", idx); |
770605e4f
|
1023 |
bootstage_error(BOOTSTAGE_ID_NAND_AVAILABLE); |
856f05441
|
1024 1025 |
return 1; } |
770605e4f
|
1026 |
bootstage_mark(BOOTSTAGE_ID_NAND_AVAILABLE); |
856f05441
|
1027 1028 1029 1030 1031 |
return nand_load_image(cmdtp, &nand_info[idx], offset, addr, argv[0]); } U_BOOT_CMD(nboot, 4, 1, do_nandboot, |
2fb2604d5
|
1032 |
"boot from NAND device", |
a89c33db9
|
1033 1034 |
"[partition] | [[[loadAddr] dev] offset]" ); |