cmd_nand.c 26.8 KB
Newer Older
W
wdenk 已提交
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>
W
wdenk 已提交
6 7 8
 *
 * Added 16-bit nand support
 * (C) 2004 Texas Instruments
W
wdenk 已提交
9 10 11
 */

#include <common.h>
12 13


14
#ifndef CONFIG_NAND_LEGACY
15 16 17 18 19 20
/*
 *
 * New NAND support
 *
 */
#include <common.h>
21
#include <linux/mtd/mtd.h>
22

23
#if defined(CONFIG_CMD_NAND)
24 25 26 27 28 29 30 31

#include <command.h>
#include <watchdog.h>
#include <malloc.h>
#include <asm/byteorder.h>
#include <jffs2/jffs2.h>
#include <nand.h>

32
#if defined(CONFIG_CMD_JFFS2) && defined(CONFIG_JFFS2_CMDLINE)
33 34 35 36 37

/* parition handling routines */
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,
W
William Juul 已提交
38
                      u8 *part_num, struct part_info **part);
39 40
#endif

41
static int nand_dump(nand_info_t *nand, ulong off, int only_oob)
42 43
{
	int i;
44
	u_char *datbuf, *oobbuf, *p;
45

46 47 48
	datbuf = malloc(nand->writesize + nand->oobsize);
	oobbuf = malloc(nand->oobsize);
	if (!datbuf || !oobbuf) {
49 50 51
		puts("No memory for page buffer\n");
		return 1;
	}
52 53
	off &= ~(nand->writesize - 1);
	loff_t addr = (loff_t) off;
54 55 56 57 58 59 60 61
	struct mtd_oob_ops ops;
	memset(&ops, 0, sizeof(ops));
	ops.datbuf = datbuf;
	ops.oobbuf = oobbuf; /* must exist, but oob data will be appended to ops.datbuf */
	ops.len = nand->writesize;
	ops.ooblen = nand->oobsize;
	ops.mode = MTD_OOB_RAW;
	i = nand->read_oob(nand, addr, &ops);
62
	if (i < 0) {
63
		printf("Error (%d) reading page %08lx\n", i, off);
64 65
		free(datbuf);
		free(oobbuf);
66 67
		return 1;
	}
68
	printf("Page %08lx dump:\n", off);
69 70 71
	i = nand->writesize >> 4;
	p = datbuf;
		
72
	while (i--) {
73 74 75 76
		if (!only_oob)
			printf("\t%02x %02x %02x %02x %02x %02x %02x %02x"
			       "  %02x %02x %02x %02x %02x %02x %02x %02x\n",
			       p[0], p[1], p[2], p[3], p[4], p[5], p[6], p[7],
S
Scott Wood 已提交
77 78
			       p[8], p[9], p[10], p[11], p[12], p[13], p[14],
			       p[15]);
79 80 81 82 83
		p += 16;
	}
	puts("OOB:\n");
	i = nand->oobsize >> 3;
	while (i--) {
84 85
		printf("\t%02x %02x %02x %02x %02x %02x %02x %02x\n",
		       p[0], p[1], p[2], p[3], p[4], p[5], p[6], p[7]);
86 87
		p += 8;
	}
88 89
	free(datbuf);
	free(oobbuf);
90 91 92 93 94 95

	return 0;
}

/* ------------------------------------------------------------------------- */

96
static inline int str2long(char *p, ulong *num)
97
{
98
	char *endptr;
99

100 101 102
	*num = simple_strtoul(p, &endptr, 16);
	return (*p != '\0' && *endptr == '\0') ? 1 : 0;
}
103

104
static int
105
arg_off_size(int argc, char *argv[], nand_info_t *nand, ulong *off, size_t *size)
106 107
{
	int idx = nand_curr_device;
108
#if defined(CONFIG_CMD_JFFS2) && defined(CONFIG_JFFS2_CMDLINE)
109 110 111 112 113 114 115 116 117 118 119 120 121
	struct mtd_device *dev;
	struct part_info *part;
	u8 pnum;

	if (argc >= 1 && !(str2long(argv[0], off))) {
		if ((mtdparts_init() == 0) &&
		    (find_dev_and_part(argv[0], &dev, &pnum, &part) == 0)) {
			if (dev->id->type != MTD_DEV_TYPE_NAND) {
				puts("not a NAND device\n");
				return -1;
			}
			*off = part->offset;
			if (argc >= 2) {
122
				if (!(str2long(argv[1], (ulong *)size))) {
123 124 125 126 127 128 129 130 131 132 133
					printf("'%s' is not a number\n", argv[1]);
					return -1;
				}
				if (*size > part->size)
					*size = part->size;
			} else {
				*size = part->size;
			}
			idx = dev->id->num;
			*nand = nand_info[idx];
			goto out;
134
		}
135
	}
136 137
#endif

138 139 140 141 142 143 144 145
	if (argc >= 1) {
		if (!(str2long(argv[0], off))) {
			printf("'%s' is not a number\n", argv[0]);
			return -1;
		}
	} else {
		*off = 0;
	}
146

147
	if (argc >= 2) {
148
		if (!(str2long(argv[1], (ulong *)size))) {
149 150 151 152 153
			printf("'%s' is not a number\n", argv[1]);
			return -1;
		}
	} else {
		*size = nand->size - *off;
154 155
	}

156
#if  defined(CONFIG_CMD_JFFS2) && defined(CONFIG_JFFS2_CMDLINE)
157 158 159 160 161 162
out:
#endif
	printf("device %d ", idx);
	if (*size == nand->size)
		puts("whole chip\n");
	else
163
		printf("offset 0x%lx, size 0x%x\n", *off, *size);
164
	return 0;
165 166 167 168
}

