[U-Boot] [PATCH v9 19/21] cmd: Add mtd command support
Jagan Teki
jagan at openedev.com
Sun Oct 30 19:23:51 CET 2016
cmd/mtd.c is a generic command to access all low level
MTD devices, like SPI-NOR, Parallel NOR and NAND.
This is implemented based on u-boot driver model, so any
new driver added for using this command must follow dm principles.
Signed-off-by: Jagan Teki <jagan at openedev.com>
---
cmd/Kconfig | 6 +
cmd/Makefile | 1 +
cmd/mtd.c | 285 +++++++++++++++++++++++++++++++++++++++++++++++
drivers/mtd/Makefile | 2 +-
drivers/mtd/mtd-uclass.c | 17 +++
include/mtd.h | 9 ++
6 files changed, 319 insertions(+), 1 deletion(-)
create mode 100644 cmd/mtd.c
diff --git a/cmd/Kconfig b/cmd/Kconfig
index 86554ea..9386692 100644
--- a/cmd/Kconfig
+++ b/cmd/Kconfig
@@ -706,6 +706,12 @@ config CMD_FS_GENERIC
fs types.
endmenu
+config CMD_MTD
+ bool "Generic command for accessing MTD devices"
+ help
+ Command to support MTD devices accessing.
+ MTD devices like SPI-NOR, Parallel NOR and NAND.
+
config CMD_UBI
tristate "Enable UBI - Unsorted block images commands"
select CRC32
diff --git a/cmd/Makefile b/cmd/Makefile
index 81b98ee..d50a405 100644
--- a/cmd/Makefile
+++ b/cmd/Makefile
@@ -95,6 +95,7 @@ obj-$(CONFIG_CMD_MISC) += misc.o
obj-$(CONFIG_CMD_MMC) += mmc.o
obj-$(CONFIG_CMD_MMC_SPI) += mmc_spi.o
obj-$(CONFIG_MP) += mp.o
+obj-$(CONFIG_CMD_MTD) += mtd.o
obj-$(CONFIG_CMD_MTDPARTS) += mtdparts.o
obj-$(CONFIG_CMD_NAND) += nand.o
obj-$(CONFIG_CMD_NET) += net.o
diff --git a/cmd/mtd.c b/cmd/mtd.c
new file mode 100644
index 0000000..0dc529d
--- /dev/null
+++ b/cmd/mtd.c
@@ -0,0 +1,285 @@
+/*
+ * Command for accessing MTD device.
+ *
+ * Copyright (C) 2016 Jagan Teki <jagan at openedev.com>
+ *
+ * SPDX-License-Identifier: GPL-2.0+
+ */
+
+#include <common.h>
+#include <dm.h>
+#include <mtd.h>
+
+#include <asm/io.h>
+#include <jffs2/jffs2.h>
+
+static struct udevice *mtd_cur_dev;
+
+static int cmd_mtd_set_devnum(unsigned int devnum)
+{
+ struct udevice *dev;
+ int ret;
+
+ ret = uclass_get_device_by_seq(UCLASS_MTD, devnum, &dev);
+ if (ret) {
+ debug("%s: No MTD device %d\n", __func__, devnum);
+ return ret;
+ }
+ mtd_cur_dev = dev;
+
+ return 0;
+}
+
+static int mtd_get_cur_dev(struct udevice **devp)
+{
+ if (!mtd_cur_dev) {
+ puts("No MTD device selected\n");
+ return -ENODEV;
+ }
+ *devp = mtd_cur_dev;
+
+ return 0;
+}
+
+static int do_mtd_write_read(int argc, char * const argv[])
+{
+ struct udevice *dev;
+ struct mtd_info *mtd;
+ loff_t offset, addr, len, maxsize;
+ u_char *buf;
+ char *endp;
+ int idx = 0;
+ int ret;
+
+ if (argc < 3)
+ return -1;
+
+ ret = mtd_get_cur_dev(&dev);
+ if (ret)
+ return CMD_RET_FAILURE;
+
+ addr = simple_strtoul(argv[1], &endp, 16);
+ if (*argv[1] == 0 || *endp != 0)
+ return -1;
+
+ mtd = mtd_get_info(dev);
+ if (mtd_arg_off_size(argc - 2, &argv[2], &idx, &offset, &len,
+ &maxsize, MTD_DEV_TYPE_NOR, mtd->size))
+ return -1;
+
+ buf = map_physmem(addr, len, MAP_WRBACK);
+ if (!buf) {
+ puts("failed to map physical memory\n");
+ return 1;
+ }
+
+ if (strcmp(argv[0], "write") == 0)
+ ret = dm_mtd_write(dev, offset, len, (size_t *)&len, buf);
+ else if (strcmp(argv[0], "read") == 0)
+ ret = dm_mtd_read(dev, offset, len, (size_t *)&len, buf);
+
+ printf("MTD: %zu bytes @ %#llx %s: ", (size_t)len, offset,
+ (strcmp(argv[0], "read") == 0) ? "Read" : "Written");
+ if (ret)
+ printf("ERROR %d\n", ret);
+ else
+ printf("OK\n");
+
+ unmap_physmem(buf, len);
+
+ return ret == 0 ? 0 : 1;
+}
+
+static int mtd_parse_len_arg(struct mtd_info *mtd, char *arg, loff_t *len)
+{
+ char *ep;
+ char round_up_len; /* indicates if the "+length" form used */
+ ulong len_arg;
+
+ round_up_len = 0;
+ if (*arg == '+') {
+ round_up_len = 1;
+ ++arg;
+ }
+
+ len_arg = simple_strtoul(arg, &ep, 16);
+ if (ep == arg || *ep != '\0')
+ return -1;
+
+ if (round_up_len && mtd->erasesize > 0)
+ *len = ROUND(len_arg, mtd->erasesize);
+ else
+ *len = len_arg;
+
+ return 1;
+}
+
+static int do_mtd_erase(int argc, char * const argv[])
+{
+ struct udevice *dev;
+ struct mtd_info *mtd;
+ struct erase_info instr;
+ loff_t addr, len, maxsize;
+ int idx = 0;
+ int ret;
+
+ if (argc < 3)
+ return -1;
+
+ ret = mtd_get_cur_dev(&dev);
+ if (ret)
+ return CMD_RET_FAILURE;
+
+ mtd = mtd_get_info(dev);
+ if (mtd_arg_off(argv[1], &idx, &addr, &len, &maxsize,
+ MTD_DEV_TYPE_NOR, mtd->size))
+ return -1;
+
+ ret = mtd_parse_len_arg(mtd, argv[2], &len);
+ if (ret != 1)
+ return -1;
+
+ instr.mtd = mtd;
+ instr.addr = addr;
+ instr.len = len;
+ instr.callback = 0;
+ ret = dm_mtd_erase(dev, &instr);
+ printf("MTD: %zu bytes @ %#llx Erased: %s\n", (size_t)len, addr,
+ ret ? "ERROR" : "OK");
+
+ return ret == 0 ? 0 : 1;
+}
+
+static int do_mtd_probe(int argc, char * const argv[])
+{
+ struct udevice *dev, *devp;
+ int devnum;
+ int ret;
+
+ devnum = simple_strtoul(argv[1], NULL, 10);
+
+ debug("Setting MTD device to %d\n", devnum);
+ ret = cmd_mtd_set_devnum(devnum);
+ if (ret) {
+ printf("failing to set MTD device %d\n", devnum);
+ return CMD_RET_FAILURE;
+ }
+
+ ret = mtd_get_cur_dev(&dev);
+ if (ret)
+ return CMD_RET_FAILURE;
+
+ ret = dm_mtd_probe(dev, &devp);
+ if (ret) {
+ printf("failed to probe MTD device %d\n", devnum);
+ return CMD_RET_FAILURE;
+ }
+
+ return 0;
+}
+
+static int do_mtd_info(void)
+{
+ struct udevice *dev;
+ struct mtd_info *mtd;
+ int ret;
+
+ ret = mtd_get_cur_dev(&dev);
+ if (ret)
+ return CMD_RET_FAILURE;
+
+ mtd = mtd_get_info(dev);
+ printf("MTD Device %d: %s\n", dev->req_seq, mtd->name);
+ printf(" Page size:\t%d B\n Erase size:\t", mtd->writebufsize);
+ print_size(mtd->erasesize, "\n Size:\t\t");
+ print_size(mtd->size, "");
+ printf("\n");
+
+ return 0;
+}
+
+static int do_mtd_list(void)
+{
+ struct udevice *dev;
+ struct uclass *uc;
+ int ret;
+
+ ret = uclass_get(UCLASS_MTD, &uc);
+ if (ret)
+ return CMD_RET_FAILURE;
+
+ uclass_foreach_dev(dev, uc) {
+ printf("MTD %d:\t%s", dev->req_seq, dev->name);
+ if (device_active(dev))
+ printf(" (active %d)", dev->seq);
+ printf("\n");
+ }
+
+ return 0;
+}
+
+static int do_mtd(cmd_tbl_t *cmdtp, int flag, int argc,
+ char * const argv[])
+{
+ const char *cmd;
+ int ret = 0;
+
+ cmd = argv[1];
+ if (strcmp(cmd, "list") == 0) {
+ if (argc > 2)
+ goto usage;
+
+ ret = do_mtd_list();
+ goto done;
+ }
+
+ if (strcmp(cmd, "info") == 0) {
+ if (argc > 2)
+ goto usage;
+
+ ret = do_mtd_info();
+ goto done;
+ }
+
+ if (argc < 3)
+ goto usage;
+
+ --argc;
+ ++argv;
+
+ if (strcmp(cmd, "probe") == 0) {
+ ret = do_mtd_probe(argc, argv);
+ goto done;
+ }
+
+ if (strcmp(cmd, "erase") == 0) {
+ ret = do_mtd_erase(argc, argv);
+ goto done;
+ }
+
+ if (strcmp(cmd, "write") == 0 || strcmp(cmd, "read") == 0) {
+ ret = do_mtd_write_read(argc, argv);
+ goto done;
+ }
+
+done:
+ if (ret != -1)
+ return ret;
+
+usage:
+ return CMD_RET_USAGE;
+}
+
+static char mtd_help_text[] =
+ "list - show list of MTD devices\n"
+ "mtd info - show current MTD device info\n"
+ "mtd probe devnum - probe the 'devnum' MTD device\n"
+ "mtd erase offset len - erase 'len' bytes from 'offset'\n"
+ "mtd write addr to len - write 'len' bytes to 'to' from 'addr'\n"
+ "mtd read addr from len - read 'len' bytes from 'from' to 'addr'";
+
+U_BOOT_CMD(
+ mtd, 5, 1, do_mtd,
+ "MTD Sub-system",
+ mtd_help_text
+);
diff --git a/drivers/mtd/Makefile b/drivers/mtd/Makefile
index bd680a7..6b54b79 100644
--- a/drivers/mtd/Makefile
+++ b/drivers/mtd/Makefile
@@ -8,7 +8,7 @@
ifneq (,$(findstring y,$(CONFIG_MTD_DEVICE)$(CONFIG_CMD_NAND)$(CONFIG_CMD_ONENAND)$(CONFIG_CMD_SF)))
obj-y += mtdcore.o mtd_uboot.o
endif
-obj-$(CONFIG_MTD) += mtd-uclass.o
+obj-$(CONFIG_MTD) += mtd-uclass.o mtd_uboot.o
obj-$(CONFIG_MTD_PARTITIONS) += mtdpart.o
obj-$(CONFIG_MTD_CONCAT) += mtdconcat.o
obj-$(CONFIG_ALTERA_QSPI) += altera_qspi.o
diff --git a/drivers/mtd/mtd-uclass.c b/drivers/mtd/mtd-uclass.c
index 8eb6e8f..3e63de4 100644
--- a/drivers/mtd/mtd-uclass.c
+++ b/drivers/mtd/mtd-uclass.c
@@ -83,6 +83,23 @@ int dm_add_mtd_device(struct udevice *dev)
return 0;
}
+int dm_mtd_probe(struct udevice *dev, struct udevice **devp)
+{
+ *devp = NULL;
+ int ret;
+
+ ret = device_probe(dev);
+ debug("%s: device_probe: ret=%d\n", __func__, ret);
+ if (ret)
+ goto err;
+
+ *devp = dev;
+ return 0;
+err:
+ device_unbind(dev);
+ return ret;
+}
+
/*
* Implement a MTD uclass which should include most flash drivers.
* The uclass private is pointed to mtd_info.
diff --git a/include/mtd.h b/include/mtd.h
index 93b5eaf..49b8272 100644
--- a/include/mtd.h
+++ b/include/mtd.h
@@ -74,4 +74,13 @@ int dm_mtd_erase(struct udevice *dev, struct erase_info *instr);
*/
int dm_add_mtd_device(struct udevice *dev);
+/**
+ * dm_mtd_probe() - Probe MTD device
+ *
+ * @dev: MTD device
+ * @devp: MTD device pointer
+ * @return 0 if OK, -ve on error
+ */
+int dm_mtd_probe(struct udevice *dev, struct udevice **devp);
+
#endif /* _MTD_H_ */
--
2.7.4
More information about the U-Boot
mailing list