Rewrite of NAND code based on what is in 2.6.12 Linux kernel
[platform/kernel/u-boot.git] / common / cmd_nand.c
1 /*
2  * Rick Bronson and Pantelis Antoniou
3  */
4
5 #include <common.h>
6
7 #if (CONFIG_COMMANDS & CFG_CMD_NAND)
8
9 #include <command.h>
10 #include <watchdog.h>
11 #include <malloc.h>
12 #include <asm/byteorder.h>
13
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
21 #include <jffs2/jffs2.h>
22 #include <nand.h>
23
24 extern nand_info_t nand_info[];       /* info for NAND chips */
25
26 static int nand_dump_oob(nand_info_t *nand, ulong off)
27 {
28         return 0;
29 }
30
31 static int nand_dump(nand_info_t *nand, ulong off)
32 {
33         int i;
34         u_char *buf, *p;
35
36         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);
65
66         return 0;
67 }
68
69 /* ------------------------------------------------------------------------- */
70
71 static void
72 arg_off_size(int argc, char *argv[], ulong *off, ulong *size, ulong totsize)
73 {
74         *off = 0;
75         *size = 0;
76
77 #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
101 #endif
102         {
103                 if (argc >= 1)
104                         *off = (ulong)simple_strtoul(argv[0], NULL, 16);
105                 else
106                         *off = 0;
107
108                 if (argc >= 2)
109                         *size = (ulong)simple_strtoul(argv[1], NULL, 16);
110                 else
111                         *size = totsize - *off;
112
113         }
114
115 }
116
117 int do_nand(cmd_tbl_t * cmdtp, int flag, int argc, char *argv[])
118 {
119         int i, dev, ret;
120         ulong addr, off, size;
121         char *cmd, *s;
122         nand_info_t *nand;
123
124         /* at least two arguments please */
125         if (argc < 2)
126                 goto usage;
127
128         cmd = argv[1];
129
130         if (strcmp(cmd, "info") == 0) {
131
132                 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);
136                 }
137                 return 0;
138         }
139
140         if (strcmp(cmd, "device") == 0) {
141
142                 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");
154                         return 1;
155                 }
156                 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         }
161
162         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;
171
172         /* 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];
179
180         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);
185                 return 0;
186         }
187
188         if (strcmp(cmd, "erase") == 0
189 #ifdef CONFIG_MTD_NAND_UNSAFE
190             || strcmp(cmd, "scrub") == 0
191 #endif
192             ) {
193
194 #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);
215
216                 printf("%s\n", ret ? "ERROR" : "OK");
217
218                 return ret == 0 ? 0 : 1;
219         }
220
221         if (strncmp(cmd, "dump", 4) == 0) {
222                 if (argc < 3)
223                         goto usage;
224
225                 s = strchr(cmd, '.');
226                 off = (int)simple_strtoul(argv[2], NULL, 16);
227
228                 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;
248                 }
249 */
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)
254                         return 1;
255
256                 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);
259
260                 if (i)
261                         ret = nand_read(nand, off, &size, (u_char *)addr);
262                 else
263                         ret = nand_write(nand, off, &size, (u_char *)addr);
264
265                 printf(" %d bytes %s: %s\n", size,
266                        i ? "read" : "written", ret ? "ERROR" : "OK");
267
268                 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;
274
275                 i = strcmp(cmd, "biterr") == 0;
276
277                 off = (int)simple_strtoul(argv[2], NULL, 16);
278
279                 if (i)
280                         ret = nand_make_bit_error(nand, off);
281                 else
282                         ret = nand_mark_bad(nand, off);
283
284                 return ret == 0 ? 0 : 1;
285         }
286 #endif
287
288 usage:
289         printf("Usage:\n%s\n", cmdtp->usage);
290         return 1;
291 }
292
293 U_BOOT_CMD(nand, 5, 1, do_nand,
294         "nand    - NAND sub-system\n",
295         "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"
299         "    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"
303         "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");
307
308 int do_nandboot(cmd_tbl_t * cmdtp, int flag, int argc, char *argv[])
309 {
310         char *boot_device = NULL;
311         char *ep;
312         int dev;
313         int r;
314         ulong addr, cnt, offset = 0;
315         image_header_t *hdr;
316         nand_info_t *nand;
317
318         switch (argc) {
319         case 1:
320                 addr = CFG_LOAD_ADDR;
321                 boot_device = getenv("bootdevice");
322                 break;
323         case 2:
324                 addr = simple_strtoul(argv[1], NULL, 16);
325                 boot_device = getenv("bootdevice");
326                 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:
337                 printf("Usage:\n%s\n", cmdtp->usage);
338                 SHOW_BOOT_PROGRESS(-1);
339                 return 1;
340         }
341
342         if (!boot_device) {
343                 puts("\n** No boot device **\n");
344                 SHOW_BOOT_PROGRESS(-1);
345                 return 1;
346         }
347
348         dev = simple_strtoul(boot_device, &ep, 16);
349
350         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);
353                 return 1;
354         }
355
356         nand = &nand_info[dev];
357         printf("\nLoading from device %d: %s (offset 0x%lx)\n",
358                dev, nand->name, offset);
359
360         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);
365                 return 1;
366         }
367
368         hdr = (image_header_t *) addr;
369
370         if (ntohl(hdr->ih_magic) != IH_MAGIC) {
371                 printf("\n** Bad Magic Number 0x%x **\n", hdr->ih_magic);
372                 SHOW_BOOT_PROGRESS(-1);
373                 return 1;
374         }
375
376         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);
384                 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 */
392         if (((ep = getenv("autostart")) != NULL) && (strcmp(ep, "yes") == 0)) {
393                 char *local_args[2];
394                 extern int do_bootm(cmd_tbl_t *, int, int, char *[]);
395
396                 local_args[0] = argv[0];
397                 local_args[1] = NULL;
398
399                 printf("Automatic boot of image at addr 0x%08lx ...\n", addr);
400
401                 do_bootm(cmdtp, 0, 1, local_args);
402                 return 1;
403         }
404         return 0;
405 }
406
407 U_BOOT_CMD(nboot, 4, 1, do_nandboot,
408         "nboot   - boot from NAND device\n", "loadAddr dev\n");
409
410
411 #endif                          /* (CONFIG_COMMANDS & CFG_CMD_NAND) */
412