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

Commit eec8e250 authored by Linux Build Service Account's avatar Linux Build Service Account Committed by Gerrit - the friendly Code Review server
Browse files

Merge "i2c-msm-v2: add missing error conditions"

parents 896459c8 a7781be5
Loading
Loading
Loading
Loading
+91 −64
Original line number Diff line number Diff line
@@ -391,6 +391,21 @@ static const struct i2c_msm_qup_reg_dump i2c_msm_qup_reg_dump_map[] = {
{0,                       NULL         },
};

const char *i2c_msm_err_str_table[] = {
	[I2C_MSM_ERR_NACK] =
		 "NACK: slave not responding, ensure its powered",
	[I2C_MSM_ERR_ARB_LOST] = "arb_lost",
	[I2C_MSM_ERR_BUS_ERR] =
		"BUS ERROR : noisy bus or unexpected start/stop tag",
	[I2C_MSM_ERR_TIMEOUT] = "",
	[I2C_MSM_ERR_CORE_CLK] = "CLOCK OFF : Check Core Clock",
	[I2C_MSM_ERR_OVR_UNDR_RUN] = "OVER_UNDER_RUN_ERROR",
	[I2C_MSM_ERR_INVALID_WRITE] = "invalid data write",
	[I2C_MSM_ERR_INVALID_TAG] = "invalid tag",
	[I2C_MSM_ERR_INVALID_READ_ADDR] = "Invalid slave addr",
	[I2C_MSM_ERR_INVALID_READ_SEQ] = "start tag error for read",
	[I2C_MSM_ERR_FAILED] = "I2C transfer failed",
};
/*
 * see: struct i2c_msm_qup_reg_dump for more
 */
