Consolidate arch-specific mem_malloc_init() implementations
[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 #if !defined(CONFIG_SYS_64BIT_VSPRINTF)
23 #warning Please define CONFIG_SYS_64BIT_VSPRINTF for correct output!
24 #endif
25
26 static struct mtd_info *mtd;
27
28 static loff_t next_ofs;
29 static loff_t skip_ofs;
30
31 static inline int str2long(char *p, ulong *num)
32 {
33         char *endptr;
34
35         *num = simple_strtoul(p, &endptr, 16);
36         return (*p != '\0' && *endptr == '\0') ? 1 : 0;
37 }
38
39 static int arg_off_size(int argc, char *argv[], ulong *off, size_t *size)
40 {
41         if (argc >= 1) {
42                 if (!(str2long(argv[0], off))) {
43                         printf("'%s' is not a number\n", argv[0]);
44                         return -1;
45                 }
46         } else {
47                 *off = 0;
48         }
49
50         if (argc >= 2) {
51                 if (!(str2long(argv[1], (ulong *)size))) {
52                         printf("'%s' is not a number\n", argv[1]);
53                         return -1;
54                 }
55         } else {
56                 *size = mtd->size - *off;
57         }
58
59         if ((*off + *size) > mtd->size) {
60                 printf("total chip size (0x%llx) exceeded!\n", mtd->size);
61                 return -1;
62         }
63
64         if (*size == mtd->size)
65                 puts("whole chip\n");
66         else
67                 printf("offset 0x%lx, size 0x%x\n", *off, *size);
68
69         return 0;
70 }
71
72 static int onenand_block_read(loff_t from, size_t len,
73                               size_t *retlen, u_char *buf, int oob)
74 {
75         struct onenand_chip *this = mtd->priv;
76         int blocks = (int) len >> this->erase_shift;
77         int blocksize = (1 << this->erase_shift);
78         loff_t ofs = from;
79         struct mtd_oob_ops ops = {
80                 .retlen         = 0,
81         };
82         int ret;
83
84         if (oob)
85                 ops.ooblen = blocksize;
86         else
87                 ops.len = blocksize;
88
89         while (blocks) {
90                 ret = mtd->block_isbad(mtd, ofs);
91                 if (ret) {
92                         printk("Bad blocks %d at 0x%x\n",
93                                (u32)(ofs >> this->erase_shift), (u32)ofs);
94                         ofs += blocksize;
95                         continue;
96                 }
97
98                 if (oob)
99                         ops.oobbuf = buf;
100                 else
101                         ops.datbuf = buf;
102
103                 ops.retlen = 0;
104                 ret = mtd->read_oob(mtd, ofs, &ops);
105                 if (ret) {
106                         printk("Read failed 0x%x, %d\n", (u32)ofs, ret);
107                         ofs += blocksize;
108                         continue;
109                 }
110                 ofs += blocksize;
111                 buf += blocksize;
112                 blocks--;
113                 *retlen += ops.retlen;
114         }
115
116         return 0;
117 }
118
119 static int onenand_block_write(loff_t to, size_t len,
120                                size_t *retlen, const u_char * buf)
121 {
122         struct onenand_chip *this = mtd->priv;
123         int blocks = len >> this->erase_shift;
124         int blocksize = (1 << this->erase_shift);
125         loff_t ofs;
126         size_t _retlen = 0;
127         int ret;
128
129         if (to == next_ofs) {
130                 next_ofs = to + len;
131                 to += skip_ofs;
132         } else {
133                 next_ofs = to + len;
134                 skip_ofs = 0;
135         }
136         ofs = to;
137
138         while (blocks) {
139                 ret = mtd->block_isbad(mtd, ofs);
140                 if (ret) {
141                         printk("Bad blocks %d at 0x%x\n",
142                                (u32)(ofs >> this->erase_shift), (u32)ofs);
143                         skip_ofs += blocksize;
144                         goto next;
145                 }
146
147                 ret = mtd->write(mtd, ofs, blocksize, &_retlen, buf);
148                 if (ret) {
149                         printk("Write failed 0x%x, %d", (u32)ofs, ret);
150                         skip_ofs += blocksize;
151                         goto next;
152                 }
153
154                 buf += blocksize;
155                 blocks--;
156                 *retlen += _retlen;
157 next:
158                 ofs += blocksize;
159         }
160
161         return 0;
162 }
163
164 static int onenand_block_erase(u32 start, u32 size, int force)
165 {
166         struct onenand_chip *this = mtd->priv;
167         struct erase_info instr = {
168                 .callback       = NULL,
169         };
170         loff_t ofs;
171         int ret;
172         int blocksize = 1 << this->erase_shift;
173
174         for (ofs = start; ofs < (start + size); ofs += blocksize) {
175                 ret = mtd->block_isbad(mtd, ofs);
176                 if (ret && !force) {
177                         printf("Skip erase bad block %d at 0x%x\n",
178                                (u32)(ofs >> this->erase_shift), (u32)ofs);
179                         continue;
180                 }
181
182                 instr.addr = ofs;
183                 instr.len = blocksize;
184                 instr.priv = force;
185                 instr.mtd = mtd;
186                 ret = mtd->erase(mtd, &instr);
187                 if (ret) {
188                         printf("erase failed block %d at 0x%x\n",
189                                (u32)(ofs >> this->erase_shift), (u32)ofs);
190                         continue;
191                 }
192         }
193
194         return 0;
195 }
196
197 static int onenand_block_test(u32 start, u32 size)
198 {
199         struct onenand_chip *this = mtd->priv;
200         struct erase_info instr = {
201                 .callback       = NULL,
202                 .priv           = 0,
203         };
204
205         int blocks;
206         loff_t ofs;
207         int blocksize = 1 << this->erase_shift;
208         int start_block, end_block;
209         size_t retlen;
210         u_char *buf;
211         u_char *verify_buf;
212         int ret;
213
214         buf = malloc(blocksize);
215         if (!buf) {
216                 printf("Not enough malloc space available!\n");
217                 return -1;
218         }
219
220         verify_buf = malloc(blocksize);
221         if (!verify_buf) {
222                 printf("Not enough malloc space available!\n");
223                 return -1;
224         }
225
226         start_block = start >> this->erase_shift;
227         end_block = (start + size) >> this->erase_shift;
228
229         /* Protect boot-loader from badblock testing */
230         if (start_block < 2)
231                 start_block = 2;
232
233         if (end_block > (mtd->size >> this->erase_shift))
234                 end_block = mtd->size >> this->erase_shift;
235
236         blocks = start_block;
237         ofs = start;
238         while (blocks < end_block) {
239                 printf("\rTesting block %d at 0x%x", (u32)(ofs >> this->erase_shift), (u32)ofs);
240
241                 ret = mtd->block_isbad(mtd, ofs);
242                 if (ret) {
243                         printf("Skip erase bad block %d at 0x%x\n",
244                                (u32)(ofs >> this->erase_shift), (u32)ofs);
245                         goto next;
246                 }
247
248                 instr.addr = ofs;
249                 instr.len = blocksize;
250                 ret = mtd->erase(mtd, &instr);
251                 if (ret) {
252                         printk("Erase failed 0x%x, %d\n", (u32)ofs, ret);
253                         goto next;
254                 }
255
256                 ret = mtd->write(mtd, ofs, blocksize, &retlen, buf);
257                 if (ret) {
258                         printk("Write failed 0x%x, %d\n", (u32)ofs, ret);
259                         goto next;
260                 }
261
262                 ret = mtd->read(mtd, ofs, blocksize, &retlen, verify_buf);
263                 if (ret) {
264                         printk("Read failed 0x%x, %d\n", (u32)ofs, ret);
265                         goto next;
266                 }
267
268                 if (memcmp(buf, verify_buf, blocksize))
269                         printk("\nRead/Write test failed at 0x%x\n", (u32)ofs);
270
271 next:
272                 ofs += blocksize;
273                 blocks++;
274         }
275         printf("...Done\n");
276
277         free(buf);
278         free(verify_buf);
279
280         return 0;
281 }
282
283 static int onenand_dump(struct mtd_info *mtd, ulong off, int only_oob)
284 {
285         int i;
286         u_char *datbuf, *oobbuf, *p;
287         struct mtd_oob_ops ops;
288         loff_t addr;
289
290         datbuf = malloc(mtd->writesize + mtd->oobsize);
291         oobbuf = malloc(mtd->oobsize);
292         if (!datbuf || !oobbuf) {
293                 puts("No memory for page buffer\n");
294                 return 1;
295         }
296         off &= ~(mtd->writesize - 1);
297         addr = (loff_t) off;
298         memset(&ops, 0, sizeof(ops));
299         ops.datbuf = datbuf;
300         ops.oobbuf = oobbuf; /* must exist, but oob data will be appended to ops.datbuf */
301         ops.len = mtd->writesize;
302         ops.ooblen = mtd->oobsize;
303         ops.retlen = 0;
304         i = mtd->read_oob(mtd, addr, &ops);
305         if (i < 0) {
306                 printf("Error (%d) reading page %08lx\n", i, off);
307                 free(datbuf);
308                 free(oobbuf);
309                 return 1;
310         }
311         printf("Page %08lx dump:\n", off);
312         i = mtd->writesize >> 4;
313         p = datbuf;
314
315         while (i--) {
316                 if (!only_oob)
317                         printf("\t%02x %02x %02x %02x %02x %02x %02x %02x"
318                                "  %02x %02x %02x %02x %02x %02x %02x %02x\n",
319                                p[0], p[1], p[2], p[3], p[4], p[5], p[6], p[7],
320                                p[8], p[9], p[10], p[11], p[12], p[13], p[14],
321                                p[15]);
322                 p += 16;
323         }
324         puts("OOB:\n");
325         i = mtd->oobsize >> 3;
326         while (i--) {
327                 printf("\t%02x %02x %02x %02x %02x %02x %02x %02x\n",
328                        p[0], p[1], p[2], p[3], p[4], p[5], p[6], p[7]);
329                 p += 8;
330         }
331         free(datbuf);
332         free(oobbuf);
333
334         return 0;
335 }
336
337 int do_onenand(cmd_tbl_t * cmdtp, int flag, int argc, char *argv[])
338 {
339         struct onenand_chip *this;
340         int blocksize;
341         ulong addr, ofs;
342         size_t len, retlen = 0;
343         int ret = 0;
344         char *cmd, *s;
345
346         mtd = &onenand_mtd;
347         this = mtd->priv;
348         blocksize = (1 << this->erase_shift);
349
350         cmd = argv[1];
351
352         switch (argc) {
353         case 0:
354         case 1:
355                 goto usage;
356
357         case 2:
358                 if (strcmp(cmd, "info") == 0) {
359                         printf("%s\n", mtd->name);
360                         return 0;
361                 }
362
363                 if (strcmp(cmd, "bad") == 0) {
364                         /* Currently only one OneNAND device is supported */
365                         printf("\nDevice %d bad blocks:\n", 0);
366                         for (ofs = 0; ofs < mtd->size; ofs += mtd->erasesize) {
367                                 if (mtd->block_isbad(mtd, ofs))
368                                         printf("  %08x\n", (u32)ofs);
369                         }
370
371                         return 0;
372                 }
373
374         default:
375                 /* At least 4 args */
376
377                 /*
378                  * Syntax is:
379                  *   0       1     2       3    4
380                  *   onenand erase [force] [off size]
381                  */
382                 if ((strcmp(cmd, "erase") == 0) || (strcmp(cmd, "test") == 0)) {
383                         int force = argc > 2 && !strcmp("force", argv[2]);
384                         int o = force ? 3 : 2;
385                         int erase;
386
387                         erase = strcmp(cmd, "erase") == 0; /* 1 = erase, 0 = test */
388                         printf("\nOneNAND %s: ", erase ? "erase" : "test");
389
390                         /* skip first two or three arguments, look for offset and size */
391                         if (arg_off_size(argc - o, argv + o, &ofs, &len) != 0)
392                                 return 1;
393
394                         if (erase)
395                                 ret = onenand_block_erase(ofs, len, force);
396                         else
397                                 ret = onenand_block_test(ofs, len);
398
399                         printf("%s\n", ret ? "ERROR" : "OK");
400
401                         return ret == 0 ? 0 : 1;
402                 }
403
404                 if (strncmp(cmd, "read", 4) == 0 || strncmp(cmd, "write", 5) == 0) {
405                         int read;
406                         int oob = 0;
407
408                         if (argc < 4)
409                                 goto usage;
410
411                         addr = (ulong)simple_strtoul(argv[2], NULL, 16);
412
413                         read = strncmp(cmd, "read", 4) == 0; /* 1 = read, 0 = write */
414                         printf("\nOneNAND %s: ", read ? "read" : "write");
415                         if (arg_off_size(argc - 3, argv + 3, &ofs, &len) != 0)
416                                 return 1;
417
418                         s = strchr(cmd, '.');
419                         if ((s != NULL) && (!strcmp(s, ".oob")))
420                                 oob = 1;
421
422                         if (read) {
423                                 ret = onenand_block_read(ofs, len, &retlen,
424                                                          (u8 *)addr, oob);
425                         } else {
426                                 ret = onenand_block_write(ofs, len, &retlen,
427                                                           (u8 *)addr);
428                         }
429
430                         printf(" %d bytes %s: %s\n", retlen,
431                                read ? "read" : "written", ret ? "ERROR" : "OK");
432
433                         return ret == 0 ? 0 : 1;
434                 }
435
436                 if (strcmp(cmd, "markbad") == 0) {
437                         argc -= 2;
438                         argv += 2;
439
440                         if (argc <= 0)
441                                 goto usage;
442
443                         while (argc > 0) {
444                                 addr = simple_strtoul(*argv, NULL, 16);
445
446                                 if (mtd->block_markbad(mtd, addr)) {
447                                         printf("block 0x%08lx NOT marked "
448                                                 "as bad! ERROR %d\n",
449                                                 addr, ret);
450                                         ret = 1;
451                                 } else {
452                                         printf("block 0x%08lx successfully "
453                                                 "marked as bad\n",
454                                                 addr);
455                                 }
456                                 --argc;
457                                 ++argv;
458                         }
459                         return ret;
460                 }
461
462                 if (strncmp(cmd, "dump", 4) == 0) {
463                         if (argc < 3)
464                                 goto usage;
465
466                         s = strchr(cmd, '.');
467                         ofs = (int)simple_strtoul(argv[2], NULL, 16);
468
469                         if (s != NULL && strcmp(s, ".oob") == 0)
470                                 ret = onenand_dump(mtd, ofs, 1);
471                         else
472                                 ret = onenand_dump(mtd, ofs, 0);
473
474                         return ret == 0 ? 1 : 0;
475                 }
476
477                 break;
478         }
479
480         return 0;
481
482 usage:
483         cmd_usage(cmdtp);
484         return 1;
485 }
486
487 U_BOOT_CMD(
488         onenand,        CONFIG_SYS_MAXARGS,     1,      do_onenand,
489         "OneNAND sub-system",
490         "info - show available OneNAND devices\n"
491         "onenand bad - show bad blocks\n"
492         "onenand read[.oob] addr off size\n"
493         "onenand write[.oob] addr off size\n"
494         "    read/write 'size' bytes starting at offset 'off'\n"
495         "    to/from memory address 'addr', skipping bad blocks.\n"
496         "onenand erase [force] [off size] - erase 'size' bytes from\n"
497         "onenand test [off size] - test 'size' bytes from\n"
498         "    offset 'off' (entire device if not specified)\n"
499         "onenand dump[.oob] off - dump page\n"
500         "onenand markbad off [...] - mark bad block(s) at offset (UNSAFE)"
501 );