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