fg-memif: update IMA error handling and clear sequence

Based on the hardware documentation, update the IMA error
handling and clear sequence. In addition, check for DMA errors
and clear it before SRAM transactions begin. Also, check for IMA
hardware status to run the IMA clear sequence during ima_init and
not just based on IMA exception status alone. This is to help
with FG SRAM access to resume again properly in case of an error
encountered.

Change-Id: I583fa51599a1cbbd029cb45c075429730d2e071b
Signed-off-by: Subbaraman Narayanamurthy <subbaram@codeaurora.org>
diff --git a/drivers/power/qcom-charger/fg-memif.c b/drivers/power/qcom-charger/fg-memif.c
index c271b24..88c96bc 100644
--- a/drivers/power/qcom-charger/fg-memif.c
+++ b/drivers/power/qcom-charger/fg-memif.c
@@ -64,44 +64,90 @@
 
 static int fg_run_iacs_clear_sequence(struct fg_chip *chip)
 {
-	u8 tmp;
-	int rc;
+	u8 val, hw_sts, exp_sts;
+	int rc, tries = 250;
 
 	/*
 	 * Values to write for running IACS clear sequence comes from
 	 * hardware documentation.
 	 */
-	rc = fg_masked_write(chip, MEM_IF_IMA_CFG(chip), IACS_CLR_BIT,
-				IACS_CLR_BIT);
+	rc = fg_masked_write(chip, MEM_IF_IMA_CFG(chip),
+			IACS_CLR_BIT | STATIC_CLK_EN_BIT,
+			IACS_CLR_BIT | STATIC_CLK_EN_BIT);
 	if (rc < 0) {
 		pr_err("failed to write 0x%04x, rc=%d\n", MEM_IF_IMA_CFG(chip),
 			rc);
 		return rc;
 	}
 
-	tmp = 0x4;
-	rc = fg_write(chip, MEM_IF_ADDR_MSB(chip), &tmp, 1);
+	rc = fg_config_access_mode(chip, FG_READ, false);
 	if (rc < 0) {
-		pr_err("failed to write 0x%04x, rc=%d\n", MEM_IF_ADDR_LSB(chip),
-			rc);
+		pr_err("failed to write to 0x%04x, rc=%d\n",
+			MEM_IF_IMA_CTL(chip), rc);
 		return rc;
 	}
 
-	tmp = 0x0;
-	rc = fg_write(chip, MEM_IF_WR_DATA3(chip), &tmp, 1);
+	rc = fg_masked_write(chip, MEM_IF_MEM_INTF_CFG(chip),
+				MEM_ACCESS_REQ_BIT | IACS_SLCT_BIT,
+				MEM_ACCESS_REQ_BIT | IACS_SLCT_BIT);
 	if (rc < 0) {
-		pr_err("failed to write 0x%04x, rc=%d\n", MEM_IF_WR_DATA3(chip),
-			rc);
+		pr_err("failed to set ima_req_access bit rc=%d\n", rc);
 		return rc;
 	}
 
-	rc = fg_read(chip, MEM_IF_RD_DATA3(chip), &tmp, 1);
-	if (rc < 0) {
-		pr_err("failed to read 0x%04x, rc=%d\n", MEM_IF_RD_DATA3(chip),
-			rc);
-		return rc;
+	/* Delay for the clock to reach FG */
+	usleep_range(35, 40);
+
+	while (1) {
+		val = 0;
+		rc = fg_write(chip, MEM_IF_ADDR_MSB(chip), &val, 1);
+		if (rc < 0) {
+			pr_err("failed to write 0x%04x, rc=%d\n",
+				MEM_IF_ADDR_MSB(chip), rc);
+			return rc;
+		}
+
+		val = 0;
+		rc = fg_write(chip, MEM_IF_WR_DATA3(chip), &val, 1);
+		if (rc < 0) {
+			pr_err("failed to write 0x%04x, rc=%d\n",
+				MEM_IF_WR_DATA3(chip), rc);
+			return rc;
+		}
+
+		rc = fg_read(chip, MEM_IF_RD_DATA3(chip), &val, 1);
+		if (rc < 0) {
+			pr_err("failed to read 0x%04x, rc=%d\n",
+				MEM_IF_RD_DATA3(chip), rc);
+			return rc;
+		}
+
+		/* Delay for IMA hardware to clear */
+		usleep_range(35, 40);
+
+		rc = fg_read(chip, MEM_IF_IMA_HW_STS(chip), &hw_sts, 1);
+		if (rc < 0) {
+			pr_err("failed to read ima_hw_sts rc=%d\n", rc);
+			return rc;
+		}
+
+		if (hw_sts != 0)
+			continue;
+
+		rc = fg_read(chip, MEM_IF_IMA_EXP_STS(chip), &exp_sts, 1);
+		if (rc < 0) {
+			pr_err("failed to read ima_exp_sts rc=%d\n", rc);
+			return rc;
+		}
+
+		if (exp_sts == 0 || !(--tries))
+			break;
 	}
 
+	if (!tries)
+		pr_err("Failed to clear the error? hw_sts: %x exp_sts: %d\n",
+			hw_sts, exp_sts);
+
 	rc = fg_masked_write(chip, MEM_IF_IMA_CFG(chip), IACS_CLR_BIT, 0);
 	if (rc < 0) {
 		pr_err("failed to write 0x%04x, rc=%d\n", MEM_IF_IMA_CFG(chip),
@@ -109,14 +155,65 @@
 		return rc;
 	}
 
+	udelay(5);
+
+	rc = fg_masked_write(chip, MEM_IF_MEM_INTF_CFG(chip),
+				MEM_ACCESS_REQ_BIT | IACS_SLCT_BIT, 0);
+	if (rc < 0) {
+		pr_err("failed to write to 0x%04x, rc=%d\n",
+			MEM_IF_MEM_INTF_CFG(chip), rc);
+		return rc;
+	}
+
+	/* Delay before next transaction is attempted */
+	usleep_range(35, 40);
 	fg_dbg(chip, FG_SRAM_READ | FG_SRAM_WRITE, "IACS clear sequence complete\n");
 	return rc;
 }
 
-static int fg_check_for_ima_errors(struct fg_chip *chip)
+int fg_clear_dma_errors_if_any(struct fg_chip *chip)
+{
+	int rc;
+	u8 dma_sts;
+
+	rc = fg_read(chip, MEM_IF_DMA_STS(chip), &dma_sts, 1);
+	if (rc < 0) {
+		pr_err("failed to read addr=0x%04x, rc=%d\n",
+			MEM_IF_DMA_STS(chip), rc);
+		return rc;
+	}
+	fg_dbg(chip, FG_STATUS, "dma_sts: %x\n", dma_sts);
+
+	if (dma_sts & (DMA_WRITE_ERROR_BIT | DMA_READ_ERROR_BIT)) {
+		rc = fg_masked_write(chip, MEM_IF_DMA_CTL(chip),
+				DMA_CLEAR_LOG_BIT, DMA_CLEAR_LOG_BIT);
+		if (rc < 0) {
+			pr_err("failed to write addr=0x%04x, rc=%d\n",
+				MEM_IF_DMA_CTL(chip), rc);
+			return rc;
+		}
+	}
+
+	return 0;
+}
+
+int fg_clear_ima_errors_if_any(struct fg_chip *chip, bool check_hw_sts)
 {
 	int rc = 0;
 	u8 err_sts, exp_sts = 0, hw_sts = 0;
+	bool run_err_clr_seq = false;
+
+	rc = fg_read(chip, MEM_IF_IMA_EXP_STS(chip), &exp_sts, 1);
+	if (rc < 0) {
+		pr_err("failed to read ima_exp_sts rc=%d\n", rc);
+		return rc;
+	}
+
+	rc = fg_read(chip, MEM_IF_IMA_HW_STS(chip), &hw_sts, 1);
+	if (rc < 0) {
+		pr_err("failed to read ima_hw_sts rc=%d\n", rc);
+		return rc;
+	}
 
 	rc = fg_read(chip, MEM_IF_IMA_ERR_STS(chip), &err_sts, 1);
 	if (rc < 0) {
@@ -124,22 +221,30 @@
 		return rc;
 	}
 
-	if (err_sts & (ADDR_STBL_ERR_BIT | WR_ACS_ERR_BIT | RD_ACS_ERR_BIT)) {
-		rc = fg_read(chip, MEM_IF_IMA_EXP_STS(chip), &exp_sts, 1);
-		if (rc < 0) {
-			pr_err("failed to read ima_exp_sts rc=%d\n", rc);
-			return rc;
+	fg_dbg(chip, FG_STATUS, "ima_err_sts=%x ima_exp_sts=%x ima_hw_sts=%x\n",
+		err_sts, exp_sts, hw_sts);
+
+	if (check_hw_sts) {
+		/*
+		 * Lower nibble should be equal to upper nibble before SRAM
+		 * transactions begins from SW side. If they are unequal, then
+		 * the error clear sequence should be run irrespective of IMA
+		 * exception errors.
+		 */
+		if ((hw_sts & 0x0F) != hw_sts >> 4) {
+			pr_err("IMA HW not in correct state, hw_sts=%x\n",
+				hw_sts);
+			run_err_clr_seq = true;
 		}
+	}
 
-		rc = fg_read(chip, MEM_IF_IMA_HW_STS(chip), &hw_sts, 1);
-		if (rc < 0) {
-			pr_err("failed to read ima_hw_sts rc=%d\n", rc);
-			return rc;
-		}
+	if (exp_sts & (IACS_ERR_BIT | XCT_TYPE_ERR_BIT | DATA_RD_ERR_BIT |
+		DATA_WR_ERR_BIT | ADDR_BURST_WRAP_BIT | ADDR_STABLE_ERR_BIT)) {
+		pr_err("IMA exception bit set, exp_sts=%x\n", exp_sts);
+		run_err_clr_seq = true;
+	}
 
-		pr_err("ima_err_sts=%x ima_exp_sts=%x ima_hw_sts=%x\n",
-			err_sts, exp_sts, hw_sts);
-
+	if (run_err_clr_seq) {
 		/* clear the error */
 		rc = fg_run_iacs_clear_sequence(chip);
 		if (rc < 0) {
@@ -156,7 +261,7 @@
 
 static int fg_check_iacs_ready(struct fg_chip *chip)
 {
-	int rc = 0, timeout = 250;
+	int rc = 0, tries = 250;
 	u8 ima_opr_sts = 0;
 
 	/*
@@ -176,17 +281,17 @@
 		if (ima_opr_sts & IACS_RDY_BIT)
 			break;
 
-		if (!(--timeout))
+		if (!(--tries))
 			break;
 
 		/* delay for iacs_ready to be asserted */
 		usleep_range(5000, 7000);
 	}
 
-	if (!timeout) {
+	if (!tries) {
 		pr_err("IACS_RDY not set\n");
-
-		rc = fg_check_for_ima_errors(chip);
+		/* check for error condition */
+		rc = fg_clear_ima_errors_if_any(chip, false);
 		if (rc < 0) {
 			pr_err("Failed to check for ima errors rc=%d\n", rc);
 			return rc;
@@ -250,7 +355,7 @@
 		}
 
 		/* check for error condition */
-		rc = fg_check_for_ima_errors(chip);
+		rc = fg_clear_ima_errors_if_any(chip, false);
 		if (rc < 0) {
 			pr_err("Failed to check for ima errors rc=%d\n", rc);
 			return rc;
@@ -296,7 +401,7 @@
 		offset = 0;
 
 		/* check for error condition */
-		rc = fg_check_for_ima_errors(chip);
+		rc = fg_clear_ima_errors_if_any(chip, false);
 		if (rc < 0) {
 			pr_err("Failed to check for ima errors rc=%d\n", rc);
 			return rc;
@@ -581,5 +686,19 @@
 		return rc;
 	}
 
+	/* Clear DMA errors if any before clearing IMA errors */
+	rc = fg_clear_dma_errors_if_any(chip);
+	if (rc < 0) {
+		pr_err("Error in checking DMA errors rc:%d\n", rc);
+		return rc;
+	}
+
+	/* Clear IMA errors if any before SRAM transactions can begin */
+	rc = fg_clear_ima_errors_if_any(chip, true);
+	if (rc < 0 && rc != -EAGAIN) {
+		pr_err("Error in checking IMA errors rc:%d\n", rc);
+		return rc;
+	}
+
 	return 0;
 }