[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