fastboot: Implement NAND backend
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>
diff --git a/common/Makefile b/common/Makefile
index d986cde..4c1b2f0 100644
--- a/common/Makefile
+++ b/common/Makefile
@@ -276,10 +276,15 @@
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 0000000..1c80ba9
--- /dev/null
+++ b/common/fb_nand.c
@@ -0,0 +1,195 @@
+/*
+ * 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, size_t *written)
+{
+ int flags = WITH_WR_VERIFY;
+
+#ifdef CONFIG_FASTBOOT_FLASH_NAND_TRIMFFS
+ flags |= WITH_DROP_FFS;
+#endif
+
+ return nand_write_skip_bad(nand, offset, &length, written,
+ part->size - (offset - part->offset),
+ buffer, flags);
+}
+
+static 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;
+ size_t written;
+ int ret;
+
+ ret = _fb_nand_write(sparse->nand, sparse->part, data,
+ offset * storage->block_sz,
+ size * storage->block_sz, &written);
+ if (ret < 0) {
+ printf("Failed to write sparse chunk\n");
+ return ret;
+ }
+
+ return written / storage->block_sz;
+}
+
+void fb_nand_flash_write(const char *partname, unsigned int session_id,
+ 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;
+ }
+
+ 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;
+
+ ret = store_sparse_image(&sparse, &sparse_priv, session_id,
+ download_buffer);
+ } else {
+ printf("Flashing raw image at offset 0x%llx\n",
+ part->offset);
+
+ ret = _fb_nand_write(nand, part, download_buffer, part->offset,
+ download_bytes, NULL);
+
+ printf("........ wrote %u bytes to '%s'\n",
+ download_bytes, part->name);
+ }
+
+ if (ret) {
+ fastboot_fail(response_str, "error writing the image");
+ return;
+ }
+
+ fastboot_okay(response_str, "");
+}
+
+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(response_str, "");
+}
diff --git a/drivers/usb/gadget/f_fastboot.c b/drivers/usb/gadget/f_fastboot.c
index a70463d..20b6c18 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"
@@ -569,6 +572,11 @@
(void *)CONFIG_FASTBOOT_BUF_ADDR,
download_bytes, response);
#endif
+#ifdef CONFIG_FASTBOOT_FLASH_NAND_DEV
+ fb_nand_flash_write(cmd, fastboot_flash_session_id,
+ (void *)CONFIG_FASTBOOT_BUF_ADDR,
+ download_bytes, response);
+#endif
fastboot_flash_session_id++;
fastboot_tx_write_str(response);
}
@@ -614,6 +622,9 @@
#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 0000000..80ddef5
--- /dev/null
+++ b/include/fb_nand.h
@@ -0,0 +1,11 @@
+/*
+ * Copyright 2014 Broadcom Corporation.
+ * Copyright 2015 Free Electrons.
+ *
+ * SPDX-License-Identifier: GPL-2.0+
+ */
+
+void fb_nand_flash_write(const char *cmd, unsigned int session_id,
+ void *download_buffer, unsigned int download_bytes,
+ char *response);
+void fb_nand_erase(const char *cmd, char *response);