int do_nand(cmd_tbl_t * cmdtp, int flag, int argc, char *argv[])
{
169
	int i, dev, ret = 0;
170 171
	ulong addr, off;
	size_t size;
172 173
	char *cmd, *s;
	nand_info_t *nand;
M
Matthias Fuchs 已提交
174 175 176
#ifdef CFG_NAND_QUIET
	int quiet = CFG_NAND_QUIET;
#else
177
	int quiet = 0;
M
Matthias Fuchs 已提交
178
#endif
179
	const char *quiet_str = getenv("quiet");
180 181 182 183 184

	/* at least two arguments please */
	if (argc < 2)
		goto usage;

185 186 187
	if (quiet_str)
		quiet = simple_strtoul(quiet_str, NULL, 0) != 0;

188 189 190 191 192 193 194
	cmd = argv[1];

	if (strcmp(cmd, "info") == 0) {

		putc('\n');
		for (i = 0; i < CFG_MAX_NAND_DEVICE; i++) {
			if (nand_info[i].name)
195
				printf("Device %d: %s, sector size %u KiB\n",
196 197
 				       i, nand_info[i].name,
				       nand_info[i].erasesize >> 10);
198 199 200 201 202 203 204 205
		}
		return 0;
	}

	if (strcmp(cmd, "device") == 0) {

		if (argc < 3) {
			if ((nand_curr_device < 0) ||
206
			    (nand_curr_device >= CFG_MAX_NAND_DEVICE))
207 208 209
				puts("\nno devices available\n");
			else
				printf("\nDevice %d: %s\n", nand_curr_device,
210
				       nand_info[nand_curr_device].name);
211 212 213 214 215 216 217 218 219 220
			return 0;
		}
		dev = (int)simple_strtoul(argv[2], NULL, 10);
		if (dev < 0 || dev >= CFG_MAX_NAND_DEVICE || !nand_info[dev].name) {
			puts("No such device\n");
			return 1;
		}
		printf("Device %d: %s", dev, nand_info[dev].name);
		puts("... is now current device\n");
		nand_curr_device = dev;
221 222 223 224 225 226 227 228

#ifdef CFG_NAND_SELECT_DEVICE
		/*
		 * Select the chip in the board/cpu specific driver
		 */
		board_nand_select_device(nand_info[dev].priv, dev);
#endif

229 230 231 232
		return 0;
	}

	if (strcmp(cmd, "bad") != 0 && strcmp(cmd, "erase") != 0 &&
233 234 235 236 237
	    strncmp(cmd, "dump", 4) != 0 &&
	    strncmp(cmd, "read", 4) != 0 && strncmp(cmd, "write", 5) != 0 &&
	    strcmp(cmd, "scrub") != 0 && strcmp(cmd, "markbad") != 0 &&
	    strcmp(cmd, "biterr") != 0 &&
	    strcmp(cmd, "lock") != 0 && strcmp(cmd, "unlock") != 0 )
238 239 240 241 242 243 244 245 246 247 248 249 250 251
		goto usage;

	/* the following commands operate on the current device */
	if (nand_curr_device < 0 || nand_curr_device >= CFG_MAX_NAND_DEVICE ||
	    !nand_info[nand_curr_device].name) {
		puts("\nno devices available\n");
		return 1;
	}
	nand = &nand_info[nand_curr_device];

	if (strcmp(cmd, "bad") == 0) {
		printf("\nDevice %d bad blocks:\n", nand_curr_device);
		for (off = 0; off < nand->size; off += nand->erasesize)
			if (nand_block_isbad(nand, off))
252
				printf("  %08lx\n", off);
253 254 255
		return 0;
	}

256 257 258 259 260
	/*
	 * Syntax is:
	 *   0    1     2       3    4
	 *   nand erase [clean] [off size]
	 */
261 262
	if (strcmp(cmd, "erase") == 0 || strcmp(cmd, "scrub") == 0) {
		nand_erase_options_t opts;
263
		/* "clean" at index 2 means request to write cleanmarker */
264
		int clean = argc > 2 && !strcmp("clean", argv[2]);
265
		int o = clean ? 3 : 2;
266 267
		int scrub = !strcmp(cmd, "scrub");

268 269 270 271
		printf("\nNAND %s: ", scrub ? "scrub" : "erase");
		/* skip first two or three arguments, look for offset and size */
		if (arg_off_size(argc - o, argv + o, nand, &off, &size) != 0)
			return 1;
272 273 274 275 276 277 278 279

		memset(&opts, 0, sizeof(opts));
		opts.offset = off;
		opts.length = size;
		opts.jffs2  = clean;
		opts.quiet  = quiet;

		if (scrub) {
280 281 282 283 284 285 286 287 288 289 290
			puts("Warning: "
			     "scrub option will erase all factory set "
			     "bad blocks!\n"
			     "         "
			     "There is no reliable way to recover them.\n"
			     "         "
			     "Use this command only for testing purposes "
			     "if you\n"
			     "         "
			     "are sure of what you are doing!\n"
			     "\nReally scrub this NAND flash? <y/N>\n");
291 292 293 294

			if (getc() == 'y' && getc() == '\r') {
				opts.scrub = 1;
			} else {
295
				puts("scrub aborted\n");
296 297 298 299
				return -1;
			}
		}
		ret = nand_erase_opts(nand, &opts);
300 301 302 303 304 305 306 307 308 309 310 311 312
		printf("%s\n", ret ? "ERROR" : "OK");

		return ret == 0 ? 0 : 1;
	}

	if (strncmp(cmd, "dump", 4) == 0) {
		if (argc < 3)
			goto usage;

		s = strchr(cmd, '.');
		off = (int)simple_strtoul(argv[2], NULL, 16);

		if (s != NULL && strcmp(s, ".oob") == 0)
313
			ret = nand_dump(nand, off, 1);
314
		else
315
			ret = nand_dump(nand, off, 0);
316 317 318 319 320 321

		return ret == 0 ? 1 : 0;

	}

	if (strncmp(cmd, "read", 4) == 0 || strncmp(cmd, "write", 5) == 0) {
322 323
		int read;

324 325
		if (argc < 4)
			goto usage;
326

327 328
		addr = (ulong)simple_strtoul(argv[2], NULL, 16);

329
		read = strncmp(cmd, "read", 4) == 0; /* 1 = read, 0 = write */
330 331 332
		printf("\nNAND %s: ", read ? "read" : "write");
		if (arg_off_size(argc - 3, argv + 3, nand, &off, &size) != 0)
			return 1;
W
William Juul 已提交
333

334
		s = strchr(cmd, '.');
335 336
		if (!s || !strcmp(s, ".jffs2") ||
		    !strcmp(s, ".e") || !strcmp(s, ".i")) {
S
Scott Wood 已提交
337 338 339 340 341 342
			if (read)
				ret = nand_read_skip_bad(nand, off, &size,
				                         (u_char *)addr);
			else
				ret = nand_write_skip_bad(nand, off, &size,
				                          (u_char *)addr);
343
		} else if (s != NULL && !strcmp(s, ".oob")) {
344 345 346 347 348 349 350
			/* out-of-band data */
			mtd_oob_ops_t ops = {
				.oobbuf = (u8 *)addr,
				.ooblen = size,
				.mode = MTD_OOB_RAW
			};

351
			if (read)
352
				ret = nand->read_oob(nand, off, &ops);
353
			else
354
				ret = nand->write_oob(nand, off, &ops);
355
		} else {
356 357
			printf("Unknown nand command suffix '%s'.\n", s);
			return 1;
358 359
		}

360
		printf(" %d bytes %s: %s\n", size,
361
		       read ? "read" : "written", ret ? "ERROR" : "OK");
362 363 364

		return ret == 0 ? 0 : 1;
	}
365 366 367 368 369 370 371 372 373 374 375 376 377 378 379

	if (strcmp(cmd, "markbad") == 0) {
		addr = (ulong)simple_strtoul(argv[2], NULL, 16);

		int ret = nand->block_markbad(nand, addr);
		if (ret == 0) {
			printf("block 0x%08lx successfully marked as bad\n",
			       (ulong) addr);
			return 0;
		} else {
			printf("block 0x%08lx NOT marked as bad! ERROR %d\n",
			       (ulong) addr, ret);
		}
		return 1;
	}
S
Scott Wood 已提交
380

381 382 383 384 385 386 387 388 389 390 391 392 393 394
	if (strcmp(cmd, "biterr") == 0) {
		/* todo */
		return 1;
	}

	if (strcmp(cmd, "lock") == 0) {
		int tight  = 0;
		int status = 0;
		if (argc == 3) {
			if (!strcmp("tight", argv[2]))
				tight = 1;
			if (!strcmp("status", argv[2]))
				status = 1;
		}
395 396
/*
 * ! BROKEN !
397
 *
398 399 400
 * TODO: must be implemented and tested by someone with HW
 */
#if 0
401
		if (status) {
402
			ulong block_start = 0;
403
			ulong off;
W
William Juul 已提交
404
			int last_status = -1;
405

406 407 408 409 410
			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\n",
			       (nand_chip->read_byte(nand) & 0x80 ?
W
William Juul 已提交
411
			       "NOT " : ""));
412 413

			for (off = 0; off < nand->size; off += nand->writesize) {
W
William Juul 已提交
414 415 416 417 418 419
				int s = nand_get_lock_status(nand, off);

				/* print message only if status has changed
				 * or at end of chip
				 */
				if (off == nand->size - nand->writesize
420
				    || (s != last_status && off != 0))	{
W
William Juul 已提交
421 422

					printf("%08lx - %08lx: %8d pages %s%s%s\n",
423 424 425 426 427 428
					       block_start,
					       off-1,
					       (off-block_start)/nand->writesize,
					       ((last_status & NAND_LOCK_STATUS_TIGHT) ? "TIGHT " : ""),
					       ((last_status & NAND_LOCK_STATUS_LOCK) ? "LOCK " : ""),
					       ((last_status & NAND_LOCK_STATUS_UNLOCK) ? "UNLOCK " : ""));
W
William Juul 已提交
429 430 431
				}

				last_status = s;
432
			}
433
		} else {
W
William Juul 已提交
434 435 436 437 438 439
			if (!nand_lock(nand, tight)) {
				puts("NAND flash successfully locked\n");
			} else {
				puts("Error locking NAND flash\n");
				return 1;
			}
440
		}
441
#endif
442 443 444 445
		return 0;
	}

	if (strcmp(cmd, "unlock") == 0) {
446 447
		if (arg_off_size(argc - 2, argv + 2, nand, &off, &size) < 0)
			return 1;
448

449 450
/*
 * ! BROKEN !
451
 *
452 453 454
 * TODO: must be implemented and tested by someone with HW
 */
#if 0
W
William Juul 已提交
455 456 457 458
		if (!nand_unlock(nand, off, size)) {
			puts("NAND flash successfully unlocked\n");
		} else {
			puts("Error unlocking NAND flash, "
459
			     "write and erase will probably fail\n");
W
William Juul 已提交
460 461 462
			return 1;
		}
#endif
463 464 465
		return 0;
	}

