

The i2c bus gets blocked when accessing an external device along with
Sierra MCU. When the devices are accessed through the /dev/i2c
interface the communication uses the i2c-qup Qualcomm driver while
communication with Sierra MCU uses i2c-core driver. During this
simultaneous access i2c bus is acquired by the slave and gets blocked.
The function for bus recovery is called from i2c-qup driver but the
bus does not recover. Found that replacing the old recovery mechanism
which relied on resetting the gpios with the new one found in
i2c-msm-qup to reset all the registers after caching the status works.


 drivers/i2c/busses/i2c-qup.c | 135 ++++++++++++++++++++++++++++++++++-
 1 file changed, 134 insertions(+), 1 deletion(-)

diff --git a/drivers/i2c/busses/i2c-qup.c b/drivers/i2c/busses/i2c-qup.c
index 1b351d6d73a..58cc3fcd1ca 100644
--- a/drivers/i2c/busses/i2c-qup.c
+++ b/drivers/i2c/busses/i2c-qup.c
@@ -137,14 +137,23 @@ enum {
 };
 
 #define QUP_MAX_CLK_STATE_RETRIES	300
+/* SWISTART */
+#ifdef CONFIG_SIERRA
+#define QUP_I2C_BUS_RECOVER_RETRY_COUNT 10
+#endif /* CONFIG_SIERRA */
+/* SWISTOP */
 
 static char const * const i2c_rsrcs[] = {"i2c_clk", "i2c_sda"};
 
+/* SWISTART */
+#ifndef CONFIG_SIERRA
 static struct gpiomux_setting recovery_config = {
 	.func = GPIOMUX_FUNC_GPIO,
 	.drv = GPIOMUX_DRV_8MA,
 	.pull = GPIOMUX_PULL_NONE,
 };
+#endif /* CONFIG_SIERRA */
+/* SWISTOP */
 
 struct qup_i2c_dev {
 	struct device                *dev;
@@ -177,6 +186,12 @@ struct qup_i2c_dev {
 	struct mutex                 mlock;
 	void                         *complete;
 	int                          i2c_gpios[ARRAY_SIZE(i2c_rsrcs)];
+/* SWISTART */
+#ifdef CONFIG_SIERRA
+	uint32_t                     status_flag;
+	uint32_t                     op_flag;
+#endif /* CONFIG_SIERRA */
+/* SWISTOP */
 };
 
 #ifdef DEBUG
@@ -286,6 +301,12 @@ intr_done:
 			irq, status, status1);
 	qup_print_status(dev);
 	dev->err = err;
+/* SWISTART */
+#ifdef CONFIG_SIERRA
+	dev->status_flag = status;
+	dev->op_flag = op_flgs;
+#endif /* CONFIG_SIERRA */
+/* SWISTOP */
 	complete(dev->complete);
 	return IRQ_HANDLED;
 }
@@ -694,7 +715,8 @@ qup_set_wr_mode(struct qup_i2c_dev *dev, int rem)
 	return ret;
 }
 
-
+/* SWISTART */
+#ifndef CONFIG_SIERRA
 static void qup_i2c_recover_bus_busy(struct qup_i2c_dev *dev)
 {
 	int i;
@@ -771,6 +793,96 @@ recovery_end:
 	enable_irq(dev->err_irq);
 }
 
