On 03/11/2015 03:08 PM, Alexander Sverdlin wrote: > There is one big problem in the current design: ISR accesses the controller > registers in parallel with i2c_davinci_xfer_msg() in process context. The whole > logic is not obvious, many operations are performed in process context while > ISR is always enabled and does something asynchronous even while it's not > expected. We have faced these races on 4-cores Keystone chip. Some examples: > > - when non-existing device is accessed first comes NAK IRQ, then ARDY IRQ. After > NAK we already jump out of wait_for_completion_timeout() and depending on how > lucky we are ARDY IRQ will access MDR register in the middle of some other > operation in process context; > > - STOP condition is triggered in many places in the driver, in ISR, in > i2c_davinci_xfer_msg(), but there is no code which guarantees that STOP will > be really completed. We have seen many STOP conditions simply missing in > back-to-back transfers, when next i2c_davinci_xfer_msg() call simply overwrites > MDR register while STOP is still not generated. > > So, make the design more robust and obvious: > - leave hot path (buffers management) in ISR, remove other registers access from > ISR; > - introduce second synchronization point, to make sure that STOP condition is > really generated and it's safe to begin next transfer; > - simplify the state machine; > - enable IRQs only when they are expected, disable them in ISR when transfer is > completed/failed; > - STOP is normally set simultaneously with START condition (in case of last > message); only special case when STOP is additionally generated is received NAK > -- this case is handled separately. I'm not sure about this change (- It's too significant and definitely will need more review & Tested-by. We need to be careful with it, especially taking into account DAVINCI_I2C_MDR_RM mode and future changes like https://lkml.org/lkml/2014/5/1/348. May be you can split it? > > Signed-off-by: Alexander Sverdlin <alexander.sverdlin@xxxxxxxxx> > --- > drivers/i2c/busses/i2c-davinci.c | 219 ++++++++++++++++--------------------- > 1 files changed, 95 insertions(+), 124 deletions(-) > > diff --git a/drivers/i2c/busses/i2c-davinci.c b/drivers/i2c/busses/i2c-davinci.c > index 6dc7ff5..98759ae 100644 > --- a/drivers/i2c/busses/i2c-davinci.c > +++ b/drivers/i2c/busses/i2c-davinci.c > @@ -72,10 +72,19 @@ > #define DAVINCI_I2C_STR_BB BIT(12) > #define DAVINCI_I2C_STR_RSFULL BIT(11) > #define DAVINCI_I2C_STR_SCD BIT(5) > +#define DAVINCI_I2C_STR_ICXRDY BIT(4) > +#define DAVINCI_I2C_STR_ICRDRDY BIT(3) > #define DAVINCI_I2C_STR_ARDY BIT(2) > #define DAVINCI_I2C_STR_NACK BIT(1) > #define DAVINCI_I2C_STR_AL BIT(0) > > +#define DAVINCI_I2C_STR_ALL (DAVINCI_I2C_STR_SCD | \ > + DAVINCI_I2C_STR_ICXRDY | \ > + DAVINCI_I2C_STR_ICRDRDY | \ > + DAVINCI_I2C_STR_ARDY | \ > + DAVINCI_I2C_STR_NACK | \ > + DAVINCI_I2C_STR_AL) > + > #define DAVINCI_I2C_MDR_NACK BIT(15) > #define DAVINCI_I2C_MDR_STT BIT(13) > #define DAVINCI_I2C_MDR_STP BIT(11) > @@ -98,12 +107,10 @@ struct davinci_i2c_dev { > void __iomem *base; > struct completion cmd_complete; > struct clk *clk; > - int cmd_err; > + u32 cmd_stat; > u8 *buf; > size_t buf_len; > int irq; > - int stop; > - u8 terminate; > struct i2c_adapter adapter; > #ifdef CONFIG_CPU_FREQ > struct completion xfr_complete; > @@ -256,9 +263,6 @@ static int i2c_davinci_init(struct davinci_i2c_dev *dev) > /* Take the I2C module out of reset: */ > davinci_i2c_reset_ctrl(dev, 1); > > - /* Enable interrupts */ > - davinci_i2c_write_reg(dev, DAVINCI_I2C_IMR_REG, I2C_DAVINCI_INTR_ALL); > - > return 0; > } > > @@ -293,6 +297,36 @@ static int i2c_davinci_wait_bus_not_busy(struct davinci_i2c_dev *dev, > return 0; > } > > +static int i2c_davinci_wait_for_completion(struct davinci_i2c_dev *dev) > +{ > + int r; > + > + r = wait_for_completion_timeout(&dev->cmd_complete, dev->adapter.timeout); > + if (r == 0) { > + /* Disable IRQ, sources were NOT masked by the handler */ > + disable_irq(dev->irq); > + > + dev_err(dev->dev, "controller timed out\n"); > + davinci_i2c_recover_bus(dev); > + i2c_davinci_init(dev); > + > + /* It's safe to enable IRQ after reset */ > + enable_irq(dev->irq); > + > + return -ETIMEDOUT; > + } > + > + /* If it wasn't a timeout, then the IRQs are masked */ > + > + if (r < 0) { > + dev_err(dev->dev, "abnormal termination buf_len=%i\n", > + dev->buf_len); > + return r; > + } > + > + return 0; > +} > + > /* > * Low level master read/write transaction. This function is called > * from i2c_davinci_xfer. > @@ -315,12 +349,10 @@ i2c_davinci_xfer_msg(struct i2c_adapter *adap, struct i2c_msg *msg, int stop) > > dev->buf = msg->buf; > dev->buf_len = msg->len; > - dev->stop = stop; > > davinci_i2c_write_reg(dev, DAVINCI_I2C_CNT_REG, dev->buf_len); > > reinit_completion(&dev->cmd_complete); > - dev->cmd_err = 0; > > /* Take I2C out of reset and configure it as master */ > flag = DAVINCI_I2C_MDR_IRS | DAVINCI_I2C_MDR_MST; > @@ -333,16 +365,6 @@ i2c_davinci_xfer_msg(struct i2c_adapter *adap, struct i2c_msg *msg, int stop) > if (msg->len == 0) > flag |= DAVINCI_I2C_MDR_RM; > > - /* Enable receive or transmit interrupts */ > - w = davinci_i2c_read_reg(dev, DAVINCI_I2C_IMR_REG); > - if (msg->flags & I2C_M_RD) > - w |= DAVINCI_I2C_IMR_RRDY; > - else > - w |= DAVINCI_I2C_IMR_XRDY; > - davinci_i2c_write_reg(dev, DAVINCI_I2C_IMR_REG, w); > - > - dev->terminate = 0; > - > /* > * Write mode register first as needed for correct behaviour > * on OMAP-L138, but don't set STT yet to avoid a race with XRDY > @@ -350,6 +372,9 @@ i2c_davinci_xfer_msg(struct i2c_adapter *adap, struct i2c_msg *msg, int stop) > */ > davinci_i2c_write_reg(dev, DAVINCI_I2C_MDR_REG, flag); > > + /* Clear IRQ flags */ > + davinci_i2c_write_reg(dev, DAVINCI_I2C_STR_REG, DAVINCI_I2C_STR_ALL); > + > /* > * First byte should be set here, not after interrupt, > * because transmit-data-ready interrupt can come before > @@ -368,49 +393,48 @@ i2c_davinci_xfer_msg(struct i2c_adapter *adap, struct i2c_msg *msg, int stop) > flag |= DAVINCI_I2C_MDR_STP; > davinci_i2c_write_reg(dev, DAVINCI_I2C_MDR_REG, flag); > > - r = wait_for_completion_timeout(&dev->cmd_complete, dev->adapter.timeout); > - if (r == 0) { > - dev_err(dev->dev, "controller timed out\n"); > - davinci_i2c_recover_bus(dev); > - i2c_davinci_init(dev); > - dev->buf_len = 0; > - return -ETIMEDOUT; > - } > - if (dev->buf_len) { > - /* This should be 0 if all bytes were transferred > - * or dev->cmd_err denotes an error. > - */ > - if (r >= 0) { > - dev_err(dev->dev, "abnormal termination buf_len=%i\n", > - dev->buf_len); > - r = -EREMOTEIO; > - } > - dev->terminate = 1; > - wmb(); > - dev->buf_len = 0; > - } > - if (r < 0) > + /* Enable status interrupts */ > + w = I2C_DAVINCI_INTR_ALL; > + /* Enable receive or transmit interrupts */ > + if (msg->flags & I2C_M_RD) > + w |= DAVINCI_I2C_IMR_RRDY; > + else > + w |= DAVINCI_I2C_IMR_XRDY; > + davinci_i2c_write_reg(dev, DAVINCI_I2C_IMR_REG, w); > + > + r = i2c_davinci_wait_for_completion(dev); > + if (r) > return r; > > - /* no error */ > - if (likely(!dev->cmd_err)) > + switch (dev->cmd_stat) { > + case DAVINCI_I2C_IVR_SCD: > + case DAVINCI_I2C_IVR_ARDY: > return msg->len; > > - /* We have an error */ > - if (dev->cmd_err & DAVINCI_I2C_STR_AL) { > + case DAVINCI_I2C_IVR_AL: > i2c_davinci_init(dev); > return -EIO; > } > > - if (dev->cmd_err & DAVINCI_I2C_STR_NACK) { > - if (msg->flags & I2C_M_IGNORE_NAK) > - return msg->len; > - w = davinci_i2c_read_reg(dev, DAVINCI_I2C_MDR_REG); > - w |= DAVINCI_I2C_MDR_STP; > - davinci_i2c_write_reg(dev, DAVINCI_I2C_MDR_REG, w); > - return -EREMOTEIO; > - } > - return -EIO; > + /* We are here because of NAK */ > + > + if (msg->flags & I2C_M_IGNORE_NAK) > + return msg->len; > + > + reinit_completion(&dev->cmd_complete); > + /* Clear IRQ flags */ > + davinci_i2c_write_reg(dev, DAVINCI_I2C_STR_REG, DAVINCI_I2C_STR_ALL); > + /* We going to wait for SCD IRQ */ > + davinci_i2c_write_reg(dev, DAVINCI_I2C_IMR_REG, I2C_DAVINCI_INTR_ALL); > + > + w = davinci_i2c_read_reg(dev, DAVINCI_I2C_MDR_REG); > + w |= DAVINCI_I2C_MDR_STP; > + davinci_i2c_write_reg(dev, DAVINCI_I2C_MDR_REG, w); > + > + r = i2c_davinci_wait_for_completion(dev); > + if (r) > + return r; > + return -EREMOTEIO; > } > > /* > @@ -451,27 +475,6 @@ static u32 i2c_davinci_func(struct i2c_adapter *adap) > return I2C_FUNC_I2C | I2C_FUNC_SMBUS_EMUL; > } > > -static void terminate_read(struct davinci_i2c_dev *dev) > -{ > - u16 w = davinci_i2c_read_reg(dev, DAVINCI_I2C_MDR_REG); > - w |= DAVINCI_I2C_MDR_NACK; > - davinci_i2c_write_reg(dev, DAVINCI_I2C_MDR_REG, w); > - > - /* Throw away data */ > - davinci_i2c_read_reg(dev, DAVINCI_I2C_DRR_REG); > - if (!dev->terminate) > - dev_err(dev->dev, "RDR IRQ while no data requested\n"); > -} > -static void terminate_write(struct davinci_i2c_dev *dev) > -{ > - u16 w = davinci_i2c_read_reg(dev, DAVINCI_I2C_MDR_REG); > - w |= DAVINCI_I2C_MDR_RM | DAVINCI_I2C_MDR_STP; > - davinci_i2c_write_reg(dev, DAVINCI_I2C_MDR_REG, w); > - > - if (!dev->terminate) > - dev_dbg(dev->dev, "TDR IRQ while no data to send\n"); > -} > - > /* > * Interrupt service routine. This gets called whenever an I2C interrupt > * occurs. > @@ -491,49 +494,19 @@ static irqreturn_t i2c_davinci_isr(int this_irq, void *dev_id) > } > > switch (stat) { > - case DAVINCI_I2C_IVR_AL: > - /* Arbitration lost, must retry */ > - dev->cmd_err |= DAVINCI_I2C_STR_AL; > - dev->buf_len = 0; > - complete(&dev->cmd_complete); > - break; > - > - case DAVINCI_I2C_IVR_NACK: > - dev->cmd_err |= DAVINCI_I2C_STR_NACK; > - dev->buf_len = 0; > - complete(&dev->cmd_complete); > - break; > - > - case DAVINCI_I2C_IVR_ARDY: > - davinci_i2c_write_reg(dev, > - DAVINCI_I2C_STR_REG, DAVINCI_I2C_STR_ARDY); > - if (((dev->buf_len == 0) && (dev->stop != 0)) || > - (dev->cmd_err & DAVINCI_I2C_STR_NACK)) { > - w = davinci_i2c_read_reg(dev, > - DAVINCI_I2C_MDR_REG); > - w |= DAVINCI_I2C_MDR_STP; > - davinci_i2c_write_reg(dev, > - DAVINCI_I2C_MDR_REG, w); > - } > - complete(&dev->cmd_complete); > - break; > - > case DAVINCI_I2C_IVR_RDR: > if (dev->buf_len) { > *dev->buf++ = > davinci_i2c_read_reg(dev, > DAVINCI_I2C_DRR_REG); > dev->buf_len--; > - if (dev->buf_len) > - continue; > - > - davinci_i2c_write_reg(dev, > - DAVINCI_I2C_STR_REG, > - DAVINCI_I2C_IMR_RRDY); > - } else { > - /* signal can terminate transfer */ > - terminate_read(dev); > + continue; > } > + > + /* Read transfer completed, mask the IRQ */ > + w = davinci_i2c_read_reg(dev, DAVINCI_I2C_IMR_REG); > + w &= ~DAVINCI_I2C_IMR_RRDY; > + davinci_i2c_write_reg(dev, DAVINCI_I2C_IMR_REG, w); > break; > > case DAVINCI_I2C_IVR_XRDY: > @@ -541,24 +514,22 @@ static irqreturn_t i2c_davinci_isr(int this_irq, void *dev_id) > davinci_i2c_write_reg(dev, DAVINCI_I2C_DXR_REG, > *dev->buf++); > dev->buf_len--; > - if (dev->buf_len) > - continue; > - > - w = davinci_i2c_read_reg(dev, > - DAVINCI_I2C_IMR_REG); > - w &= ~DAVINCI_I2C_IMR_XRDY; > - davinci_i2c_write_reg(dev, > - DAVINCI_I2C_IMR_REG, > - w); > - } else { > - /* signal can terminate transfer */ > - terminate_write(dev); > + continue; > } > + > + /* Write transfer completed, mask the IRQ */ > + w = davinci_i2c_read_reg(dev, DAVINCI_I2C_IMR_REG); > + w &= ~DAVINCI_I2C_IMR_XRDY; > + davinci_i2c_write_reg(dev, DAVINCI_I2C_IMR_REG, w); > break; > > + case DAVINCI_I2C_IVR_AL: > + case DAVINCI_I2C_IVR_NACK: > case DAVINCI_I2C_IVR_SCD: > - davinci_i2c_write_reg(dev, > - DAVINCI_I2C_STR_REG, DAVINCI_I2C_STR_SCD); > + case DAVINCI_I2C_IVR_ARDY: > + dev->cmd_stat = stat; > + /* Mask all IRQs */ > + davinci_i2c_write_reg(dev, DAVINCI_I2C_IMR_REG, 0); > complete(&dev->cmd_complete); > break; > > -- regards, -grygorii -- To unsubscribe from this list: send the line "unsubscribe linux-i2c" in the body of a message to majordomo@xxxxxxxxxxxxxxx More majordomo info at http://vger.kernel.org/majordomo-info.html