466 467 468 469 470 471
usage:
	printf("Usage:\n%s\n", cmdtp->usage);
	return 1;
}

U_BOOT_CMD(nand, 5, 1, do_nand,
472 473 474
           "nand - NAND sub-system\n",
           "info - show available NAND devices\n"
           "nand device [dev] - show or set current device\n"
475 476
           "nand read - addr off|partition size\n"
           "nand write - addr off|partition size\n"
477
           "    read/write 'size' bytes starting at offset 'off'\n"
478
           "    to/from memory address 'addr', skipping bad blocks.\n"
479 480 481 482 483 484 485 486 487 488
           "nand erase [clean] [off size] - erase 'size' bytes from\n"
           "    offset 'off' (entire device if not specified)\n"
           "nand bad - show bad blocks\n"
           "nand dump[.oob] off - dump page\n"
           "nand scrub - really clean NAND erasing bad blocks (UNSAFE)\n"
           "nand markbad off - mark bad block at offset (UNSAFE)\n"
           "nand biterr off - make a bit error at offset (UNSAFE)\n"
           "nand lock [tight] [status]\n"
           "    bring nand to lock state or display locked pages\n"
           "nand unlock [offset] [size] - unlock section\n");
489

490
static int nand_load_image(cmd_tbl_t *cmdtp, nand_info_t *nand,
491
                           ulong offset, ulong addr, char *cmd)
