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

Commit 677936e0 authored by Ankit Gupta's avatar Ankit Gupta
Browse files

i2c-msm-v2: wait for qup core to be in valid state after reset



Failure to wait until state transition is complete before accessing
QUP registers or turning off the clock may cause bus error and
even a crash. Additionally, the bam tags data must be flushed prior
to being disconnected at the end of transfer. Failure to do so may
result in unexpected behavior.

Change-Id: Ife423df8752cc318e8e24bef588bd69ebff26ab1
Signed-off-by: default avatarAnkit Gupta <ankgupta@codeaurora.org>
parent 299a6262
Loading
Loading
Loading
Loading
+22 −22
Original line number Diff line number Diff line
@@ -2278,12 +2278,6 @@ static int i2c_msm_bam_init(struct i2c_msm_ctrl *ctrl)
	if (bam->is_init)
		return 0;

	ret = (*ctrl->ver.init)(ctrl);
	if (ret) {
		dev_err(ctrl->dev, "error on initializing QUP registers\n");
		return ret;
	}

	i2c_msm_dbg(ctrl, MSM_DBG, "initializing BAM@0x%p", bam);

	if (bam->is_core_init)
@@ -2729,12 +2723,18 @@ static irqreturn_t i2c_msm_qup_isr(int irq, void *devid)
		/* Dump the register values before reset the core */
		if (ctrl->dbgfs.dbg_lvl >= MSM_DBG)
			i2c_msm_dbg_qup_reg_dump(ctrl);
		/*
		 * HW workaround: when interrupt is level triggerd, more than
		 * one interrupt may fire in error cases. Thus we reset the
		 * core immidiatly in the ISR to ward off the next interrupt.

		/* Flush for the tags in case of an error and BAM Mode*/
		if (ctrl->xfer.mode_id == I2C_MSM_XFER_MODE_BAM)
			writel_relaxed(QUP_I2C_FLUSH, ctrl->rsrcs.base
								+ QUP_STATE);

		/* HW workaround: when interrupt is level triggerd, more
		 * than one interrupt may fire in error cases. Thus we
		 * reset the core immidiatly in the ISR to ward off the
		 * next interrupt.
		 */
		writel_relaxed(1, ctrl->rsrcs.base + QUP_SW_RESET);
		i2c_msm_qup_sw_reset(ctrl);

		need_wmb        = true;
		signal_complete = true;
@@ -2978,8 +2978,6 @@ static void i2c_msm_qup_teardown(struct i2c_msm_ctrl *ctrl)

static int i2c_msm_qup_post_xfer(struct i2c_msm_ctrl *ctrl, int err)
{
	bool need_reset = false;

	/* poll until bus is released */
	if (i2c_msm_qup_poll_bus_active_unset(ctrl)) {
		if ((ctrl->xfer.err & I2C_MSM_ERR_ARB_LOST) ||
@@ -2994,25 +2992,27 @@ static int i2c_msm_qup_post_xfer(struct i2c_msm_ctrl *ctrl, int err)
		}
	}

	/* flush bam data and reset the qup core in timeout error.
	 * for other error case, its handled by the ISR
	 */
	if (ctrl->xfer.err & I2C_MSM_ERR_TIMEOUT) {
		/* Flush for the BAM registers */
		if (ctrl->xfer.mode_id == I2C_MSM_XFER_MODE_BAM)
			writel_relaxed(QUP_I2C_FLUSH, ctrl->rsrcs.base
								+ QUP_STATE);

		/* reset the sw core */
		i2c_msm_qup_sw_reset(ctrl);
		err = -ETIMEDOUT;
		need_reset = true;
	}

	if (ctrl->xfer.err & I2C_MSM_ERR_BUS_ERR) {
		if (!err)
			err = -EIO;
		need_reset = true;
	}

	if (ctrl->xfer.err & I2C_MSM_ERR_NACK) {
		writel_relaxed(QUP_I2C_FLUSH,  ctrl->rsrcs.base + QUP_STATE);
	if (ctrl->xfer.err & I2C_MSM_ERR_NACK)
		err = -ENOTCONN;
		need_reset = true;
	}

	if (need_reset)
		i2c_msm_qup_init(ctrl);

	return err;
}