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

This commit is contained in:
Linux Build Service Account 2014-11-24 02:57:53 -08:00 committed by Gerrit - the friendly Code Review server
commit eec8e25050
2 changed files with 108 additions and 73 deletions

View file

@ -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,

View file

@ -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,
};
/*