This patch includes the workarround for I2C Errata 1.153: When an XRDY/XDR is hit, wait for XUDF before writing data to DATA_REG Signed-off-by: Jagadeesh Bhaskar Pakaravoor <j-pakaravoor@xxxxxx> Signed-off by: Nishant Kamat <nskamat@xxxxxx> Signed-off-by: Moiz Sonasath<m-sonasath@xxxxxx> --- drivers/i2c/busses/i2c-omap.c | 54 +++++++++++++++++++++++++++++++++++++--- 1 files changed, 50 insertions(+), 4 deletions(-) diff --git a/drivers/i2c/busses/i2c-omap.c b/drivers/i2c/busses/i2c-omap.c index ece0125..e84836b 100644 --- a/drivers/i2c/busses/i2c-omap.c +++ b/drivers/i2c/busses/i2c-omap.c @@ -632,6 +632,37 @@ omap_i2c_rev1_isr(int this_irq, void *dev_id) #define omap_i2c_rev1_isr NULL #endif +/* I2C Errata 1.153: + * When an XRDY/XDR is hit, wait for XUDF before writing data to DATA_REG. + * Otherwise some data bytes can be lost while transferring them from the + * memory to the I2C interface. + */ + +static int omap_i2c_wait_for_xudf(struct omap_i2c_dev *dev) +{ + u16 xudf; + int counter = 500; + + /* We are in interrupt context. Wait for XUDF for max 7 msec */ + xudf = omap_i2c_read_reg(dev, OMAP_I2C_STAT_REG); + while (!(xudf & OMAP_I2C_STAT_XUDF) && counter--) { + if (xudf & (OMAP_I2C_STAT_ROVR | OMAP_I2C_STAT_NACK | + OMAP_I2C_STAT_AL)) + return -EINVAL; + udelay(10); + xudf = omap_i2c_read_reg(dev, OMAP_I2C_STAT_REG); + } + + if (!counter) { + /* Clear Tx FIFO */ + omap_i2c_write_reg(dev, OMAP_I2C_BUF_REG, + OMAP_I2C_BUF_TXFIF_CLR); + return -ETIMEDOUT; + } + + return 0; +} + static irqreturn_t omap_i2c_isr(int this_irq, void *dev_id) { @@ -639,6 +670,7 @@ omap_i2c_isr(int this_irq, void *dev_id) u16 bits; u16 stat, w; int err, count = 0; + int error; if (dev->idle) return IRQ_NONE; @@ -647,7 +679,7 @@ omap_i2c_isr(int this_irq, void *dev_id) while ((stat = (omap_i2c_read_reg(dev, OMAP_I2C_STAT_REG))) & bits) { dev_dbg(dev->dev, "IRQ (ISR = 0x%04x)\n", stat); if (count++ == 100) { - dev_warn(dev->dev, "Too much work in one IRQ\n"); + dev_dbg(dev->dev, "Too much work in one IRQ\n"); break; } @@ -715,11 +747,22 @@ omap_i2c_isr(int this_irq, void *dev_id) OMAP_I2C_BUFSTAT_REG); } while (num_bytes) { - num_bytes--; w = 0; if (dev->buf_len) { + if (cpu_is_omap34xx()) { + /* OMAP3430 Errata 1.153 */ + error = omap_i2c_wait_for_xudf(dev); + if (error) { + omap_i2c_ack_stat(dev, stat & + (OMAP_I2C_STAT_XRDY | + OMAP_I2C_STAT_XDR)); + dev_err(dev->dev, "Transmit error\n"); + omap_i2c_complete_cmd(dev, OMAP_I2C_STAT_XUDF); + + return IRQ_HANDLED; + } + } w = *dev->buf++; - dev->buf_len--; /* Data reg from 2430 is 8 bit wide */ if (!cpu_is_omap2430() && !cpu_is_omap34xx()) { @@ -728,6 +771,10 @@ omap_i2c_isr(int this_irq, void *dev_id) dev->buf_len--; } } + omap_i2c_write_reg(dev, + OMAP_I2C_DATA_REG, w); + num_bytes--; + dev->buf_len--; } else { if (stat & OMAP_I2C_STAT_XRDY) dev_err(dev->dev, @@ -739,7 +786,6 @@ omap_i2c_isr(int this_irq, void *dev_id) "data to send\n"); break; } - omap_i2c_write_reg(dev, OMAP_I2C_DATA_REG, w); } omap_i2c_ack_stat(dev, stat & (OMAP_I2C_STAT_XRDY | OMAP_I2C_STAT_XDR)); -- 1.5.6.3 -- To unsubscribe from this list: send the line "unsubscribe linux-omap" in the body of a message to majordomo@xxxxxxxxxxxxxxx More majordomo info at http://vger.kernel.org/majordomo-info.html