
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@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@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_ */