mirror of
https://github.com/team-infusion-developers/android_kernel_samsung_msm8976.git
synced 2024-09-21 03:43:03 +00:00
Merge "i2c-msm-v2: add missing error conditions"
This commit is contained in:
commit
eec8e25050
|
@ -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",
|
||||
jiffies_to_msecs(xfer->timeout));
|
||||
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));
|
||||
|
||||
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";
|
||||
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) {
|
||||
|
||||
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,42 +2747,37 @@ 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)
|
||||
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;
|
||||
}
|
||||
/* Dump the register values before reset the core */
|
||||
if (ctrl->xfer.err && ctrl->dbgfs.dbg_lvl >= MSM_DBG)
|
||||
i2c_msm_dbg_qup_reg_dump(ctrl);
|
||||
|
||||
/* clear interrupts fields */
|
||||
clr_flds = i2c_status & QUP_MSTR_STTS_ERR_MASK;
|
||||
|
@ -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,
|
||||
|
|
|
@ -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,
|
||||
};
|
||||
|
||||
/*
|
||||
|
|
Loading…
Reference in a new issue