/* * U-Boot command for OneNAND support * * Copyright (C) 2005-2007 Samsung Electronics * Kyungmin Park <kyungmin.park@samsung.com> * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2 as * published by the Free Software Foundation. */ #include <common.h> #include <command.h> #include <linux/mtd/compat.h> #include <linux/mtd/mtd.h> #include <linux/mtd/onenand.h> #include <asm/io.h> extern struct mtd_info onenand_mtd; extern struct onenand_chip onenand_chip; int do_onenand(cmd_tbl_t * cmdtp, int flag, int argc, char *argv[]) { int ret = 0; switch (argc) { case 0: case 1: printf("Usage:\n%s\n", cmdtp->usage); return 1; case 2: if (strncmp(argv[1], "open", 4) == 0) { onenand_init(); return 0; } printf("%s\n", onenand_mtd.name); return 0; default: /* At least 4 args */ if (strncmp(argv[1], "erase", 5) == 0) { struct erase_info instr = { .callback = NULL, }; ulong start, end; ulong block; char *endtail; if (strncmp(argv[2], "block", 5) == 0) { start = simple_strtoul(argv[3], NULL, 10); endtail = strchr(argv[3], '-'); end = simple_strtoul(endtail + 1, NULL, 10); } else { start = simple_strtoul(argv[2], NULL, 10); end = simple_strtoul(argv[3], NULL, 10); start >>= onenand_chip.erase_shift; end >>= onenand_chip.erase_shift; /* Don't include the end block */ end--; } if (!end || end < 0) end = start; printf("Erase block from %lu to %lu\n", start, end); for (block = start; block <= end; block++) { instr.addr = block << onenand_chip.erase_shift; instr.len = 1 << onenand_chip.erase_shift; ret = onenand_erase(&onenand_mtd, &instr); if (ret) { printf("erase failed %lu\n", block); break; } } return 0; } if (strncmp(argv[1], "read", 4) == 0) { ulong addr = simple_strtoul(argv[2], NULL, 16); ulong ofs = simple_strtoul(argv[3], NULL, 16); size_t len = simple_strtoul(argv[4], NULL, 16); int oob = strncmp(argv[1], "read.oob", 8) ? 0 : 1; struct mtd_oob_ops ops; ops.mode = MTD_OOB_PLACE; if (oob) { ops.len = 0; ops.datbuf = NULL; ops.ooblen = len; ops.oobbuf = (u_char *) addr; } else { ops.len = len; ops.datbuf = (u_char *) addr; ops.ooblen = 0; ops.oobbuf = NULL; } ops.retlen = ops.oobretlen = 0; onenand_mtd.read_oob(&onenand_mtd, ofs, &ops); printf("Done\n"); return 0; } if (strncmp(argv[1], "write", 5) == 0) { ulong addr = simple_strtoul(argv[2], NULL, 16); ulong ofs = simple_strtoul(argv[3], NULL, 16); size_t len = simple_strtoul(argv[4], NULL, 16); size_t retlen = 0; onenand_write(&onenand_mtd, ofs, len, &retlen, (u_char *) addr); printf("Done\n"); return 0; } if (strncmp(argv[1], "block", 5) == 0) { ulong addr = simple_strtoul(argv[2], NULL, 16); ulong block = simple_strtoul(argv[3], NULL, 10); ulong page = simple_strtoul(argv[4], NULL, 10); size_t len = simple_strtol(argv[5], NULL, 10); ulong ofs; int oob = strncmp(argv[1], "block.oob", 9) ? 0 : 1; struct mtd_oob_ops ops; ops.mode = MTD_OOB_PLACE; ofs = block << onenand_chip.erase_shift; if (page) ofs += page << onenand_chip.page_shift; if (!len) { if (oob) ops.ooblen = 64; else ops.len = 512; } if (oob) { ops.datbuf = NULL; ops.oobbuf = (u_char *) addr; } else { ops.datbuf = (u_char *) addr; ops.oobbuf = NULL; } ops.retlen = ops.oobretlen = 0; onenand_read_oob(&onenand_mtd, ofs, &ops); return 0; } break; } return 0; } U_BOOT_CMD( onenand, 6, 1, do_onenand, "onenand - OneNAND sub-system\n", "info - show available OneNAND devices\n" "onenand read[.oob] addr ofs len - read data at ofs with len to addr\n" "onenand write addr ofs len - write data at ofs with len from addr\n" "onenand erase saddr eaddr - erase block start addr to end addr\n" "onenand block[.oob] addr block [page] [len] - " "read data with (block [, page]) to addr" );