mirror of
https://mirrors.bfsu.edu.cn/git/linux.git
synced 2024-12-16 15:34:48 +08:00
i2c: uniphier-f: fix timeout error after reading 8 bytes
I was totally screwed up in commiteaba68785c
("i2c: uniphier-f: fix race condition when IRQ is cleared"). Since that commit, if the number of read bytes is multiple of the FIFO size (8, 16, 24... bytes), the STOP condition could be issued twice, depending on the timing. If this happens, the controller will go wrong, resulting in the timeout error. It was more than 3 years ago when I wrote this driver, so my memory about this hardware was vague. Please let me correct the description in the commit log ofeaba68785c
. Clearing the IRQ status on exiting the IRQ handler is absolutely fine. This controller makes a pause while any IRQ status is asserted. If the IRQ status is cleared first, the hardware may start the next transaction before the IRQ handler finishes what it supposed to do. This partially reverts the bad commit with clear comments so that I will never repeat this mistake. I also investigated what is happening at the last moment of the read mode. The UNIPHIER_FI2C_INT_RF interrupt is asserted a bit earlier (by half a period of the clock cycle) than UNIPHIER_FI2C_INT_RB. I consulted a hardware engineer, and I got the following information: UNIPHIER_FI2C_INT_RF asserted at the falling edge of SCL at the 8th bit. UNIPHIER_FI2C_INT_RB asserted at the rising edge of SCL at the 9th (ACK) bit. In order to avoid calling uniphier_fi2c_stop() twice, check the latter interrupt. I also commented this because it is obscure hardware internal. Fixes:eaba68785c
("i2c: uniphier-f: fix race condition when IRQ is cleared") Signed-off-by: Masahiro Yamada <yamada.masahiro@socionext.com> Signed-off-by: Wolfram Sang <wsa@the-dreams.de>
This commit is contained in:
parent
0544ee4b1a
commit
c2a653deaa
@ -173,8 +173,6 @@ static irqreturn_t uniphier_fi2c_interrupt(int irq, void *dev_id)
|
||||
"interrupt: enabled_irqs=%04x, irq_status=%04x\n",
|
||||
priv->enabled_irqs, irq_status);
|
||||
|
||||
uniphier_fi2c_clear_irqs(priv, irq_status);
|
||||
|
||||
if (irq_status & UNIPHIER_FI2C_INT_STOP)
|
||||
goto complete;
|
||||
|
||||
@ -214,7 +212,13 @@ static irqreturn_t uniphier_fi2c_interrupt(int irq, void *dev_id)
|
||||
|
||||
if (irq_status & (UNIPHIER_FI2C_INT_RF | UNIPHIER_FI2C_INT_RB)) {
|
||||
uniphier_fi2c_drain_rxfifo(priv);
|
||||
if (!priv->len)
|
||||
/*
|
||||
* If the number of bytes to read is multiple of the FIFO size
|
||||
* (msg->len == 8, 16, 24, ...), the INT_RF bit is set a little
|
||||
* earlier than INT_RB. We wait for INT_RB to confirm the
|
||||
* completion of the current message.
|
||||
*/
|
||||
if (!priv->len && (irq_status & UNIPHIER_FI2C_INT_RB))
|
||||
goto data_done;
|
||||
|
||||
if (unlikely(priv->flags & UNIPHIER_FI2C_MANUAL_NACK)) {
|
||||
@ -253,6 +257,13 @@ complete:
|
||||
}
|
||||
|
||||
handled:
|
||||
/*
|
||||
* This controller makes a pause while any bit of the IRQ status is
|
||||
* asserted. Clear the asserted bit to kick the controller just before
|
||||
* exiting the handler.
|
||||
*/
|
||||
uniphier_fi2c_clear_irqs(priv, irq_status);
|
||||
|
||||
spin_unlock(&priv->lock);
|
||||
|
||||
return IRQ_HANDLED;
|
||||
|
Loading…
Reference in New Issue
Block a user