492 493
{
	int r;
494
	char *ep, *s;
495
	size_t cnt;
496
	image_header_t *hdr;
497
#if defined(CONFIG_FIT)
498
	const void *fit_hdr = NULL;
499
#endif
500 501 502

	s = strchr(cmd, '.');
	if (s != NULL &&
503 504 505 506 507
	    (strcmp(s, ".jffs2") && !strcmp(s, ".e") && !strcmp(s, ".i"))) {
		printf("Unknown nand load suffix '%s'\n", s);
		show_boot_progress(-53);
		return 1;
	}
508

509
	printf("\nLoading from %s, offset 0x%lx\n", nand->name, offset);
510

511 512
	cnt = nand->writesize;
	r = nand_read(nand, offset, &cnt, (u_char *) addr);
513
	if (r) {
514
		puts("** Read error\n");
515
		show_boot_progress (-56);
516 517
		return 1;
	}
518
	show_boot_progress (56);
519

520
	switch (genimg_get_format ((void *)addr)) {
521 522 523 524 525
	case IMAGE_FORMAT_LEGACY:
		hdr = (image_header_t *)addr;

		show_boot_progress (57);
		image_print_contents (hdr);
526

527 528 529 530
		cnt = image_get_image_size (hdr);
		break;
#if defined(CONFIG_FIT)
	case IMAGE_FORMAT_FIT:
531 532 533 534 535
		fit_hdr = (const void *)addr;
		puts ("Fit image detected...\n");

		cnt = fit_get_size (fit_hdr);
		break;
536 537
#endif
	default:
538
		show_boot_progress (-57);
539
		puts ("** Unknown image type\n");
540 541
		return 1;
	}
542
	show_boot_progress (57);
543

544
	/* FIXME: skip bad blocks */
545
	r = nand_read(nand, offset, &cnt, (u_char *) addr);
546
	if (r) {
547
		puts("** Read error\n");
548
		show_boot_progress (-58);
549 550
		return 1;
	}
551
	show_boot_progress (58);
552

553 554
#if defined(CONFIG_FIT)
	/* This cannot be done earlier, we need complete FIT image in RAM first */
555 556 557 558 559 560 561 562 563
	if (genimg_get_format ((void *)addr) == IMAGE_FORMAT_FIT) {
		if (!fit_check_format (fit_hdr)) {
			show_boot_progress (-150);
			puts ("** Bad FIT image format\n");
			return 1;
		}
		show_boot_progress (151);
		fit_print_contents (fit_hdr);
	}
564 565
#endif

566 567 568 569 570 571 572 573 574
	/* Loading ok, update default load address */

	load_addr = addr;

	/* Check if we should attempt an auto-start */
	if (((ep = getenv("autostart")) != NULL) && (strcmp(ep, "yes") == 0)) {
		char *local_args[2];
		extern int do_bootm(cmd_tbl_t *, int, int, char *[]);

575
		local_args[0] = cmd;
576 577 578 579 580 581 582 583 584 585
		local_args[1] = NULL;

		printf("Automatic boot of image at addr 0x%08lx ...\n", addr);

		do_bootm(cmdtp, 0, 1, local_args);
		return 1;
	}
	return 0;
}

