mirror of https://gitee.com/openkylin/linux.git
i2c: aspeed: Acknowledge most interrupts early in interrupt handler
Commit3e9efc3299
("i2c: aspeed: Handle master/slave combined irq events properly") moved interrupt acknowledgment to the end of the interrupt handler. In part this was done because the AST2500 datasheet says: I2CD10 Interrupt Status Register bit 2 Receive Done Interrupt status S/W needs to clear this status bit to allow next data receiving. Acknowledging Receive Done before receive data was handled resulted in receive errors on high speed I2C busses. However, interrupt acknowledgment was not only moved to the end of the interrupt handler for Receive Done Interrupt status, but for all interrupt status bits. This could result in race conditions if a second interrupt was received during interrupt handling and not handled but still acknowledged at the end of the interrupt handler. Acknowledge only "Receive Done Interrupt status" late in the interrupt handler to solve the problem. Fixes:3e9efc3299
("i2c: aspeed: Handle master/slave combined irq events properly") Cc: Jae Hyun Yoo <jae.hyun.yoo@linux.intel.com> Cc: Joel Stanley <joel@jms.id.au> Signed-off-by: Guenter Roeck <linux@roeck-us.net> Acked-by: Jae Hyun Yoo <jae.hyun.yoo@linux.intel.com> Tested-by: Joel Stanley <joel@jms.id.au> Acked-by: Brendan Higgins <brendanhiggins@google.com> Signed-off-by: Wolfram Sang <wsa@the-dreams.de>
This commit is contained in:
parent
3e9efc3299
commit
2be6b47211
|
@ -552,6 +552,9 @@ static irqreturn_t aspeed_i2c_bus_irq(int irq, void *dev_id)
|
|||
|
||||
spin_lock(&bus->lock);
|
||||
irq_received = readl(bus->base + ASPEED_I2C_INTR_STS_REG);
|
||||
/* Ack all interrupts except for Rx done */
|
||||
writel(irq_received & ~ASPEED_I2CD_INTR_RX_DONE,
|
||||
bus->base + ASPEED_I2C_INTR_STS_REG);
|
||||
irq_remaining = irq_received;
|
||||
|
||||
#if IS_ENABLED(CONFIG_I2C_SLAVE)
|
||||
|
@ -584,8 +587,10 @@ static irqreturn_t aspeed_i2c_bus_irq(int irq, void *dev_id)
|
|||
"irq handled != irq. expected 0x%08x, but was 0x%08x\n",
|
||||
irq_received, irq_handled);
|
||||
|
||||
/* Ack all interrupt bits. */
|
||||
writel(irq_received, bus->base + ASPEED_I2C_INTR_STS_REG);
|
||||
/* Ack Rx done */
|
||||
if (irq_received & ASPEED_I2CD_INTR_RX_DONE)
|
||||
writel(ASPEED_I2CD_INTR_RX_DONE,
|
||||
bus->base + ASPEED_I2C_INTR_STS_REG);
|
||||
spin_unlock(&bus->lock);
|
||||
return irq_remaining ? IRQ_NONE : IRQ_HANDLED;
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue