Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 052480e3 authored by Mukesh Kumar Savaliya's avatar Mukesh Kumar Savaliya Committed by Gerrit - the friendly Code Review server
Browse files

i2c: i2c-msm-geni: WAR to halt cancel if IOS not in good state



This change halts cancel command operation if IOS lines not in
good state for i2c due to Power or slave side issue. Perform cancel
once IO lines comes to good state. Till that i2c transfer will not
be servied.

This is a workaround fix if the abort is failing after cancel and
never recovers.

Change-Id: Icca5904d0d3df7432b948343ef9396cb25d93cd0
Signed-off-by: default avatarMukesh Kumar Savaliya <msavaliy@codeaurora.org>
parent 8aafd51f
Loading
Loading
Loading
Loading
+69 −15
Original line number Diff line number Diff line
@@ -134,6 +134,7 @@ struct geni_i2c_dev {
	bool first_resume;
	bool gpi_reset;
	bool disable_dma_mode;
	bool prev_cancel_pending; //Halt cancel till IOS in good state
};

static struct geni_i2c_dev *gi2c_dev_dbg[MAX_SE];
@@ -241,10 +242,45 @@ static void geni_i2c_err(struct geni_i2c_dev *gi2c, int err)
	gi2c->err = gi2c_log[err].err;
}

static int geni_i2c_prepare(struct geni_i2c_dev *gi2c)
static int do_pending_cancel(struct geni_i2c_dev *gi2c)
{
	int timeout = 0;
	u32 geni_ios = 0;

	geni_ios = geni_read_reg_nolog(gi2c->base, SE_GENI_IOS);
	if ((geni_ios & 0x3) != 0x3) {
		GENI_SE_ERR(gi2c->ipcl, true, gi2c->dev,
			"%s: Can't do pending cancel, IOS bad state: 0x%x\n",
			__func__, geni_ios);
		return -EINVAL;
	}

	if (gi2c->se_mode == GSI_ONLY) {
		dmaengine_terminate_all(gi2c->tx_c);
		gi2c->cfg_sent = 0;
	} else {
		reinit_completion(&gi2c->xfer);
		geni_cancel_m_cmd(gi2c->base);
		timeout = wait_for_completion_timeout(&gi2c->xfer, HZ);
		if (!timeout) {
			GENI_SE_ERR(gi2c->ipcl, true, gi2c->dev,
				"%s:Pending Cancel failed\n", __func__);
			reinit_completion(&gi2c->xfer);
			geni_abort_m_cmd(gi2c->base);
			timeout = wait_for_completion_timeout(&gi2c->xfer, HZ);
			if (!timeout)
				GENI_SE_ERR(gi2c->ipcl, true, gi2c->dev,
				"%s:Abort failed\n", __func__);
		}
	}
	gi2c->prev_cancel_pending = false;
	GENI_SE_ERR(gi2c->ipcl, true, gi2c->dev,
			"%s: Pending Cancel done\n", __func__);
	return timeout;
}