586 587 588 589 590
int do_nandboot(cmd_tbl_t * cmdtp, int flag, int argc, char *argv[])
{
	char *boot_device = NULL;
	int idx;
	ulong addr, offset = 0;
591
#if defined(CONFIG_CMD_JFFS2) && defined(CONFIG_JFFS2_CMDLINE)
592 593 594 595 596 597 598 599 600 601 602 603 604 605 606
	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\n");
				return 1;
			}
			if (argc > 3)
				goto usage;
			if (argc == 3)
607
				addr = simple_strtoul(argv[1], NULL, 16);
608 609 610
			else
				addr = CFG_LOAD_ADDR;
			return nand_load_image(cmdtp, &nand_info[dev->id->num],
611
			                       part->offset, addr, argv[0]);
612 613 614 615
		}
	}
#endif

616
	show_boot_progress(52);
617 618 619 620 621 622 623 624 625 626 627 628 629 630 631 632 633 634 635
	switch (argc) {
	case 1:
		addr = CFG_LOAD_ADDR;
		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:
636
#if defined(CONFIG_CMD_JFFS2) && defined(CONFIG_JFFS2_CMDLINE)
637 638 639
usage:
#endif
		printf("Usage:\n%s\n", cmdtp->usage);
640
		show_boot_progress(-53);
641 642 643
		return 1;
	}

644
	show_boot_progress(53);
645 646
	if (!boot_device) {
		puts("\n** No boot device **\n");
647
		show_boot_progress(-54);
648 649
		return 1;
	}
650
	show_boot_progress(54);
651

652 653 654 655
	idx = simple_strtoul(boot_device, NULL, 16);

	if (idx < 0 || idx >= CFG_MAX_NAND_DEVICE || !nand_info[idx].name) {
		printf("\n** Device %d not available\n", idx);
656
		show_boot_progress(-55);
657 658
		return 1;
	}
659
	show_boot_progress(55);
660 661 662 663 664 665

	return nand_load_image(cmdtp, &nand_info[idx], offset, addr, argv[0]);
}

U_BOOT_CMD(nboot, 4, 1, do_nandboot,
	"nboot   - boot from NAND device\n",
666
	"[partition] | [[[loadAddr] dev] offset]\n");
667

668
#endif
669

670
#else /* CONFIG_NAND_LEGACY */
671 672 673 674 675
/*
 *
 * Legacy NAND support - to be phased out
 *
 */
W
wdenk 已提交
676 677 678
#include <command.h>
#include <malloc.h>
#include <asm/io.h>
679
#include <watchdog.h>
W
wdenk 已提交
680

681
#ifdef CONFIG_show_boot_progress
W
wdenk 已提交
682
# include <status_led.h>
683
# define show_boot_progress(arg)	show_boot_progress(arg)
W
wdenk 已提交
684
#else
685
# define show_boot_progress(arg)
W
wdenk 已提交
686 687
#endif

688
#if defined(CONFIG_CMD_NAND)
689 690
#include <linux/mtd/nand_legacy.h>
#if 0
W
wdenk 已提交
691
#include <linux/mtd/nand_ids.h>
W
wdenk 已提交
692
#include <jffs2/jffs2.h>
693
#endif
W
wdenk 已提交
694

W
wdenk 已提交
695 696 697 698
#ifdef CONFIG_OMAP1510
void archflashwp(void *archdata, int wp);
#endif

699
#define ROUND_DOWN(value,boundary)      ((value) & (~((boundary)-1)))
W
wdenk 已提交
700

701 702
#undef	NAND_DEBUG
#undef	PSYCHO_DEBUG
W
wdenk 已提交
703 704 705 706 707 708 709 710 711 712 713 714 715 716

/* ****************** WARNING *********************
 * When ALLOW_ERASE_BAD_DEBUG is non-zero the erase command will
 * erase (or at least attempt to erase) blocks that are marked
 * bad. This can be very handy if you are _sure_ that the block
 * is OK, say because you marked a good block bad to test bad
 * block handling and you are done testing, or if you have
 * accidentally marked blocks bad.
 *
 * Erasing factory marked bad blocks is a _bad_ idea. If the
 * erase succeeds there is no reliable way to find them again,
 * and attempting to program or erase bad blocks can affect
 * the data in _other_ (good) blocks.
 */
717
#define	 ALLOW_ERASE_BAD_DEBUG 0
W
wdenk 已提交
718 719

#define CONFIG_MTD_NAND_ECC  /* enable ECC */
W
wdenk 已提交
720
#define CONFIG_MTD_NAND_ECC_JFFS2
W
wdenk 已提交
721

722
/* bits for nand_legacy_rw() `cmd'; or together as needed */
W
William Juul 已提交
723 724 725
#define NANDRW_READ         0x01
#define NANDRW_WRITE        0x00
#define NANDRW_JFFS2	    0x02
726
#define NANDRW_JFFS2_SKIP   0x04
W
wdenk 已提交
727

728 729 730 731 732 733
/*
 * Imports from nand_legacy.c
 */
extern struct nand_chip nand_dev_desc[CFG_MAX_NAND_DEVICE];
extern int curr_device;
extern int nand_legacy_erase(struct nand_chip *nand, size_t ofs,
734
			    size_t len, int clean);
735
extern int nand_legacy_rw(struct nand_chip *nand, int cmd, size_t start,
W
William Juul 已提交
736
			 size_t len, size_t *retlen, u_char *buf);
