Commit a9bed6b1 authored by Ludovic Desroches's avatar Ludovic Desroches Committed by Wolfram Sang

i2c: at91: manage unexpected RXRDY flag when starting a transfer

In some cases, we could start a new i2c transfer with the RXRDY flag
set. It is not a clean state and it leads to print annoying error
messages even if there no real issue. The cause is only having garbage
data in the Receive Holding Register because of a weird behavior of the
RXRDY flag.
Reported-by: default avatarPeter Rosin <peda@lysator.liu.se>
Signed-off-by: default avatarLudovic Desroches <ludovic.desroches@atmel.com>
Tested-by: default avatarPeter Rosin <peda@lysator.liu.se>
Signed-off-by: default avatarWolfram Sang <wsa@the-dreams.de>
Fixes: 93563a6a ("i2c: at91: fix a race condition when using the DMA controller")
Cc: stable@vger.kernel.org #4.1
parent 43e9f2aa
...@@ -347,8 +347,14 @@ static void at91_twi_write_data_dma(struct at91_twi_dev *dev) ...@@ -347,8 +347,14 @@ static void at91_twi_write_data_dma(struct at91_twi_dev *dev)
static void at91_twi_read_next_byte(struct at91_twi_dev *dev) static void at91_twi_read_next_byte(struct at91_twi_dev *dev)
{ {
if (!dev->buf_len) /*
* If we are in this case, it means there is garbage data in RHR, so
* delete them.
*/
if (!dev->buf_len) {
at91_twi_read(dev, AT91_TWI_RHR);
return; return;
}
/* 8bit read works with and without FIFO */ /* 8bit read works with and without FIFO */
*dev->buf = readb_relaxed(dev->base + AT91_TWI_RHR); *dev->buf = readb_relaxed(dev->base + AT91_TWI_RHR);
...@@ -465,6 +471,24 @@ static irqreturn_t atmel_twi_interrupt(int irq, void *dev_id) ...@@ -465,6 +471,24 @@ static irqreturn_t atmel_twi_interrupt(int irq, void *dev_id)
if (!irqstatus) if (!irqstatus)
return IRQ_NONE; return IRQ_NONE;
/*
* In reception, the behavior of the twi device (before sama5d2) is
* weird. There is some magic about RXRDY flag! When a data has been
* almost received, the reception of a new one is anticipated if there
* is no stop command to send. That is the reason why ask for sending
* the stop command not on the last data but on the second last one.
*
* Unfortunately, we could still have the RXRDY flag set even if the
* transfer is done and we have read the last data. It might happen
* when the i2c slave device sends too quickly data after receiving the
* ack from the master. The data has been almost received before having
* the order to send stop. In this case, sending the stop command could
* cause a RXRDY interrupt with a TXCOMP one. It is better to manage
* the RXRDY interrupt first in order to not keep garbage data in the
* Receive Holding Register for the next transfer.
*/
if (irqstatus & AT91_TWI_RXRDY)
at91_twi_read_next_byte(dev);
/* /*
* When a NACK condition is detected, the I2C controller sets the NACK, * When a NACK condition is detected, the I2C controller sets the NACK,
...@@ -507,8 +531,6 @@ static irqreturn_t atmel_twi_interrupt(int irq, void *dev_id) ...@@ -507,8 +531,6 @@ static irqreturn_t atmel_twi_interrupt(int irq, void *dev_id)
if (irqstatus & (AT91_TWI_TXCOMP | AT91_TWI_NACK)) { if (irqstatus & (AT91_TWI_TXCOMP | AT91_TWI_NACK)) {
at91_disable_twi_interrupts(dev); at91_disable_twi_interrupts(dev);
complete(&dev->cmd_complete); complete(&dev->cmd_complete);
} else if (irqstatus & AT91_TWI_RXRDY) {
at91_twi_read_next_byte(dev);
} else if (irqstatus & AT91_TWI_TXRDY) { } else if (irqstatus & AT91_TWI_TXRDY) {
at91_twi_write_next_byte(dev); at91_twi_write_next_byte(dev);
} }
...@@ -525,7 +547,6 @@ static int at91_do_twi_transfer(struct at91_twi_dev *dev) ...@@ -525,7 +547,6 @@ static int at91_do_twi_transfer(struct at91_twi_dev *dev)
unsigned long time_left; unsigned long time_left;
bool has_unre_flag = dev->pdata->has_unre_flag; bool has_unre_flag = dev->pdata->has_unre_flag;
bool has_alt_cmd = dev->pdata->has_alt_cmd; bool has_alt_cmd = dev->pdata->has_alt_cmd;
unsigned sr;
/* /*
* WARNING: the TXCOMP bit in the Status Register is NOT a clear on * WARNING: the TXCOMP bit in the Status Register is NOT a clear on
...@@ -577,7 +598,7 @@ static int at91_do_twi_transfer(struct at91_twi_dev *dev) ...@@ -577,7 +598,7 @@ static int at91_do_twi_transfer(struct at91_twi_dev *dev)
dev->transfer_status = 0; dev->transfer_status = 0;
/* Clear pending interrupts, such as NACK. */ /* Clear pending interrupts, such as NACK. */
sr = at91_twi_read(dev, AT91_TWI_SR); at91_twi_read(dev, AT91_TWI_SR);
if (dev->fifo_size) { if (dev->fifo_size) {
unsigned fifo_mr = at91_twi_read(dev, AT91_TWI_FMR); unsigned fifo_mr = at91_twi_read(dev, AT91_TWI_FMR);
...@@ -600,11 +621,6 @@ static int at91_do_twi_transfer(struct at91_twi_dev *dev) ...@@ -600,11 +621,6 @@ static int at91_do_twi_transfer(struct at91_twi_dev *dev)
} else if (dev->msg->flags & I2C_M_RD) { } else if (dev->msg->flags & I2C_M_RD) {
unsigned start_flags = AT91_TWI_START; unsigned start_flags = AT91_TWI_START;
if (sr & AT91_TWI_RXRDY) {
dev_err(dev->dev, "RXRDY still set!");
at91_twi_read(dev, AT91_TWI_RHR);
}
/* if only one byte is to be read, immediately stop transfer */ /* if only one byte is to be read, immediately stop transfer */
if (!has_alt_cmd && dev->buf_len <= 1 && if (!has_alt_cmd && dev->buf_len <= 1 &&
!(dev->msg->flags & I2C_M_RECV_LEN)) !(dev->msg->flags & I2C_M_RECV_LEN))
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment