ASR_BASE

Change-Id: Icf3719cc0afe3eeb3edc7fa80a2eb5199ca9dda1
diff --git a/marvell/uboot/drivers/dfu/Makefile b/marvell/uboot/drivers/dfu/Makefile
new file mode 100644
index 0000000..def628d
--- /dev/null
+++ b/marvell/uboot/drivers/dfu/Makefile
@@ -0,0 +1,11 @@
+#
+# Copyright (C) 2012 Samsung Electronics
+# Lukasz Majewski <l.majewski@samsung.com>
+#
+# SPDX-License-Identifier:	GPL-2.0+
+#
+
+obj-$(CONFIG_DFU_FUNCTION) += dfu.o
+obj-$(CONFIG_DFU_MMC) += dfu_mmc.o
+obj-$(CONFIG_DFU_NAND) += dfu_nand.o
+obj-$(CONFIG_DFU_RAM) += dfu_ram.o
diff --git a/marvell/uboot/drivers/dfu/dfu.c b/marvell/uboot/drivers/dfu/dfu.c
new file mode 100644
index 0000000..07011e9
--- /dev/null
+++ b/marvell/uboot/drivers/dfu/dfu.c
@@ -0,0 +1,460 @@
+/*
+ * dfu.c -- DFU back-end routines
+ *
+ * Copyright (C) 2012 Samsung Electronics
+ * author: Lukasz Majewski <l.majewski@samsung.com>
+ *
+ * SPDX-License-Identifier:	GPL-2.0+
+ */
+
+#include <common.h>
+#include <errno.h>
+#include <malloc.h>
+#include <mmc.h>
+#include <fat.h>
+#include <dfu.h>
+#include <linux/list.h>
+#include <linux/compiler.h>
+
+static bool dfu_reset_request;
+static LIST_HEAD(dfu_list);
+static int dfu_alt_num;
+
+bool dfu_reset(void)
+{
+	return dfu_reset_request;
+}
+
+void dfu_trigger_reset()
+{
+	dfu_reset_request = true;
+}
+
+static int dfu_find_alt_num(const char *s)
+{
+	int i = 0;
+
+	for (; *s; s++)
+		if (*s == ';')
+			i++;
+
+	return ++i;
+}
+
+int dfu_init_env_entities(char *interface, int dev)
+{
+	const char *str_env;
+	char *env_bkp;
+	int ret;
+
+	str_env = getenv("dfu_alt_info");
+	if (!str_env) {
+		error("\"dfu_alt_info\" env variable not defined!\n");
+		return -EINVAL;
+	}
+
+	env_bkp = strdup(str_env);
+	ret = dfu_config_entities(env_bkp, interface, dev);
+	if (ret) {
+		error("DFU entities configuration failed!\n");
+		return ret;
+	}
+
+	free(env_bkp);
+	return 0;
+}
+
+static unsigned char *dfu_buf;
+static unsigned long dfu_buf_size = CONFIG_SYS_DFU_DATA_BUF_SIZE;
+
+unsigned char *dfu_free_buf(void)
+{
+	free(dfu_buf);
+	dfu_buf = NULL;
+	return dfu_buf;
+}
+
+unsigned long dfu_get_buf_size(void)
+{
+	return dfu_buf_size;
+}
+
+unsigned char *dfu_get_buf(void)
+{
+	char *s;
+
+	if (dfu_buf != NULL)
+		return dfu_buf;
+
+	s = getenv("dfu_bufsiz");
+	dfu_buf_size = s ? (unsigned long)simple_strtol(s, NULL, 16) :
+			CONFIG_SYS_DFU_DATA_BUF_SIZE;
+
+	dfu_buf = memalign(CONFIG_SYS_CACHELINE_SIZE, dfu_buf_size);
+	if (dfu_buf == NULL)
+		printf("%s: Could not memalign 0x%lx bytes\n",
+		       __func__, dfu_buf_size);
+
+	return dfu_buf;
+}
+
+static int dfu_write_buffer_drain(struct dfu_entity *dfu)
+{
+	long w_size;
+	int ret;
+
+	/* flush size? */
+	w_size = dfu->i_buf - dfu->i_buf_start;
+	if (w_size == 0)
+		return 0;
+
+	/* update CRC32 */
+	dfu->crc = crc32(dfu->crc, dfu->i_buf_start, w_size);
+
+	ret = dfu->write_medium(dfu, dfu->offset, dfu->i_buf_start, &w_size);
+	if (ret)
+		debug("%s: Write error!\n", __func__);
+
+	/* point back */
+	dfu->i_buf = dfu->i_buf_start;
+
+	/* update offset */
+	dfu->offset += w_size;
+
+	puts("#");
+
+	return ret;
+}
+
+int dfu_write(struct dfu_entity *dfu, void *buf, int size, int blk_seq_num)
+{
+	int ret = 0;
+	int tret;
+
+	debug("%s: name: %s buf: 0x%p size: 0x%x p_num: 0x%x offset: 0x%llx bufoffset: 0x%x\n",
+	      __func__, dfu->name, buf, size, blk_seq_num, dfu->offset,
+	      dfu->i_buf - dfu->i_buf_start);
+
+	if (!dfu->inited) {
+		/* initial state */
+		dfu->crc = 0;
+		dfu->offset = 0;
+		dfu->bad_skip = 0;
+		dfu->i_blk_seq_num = 0;
+		dfu->i_buf_start = dfu_get_buf();
+		if (dfu->i_buf_start == NULL)
+			return -ENOMEM;
+		dfu->i_buf_end = dfu_get_buf() + dfu_buf_size;
+		dfu->i_buf = dfu->i_buf_start;
+
+		dfu->inited = 1;
+	}
+
+	if (dfu->i_blk_seq_num != blk_seq_num) {
+		printf("%s: Wrong sequence number! [%d] [%d]\n",
+		       __func__, dfu->i_blk_seq_num, blk_seq_num);
+		return -1;
+	}
+
+	/* DFU 1.1 standard says:
+	 * The wBlockNum field is a block sequence number. It increments each
+	 * time a block is transferred, wrapping to zero from 65,535. It is used
+	 * to provide useful context to the DFU loader in the device."
+	 *
+	 * This means that it's a 16 bit counter that roll-overs at
+	 * 0xffff -> 0x0000. By having a typical 4K transfer block
+	 * we roll-over at exactly 256MB. Not very fun to debug.
+	 *
+	 * Handling rollover, and having an inited variable,
+	 * makes things work.
+	 */
+
+	/* handle rollover */
+	dfu->i_blk_seq_num = (dfu->i_blk_seq_num + 1) & 0xffff;
+
+	/* flush buffer if overflow */
+	if ((dfu->i_buf + size) > dfu->i_buf_end) {
+		tret = dfu_write_buffer_drain(dfu);
+		if (ret == 0)
+			ret = tret;
+	}
+
+	/* we should be in buffer now (if not then size too large) */
+	if ((dfu->i_buf + size) > dfu->i_buf_end) {
+		error("Buffer overflow! (0x%p + 0x%x > 0x%p)\n", dfu->i_buf,
+		      size, dfu->i_buf_end);
+		return -1;
+	}
+
+	memcpy(dfu->i_buf, buf, size);
+	dfu->i_buf += size;
+
+	/* if end or if buffer full flush */
+	if (size == 0 || (dfu->i_buf + size) > dfu->i_buf_end) {
+		tret = dfu_write_buffer_drain(dfu);
+		if (ret == 0)
+			ret = tret;
+	}
+
+	/* end? */
+	if (size == 0) {
+		/* Now try and flush to the medium if needed. */
+		if (dfu->flush_medium)
+			ret = dfu->flush_medium(dfu);
+		printf("\nDFU complete CRC32: 0x%08x\n", dfu->crc);
+
+		/* clear everything */
+		dfu_free_buf();
+		dfu->crc = 0;
+		dfu->offset = 0;
+		dfu->i_blk_seq_num = 0;
+		dfu->i_buf_start = dfu_buf;
+		dfu->i_buf_end = dfu_buf;
+		dfu->i_buf = dfu->i_buf_start;
+
+		dfu->inited = 0;
+
+	}
+
+	return ret = 0 ? size : ret;
+}
+
+static int dfu_read_buffer_fill(struct dfu_entity *dfu, void *buf, int size)
+{
+	long chunk;
+	int ret, readn;
+
+	readn = 0;
+	while (size > 0) {
+		/* get chunk that can be read */
+		chunk = min(size, dfu->b_left);
+		/* consume */
+		if (chunk > 0) {
+			memcpy(buf, dfu->i_buf, chunk);
+			dfu->crc = crc32(dfu->crc, buf, chunk);
+			dfu->i_buf += chunk;
+			dfu->b_left -= chunk;
+			dfu->r_left -= chunk;
+			size -= chunk;
+			buf += chunk;
+			readn += chunk;
+		}
+
+		/* all done */
+		if (size > 0) {
+			/* no more to read */
+			if (dfu->r_left == 0)
+				break;
+
+			dfu->i_buf = dfu->i_buf_start;
+			dfu->b_left = dfu->i_buf_end - dfu->i_buf_start;
+
+			/* got to read, but buffer is empty */
+			if (dfu->b_left > dfu->r_left)
+				dfu->b_left = dfu->r_left;
+			ret = dfu->read_medium(dfu, dfu->offset, dfu->i_buf,
+					&dfu->b_left);
+			if (ret != 0) {
+				debug("%s: Read error!\n", __func__);
+				return ret;
+			}
+			dfu->offset += dfu->b_left;
+			dfu->r_left -= dfu->b_left;
+
+			puts("#");
+		}
+	}
+
+	return readn;
+}
+
+int dfu_read(struct dfu_entity *dfu, void *buf, int size, int blk_seq_num)
+{
+	int ret = 0;
+
+	debug("%s: name: %s buf: 0x%p size: 0x%x p_num: 0x%x i_buf: 0x%p\n",
+	       __func__, dfu->name, buf, size, blk_seq_num, dfu->i_buf);
+
+	if (!dfu->inited) {
+		dfu->i_buf_start = dfu_get_buf();
+		if (dfu->i_buf_start == NULL)
+			return -ENOMEM;
+
+		ret = dfu->read_medium(dfu, 0, dfu->i_buf_start, &dfu->r_left);
+		if (ret != 0) {
+			debug("%s: failed to get r_left\n", __func__);
+			return ret;
+		}
+
+		debug("%s: %s %ld [B]\n", __func__, dfu->name, dfu->r_left);
+
+		dfu->i_blk_seq_num = 0;
+		dfu->crc = 0;
+		dfu->offset = 0;
+		dfu->i_buf_end = dfu_get_buf() + dfu_buf_size;
+		dfu->i_buf = dfu->i_buf_start;
+		dfu->b_left = min(dfu_buf_size, dfu->r_left);
+
+		dfu->bad_skip = 0;
+
+		dfu->inited = 1;
+	}
+
+	if (dfu->i_blk_seq_num != blk_seq_num) {
+		printf("%s: Wrong sequence number! [%d] [%d]\n",
+		       __func__, dfu->i_blk_seq_num, blk_seq_num);
+		return -1;
+	}
+	/* handle rollover */
+	dfu->i_blk_seq_num = (dfu->i_blk_seq_num + 1) & 0xffff;
+
+	ret = dfu_read_buffer_fill(dfu, buf, size);
+	if (ret < 0) {
+		printf("%s: Failed to fill buffer\n", __func__);
+		return -1;
+	}
+
+	if (ret < size) {
+		debug("%s: %s CRC32: 0x%x\n", __func__, dfu->name, dfu->crc);
+		puts("\nUPLOAD ... done\nCtrl+C to exit ...\n");
+
+		dfu_free_buf();
+		dfu->i_blk_seq_num = 0;
+		dfu->crc = 0;
+		dfu->offset = 0;
+		dfu->i_buf_start = dfu_buf;
+		dfu->i_buf_end = dfu_buf;
+		dfu->i_buf = dfu->i_buf_start;
+		dfu->b_left = 0;
+
+		dfu->bad_skip = 0;
+
+		dfu->inited = 0;
+	}
+
+	return ret;
+}
+
+static int dfu_fill_entity(struct dfu_entity *dfu, char *s, int alt,
+			   char *interface, int num)
+{
+	char *st;
+
+	debug("%s: %s interface: %s num: %d\n", __func__, s, interface, num);
+	st = strsep(&s, " ");
+	strcpy(dfu->name, st);
+
+	dfu->dev_num = num;
+	dfu->alt = alt;
+
+	/* Specific for mmc device */
+	if (strcmp(interface, "mmc") == 0) {
+		if (dfu_fill_entity_mmc(dfu, s))
+			return -1;
+	} else if (strcmp(interface, "nand") == 0) {
+		if (dfu_fill_entity_nand(dfu, s))
+			return -1;
+	} else if (strcmp(interface, "ram") == 0) {
+		if (dfu_fill_entity_ram(dfu, s))
+			return -1;
+	} else {
+		printf("%s: Device %s not (yet) supported!\n",
+		       __func__,  interface);
+		return -1;
+	}
+
+	return 0;
+}
+
+void dfu_free_entities(void)
+{
+	struct dfu_entity *dfu, *p, *t = NULL;
+
+	list_for_each_entry_safe_reverse(dfu, p, &dfu_list, list) {
+		list_del(&dfu->list);
+		t = dfu;
+	}
+	if (t)
+		free(t);
+	INIT_LIST_HEAD(&dfu_list);
+}
+
+int dfu_config_entities(char *env, char *interface, int num)
+{
+	struct dfu_entity *dfu;
+	int i, ret;
+	char *s;
+
+	dfu_alt_num = dfu_find_alt_num(env);
+	debug("%s: dfu_alt_num=%d\n", __func__, dfu_alt_num);
+
+	dfu = calloc(sizeof(*dfu), dfu_alt_num);
+	if (!dfu)
+		return -1;
+	for (i = 0; i < dfu_alt_num; i++) {
+
+		s = strsep(&env, ";");
+		ret = dfu_fill_entity(&dfu[i], s, i, interface, num);
+		if (ret)
+			return -1;
+
+		list_add_tail(&dfu[i].list, &dfu_list);
+	}
+
+	return 0;
+}
+
+const char *dfu_get_dev_type(enum dfu_device_type t)
+{
+	const char *dev_t[] = {NULL, "eMMC", "OneNAND", "NAND", "RAM" };
+	return dev_t[t];
+}
+
+const char *dfu_get_layout(enum dfu_layout l)
+{
+	const char *dfu_layout[] = {NULL, "RAW_ADDR", "FAT", "EXT2",
+					   "EXT3", "EXT4", "RAM_ADDR" };
+	return dfu_layout[l];
+}
+
+void dfu_show_entities(void)
+{
+	struct dfu_entity *dfu;
+
+	puts("DFU alt settings list:\n");
+
+	list_for_each_entry(dfu, &dfu_list, list) {
+		printf("dev: %s alt: %d name: %s layout: %s\n",
+		       dfu_get_dev_type(dfu->dev_type), dfu->alt,
+		       dfu->name, dfu_get_layout(dfu->layout));
+	}
+}
+
+int dfu_get_alt_number(void)
+{
+	return dfu_alt_num;
+}
+
+struct dfu_entity *dfu_get_entity(int alt)
+{
+	struct dfu_entity *dfu;
+
+	list_for_each_entry(dfu, &dfu_list, list) {
+		if (dfu->alt == alt)
+			return dfu;
+	}
+
+	return NULL;
+}
+
+int dfu_get_alt(char *name)
+{
+	struct dfu_entity *dfu;
+
+	list_for_each_entry(dfu, &dfu_list, list) {
+		if (!strncmp(dfu->name, name, strlen(dfu->name)))
+			return dfu->alt;
+	}
+
+	return -ENODEV;
+}
diff --git a/marvell/uboot/drivers/dfu/dfu_mmc.c b/marvell/uboot/drivers/dfu/dfu_mmc.c
new file mode 100644
index 0000000..f942758
--- /dev/null
+++ b/marvell/uboot/drivers/dfu/dfu_mmc.c
@@ -0,0 +1,234 @@
+/*
+ * dfu.c -- DFU back-end routines
+ *
+ * Copyright (C) 2012 Samsung Electronics
+ * author: Lukasz Majewski <l.majewski@samsung.com>
+ *
+ * SPDX-License-Identifier:	GPL-2.0+
+ */
+
+#include <common.h>
+#include <malloc.h>
+#include <errno.h>
+#include <div64.h>
+#include <dfu.h>
+
+static unsigned char __aligned(CONFIG_SYS_CACHELINE_SIZE)
+				dfu_file_buf[CONFIG_SYS_DFU_MAX_FILE_SIZE];
+static long dfu_file_buf_len;
+
+static int mmc_block_op(enum dfu_op op, struct dfu_entity *dfu,
+			u64 offset, void *buf, long *len)
+{
+	char cmd_buf[DFU_CMD_BUF_SIZE];
+	u32 blk_start, blk_count;
+
+	/*
+	 * We must ensure that we work in lba_blk_size chunks, so ALIGN
+	 * this value.
+	 */
+	*len = ALIGN(*len, dfu->data.mmc.lba_blk_size);
+
+	blk_start = dfu->data.mmc.lba_start +
+			(u32)lldiv(offset, dfu->data.mmc.lba_blk_size);
+	blk_count = *len / dfu->data.mmc.lba_blk_size;
+	if (blk_start + blk_count >
+			dfu->data.mmc.lba_start + dfu->data.mmc.lba_size) {
+		puts("Request would exceed designated area!\n");
+		return -EINVAL;
+	}
+
+	sprintf(cmd_buf, "mmc %s %p %x %x",
+		op == DFU_OP_READ ? "read" : "write",
+		 buf, blk_start, blk_count);
+
+	debug("%s: %s 0x%p\n", __func__, cmd_buf, cmd_buf);
+	return run_command(cmd_buf, 0);
+}
+
+static int mmc_file_buffer(struct dfu_entity *dfu, void *buf, long *len)
+{
+	if (dfu_file_buf_len + *len > CONFIG_SYS_DFU_MAX_FILE_SIZE) {
+		dfu_file_buf_len = 0;
+		return -EINVAL;
+	}
+
+	/* Add to the current buffer. */
+	memcpy(dfu_file_buf + dfu_file_buf_len, buf, *len);
+	dfu_file_buf_len += *len;
+
+	return 0;
+}
+
+static int mmc_file_op(enum dfu_op op, struct dfu_entity *dfu,
+			void *buf, long *len)
+{
+	char cmd_buf[DFU_CMD_BUF_SIZE];
+	char *str_env;
+	int ret;
+
+	switch (dfu->layout) {
+	case DFU_FS_FAT:
+		sprintf(cmd_buf, "fat%s mmc %d:%d 0x%x %s",
+			op == DFU_OP_READ ? "load" : "write",
+			dfu->data.mmc.dev, dfu->data.mmc.part,
+			(unsigned int) buf, dfu->name);
+		if (op == DFU_OP_WRITE)
+			sprintf(cmd_buf + strlen(cmd_buf), " %lx", *len);
+		break;
+	case DFU_FS_EXT4:
+		sprintf(cmd_buf, "ext4%s mmc %d:%d 0x%x /%s",
+			op == DFU_OP_READ ? "load" : "write",
+			dfu->data.mmc.dev, dfu->data.mmc.part,
+			(unsigned int) buf, dfu->name);
+		if (op == DFU_OP_WRITE)
+			sprintf(cmd_buf + strlen(cmd_buf), " %ld", *len);
+		break;
+	default:
+		printf("%s: Layout (%s) not (yet) supported!\n", __func__,
+		       dfu_get_layout(dfu->layout));
+		return -1;
+	}
+
+	debug("%s: %s 0x%p\n", __func__, cmd_buf, cmd_buf);
+
+	ret = run_command(cmd_buf, 0);
+	if (ret) {
+		puts("dfu: Read error!\n");
+		return ret;
+	}
+
+	if (dfu->layout != DFU_RAW_ADDR && op == DFU_OP_READ) {
+		str_env = getenv("filesize");
+		if (str_env == NULL) {
+			puts("dfu: Wrong file size!\n");
+			return -1;
+		}
+		*len = simple_strtoul(str_env, NULL, 16);
+	}
+
+	return ret;
+}
+
+int dfu_write_medium_mmc(struct dfu_entity *dfu,
+		u64 offset, void *buf, long *len)
+{
+	int ret = -1;
+
+	switch (dfu->layout) {
+	case DFU_RAW_ADDR:
+		ret = mmc_block_op(DFU_OP_WRITE, dfu, offset, buf, len);
+		break;
+	case DFU_FS_FAT:
+	case DFU_FS_EXT4:
+		ret = mmc_file_buffer(dfu, buf, len);
+		break;
+	default:
+		printf("%s: Layout (%s) not (yet) supported!\n", __func__,
+		       dfu_get_layout(dfu->layout));
+	}
+
+	return ret;
+}
+
+int dfu_flush_medium_mmc(struct dfu_entity *dfu)
+{
+	int ret = 0;
+
+	if (dfu->layout != DFU_RAW_ADDR) {
+		/* Do stuff here. */
+		ret = mmc_file_op(DFU_OP_WRITE, dfu, &dfu_file_buf,
+				&dfu_file_buf_len);
+
+		/* Now that we're done */
+		dfu_file_buf_len = 0;
+	}
+
+	return ret;
+}
+
+int dfu_read_medium_mmc(struct dfu_entity *dfu, u64 offset, void *buf,
+		long *len)
+{
+	int ret = -1;
+
+	switch (dfu->layout) {
+	case DFU_RAW_ADDR:
+		ret = mmc_block_op(DFU_OP_READ, dfu, offset, buf, len);
+		break;
+	case DFU_FS_FAT:
+	case DFU_FS_EXT4:
+		ret = mmc_file_op(DFU_OP_READ, dfu, buf, len);
+		break;
+	default:
+		printf("%s: Layout (%s) not (yet) supported!\n", __func__,
+		       dfu_get_layout(dfu->layout));
+	}
+
+	return ret;
+}
+
+int dfu_fill_entity_mmc(struct dfu_entity *dfu, char *s)
+{
+	int dev, part;
+	struct mmc *mmc;
+	block_dev_desc_t *blk_dev;
+	disk_partition_t partinfo;
+	char *st;
+
+	dfu->dev_type = DFU_DEV_MMC;
+	st = strsep(&s, " ");
+	if (!strcmp(st, "mmc")) {
+		dfu->layout = DFU_RAW_ADDR;
+		dfu->data.mmc.lba_start = simple_strtoul(s, &s, 16);
+		dfu->data.mmc.lba_size = simple_strtoul(++s, &s, 16);
+		dfu->data.mmc.lba_blk_size = get_mmc_blk_size(dfu->dev_num);
+	} else if (!strcmp(st, "fat")) {
+		dfu->layout = DFU_FS_FAT;
+	} else if (!strcmp(st, "ext4")) {
+		dfu->layout = DFU_FS_EXT4;
+	} else if (!strcmp(st, "part")) {
+
+		dfu->layout = DFU_RAW_ADDR;
+
+		dev = simple_strtoul(s, &s, 10);
+		s++;
+		part = simple_strtoul(s, &s, 10);
+
+		mmc = find_mmc_device(dev);
+		if (mmc == NULL || mmc_init(mmc)) {
+			printf("%s: could not find mmc device #%d!\n",
+			       __func__, dev);
+			return -ENODEV;
+		}
+
+		blk_dev = &mmc->block_dev;
+		if (get_partition_info(blk_dev, part, &partinfo) != 0) {
+			printf("%s: could not find partition #%d on mmc device #%d!\n",
+			       __func__, part, dev);
+			return -ENODEV;
+		}
+
+		dfu->data.mmc.lba_start = partinfo.start;
+		dfu->data.mmc.lba_size = partinfo.size;
+		dfu->data.mmc.lba_blk_size = partinfo.blksz;
+
+	} else {
+		printf("%s: Memory layout (%s) not supported!\n", __func__, st);
+		return -ENODEV;
+	}
+
+	if (dfu->layout == DFU_FS_EXT4 || dfu->layout == DFU_FS_FAT) {
+		dfu->data.mmc.dev = simple_strtoul(s, &s, 10);
+		dfu->data.mmc.part = simple_strtoul(++s, &s, 10);
+	}
+
+	dfu->read_medium = dfu_read_medium_mmc;
+	dfu->write_medium = dfu_write_medium_mmc;
+	dfu->flush_medium = dfu_flush_medium_mmc;
+
+	/* initial state */
+	dfu->inited = 0;
+
+	return 0;
+}
diff --git a/marvell/uboot/drivers/dfu/dfu_nand.c b/marvell/uboot/drivers/dfu/dfu_nand.c
new file mode 100644
index 0000000..2d07097
--- /dev/null
+++ b/marvell/uboot/drivers/dfu/dfu_nand.c
@@ -0,0 +1,219 @@
+/*
+ * dfu_nand.c -- DFU for NAND routines.
+ *
+ * Copyright (C) 2012-2013 Texas Instruments, Inc.
+ *
+ * Based on dfu_mmc.c which is:
+ * Copyright (C) 2012 Samsung Electronics
+ * author: Lukasz Majewski <l.majewski@samsung.com>
+ *
+ * SPDX-License-Identifier:	GPL-2.0+
+ */
+
+#include <common.h>
+#include <malloc.h>
+#include <errno.h>
+#include <div64.h>
+#include <dfu.h>
+#include <linux/mtd/mtd.h>
+#include <jffs2/load_kernel.h>
+#include <nand.h>
+
+static int nand_block_op(enum dfu_op op, struct dfu_entity *dfu,
+			u64 offset, void *buf, long *len)
+{
+	loff_t start, lim;
+	size_t count, actual;
+	int ret;
+	nand_info_t *nand;
+
+	/* if buf == NULL return total size of the area */
+	if (buf == NULL) {
+		*len = dfu->data.nand.size;
+		return 0;
+	}
+
+	start = dfu->data.nand.start + offset + dfu->bad_skip;
+	lim = dfu->data.nand.start + dfu->data.nand.size - start;
+	count = *len;
+
+	if (nand_curr_device < 0 ||
+	    nand_curr_device >= CONFIG_SYS_MAX_NAND_DEVICE ||
+	    !nand_info[nand_curr_device].name) {
+		printf("%s: invalid nand device\n", __func__);
+		return -1;
+	}
+
+	nand = &nand_info[nand_curr_device];
+
+	if (op == DFU_OP_READ) {
+		ret = nand_read_skip_bad(nand, start, &count, &actual,
+				lim, buf);
+	} else {
+		nand_erase_options_t opts;
+
+		memset(&opts, 0, sizeof(opts));
+		opts.offset = start;
+		opts.length = count;
+		opts.spread = 1;
+		opts.quiet = 1;
+		opts.lim = lim;
+		/* first erase */
+		ret = nand_erase_opts(nand, &opts);
+		if (ret)
+			return ret;
+		/* then write */
+		ret = nand_write_skip_bad(nand, start, &count, &actual,
+				lim, buf, 0);
+	}
+
+	if (ret != 0) {
+		printf("%s: nand_%s_skip_bad call failed at %llx!\n",
+		       __func__, op == DFU_OP_READ ? "read" : "write",
+		       start);
+		return ret;
+	}
+
+	/*
+	 * Find out where we stopped writing data.  This can be deeper into
+	 * the NAND than we expected due to having to skip bad blocks.  So
+	 * we must take this into account for the next write, if any.
+	 */
+	if (actual > count)
+		dfu->bad_skip += actual - count;
+
+	return ret;
+}
+
+static inline int nand_block_write(struct dfu_entity *dfu,
+		u64 offset, void *buf, long *len)
+{
+	return nand_block_op(DFU_OP_WRITE, dfu, offset, buf, len);
+}
+
+static inline int nand_block_read(struct dfu_entity *dfu,
+		u64 offset, void *buf, long *len)
+{
+	return nand_block_op(DFU_OP_READ, dfu, offset, buf, len);
+}
+
+static int dfu_write_medium_nand(struct dfu_entity *dfu,
+		u64 offset, void *buf, long *len)
+{
+	int ret = -1;
+
+	switch (dfu->layout) {
+	case DFU_RAW_ADDR:
+		ret = nand_block_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_read_medium_nand(struct dfu_entity *dfu, u64 offset, void *buf,
+		long *len)
+{
+	int ret = -1;
+
+	switch (dfu->layout) {
+	case DFU_RAW_ADDR:
+		*len = dfu->data.nand.size;
+		ret = nand_block_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_flush_medium_nand(struct dfu_entity *dfu)
+{
+	int ret = 0;
+
+	/* in case of ubi partition, erase rest of the partition */
+	if (dfu->data.nand.ubi) {
+		nand_info_t *nand;
+		nand_erase_options_t opts;
+
+		if (nand_curr_device < 0 ||
+		    nand_curr_device >= CONFIG_SYS_MAX_NAND_DEVICE ||
+		    !nand_info[nand_curr_device].name) {
+			printf("%s: invalid nand device\n", __func__);
+			return -1;
+		}
+
+		nand = &nand_info[nand_curr_device];
+
+		memset(&opts, 0, sizeof(opts));
+		opts.offset = dfu->data.nand.start + dfu->offset +
+				dfu->bad_skip;
+		opts.length = dfu->data.nand.start +
+				dfu->data.nand.size - opts.offset;
+		ret = nand_erase_opts(nand, &opts);
+		if (ret != 0)
+			printf("Failure erase: %d\n", ret);
+	}
+
+	return ret;
+}
+
+int dfu_fill_entity_nand(struct dfu_entity *dfu, char *s)
+{
+	char *st;
+	int ret, dev, part;
+
+	dfu->data.nand.ubi = 0;
+	dfu->dev_type = DFU_DEV_NAND;
+	st = strsep(&s, " ");
+	if (!strcmp(st, "raw")) {
+		dfu->layout = DFU_RAW_ADDR;
+		dfu->data.nand.start = simple_strtoul(s, &s, 16);
+		s++;
+		dfu->data.nand.size = simple_strtoul(s, &s, 16);
+	} else if ((!strcmp(st, "part")) || (!strcmp(st, "partubi"))) {
+		char mtd_id[32];
+		struct mtd_device *mtd_dev;
+		u8 part_num;
+		struct part_info *pi;
+
+		dfu->layout = DFU_RAW_ADDR;
+
+		dev = simple_strtoul(s, &s, 10);
+		s++;
+		part = simple_strtoul(s, &s, 10);
+
+		sprintf(mtd_id, "%s%d,%d", "nand", dev, part - 1);
+		printf("using id '%s'\n", mtd_id);
+
+		mtdparts_init();
+
+		ret = find_dev_and_part(mtd_id, &mtd_dev, &part_num, &pi);
+		if (ret != 0) {
+			printf("Could not locate '%s'\n", mtd_id);
+			return -1;
+		}
+
+		dfu->data.nand.start = pi->offset;
+		dfu->data.nand.size = pi->size;
+		if (!strcmp(st, "partubi"))
+			dfu->data.nand.ubi = 1;
+	} else {
+		printf("%s: Memory layout (%s) not supported!\n", __func__, st);
+		return -1;
+	}
+
+	dfu->read_medium = dfu_read_medium_nand;
+	dfu->write_medium = dfu_write_medium_nand;
+	dfu->flush_medium = dfu_flush_medium_nand;
+
+	/* initial state */
+	dfu->inited = 0;
+
+	return 0;
+}
diff --git a/marvell/uboot/drivers/dfu/dfu_ram.c b/marvell/uboot/drivers/dfu/dfu_ram.c
new file mode 100644
index 0000000..335a8e1
--- /dev/null
+++ b/marvell/uboot/drivers/dfu/dfu_ram.c
@@ -0,0 +1,77 @@
+/*
+ * (C) Copyright 2013
+ * Afzal Mohammed <afzal.mohd.ma@gmail.com>
+ *
+ * Reference: dfu_mmc.c
+ * Copyright (C) 2012 Samsung Electronics
+ * author: Lukasz Majewski <l.majewski@samsung.com>
+ *
+ * SPDX-License-Identifier:	GPL-2.0+
+ */
+
+#include <common.h>
+#include <malloc.h>
+#include <errno.h>
+#include <dfu.h>
+
+static int dfu_transfer_medium_ram(enum dfu_op op, struct dfu_entity *dfu,
+				   u64 offset, void *buf, long *len)
+{
+	if (dfu->layout != DFU_RAM_ADDR) {
+		error("unsupported layout: %s\n", dfu_get_layout(dfu->layout));
+		return  -EINVAL;
+	}
+
+	if (offset > dfu->data.ram.size) {
+		error("request exceeds allowed area\n");
+		return -EINVAL;
+	}
+
+	if (op == DFU_OP_WRITE)
+		memcpy(dfu->data.ram.start + offset, buf, *len);
+	else
+		memcpy(buf, dfu->data.ram.start + offset, *len);
+
+	return 0;
+}
+
+static int dfu_write_medium_ram(struct dfu_entity *dfu, u64 offset,
+				void *buf, long *len)
+{
+	return dfu_transfer_medium_ram(DFU_OP_WRITE, dfu, offset, buf, len);
+}
+
+static int dfu_read_medium_ram(struct dfu_entity *dfu, u64 offset,
+			       void *buf, long *len)
+{
+	if (!*len) {
+		*len = dfu->data.ram.size;
+		return 0;
+	}
+
+	return dfu_transfer_medium_ram(DFU_OP_READ, dfu, offset, buf, len);
+}
+
+int dfu_fill_entity_ram(struct dfu_entity *dfu, char *s)
+{
+	char *st;
+
+	dfu->dev_type = DFU_DEV_RAM;
+	st = strsep(&s, " ");
+	if (strcmp(st, "ram")) {
+		error("unsupported device: %s\n", st);
+		return -ENODEV;
+	}
+
+	dfu->layout = DFU_RAM_ADDR;
+	dfu->data.ram.start = (void *)simple_strtoul(s, &s, 16);
+	s++;
+	dfu->data.ram.size = simple_strtoul(s, &s, 16);
+
+	dfu->write_medium = dfu_write_medium_ram;
+	dfu->read_medium = dfu_read_medium_ram;
+
+	dfu->inited = 0;
+
+	return 0;
+}