737 738 739
extern void nand_print(struct nand_chip *nand);
extern void nand_print_bad(struct nand_chip *nand);
extern int nand_read_oob(struct nand_chip *nand, size_t ofs,
740
			       size_t len, size_t *retlen, u_char *buf);
741
extern int nand_write_oob(struct nand_chip *nand, size_t ofs,
W
William Juul 已提交
742
				size_t len, size_t *retlen, const u_char *buf);
W
wdenk 已提交
743 744


W
Wolfgang Denk 已提交
745
int do_nand (cmd_tbl_t * cmdtp, int flag, int argc, char *argv[])
W
wdenk 已提交
746
{
W
Wolfgang Denk 已提交
747
	int rcode = 0;
W
wdenk 已提交
748

W
Wolfgang Denk 已提交
749 750 751 752 753 754 755 756
	switch (argc) {
	case 0:
	case 1:
		printf ("Usage:\n%s\n", cmdtp->usage);
		return 1;
	case 2:
		if (strcmp (argv[1], "info") == 0) {
			int i;
W
wdenk 已提交
757

W
Wolfgang Denk 已提交
758
			putc ('\n');
W
wdenk 已提交
759

W
Wolfgang Denk 已提交
760 761 762 763 764 765 766 767
			for (i = 0; i < CFG_MAX_NAND_DEVICE; ++i) {
				if (nand_dev_desc[i].ChipID ==
				    NAND_ChipID_UNKNOWN)
					continue;	/* list only known devices */
				printf ("Device %d: ", i);
				nand_print (&nand_dev_desc[i]);
			}
			return 0;
W
wdenk 已提交
768

W
Wolfgang Denk 已提交
769 770 771 772 773 774 775 776 777
		} else if (strcmp (argv[1], "device") == 0) {
			if ((curr_device < 0)
			    || (curr_device >= CFG_MAX_NAND_DEVICE)) {
				puts ("\nno devices available\n");
				return 1;
			}
			printf ("\nDevice %d: ", curr_device);
			nand_print (&nand_dev_desc[curr_device]);
			return 0;
W
wdenk 已提交
778

W
Wolfgang Denk 已提交
779 780 781 782 783 784 785 786 787
		} else if (strcmp (argv[1], "bad") == 0) {
			if ((curr_device < 0)
			    || (curr_device >= CFG_MAX_NAND_DEVICE)) {
				puts ("\nno devices available\n");
				return 1;
			}
			printf ("\nDevice %d bad blocks:\n", curr_device);
			nand_print_bad (&nand_dev_desc[curr_device]);
			return 0;
W
wdenk 已提交
788 789

		}
W
Wolfgang Denk 已提交
790 791 792 793 794
		printf ("Usage:\n%s\n", cmdtp->usage);
		return 1;
	case 3:
		if (strcmp (argv[1], "device") == 0) {
			int dev = (int) simple_strtoul (argv[2], NULL, 10);
W
wdenk 已提交
795

W
Wolfgang Denk 已提交
796 797 798 799 800 801 802
			printf ("\nDevice %d: ", dev);
			if (dev >= CFG_MAX_NAND_DEVICE) {
				puts ("unknown device\n");
				return 1;
			}
			nand_print (&nand_dev_desc[dev]);
			/*nand_print (dev); */
W
wdenk 已提交
803

W
Wolfgang Denk 已提交
804 805 806
			if (nand_dev_desc[dev].ChipID == NAND_ChipID_UNKNOWN) {
				return 1;
			}
W
wdenk 已提交
807

W
Wolfgang Denk 已提交
808
			curr_device = dev;
W
wdenk 已提交
809

W
Wolfgang Denk 已提交
810
			puts ("... is now current device\n");
W
wdenk 已提交
811

W
Wolfgang Denk 已提交
812 813 814 815 816 817 818
			return 0;
		} else if (strcmp (argv[1], "erase") == 0
			   && strcmp (argv[2], "clean") == 0) {
			struct nand_chip *nand = &nand_dev_desc[curr_device];
			ulong off = 0;
			ulong size = nand->totlen;
			int ret;
W
wdenk 已提交
819

W
Wolfgang Denk 已提交
820
			printf ("\nNAND erase: device %d offset %ld, size %ld ... ", curr_device, off, size);
W
wdenk 已提交
821

W
Wolfgang Denk 已提交
822
			ret = nand_legacy_erase (nand, off, size, 1);
W
wdenk 已提交
823

W
Wolfgang Denk 已提交
824
			printf ("%s\n", ret ? "ERROR" : "OK");
W
wdenk 已提交
825

W
wdenk 已提交
826 827
			return ret;
		}
W
Wolfgang Denk 已提交
828 829 830 831 832 833 834 835

		printf ("Usage:\n%s\n", cmdtp->usage);
		return 1;
	default:
		/* at least 4 args */

		if (strncmp (argv[1], "read", 4) == 0 ||
		    strncmp (argv[1], "write", 5) == 0) {
W
William Juul 已提交
836 837 838 839 840
			ulong addr = simple_strtoul (argv[2], NULL, 16);
			off_t off = simple_strtoul (argv[3], NULL, 16);
			size_t size = simple_strtoul (argv[4], NULL, 16);
			int cmd = (strncmp (argv[1], "read", 4) == 0) ?
			          NANDRW_READ : NANDRW_WRITE;
841 842
			size_t total;
			int ret;
W
Wolfgang Denk 已提交
843 844 845 846 847 848
			char *cmdtail = strchr (argv[1], '.');

			if (cmdtail && !strncmp (cmdtail, ".oob", 2)) {
				/* read out-of-band data */
				if (cmd & NANDRW_READ) {
					ret = nand_read_oob (nand_dev_desc + curr_device,
849
							     off, size, &total,
W
Wolfgang Denk 已提交
850 851 852
							     (u_char *) addr);
				} else {
					ret = nand_write_oob (nand_dev_desc + curr_device,
853
							      off, size, &total,
W
Wolfgang Denk 已提交
854 855 856 857 858 859 860 861 862 863
							      (u_char *) addr);
				}
				return ret;
			} else if (cmdtail && !strncmp (cmdtail, ".jffs2", 2))
				cmd |= NANDRW_JFFS2;	/* skip bad blocks */
			else if (cmdtail && !strncmp (cmdtail, ".jffs2s", 2)) {
				cmd |= NANDRW_JFFS2;	/* skip bad blocks (on read too) */
				if (cmd & NANDRW_READ)
					cmd |= NANDRW_JFFS2_SKIP;	/* skip bad blocks (on read too) */
			}
W
wdenk 已提交
864
#ifdef SXNI855T
W
Wolfgang Denk 已提交
865 866 867
			/* need ".e" same as ".j" for compatibility with older units */
			else if (cmdtail && !strcmp (cmdtail, ".e"))
				cmd |= NANDRW_JFFS2;	/* skip bad blocks */
W
wdenk 已提交
868
#endif
869
#ifdef CFG_NAND_SKIP_BAD_DOT_I
W
Wolfgang Denk 已提交
870 871 872 873 874
			/* need ".i" same as ".jffs2s" for compatibility with older units (esd) */
			/* ".i" for image -> read skips bad block (no 0xff) */
			else if (cmdtail && !strcmp (cmdtail, ".i")) {
				cmd |= NANDRW_JFFS2;	/* skip bad blocks (on read too) */
				if (cmd & NANDRW_READ)
875
					cmd |= NANDRW_JFFS2_SKIP;	/* skip bad blocks (on read too) */
W
Wolfgang Denk 已提交
876
			}
877
#endif /* CFG_NAND_SKIP_BAD_DOT_I */
W
Wolfgang Denk 已提交
878 879 880 881
			else if (cmdtail) {
				printf ("Usage:\n%s\n", cmdtp->usage);
				return 1;
			}
W
wdenk 已提交
882

883
			printf ("\nNAND %s: device %d offset %ld, size %lu ...\n",
W
Wolfgang Denk 已提交
884
				(cmd & NANDRW_READ) ? "read" : "write",
885
				curr_device, off, (ulong)size);
W
wdenk 已提交
886

W
Wolfgang Denk 已提交
887 888
			ret = nand_legacy_rw (nand_dev_desc + curr_device,
					      cmd, off, size,
889
					      &total, (u_char *) addr);
W
wdenk 已提交
890

W
Wolfgang Denk 已提交
891 892 893
			printf (" %d bytes %s: %s\n", total,
				(cmd & NANDRW_READ) ? "read" : "written",
				ret ? "ERROR" : "OK");
W
wdenk 已提交
894

W
Wolfgang Denk 已提交
895 896 897 898 899 900 901 902 903
			return ret;
		} else if (strcmp (argv[1], "erase") == 0 &&
			   (argc == 4 || strcmp ("clean", argv[2]) == 0)) {
			int clean = argc == 5;
			ulong off =
				simple_strtoul (argv[2 + clean], NULL, 16);
			ulong size =
				simple_strtoul (argv[3 + clean], NULL, 16);
			int ret;
W
wdenk 已提交
904

W
Wolfgang Denk 已提交
905 906
			printf ("\nNAND erase: device %d offset %ld, size %ld ...\n",
				curr_device, off, size);
W
wdenk 已提交
907

W
Wolfgang Denk 已提交
908 909
			ret = nand_legacy_erase (nand_dev_desc + curr_device,
						 off, size, clean);
W
wdenk 已提交
910

W
Wolfgang Denk 已提交
911
			printf ("%s\n", ret ? "ERROR" : "OK");
W
wdenk 已提交
912

W
Wolfgang Denk 已提交
913 914 915 916 917
			return ret;
		} else {
			printf ("Usage:\n%s\n", cmdtp->usage);
			rcode = 1;
		}
W
wdenk 已提交
918

W
Wolfgang Denk 已提交
919 920
		return rcode;
	}
W
wdenk 已提交
921 922
}

