diff mbox

[U-Boot,07/13] fastboot: Implement NAND backend

Message ID 1441032373-16992-8-git-send-email-maxime.ripard@free-electrons.com
State Superseded
Delegated to: Tom Rini
Headers show

Commit Message

Maxime Ripard Aug. 31, 2015, 2:46 p.m. UTC
So far the fastboot code was only supporting MMC-backed devices for its
flashing operations (flash and erase).

Add a storage backend for NAND-backed devices.

Signed-off-by: Maxime Ripard <maxime.ripard@free-electrons.com>
---
 common/Makefile                 |   7 +-
 common/fb_nand.c                | 198 ++++++++++++++++++++++++++++++++++++++++
 drivers/usb/gadget/f_fastboot.c |  12 ++-
 include/fb_nand.h               |  10 ++
 4 files changed, 225 insertions(+), 2 deletions(-)
 create mode 100644 common/fb_nand.c
 create mode 100644 include/fb_nand.h

Comments

Tom Rini Sept. 4, 2015, 5:20 p.m. UTC | #1
On Mon, Aug 31, 2015 at 04:46:07PM +0200, Maxime Ripard wrote:

> So far the fastboot code was only supporting MMC-backed devices for its
> flashing operations (flash and erase).
> 
> Add a storage backend for NAND-backed devices.
> 
> Signed-off-by: Maxime Ripard <maxime.ripard@free-electrons.com>

In general:

Reviewed-by: Tom Rini <trini@konsulko.com>

But would it be possible to do this similar to how DFU is where we can
have NAND and MMC and whatever else built-in and then use the right one
as instructed by the user?  ie change the command from 'fastboot
<USB_controller>' to 'fastboot <USB_controller> [interface]' (or maybe
have to make interface required, not sure).  Thanks!
Maxime Ripard Sept. 6, 2015, 3:57 p.m. UTC | #2
On Fri, Sep 04, 2015 at 01:20:49PM -0400, Tom Rini wrote:
> On Mon, Aug 31, 2015 at 04:46:07PM +0200, Maxime Ripard wrote:
> 
> > So far the fastboot code was only supporting MMC-backed devices for its
> > flashing operations (flash and erase).
> > 
> > Add a storage backend for NAND-backed devices.
> > 
> > Signed-off-by: Maxime Ripard <maxime.ripard@free-electrons.com>
> 
> In general:
> 
> Reviewed-by: Tom Rini <trini@konsulko.com>
> 
> But would it be possible to do this similar to how DFU is where we can
> have NAND and MMC and whatever else built-in and then use the right one
> as instructed by the user?  ie change the command from 'fastboot
> <USB_controller>' to 'fastboot <USB_controller> [interface]' (or maybe
> have to make interface required, not sure).  Thanks!

As in something like "fastboot 0 mmc 0" ?

Sounds good, however, since fastboot only refers to partitions by
name, we could simply allow mix and matching them. What do you think?

Maxime
Tom Rini Sept. 6, 2015, 7:41 p.m. UTC | #3
On Sun, Sep 06, 2015 at 05:57:25PM +0200, Maxime Ripard wrote:
> On Fri, Sep 04, 2015 at 01:20:49PM -0400, Tom Rini wrote:
> > On Mon, Aug 31, 2015 at 04:46:07PM +0200, Maxime Ripard wrote:
> > 
> > > So far the fastboot code was only supporting MMC-backed devices for its
> > > flashing operations (flash and erase).
> > > 
> > > Add a storage backend for NAND-backed devices.
> > > 
> > > Signed-off-by: Maxime Ripard <maxime.ripard@free-electrons.com>
> > 
> > In general:
> > 
> > Reviewed-by: Tom Rini <trini@konsulko.com>
> > 
> > But would it be possible to do this similar to how DFU is where we can
> > have NAND and MMC and whatever else built-in and then use the right one
> > as instructed by the user?  ie change the command from 'fastboot
> > <USB_controller>' to 'fastboot <USB_controller> [interface]' (or maybe
> > have to make interface required, not sure).  Thanks!
> 
> As in something like "fastboot 0 mmc 0" ?
> 
> Sounds good, however, since fastboot only refers to partitions by
> name, we could simply allow mix and matching them. What do you think?

