Update latest codes
[platform/kernel/u-boot.git] / common / cmd_onenand.c
1 /*
2  *  U-Boot command for OneNAND support
3  *
4  *  Copyright (C) 2005-2008 Samsung Electronics
5  *  Kyungmin Park <kyungmin.park@samsung.com>
6  *
7  * This program is free software; you can redistribute it and/or modify
8  * it under the terms of the GNU General Public License version 2 as
9  * published by the Free Software Foundation.
10  */
11
12 #include <common.h>
13 #include <command.h>
14 #include <malloc.h>
15
16 #include <linux/mtd/compat.h>
17 #include <linux/mtd/mtd.h>
18 #include <linux/mtd/onenand.h>
19
20 #include <asm/io.h>
21
22 static struct mtd_info *mtd;
23
24 static loff_t next_ofs;
25 static loff_t skip_ofs;
26
27 static inline int str2long(char *p, ulong *num)
28 {
29         char *endptr;
30
31         *num = simple_strtoul(p, &endptr, 16);
32         return (*p != '\0' && *endptr == '\0') ? 1 : 0;
33 }
34
35 static int arg_off_size(int argc, char * const argv[], ulong *off, ssize_t *size)
36 {
37         if (argc >= 1) {
38                 if (!(str2long(argv[0], off))) {
39                         printf("'%s' is not a number\n", argv[0]);
40                         return -1;
41                 }
42         } else {
43                 *off = 0;
44         }
45
46         if (argc >= 2) {
47                 if (!(str2long(argv[1], (ulong *)size))) {
48                         printf("'%s' is not a number\n", argv[1]);
49                         return -1;
50                 }
51         } else {
52                 *size = mtd->size - *off;
53         }
54
55         if ((*off + *size) > mtd->size) {
56                 printf("total chip size (0x%llx) exceeded!\n", mtd->size);
57                 return -1;
58         }
59
60         if (*size == mtd->size)
61                 puts("whole chip\n");
62         else
63                 printf("offset 0x%lx, size 0x%x\n", *off, *size);
64
65         return 0;
66 }
67
68 static int onenand_block_read(loff_t from, ssize_t len,
69                               ssize_t *retlen, u_char *buf, int oob)
70 {
71         struct onenand_chip *this = mtd->priv;
72         int blocksize = (1 << this->erase_shift);
73         loff_t ofs = from;
74         struct mtd_oob_ops ops = {
75                 .retlen         = 0,
76         };
77         ssize_t thislen;
78         int ret;
79
80         while (len > 0) {
81                 thislen = min_t(ssize_t, len, blocksize);
82                 thislen = ALIGN(thislen, mtd->writesize);
83
84                 ret = mtd->block_isbad(mtd, ofs);
85                 if (ret) {
86                         printk("Bad blocks %d at 0x%x\n",
87                                (u32)(ofs >> this->erase_shift), (u32)ofs);
88                         ofs += blocksize;
89                         /* FIXME need to check how to handle the 'len' */
90                         len -= blocksize;
91                         continue;
92                 }
93
94                 if (oob) {
95                         ops.oobbuf = buf;
96                         ops.ooblen = thislen;
97                 } else {
98                         ops.datbuf = buf;
99                         ops.len = thislen;
100                 }
101
102                 ops.retlen = 0;
103                 ret = mtd->read_oob(mtd, ofs, &ops);
104                 if (ret) {
105                         printk("Read failed 0x%x, %d\n", (u32)ofs, ret);
106                         ofs += thislen;
107                         continue;
108                 }
109                 ofs += thislen;
110                 buf += thislen;
111                 len -= thislen;
112                 *retlen += ops.retlen;
113         }
114
115         return 0;
116 }
117
118 static int onenand_write_oneblock_withoob(loff_t to, const u_char * buf,
119                                           ssize_t *retlen)
120 {
121         struct mtd_oob_ops ops = {
122                 .len = mtd->writesize,
123                 .ooblen = mtd->oobsize,
124                 .mode = MTD_OOB_AUTO,
125         };
126         int page, ret = 0;
127         for (page = 0; page < (mtd->erasesize / mtd->writesize); page ++) {
128                 ops.datbuf = (u_char *)buf;
129                 buf += mtd->writesize;
130                 ops.oobbuf = (u_char *)buf;
131                 buf += mtd->oobsize;
132                 ret = mtd->write_oob(mtd, to, &ops);
133                 if (ret)
134                         break;
135                 to += mtd->writesize;
136         }
137
138         *retlen = (ret) ? 0 : mtd->erasesize;
139         return ret;
140 }
141
142 static int onenand_block_write(loff_t to, ssize_t len,
143                                ssize_t *retlen, const u_char * buf, int withoob)
144 {
145         struct onenand_chip *this = mtd->priv;
146         int blocksize = (1 << this->erase_shift);
147         struct mtd_oob_ops ops = {
148                 .retlen         = 0,
149                 .oobbuf         = NULL,
150         };
151         loff_t ofs;
152         size_t thislen;
153         ssize_t _retlen = 0;
154         int ret;
155
156         if (to == next_ofs) {
157                 next_ofs = to + len;
158                 to += skip_ofs;
159         } else {
160                 next_ofs = to + len;
161                 skip_ofs = 0;
162         }
163         ofs = to;
164
165         while (len > 0) {
166                 thislen = min_t(size_t, len, blocksize);
167                 thislen = ALIGN(thislen, mtd->writesize);
168
169                 ret = mtd->block_isbad(mtd, ofs);
170                 if (ret) {
171                         printk("Bad blocks %d at 0x%x\n",
172                                (u32)(ofs >> this->erase_shift), (u32)ofs);
173                         skip_ofs += blocksize;
174                         ofs += blocksize;
175                         continue;
176                 }
177
178                 ops.datbuf = (u_char *) buf;
179                 ops.len = thislen;
180                 ops.retlen = 0;
181                 /*printf("\t\tunit write 0x%p 0x%llx 0x%x\n", buf, ofs, thislen);*/
182                 ret = mtd->write_oob(mtd, ofs, &ops);
183                 if (ret) {
184                         printk("Write failed 0x%x, %d", (u32)ofs, ret);
185                         skip_ofs += thislen;
186                         ofs += thislen;
187                         continue;
188                 }
189
190                 buf += thislen;
191                 len -= thislen;
192                 ofs += thislen;
193                 *retlen += ops.retlen;
194         }
195
196         return 0;
197 }
198
199 static int onenand_block_erase(u32 start, u32 size, int force)
200 {
201         struct onenand_chip *this = mtd->priv;
202         struct erase_info instr = {
203                 .callback       = NULL,
204         };
205         loff_t ofs;
206         int ret;
207         int blocksize = 1 << this->erase_shift;
208
209         for (ofs = start; ofs < (start + size); ofs += blocksize) {
210                 ret = mtd->block_isbad(mtd, ofs);
211                 if (ret && !force) {
212                         printf("Skip erase bad block %d at 0x%x\n",
213                                (u32)(ofs >> this->erase_shift), (u32)ofs);
214                         continue;
215                 }
216
217                 instr.addr = ofs;
218                 instr.len = blocksize;
219                 instr.priv = force;
220                 instr.mtd = mtd;
221                 ret = mtd->erase(mtd, &instr);
222                 if (ret) {
223                         printf("erase failed block %d at 0x%x\n",
224                                (u32)(ofs >> this->erase_shift), (u32)ofs);
225                         continue;
226                 }
227         }
228
229         return 0;
230 }
231
232 static int onenand_block_test(u32 start, u32 size)
233 {
234         struct onenand_chip *this = mtd->priv;
235         struct erase_info instr = {
236                 .callback       = NULL,
237                 .priv           = 0,
238         };
239
240         int blocks;
241         loff_t ofs;
242         int blocksize = 1 << this->erase_shift;
243         int start_block, end_block;
244         ssize_t retlen;
245         u_char *buf;
246         u_char *verify_buf;
247         int ret;
248
249         buf = malloc(blocksize);
250         if (!buf) {
251                 printf("Not enough malloc space available!\n");
252                 return -1;
253         }
254
255         verify_buf = malloc(blocksize);
256         if (!verify_buf) {
257                 printf("Not enough malloc space available!\n");
258                 return -1;
259         }
260
261         start_block = start >> this->erase_shift;
262         end_block = (start + size) >> this->erase_shift;
263
264         /* Protect boot-loader from badblock testing */
265         if (start_block < 2)
266                 start_block = 2;
267
268         if (end_block > (mtd->size >> this->erase_shift))
269                 end_block = mtd->size >> this->erase_shift;
270
271         blocks = start_block;
272         ofs = start_block << this->erase_shift;
273         while (blocks < end_block) {
274                 printf("\rTesting block %d at 0x%x", (u32)(ofs >> this->erase_shift), (u32)ofs);
275
276                 ret = mtd->block_isbad(mtd, ofs);
277                 if (ret) {
278                         printf("Skip erase bad block %d at 0x%x\n",
279                                (u32)(ofs >> this->erase_shift), (u32)ofs);
280                         goto next;
281                 }
282
283                 instr.addr = ofs;
284                 instr.len = blocksize;
285                 ret = mtd->erase(mtd, &instr);
286                 if (ret) {
287                         printk("Erase failed 0x%x, %d\n", (u32)ofs, ret);
288                         goto next;
289                 }
290
291                 ret = mtd->write(mtd, ofs, blocksize, &retlen, buf);
292                 if (ret) {
293                         printk("Write failed 0x%x, %d\n", (u32)ofs, ret);
294                         goto next;
295                 }
296
297                 ret = mtd->read(mtd, ofs, blocksize, &retlen, verify_buf);
298                 if (ret) {
299                         printk("Read failed 0x%x, %d\n", (u32)ofs, ret);
300                         goto next;
301                 }
302
303                 if (memcmp(buf, verify_buf, blocksize)) {
304                         printk("\nRead/Write test failed at 0x%x\n", (u32)ofs);
305                         break;
306                 }
307 next:
308                 ofs += blocksize;
309                 blocks++;
310         }
311         printf("...Done\n");
312
313         free(buf);
314         free(verify_buf);
315
316         return 0;
317 }
318
319 static int onenand_dump(struct mtd_info *mtd, ulong off, int only_oob)
320 {
321         int i;
322         u_char *datbuf, *oobbuf, *p;
323         struct mtd_oob_ops ops;
324         loff_t addr;
325
326         datbuf = malloc(mtd->writesize + mtd->oobsize);
327         oobbuf = malloc(mtd->oobsize);
328         if (!datbuf || !oobbuf) {
329                 puts("No memory for page buffer\n");
330                 return 1;
331         }
332         off &= ~(mtd->writesize - 1);
333         addr = (loff_t) off;
334         memset(&ops, 0, sizeof(ops));
335         ops.datbuf = datbuf;
336         ops.oobbuf = oobbuf;
337         ops.len = mtd->writesize;
338         ops.ooblen = mtd->oobsize;
339         ops.retlen = 0;
340         i = mtd->read_oob(mtd, addr, &ops);
341         if (i < 0) {
342                 printf("Error (%d) reading page %08lx\n", i, off);
343                 free(datbuf);
344                 free(oobbuf);
345                 return 1;
346         }
347         printf("Page %08lx dump:\n", off);
348         i = mtd->writesize >> 4;
349         p = datbuf;
350
351         while (i--) {
352                 if (!only_oob)
353                         printf("\t%02x %02x %02x %02x %02x %02x %02x %02x"
354                                "  %02x %02x %02x %02x %02x %02x %02x %02x\n",
355                                p[0], p[1], p[2], p[3], p[4], p[5], p[6], p[7],
356                                p[8], p[9], p[10], p[11], p[12], p[13], p[14],
357                                p[15]);
358                 p += 16;
359         }
360         puts("OOB:\n");
361         i = mtd->oobsize >> 3;
362         p = oobbuf;
363
364         while (i--) {
365                 printf("\t%02x %02x %02x %02x %02x %02x %02x %02x\n",
366                        p[0], p[1], p[2], p[3], p[4], p[5], p[6], p[7]);
367                 p += 8;
368         }
369         free(datbuf);
370         free(oobbuf);
371
372         return 0;
373 }
374
375 static int do_onenand_info(cmd_tbl_t * cmdtp, int flag, int argc, char * const argv[])
376 {
377         printf("%s\n", mtd->name);
378         return 0;
379 }
380
381 static int do_onenand_bad(cmd_tbl_t * cmdtp, int flag, int argc, char * const argv[])
382 {
383         ulong ofs;
384
385         mtd = &onenand_mtd;
386         /* Currently only one OneNAND device is supported */
387         printf("\nDevice %d bad blocks:\n", 0);
388         for (ofs = 0; ofs < mtd->size; ofs += mtd->erasesize) {
389                 if (mtd->block_isbad(mtd, ofs))
390                         printf("  %08x\n", (u32)ofs);
391         }
392
393         return 0;
394 }
395
396 static int do_onenand_read(cmd_tbl_t * cmdtp, int flag, int argc, char * const argv[])
397 {
398         char *s;
399         int oob = 0;
400         ulong addr, ofs;
401         ssize_t len;
402         int ret = 0;
403         ssize_t retlen = 0;
404
405         if (argc < 3)
406                 return cmd_usage(cmdtp);
407
408         s = strchr(argv[0], '.');
409         if ((s != NULL) && (!strcmp(s, ".oob")))
410                 oob = 1;
411
412         addr = (ulong)simple_strtoul(argv[1], NULL, 16);
413
414         printf("OneNAND read: ");
415         if (arg_off_size(argc - 2, argv + 2, &ofs, &len) != 0)
416                 return 1;
417
418         ret = onenand_block_read(ofs, len, &retlen, (u8 *)addr, oob);
419
420         printf(" %d bytes read: %s\n", retlen, ret ? "ERROR" : "OK");
421
422         return ret == 0 ? 0 : 1;
423 }
424
425 static int do_onenand_write(cmd_tbl_t * cmdtp, int flag, int argc, char * const argv[])
426 {
427         ulong addr, ofs;
428         size_t len = 0;
429         size_t part_len = 0;
430         int ret = 0, withoob = 0;
431         ssize_t retlen = 0;
432         struct onenand_chip *this = mtd->priv;
433         u32 blocksize = (1 << this->erase_shift);
434         u32 blockmask = blocksize - 1;
435
436         if (argc < 3)
437                 return cmd_usage(cmdtp);
438
439         if (strncmp(argv[0] + 6, "yaffs", 5) == 0)
440                 withoob = 1;
441
442         addr = (ulong)simple_strtoul(argv[1], NULL, 16);
443
444         printf("OneNAND write: ");
445         if (arg_off_size(argc - 2, argv + 2, &ofs, &len) != 0)
446                 return 1;
447
448         /* Must block aligned.
449          * Writing page across block will not be filtered by bad block check routine */
450         if (ofs & blockmask) {
451                 part_len = blocksize - (ofs & blockmask);
452                 if (len < part_len)
453                         part_len = len;
454                 printf("\tNot block aligned write (0x%x byte @ 0x%x -> 0x%x)\n", part_len, addr, ofs);
455                 ret = onenand_block_write(ofs, part_len, &retlen, (u8 *)addr, withoob);
456                 printf(" %d bytes written: %s\n", retlen, ret ? "ERROR" : "OK");
457                 len -= part_len;
458                 ofs += part_len;
459                 addr += part_len;
460         }
461
462         if (len) {
463                 ret = onenand_block_write(ofs, len, &retlen, (u8 *)addr, withoob);
464                 printf(" %d bytes written: %s\n", retlen, ret ? "ERROR" : "OK");
465         }
466
467         return ret == 0 ? 0 : 1;
468 }
469
470 static int do_onenand_erase(cmd_tbl_t * cmdtp, int flag, int argc, char * const argv[])
471 {
472         ulong ofs;
473         int ret = 0;
474         ssize_t len;
475         int force;
476
477         /*
478          * Syntax is:
479          *   0       1     2       3    4
480          *   onenand erase [force] [off size]
481          */
482         argc--;
483         argv++;
484         if (argc)
485         {
486                 if (!strcmp("force", argv[0]))
487                 {
488                         force = 1;
489                         argc--;
490                         argv++;
491                 }
492         }
493         printf("OneNAND erase: ");
494
495         /* skip first two or three arguments, look for offset and size */
496         if (arg_off_size(argc, argv, &ofs, &len) != 0)
497                 return 1;
498
499         ret = onenand_block_erase(ofs, len, force);
500
501         printf("%s\n", ret ? "ERROR" : "OK");
502
503         return ret == 0 ? 0 : 1;
504 }
505
506 static int do_onenand_test(cmd_tbl_t * cmdtp, int flag, int argc, char * const argv[])
507 {
508         ulong ofs;
509         int ret = 0;
510         ssize_t len;
511
512         /*
513          * Syntax is:
514          *   0       1     2       3    4
515          *   onenand test [force] [off size]
516          */
517
518         printf("OneNAND test: ");
519
520         /* skip first two or three arguments, look for offset and size */
521         if (arg_off_size(argc - 1, argv + 1, &ofs, &len) != 0)
522                 return 1;
523
524         ret = onenand_block_test(ofs, len);
525
526         printf("%s\n", ret ? "ERROR" : "OK");
527
528         return ret == 0 ? 0 : 1;
529 }
530
531 static int do_onenand_dump(cmd_tbl_t * cmdtp, int flag, int argc, char * const argv[])
532 {
533         ulong ofs;
534         int ret = 0;
535         char *s;
536
537         if (argc < 2)
538                 return cmd_usage(cmdtp);
539
540         s = strchr(argv[0], '.');
541         ofs = (int)simple_strtoul(argv[1], NULL, 16);
542
543         if (s != NULL && strcmp(s, ".oob") == 0)
544                 ret = onenand_dump(mtd, ofs, 1);
545         else
546                 ret = onenand_dump(mtd, ofs, 0);
547
548         return ret == 0 ? 1 : 0;
549 }
550
551 static int do_onenand_markbad(cmd_tbl_t * cmdtp, int flag, int argc, char * const argv[])
552 {
553         int ret = 0;
554         ulong addr;
555
556         argc -= 2;
557         argv += 2;
558
559         if (argc <= 0)
560                 return cmd_usage(cmdtp);
561
562         while (argc > 0) {
563                 addr = simple_strtoul(*argv, NULL, 16);
564
565                 if (mtd->block_markbad(mtd, addr)) {
566                         printf("block 0x%08lx NOT marked "
567                                 "as bad! ERROR %d\n",
568                                 addr, ret);
569                         ret = 1;
570                 } else {
571                         printf("block 0x%08lx successfully "
572                                 "marked as bad\n",
573                                 addr);
574                 }
575                 --argc;
576                 ++argv;
577         }
578         return ret;
579 }
580
581 static int do_onenand_lock(cmd_tbl_t * cmdtp, int flag, int argc, char *const argv[])
582 {
583         ulong start;
584         ssize_t size;
585         struct onenand_chip *this = mtd->priv;
586         loff_t ofs;
587         int status;
588         int status_mask = ONENAND_WP_LS|ONENAND_WP_LTS;
589
590         if (argc < 4) {
591                 start = 0x0;
592                 size = mtd->size;
593         } else {
594                 if (arg_off_size(argc - 2, argv + 2, &start, &size))
595                         return 1;
596         }
597
598         for (ofs = start; ofs < (start + size); ofs += mtd->erasesize) {
599                 if (this->block_islock != NULL)
600                         status = this->block_islock(mtd, ofs);
601                 else
602                         status = 0;
603
604                 if (status & status_mask)
605                         return (status & status_mask);
606         }
607
608         return 0;
609 }
610
611 static cmd_tbl_t cmd_onenand_sub[] = {
612         U_BOOT_CMD_MKENT(info, 1, 0, do_onenand_info, "", ""),
613         U_BOOT_CMD_MKENT(bad, 1, 0, do_onenand_bad, "", ""),
614         U_BOOT_CMD_MKENT(read, 4, 0, do_onenand_read, "", ""),
615         U_BOOT_CMD_MKENT(write, 4, 0, do_onenand_write, "", ""),
616         U_BOOT_CMD_MKENT(write.yaffs, 4, 0, do_onenand_write, "", ""),
617         U_BOOT_CMD_MKENT(erase, 3, 0, do_onenand_erase, "", ""),
618         U_BOOT_CMD_MKENT(test, 3, 0, do_onenand_test, "", ""),
619         U_BOOT_CMD_MKENT(dump, 2, 0, do_onenand_dump, "", ""),
620         U_BOOT_CMD_MKENT(markbad, CONFIG_SYS_MAXARGS, 0, do_onenand_markbad, "", ""),
621         U_BOOT_CMD_MKENT(lock, 3, 0, do_onenand_lock, "", ""),
622 };
623
624 #ifdef CONFIG_NEEDS_MANUAL_RELOC
625 void onenand_reloc(void) {
626         fixup_cmdtable(cmd_onenand_sub, ARRAY_SIZE(cmd_onenand_sub));
627 }
628 #endif
629
630 int do_onenand(cmd_tbl_t * cmdtp, int flag, int argc, char * const argv[])
631 {
632         cmd_tbl_t *c;
633
634         if (argc < 2)
635                 return cmd_usage(cmdtp);
636
637         mtd = &onenand_mtd;
638
639         /* Strip off leading 'onenand' command argument */
640         argc--;
641         argv++;
642
643         c = find_cmd_tbl(argv[0], &cmd_onenand_sub[0], ARRAY_SIZE(cmd_onenand_sub));
644
645         if (c)
646                 return c->cmd(cmdtp, flag, argc, argv);
647         else
648                 return cmd_usage(cmdtp);
649 }
650
651 U_BOOT_CMD(
652         onenand,        CONFIG_SYS_MAXARGS,     1,      do_onenand,
653         "OneNAND sub-system",
654         "info - show available OneNAND devices\n"
655         "onenand bad - show bad blocks\n"
656         "onenand read[.oob] addr off size\n"
657         "onenand write[.yaffs] addr off size\n"
658         "    read/write 'size' bytes starting at offset 'off'\n"
659         "    to/from memory address 'addr', skipping bad blocks.\n"
660         "onenand erase [force] [off size] - erase 'size' bytes from\n"
661         "onenand test [off size] - test 'size' bytes from\n"
662         "    offset 'off' (entire device if not specified)\n"
663         "onenand dump[.oob] off - dump page\n"
664         "onenand markbad off [...] - mark bad block(s) at offset (UNSAFE)"
665 );