Merge "ipq4019: fdt_fixup: Delete the 'sd-ldo-gpios' property from sdhci node"
diff --git a/arch/arm/cpu/armv7/qca/common/smem.c b/arch/arm/cpu/armv7/qca/common/smem.c
index 8c12fc7..32e0810 100644
--- a/arch/arm/cpu/armv7/qca/common/smem.c
+++ b/arch/arm/cpu/armv7/qca/common/smem.c
@@ -649,6 +649,49 @@
 	return flash_var;
 }
 
+/*
+ * getpart_offset_size - retreive partition offset and size
+ * @part_name - partition name
+ * @offset - location where the offset of partition to be stored
+ * @size - location where partition size to be stored
+ *
+ * Retreive partition offset and size in bytes with respect to the
+ * partition specific flash block size
+ */
+int getpart_offset_size(char *part_name, uint32_t *offset, uint32_t *size)
+{
+	int i;
+	uint32_t bsize;
+	qca_smem_flash_info_t *sfi = &qca_smem_flash_info;
+
+	for (i = 0; i < smem_ptable.len; i++) {
+		struct smem_ptn *p = &smem_ptable.parts[i];
+		loff_t psize;
+		if (!strncmp(p->name, part_name, SMEM_PTN_NAME_MAX)) {
+			bsize = get_part_block_size(p, sfi);
+			if (p->size == (~0u)) {
+				/*
+				 * Partition size is 'till end of device', calculate
+				 * appropriately
+				 */
+				psize = nand_info[get_device_id_by_part(p)].size
+					- (((loff_t)p->start) * bsize);
+			} else {
+				psize = ((loff_t)p->size) * bsize;
+			}
+
+		*offset = ((loff_t)p->start) * bsize;
+		*size = psize;
+		break;
+		}
+	}
+
+	if (i == smem_ptable.len)
+		return -ENOENT;
+
+	return 0;
+}
+
 int do_smeminfo(cmd_tbl_t *cmdtp, int flag, int argc, char * const argv[])
 {
 	qca_smem_flash_info_t *sfi = &qca_smem_flash_info;
diff --git a/arch/arm/include/asm/arch-qca-common/smem.h b/arch/arm/include/asm/arch-qca-common/smem.h
index 133d6a4..070b83a 100644
--- a/arch/arm/include/asm/arch-qca-common/smem.h
+++ b/arch/arm/include/asm/arch-qca-common/smem.h
@@ -60,6 +60,7 @@
 			uint32_t *flash_block_size,
 			uint32_t *flash_density);
 int smem_getpart(char *name, uint32_t *start, uint32_t *size);
+int getpart_offset_size(char *part_name, uint32_t *offset, uint32_t *size);
 unsigned int smem_get_board_machtype(void);
 uint32_t get_nand_block_size(uint8_t dev_id);
 unsigned int get_which_flash_param(char *part_name);
diff --git a/board/qca/arm/ipq806x/ipq806x.c b/board/qca/arm/ipq806x/ipq806x.c
index 6e583ad..7af5148 100644
--- a/board/qca/arm/ipq806x/ipq806x.c
+++ b/board/qca/arm/ipq806x/ipq806x.c
@@ -535,6 +535,13 @@
 	uint32_t soc_version, soc_version_major, soc_version_minor;
 	int nodeoff, ret;
 
