Merge "Revert "qca: Disabling dcache before scheduling secondary cores""
diff --git a/board/qca/arm/common/board_init.c b/board/qca/arm/common/board_init.c
index 8891fbc..2c9ab0e 100644
--- a/board/qca/arm/common/board_init.c
+++ b/board/qca/arm/common/board_init.c
@@ -16,7 +16,10 @@
 #include <asm/arch-qca-common/smem.h>
 #include <asm/arch-qca-common/uart.h>
 #include <asm/arch-qca-common/gpio.h>
+#include <memalign.h>
 #include <fdtdec.h>
+#include <mmc.h>
+#include <sdhci.h>
 
 DECLARE_GLOBAL_DATA_PTR;
 
@@ -31,6 +34,12 @@
 extern env_t *mmc_env_ptr;
 extern char *mmc_env_name_spec;
 extern int mmc_saveenv(void);
+
+#ifndef CONFIG_SDHCI_SUPPORT
+extern qca_mmc mmc_host;
+#else
+extern struct sdhci_host mmc_host;
+#endif
 #endif
 
 env_t *env_ptr;
@@ -222,6 +231,39 @@
 	return 0;
 }
 
+#ifdef CONFIG_FLASH_PROTECT
+void board_flash_protect(void)
+{
+	unsigned int num_part;
+	int i;
+#ifdef CONFIG_QCA_MMC
+	block_dev_desc_t *mmc_dev;
+	disk_partition_t info;
+
+	mmc_dev = mmc_get_dev(mmc_host.dev_num);
+	if (mmc_dev != NULL && mmc_dev->type != DEV_TYPE_UNKNOWN) {
+		ALLOC_CACHE_ALIGN_BUFFER_PAD(gpt_header,
+					     gpt_head, 1, mmc_dev->blksz);
+		if (mmc_dev->block_read(mmc_dev->dev,
+		    (lbaint_t)GPT_PRIMARY_PARTITION_TABLE_LBA,
+		    1, gpt_head) == 1) {
+			num_part = le32_to_cpu(gpt_head->num_partition_entries);
+			for (i = 1; i <= num_part; i++) {
+				if (!get_partition_info_efi(mmc_dev, i, &info)
+				    && info.readonly
+				    && !mmc_write_protect(mmc_host.mmc,
+							  info.start,
+							  info.size, 1))
+					printf("\"%s\""
+						"-protected MMC partition\n",
+						info.name);
+			}
+		}
+	}
+#endif
+}
+#endif
+
 int board_late_init(void)
 {
 	unsigned int machid;
@@ -238,6 +280,9 @@
 		setenv_addr("machid", (void *)machid);
 		gd->bd->bi_arch_number = machid;
 	}
+#ifdef CONFIG_FLASH_PROTECT
+	board_flash_protect();
+#endif
 	set_ethmac_addr();
 	return 0;
 }
diff --git a/common/cmd_mmc.c b/common/cmd_mmc.c
index a6b7313..5111980 100644
--- a/common/cmd_mmc.c
+++ b/common/cmd_mmc.c
@@ -330,6 +330,31 @@
 }
 #endif
 
+static int do_mmc_protect (cmd_tbl_t *cmdtp, int flag,
+			   int argc, char * const argv[])
+{
+	struct mmc *mmc;
+	unsigned int ret;
+	unsigned int blk, cnt;
+
+	if (argc != 3)
+		return CMD_RET_USAGE;
+
+	blk = (unsigned int)simple_strtoul(argv[1], NULL, 16);
+	cnt = (unsigned int)simple_strtoul(argv[2], NULL, 16);
+
+	mmc = init_mmc_device(curr_device, false);
+	if (!mmc)
+		return CMD_RET_FAILURE;
+
+	ret = mmc_write_protect(mmc, blk, cnt, 1);
+
+	if (!ret)
+		printf("Offset: 0x%x Count: %d blocks\nDone!\n", blk, cnt);
+
+	return ret ? CMD_RET_FAILURE : CMD_RET_SUCCESS;
+}
+
 static int do_mmc_read(cmd_tbl_t *cmdtp, int flag,
 		       int argc, char * const argv[])
 {
@@ -802,6 +827,7 @@
 	U_BOOT_CMD_MKENT(dev, 3, 0, do_mmc_dev, "", ""),
 	U_BOOT_CMD_MKENT(list, 1, 1, do_mmc_list, "", ""),
 	U_BOOT_CMD_MKENT(hwpartition, 28, 0, do_mmc_hwpartition, "", ""),
+	U_BOOT_CMD_MKENT(protect, 4, 0, do_mmc_protect, "", ""),
 #ifdef CONFIG_SUPPORT_EMMC_BOOT
 	U_BOOT_CMD_MKENT(bootbus, 5, 0, do_mmc_bootbus, "", ""),
 	U_BOOT_CMD_MKENT(bootpart-resize, 4, 0, do_mmc_boot_resize, "", ""),
@@ -858,6 +884,7 @@
 	"    [check|set|complete] - mode, complete set partitioning completed\n"
 	"  WARNING: Partitioning is a write-once setting once it is set to complete.\n"
 	"  Power cycling is required to initialize partitions after set to complete.\n"
+	"mmc protect start_blk# cnt_blk\n"
 #ifdef CONFIG_SUPPORT_EMMC_BOOT
 	"mmc bootbus dev boot_bus_width reset_boot_bus_width boot_mode\n"
 	" - Set the BOOT_BUS_WIDTH field of the specified device\n"
diff --git a/disk/part_efi.c b/disk/part_efi.c
index 37735e9..57840b0 100644
--- a/disk/part_efi.c
+++ b/disk/part_efi.c
@@ -70,6 +70,14 @@
 			sizeof(efi_guid_t));
 }
 
