From patchwork Sun Oct 30 18:23:51 2016 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Jagan Teki X-Patchwork-Id: 689026 X-Patchwork-Delegate: jagannadh.teki@gmail.com Return-Path: X-Original-To: incoming@patchwork.ozlabs.org Delivered-To: patchwork-incoming@bilbo.ozlabs.org Received: from theia.denx.de (theia.denx.de [85.214.87.163]) by ozlabs.org (Postfix) with ESMTP id 3t6Qwg5wtgz9s8x for ; Mon, 31 Oct 2016 05:28:19 +1100 (AEDT) Received: from localhost (localhost [127.0.0.1]) by theia.denx.de (Postfix) with ESMTP id F2D60A75F8; Sun, 30 Oct 2016 19:26:55 +0100 (CET) Received: from theia.denx.de ([127.0.0.1]) by localhost (theia.denx.de [127.0.0.1]) (amavisd-new, port 10024) with ESMTP id ANwvQAYq0apu; Sun, 30 Oct 2016 19:26:55 +0100 (CET) Received: from theia.denx.de (localhost [127.0.0.1]) by theia.denx.de (Postfix) with ESMTP id B887EA75FD; Sun, 30 Oct 2016 19:26:32 +0100 (CET) Received: from localhost (localhost [127.0.0.1]) by theia.denx.de (Postfix) with ESMTP id BFA58B383B for ; Sun, 30 Oct 2016 19:26:22 +0100 (CET) Received: from theia.denx.de ([127.0.0.1]) by localhost (theia.denx.de [127.0.0.1]) (amavisd-new, port 10024) with ESMTP id FtUq4D1oGMBM for ; Sun, 30 Oct 2016 19:26:22 +0100 (CET) X-policyd-weight: NOT_IN_SBL_XBL_SPAMHAUS=-1.5 NOT_IN_SPAMCOP=-1.5 NOT_IN_BL_NJABL=-1.5 (only DNSBL check requested) Received: from mail-pf0-f196.google.com (mail-pf0-f196.google.com [209.85.192.196]) by theia.denx.de (Postfix) with ESMTPS id 7562CB381D for ; Sun, 30 Oct 2016 19:25:50 +0100 (CET) Received: by mail-pf0-f196.google.com with SMTP id a136so3260375pfa.0 for ; Sun, 30 Oct 2016 11:25:50 -0700 (PDT) X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20130820; h=x-gm-message-state:from:to:cc:subject:date:message-id:in-reply-to :references; bh=N5EOvJgVg045hxou2PgoGZgiOxFlR5zT1FPBi7uiXAo=; b=Cm40w+jBHTFMV4S9jtsjeqtw8f1GrwBxDvX8hUMc91WJjwm9gPcPUD7uBqalFAxG2r 0aZ6DdTW8eFLzJNYYWQd+dUS0whUrFQAFUR2TpMXTyIHMOr4zujecIyI7lpB0T+eHvDh +uPHDXWjYxYUyqdlxZ8fyp3lHXCfMfOc5mzoS/iPdTi5Z0mvYPVFEo+YapTmstKWc6i8 HbRLq9bVyo5vbhPA54zdAwtjLtY9GQcNgRB5stKB3GpnUqNUKgEMLPG21Gn3YAbA8QIv c96NFW/ONoWtY0zMV571D3XPTDhgbZDkOZ34l/DFsyyahVCIzAu0+XbgvyMH0w5v//Bq qGsQ== X-Gm-Message-State: ABUngvd6MrZ0qxUae60R5o4uB9PfmVUFMKwjAp6DTUjP5JsfmUEk/tKzKqAF7La3rbLsZQ== X-Received: by 10.98.28.79 with SMTP id c76mr2658764pfc.8.1477851948875; Sun, 30 Oct 2016 11:25:48 -0700 (PDT) Received: from Mr.J ([49.204.230.134]) by smtp.gmail.com with ESMTPSA id k7sm31357901pan.8.2016.10.30.11.25.45 (version=TLS1_2 cipher=ECDHE-RSA-AES128-SHA bits=128/128); Sun, 30 Oct 2016 11:25:47 -0700 (PDT) From: Jagan Teki To: u-boot@lists.denx.de Date: Sun, 30 Oct 2016 23:53:51 +0530 Message-Id: <1477851833-23960-20-git-send-email-jagan@openedev.com> X-Mailer: git-send-email 2.7.4 In-Reply-To: <1477851833-23960-1-git-send-email-jagan@openedev.com> References: <1477851833-23960-1-git-send-email-jagan@openedev.com> Cc: Tom Rini , Stefan Roese , Jagan Teki Subject: [U-Boot] [PATCH v9 19/21] cmd: Add mtd command support X-BeenThere: u-boot@lists.denx.de X-Mailman-Version: 2.1.15 Precedence: list List-Id: U-Boot discussion List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , MIME-Version: 1.0 Errors-To: u-boot-bounces@lists.denx.de Sender: "U-Boot" 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 Reviewed-by: Simon Glass --- 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 + * + * SPDX-License-Identifier: GPL-2.0+ + */ + +#include +#include +#include + +#include +#include + +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_ */