]> git.kernelconcepts.de Git - karo-tx-uboot.git/blob - common/cmd_onenand.c
Merge branch 'master' of git://git.denx.de/u-boot-imx
[karo-tx-uboot.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 *argv[], ulong *off, size_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, size_t len,
69                               size_t *retlen, u_char *buf, int oob)
70 {
71         struct onenand_chip *this = mtd->priv;
72         int blocks = (int) len >> this->erase_shift;
73         int blocksize = (1 << this->erase_shift);
74         loff_t ofs = from;
75         struct mtd_oob_ops ops = {
76                 .retlen         = 0,
77         };
78         int ret;
79
80         if (oob)
81                 ops.ooblen = blocksize;
82         else
83                 ops.len = blocksize;
84
85         while (blocks) {
86                 ret = mtd->block_isbad(mtd, ofs);
87                 if (ret) {
88                         printk("Bad blocks %d at 0x%x\n",
89                                (u32)(ofs >> this->erase_shift), (u32)ofs);
90                         ofs += blocksize;
91                         continue;
92                 }
93
94                 if (oob)
95                         ops.oobbuf = buf;
96                 else
97                         ops.datbuf = buf;
98
99                 ops.retlen = 0;
100                 ret = mtd->read_oob(mtd, ofs, &ops);
101                 if (ret) {
102                         printk("Read failed 0x%x, %d\n", (u32)ofs, ret);
103                         ofs += blocksize;
104                         continue;
105                 }
106                 ofs += blocksize;
107                 buf += blocksize;
108                 blocks--;
109                 *retlen += ops.retlen;
110         }
111
112         return 0;
113 }
114
115 static int onenand_block_write(loff_t to, size_t len,
116                                size_t *retlen, const u_char * buf)
117 {
118         struct onenand_chip *this = mtd->priv;
119         int blocks = len >> this->erase_shift;
120         int blocksize = (1 << this->erase_shift);
121         loff_t ofs;
122         size_t _retlen = 0;
123         int ret;
124
125         if (to == next_ofs) {
126                 next_ofs = to + len;
127                 to += skip_ofs;
128         } else {
129                 next_ofs = to + len;
130                 skip_ofs = 0;
131         }
132         ofs = to;
133
134         while (blocks) {
135                 ret = mtd->block_isbad(mtd, ofs);
136                 if (ret) {
137                         printk("Bad blocks %d at 0x%x\n",
138                                (u32)(ofs >> this->erase_shift), (u32)ofs);
139                         skip_ofs += blocksize;
140                         goto next;
141                 }
142
143                 ret = mtd->write(mtd, ofs, blocksize, &_retlen, buf);
144                 if (ret) {
145                         printk("Write failed 0x%x, %d", (u32)ofs, ret);
146                         skip_ofs += blocksize;
147                         goto next;
148                 }
149
150                 buf += blocksize;
151                 blocks--;
152                 *retlen += _retlen;
153 next:
154                 ofs += blocksize;
155         }
156
157         return 0;
158 }
159
160 static int onenand_block_erase(u32 start, u32 size, int force)
161 {
162         struct onenand_chip *this = mtd->priv;
163         struct erase_info instr = {
164                 .callback       = NULL,
165         };
166         loff_t ofs;
167         int ret;
168         int blocksize = 1 << this->erase_shift;
169
170         for (ofs = start; ofs < (start + size); ofs += blocksize) {
171                 ret = mtd->block_isbad(mtd, ofs);
172                 if (ret && !force) {
173                         printf("Skip erase bad block %d at 0x%x\n",
174                                (u32)(ofs >> this->erase_shift), (u32)ofs);
175                         continue;
176                 }
177
178                 instr.addr = ofs;
179                 instr.len = blocksize;
180                 instr.priv = force;
181                 instr.mtd = mtd;
182                 ret = mtd->erase(mtd, &instr);
183                 if (ret) {
184                         printf("erase failed block %d at 0x%x\n",
185                                (u32)(ofs >> this->erase_shift), (u32)ofs);
186                         continue;
187                 }
188         }
189
190         return 0;
191 }
192
193 static int onenand_block_test(u32 start, u32 size)
194 {
195         struct onenand_chip *this = mtd->priv;
196         struct erase_info instr = {
197                 .callback       = NULL,
198                 .priv           = 0,
199         };
200
201         int blocks;
202         loff_t ofs;
203         int blocksize = 1 << this->erase_shift;
204         int start_block, end_block;
205         size_t retlen;
206         u_char *buf;
207         u_char *verify_buf;
208         int ret;
209
210         buf = malloc(blocksize);
211         if (!buf) {
212                 printf("Not enough malloc space available!\n");
213                 return -1;
214         }
215
216         verify_buf = malloc(blocksize);
217         if (!verify_buf) {
218                 printf("Not enough malloc space available!\n");
219                 return -1;
220         }
221
222         start_block = start >> this->erase_shift;
223         end_block = (start + size) >> this->erase_shift;
224
225         /* Protect boot-loader from badblock testing */
226         if (start_block < 2)
227                 start_block = 2;
228
229         if (end_block > (mtd->size >> this->erase_shift))
230                 end_block = mtd->size >> this->erase_shift;
231
232         blocks = start_block;
233         ofs = start;
234         while (blocks < end_block) {
235                 printf("\rTesting block %d at 0x%x", (u32)(ofs >> this->erase_shift), (u32)ofs);
236
237                 ret = mtd->block_isbad(mtd, ofs);
238                 if (ret) {
239                         printf("Skip erase bad block %d at 0x%x\n",
240                                (u32)(ofs >> this->erase_shift), (u32)ofs);
241                         goto next;
242                 }
243
244                 instr.addr = ofs;
245                 instr.len = blocksize;
246                 ret = mtd->erase(mtd, &instr);
247                 if (ret) {
248                         printk("Erase failed 0x%x, %d\n", (u32)ofs, ret);
249                         goto next;
250                 }
251
252                 ret = mtd->write(mtd, ofs, blocksize, &retlen, buf);
253                 if (ret) {
254                         printk("Write failed 0x%x, %d\n", (u32)ofs, ret);
255                         goto next;
256                 }
257
258                 ret = mtd->read(mtd, ofs, blocksize, &retlen, verify_buf);
259                 if (ret) {
260                         printk("Read failed 0x%x, %d\n", (u32)ofs, ret);
261                         goto next;
262                 }
263
264                 if (memcmp(buf, verify_buf, blocksize))
265                         printk("\nRead/Write test failed at 0x%x\n", (u32)ofs);
266
267 next:
268                 ofs += blocksize;
269                 blocks++;
270         }
271         printf("...Done\n");
272
273         free(buf);
274         free(verify_buf);
275
276         return 0;
277 }
278
279 static int onenand_dump(struct mtd_info *mtd, ulong off, int only_oob)
280 {
281         int i;
282         u_char *datbuf, *oobbuf, *p;
283         struct mtd_oob_ops ops;
284         loff_t addr;
285
286         datbuf = malloc(mtd->writesize + mtd->oobsize);
287         oobbuf = malloc(mtd->oobsize);
288         if (!datbuf || !oobbuf) {
289                 puts("No memory for page buffer\n");
290                 return 1;
291         }
292         off &= ~(mtd->writesize - 1);
293         addr = (loff_t) off;
294         memset(&ops, 0, sizeof(ops));
295         ops.datbuf = datbuf;
296         ops.oobbuf = oobbuf; /* must exist, but oob data will be appended to ops.datbuf */
297         ops.len = mtd->writesize;
298         ops.ooblen = mtd->oobsize;
299         ops.retlen = 0;
300         i = mtd->read_oob(mtd, addr, &ops);
301         if (i < 0) {
302                 printf("Error (%d) reading page %08lx\n", i, off);
303                 free(datbuf);
304                 free(oobbuf);
305                 return 1;
306         }
307         printf("Page %08lx dump:\n", off);
308         i = mtd->writesize >> 4;
309         p = datbuf;
310
311         while (i--) {
312                 if (!only_oob)
313                         printf("\t%02x %02x %02x %02x %02x %02x %02x %02x"
314                                "  %02x %02x %02x %02x %02x %02x %02x %02x\n",
315                                p[0], p[1], p[2], p[3], p[4], p[5], p[6], p[7],
316                                p[8], p[9], p[10], p[11], p[12], p[13], p[14],
317                                p[15]);
318                 p += 16;
319         }
320         puts("OOB:\n");
321         i = mtd->oobsize >> 3;
322         while (i--) {
323                 printf("\t%02x %02x %02x %02x %02x %02x %02x %02x\n",
324                        p[0], p[1], p[2], p[3], p[4], p[5], p[6], p[7]);
325                 p += 8;
326         }
327         free(datbuf);
328         free(oobbuf);
329
330         return 0;
331 }
332
333 static int do_onenand_info(cmd_tbl_t * cmdtp, int flag, int argc, char *argv[])
334 {
335         printf("%s\n", mtd->name);
336         return 0;
337 }
338
339 static int do_onenand_bad(cmd_tbl_t * cmdtp, int flag, int argc, char *argv[])
340 {
341         ulong ofs;
342
343         mtd = &onenand_mtd;
344         /* Currently only one OneNAND device is supported */
345         printf("\nDevice %d bad blocks:\n", 0);
346         for (ofs = 0; ofs < mtd->size; ofs += mtd->erasesize) {
347                 if (mtd->block_isbad(mtd, ofs))
348                         printf("  %08x\n", (u32)ofs);
349         }
350
351         return 0;
352 }
353
354 static int do_onenand_read(cmd_tbl_t * cmdtp, int flag, int argc, char *argv[])
355 {
356         char *s;
357         int oob = 0;
358         ulong addr, ofs;
359         size_t len;
360         int ret = 0;
361         size_t retlen = 0;
362
363         if (argc < 3)
364         {
365                 cmd_usage(cmdtp);
366                 return 1;
367         }
368
369         s = strchr(argv[0], '.');
370         if ((s != NULL) && (!strcmp(s, ".oob")))
371                 oob = 1;
372
373         addr = (ulong)simple_strtoul(argv[1], NULL, 16);
374
375         printf("\nOneNAND read: ");
376         if (arg_off_size(argc - 2, argv + 2, &ofs, &len) != 0)
377                 return 1;
378
379         ret = onenand_block_read(ofs, len, &retlen, (u8 *)addr, oob);
380
381         printf(" %d bytes read: %s\n", retlen, ret ? "ERROR" : "OK");
382
383         return ret == 0 ? 0 : 1;
384 }
385
386 static int do_onenand_write(cmd_tbl_t * cmdtp, int flag, int argc, char *argv[])
387 {
388         ulong addr, ofs;
389         size_t len;
390         int ret = 0;
391         size_t retlen = 0;
392
393         if (argc < 3)
394         {
395                 cmd_usage(cmdtp);
396                 return 1;
397         }
398
399         addr = (ulong)simple_strtoul(argv[1], NULL, 16);
400
401         printf("\nOneNAND write: ");
402         if (arg_off_size(argc - 2, argv + 2, &ofs, &len) != 0)
403                 return 1;
404
405         ret = onenand_block_write(ofs, len, &retlen, (u8 *)addr);
406
407         printf(" %d bytes written: %s\n", retlen, ret ? "ERROR" : "OK");
408
409         return ret == 0 ? 0 : 1;
410 }
411
412 static int do_onenand_erase(cmd_tbl_t * cmdtp, int flag, int argc, char *argv[])
413 {
414         ulong ofs;
415         int ret = 0;
416         size_t len;
417         int force;
418
419         /*
420          * Syntax is:
421          *   0       1     2       3    4
422          *   onenand erase [force] [off size]
423          */
424         argc--;
425         argv++;
426         if (argc)
427         {
428                 if (!strcmp("force", argv[0]))
429                 {
430                         force = 1;
431                         argc--;
432                         argv++;
433                 }
434         }
435         printf("\nOneNAND erase: ");
436
437         /* skip first two or three arguments, look for offset and size */
438         if (arg_off_size(argc, argv, &ofs, &len) != 0)
439                 return 1;
440
441         ret = onenand_block_erase(ofs, len, force);
442
443         printf("%s\n", ret ? "ERROR" : "OK");
444
445         return ret == 0 ? 0 : 1;
446 }
447
448 static int do_onenand_test(cmd_tbl_t * cmdtp, int flag, int argc, char *argv[])
449 {
450         ulong ofs;
451         int ret = 0;
452         size_t len;
453
454         /*
455          * Syntax is:
456          *   0       1     2       3    4
457          *   onenand test [force] [off size]
458          */
459
460         printf("\nOneNAND test: ");
461
462         /* skip first two or three arguments, look for offset and size */
463         if (arg_off_size(argc - 1, argv + 1, &ofs, &len) != 0)
464                 return 1;
465
466         ret = onenand_block_test(ofs, len);
467
468         printf("%s\n", ret ? "ERROR" : "OK");
469
470         return ret == 0 ? 0 : 1;
471 }
472
473 static int do_onenand_dump(cmd_tbl_t * cmdtp, int flag, int argc, char *argv[])
474 {
475         ulong ofs;
476         int ret = 0;
477         char *s;
478
479         if (argc < 2)
480         {
481                 cmd_usage(cmdtp);
482                 return 1;
483         }
484
485         s = strchr(argv[0], '.');
486         ofs = (int)simple_strtoul(argv[1], NULL, 16);
487
488         if (s != NULL && strcmp(s, ".oob") == 0)
489                 ret = onenand_dump(mtd, ofs, 1);
490         else
491                 ret = onenand_dump(mtd, ofs, 0);
492
493         return ret == 0 ? 1 : 0;
494 }
495
496 static int do_onenand_markbad(cmd_tbl_t * cmdtp, int flag, int argc, char *argv[])
497 {
498         int ret = 0;
499         ulong addr;
500
501         argc -= 2;
502         argv += 2;
503
504         if (argc <= 0)
505         {
506                 cmd_usage(cmdtp);
507                 return 1;
508         }
509
510         while (argc > 0) {
511                 addr = simple_strtoul(*argv, NULL, 16);
512
513                 if (mtd->block_markbad(mtd, addr)) {
514                         printf("block 0x%08lx NOT marked "
515                                 "as bad! ERROR %d\n",
516                                 addr, ret);
517                         ret = 1;
518                 } else {
519                         printf("block 0x%08lx successfully "
520                                 "marked as bad\n",
521                                 addr);
522                 }
523                 --argc;
524                 ++argv;
525         }
526         return ret;
527 }
528
529 static cmd_tbl_t cmd_onenand_sub[] = {
530         U_BOOT_CMD_MKENT(info, 1, 0, do_onenand_info, "", ""),
531         U_BOOT_CMD_MKENT(bad, 1, 0, do_onenand_bad, "", ""),
532         U_BOOT_CMD_MKENT(read, 4, 0, do_onenand_read, "", ""),
533         U_BOOT_CMD_MKENT(write, 4, 0, do_onenand_write, "", ""),
534         U_BOOT_CMD_MKENT(erase, 3, 0, do_onenand_erase, "", ""),
535         U_BOOT_CMD_MKENT(test, 3, 0, do_onenand_test, "", ""),
536         U_BOOT_CMD_MKENT(dump, 2, 0, do_onenand_dump, "", ""),
537         U_BOOT_CMD_MKENT(markbad, CONFIG_SYS_MAXARGS, 0, do_onenand_markbad, "", ""),
538 };
539
540 static int do_onenand(cmd_tbl_t * cmdtp, int flag, int argc, char *argv[])
541 {
542         cmd_tbl_t *c;
543
544         mtd = &onenand_mtd;
545
546         /* Strip off leading 'onenand' command argument */
547         argc--;
548         argv++;
549
550         c = find_cmd_tbl(argv[0], &cmd_onenand_sub[0], ARRAY_SIZE(cmd_onenand_sub));
551
552         if (c) {
553                 return  c->cmd(cmdtp, flag, argc, argv);
554         } else {
555                 cmd_usage(cmdtp);
556                 return 1;
557         }
558 }
559
560 U_BOOT_CMD(
561         onenand,        CONFIG_SYS_MAXARGS,     1,      do_onenand,
562         "OneNAND sub-system",
563         "info - show available OneNAND devices\n"
564         "onenand bad - show bad blocks\n"
565         "onenand read[.oob] addr off size\n"
566         "onenand write addr off size\n"
567         "    read/write 'size' bytes starting at offset 'off'\n"
568         "    to/from memory address 'addr', skipping bad blocks.\n"
569         "onenand erase [force] [off size] - erase 'size' bytes from\n"
570         "onenand test [off size] - test 'size' bytes from\n"
571         "    offset 'off' (entire device if not specified)\n"
572         "onenand dump[.oob] off - dump page\n"
573         "onenand markbad off [...] - mark bad block(s) at offset (UNSAFE)"
574 );