Merge "qca: Add logic to restrict runmulticore command on secondary cores"
diff --git a/arch/arm/dts/ipq40xx-dk04.dtsi b/arch/arm/dts/ipq40xx-dk04.dtsi
index df68c2e..879fdbd 100644
--- a/arch/arm/dts/ipq40xx-dk04.dtsi
+++ b/arch/arm/dts/ipq40xx-dk04.dtsi
@@ -98,7 +98,7 @@
 				gpio = <23>;
 				func = <1>;
 				pull = <GPIO_PULL_UP>;
-				drvstr = <GPIO_10MA>;
+				drvstr = <DRV_TYPE_C>;
 				oe = <GPIO_OE_DISABLE>;
 				vm = <GPIO_VM_ENABLE>;
 				od_en = <GPIO_OD_DISABLE>;
@@ -109,7 +109,7 @@
 				gpio = <24>;
 				func = <1>;
 				pull = <GPIO_PULL_UP>;
-				drvstr = <GPIO_10MA>;
+				drvstr = <DRV_TYPE_C>;
 				oe = <GPIO_OE_DISABLE>;
 				vm = <GPIO_VM_ENABLE>;
 				od_en = <GPIO_OD_DISABLE>;
@@ -120,7 +120,7 @@
 				gpio = <25>;
 				func = <1>;
 				pull = <GPIO_PULL_UP>;
-				drvstr = <GPIO_10MA>;
+				drvstr = <DRV_TYPE_C>;
 				oe = <GPIO_OE_DISABLE>;
 				vm = <GPIO_VM_ENABLE>;
 				od_en = <GPIO_OD_DISABLE>;