+static inline int is_readonly(gpt_entry *p)
+{
+	/* bit 60 of gpt attribute denotes read-only flag */
+	if (p->attributes.raw & ((unsigned long long)1 << 60))
+		return 1;
+	return 0;
+}
+
 static int validate_gpt_header(gpt_header *gpt_h, lbaint_t lba,
 		lbaint_t lastlba)
 {
@@ -283,6 +291,7 @@
 			print_efiname(&gpt_pte[part - 1]));
 	snprintf((char *)info->type, sizeof(info->type), "U-Boot");
 	info->bootable = is_bootable(&gpt_pte[part - 1]);
+	info->readonly = is_readonly(&gpt_pte[part - 1]);
 #ifdef CONFIG_PARTITION_UUIDS
 	uuid_bin_to_str(gpt_pte[part - 1].unique_partition_guid.b, info->uuid,
 			UUID_STR_FORMAT_GUID);
diff --git a/drivers/mmc/mmc.c b/drivers/mmc/mmc.c
index 62765d8..0320942 100644
--- a/drivers/mmc/mmc.c
+++ b/drivers/mmc/mmc.c
@@ -1181,6 +1181,8 @@
 	 * For SD, its erase group is always one sector
 	 */
 	mmc->erase_grp_size = 1;
+	mmc->wp_grp_size = WP_GRP_SIZE(mmc->csd);
+	mmc->wp_grp_enable = WP_GRP_ENABLE(mmc->csd);
 	mmc->part_config = MMCPART_NOAVAILABLE;
 	if (!IS_SD(mmc) && (mmc->version >= MMC_VERSION_4)) {
 		/* check  ext_csd version and capacity */
@@ -1836,6 +1838,87 @@
 	return 0;
 }
 
