Merge branch 'master' of git://git.denx.de/u-boot-arm
[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 *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%x) 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 int do_onenand(cmd_tbl_t * cmdtp, int flag, int argc, char *argv[])
334 {
335         struct onenand_chip *this;
336         int blocksize;
337         ulong addr, ofs;
338         size_t len, retlen = 0;
339         int ret;
340         char *cmd, *s;
341
342         mtd = &onenand_mtd;
343         this = mtd->priv;
344         blocksize = (1 << this->erase_shift);
345
346         cmd = argv[1];
347
348         switch (argc) {
349         case 0:
350         case 1:
351                 goto usage;
352
353         case 2:
354                 if (strcmp(cmd, "info") == 0) {
355                         printf("%s\n", mtd->name);
356                         return 0;
357                 }
358
359                 if (strcmp(cmd, "bad") == 0) {
360                         /* Currently only one OneNAND device is supported */
361                         printf("\nDevice %d bad blocks:\n", 0);
362                         for (ofs = 0; ofs < mtd->size; ofs += mtd->erasesize) {
363                                 if (mtd->block_isbad(mtd, ofs))
364                                         printf("  %08x\n", (u32)ofs);
365                         }
366
367                         return 0;
368                 }
369
370         default:
371                 /* At least 4 args */
372
373                 /*
374                  * Syntax is:
375                  *   0       1     2       3    4
376                  *   onenand erase [force] [off size]
377                  */
378                 if ((strcmp(cmd, "erase") == 0) || (strcmp(cmd, "test") == 0)) {
379                         int force = argc > 2 && !strcmp("force", argv[2]);
380                         int o = force ? 3 : 2;
381                         int erase;
382
383                         erase = strcmp(cmd, "erase") == 0; /* 1 = erase, 0 = test */
384                         printf("\nOneNAND %s: ", erase ? "erase" : "test");
385
386                         /* skip first two or three arguments, look for offset and size */
387                         if (arg_off_size(argc - o, argv + o, &ofs, &len) != 0)
388                                 return 1;
389
390                         if (erase)
391                                 ret = onenand_block_erase(ofs, len, force);
392                         else
393                                 ret = onenand_block_test(ofs, len);
394
395                         printf("%s\n", ret ? "ERROR" : "OK");
396
397                         return ret == 0 ? 0 : 1;
398                 }
399
400                 if (strncmp(cmd, "read", 4) == 0 || strncmp(cmd, "write", 5) == 0) {
401                         int read;
402                         int oob = 0;
403
404                         if (argc < 4)
405                                 goto usage;
406
407                         addr = (ulong)simple_strtoul(argv[2], NULL, 16);
408
409                         read = strncmp(cmd, "read", 4) == 0; /* 1 = read, 0 = write */
410                         printf("\nOneNAND %s: ", read ? "read" : "write");
411                         if (arg_off_size(argc - 3, argv + 3, &ofs, &len) != 0)
412                                 return 1;
413
414                         s = strchr(cmd, '.');
415                         if ((s != NULL) && (!strcmp(s, ".oob")))
416                                 oob = 1;
417
418                         if (read) {
419                                 ret = onenand_block_read(ofs, len, &retlen,
420                                                          (u8 *)addr, oob);
421                         } else {
422                                 ret = onenand_block_write(ofs, len, &retlen,
423                                                           (u8 *)addr);
424                         }
425
426                         printf(" %d bytes %s: %s\n", retlen,
427                                read ? "read" : "written", ret ? "ERROR" : "OK");
428
429                         return ret == 0 ? 0 : 1;
430                 }
431
432                 if (strcmp(cmd, "markbad") == 0) {
433                         addr = (ulong)simple_strtoul(argv[2], NULL, 16);
434
435                         int ret = mtd->block_markbad(mtd, addr);
436                         if (ret == 0) {
437                                 printf("block 0x%08lx successfully marked as bad\n",
438                                                 (ulong) addr);
439                                 return 0;
440                         } else {
441                                 printf("block 0x%08lx NOT marked as bad! ERROR %d\n",
442                                                 (ulong) addr, ret);
443                         }
444                         return 1;
445                 }
446
447                 if (strncmp(cmd, "dump", 4) == 0) {
448                         if (argc < 3)
449                                 goto usage;
450
451                         s = strchr(cmd, '.');
452                         ofs = (int)simple_strtoul(argv[2], NULL, 16);
453
454                         if (s != NULL && strcmp(s, ".oob") == 0)
455                                 ret = onenand_dump(mtd, ofs, 1);
456                         else
457                                 ret = onenand_dump(mtd, ofs, 0);
458
459                         return ret == 0 ? 1 : 0;
460                 }
461
462                 break;
463         }
464
465         return 0;
466
467 usage:
468         cmd_usage(cmdtp);
469         return 1;
470 }
471
472 U_BOOT_CMD(
473         onenand,        6,      1,      do_onenand,
474         "OneNAND sub-system",
475         "info - show available OneNAND devices\n"
476         "onenand bad - show bad blocks\n"
477         "onenand read[.oob] addr off size\n"
478         "onenand write[.oob] addr off size\n"
479         "    read/write 'size' bytes starting at offset 'off'\n"
480         "    to/from memory address 'addr', skipping bad blocks.\n"
481         "onenand erase [force] [off size] - erase 'size' bytes from\n"
482         "onenand test [off size] - test 'size' bytes from\n"
483         "    offset 'off' (entire device if not specified)\n"
484         "onenand dump[.oob] off - dump page\n"
485         "onenand markbad off - mark bad block at offset (UNSAFE)\n"
486 );