@@ -130,7 +130,7 @@
 			gpio4 {
 				gpio = <26>;
 				func = <1>;
-				pull = <GPIO_PULL_UP>;
+				pull = <DRV_TYPE_C>;
 				drvstr = <GPIO_10MA>;
 				oe = <GPIO_OE_DISABLE>;
 				vm = <GPIO_VM_ENABLE>;
@@ -142,7 +142,7 @@
 				gpio = <27>;
 				func = <1>;
 				pull = <GPIO_PULL_UP>;
-				drvstr = <GPIO_16MA>;
+				drvstr = <DRV_TYPE_A>;
 				oe = <GPIO_OE_DISABLE>;
 				vm = <GPIO_VM_ENABLE>;
 				od_en = <GPIO_OD_DISABLE>;
@@ -153,7 +153,7 @@
 				gpio = <28>;
 				func = <1>;
 				pull = <GPIO_PULL_UP>;
-				drvstr = <GPIO_10MA>;
+				drvstr = <DRV_TYPE_C>;
 				oe = <GPIO_OE_DISABLE>;
 				vm = <GPIO_VM_ENABLE>;
 				od_en = <GPIO_OD_DISABLE>;
@@ -164,7 +164,7 @@
 				gpio = <29>;
 				func = <1>;
 				pull = <GPIO_PULL_UP>;
-				drvstr = <GPIO_10MA>;
+				drvstr = <DRV_TYPE_C>;
 				oe = <GPIO_OE_DISABLE>;
 				vm = <GPIO_VM_ENABLE>;
 				od_en = <GPIO_OD_DISABLE>;
@@ -175,7 +175,7 @@
 				gpio = <30>;
 				func = <1>;
 				pull = <GPIO_PULL_UP>;
-				drvstr = <GPIO_10MA>;
+				drvstr = <DRV_TYPE_C>;
 				oe = <GPIO_OE_DISABLE>;
 				vm = <GPIO_VM_ENABLE>;
 				od_en = <GPIO_OD_DISABLE>;
@@ -186,7 +186,7 @@
 				gpio = <31>;
 				func = <1>;
 				pull = <GPIO_PULL_UP>;
-				drvstr = <GPIO_10MA>;
+				drvstr = <DRV_TYPE_C>;
 				oe = <GPIO_OE_DISABLE>;
 				vm = <GPIO_VM_ENABLE>;
 				od_en = <GPIO_OD_DISABLE>;
@@ -196,8 +196,8 @@
 			gpio10 {
 				gpio = <32>;
 				func = <1>;
-				pull = <GPIO_NO_PULL>;
-				drvstr = <GPIO_10MA>;
+				pull = <GPIO_PULL_UP>;
+				drvstr = <DRV_TYPE_C>;
 				oe = <GPIO_OE_DISABLE>;
 				vm = <GPIO_VM_ENABLE>;
 				od_en = <GPIO_OD_DISABLE>;
diff --git a/arch/arm/dts/ipq40xx-dk07.dtsi b/arch/arm/dts/ipq40xx-dk07.dtsi
index dda45df..bebf5cf 100644
--- a/arch/arm/dts/ipq40xx-dk07.dtsi
+++ b/arch/arm/dts/ipq40xx-dk07.dtsi
@@ -48,9 +48,9 @@
 		mmc_gpio {
 			gpio1 {
 				gpio = <23>;
-				func = <1>;
+				func = <1>;	/* sdio0 */
 				pull = <GPIO_PULL_UP>;
-				drvstr = <GPIO_10MA>;
+				drvstr = <DRV_TYPE_C>;
 				oe = <GPIO_OE_DISABLE>;
 				vm = <GPIO_VM_ENABLE>;
 				od_en = <GPIO_OD_DISABLE>;
@@ -59,9 +59,9 @@
 
 			gpio2 {
 				gpio = <24>;
-				func = <1>;
+				func = <1>;	/* sdio1 */
 				pull = <GPIO_PULL_UP>;
-				drvstr = <GPIO_10MA>;
+				drvstr = <DRV_TYPE_C>;
 				oe = <GPIO_OE_DISABLE>;
 				vm = <GPIO_VM_ENABLE>;
 				od_en = <GPIO_OD_DISABLE>;
@@ -70,9 +70,9 @@
 
 			gpio3 {
 				gpio = <25>;
-				func = <1>;
+				func = <1>;	/* sdio2 */
 				pull = <GPIO_PULL_UP>;
-				drvstr = <GPIO_10MA>;
+				drvstr = <DRV_TYPE_C>;
 				oe = <GPIO_OE_DISABLE>;
 				vm = <GPIO_VM_ENABLE>;
 				od_en = <GPIO_OD_DISABLE>;
@@ -81,9 +81,9 @@
 
 			gpio4 {
 				gpio = <26>;
-				func = <1>;
+				func = <1>;	/* sdio3 */
 				pull = <GPIO_PULL_UP>;
-				drvstr = <GPIO_10MA>;
+				drvstr = <DRV_TYPE_C>;
 				oe = <GPIO_OE_DISABLE>;
 				vm = <GPIO_VM_ENABLE>;
 				od_en = <GPIO_OD_DISABLE>;
@@ -92,9 +92,9 @@
 
 			gpio5 {
 				gpio = <27>;
-				func = <1>;
+				func = <1>;	/* sdio_clk */
 				pull = <GPIO_PULL_UP>;
-				drvstr = <GPIO_16MA>;
+				drvstr = <DRV_TYPE_A>;
 				oe = <GPIO_OE_DISABLE>;
 				vm = <GPIO_VM_ENABLE>;
 				od_en = <GPIO_OD_DISABLE>;
@@ -103,9 +103,9 @@
 
 			gpio6 {
 				gpio = <28>;
-				func = <1>;
+				func = <1>;	/* sdio_cmd */
 				pull = <GPIO_PULL_UP>;
-				drvstr = <GPIO_10MA>;
+				drvstr = <DRV_TYPE_C>;
 				oe = <GPIO_OE_DISABLE>;
 				vm = <GPIO_VM_ENABLE>;
 				od_en = <GPIO_OD_DISABLE>;
diff --git a/arch/arm/include/asm/arch-qca-common/qpic_nand.h b/arch/arm/include/asm/arch-qca-common/qpic_nand.h
index 64460c8..41f9fbc 100644
--- a/arch/arm/include/asm/arch-qca-common/qpic_nand.h
+++ b/arch/arm/include/asm/arch-qca-common/qpic_nand.h
@@ -130,6 +130,8 @@
 #define NAND_ERASED_CW_DETECT_STATUS_PAGE_ALL_ERASED		7
 #define NAND_ERASED_CW_DETECT_STATUS_CODEWORD_ALL_ERASED	6
 #define NAND_ERASED_CW_DETECT_STATUS_CODEWORD_ERASED		4
+#define NAND_CW_ERASED	(BIT(NAND_ERASED_CW_DETECT_STATUS_CODEWORD_ERASED) | \
+			 BIT(NAND_ERASED_CW_DETECT_STATUS_CODEWORD_ALL_ERASED))
 
 #define NAND_ERASED_CW_DETECT_CFG_RESET_CTRL		1
 #define NAND_ERASED_CW_DETECT_CFG_ACTIVATE_CTRL		0
@@ -159,6 +161,8 @@
 
 #define PROG_ERASE_OP_RESULT			(1 << 7)
 
+/* NAND buffer status */
+#define NAND_BUFFER_UNCORRECTABLE		(1 << 8)
 #define NUM_ERRORS_MASK				0x0000001f
 #define NUM_ERRORS(i)				((i) << 0)
 
@@ -288,8 +292,7 @@
 	NANDC_RESULT_TIMEOUT = 2,
 	NANDC_RESULT_PARAM_INVALID = 3,
 	NANDC_RESULT_DEV_NOT_SUPPORTED = 4,
-	NANDC_RESULT_BAD_PAGE = 5,
-	NANDC_RESULT_BAD_BLOCK = 6,
+	NANDC_RESULT_BAD_BLOCK = 5,
 } nand_result_t;
 
 enum nand_bad_block_value
@@ -405,10 +408,10 @@
 	struct qpic_nand_bam_pipes pipes;
 };
 
-enum nand_ecc_width
-{
-	NAND_WITH_4_BIT_ECC,
-	NAND_WITH_8_BIT_ECC,
+struct read_stats {
+	uint32_t flash_sts;
+	uint32_t buffer_sts;
+	uint32_t erased_cw_sts;
 };
 
 struct qpic_nand_dev {
@@ -420,7 +423,6 @@
 	unsigned block_size;
 	unsigned spare_size;
 	unsigned num_blocks;
-	enum nand_ecc_width ecc_width;
 	unsigned num_pages_per_blk;
 	unsigned num_pages_per_blk_mask;
 	unsigned widebus;
@@ -428,6 +430,9 @@
 	unsigned cw_size;
 	unsigned cws_per_page;
 	unsigned bad_blk_loc;
+	unsigned ecc_bytes_hw;
+	unsigned spare_bytes;
+	unsigned bbm_size;
 	unsigned dev_cfg;
 	uint32_t cfg0;
 	uint32_t cfg1;
@@ -440,7 +445,10 @@
 	unsigned char *pad_oob;
 	unsigned char *zero_page;
 	unsigned char *zero_oob;
+	unsigned char *tmp_datbuf;
+	unsigned char *tmp_oobbuf;
 	uint16_t timing_mode_support;
+	struct read_stats stats[QPIC_NAND_MAX_CWS_IN_PAGE];
 };
 
 void qpic_nand_init(void);
diff --git a/common/cmd_sf.c b/common/cmd_sf.c
index 42862d9..7815bdc 100644
--- a/common/cmd_sf.c
+++ b/common/cmd_sf.c
@@ -135,8 +135,7 @@
 
 	flash = dev_get_uclass_priv(new);
 #else
-	if (flash)
-		spi_flash_free(flash);
+	spi_flash_free(flash);
 
 	new = spi_flash_probe(bus, cs, speed, mode);
 	flash = new;
diff --git a/common/env_sf.c b/common/env_sf.c
index 8463f26..d079aaf 100644
--- a/common/env_sf.c
+++ b/common/env_sf.c
@@ -214,8 +214,6 @@
 	}
 
 err_read:
-	spi_flash_free(env_flash);
-	env_flash = NULL;
 out:
 	free(tmp_env1);
 	free(tmp_env2);
@@ -316,7 +314,6 @@
 	if (ret)
 		gd->env_valid = 1;
 out:
-	spi_flash_free(env_flash);
 	if (buf)
 		free(buf);
 	env_flash = NULL;
diff --git a/drivers/mtd/nand/qpic_nand.c b/drivers/mtd/nand/qpic_nand.c
index faa9498..dfaecd3 100644
--- a/drivers/mtd/nand/qpic_nand.c
+++ b/drivers/mtd/nand/qpic_nand.c
@@ -53,6 +53,10 @@
 static struct bam_instance bam;
 struct nand_ecclayout fake_ecc_layout;
 
+static int
+qpic_nand_read_page(struct mtd_info *mtd, uint32_t page,
+		    enum nand_cfg_value cfg_mode, struct mtd_oob_ops *ops);
+
 static const struct udevice_id qpic_ver_ids[] = {
 	{ .compatible = "qcom,qpic-nand.1.4.20", .data = QCA_QPIC_V1_4_20 },
 	{ .compatible = "qcom,qpic-nand.1.5.20", .data = QCA_QPIC_V1_5_20 },
@@ -171,48 +175,52 @@
 	qpic_nand_wait_for_cmd_exec(1);
 }
 
-static nand_result_t
-qpic_nand_check_status(struct mtd_info *mtd, uint32_t status)
+static int
+qpic_nand_check_read_status(struct mtd_info *mtd, struct read_stats *stats)
 {
-	uint32_t erase_sts;
+	uint32_t status = stats->flash_sts;
 
 	/* Check for errors */
-	if (status & NAND_FLASH_ERR) {
+	if (!(status & NAND_FLASH_ERR)) {
+		uint32_t corrected = stats->buffer_sts & NUM_ERRORS_MASK;
+		mtd->ecc_stats.corrected += corrected;
+		return corrected;
+	}
+
+	if (status & NAND_FLASH_MPU_ERR)
+		return -EPERM;
+
+	if (status & NAND_FLASH_TIMEOUT_ERR)
+		return -ETIMEDOUT;
+
+	if (stats->buffer_sts & NAND_BUFFER_UNCORRECTABLE) {
 		/* Check if this is an ECC error on an erased page. */
-		if (status & NAND_FLASH_OP_ERR) {
-			erase_sts = qpic_nand_read_reg(
-					NAND_ERASED_CW_DETECT_STATUS, 0);
-			if ((erase_sts &
-			    (1 << NAND_ERASED_CW_DETECT_STATUS_PAGE_ALL_ERASED))) {
-				/* Mask the OP ERROR. */
-				status &= ~NAND_FLASH_OP_ERR;
-				qpic_nand_erased_status_reset(ce_array, 0);
-			}
-		}
-
-		/* ECC error flagged on an erased page read.
-		 * Ignore and return success.
-		 */
-		if (!(status & NAND_FLASH_ERR))
-			return NANDC_RESULT_SUCCESS;
-
-		printf("Nand Flash error. Status = %d\n", status);
-
-		if (status & NAND_FLASH_OP_ERR) {
-			mtd->ecc_stats.failed++;
+		if ((stats->erased_cw_sts & NAND_CW_ERASED) != NAND_CW_ERASED)
 			return -EBADMSG;
-		}
+
+		return 0;
+	}
+
+	return -EIO;
+}
+
+static int
+qpic_nand_check_status(struct mtd_info *mtd, uint32_t status)
+{
+	/* Check for errors */
+	if (status & NAND_FLASH_ERR) {
+		printf("Nand Flash error. Status = %d\n", status);
 
 		if (status & NAND_FLASH_MPU_ERR)
 			return -EPERM;
 
 		if (status & NAND_FLASH_TIMEOUT_ERR)
 			return -ETIMEDOUT;
-		else
-			return NANDC_RESULT_FAILURE;
+
+		return -EIO;
 	}
 
-	return NANDC_RESULT_SUCCESS;
+	return 0;
 }
 
 static uint32_t
@@ -599,9 +607,9 @@
 	dev->timing_mode_support = param_page->timing_mode_support;
 
 	if (ecc_bits >= 8)
-		dev->ecc_width = NAND_WITH_8_BIT_ECC;
+		mtd->ecc_strength = 8;
 	else
-		dev->ecc_width = NAND_WITH_4_BIT_ECC;
+		mtd->ecc_strength = 4;
 
 	onfi_save_params_err:
 		return onfi_ret;
@@ -626,41 +634,39 @@
 	/* Codeword Size = UD_SIZE_BYTES + ECC_PARITY_SIZE_BYTES
 	 *                          + SPARE_SIZE_BYTES + Bad Block size
 	 */
-	if (dev->ecc_width & NAND_WITH_8_BIT_ECC) {
+	if (mtd->ecc_strength == 8) {
 		dev->cw_size = NAND_CW_SIZE_8_BIT_ECC;
 		/* Use 8-bit ecc */
 		dev->ecc_bch_cfg |= (1 << NAND_DEV0_ECC_MODE_SHIFT);
 
 		if (dev->widebus) {
-			/* spare size bytes in each CW */
-			dev->cfg0 |= (0 << NAND_DEV0_CFG0_SPARE_SZ_BYTES_SHIFT);
-			/* parity bytes in each CW */
-			dev->ecc_bch_cfg |= (14 <<
-					     NAND_DEV0_ECC_PARITY_SZ_BYTES_SHIFT);
+			dev->ecc_bytes_hw = 14;
+			dev->spare_bytes = 0;
+			dev->bbm_size = 2;
 		} else {
-			/* spare size bytes in each CW */
-			dev->cfg0 |= (2 << NAND_DEV0_CFG0_SPARE_SZ_BYTES_SHIFT);
-			/* parity bytes in each CW */
-			dev->ecc_bch_cfg |= (13 <<
-					     NAND_DEV0_ECC_PARITY_SZ_BYTES_SHIFT);
+			dev->ecc_bytes_hw = 13;
+			dev->spare_bytes = 2;
+			dev->bbm_size = 1;
 		}
 	} else {
 		dev->cw_size = NAND_CW_SIZE_4_BIT_ECC;
 		if (dev->widebus) {
-			/* spare size bytes in each CW */
-			dev->cfg0 |= (2 << NAND_DEV0_CFG0_SPARE_SZ_BYTES_SHIFT);
-			/* parity bytes in each CW */
-			dev->ecc_bch_cfg |= (8 <<
-					     NAND_DEV0_ECC_PARITY_SZ_BYTES_SHIFT);
+			dev->ecc_bytes_hw = 8;
+			dev->spare_bytes = 2;
+			dev->bbm_size = 2;
 		} else {
-			/* spare size bytes in each CW */
-			dev->cfg0 |= (4 << NAND_DEV0_CFG0_SPARE_SZ_BYTES_SHIFT);
-			/* parity bytes in each CW */
-			dev->ecc_bch_cfg |= (7 <<
-					     NAND_DEV0_ECC_PARITY_SZ_BYTES_SHIFT);
+			dev->ecc_bytes_hw = 7;
+			dev->spare_bytes = 4;
+			dev->bbm_size = 1;
 		}
 	}
 
+	/* spare size bytes in each CW */
+	dev->cfg0 |= dev->spare_bytes << NAND_DEV0_CFG0_SPARE_SZ_BYTES_SHIFT;
+	/* parity bytes in each CW */
+	dev->ecc_bch_cfg |=
+		dev->ecc_bytes_hw << NAND_DEV0_ECC_PARITY_SZ_BYTES_SHIFT;
+
 	qpic_oob_size = dev->cw_size * dev->cws_per_page - mtd->writesize;
 
 	if (mtd->oobsize < qpic_oob_size) {
@@ -1448,7 +1454,7 @@
 	else
 		qpic_nand_get_info_flash_dev(mtd, flash_dev);
 
-	dev->ecc_width = NAND_WITH_4_BIT_ECC;
+	mtd->ecc_strength = 4;
 
 	dev->num_blocks = mtd->size;
 	dev->num_blocks /= (dev->block_size);
@@ -1587,6 +1593,95 @@
 }
 
 static int
+qpic_nand_check_erased_buf(unsigned char *buf, int len, int bitflips_threshold)
+{
+	int bitflips = 0;
+
+	for (; len > 0; len--, buf++) {
+		bitflips += 8 - hweight8(*buf);
+		if (unlikely(bitflips > bitflips_threshold))
+			return -EBADMSG;
+	}
+
+	return bitflips;
+}
+
+/*
+ * Now following logic is being added to identify the erased codeword
+ * bitflips.
+ * 1. Maintain the bitmasks for the codewords which generated uncorrectable
+ *    error.
+ * 2. Read the raw data again in temp buffer and count the number of zeros.
+ *    Since spare bytes are unused in ECC layout and won’t affect ECC
+ *    correctability so no need to count number of zero in spare bytes.
+ * 3. If the number of zero is below ECC correctability then it can be
+ *    treated as erased CW. In this case, make all the data/oob of actual user
+ *    buffers as 0xff.
+ */
+static int
+qpic_nand_check_erased_page(struct mtd_info *mtd, uint32_t page,
+			    unsigned char *datbuf,
+			    unsigned char *oobbuf,
+			    unsigned int uncorrectable_err_cws,
+			    unsigned int *max_bitflips)
+{
+	struct mtd_oob_ops raw_page_ops;
+	struct qpic_nand_dev *dev = MTD_QPIC_NAND_DEV(mtd);
+	unsigned char *tmp_datbuf;
+	unsigned int tmp_datasize, datasize, oobsize;
+	int i, start_cw, last_cw, ret, data_bitflips;
+
+	raw_page_ops.mode = MTD_OPS_RAW;
+	raw_page_ops.len = mtd->writesize;
+	raw_page_ops.ooblen =  mtd->oobsize;
+	raw_page_ops.datbuf = dev->tmp_datbuf;
+	raw_page_ops.oobbuf = dev->tmp_oobbuf;
+	raw_page_ops.retlen = 0;
+	raw_page_ops.oobretlen = 0;
+
+	ret = qpic_nand_read_page(mtd, page, NAND_CFG_RAW, &raw_page_ops);
+	if (ret)
+		return ret;
+
+	start_cw = ffs(uncorrectable_err_cws) - 1;
+	last_cw = fls(uncorrectable_err_cws);
+
+	tmp_datbuf = dev->tmp_datbuf + start_cw * dev->cw_size;
+	tmp_datasize = dev->cw_size - dev->spare_bytes;
+	datasize = DATA_BYTES_IN_IMG_PER_CW;
+	datbuf += start_cw * datasize;
+
+	for (i = start_cw; i < last_cw;
+	     i++, datbuf += datasize, tmp_datbuf += dev->cw_size) {
+		if (!(BIT(i) & uncorrectable_err_cws))
+			continue;
+
+		data_bitflips =
+			qpic_nand_check_erased_buf(tmp_datbuf, tmp_datasize,
+						   mtd->ecc_strength);
+		if (data_bitflips < 0) {
+			mtd->ecc_stats.failed++;
+			continue;
+		}
+
+		*max_bitflips =
+			max_t(unsigned int, *max_bitflips, data_bitflips);
+
+		if (i == dev->cws_per_page - 1) {
+			oobsize = dev->cws_per_page << 2;
+			datasize = DATA_BYTES_IN_IMG_PER_CW - oobsize;
+			if (oobbuf)
+				memset(oobbuf, 0xff, oobsize);
+		}
+
+		if (datbuf)
+			memset(datbuf, 0xff, datasize);
+	}
+
+	return 0;
+}
+
+static int
 qpic_nand_read_page(struct mtd_info *mtd, uint32_t page,
 		    enum nand_cfg_value cfg_mode,
 		    struct mtd_oob_ops *ops)
@@ -1594,8 +1689,7 @@
 	struct qpic_nand_dev *dev = MTD_QPIC_NAND_DEV(mtd);
 	struct cfg_params params;
 	uint32_t ecc;
-	uint32_t flash_sts[QPIC_NAND_MAX_CWS_IN_PAGE];
-	uint32_t buffer_sts[QPIC_NAND_MAX_CWS_IN_PAGE];
+	struct read_stats *stats = dev->stats;
 	uint32_t addr_loc_0;
 	uint32_t addr_loc_1;
 	struct cmd_element *cmd_list_ptr = ce_array;
@@ -1606,12 +1700,12 @@
 	int nand_ret = NANDC_RESULT_SUCCESS;
 	uint8_t flags = 0;
 	uint32_t *cmd_list_temp = NULL;
-	uint32_t num_errors;
 	uint16_t data_bytes;
 	uint16_t ud_bytes_in_last_cw;
 	uint16_t oob_bytes;
-	unsigned char *buffer;
-	unsigned char *spareaddr;
+	unsigned char *buffer, *ops_datbuf = ops->datbuf;
+	unsigned char *spareaddr, *ops_oobbuf = ops->oobbuf;
+	unsigned int max_bitflips = 0, uncorrectable_err_cws = 0;
 
 	params.addr0 = page << 16;
 	params.addr1 = (page >> 16) & 0xff;
@@ -1735,7 +1829,7 @@
 		num_cmd_desc++;
 
 		bam_add_cmd_element(cmd_list_ptr, NAND_FLASH_STATUS,
-				   (uint32_t)((addr_t)&(flash_sts[i])),
+				   (uint32_t)((addr_t)&(stats[i].flash_sts)),
 				   CE_READ_TYPE);
 
 		cmd_list_temp = (uint32_t *)cmd_list_ptr;
@@ -1743,10 +1837,15 @@
 		cmd_list_ptr++;
 
 		bam_add_cmd_element(cmd_list_ptr, NAND_BUFFER_STATUS,
-				   (uint32_t)((addr_t)&(buffer_sts[i])),
+				    (uint32_t)((addr_t)&(stats[i].buffer_sts)),
 				   CE_READ_TYPE);
 		cmd_list_ptr++;
 
+		bam_add_cmd_element(cmd_list_ptr, NAND_ERASED_CW_DETECT_STATUS,
+				    (uint32_t)((addr_t)&(stats[i].erased_cw_sts)),
+				    CE_READ_TYPE);
+		cmd_list_ptr++;
+
 		if (i == (dev->cws_per_page) - 1) {
 			flags = BAM_DESC_CMD_FLAG | BAM_DESC_UNLOCK_FLAG;
 		} else
@@ -1794,22 +1893,39 @@
 
 	/* Check status */
 	for (i = 0; i < (dev->cws_per_page) ; i ++) {
-		flash_sts[i] = qpic_nand_check_status(mtd, flash_sts[i]);
-		if (flash_sts[i]) {
-			printf("NAND page read failed. page: %x status %x\n",
-				 page, flash_sts[i]);
+		if (cfg_mode == NAND_CFG_RAW)
+			nand_ret = qpic_nand_check_status(mtd,
+							  stats[i].flash_sts);
+		else
+			nand_ret = qpic_nand_check_read_status(mtd, &stats[i]);
+
+		if (nand_ret < 0) {
+			if (nand_ret == -EBADMSG) {
+				uncorrectable_err_cws |= BIT(i);
+				continue;
+			}
+
 			goto qpic_nand_read_page_error;
 		}
+
+		max_bitflips = max_t(unsigned int, max_bitflips, nand_ret);
 	}
 
-	for (i = 0; i < (dev->cws_per_page); i++) {
-		num_errors = buffer_sts[i];
-		num_errors &= NUM_ERRORS_MASK;
-		if(num_errors)
-			mtd->ecc_stats.corrected++;
+	if (uncorrectable_err_cws) {
+		nand_ret = qpic_nand_check_erased_page(mtd, page, ops_datbuf,
+						       ops_oobbuf,
+						       uncorrectable_err_cws,
+						       &max_bitflips);
+		if (nand_ret < 0)
+			goto qpic_nand_read_page_error;
 	}
+
+	return max_bitflips;
+
 qpic_nand_read_page_error:
-return nand_ret;
+	printf("NAND page read failed. page: %x status %x\n",
+	       page, nand_ret);
+	return nand_ret;
 }
 
 static int qpic_nand_read_oob(struct mtd_info *mtd, loff_t to,
@@ -1820,9 +1936,9 @@
 	struct nand_chip *chip = MTD_NAND_CHIP(mtd);
 	uint32_t start_page;
 	uint32_t num_pages;
-	loff_t offs;
-	uint32_t corrected;
 	enum nand_cfg_value cfg_mode;
+	unsigned int max_bitflips = 0;
+	unsigned int ecc_failures = mtd->ecc_stats.failed;
 
 	/* We don't support MTD_OOB_PLACE as of yet. */
 	if (ops->mode == MTD_OPS_PLACE_OOB)
@@ -1849,8 +1965,6 @@
 	start_page = ((to >> chip->page_shift));
 	num_pages = qpic_get_read_page_count(mtd, ops);
 
-	corrected = mtd->ecc_stats.corrected;
-
 	for (i = 0; i < num_pages; i++) {
 		struct mtd_oob_ops page_ops;
 		page_ops.mode = ops->mode;
@@ -1863,25 +1977,24 @@
 
 		ret = qpic_nand_read_page(mtd, start_page + i, cfg_mode,
 					  &page_ops);
-		if (ret == NANDC_RESULT_BAD_PAGE) {
-			offs = (start_page + i) << chip->page_shift;
-			qpic_nand_mark_badblock(mtd, offs);
-		}
-		if (ret) {
-			printf("qpic_nand_read: reading page %d failed with %d err \n",
-					start_page + i, ret);
+		if (ret < 0) {
+			printf("%s: reading page %d failed with %d err\n",
+			       __func__, start_page + i, ret);
 			return ret;
 		}
+
+		max_bitflips = max_t(unsigned int, max_bitflips, ret);
 		qpic_nand_read_datcopy(mtd, ops);
 		qpic_nand_read_oobcopy(mtd, ops);
 	}
 
-	if (mtd->ecc_stats.corrected != corrected) {
-		ret = -EUCLEAN;
-		return ret;
+	if (ecc_failures != mtd->ecc_stats.failed) {
+		printf("%s: ecc failure while reading from %llx\n",
+		       __func__, to);
+		return -EBADMSG;
 	}
 
-	return NANDC_RESULT_SUCCESS;
+	return max_bitflips;
 }
 
 /**
@@ -1989,7 +2102,6 @@
 	struct qpic_nand_dev *dev = MTD_QPIC_NAND_DEV(mtd);
 	int i, ret = NANDC_RESULT_SUCCESS;
 	struct nand_chip *chip = MTD_NAND_CHIP(mtd);
-	loff_t offs;
 	u_long start_page;
 	u_long num_pages;
 	enum nand_cfg_value cfg_mode;
@@ -2045,10 +2157,6 @@
 			printf("flash_write: write failure @ page %ld, block %ld\n",
 					start_page + i,
 				(start_page + i) / (dev->num_pages_per_blk));
-			if (ret == NANDC_RESULT_BAD_PAGE) {
-				offs = (start_page + i) << chip->page_shift;
-				qpic_nand_mark_badblock(mtd, offs);
-			}
 			goto out;
 		} else {
 			qpic_nand_write_datinc(mtd, ops);
@@ -2257,6 +2365,7 @@
 	mtd->_sync = qpic_nand_sync;
 
 	mtd->ecclayout = NULL;
+	mtd->bitflip_threshold = DIV_ROUND_UP(mtd->ecc_strength * 3, 4);
 
 	chip->page_shift = ffs(mtd->writesize) - 1;
 	chip->phys_erase_shift = ffs(mtd->erasesize) - 1;
@@ -2353,10 +2462,11 @@
 	dev = MTD_QPIC_NAND_DEV(mtd);
 	qpic_nand_mtd_params(mtd);
 
-	alloc_size = (mtd->writesize  /* For dev->pad_dat */
-		     + mtd->oobsize   /* For dev->pad_oob */
-		     + mtd->writesize /* For dev->zero_page */
-		     + mtd->oobsize); /* For dev->zero_oob */
+	/*
+	 * allocate buffer for dev->pad_dat, dev->pad_oob, dev->zero_page,
+	 * dev->zero_oob, dev->tmp_datbuf, dev->tmp_oobbuf
+	 */
+	alloc_size = 3 * (mtd->writesize + mtd->oobsize);
 
 	dev->buffers = malloc(alloc_size);
 	if (dev->buffers == NULL) {
@@ -2380,6 +2490,11 @@
 	memset(dev->zero_page, 0x0, mtd->writesize);
 	memset(dev->zero_oob, 0x0, mtd->oobsize);
 
+	dev->tmp_datbuf = buf;
+	buf += mtd->writesize;
+	dev->tmp_oobbuf = buf;
+	buf += mtd->oobsize;
+
 	/* Register with MTD subsystem. */
 	ret = nand_register(CONFIG_QPIC_NAND_NAND_INFO_IDX);
 	if (ret < 0) {
diff --git a/drivers/mtd/spi/sf_probe.c b/drivers/mtd/spi/sf_probe.c
index 0cafc29..071c42d 100644
--- a/drivers/mtd/spi/sf_probe.c
+++ b/drivers/mtd/spi/sf_probe.c
@@ -17,6 +17,8 @@
 
 #include "sf_internal.h"
 
+struct spi_flash *spi_flash_ptr[MAX_SF_BUS_NUM][MAX_SF_CS_NUM] = {NULL};
+
 /**
  * spi_flash_probe_slave() - Probe for a SPI flash device on a bus
  *
@@ -83,10 +85,16 @@
 {
 	struct spi_slave *bus;
 
+	if (spi_flash_ptr[busnum][cs] != NULL)
+		return spi_flash_ptr[busnum][cs];
+
 	bus = spi_setup_slave(busnum, cs, max_hz, spi_mode);
 	if (!bus)
 		return NULL;
-	return spi_flash_probe_tail(bus);
+
+	spi_flash_ptr[busnum][cs] = spi_flash_probe_tail(bus);
+
+	return spi_flash_ptr[busnum][cs];
 }
 
 #ifdef CONFIG_OF_SPI_FLASH
@@ -104,11 +112,15 @@
 
 void spi_flash_free(struct spi_flash *flash)
 {
+	if (flash) {
 #ifdef CONFIG_SPI_FLASH_MTD
-	spi_flash_mtd_unregister();
+		spi_flash_mtd_unregister();
 #endif
-	spi_free_slave(flash->spi);
-	free(flash);
+		spi_free_slave(flash->spi);
+		free(flash);
+	}
+
+	memset(spi_flash_ptr, NULL, sizeof(spi_flash_ptr));
 }
 
 #else /* defined CONFIG_DM_SPI_FLASH */
diff --git a/include/dt-bindings/qcom/gpio-ipq40xx.h b/include/dt-bindings/qcom/gpio-ipq40xx.h
index 57829d2..6074692 100644
--- a/include/dt-bindings/qcom/gpio-ipq40xx.h
+++ b/include/dt-bindings/qcom/gpio-ipq40xx.h
@@ -55,6 +55,12 @@
 #define GPIO_14MA	6
 #define GPIO_16MA	7
 
+/* GPIO TLMM for IPQ40XX: Drive Strength */
+#define DRV_TYPE_A	7
+#define DRV_TYPE_B	3
+#define DRV_TYPE_C	1
+#define DRV_TYPE_D	0
+
 /* GPIO TLMM: Status */
 #define GPIO_OE_DISABLE	0
 #define GPIO_OE_ENABLE	1
diff --git a/include/spi_flash.h b/include/spi_flash.h
index 4a51b5f..995d123 100644
--- a/include/spi_flash.h
+++ b/include/spi_flash.h
@@ -30,6 +30,8 @@
 #ifndef CONFIG_SF_DEFAULT_BUS
 # define CONFIG_SF_DEFAULT_BUS		0
 #endif
+#define MAX_SF_BUS_NUM			5
+#define MAX_SF_CS_NUM			5
 
 struct spi_slave;