+int mmc_send_wp_set_clr(struct mmc *mmc, unsigned int start,
+			unsigned int size, int set_clr)
+{
+	unsigned int err;
+	unsigned int wp_group_size, count, i;
+	struct mmc_cmd cmd;
+
+	wp_group_size = (mmc->wp_grp_size + 1) * mmc->erase_grp_size;
+	count = DIV_ROUND_UP(size, wp_group_size);
+
+	if (set_clr)
+		cmd.cmdidx = MMC_CMD_SET_WRITE_PROT;
+	else
+		cmd.cmdidx = MMC_CMD_CLR_WRITE_PROT;
+	cmd.resp_type = MMC_RSP_R1b;
+
+	for (i = 0; i < count; i++) {
+		cmd.cmdarg = start + (i * wp_group_size);
+		err = mmc_send_cmd(mmc, &cmd, NULL);
+		if (err) {
+			printf("%s: Error at block 0x%lx - %d\n", __func__,
+				cmd.cmdarg, err);
+			return err;
+		}
+
+		if(MMC_ADDR_OUT_OF_RANGE(cmd.response[0])) {
+			printf("%s: mmc block(0x%lx) out of range", __func__,
+				cmd.cmdarg);
+			return -EINVAL;
+		}
+
+		err = mmc_send_status(mmc, MMC_RESP_TIMEOUT);
+		if (err)
+			return err;
+	}
+
+	return 0;
+}
+
+int mmc_write_protect(struct mmc *mmc, unsigned int start_blk,
+		      unsigned int cnt_blk, int set_clr)
+{
+	ALLOC_CACHE_ALIGN_BUFFER(u8, ext_csd, MMC_MAX_BLOCK_LEN);
+	int err;
+	unsigned int wp_group_size;
+
+	if (!mmc->wp_grp_enable)
+		return -1; /* group write protection is not supported */
+
+	err = mmc_send_ext_csd(mmc, ext_csd);
+
+	if (err) {
+		debug("ext_csd register cannot be retrieved\n");
+		return err;
+	}
+
+	if ((ext_csd[EXT_CSD_USER_WP] & EXT_CSD_US_PWR_WP_DIS)
+	    || (ext_csd[EXT_CSD_USER_WP] & EXT_CSD_US_PERM_WP_EN)) {
+		printf("User power-on write protection is disabled. \n");
+		return -1;
+	}
+
+	err = mmc_switch(mmc, EXT_CSD_CMD_SET_NORMAL, EXT_CSD_USER_WP,
+		         EXT_CSD_US_PWR_WP_EN);
+	if (err) {
+		printf("Failed to enable user power-on write protection\n");
+		return err;
+	}
+
+	wp_group_size = (mmc->wp_grp_size + 1) * mmc->erase_grp_size;
+
+	if (!cnt_blk || start_blk % wp_group_size || cnt_blk % wp_group_size) {
+		printf("Error: Unaligned offset/count. offset/count should be aligned to 0x%x blocks\n", wp_group_size);
+		return -1;
+	}
+
+	err = mmc_send_wp_set_clr(mmc, start_blk, cnt_blk, set_clr);
+
+	return err;
+}
+
 #ifdef CONFIG_SUPPORT_EMMC_BOOT
 /*
  * This function changes the size of boot partition and the size of rpmb
diff --git a/drivers/spi/ipq_spi.c b/drivers/spi/ipq_spi.c
index 511edae..682d62f 100644
--- a/drivers/spi/ipq_spi.c
+++ b/drivers/spi/ipq_spi.c
@@ -43,6 +43,14 @@
 DECLARE_GLOBAL_DATA_PTR;
 
 /*
+ * CS GPIO number array cs_gpio_array[port_num][cs_num]
+ * cs_gpio_array[0][x] -- GSBI5
+ * cs_gpio_array[1][x] -- GSBI6
+ * cs_gpio_array[2][x] -- GSBI7
+ */
+static unsigned int cs_gpio_array[NUM_PORTS][NUM_CS];
+
+/*
  * GSBI HCLK state register bit
  * hclk_state[0] -- GSBI5
  * hclk_state[1] -- GSBI6
@@ -203,18 +211,11 @@
  */
 static void CS_change(int port_num, int cs_num, int enable)
 {
-	unsigned int cs_gpio = 0;
+	unsigned int cs_gpio;
 	uint32_t addr = 0;
 	uint32_t val = 0;
-        char spi_node_path[32];
-	int spi_cs_node = 0, spi_cs_gpio_node = 0;
 
-	sprintf(spi_node_path, "/spi/spi%d/cs%d", port_num, cs_num);
-
-        spi_cs_node = fdt_path_offset(gd->fdt_blob, spi_node_path);
-	spi_cs_gpio_node = fdt_first_subnode(gd->fdt_blob, spi_cs_node);
-
-	cs_gpio = fdtdec_get_uint(gd->fdt_blob, spi_cs_gpio_node, "gpio", 0);
+	cs_gpio = cs_gpio_array[port_num][cs_num];
 
 	addr = GPIO_IN_OUT_ADDR(cs_gpio);
 	val = readl(addr);
@@ -232,6 +233,21 @@
 {
 	char spi_node_path[32];
 	int spi_node = 0;
+	int i,j;
+	int spi_cs_node = 0, spi_cs_gpio_node = 0;
+
+	for (i = 0; i < NUM_PORTS; i++) {
+		for (j = 0; j < NUM_CS; j++) {
+			sprintf(spi_node_path, "/spi/spi%d/cs%d", i, j);
+			spi_cs_node = fdt_path_offset
+					(gd->fdt_blob, spi_node_path);
+			spi_cs_gpio_node = fdt_first_subnode
+					(gd->fdt_blob, spi_cs_node);
+			cs_gpio_array[i][j] = fdtdec_get_uint
+				(gd->fdt_blob, spi_cs_gpio_node, "gpio", 0);
+		}
+	}
+
 	/* Hold the GSBIn (core_num) core in reset */
 	clrsetbits_le32(GSBIn_RESET_REG(GSBI_IDX_TO_GSBI(port_num)),
 			GSBI1_RESET_MSK, GSBI1_RESET);
diff --git a/include/configs/ipq807x.h b/include/configs/ipq807x.h
index 34a1355..c1c85ae 100644
--- a/include/configs/ipq807x.h
+++ b/include/configs/ipq807x.h
@@ -137,6 +137,7 @@
 #define CONFIG_SYS_MALLOC_LEN		(CONFIG_ENV_SIZE_MAX + (1024 << 10))
 
 #define CONFIG_ENV_IS_IN_NAND		1
+#define CONFIG_FLASH_PROTECT
 
 /* Allow to overwrite serial and ethaddr */
 #define CONFIG_ENV_OVERWRITE
diff --git a/include/mmc.h b/include/mmc.h
index 20d83f8..6c4b3c1 100644
--- a/include/mmc.h
+++ b/include/mmc.h
@@ -90,6 +90,10 @@
 #define MMC_CMD_SET_BLOCK_COUNT         23
 #define MMC_CMD_WRITE_SINGLE_BLOCK	24
 #define MMC_CMD_WRITE_MULTIPLE_BLOCK	25
+#define MMC_CMD_SET_WRITE_PROT		28
+#define MMC_CMD_CLR_WRITE_PROT		29
+#define MMC_CMD_SEND_WRITE_PROT		30
+#define MMC_CMD_SEND_WRITE_PROT_TYPE	31
 #define MMC_CMD_ERASE_GROUP_START	35
 #define MMC_CMD_ERASE_GROUP_END		36
 #define MMC_CMD_ERASE			38
@@ -167,6 +171,17 @@
 #define SD_SWITCH_CHECK		0
 #define SD_SWITCH_SWITCH	1
 
+#define MMC_RESP_TIMEOUT		2000
+#define MMC_ADDR_OUT_OF_RANGE(resp)	((resp >> 31) & 0x01)
+
+/*
+ * CSD fields
+*/
+#define WP_GRP_ENABLE(csd)		((csd[3] & 0x80000000) >> 31)
+#define WP_GRP_SIZE(csd)		((csd[2] & 0x0000001f))
+#define ERASE_GRP_MULT(csd)		((csd[2] & 0x000003e0) >> 5)
+#define ERASE_GRP_SIZE(csd)		((csd[2] & 0x00007c00) >> 10)
+
 /*
  * EXT_CSD fields
  */
@@ -181,6 +196,7 @@
 #define EXT_CSD_WR_REL_PARAM		166	/* R */
 #define EXT_CSD_WR_REL_SET		167	/* R/W */
 #define EXT_CSD_RPMB_MULT		168	/* RO */
+#define EXT_CSD_USER_WP			171	/* R/W */
 #define EXT_CSD_ERASE_GROUP_DEF		175	/* R/W */
 #define EXT_CSD_BOOT_BUS_WIDTH		177
 #define EXT_CSD_PART_CONF		179	/* R/W */
@@ -241,6 +257,11 @@
 #define EXT_CSD_WR_DATA_REL_USR		(1 << 0)	/* user data area WR_REL */
 #define EXT_CSD_WR_DATA_REL_GP(x)	(1 << ((x)+1))	/* GP part (x+1) WR_REL */
 
+#define EXT_CSD_US_PERM_WP_DIS		(1 << 4)
+#define EXT_CSD_US_PWR_WP_DIS		(1 << 3)
+#define EXT_CSD_US_PERM_WP_EN		(1 << 2)
+#define EXT_CSD_US_PWR_WP_EN		(1 << 0)
+
 #define R1_ILLEGAL_COMMAND		(1 << 22)
 #define R1_APP_CMD			(1 << 5)
 
@@ -379,6 +400,8 @@
 	uint write_bl_len;
 	uint erase_grp_size;	/* in 512-byte sectors */
 	uint hc_wp_grp_size;	/* in 512-byte sectors */
+	uint wp_grp_size;
+	uint wp_grp_enable;
 	u64 capacity;
 	u64 capacity_user;
 	u64 capacity_boot;
@@ -506,6 +529,9 @@
 int pci_mmc_init(const char *name, struct pci_device_id *mmc_supported,
 		 int num_ids);
 
+int mmc_write_protect(struct mmc *mmc, unsigned int start_blk,
+		      unsigned int cnt_blk, int set_clr);
+
 /* Set block count limit because of 16 bit register limit on some hardware*/
 #ifndef CONFIG_SYS_MMC_MAX_BLK_COUNT
 #define CONFIG_SYS_MMC_MAX_BLK_COUNT 65535
diff --git a/include/part.h b/include/part.h
index 720a867..ce8b497 100644
--- a/include/part.h
+++ b/include/part.h
@@ -96,6 +96,7 @@
 #ifdef CONFIG_PARTITION_TYPE_GUID
 	char	type_guid[37];	/* type GUID as string, if exists	*/
 #endif
+	int readonly;		/* read-only flag is set		*/
 } disk_partition_t;
 
 /* Misc _get_dev functions */