Sure, sounds good, thanks!
Boris Brezillon Sept. 10, 2015, 7:41 a.m. UTC | #4
Hi Maxime,

On Mon, 31 Aug 2015 16:46:07 +0200
Maxime Ripard <maxime.ripard@free-electrons.com> wrote:

> So far the fastboot code was only supporting MMC-backed devices for its
> flashing operations (flash and erase).
> 
> Add a storage backend for NAND-backed devices.
> 
> Signed-off-by: Maxime Ripard <maxime.ripard@free-electrons.com>
> ---

[...]

> +
> +static int _fb_nand_write(nand_info_t *nand, struct part_info *part,
> +			  void *buffer, unsigned int offset,
> +			  unsigned int length)
> +{
> +	int flags = WITH_WR_VERIFY;
> +	int ret;
> +
> +#ifdef CONFIG_FASTBOOT_FLASH_NAND_TRIMFFS
> +	flags |= WITH_DROP_FFS;
> +#endif
> +	ret = nand_write_skip_bad(nand, offset, &length, NULL,
> +				  part->size - (offset - part->offset),
> +				  buffer, flags);

Hm, you seem to ignore skipped blocks here (the 'actual' parameter is
NULL), which means you won't adapt the offset accordingly and will
likely override some data if you appear to have bad blocks in the
section you're writing with the sparse method.

> +
> +	if (ret)
> +		return ret;
> +
> +	return ret;
> +}
> +
> +static unsigned int fb_nand_sparse_write(struct sparse_storage *storage,
> +					 void *priv,
> +					 unsigned int offset,

Maybe you should pass a pointer here, so that you can properly update it
with the number of skipped blocks (the same goes for the _fb_nand_write
function).

Best Regards,

Boris
diff mbox

Patch

diff --git a/common/Makefile b/common/Makefile
index dc82433e90de..20e8027a8913 100644
--- a/common/Makefile
+++ b/common/Makefile
@@ -271,10 +271,15 @@  obj-y += memsize.o
 obj-y += stdio.o
 
 # This option is not just y/n - it can have a numeric value
-ifdef CONFIG_FASTBOOT_FLASH_MMC_DEV
+ifdef CONFIG_FASTBOOT_FLASH
 obj-y += aboot.o
+ifdef CONFIG_FASTBOOT_FLASH_MMC_DEV
 obj-y += fb_mmc.o
 endif
+ifdef CONFIG_FASTBOOT_FLASH_NAND_DEV
+obj-y += fb_nand.o
+endif
+endif
 
 obj-$(CONFIG_CMD_BLOB) += cmd_blob.o
 
diff --git a/common/fb_nand.c b/common/fb_nand.c
new file mode 100644
index 000000000000..0e1d0603dbcf
--- /dev/null
+++ b/common/fb_nand.c
@@ -0,0 +1,198 @@ 
+/*
+ * Copyright 2014 Broadcom Corporation.
+ * Copyright 2015 Free Electrons.
+ *
+ * SPDX-License-Identifier:	GPL-2.0+
+ */
+
+#include <config.h>
+#include <common.h>
+
+#include <aboot.h>
+#include <fastboot.h>
+#include <sparse_format.h>
+
+#include <linux/mtd/mtd.h>
+#include <jffs2/jffs2.h>
+#include <nand.h>
+
+static char *response_str;
+
+struct fb_nand_sparse {
+	nand_info_t		*nand;
+	struct part_info	*part;
+};
+
+static int fb_nand_lookup(const char *partname, char *response,
+			  nand_info_t **nand,
+			  struct part_info **part)
+{
+	struct mtd_device *dev;
+	int ret;
+	u8 pnum;
+
+	ret = mtdparts_init();
+	if (ret) {
+		error("Cannot initialize MTD partitions\n");
+		fastboot_fail(response_str, "cannot init mtdparts");
+		return ret;
+	}
+
+	ret = find_dev_and_part(partname, &dev, &pnum, part);
+	if (ret) {
+		error("cannot find partition: '%s'", partname);
+		fastboot_fail(response_str, "cannot find partition");
+		return ret;
+	}
+
+	if (dev->id->type != MTD_DEV_TYPE_NAND) {
+		error("partition '%s' is not stored on a NAND device",
+		      partname);
+		fastboot_fail(response_str, "not a NAND device");
+		return -EINVAL;
+	}
+
+	*nand = &nand_info[dev->id->num];
+
+	return 0;
+}
+
+static int _fb_nand_erase(nand_info_t *nand, struct part_info *part)
+{
+	nand_erase_options_t opts;
+	int ret;
+
+	memset(&opts, 0, sizeof(opts));
+	opts.offset = part->offset;
+	opts.length = part->size;
+	opts.quiet = 1;
+
+	printf("Erasing blocks 0x%llx to 0x%llx\n",
+	       part->offset, part->offset + part->size);
+
+	ret = nand_erase_opts(nand, &opts);
+	if (ret)
+		return ret;
+
+	printf("........ erased 0x%llx bytes from '%s'\n",
+	       part->size, part->name);
+
+	return 0;
+}
+
+static int _fb_nand_write(nand_info_t *nand, struct part_info *part,
+			  void *buffer, unsigned int offset,
+			  unsigned int length)
+{
+	int flags = WITH_WR_VERIFY;
+	int ret;
+
+#ifdef CONFIG_FASTBOOT_FLASH_NAND_TRIMFFS
+	flags |= WITH_DROP_FFS;
+#endif
+	ret = nand_write_skip_bad(nand, offset, &length, NULL,
+				  part->size - (offset - part->offset),
+				  buffer, flags);
+
+	if (ret)
+		return ret;
+
+	return ret;
+}
+
+static unsigned int fb_nand_sparse_write(struct sparse_storage *storage,
+					 void *priv,
+					 unsigned int offset,
+					 unsigned int size,
+					 char *data)
+{
+	struct fb_nand_sparse *sparse = priv;
+
+	_fb_nand_write(sparse->nand, sparse->part, data,
+		       offset * storage->block_sz,
+		       size * storage->block_sz);
+
+	return size;
+}
+
+void fb_nand_flash_write(const char *partname, void *download_buffer,
+			 unsigned int download_bytes, char *response)
+{
+	struct part_info *part;
+	nand_info_t *nand = NULL;
+	int ret;
+
+	/* initialize the response buffer */
+	response_str = response;
+
+	ret = fb_nand_lookup(partname, response, &nand, &part);
+	if (ret) {
+		error("invalid NAND device");
+		fastboot_fail(response_str, "invalid NAND device");
+		return;
+	}
+
+
+	ret = _fb_nand_erase(nand, part);
+	if (ret) {
+		error("failed erasing from device %s", nand->name);
+		fastboot_fail(response_str, "failed erasing from device");
+		return;
+	}
+
+	if (is_sparse_image(download_buffer)) {
+		struct fb_nand_sparse sparse_priv;
+		sparse_storage_t sparse;
+
+		sparse_priv.nand = nand;
+		sparse_priv.part = part;
+
+		sparse.block_sz = nand->writesize;
+		sparse.start = part->offset / sparse.block_sz;
+		sparse.size = part->size  / sparse.block_sz;
+		sparse.name = part->name;
+		sparse.write = fb_nand_sparse_write;
+
+		printf("Flashing sparse image at offset 0x%llx\n",
+		       part->offset);
+
+		store_sparse_image(&sparse, &sparse_priv, download_buffer);
+	} else {
+		printf("Flashing raw image at offset 0x%llx\n",
+		       part->offset);
+
+		_fb_nand_write(nand, part, download_buffer, part->offset,
+			       download_bytes);
+
+		printf("........ wrote %u bytes to '%s'\n",
+		       download_bytes, part->name);
+	}
+
+	fastboot_okay("");
+}
+
+void fb_nand_erase(const char *partname, char *response)
+{
+	struct part_info *part;
+	nand_info_t *nand = NULL;
+	int ret;
+
+	/* initialize the response buffer */
+	response_str = response;
+
+	ret = fb_nand_lookup(partname, response, &nand, &part);
+	if (ret) {
+		error("invalid NAND device");
+		fastboot_fail(response_str, "invalid NAND device");
+		return;
+	}
+
+	ret = _fb_nand_erase(nand, part);
+	if (ret) {
+		error("failed erasing from device %s", nand->name);
+		fastboot_fail(response_str, "failed erasing from device");
+		return;
+	}
+
+	fastboot_okay("");
+}
diff --git a/drivers/usb/gadget/f_fastboot.c b/drivers/usb/gadget/f_fastboot.c
index 5703decfd360..866d426732fa 100644
--- a/drivers/usb/gadget/f_fastboot.c
+++ b/drivers/usb/gadget/f_fastboot.c
@@ -24,6 +24,9 @@ 
 #ifdef CONFIG_FASTBOOT_FLASH_MMC_DEV
 #include <fb_mmc.h>
 #endif
+#ifdef CONFIG_FASTBOOT_FLASH_NAND_DEV
+#include <fb_nand.h>
+#endif
 
 #define FASTBOOT_VERSION		"0.4"
 
@@ -558,6 +561,10 @@  static void cb_flash(struct usb_ep *ep, struct usb_request *req)
 	fb_mmc_flash_write(cmd, (void *)CONFIG_FASTBOOT_BUF_ADDR,
 			   download_bytes, response);
 #endif
+#ifdef CONFIG_FASTBOOT_FLASH_NAND_DEV
+	fb_nand_flash_write(cmd, (void *)CONFIG_USB_FASTBOOT_BUF_ADDR,
+			    download_bytes, response);
+#endif
 	fastboot_tx_write_str(response);
 }
 #endif
