All of lore.kernel.org
 help / color / mirror / Atom feed
From: Patrick Delaunay <patrick.delaunay@st.com>
To: u-boot@lists.denx.de
Subject: [U-Boot] [RFC PATCH 06/14] dfu: add backend for MTD device
Date: Fri, 19 Jul 2019 14:57:18 +0200	[thread overview]
Message-ID: <1563541046-6432-7-git-send-email-patrick.delaunay@st.com> (raw)
In-Reply-To: <1563541046-6432-1-git-send-email-patrick.delaunay@st.com>

Add DFU backend for MTD device: allow to read
and write on any MTD device (RAW or SPI)

For example :
> set dfu_alt_info "nand_raw raw 0x0 0x100000"
> dfu 0 mtd nand0

This MTD backend provides the same level than dfu nand
backend for NAND in RAW mode and sf backend for NOR;
So it can replace booth of them but it can also
add support of spi-nand.

> set dfu_alt_info "nand_raw raw 0x0 0x100000"
> dfu 0 mtd spi-nand0

The backend code is based on the MTD command
introduced by commit 5db66b3aee6f ("cmd: mtd:
add 'mtd' command")

Signed-off-by: Patrick Delaunay <patrick.delaunay@st.com>
---

 drivers/dfu/Kconfig   |   6 ++
 drivers/dfu/Makefile  |   1 +
 drivers/dfu/dfu.c     |   5 +-
 drivers/dfu/dfu_mtd.c | 230 ++++++++++++++++++++++++++++++++++++++++++++++++++
 include/dfu.h         |  21 +++++
 5 files changed, 262 insertions(+), 1 deletion(-)
 create mode 100644 drivers/dfu/dfu_mtd.c

diff --git a/drivers/dfu/Kconfig b/drivers/dfu/Kconfig
index 4692736..ee664a3 100644
--- a/drivers/dfu/Kconfig
+++ b/drivers/dfu/Kconfig
@@ -46,5 +46,11 @@ config DFU_SF
 	  This option enables using DFU to read and write to SPI flash based
 	  storage.
 
+config DFU_MTD
+	bool "MTD back end for DFU"
+	depends on MTD
+	help
+	  This option enables using DFU to read and write to on any MTD device.
+
 endif
 endmenu
diff --git a/drivers/dfu/Makefile b/drivers/dfu/Makefile
index 4164f34..ebb119f 100644
--- a/drivers/dfu/Makefile
+++ b/drivers/dfu/Makefile
@@ -5,6 +5,7 @@
 
 obj-$(CONFIG_$(SPL_)DFU) += dfu.o
 obj-$(CONFIG_$(SPL_)DFU_MMC) += dfu_mmc.o
+obj-$(CONFIG_$(SPL_)DFU_MTD) += dfu_mtd.o
 obj-$(CONFIG_$(SPL_)DFU_NAND) += dfu_nand.o
 obj-$(CONFIG_$(SPL_)DFU_RAM) += dfu_ram.o
 obj-$(CONFIG_$(SPL_)DFU_SF) += dfu_sf.o
diff --git a/drivers/dfu/dfu.c b/drivers/dfu/dfu.c
index bcfa170..ab7fdc0 100644
--- a/drivers/dfu/dfu.c
+++ b/drivers/dfu/dfu.c
@@ -461,6 +461,9 @@ static int dfu_fill_entity(struct dfu_entity *dfu, char *s, int alt,
 	if (strcmp(interface, "mmc") == 0) {
 		if (dfu_fill_entity_mmc(dfu, devstr, s))
 			return -1;
+	} else if (strcmp(interface, "mtd") == 0) {
+		if (dfu_fill_entity_mtd(dfu, devstr, s))
+			return -1;
 	} else if (strcmp(interface, "nand") == 0) {
 		if (dfu_fill_entity_nand(dfu, devstr, s))
 			return -1;
@@ -565,7 +568,7 @@ int dfu_config_entities(char *env, char *interface, char *devstr)
 const char *dfu_get_dev_type(enum dfu_device_type t)
 {
 	const char *const dev_t[] = {NULL, "eMMC", "OneNAND", "NAND", "RAM",
-				     "SF"};
+				     "SF", "MTD"};
 	return dev_t[t];
 }
 
diff --git a/drivers/dfu/dfu_mtd.c b/drivers/dfu/dfu_mtd.c
new file mode 100644
index 0000000..1168a6e
--- /dev/null
+++ b/drivers/dfu/dfu_mtd.c
@@ -0,0 +1,230 @@
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * dfu_mtd.c -- DFU for MTD device.
+ *
+ * Copyright (C) 2019,STMicroelectronics - All Rights Reserved
+ *
+ * Based on dfu_nand.c
+ */
+
+#include <common.h>
+#include <dfu.h>
+#include <mtd.h>
+
+static bool mtd_is_aligned_with_block_size(struct mtd_info *mtd, u64 size)
+{
+	return !do_div(size, mtd->erasesize);
+}
+
+static int mtd_block_op(enum dfu_op op, struct dfu_entity *dfu,
+			u64 offset, void *buf, long *len)
+{
+	u64 off, lim, remaining;
+	struct mtd_info *mtd = dfu->data.mtd.info;
+	struct mtd_oob_ops io_op = {};
+	int ret = 0;
+	bool has_pages = mtd->type == MTD_NANDFLASH ||
+			 mtd->type == MTD_MLCNANDFLASH;
+
+	/* if buf == NULL return total size of the area */
+	if (!buf) {
+		*len = dfu->data.mtd.size;
+		return 0;
+	}
+
+	off = dfu->data.mtd.start + offset + dfu->bad_skip;
+	lim = dfu->data.mtd.start + dfu->data.mtd.size;
+
+	if (off >= lim) {
+		printf("Limit reached 0x%llx\n", lim);
+		*len = 0;
+		return op == DFU_OP_READ ? 0 : -EIO;
+	}
+	/* limit request with the available size */
+	if (off + *len >= lim)
+		*len = lim - off;
+
+	if (!mtd_is_aligned_with_block_size(mtd, off)) {
+		printf("Offset not aligned with a block (0x%x)\n",
+		       mtd->erasesize);
+		return 0;
+	}
+
+	/* first erase */
+	if (op == DFU_OP_WRITE) {
+		struct erase_info erase_op = {};
+
+		erase_op.mtd = mtd;
+		erase_op.addr = off;
+		erase_op.len = round_up(*len, mtd->erasesize);
+		erase_op.scrub = 0;
+
+		while (erase_op.len) {
+			if (erase_op.addr + erase_op.len > lim) {
+				printf("Limit reached 0x%llx while erasing at offset 0x%llx\n",
+				       lim, off);
+				return -EIO;
+			}
+
+			ret = mtd_erase(mtd, &erase_op);
+			/* Abort if its not a bad block error */
+			if (ret != -EIO)
+				break;
+
+			printf("Skipping bad block at 0x%08llx\n",
+			       erase_op.fail_addr);
+
+			/* Continue erase behind bad block */
+			erase_op.len -= erase_op.fail_addr - erase_op.addr;
+			erase_op.addr = erase_op.fail_addr + mtd->erasesize;
+		}
+		if (ret && ret != -EIO) {
+			printf("Failure while erasing at offset 0x%llx\n",
+			       erase_op.fail_addr);
+			return 0;
+		}
+	}
+
+	io_op.mode = MTD_OPS_AUTO_OOB;
+	io_op.len = *len;
+	if (has_pages && io_op.len > mtd->writesize)
+		io_op.len = mtd->writesize;
+	io_op.ooblen = 0;
+	io_op.datbuf = buf;
+	io_op.oobbuf = NULL;
+
+	/* Loop over to do the actual read/write */
+	remaining = *len;
+	while (remaining) {
+		if (off + remaining > lim) {
+			printf("Limit reached 0x%llx while %s at offset 0x%llx\n",
+			       lim, op == DFU_OP_READ ? "reading" : "writing",
+			       off);
+			if (op == DFU_OP_READ) {
+				*len -= remaining;
+				return 0;
+			} else {
+				return -EIO;
+			}
+		}
+
+		/* Skip the block if it is bad */
+		if (mtd_is_aligned_with_block_size(mtd, off) &&
+		    mtd_block_isbad(mtd, off)) {
+			off += mtd->erasesize;
+			dfu->bad_skip += mtd->erasesize;
+			continue;
+		}
+
+		if (op == DFU_OP_READ)
+			ret = mtd_read_oob(mtd, off, &io_op);
+		else
+			ret = mtd_write_oob(mtd, off, &io_op);
+
+		if (ret) {
+			printf("Failure while %s at offset 0x%llx\n",
+			       op == DFU_OP_READ ? "reading" : "writing", off);
+			return -EIO;
+		}
+
+		off += io_op.retlen;
+		remaining -= io_op.retlen;
+		io_op.datbuf += io_op.retlen;
+		io_op.len = remaining;
+		if (has_pages && io_op.len > mtd->writesize)
+			io_op.len = mtd->writesize;
+	}
+
+	return ret;
+}
+
+static int dfu_get_medium_size_mtd(struct dfu_entity *dfu, u64 *size)
+{
+	*size = dfu->data.mtd.info->size;
+
+	return 0;
+}
+
+static int dfu_read_medium_mtd(struct dfu_entity *dfu, u64 offset, void *buf,
+			       long *len)
+{
+	int ret = -1;
+
+	switch (dfu->layout) {
+	case DFU_RAW_ADDR:
+		ret = mtd_block_op(DFU_OP_READ, dfu, offset, buf, len);
+		break;
+	default:
+		printf("%s: Layout (%s) not (yet) supported!\n", __func__,
+		       dfu_get_layout(dfu->layout));
+	}
+
+	return ret;
+}
+
+static int dfu_write_medium_mtd(struct dfu_entity *dfu,
+				u64 offset, void *buf, long *len)
+{
+	int ret = -1;
+
+	switch (dfu->layout) {
+	case DFU_RAW_ADDR:
+		ret = mtd_block_op(DFU_OP_WRITE, dfu, offset, buf, len);
+		break;
+	default:
+		printf("%s: Layout (%s) not (yet) supported!\n", __func__,
+		       dfu_get_layout(dfu->layout));
+	}
+
+	return ret;
+}
+
+static int dfu_flush_medium_mtd(struct dfu_entity *dfu)
+{
+	return 0;
+}
+
+static unsigned int dfu_polltimeout_mtd(struct dfu_entity *dfu)
+{
+	return DFU_DEFAULT_POLL_TIMEOUT;
+}
+
+int dfu_fill_entity_mtd(struct dfu_entity *dfu, char *devstr, char *s)
+{
+	char *st;
+	struct mtd_info *mtd;
+	bool has_pages;
+
+	mtd = get_mtd_device_nm(devstr);
+	if (IS_ERR_OR_NULL(mtd))
+		return -ENODEV;
+	put_mtd_device(mtd);
+
+	dfu->dev_type = DFU_DEV_MTD;
+	dfu->data.mtd.info = mtd;
+
+	has_pages = mtd->type == MTD_NANDFLASH || mtd->type == MTD_MLCNANDFLASH;
+	dfu->max_buf_size = has_pages ? mtd->erasesize : 0;
+
+	st = strsep(&s, " ");
+	if (!strcmp(st, "raw")) {
+		dfu->layout = DFU_RAW_ADDR;
+		dfu->data.mtd.start = simple_strtoul(s, &s, 16);
+		s++;
+		dfu->data.mtd.size = simple_strtoul(s, &s, 16);
+	} else {
+		printf("%s: (%s) not supported!\n", __func__, st);
+		return -1;
+	}
+
+	dfu->get_medium_size = dfu_get_medium_size_mtd;
+	dfu->read_medium = dfu_read_medium_mtd;
+	dfu->write_medium = dfu_write_medium_mtd;
+	dfu->flush_medium = dfu_flush_medium_mtd;
+	dfu->poll_timeout = dfu_polltimeout_mtd;
+
+	/* initial state */
+	dfu->inited = 0;
+
+	return 0;
+}
diff --git a/include/dfu.h b/include/dfu.h
index 7d60ffc..924952f 100644
--- a/include/dfu.h
+++ b/include/dfu.h
@@ -22,6 +22,7 @@ enum dfu_device_type {
 	DFU_DEV_NAND,
 	DFU_DEV_RAM,
 	DFU_DEV_SF,
+	DFU_DEV_MTD,
 };
 
 enum dfu_layout {
@@ -55,6 +56,14 @@ struct mmc_internal_data {
 	unsigned int part;
 };
 
+struct mtd_internal_data {
+	struct mtd_info *info;
+
+	/* RAW programming */
+	u64 start;
+	u64 size;
+};
+
 struct nand_internal_data {
 	/* RAW programming */
 	u64 start;
@@ -105,6 +114,7 @@ struct dfu_entity {
 
 	union {
 		struct mmc_internal_data mmc;
+		struct mtd_internal_data mtd;
 		struct nand_internal_data nand;
 		struct ram_internal_data ram;
 		struct sf_internal_data sf;
@@ -249,6 +259,17 @@ static inline int dfu_fill_entity_sf(struct dfu_entity *dfu, char *devstr,
 }
 #endif
 
+#if CONFIG_IS_ENABLED(DFU_MTD)
+int dfu_fill_entity_mtd(struct dfu_entity *dfu, char *devstr, char *s);
+#else
+static inline int dfu_fill_entity_mtd(struct dfu_entity *dfu, char *devstr,
+				      char *s)
+{
+	puts("MTD support not available!\n");
+	return -1;
+}
+#endif
+
 /**
  * dfu_tftp_write - Write TFTP data to DFU medium
  *
-- 
2.7.4

  parent reply	other threads:[~2019-07-19 12:57 UTC|newest]

Thread overview: 25+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2019-07-19 12:57 [U-Boot] [RFC PATCH 00/14] dfu: update dfu stack and use them for stm32mp1 Patrick Delaunay
2019-07-19 12:57 ` [U-Boot] [RFC PATCH 01/14] dfu: cosmetic: cleanup sf to avoid checkpatch error Patrick Delaunay
2019-07-22  7:51   ` Lukasz Majewski
2019-07-19 12:57 ` [U-Boot] [RFC PATCH 02/14] dfu: sf: add partition support for nor backend Patrick Delaunay
2019-07-22  7:54   ` Lukasz Majewski
2019-07-19 12:57 ` [U-Boot] [RFC PATCH 03/14] dfu: prepare the support of multiple interface Patrick Delaunay
2019-07-22  7:57   ` Lukasz Majewski
2019-07-19 12:57 ` [U-Boot] [RFC PATCH 04/14] dfu: allow to manage DFU on several devices Patrick Delaunay
2019-07-22  8:04   ` Lukasz Majewski
2019-07-19 12:57 ` [U-Boot] [RFC PATCH 05/14] dfu: allow read with 0 data for EOF indication Patrick Delaunay
2019-07-22  8:05   ` Lukasz Majewski
2019-07-19 12:57 ` Patrick Delaunay [this message]
2019-07-22  8:11   ` [U-Boot] [RFC PATCH 06/14] dfu: add backend for MTD device Lukasz Majewski
2019-07-19 12:57 ` [U-Boot] [RFC PATCH 07/14] dfu: add partition support for MTD backend Patrick Delaunay
2019-07-22  8:16   ` Lukasz Majewski
2019-07-19 12:57 ` [U-Boot] [RFC PATCH 08/14] dfu: add DFU virtual backend Patrick Delaunay
2019-07-22  8:20   ` Lukasz Majewski
2019-07-19 12:57 ` [U-Boot] [RFC PATCH 09/14] dfu: add callback for flush and initiated operation Patrick Delaunay
2019-07-22  8:22   ` Lukasz Majewski
2019-07-19 12:57 ` [U-Boot] [RFC PATCH 10/14] stm32mp1: activate DFU support and command MTD Patrick Delaunay
2019-07-19 12:57 ` [U-Boot] [RFC PATCH 11/14] stm32mp1: activate SET_DFU_ALT_INFO Patrick Delaunay
2019-07-19 12:57 ` [U-Boot] [RFC PATCH 12/14] stp32mp1: configs: activate CONFIG_MTD_SPI_NAND Patrick Delaunay
2019-07-19 12:57 ` [U-Boot] [RFC PATCH 13/14] stm32mp1: board: add spi nand support Patrick Delaunay
2019-07-19 12:57 ` [U-Boot] [RFC PATCH 14/14] stm32mp1: add support for virtual partition read Patrick Delaunay
2019-07-22  8:27 ` [U-Boot] [RFC PATCH 00/14] dfu: update dfu stack and use them for stm32mp1 Lukasz Majewski

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=1563541046-6432-7-git-send-email-patrick.delaunay@st.com \
    --to=patrick.delaunay@st.com \
    --cc=u-boot@lists.denx.de \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.