]>
git.ipfire.org Git - thirdparty/u-boot.git/blob - cmd/onenand.c
2 * U-Boot command for OneNAND support
4 * Copyright (C) 2005-2008 Samsung Electronics
5 * Kyungmin Park <kyungmin.park@samsung.com>
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.
15 #include <linux/printk.h>
17 #include <linux/compat.h>
18 #include <linux/mtd/mtd.h>
19 #include <linux/mtd/onenand.h>
23 static struct mtd_info
*mtd
;
25 static loff_t next_ofs
;
26 static loff_t skip_ofs
;
28 static int arg_off_size_onenand(int argc
, char *const argv
[], ulong
*off
,
32 if (!(str2long(argv
[0], off
))) {
33 printf("'%s' is not a number\n", argv
[0]);
41 if (!(str2long(argv
[1], (ulong
*)size
))) {
42 printf("'%s' is not a number\n", argv
[1]);
46 *size
= mtd
->size
- *off
;
49 if ((*off
+ *size
) > mtd
->size
) {
50 printf("total chip size (0x%llx) exceeded!\n", mtd
->size
);
54 if (*size
== mtd
->size
)
57 printf("offset 0x%lx, size 0x%zx\n", *off
, *size
);
62 static int onenand_block_read(loff_t from
, size_t len
,
63 size_t *retlen
, u_char
*buf
, int oob
)
65 struct onenand_chip
*this = mtd
->priv
;
66 int blocks
= (int) len
>> this->erase_shift
;
67 int blocksize
= (1 << this->erase_shift
);
69 struct mtd_oob_ops ops
= {
75 ops
.ooblen
= blocksize
;
80 ret
= mtd_block_isbad(mtd
, ofs
);
82 printk("Bad blocks %d at 0x%x\n",
83 (u32
)(ofs
>> this->erase_shift
), (u32
)ofs
);
94 ret
= mtd_read_oob(mtd
, ofs
, &ops
);
96 printk("Read failed 0x%x, %d\n", (u32
)ofs
, ret
);
103 *retlen
+= ops
.retlen
;
109 static int onenand_write_oneblock_withoob(loff_t to
, const u_char
* buf
,
112 struct mtd_oob_ops ops
= {
113 .len
= mtd
->writesize
,
114 .ooblen
= mtd
->oobsize
,
115 .mode
= MTD_OPS_AUTO_OOB
,
118 for (page
= 0; page
< (mtd
->erasesize
/ mtd
->writesize
); page
++) {
119 ops
.datbuf
= (u_char
*)buf
;
120 buf
+= mtd
->writesize
;
121 ops
.oobbuf
= (u_char
*)buf
;
123 ret
= mtd_write_oob(mtd
, to
, &ops
);
126 to
+= mtd
->writesize
;
129 *retlen
= (ret
) ? 0 : mtd
->erasesize
;
133 static int onenand_block_write(loff_t to
, size_t len
,
134 size_t *retlen
, const u_char
* buf
, int withoob
)
136 struct onenand_chip
*this = mtd
->priv
;
137 int blocks
= len
>> this->erase_shift
;
138 int blocksize
= (1 << this->erase_shift
);
143 if ((to
& (mtd
->writesize
- 1)) != 0) {
144 printf("Attempt to write non block-aligned data\n");
149 if (to
== next_ofs
) {
159 ret
= mtd_block_isbad(mtd
, ofs
);
161 printk("Bad blocks %d at 0x%x\n",
162 (u32
)(ofs
>> this->erase_shift
), (u32
)ofs
);
163 skip_ofs
+= blocksize
;
168 ret
= mtd_write(mtd
, ofs
, blocksize
, &_retlen
, buf
);
170 ret
= onenand_write_oneblock_withoob(ofs
, buf
, &_retlen
);
172 printk("Write failed 0x%x, %d", (u32
)ofs
, ret
);
173 skip_ofs
+= blocksize
;
187 static int onenand_block_erase(u32 start
, u32 size
, int force
)
189 struct onenand_chip
*this = mtd
->priv
;
190 struct erase_info instr
= {};
193 int blocksize
= 1 << this->erase_shift
;
195 for (ofs
= start
; ofs
< (start
+ size
); ofs
+= blocksize
) {
196 ret
= mtd_block_isbad(mtd
, ofs
);
198 printf("Skip erase bad block %d at 0x%x\n",
199 (u32
)(ofs
>> this->erase_shift
), (u32
)ofs
);
204 instr
.len
= blocksize
;
207 ret
= mtd_erase(mtd
, &instr
);
209 printf("erase failed block %d at 0x%x\n",
210 (u32
)(ofs
>> this->erase_shift
), (u32
)ofs
);
218 static int onenand_block_test(u32 start
, u32 size
)
220 struct onenand_chip
*this = mtd
->priv
;
221 struct erase_info instr
= {};
225 int blocksize
= 1 << this->erase_shift
;
226 int start_block
, end_block
;
232 buf
= malloc(blocksize
);
234 printf("Not enough malloc space available!\n");
238 verify_buf
= malloc(blocksize
);
240 printf("Not enough malloc space available!\n");
244 start_block
= start
>> this->erase_shift
;
245 end_block
= (start
+ size
) >> this->erase_shift
;
247 /* Protect boot-loader from badblock testing */
251 if (end_block
> (mtd
->size
>> this->erase_shift
))
252 end_block
= mtd
->size
>> this->erase_shift
;
254 blocks
= start_block
;
256 while (blocks
< end_block
) {
257 printf("\rTesting block %d at 0x%x", (u32
)(ofs
>> this->erase_shift
), (u32
)ofs
);
259 ret
= mtd_block_isbad(mtd
, ofs
);
261 printf("Skip erase bad block %d at 0x%x\n",
262 (u32
)(ofs
>> this->erase_shift
), (u32
)ofs
);
267 instr
.len
= blocksize
;
268 ret
= mtd_erase(mtd
, &instr
);
270 printk("Erase failed 0x%x, %d\n", (u32
)ofs
, ret
);
274 ret
= mtd_write(mtd
, ofs
, blocksize
, &retlen
, buf
);
276 printk("Write failed 0x%x, %d\n", (u32
)ofs
, ret
);
280 ret
= mtd_read(mtd
, ofs
, blocksize
, &retlen
, verify_buf
);
282 printk("Read failed 0x%x, %d\n", (u32
)ofs
, ret
);
286 if (memcmp(buf
, verify_buf
, blocksize
))
287 printk("\nRead/Write test failed at 0x%x\n", (u32
)ofs
);
301 static int onenand_dump(struct mtd_info
*mtd
, ulong off
, int only_oob
)
304 u_char
*datbuf
, *oobbuf
, *p
;
305 struct mtd_oob_ops ops
;
308 datbuf
= malloc(mtd
->writesize
+ mtd
->oobsize
);
309 oobbuf
= malloc(mtd
->oobsize
);
310 if (!datbuf
|| !oobbuf
) {
311 puts("No memory for page buffer\n");
314 off
&= ~(mtd
->writesize
- 1);
316 memset(&ops
, 0, sizeof(ops
));
319 ops
.len
= mtd
->writesize
;
320 ops
.ooblen
= mtd
->oobsize
;
322 i
= mtd_read_oob(mtd
, addr
, &ops
);
324 printf("Error (%d) reading page %08lx\n", i
, off
);
329 printf("Page %08lx dump:\n", off
);
330 i
= mtd
->writesize
>> 4;
335 printf("\t%02x %02x %02x %02x %02x %02x %02x %02x"
336 " %02x %02x %02x %02x %02x %02x %02x %02x\n",
337 p
[0], p
[1], p
[2], p
[3], p
[4], p
[5], p
[6], p
[7],
338 p
[8], p
[9], p
[10], p
[11], p
[12], p
[13], p
[14],
343 i
= mtd
->oobsize
>> 3;
347 printf("\t%02x %02x %02x %02x %02x %02x %02x %02x\n",
348 p
[0], p
[1], p
[2], p
[3], p
[4], p
[5], p
[6], p
[7]);
357 static int do_onenand_info(struct cmd_tbl
*cmdtp
, int flag
, int argc
,
360 printf("%s\n", mtd
->name
);
364 static int do_onenand_bad(struct cmd_tbl
*cmdtp
, int flag
, int argc
,
370 /* Currently only one OneNAND device is supported */
371 printf("\nDevice %d bad blocks:\n", 0);
372 for (ofs
= 0; ofs
< mtd
->size
; ofs
+= mtd
->erasesize
) {
373 if (mtd_block_isbad(mtd
, ofs
))
374 printf(" %08x\n", (u32
)ofs
);
380 static int do_onenand_read(struct cmd_tbl
*cmdtp
, int flag
, int argc
,
391 return CMD_RET_USAGE
;
393 s
= strchr(argv
[0], '.');
394 if ((s
!= NULL
) && (!strcmp(s
, ".oob")))
397 addr
= (ulong
)hextoul(argv
[1], NULL
);
399 printf("\nOneNAND read: ");
400 if (arg_off_size_onenand(argc
- 2, argv
+ 2, &ofs
, &len
) != 0)
403 ret
= onenand_block_read(ofs
, len
, &retlen
, (u8
*)addr
, oob
);
405 printf(" %zu bytes read: %s\n", retlen
, ret
? "ERROR" : "OK");
407 return ret
== 0 ? 0 : 1;
410 static int do_onenand_write(struct cmd_tbl
*cmdtp
, int flag
, int argc
,
415 int ret
= 0, withoob
= 0;
419 return CMD_RET_USAGE
;
421 if (strncmp(argv
[0] + 6, "yaffs", 5) == 0)
424 addr
= (ulong
)hextoul(argv
[1], NULL
);
426 printf("\nOneNAND write: ");
427 if (arg_off_size_onenand(argc
- 2, argv
+ 2, &ofs
, &len
) != 0)
430 ret
= onenand_block_write(ofs
, len
, &retlen
, (u8
*)addr
, withoob
);
432 printf(" %zu bytes written: %s\n", retlen
, ret
? "ERROR" : "OK");
434 return ret
== 0 ? 0 : 1;
437 static int do_onenand_erase(struct cmd_tbl
*cmdtp
, int flag
, int argc
,
448 * onenand erase [force] [off size]
454 if (!strcmp("force", argv
[0]))
461 printf("\nOneNAND erase: ");
463 /* skip first two or three arguments, look for offset and size */
464 if (arg_off_size_onenand(argc
, argv
, &ofs
, &len
) != 0)
467 ret
= onenand_block_erase(ofs
, len
, force
);
469 printf("%s\n", ret
? "ERROR" : "OK");
471 return ret
== 0 ? 0 : 1;
474 static int do_onenand_test(struct cmd_tbl
*cmdtp
, int flag
, int argc
,
484 * onenand test [force] [off size]
487 printf("\nOneNAND test: ");
489 /* skip first two or three arguments, look for offset and size */
490 if (arg_off_size_onenand(argc
- 1, argv
+ 1, &ofs
, &len
) != 0)
493 ret
= onenand_block_test(ofs
, len
);
495 printf("%s\n", ret
? "ERROR" : "OK");
497 return ret
== 0 ? 0 : 1;
500 static int do_onenand_dump(struct cmd_tbl
*cmdtp
, int flag
, int argc
,
508 return CMD_RET_USAGE
;
510 s
= strchr(argv
[0], '.');
511 ofs
= (int)hextoul(argv
[1], NULL
);
513 if (s
!= NULL
&& strcmp(s
, ".oob") == 0)
514 ret
= onenand_dump(mtd
, ofs
, 1);
516 ret
= onenand_dump(mtd
, ofs
, 0);
518 return ret
== 0 ? 1 : 0;
521 static int do_onenand_markbad(struct cmd_tbl
*cmdtp
, int flag
, int argc
,
531 return CMD_RET_USAGE
;
534 addr
= hextoul(*argv
, NULL
);
536 if (mtd_block_markbad(mtd
, addr
)) {
537 printf("block 0x%08lx NOT marked "
538 "as bad! ERROR %d\n",
542 printf("block 0x%08lx successfully "
552 static struct cmd_tbl cmd_onenand_sub
[] = {
553 U_BOOT_CMD_MKENT(info
, 1, 0, do_onenand_info
, "", ""),
554 U_BOOT_CMD_MKENT(bad
, 1, 0, do_onenand_bad
, "", ""),
555 U_BOOT_CMD_MKENT(read
, 4, 0, do_onenand_read
, "", ""),
556 U_BOOT_CMD_MKENT(write
, 4, 0, do_onenand_write
, "", ""),
557 U_BOOT_CMD_MKENT(write
.yaffs
, 4, 0, do_onenand_write
, "", ""),
558 U_BOOT_CMD_MKENT(erase
, 3, 0, do_onenand_erase
, "", ""),
559 U_BOOT_CMD_MKENT(test
, 3, 0, do_onenand_test
, "", ""),
560 U_BOOT_CMD_MKENT(dump
, 2, 0, do_onenand_dump
, "", ""),
561 U_BOOT_CMD_MKENT(markbad
, CONFIG_SYS_MAXARGS
, 0, do_onenand_markbad
, "", ""),
564 static int do_onenand(struct cmd_tbl
*cmdtp
, int flag
, int argc
,
570 return CMD_RET_USAGE
;
574 /* Strip off leading 'onenand' command argument */
578 c
= find_cmd_tbl(argv
[0], &cmd_onenand_sub
[0], ARRAY_SIZE(cmd_onenand_sub
));
581 return c
->cmd(cmdtp
, flag
, argc
, argv
);
583 return CMD_RET_USAGE
;
587 onenand
, CONFIG_SYS_MAXARGS
, 1, do_onenand
,
588 "OneNAND sub-system",
589 "info - show available OneNAND devices\n"
590 "onenand bad - show bad blocks\n"
591 "onenand read[.oob] addr off size\n"
592 "onenand write[.yaffs] addr off size\n"
593 " read/write 'size' bytes starting at offset 'off'\n"
594 " to/from memory address 'addr', skipping bad blocks.\n"
595 "onenand erase [force] [off size] - erase 'size' bytes from\n"
596 "onenand test [off size] - test 'size' bytes from\n"
597 " offset 'off' (entire device if not specified)\n"
598 "onenand dump[.oob] off - dump page\n"
599 "onenand markbad off [...] - mark bad block(s) at offset (UNSAFE)"