]> git.ipfire.org Git - people/ms/u-boot.git/blob - common/cmd_onenand.c
Merge branch 'master' of git://git.denx.de/u-boot-nand-flash
[people/ms/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 );