W
wdenk 已提交
923
U_BOOT_CMD(
924
	nand,	5,	1,	do_nand,
925
	"nand    - legacy NAND sub-system\n",
926 927
	"info  - show available NAND devices\n"
	"nand device [dev] - show or set current device\n"
928
	"nand read[.jffs2[s]]  addr off size\n"
929
	"nand write[.jffs2] addr off size - read/write `size' bytes starting\n"
930
	"    at offset `off' to/from memory address `addr'\n"
931
	"nand erase [clean] [off size] - erase `size' bytes from\n"
932
	"    offset `off' (entire device if not specified)\n"
933 934 935 936 937
	"nand bad - show bad blocks\n"
	"nand read.oob addr off size - read out-of-band data\n"
	"nand write.oob addr off size - read out-of-band data\n"
);

W
wdenk 已提交
938 939 940 941 942 943 944 945 946 947
int do_nandboot (cmd_tbl_t *cmdtp, int flag, int argc, char *argv[])
{
	char *boot_device = NULL;
	char *ep;
	int dev;
	ulong cnt;
	ulong addr;
	ulong offset = 0;
	image_header_t *hdr;
	int rcode = 0;
948
#if defined(CONFIG_FIT)
949
	const void *fit_hdr = NULL;
950 951
#endif

952
	show_boot_progress (52);
W
wdenk 已提交
953 954 955 956 957 958 959 960 961 962 963 964 965 966 967 968 969 970 971 972
	switch (argc) {
	case 1:
		addr = CFG_LOAD_ADDR;
		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:
		printf ("Usage:\n%s\n", cmdtp->usage);
973
		show_boot_progress (-53);
W
wdenk 已提交
974 975 976
		return 1;
	}

977
	show_boot_progress (53);
W
wdenk 已提交
978 979
	if (!boot_device) {
		puts ("\n** No boot device **\n");
980
		show_boot_progress (-54);
W
wdenk 已提交
981 982
		return 1;
	}
983
	show_boot_progress (54);
W
wdenk 已提交
984 985 986 987

	dev = simple_strtoul(boot_device, &ep, 16);

	if ((dev >= CFG_MAX_NAND_DEVICE) ||
988
	    (nand_dev_desc[dev].ChipID == NAND_ChipID_UNKNOWN)) {
W
wdenk 已提交
989
		printf ("\n** Device %d not available\n", dev);
990
		show_boot_progress (-55);
W
wdenk 已提交
991 992
		return 1;
	}
993
	show_boot_progress (55);
W
wdenk 已提交
994

W
wdenk 已提交
995
	printf ("\nLoading from device %d: %s at 0x%lx (offset 0x%lx)\n",
996 997
	    dev, nand_dev_desc[dev].name, nand_dev_desc[dev].IO_ADDR,
	    offset);
W
wdenk 已提交
998

999
	if (nand_legacy_rw (nand_dev_desc + dev, NANDRW_READ, offset,
1000
	                    SECTORSIZE, NULL, (u_char *)addr)) {
W
wdenk 已提交
1001
		printf ("** Read error on %d\n", dev);
1002
		show_boot_progress (-56);
W
wdenk 已提交
1003 1004
		return 1;
	}
1005
	show_boot_progress (56);
W
wdenk 已提交
1006

1007
	switch (genimg_get_format ((void *)addr)) {
1008 1009
	case IMAGE_FORMAT_LEGACY:
		hdr = (image_header_t *)addr;
1010
		image_print_contents (hdr);
W
wdenk 已提交
1011

1012 1013
		cnt = image_get_image_size (hdr);
		cnt -= SECTORSIZE;
1014 1015 1016
		break;
#if defined(CONFIG_FIT)
	case IMAGE_FORMAT_FIT:
1017 1018 1019 1020 1021
		fit_hdr = (const void *)addr;
		puts ("Fit image detected...\n");

		cnt = fit_get_size (fit_hdr);
		break;
1022 1023
#endif
	default:
1024
		show_boot_progress (-57);
1025
		puts ("** Unknown image type\n");
W
wdenk 已提交
1026 1027
		return 1;
	}
1028
	show_boot_progress (57);
W
wdenk 已提交
1029

1030
	if (nand_legacy_rw (nand_dev_desc + dev, NANDRW_READ,
1031 1032
	                    offset + SECTORSIZE, cnt, NULL,
	                    (u_char *)(addr+SECTORSIZE))) {
W
wdenk 已提交
1033
		printf ("** Read error on %d\n", dev);
1034
		show_boot_progress (-58);
W
wdenk 已提交
1035 1036
		return 1;
	}
1037
	show_boot_progress (58);
W
wdenk 已提交
1038

1039 1040
#if defined(CONFIG_FIT)
	/* This cannot be done earlier, we need complete FIT image in RAM first */
1041 1042 1043 1044 1045 1046 1047 1048 1049
	if (genimg_get_format ((void *)addr) == IMAGE_FORMAT_FIT) {
		if (!fit_check_format (fit_hdr)) {
			show_boot_progress (-150);
			puts ("** Bad FIT image format\n");
			return 1;
		}
		show_boot_progress (151);
		fit_print_contents (fit_hdr);
	}
1050 1051
#endif

W
wdenk 已提交
1052 1053 1054 1055 1056 1057 1058 1059 1060 1061 1062 1063
	/* Loading ok, update default load address */

	load_addr = addr;

	/* Check if we should attempt an auto-start */
	if (((ep = getenv("autostart")) != NULL) && (strcmp(ep,"yes") == 0)) {
		char *local_args[2];
		extern int do_bootm (cmd_tbl_t *, int, int, char *[]);

		local_args[0] = argv[0];
		local_args[1] = NULL;

W
wdenk 已提交
1064
		printf ("Automatic boot of image at addr 0x%08lx ...\n", addr);
W
wdenk 已提交
1065 1066 1067 1068 1069 1070 1071

		do_bootm (cmdtp, 0, 1, local_args);
		rcode = 1;
	}
	return rcode;
}

W
wdenk 已提交
1072
U_BOOT_CMD(
1073
	nboot,	4,	1,	do_nandboot,
1074 1075 1076 1077
	"nboot   - boot from NAND device\n",
	"loadAddr dev\n"
);

1078
#endif
W
wdenk 已提交
1079

1080
#endif /* CONFIG_NAND_LEGACY */