blob: ae4c68ac1f4a200cd341f277c51739ac2febc1b6 [file] [log] [blame]
wdenkc8434db2003-03-26 06:55:25 +00001/*
Wolfgang Denk2f9b7e42005-08-17 12:55:25 +02002 * Rick Bronson and Pantelis Antoniou
wdenkc8434db2003-03-26 06:55:25 +00003 */
4
5#include <common.h>
Wolfgang Denk2f9b7e42005-08-17 12:55:25 +02006
7#if (CONFIG_COMMANDS & CFG_CMD_NAND)
8
wdenkc8434db2003-03-26 06:55:25 +00009#include <command.h>
wdenk145d2c12004-04-15 21:48:45 +000010#include <watchdog.h>
Wolfgang Denk2f9b7e42005-08-17 12:55:25 +020011#include <malloc.h>
12#include <asm/byteorder.h>
wdenkc8434db2003-03-26 06:55:25 +000013
14#ifdef CONFIG_SHOW_BOOT_PROGRESS
15# include <status_led.h>
16# define SHOW_BOOT_PROGRESS(arg) show_boot_progress(arg)
17#else
18# define SHOW_BOOT_PROGRESS(arg)
19#endif
20
wdenkabda5ca2003-05-31 18:35:21 +000021#include <jffs2/jffs2.h>
Wolfgang Denk2f9b7e42005-08-17 12:55:25 +020022#include <nand.h>
wdenkc8434db2003-03-26 06:55:25 +000023
Wolfgang Denk2f9b7e42005-08-17 12:55:25 +020024extern nand_info_t nand_info[]; /* info for NAND chips */
wdenke58b0dc2003-07-27 00:21:01 +000025
Wolfgang Denk2f9b7e42005-08-17 12:55:25 +020026static int nand_dump_oob(nand_info_t *nand, ulong off)
27{
28 return 0;
29}
wdenke58b0dc2003-07-27 00:21:01 +000030
Wolfgang Denk2f9b7e42005-08-17 12:55:25 +020031static int nand_dump(nand_info_t *nand, ulong off)
32{
33 int i;
34 u_char *buf, *p;
wdenkc8434db2003-03-26 06:55:25 +000035
Wolfgang Denk2f9b7e42005-08-17 12:55:25 +020036 buf = malloc(nand->oobblock + nand->oobsize);
37 if (!buf) {
38 puts("No memory for page buffer\n");
39 return 1;
40 }
41 off &= ~(nand->oobblock - 1);
42 i = nand_read_raw(nand, buf, off, nand->oobblock, nand->oobsize);
43 if (i < 0) {
44 printf("Error (%d) reading page %08x\n", i, off);
45 free(buf);
46 return 1;
47 }
48 printf("Page %08x dump:\n", off);
49 i = nand->oobblock >> 4; p = buf;
50 while (i--) {
51 printf( "\t%02x %02x %02x %02x %02x %02x %02x %02x"
52 " %02x %02x %02x %02x %02x %02x %02x %02x\n",
53 p[0], p[1], p[2], p[3], p[4], p[5], p[6], p[7],
54 p[8], p[9], p[10], p[11], p[12], p[13], p[14], p[15]);
55 p += 16;
56 }
57 puts("OOB:\n");
58 i = nand->oobsize >> 3;
59 while (i--) {
60 printf( "\t%02x %02x %02x %02x %02x %02x %02x %02x\n",
61 p[0], p[1], p[2], p[3], p[4], p[5], p[6], p[7]);
62 p += 8;
63 }
64 free(buf);
wdenkabda5ca2003-05-31 18:35:21 +000065
Wolfgang Denk2f9b7e42005-08-17 12:55:25 +020066 return 0;
67}
wdenkc8434db2003-03-26 06:55:25 +000068
Wolfgang Denk2f9b7e42005-08-17 12:55:25 +020069/* ------------------------------------------------------------------------- */
wdenkc8434db2003-03-26 06:55:25 +000070
Wolfgang Denk2f9b7e42005-08-17 12:55:25 +020071static void
72arg_off_size(int argc, char *argv[], ulong *off, ulong *size, ulong totsize)
73{
74 *off = 0;
75 *size = 0;
wdenkabda5ca2003-05-31 18:35:21 +000076
Wolfgang Denk2f9b7e42005-08-17 12:55:25 +020077#if defined(CONFIG_JFFS2_NAND) && defined(CFG_JFFS_CUSTOM_PART)
78 if (argc >= 1 && strcmp(argv[0], "partition") == 0) {
79 int part_num;
80 struct part_info *part;
81 const char *partstr;
82
83 if (argc >= 2)
84 partstr = argv[1];
85 else
86 partstr = getenv("partition");
87
88 if (partstr)
89 part_num = (int)simple_strtoul(partstr, NULL, 10);
90 else
91 part_num = 0;
92
93 part = jffs2_part_info(part_num);
94 if (part == NULL) {
95 printf("\nInvalid partition %d\n", part_num);
96 return;
97 }
98 *size = part->size;
99 *off = (ulong)part->offset;
100 } else
wdenkc8434db2003-03-26 06:55:25 +0000101#endif
Wolfgang Denk2f9b7e42005-08-17 12:55:25 +0200102 {
103 if (argc >= 1)
104 *off = (ulong)simple_strtoul(argv[0], NULL, 16);
105 else
106 *off = 0;
wdenkc8434db2003-03-26 06:55:25 +0000107
Wolfgang Denk2f9b7e42005-08-17 12:55:25 +0200108 if (argc >= 2)
109 *size = (ulong)simple_strtoul(argv[1], NULL, 16);
110 else
111 *size = totsize - *off;
wdenkc8434db2003-03-26 06:55:25 +0000112
Wolfgang Denk2f9b7e42005-08-17 12:55:25 +0200113 }
wdenkc8434db2003-03-26 06:55:25 +0000114
Wolfgang Denk2f9b7e42005-08-17 12:55:25 +0200115}
wdenkc8434db2003-03-26 06:55:25 +0000116
Wolfgang Denk2f9b7e42005-08-17 12:55:25 +0200117int do_nand(cmd_tbl_t * cmdtp, int flag, int argc, char *argv[])
wdenkc8434db2003-03-26 06:55:25 +0000118{
Wolfgang Denk2f9b7e42005-08-17 12:55:25 +0200119 int i, dev, ret;
120 ulong addr, off, size;
121 char *cmd, *s;
122 nand_info_t *nand;
wdenkc8434db2003-03-26 06:55:25 +0000123
Wolfgang Denk2f9b7e42005-08-17 12:55:25 +0200124 /* at least two arguments please */
125 if (argc < 2)
126 goto usage;
wdenkc8434db2003-03-26 06:55:25 +0000127
Wolfgang Denk2f9b7e42005-08-17 12:55:25 +0200128 cmd = argv[1];
wdenkc8434db2003-03-26 06:55:25 +0000129
Wolfgang Denk2f9b7e42005-08-17 12:55:25 +0200130 if (strcmp(cmd, "info") == 0) {
wdenkc8434db2003-03-26 06:55:25 +0000131
Wolfgang Denk2f9b7e42005-08-17 12:55:25 +0200132 putc('\n');
133 for (i = 0; i < CFG_MAX_NAND_DEVICE; i++) {
134 if (nand_info[i].name)
135 printf("Device %d: %s\n", i, nand_info[i].name);
wdenkc8434db2003-03-26 06:55:25 +0000136 }
wdenkc8434db2003-03-26 06:55:25 +0000137 return 0;
138 }
wdenkc8434db2003-03-26 06:55:25 +0000139
Wolfgang Denk2f9b7e42005-08-17 12:55:25 +0200140 if (strcmp(cmd, "device") == 0) {
wdenkc8434db2003-03-26 06:55:25 +0000141
Wolfgang Denk2f9b7e42005-08-17 12:55:25 +0200142 if (argc < 3) {
143 if ((nand_curr_device < 0) ||
144 (nand_curr_device >= CFG_MAX_NAND_DEVICE))
145 puts("\nno devices available\n");
146 else
147 printf("\nDevice %d: %s\n", nand_curr_device,
148 nand_info[nand_curr_device].name);
149 return 0;
150 }
151 dev = (int)simple_strtoul(argv[2], NULL, 10);
152 if (dev < 0 || dev >= CFG_MAX_NAND_DEVICE || !nand_info[dev].name) {
153 puts("No such device\n");
wdenkc8434db2003-03-26 06:55:25 +0000154 return 1;
155 }
Wolfgang Denk2f9b7e42005-08-17 12:55:25 +0200156 printf("Device %d: %s", dev, nand_info[dev].name);
157 puts("... is now current device\n");
158 nand_curr_device = dev;
159 return 0;
160 }
wdenkc8434db2003-03-26 06:55:25 +0000161
Wolfgang Denk2f9b7e42005-08-17 12:55:25 +0200162 if (strcmp(cmd, "bad") != 0 && strcmp(cmd, "erase") != 0 &&
163 strncmp(cmd, "dump", 4) != 0 &&
164 strncmp(cmd, "read", 4) != 0 && strncmp(cmd, "write", 5) != 0
165#ifdef CONFIG_MTD_NAND_UNSAFE
166 && strcmp(cmd, "scrub") != 0 && strcmp(cmd, "biterr") != 0
167 && strcmp(cmd, "markbad") != 0
168#endif
169 )
170 goto usage;
wdenkc8434db2003-03-26 06:55:25 +0000171
Wolfgang Denk2f9b7e42005-08-17 12:55:25 +0200172 /* the following commands operate on the current device */
173 if (nand_curr_device < 0 || nand_curr_device >= CFG_MAX_NAND_DEVICE ||
174 !nand_info[nand_curr_device].name) {
175 puts("\nno devices available\n");
176 return 1;
177 }
178 nand = &nand_info[nand_curr_device];
wdenkc8434db2003-03-26 06:55:25 +0000179
Wolfgang Denk2f9b7e42005-08-17 12:55:25 +0200180 if (strcmp(cmd, "bad") == 0) {
181 printf("\nDevice %d bad blocks:\n", nand_curr_device);
182 for (off = 0; off < nand->size; off += nand->erasesize)
183 if (nand_block_isbad(nand, off))
184 printf(" %08x\n", off);
wdenkc8434db2003-03-26 06:55:25 +0000185 return 0;
186 }
wdenkabda5ca2003-05-31 18:35:21 +0000187
Wolfgang Denk2f9b7e42005-08-17 12:55:25 +0200188 if (strcmp(cmd, "erase") == 0
189#ifdef CONFIG_MTD_NAND_UNSAFE
190 || strcmp(cmd, "scrub") == 0
191#endif
192 ) {
wdenkabda5ca2003-05-31 18:35:21 +0000193
Wolfgang Denk2f9b7e42005-08-17 12:55:25 +0200194#ifdef CONFIG_MTD_NAND_UNSAFE
195 i = strcmp(cmd, "scrub") == 0; /* 1 scrub, 0 = erase */
196#endif
197
198 arg_off_size(argc - 2, argv + 2, &off, &size, nand->size);
199 if (off == 0 && size == 0)
200 return 1;
201
202 printf("\nNAND %s: device %d offset 0x%x, size 0x%x ",
203#ifdef CONFIG_MTD_NAND_UNSAFE
204 i ? "scrub" :
205#endif
206 "erase",
207 nand_curr_device, off, size);
208
209#ifdef CONFIG_MTD_NAND_UNSAFE
210 if (i)
211 ret = nand_scrub(nand, off, size);
212 else
213#endif
214 ret = nand_erase(nand, off, size);
wdenkabda5ca2003-05-31 18:35:21 +0000215
216 printf("%s\n", ret ? "ERROR" : "OK");
217
Wolfgang Denk2f9b7e42005-08-17 12:55:25 +0200218 return ret == 0 ? 0 : 1;
wdenkabda5ca2003-05-31 18:35:21 +0000219 }
wdenkc8434db2003-03-26 06:55:25 +0000220
Wolfgang Denk2f9b7e42005-08-17 12:55:25 +0200221 if (strncmp(cmd, "dump", 4) == 0) {
222 if (argc < 3)
223 goto usage;
wdenkc8434db2003-03-26 06:55:25 +0000224
Wolfgang Denk2f9b7e42005-08-17 12:55:25 +0200225 s = strchr(cmd, '.');
226 off = (int)simple_strtoul(argv[2], NULL, 16);
wdenkabda5ca2003-05-31 18:35:21 +0000227
Wolfgang Denk2f9b7e42005-08-17 12:55:25 +0200228 if (s != NULL && strcmp(s, ".oob") == 0)
229 ret = nand_dump_oob(nand, off);
230 else
231 ret = nand_dump(nand, off);
232
233 return ret == 0 ? 1 : 0;
234
235 }
236
237 /* read write */
238 if (strncmp(cmd, "read", 4) == 0 || strncmp(cmd, "write", 5) == 0) {
239 if (argc < 4)
240 goto usage;
241/*
242 s = strchr(cmd, '.');
243 clean = CLEAN_NONE;
244 if (s != NULL) {
245 if (strcmp(s, ".jffs2") == 0 || strcmp(s, ".e") == 0
246 || strcmp(s, ".i"))
247 clean = CLEAN_JFFS2;
wdenk145d2c12004-04-15 21:48:45 +0000248 }
Wolfgang Denk2f9b7e42005-08-17 12:55:25 +0200249*/
250 addr = (ulong)simple_strtoul(argv[2], NULL, 16);
251
252 arg_off_size(argc - 3, argv + 3, &off, &size, nand->size);
253 if (off == 0 && size == 0)
wdenkabda5ca2003-05-31 18:35:21 +0000254 return 1;
wdenkc8434db2003-03-26 06:55:25 +0000255
Wolfgang Denk2f9b7e42005-08-17 12:55:25 +0200256 i = strncmp(cmd, "read", 4) == 0; /* 1 = read, 0 = write */
257 printf("\nNAND %s: device %d offset %u, size %u ... ",
258 i ? "read" : "write", nand_curr_device, off, size);
wdenkc8434db2003-03-26 06:55:25 +0000259
Wolfgang Denk2f9b7e42005-08-17 12:55:25 +0200260 if (i)
261 ret = nand_read(nand, off, &size, (u_char *)addr);
262 else
263 ret = nand_write(nand, off, &size, (u_char *)addr);
wdenkc8434db2003-03-26 06:55:25 +0000264
Wolfgang Denk2f9b7e42005-08-17 12:55:25 +0200265 printf(" %d bytes %s: %s\n", size,
266 i ? "read" : "written", ret ? "ERROR" : "OK");
wdenkc8434db2003-03-26 06:55:25 +0000267
Wolfgang Denk2f9b7e42005-08-17 12:55:25 +0200268 return ret == 0 ? 0 : 1;
269 }
270#ifdef CONFIG_MTD_NAND_UNSAFE
271 if (strcmp(cmd, "markbad") == 0 || strcmp(cmd, "biterr") == 0) {
272 if (argc < 3)
273 goto usage;
wdenkc8434db2003-03-26 06:55:25 +0000274
Wolfgang Denk2f9b7e42005-08-17 12:55:25 +0200275 i = strcmp(cmd, "biterr") == 0;
wdenkc8434db2003-03-26 06:55:25 +0000276
Wolfgang Denk2f9b7e42005-08-17 12:55:25 +0200277 off = (int)simple_strtoul(argv[2], NULL, 16);
wdenkc8434db2003-03-26 06:55:25 +0000278
Wolfgang Denk2f9b7e42005-08-17 12:55:25 +0200279 if (i)
280 ret = nand_make_bit_error(nand, off);
281 else
282 ret = nand_mark_bad(nand, off);
wdenkc8434db2003-03-26 06:55:25 +0000283
Wolfgang Denk2f9b7e42005-08-17 12:55:25 +0200284 return ret == 0 ? 0 : 1;
wdenkc8434db2003-03-26 06:55:25 +0000285 }
Wolfgang Denk2f9b7e42005-08-17 12:55:25 +0200286#endif
wdenkc8434db2003-03-26 06:55:25 +0000287
Wolfgang Denk2f9b7e42005-08-17 12:55:25 +0200288usage:
289 printf("Usage:\n%s\n", cmdtp->usage);
290 return 1;
wdenkc8434db2003-03-26 06:55:25 +0000291}
292
Wolfgang Denk2f9b7e42005-08-17 12:55:25 +0200293U_BOOT_CMD(nand, 5, 1, do_nand,
wdenkf12e3962003-06-29 21:03:46 +0000294 "nand - NAND sub-system\n",
Wolfgang Denk2f9b7e42005-08-17 12:55:25 +0200295 "info - show available NAND devices\n"
296 "nand device [dev] - show or set current device\n"
297 "nand read[.jffs2] - addr off size\n"
298 "nand write[.jffs2] - addr off size - read/write `size' bytes starting\n"
wdenkf12e3962003-06-29 21:03:46 +0000299 " at offset `off' to/from memory address `addr'\n"
300 "nand erase [clean] [off size] - erase `size' bytes from\n"
301 " offset `off' (entire device if not specified)\n"
302 "nand bad - show bad blocks\n"
Wolfgang Denk2f9b7e42005-08-17 12:55:25 +0200303 "nand dump[.oob] off - dump page\n"
304 "nand scrub - really clean NAND erasing bad blocks (UNSAFE)\n"
305 "nand markbad off - mark bad block at offset (UNSAFE)\n"
306 "nand biterr off - make a bit error at offset (UNSAFE)\n");
wdenkf12e3962003-06-29 21:03:46 +0000307
Wolfgang Denk2f9b7e42005-08-17 12:55:25 +0200308int do_nandboot(cmd_tbl_t * cmdtp, int flag, int argc, char *argv[])
wdenkc8434db2003-03-26 06:55:25 +0000309{
310 char *boot_device = NULL;
311 char *ep;
312 int dev;
Wolfgang Denk2f9b7e42005-08-17 12:55:25 +0200313 int r;
314 ulong addr, cnt, offset = 0;
wdenkc8434db2003-03-26 06:55:25 +0000315 image_header_t *hdr;
Wolfgang Denk2f9b7e42005-08-17 12:55:25 +0200316 nand_info_t *nand;
317
wdenkc8434db2003-03-26 06:55:25 +0000318 switch (argc) {
319 case 1:
320 addr = CFG_LOAD_ADDR;
Wolfgang Denk2f9b7e42005-08-17 12:55:25 +0200321 boot_device = getenv("bootdevice");
wdenkc8434db2003-03-26 06:55:25 +0000322 break;
323 case 2:
324 addr = simple_strtoul(argv[1], NULL, 16);
Wolfgang Denk2f9b7e42005-08-17 12:55:25 +0200325 boot_device = getenv("bootdevice");
wdenkc8434db2003-03-26 06:55:25 +0000326 break;
327 case 3:
328 addr = simple_strtoul(argv[1], NULL, 16);
329 boot_device = argv[2];
330 break;
331 case 4:
332 addr = simple_strtoul(argv[1], NULL, 16);
333 boot_device = argv[2];
334 offset = simple_strtoul(argv[3], NULL, 16);
335 break;
336 default:
Wolfgang Denk2f9b7e42005-08-17 12:55:25 +0200337 printf("Usage:\n%s\n", cmdtp->usage);
338 SHOW_BOOT_PROGRESS(-1);
wdenkc8434db2003-03-26 06:55:25 +0000339 return 1;
340 }
341
342 if (!boot_device) {
Wolfgang Denk2f9b7e42005-08-17 12:55:25 +0200343 puts("\n** No boot device **\n");
344 SHOW_BOOT_PROGRESS(-1);
wdenkc8434db2003-03-26 06:55:25 +0000345 return 1;
346 }
347
348 dev = simple_strtoul(boot_device, &ep, 16);
349
Wolfgang Denk2f9b7e42005-08-17 12:55:25 +0200350 if (dev < 0 || dev >= CFG_MAX_NAND_DEVICE || !nand_info[dev].name) {
351 printf("\n** Device %d not available\n", dev);
352 SHOW_BOOT_PROGRESS(-1);
wdenkc8434db2003-03-26 06:55:25 +0000353 return 1;
354 }
355
Wolfgang Denk2f9b7e42005-08-17 12:55:25 +0200356 nand = &nand_info[dev];
357 printf("\nLoading from device %d: %s (offset 0x%lx)\n",
358 dev, nand->name, offset);
wdenkc8434db2003-03-26 06:55:25 +0000359
Wolfgang Denk2f9b7e42005-08-17 12:55:25 +0200360 cnt = nand->oobblock;
361 r = nand_read(nand, offset, &cnt, (u_char *) addr);
362 if (r) {
363 printf("** Read error on %d\n", dev);
364 SHOW_BOOT_PROGRESS(-1);
wdenkc8434db2003-03-26 06:55:25 +0000365 return 1;
366 }
367
Wolfgang Denk2f9b7e42005-08-17 12:55:25 +0200368 hdr = (image_header_t *) addr;
wdenkc8434db2003-03-26 06:55:25 +0000369
Wolfgang Denk2f9b7e42005-08-17 12:55:25 +0200370 if (ntohl(hdr->ih_magic) != IH_MAGIC) {
371 printf("\n** Bad Magic Number 0x%x **\n", hdr->ih_magic);
372 SHOW_BOOT_PROGRESS(-1);
wdenkc8434db2003-03-26 06:55:25 +0000373 return 1;
374 }
375
Wolfgang Denk2f9b7e42005-08-17 12:55:25 +0200376 print_image_hdr(hdr);
377
378 cnt = (ntohl(hdr->ih_size) + sizeof (image_header_t));
379
380 r = nand_read(nand, offset, &cnt, (u_char *) addr);
381 if (r) {
382 printf("** Read error on %d\n", dev);
383 SHOW_BOOT_PROGRESS(-1);
wdenkc8434db2003-03-26 06:55:25 +0000384 return 1;
385 }
386
387 /* Loading ok, update default load address */
388
389 load_addr = addr;
390
391 /* Check if we should attempt an auto-start */
Wolfgang Denk2f9b7e42005-08-17 12:55:25 +0200392 if (((ep = getenv("autostart")) != NULL) && (strcmp(ep, "yes") == 0)) {
wdenkc8434db2003-03-26 06:55:25 +0000393 char *local_args[2];
Wolfgang Denk2f9b7e42005-08-17 12:55:25 +0200394 extern int do_bootm(cmd_tbl_t *, int, int, char *[]);
wdenkc8434db2003-03-26 06:55:25 +0000395
396 local_args[0] = argv[0];
397 local_args[1] = NULL;
398
Wolfgang Denk2f9b7e42005-08-17 12:55:25 +0200399 printf("Automatic boot of image at addr 0x%08lx ...\n", addr);
wdenkc8434db2003-03-26 06:55:25 +0000400
Wolfgang Denk2f9b7e42005-08-17 12:55:25 +0200401 do_bootm(cmdtp, 0, 1, local_args);
wdenkabda5ca2003-05-31 18:35:21 +0000402 return 1;
wdenk6d3c6d12005-04-03 22:35:21 +0000403 }
wdenkc8434db2003-03-26 06:55:25 +0000404 return 0;
405}
wdenk359733b2003-03-31 17:27:09 +0000406
Wolfgang Denk2f9b7e42005-08-17 12:55:25 +0200407U_BOOT_CMD(nboot, 4, 1, do_nandboot,
408 "nboot - boot from NAND device\n", "loadAddr dev\n");
wdenkc8434db2003-03-26 06:55:25 +0000409
wdenkabda5ca2003-05-31 18:35:21 +0000410
Wolfgang Denk2f9b7e42005-08-17 12:55:25 +0200411#endif /* (CONFIG_COMMANDS & CFG_CMD_NAND) */
wdenk8886a662004-04-18 19:43:36 +0000412