@@ -416,47 +431,43 @@ static void i2c_msm_dbg_dump_diag(struct i2c_msm_ctrl *ctrl,
				bool use_param_vals, u32 status, u32 qup_op)
{
	struct i2c_msm_xfer *xfer = &ctrl->xfer;
	const char *str = "DEBUG";
	char buf[I2C_MSM_REG_2_STR_BUF_SZ];
	char str[I2C_MSM_MAX_ERR_BUF_SZ] = {'\0'};
	char *p_str = str;
	const char **diag_msg = i2c_msm_err_str_table;
	int err = xfer->err;

	if (!use_param_vals) {
		void __iomem        *base = ctrl->rsrcs.base;
		status = readl_relaxed(base + QUP_I2C_STATUS);
		qup_op = readl_relaxed(base + QUP_OPERATIONAL);
	}

	 /* In case of NACK, bus and controller are in a good state */
	if (xfer->err & I2C_MSM_ERR_NACK) {
		str = "NACK: slave not responding, ensure its powered "
			"and out of reset";
	/* clock off may be the root cause of all errors below */
	} else if (xfer->err & I2C_MSM_ERR_CORE_CLK) {
		str = "CLOCK OFF: Check Core Clock";
	/* on arb lost we cant control the bus. root cause may be gpios or slv*/
	} else if (xfer->err & I2C_MSM_ERR_ARB_LOST) {
		str = "ARB LOST";
	} else if (xfer->err & I2C_MSM_ERR_TIMEOUT) {
	if ((err & BIT(I2C_MSM_ERR_TIMEOUT))) {
		/*
		 * if we are not the bus master or SDA/SCL is low then it may be
		 * that slave is pulling the lines low. Otherwise it is likely a
		 * GPIO issue
		 * if we are not the bus master or SDA/SCL is
		 * low then it may be that slave is pulling the
		 * lines low. Otherwise it is likely a GPIO
		 * issue
		 */
		if (!(status & QUP_BUS_MASTER))
			snprintf(buf, I2C_MSM_REG_2_STR_BUF_SZ,
				"TIMEOUT(val:%dmsec) check GPIO config if SDA/SCL line(s) low",
			p_str += snprintf(p_str,
				I2C_MSM_MAX_ERR_BUF_SZ,
	"TIMEOUT(val:%dmsec) check GPIO config if SDA/SCL line(s) low, ",
			jiffies_to_msecs(xfer->timeout));
		 else
			snprintf(buf, I2C_MSM_REG_2_STR_BUF_SZ,
			"TIMEOUT(val:%dmsec)", jiffies_to_msecs(xfer->timeout));
			p_str += snprintf(p_str,
				I2C_MSM_MAX_ERR_BUF_SZ,
			"TIMEOUT(val:%dmsec), ",
			jiffies_to_msecs(xfer->timeout));
	}

	for (; err; err >>= 1, ++diag_msg) {

		str = buf;
	/* invalid electric signal on the bus, may be noise or bad slave */
	} else if (xfer->err & I2C_MSM_ERR_BUS_ERR) {
		str = "BUS ERROR: noisy bus or unexpected start/stop tag";
	/* error occur due to INPUT/OUTPUT over or under run */
	} else if (xfer->err & I2C_MSM_ERR_OVR_UNDR_RUN) {
		str = "OVER_UNDER_RUN_ERROR";
		if (err & 1)
			p_str += snprintf(p_str,
			(I2C_MSM_MAX_ERR_BUF_SZ - (p_str - str)),
			 "%s, ", *diag_msg);
	}

	/* dump xfer details */
	dev_err(ctrl->dev,
		"%s: msgs(n:%d cur:%d %s) bc(rx:%zu tx:%zu) mode:%s "
@@ -970,7 +981,7 @@ static int i2c_msm_qup_sw_reset(struct i2c_msm_ctrl *ctrl)
	ret = i2c_msm_qup_state_wait_valid(ctrl, QUP_STATE_RESET, false);
	if (ret) {
		if (atomic_read(&ctrl->xfer.is_active))
			ctrl->xfer.err |= I2C_MSM_ERR_CORE_CLK;
			ctrl->xfer.err |= BIT(I2C_MSM_ERR_CORE_CLK);
		dev_err(ctrl->dev, "error on issuing QUP software-reset\n");
	}
	return ret;
@@ -2736,43 +2747,38 @@ static irqreturn_t i2c_msm_qup_isr(int irq, void *devid)
		log_event       = true;

		if (i2c_status & QUP_PACKET_NACKED)
			ctrl->xfer.err |= I2C_MSM_ERR_NACK;
			ctrl->xfer.err |= BIT(I2C_MSM_ERR_NACK);

		if (i2c_status & QUP_ARB_LOST)
			ctrl->xfer.err |= I2C_MSM_ERR_ARB_LOST;
			ctrl->xfer.err |= BIT(I2C_MSM_ERR_ARB_LOST);

		if (i2c_status & QUP_BUS_ERROR)
			ctrl->xfer.err |= I2C_MSM_ERR_BUS_ERR;
			ctrl->xfer.err |= BIT(I2C_MSM_ERR_BUS_ERR);

		if (i2c_status & QUP_INVALID_WRITE)
			ctrl->xfer.err |= BIT(I2C_MSM_ERR_INVALID_WRITE);

		if (i2c_status & QUP_INVALID_TAG)
			ctrl->xfer.err |= BIT(I2C_MSM_ERR_INVALID_TAG);

		if (i2c_status & QUP_INVALID_READ_ADDR)
			ctrl->xfer.err |= BIT(I2C_MSM_ERR_INVALID_READ_ADDR);

		if (i2c_status & QUP_INVALID_READ_SEQ)
			ctrl->xfer.err |= BIT(I2C_MSM_ERR_INVALID_READ_SEQ);

		if (i2c_status & QUP_FAILED)
			ctrl->xfer.err |= BIT(I2C_MSM_ERR_FAILED);
	}

	/* check for FIFO over/under runs error */
	if (err_flags & QUP_ERR_FLGS_MASK)
		ctrl->xfer.err |= I2C_MSM_ERR_OVR_UNDR_RUN;
		ctrl->xfer.err = BIT(I2C_MSM_ERR_OVR_UNDR_RUN);

	/* Reset and bail out on error */
	if (ctrl->xfer.err) {
	/* Dump the register values before reset the core */
		if (ctrl->dbgfs.dbg_lvl >= MSM_DBG)
	if (ctrl->xfer.err && ctrl->dbgfs.dbg_lvl >= MSM_DBG)
		i2c_msm_dbg_qup_reg_dump(ctrl);

		/* 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.
		 */
		i2c_msm_qup_sw_reset(ctrl);

		need_wmb        = true;
		signal_complete = true;
		log_event       = true;
		goto isr_end;
	}

	/* clear interrupts fields */
	clr_flds = i2c_status & QUP_MSTR_STTS_ERR_MASK;
	if (clr_flds) {
@@ -2792,6 +2798,27 @@ static irqreturn_t i2c_msm_qup_isr(int irq, void *devid)
		need_wmb = true;
	}

	/* Reset and bail out on error */
	if (ctrl->xfer.err) {

		/* 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.
		 */
		i2c_msm_qup_sw_reset(ctrl);

		need_wmb        = true;
		signal_complete = true;
		log_event       = true;
		goto isr_end;
	}

	/* handle data completion */
	if (xfer->mode_id == I2C_MSM_XFER_MODE_BLOCK) {
		/*For Block Mode */
@@ -3011,9 +3038,9 @@ static int i2c_msm_qup_post_xfer(struct i2c_msm_ctrl *ctrl, int err)
{
	/* poll until bus is released */
	if (i2c_msm_qup_poll_bus_active_unset(ctrl)) {
		if ((ctrl->xfer.err & I2C_MSM_ERR_ARB_LOST) ||
		    (ctrl->xfer.err & I2C_MSM_ERR_BUS_ERR) ||
		    (ctrl->xfer.err & I2C_MSM_ERR_TIMEOUT)) {
		if ((ctrl->xfer.err & BIT(I2C_MSM_ERR_ARB_LOST)) ||
		    (ctrl->xfer.err & BIT(I2C_MSM_ERR_BUS_ERR)) ||
		    (ctrl->xfer.err & BIT(I2C_MSM_ERR_TIMEOUT))) {
			if (i2c_msm_qup_slv_holds_bus(ctrl))
				qup_i2c_recover_bus_busy(ctrl);

@@ -3026,7 +3053,7 @@ 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) {
	if (ctrl->xfer.err & BIT(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
@@ -3037,12 +3064,12 @@ static int i2c_msm_qup_post_xfer(struct i2c_msm_ctrl *ctrl, int err)
		err = -ETIMEDOUT;
	}

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

	if (ctrl->xfer.err & I2C_MSM_ERR_NACK)
	if (ctrl->xfer.err & BIT(I2C_MSM_ERR_NACK))
		err = -ENOTCONN;

	return err;
@@ -3148,7 +3175,7 @@ static int i2c_msm_xfer_wait_for_completion(struct i2c_msm_ctrl *ctrl,

	time_left = wait_for_completion_timeout(complete, xfer->timeout);
	if (!time_left) {
		xfer->err |= I2C_MSM_ERR_TIMEOUT;
		xfer->err |= BIT(I2C_MSM_ERR_TIMEOUT);
		i2c_msm_dbg_dump_diag(ctrl, false, 0, 0);
		ret = -EIO;
		i2c_msm_prof_evnt_add(ctrl, MSM_ERR, i2c_msm_prof_dump_cmplt_fl,
+16 −8
Original line number Diff line number Diff line
@@ -127,6 +127,8 @@ enum {
	QUP_BUS_ERROR           = 1U << 2,
	QUP_PACKET_NACKED       = 1U << 3,
	QUP_ARB_LOST            = 1U << 4,
	QUP_INVALID_WRITE	= 1U << 5,
	QUP_FAILED		= 3U << 6,
	QUP_BUS_ACTIVE          = 1U << 8,
	QUP_BUS_MASTER          = 1U << 9,
	QUP_INVALID_TAG         = 1U << 23,
@@ -134,7 +136,7 @@ enum {
	QUP_INVALID_READ_SEQ    = 1U << 25,
	QUP_I2C_SDA             = 1U << 26,
	QUP_I2C_SCL             = 1U << 27,
	QUP_MSTR_STTS_ERR_MASK  = 0x380003C,
	QUP_MSTR_STTS_ERR_MASK  = 0x38000FC,
};

/* Register:QUP_I2C_MASTER_CONFIG fields */
@@ -209,6 +211,8 @@ enum msm_i2c_power_state {
#define I2C_MSM_BAM_PROD_SZ             (32) /* producer pipe n entries */
#define I2C_MSM_BAM_DESC_ARR_SIZ  (I2C_MSM_BAM_CONS_SZ + I2C_MSM_BAM_PROD_SZ)
#define I2C_MSM_REG_2_STR_BUF_SZ        (128)
/* Optimal value to hold the error strings */
#define I2C_MSM_MAX_ERR_BUF_SZ		(256)
#define I2C_MSM_BUF_DUMP_MAX_BC         (20)
#define I2C_MSM_MAX_POLL_MSEC           (100)
#define I2C_MSM_TIMEOUT_SAFTY_COEF      (10)
@@ -599,13 +603,17 @@ struct i2c_msm_prof_event {
};

enum i2c_msm_err_bit_field {
	I2C_MSM_ERR_NONE     = 0,
	I2C_MSM_ERR_NACK     = 1U << 0,
	I2C_MSM_ERR_ARB_LOST = 1U << 1,
	I2C_MSM_ERR_BUS_ERR  = 1U << 2,
	I2C_MSM_ERR_TIMEOUT  = 1U << 3,
	I2C_MSM_ERR_CORE_CLK = 1U << 4,
	I2C_MSM_ERR_OVR_UNDR_RUN = 1U << 5,
	I2C_MSM_ERR_NACK = 0,
	I2C_MSM_ERR_ARB_LOST,
	I2C_MSM_ERR_BUS_ERR,
	I2C_MSM_ERR_TIMEOUT,
	I2C_MSM_ERR_CORE_CLK,
	I2C_MSM_ERR_OVR_UNDR_RUN,
	I2C_MSM_ERR_INVALID_WRITE,
	I2C_MSM_ERR_INVALID_TAG,
	I2C_MSM_ERR_INVALID_READ_ADDR,
	I2C_MSM_ERR_INVALID_READ_SEQ,
	I2C_MSM_ERR_FAILED,
};

/*