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