static int geni_i2c_prepare(struct geni_i2c_dev *gi2c)
{
	if (gi2c->se_mode == UNINITIALIZED) {
		int proto = get_se_proto(gi2c->base);
		u32 se_mode;
@@ -256,16 +292,6 @@ static int geni_i2c_prepare(struct geni_i2c_dev *gi2c)
			return -ENXIO;
		}

		geni_ios = geni_read_reg_nolog(gi2c->base, SE_GENI_IOS);
		if ((geni_ios & 0x3) != 0x3) { //SCL:b'1, SDA:b'0
			GENI_SE_DBG(gi2c->ipcl, true, gi2c->dev,
			"IO lines not in good state, Check power to slave\n");

			if (!gi2c->is_le_vm)
				se_geni_resources_off(&gi2c->i2c_rsc);
			return -ENXIO;
		}

		se_mode = readl_relaxed(gi2c->base +
					GENI_IF_FIFO_DISABLE_RO);
		if (se_mode) {
@@ -933,6 +959,8 @@ static int geni_i2c_gsi_xfer(struct i2c_adapter *adap, struct i2c_msg msgs[],
		timeout = wait_for_completion_timeout(&gi2c->xfer,
						gi2c->xfer_timeout);
		if (!timeout) {
			u32 geni_ios = 0;

			GENI_SE_ERR(gi2c->ipcl, true, gi2c->dev,
				"I2C gsi xfer timeout:%u flags:%d addr:0x%x\n",
				gi2c->xfer_timeout, gi2c->cur->flags,
@@ -940,6 +968,15 @@ static int geni_i2c_gsi_xfer(struct i2c_adapter *adap, struct i2c_msg msgs[],
			geni_se_dump_dbg_regs(&gi2c->i2c_rsc, gi2c->base,
						gi2c->ipcl);
			gi2c->err = -ETIMEDOUT;

			/* WAR: Set flag to mark cancel pending if IOS stuck */
			geni_ios = geni_read_reg_nolog(gi2c->base, SE_GENI_IOS);
			if ((geni_ios & 0x3) != 0x3) { //SCL:b'1, SDA:b'0
				GENI_SE_DBG(gi2c->ipcl, true, gi2c->dev,
					"%s: IO lines not in good state\n", __func__);
				gi2c->prev_cancel_pending = true;
				goto geni_i2c_gsi_cancel_pending;
			}
		}
geni_i2c_err_prep_sg:
		if (gi2c->err) {
@@ -962,6 +999,7 @@ static int geni_i2c_gsi_xfer(struct i2c_adapter *adap, struct i2c_msg msgs[],
			/* Resend cfg tre for every new message on shared se */
			gi2c->cfg_sent = 0;

geni_i2c_gsi_cancel_pending:
		if (msgs[i].flags & I2C_M_RD)
			geni_se_iommu_unmap_buf(rx_dev, &gi2c->rx_ph,
				msgs[i].len, DMA_FROM_DEVICE);
@@ -1007,6 +1045,13 @@ static int geni_i2c_xfer(struct i2c_adapter *adap,
		}
	}

	// WAR : Complete previous pending cancel cmd
	if (gi2c->prev_cancel_pending) {
		ret = do_pending_cancel(gi2c);
		if (ret)
			return ret; //Don't perform xfer is cancel failed
	}

	GENI_SE_DBG(gi2c->ipcl, false, gi2c->dev,
		"n:%d addr:0x%x\n", num, msgs[0].addr);

@@ -1124,9 +1169,20 @@ static int geni_i2c_xfer(struct i2c_adapter *adap,
		timeout = wait_for_completion_timeout(&gi2c->xfer,
						gi2c->xfer_timeout);
		if (!timeout) {
			u32 geni_ios = 0;

			GENI_SE_ERR(gi2c->ipcl, true, gi2c->dev,
				"I2C xfer timeout: %d\n", gi2c->xfer_timeout);
			geni_i2c_err(gi2c, GENI_TIMEOUT);

			/* WAR: Set flag to mark cancel pending if IOS bad */
			geni_ios = geni_read_reg_nolog(gi2c->base, SE_GENI_IOS);
			if ((geni_ios & 0x3) != 0x3) { //SCL:b'1, SDA:b'0
				GENI_SE_DBG(gi2c->ipcl, true, gi2c->dev,
					"%s: IO lines not in good state\n", __func__);
				gi2c->prev_cancel_pending = true;
				goto geni_i2c_txn_ret;
			}
		}

		if (gi2c->err) {
@@ -1453,19 +1509,17 @@ static int geni_i2c_runtime_resume(struct device *dev)
	if (!gi2c->is_le_vm) {
		/* Do not control clk/gpio/icb for LE-VM */
		ret = se_geni_resources_on(&gi2c->i2c_rsc);

		if (ret)
			return ret;

		ret = geni_i2c_prepare(gi2c);
		if (ret) {
			dev_err(gi2c->dev, "I2C prepare failed\n");
			dev_err(gi2c->dev, "I2C prepare failed: %d\n", ret);
			return ret;
		}

		if (gi2c->se_mode == FIFO_SE_DMA)
			enable_irq(gi2c->irq);

	} else if (!gi2c->first_resume) {
		/*
		 * For first resume call in le, do nothing, and in
@@ -1475,7 +1529,7 @@ static int geni_i2c_runtime_resume(struct device *dev)
		 */
		ret = geni_i2c_prepare(gi2c);
		if (ret) {
			dev_err(gi2c->dev, "I2C prepare failed\n");
			dev_err(gi2c->dev, "I2C prepare failed:%d\n", ret);
			return ret;
		}