i2c: at91: fix write transfers by clearing pending interrupt first
authorCyrille Pitchen <cyrille.pitchen@atmel.com>
Wed, 21 Oct 2015 13:44:03 +0000 (15:44 +0200)
committerWilly Tarreau <w@1wt.eu>
Fri, 10 Feb 2017 10:03:34 +0000 (11:03 +0100)
commit 6f6ddbb09d2a5baded0e23add3ad2d9e9417ab30 upstream.

In some cases a NACK interrupt may be pending in the Status Register (SR)
as a result of a previous transfer. However at91_do_twi_transfer() did not
read the SR to clear pending interruptions before starting a new transfer.
Hence a NACK interrupt rose as soon as it was enabled again at the I2C
controller level, resulting in a wrong sequence of operations and strange
patterns of behaviour on the I2C bus, such as a clock stretch followed by
a restart of the transfer.

This first issue occurred with both DMA and PIO write transfers.

Also when a NACK error was detected during a PIO write transfer, the
interrupt handler used to wrongly start a new transfer by writing into the
Transmit Holding Register (THR). Then the I2C slave was likely to reply
with a second NACK.

This second issue is fixed in atmel_twi_interrupt() by handling the TXRDY
status bit only if both the TXCOMP and NACK status bits are cleared.

Tested with a at24 eeprom on sama5d36ek board running a linux-4.1-at91
kernel image. Adapted to linux-next.

Reported-by: Peter Rosin <peda@lysator.liu.se>
Signed-off-by: Cyrille Pitchen <cyrille.pitchen@atmel.com>
Signed-off-by: Ludovic Desroches <ludovic.desroches@atmel.com>
Tested-by: Peter Rosin <peda@lysator.liu.se>
Signed-off-by: Wolfram Sang <wsa@the-dreams.de>
Fixes: 93563a6a71bb ("i2c: at91: fix a race condition when using the DMA controller")
Signed-off-by: Willy Tarreau <w@1wt.eu>
drivers/i2c/busses/i2c-at91.c

index ceabcfeb587c648cee9404c50baca87cc5ae610d..c880d13f5405895d4f47f72855d85ee2f4030b99 100644 (file)
@@ -371,19 +371,57 @@ static irqreturn_t atmel_twi_interrupt(int irq, void *dev_id)
 
        if (!irqstatus)
                return IRQ_NONE;
-       else if (irqstatus & AT91_TWI_RXRDY)
-               at91_twi_read_next_byte(dev);
-       else if (irqstatus & AT91_TWI_TXRDY)
-               at91_twi_write_next_byte(dev);
-
-       /* catch error flags */
-       dev->transfer_status |= status;
 
+       /*
+        * When a NACK condition is detected, the I2C controller sets the NACK,
+        * TXCOMP and TXRDY bits all together in the Status Register (SR).
+        *
+        * 1 - Handling NACK errors with CPU write transfer.
+        *
+        * In such case, we should not write the next byte into the Transmit
+        * Holding Register (THR) otherwise the I2C controller would start a new
+        * transfer and the I2C slave is likely to reply by another NACK.
+        *
+        * 2 - Handling NACK errors with DMA write transfer.
+        *
+        * By setting the TXRDY bit in the SR, the I2C controller also triggers
+        * the DMA controller to write the next data into the THR. Then the
+        * result depends on the hardware version of the I2C controller.
+        *
+        * 2a - Without support of the Alternative Command mode.
+        *
+        * This is the worst case: the DMA controller is triggered to write the
+        * next data into the THR, hence starting a new transfer: the I2C slave
+        * is likely to reply by another NACK.
+        * Concurrently, this interrupt handler is likely to be called to manage
+        * the first NACK before the I2C controller detects the second NACK and
+        * sets once again the NACK bit into the SR.
+        * When handling the first NACK, this interrupt handler disables the I2C
+        * controller interruptions, especially the NACK interrupt.
+        * Hence, the NACK bit is pending into the SR. This is why we should
+        * read the SR to clear all pending interrupts at the beginning of
+        * at91_do_twi_transfer() before actually starting a new transfer.
+        *
+        * 2b - With support of the Alternative Command mode.
+        *
+        * When a NACK condition is detected, the I2C controller also locks the
+        * THR (and sets the LOCK bit in the SR): even though the DMA controller
+        * is triggered by the TXRDY bit to write the next data into the THR,
+        * this data actually won't go on the I2C bus hence a second NACK is not
+        * generated.
+        */
        if (irqstatus & (AT91_TWI_TXCOMP | AT91_TWI_NACK)) {
                at91_disable_twi_interrupts(dev);
                complete(&dev->cmd_complete);
+       } else if (irqstatus & AT91_TWI_RXRDY) {
+               at91_twi_read_next_byte(dev);
+       } else if (irqstatus & AT91_TWI_TXRDY) {
+               at91_twi_write_next_byte(dev);
        }
 
+       /* catch error flags */
+       dev->transfer_status |= status;
+
        return IRQ_HANDLED;
 }
 
@@ -391,6 +429,7 @@ static int at91_do_twi_transfer(struct at91_twi_dev *dev)
 {
        int ret;
        bool has_unre_flag = dev->pdata->has_unre_flag;
+       unsigned sr;
 
        /*
         * WARNING: the TXCOMP bit in the Status Register is NOT a clear on
@@ -426,13 +465,16 @@ static int at91_do_twi_transfer(struct at91_twi_dev *dev)
        INIT_COMPLETION(dev->cmd_complete);
        dev->transfer_status = 0;
 
+       /* Clear pending interrupts, such as NACK. */
+       sr = at91_twi_read(dev, AT91_TWI_SR);
+
        if (!dev->buf_len) {
                at91_twi_write(dev, AT91_TWI_CR, AT91_TWI_QUICK);
                at91_twi_write(dev, AT91_TWI_IER, AT91_TWI_TXCOMP);
        } else if (dev->msg->flags & I2C_M_RD) {
                unsigned start_flags = AT91_TWI_START;
 
-               if (at91_twi_read(dev, AT91_TWI_SR) & AT91_TWI_RXRDY) {
+               if (sr & AT91_TWI_RXRDY) {
                        dev_err(dev->dev, "RXRDY still set!");
                        at91_twi_read(dev, AT91_TWI_RHR);
                }