+#else /* CONFIG_SIERRA */
+static int qup_i2c_reset_swi(struct qup_i2c_dev *dev)
+{
+	int ret;
+
+	dev_dbg(dev->dev, "%s Trying to reset the states", __func__);
+
+	/* sw reset */
+	writel_relaxed(1, dev->base + QUP_SW_RESET);
+	ret = qup_i2c_poll_state(dev, QUP_RESET_STATE, false);
+	if (ret) {
+		dev_err(dev->dev, "QUP Busy:Trying to recover\n");
+		return ret;
+	}
+
+	/* Initialize QUP registers */
+	writel_relaxed(0, dev->base + QUP_CONFIG);
+	writel_relaxed(QUP_OPERATIONAL_RESET, dev->base + QUP_OPERATIONAL);
+	writel_relaxed(QUP_STATUS_ERROR_FLAGS, dev->base + QUP_ERROR_FLAGS_EN);
+
+	writel_relaxed(I2C_MINI_CORE | I2C_N_VAL, dev->base + QUP_CONFIG);
+
+	/* Initialize I2C mini core registers */
+	writel_relaxed(0, dev->base + QUP_I2C_CLK_CTL);
+	writel_relaxed(QUP_I2C_STATUS_RESET, dev->base + QUP_I2C_STATUS);
+
+	/* Make sure QUP I2C core reset registers are written */
+	wmb();
+
+	dev_dbg(dev->dev, "%s Completed resetting the states", __func__);
+	return ret;
+}
+
+static int qup_i2c_try_recover_bus_busy_swi(struct qup_i2c_dev *dev)
+{
+	int ret;
+	ulong min_sleep_usec;
+
+	dev_dbg(dev->dev, "%s Trying to recover the bus", __func__);
+
+	disable_irq(dev->err_irq);
+
+	qup_i2c_reset_swi(dev);
+
+	ret = qup_update_state(dev, QUP_RUN_STATE);
+	if (ret < 0) {
+		dev_err(dev->dev, "error: bus clear fail to set run state\n");
+		goto recovery_end;
+	}
+
+	writel_relaxed(dev->clk_ctl, dev->base + QUP_I2C_CLK_CTL);
+
+	/* Make sure QUP I2C core reset registers are written */
+	wmb();
+
+	/*
+	 * wait for bus status to change (9 clock pulse cycles).
+	 * min_time = 9 clock *10  (1000% margin)
+	 * max_time = 10* min_time
+	 */
+	min_sleep_usec =
+		max_t(ulong, (9 * 10 * USEC_PER_SEC) / dev->pdata->clk_freq,
+			100);
+	usleep_range(min_sleep_usec, min_sleep_usec * 10);
+
+recovery_end:
+	enable_irq(dev->err_irq);
+	return ret;
+}
+
+static void qup_i2c_recover_bus_busy_swi(struct qup_i2c_dev *dev)
+{
+	u32 bus_active, status;
+	int retry = 0;
+	dev_info(dev->dev, "Executing bus recovery procedure (9 clk pulse) in %s \n", __func__);
+
+	do {
+		qup_i2c_try_recover_bus_busy_swi(dev);
+		status     = readl_relaxed(dev->base + QUP_I2C_STATUS);
+		bus_active = status & I2C_STATUS_BUS_ACTIVE;
+		if (++retry >= QUP_I2C_BUS_RECOVER_RETRY_COUNT)
+			break;
+	} while (bus_active);
+
+	dev_info(dev->dev, "Bus recovery %s after %d retries\n",
+		(bus_active) ? "fail" : "success", retry);
+}
+#endif /* CONFIG_SIERRA */
+/* SWISTOP */
+
 static int
 qup_i2c_xfer(struct i2c_adapter *adap, struct i2c_msg msgs[], int num)
 {
@@ -987,12 +1099,21 @@ qup_i2c_xfer(struct i2c_adapter *adap, struct i2c_msg msgs[], int num)
 			timeout = wait_for_completion_timeout(&complete,
 					msecs_to_jiffies(dev->out_fifo_sz));
 			if (!timeout) {
+/* SWISTART */
+#ifndef CONFIG_SIERRA
 				uint32_t istatus = readl_relaxed(dev->base +
 							QUP_I2C_STATUS);
 				uint32_t qstatus = readl_relaxed(dev->base +
 							QUP_ERROR_FLAGS);
 				uint32_t op_flgs = readl_relaxed(dev->base +
 							QUP_OPERATIONAL);
+#else /* CONFIG_SIERRA */
+				uint32_t istatus = dev->status_flag;
+				uint32_t qstatus = readl_relaxed(dev->base +
+							QUP_ERROR_FLAGS);
+				uint32_t op_flgs = dev->op_flag;
+#endif /* CONFIG_SIERRA */
+/* SWISTOP */
 
 				/*
 				 * Dont wait for 1 sec if i2c sees the bus
@@ -1007,7 +1128,13 @@ qup_i2c_xfer(struct i2c_adapter *adap, struct i2c_msg msgs[], int num)
 					if (timeout)
 						goto timeout_err;
 				}
+/* SWISTART */
+#ifndef CONFIG_SIERRA
 				qup_i2c_recover_bus_busy(dev);
+#else /* CONFIG_SIERRA */
+				qup_i2c_recover_bus_busy_swi(dev);
+#endif /* CONFIG_SIERRA */
+/* SWISTOP */
 				dev_err(dev->dev,
 					"Transaction timed out, SL-AD = 0x%x\n",
 					dev->msg->addr);
@@ -1043,7 +1170,13 @@ timeout_err:
 					 * So you may call recover-bus-busy when
 					 * this error happens
 					 */
+/* SWISTART */
+#ifndef CONFIG_SIERRA
 					qup_i2c_recover_bus_busy(dev);
+#else /* CONFIG_SIERRA */
+					qup_i2c_recover_bus_busy_swi(dev);
+#endif /* CONFIG_SIERRA */
+/* SWISTOP */
 				}
 				ret = -dev->err;
 				goto out_err;
-- 
2.20.1