@@ -565,7 +572,7 @@  static void cb_flash(struct usb_ep *ep, struct usb_request *req)
 static void cb_oem(struct usb_ep *ep, struct usb_request *req)
 {
 	char *cmd = req->buf;
-#ifdef CONFIG_FASTBOOT_FLASH
+#ifdef CONFIG_FASTBOOT_FLASH_MMC_DEV
 	if (strncmp("format", cmd + 4, 6) == 0) {
 		char cmdbuf[32];
                 sprintf(cmdbuf, "gpt write mmc %x $partitions",
@@ -602,6 +609,9 @@  static void cb_erase(struct usb_ep *ep, struct usb_request *req)
 #ifdef CONFIG_FASTBOOT_FLASH_MMC_DEV
 	fb_mmc_erase(cmd, response);
 #endif
+#ifdef CONFIG_FASTBOOT_FLASH_NAND_DEV
+	fb_nand_erase(cmd, response);
+#endif
 	fastboot_tx_write_str(response);
 }
 #endif
diff --git a/include/fb_nand.h b/include/fb_nand.h
new file mode 100644
index 000000000000..88bdf3690de9
--- /dev/null
+++ b/include/fb_nand.h
@@ -0,0 +1,10 @@ 
+/*
+ * Copyright 2014 Broadcom Corporation.
+ * Copyright 2015 Free Electrons.
+ *
+ * SPDX-License-Identifier:	GPL-2.0+
+ */
+
+void fb_nand_flash_write(const char *cmd, void *download_buffer,
+			 unsigned int download_bytes, char *response);
+void fb_nand_erase(const char *cmd, char *response);