+	nodeoff = fdt_path_offset(blob, "/");
+
+	if (nodeoff < 0) {
+		printf("ipq: fdt fixup cannot find root node\n");
+		return;
+	}
+
 	ret = ipq_smem_get_socinfo_cpu_type(&cpu_type);
 	if (ret) {
 		return;
diff --git a/common/Makefile b/common/Makefile
index 2a1d9f8..4f6107f 100644
--- a/common/Makefile
+++ b/common/Makefile
@@ -101,6 +101,7 @@
 obj-$(CONFIG_OF_LIBFDT) += cmd_fdt.o fdt_support.o
 obj-$(CONFIG_CMD_FITUPD) += cmd_fitupd.o
 obj-$(CONFIG_CMD_FLASH) += cmd_flash.o
+obj-$(CONFIG_CMD_FLASHWRITE) += cmd_flashwrite.o
 ifdef CONFIG_FPGA
 obj-$(CONFIG_CMD_FPGA) += cmd_fpga.o
 endif
diff --git a/common/cmd_flashwrite.c b/common/cmd_flashwrite.c
new file mode 100644
index 0000000..6f91148
--- /dev/null
+++ b/common/cmd_flashwrite.c
@@ -0,0 +1,245 @@
+/*
+ * Copyright (c) 2018, The Linux Foundation. All rights reserved.
+
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ */
+
+/*
+ * FlashWrite command support
+ */
+#include <common.h>
+#include <command.h>
+#include <asm/arch-qca-common/smem.h>
+#include <part.h>
+#include <linux/mtd/mtd.h>
+#include <nand.h>
+#include <mmc.h>
+#include <sdhci.h>
+
+DECLARE_GLOBAL_DATA_PTR;
+#ifndef CONFIG_SDHCI_SUPPORT
+extern qca_mmc mmc_host;
+#else
+extern struct sdhci_host mmc_host;
+#endif
+
+#define SMEM_PTN_NAME_MAX     16
+
+static int write_to_flash(int flash_type, uint32_t address, uint32_t offset,
+uint32_t part_size, uint32_t file_size, char *layout)
+{
+
+	char runcmd[256];
+	int nand_dev = CONFIG_NAND_FLASH_INFO_IDX;
+
+	if (flash_type == SMEM_BOOT_NAND_FLASH) {
+
+		snprintf(runcmd, sizeof(runcmd), "nand device %d && ", nand_dev);
+
+		if (strcmp(layout, "default") != 0) {
+
+			snprintf(runcmd + strlen(runcmd), sizeof(runcmd),
+						"ipq_nand %s && ", layout);
+		}
+
+		snprintf(runcmd + strlen(runcmd), sizeof(runcmd),
+			"nand erase 0x%x 0x%x && "
+			"nand write 0x%x 0x%x 0x%x && ",
+			offset, part_size,
+			address, offset, file_size);
+
+	} else if (flash_type == SMEM_BOOT_MMC_FLASH) {
+
+		snprintf(runcmd, sizeof(runcmd),
+			"mmc erase 0x%x 0x%x && "
+			"mmc write 0x%x 0x%x 0x%x && ",
+			offset, part_size,
+			address, offset, file_size);
+
+	} else if (flash_type == SMEM_BOOT_SPI_FLASH) {
+
+		snprintf(runcmd, sizeof(runcmd),
+			"sf probe && "
+			"sf erase 0x%x 0x%x && "
+			"sf write 0x%x 0x%x 0x%x && ",
+			offset, part_size,
+			address, offset, file_size);
+	}
+
+	if (run_command(runcmd, 0) != CMD_RET_SUCCESS)
+		return CMD_RET_FAILURE;
+
+	return CMD_RET_SUCCESS;
+}
+
+static int do_flash(cmd_tbl_t *cmdtp, int flag, int argc,
+char * const argv[])
+{
+	uint32_t load_addr, offset, part_size, file_size, adj_size;
+	uint32_t size_block, start_block, file_size_cpy;
+	char *part_name = NULL, *filesize;
+	int flash_type, ret, retn;
+	unsigned int active_part = 0;
+	char *layout = NULL;
+#ifdef CONFIG_IPQ806X
+	char* layout_linux[] = {"rootfs", "0:BOOTCONFIG", "0:BOOTCONFIG1"};
+	int len, i;
+#endif
+	retn = CMD_RET_FAILURE;
+
+	block_dev_desc_t *blk_dev;
+	disk_partition_t disk_info;
+	qca_smem_flash_info_t *sfi = &qca_smem_flash_info;
+	nand_info_t *nand = &nand_info[CONFIG_NAND_FLASH_INFO_IDX];
+
+	if ((argc < 3) || (argc > 4))
+		return CMD_RET_USAGE;
+
+	if (argc == 4)
+		file_size = simple_strtoul(argv[3], NULL, 16);
+	else {
+		filesize = getenv("filesize");
+		if (filesize != NULL)
+			file_size = simple_strtoul(filesize, NULL, 16);
+		else
+			return CMD_RET_USAGE;
+	}
+
+	if (strncmp(argv[2], "default", 7) == 0)
+		load_addr = CONFIG_SYS_LOAD_ADDR;
+	else
+		load_addr = simple_strtoul(argv[2], NULL, 16);
+
+	flash_type = sfi->flash_type;
+	part_name = argv[1];
+
+	if (sfi->flash_type == SMEM_BOOT_NAND_FLASH) {
+
+		ret = smem_getpart(part_name, &start_block, &size_block);
+		if (ret)
+			return retn;
+
+		offset = sfi->flash_block_size * start_block;
+		part_size = sfi->flash_block_size * size_block;
+		layout = "default";
+#ifdef CONFIG_IPQ806X
+		len = sizeof(layout_linux)/sizeof(layout_linux[0]);
+
+		for (i = 0; i < len; i++) {
+
+			if (!strncmp(layout_linux[i], part_name,
+						SMEM_PTN_NAME_MAX)) {
+				layout = "linux";
+				break;
+			}
+		}
+
+		if (i == len )
+			layout = "sbl";
+#endif
+
+	} else if (sfi->flash_type == SMEM_BOOT_MMC_FLASH) {
+
+		blk_dev = mmc_get_dev(mmc_host.dev_num);
+		if (blk_dev != NULL) {
+
+			ret = get_partition_info_efi_by_name(blk_dev,
+			part_name, &disk_info);
+			if (ret)
+				return retn;
+
+			offset = (ulong)disk_info.start;
+			part_size = (ulong)disk_info.size;
+			file_size_cpy = file_size;
+		}
+
+	} else if (sfi->flash_type == SMEM_BOOT_SPI_FLASH) {
+
+		if (get_which_flash_param(part_name)) {
+
+			/* NOR + NAND*/
+			flash_type = SMEM_BOOT_NAND_FLASH;
+			ret = getpart_offset_size(part_name, &offset, &part_size);
+			if (ret)
+				return retn;
+
+		} else if ((sfi->flash_secondary_type == SMEM_BOOT_NAND_FLASH)
+				&& (strncmp(part_name, "rootfs", 6) == 0)) {
+
+			/* IPQ806X - NOR + NAND */
+			flash_type = SMEM_BOOT_NAND_FLASH;
+
+			if (sfi->rootfs.offset == 0xBAD0FF5E) {
+				if (smem_bootconfig_info() == 0)
+					active_part = get_rootfs_active_partition();
+
+				offset = (ulong) active_part * IPQ_NAND_ROOTFS_SIZE;
+				part_size = (ulong) IPQ_NAND_ROOTFS_SIZE;
+			}
+
+		}else if ((smem_getpart(part_name, &start_block, &size_block)
+				== -ENOENT) && (sfi->rootfs.offset == 0xBAD0FF5E)){
+
+			/* NOR + EMMC */
+			flash_type = SMEM_BOOT_MMC_FLASH;
+
+			blk_dev = mmc_get_dev(mmc_host.dev_num);
+			if (blk_dev != NULL) {
+
+				ret = get_partition_info_efi_by_name(blk_dev,
+							part_name, &disk_info);
+				if (ret)
+					return retn;
+
+				offset = (ulong)disk_info.start;
+				part_size = (ulong)disk_info.size;
+				file_size_cpy = file_size;
+			}
+
+		} else {
+
+			ret = smem_getpart(part_name, &start_block,
+							&size_block);
+			if (ret)
+				return retn;
+
+			offset = sfi->flash_block_size * start_block;
+			part_size = sfi->flash_block_size * size_block;
+		}
+	}
+
+	if (flash_type == SMEM_BOOT_NAND_FLASH) {
+
+		adj_size = file_size % nand->writesize;
+		if (adj_size)
+			file_size = file_size + (nand->writesize - adj_size);
+	}
+
+	if (flash_type == SMEM_BOOT_MMC_FLASH) {
+
+		file_size = file_size / disk_info.blksz;
+		adj_size = file_size_cpy % disk_info.blksz;
+		if (adj_size)
+			file_size = file_size + 1;
+	}
+
+	ret = write_to_flash(flash_type, load_addr, offset, part_size,
+							file_size, layout);
+
+return ret;
+}
+
+U_BOOT_CMD(
+	flash,       4,      0,      do_flash,
+	"flash part_name load_addr [file_size] \n"
+	"\tflash part_name 'default' \n",
+	"flash the image at load_addr, given file_size in hex\n"
+);
+
diff --git a/include/configs/ipq40xx.h b/include/configs/ipq40xx.h
index b294004..0e6e819 100644
--- a/include/configs/ipq40xx.h
+++ b/include/configs/ipq40xx.h
@@ -122,6 +122,7 @@
 #define QCA_BOOT_PARAMS_ADDR	(QCA_KERNEL_START_ADDR + 0x100)
 #endif
 #define CONFIG_FLASH_PROTECT
+#define CONFIG_CMD_FLASHWRITE
 
 /* Environment */
 #define CONFIG_ARCH_CPU_INIT
diff --git a/include/configs/ipq806x.h b/include/configs/ipq806x.h
index 40e1d6b..57af6e9 100644
--- a/include/configs/ipq806x.h
+++ b/include/configs/ipq806x.h
@@ -93,6 +93,7 @@
 
 #define CONFIG_SYS_DEVICE_NULLDEV
 #define CONFIG_FLASH_PROTECT
+#define CONFIG_CMD_FLASHWRITE
 
 /* Environment */
 #define CONFIG_MSM_PCOMM
diff --git a/include/configs/ipq807x.h b/include/configs/ipq807x.h
index 877eb25..b466956 100644
--- a/include/configs/ipq807x.h
+++ b/include/configs/ipq807x.h
@@ -140,6 +140,7 @@
 
 #define CONFIG_ENV_IS_IN_NAND		1
 #define CONFIG_FLASH_PROTECT
+#define CONFIG_CMD_FLASHWRITE
 
 /* Allow to overwrite serial and ethaddr */
 #define CONFIG_ENV_OVERWRITE