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

Maxime Ripard
2015-08-31T14:46:07Z

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

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);
--
2.5.0