aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/i2c
diff options
context:
space:
mode:
authorGuenter Roeck <linux@roeck-us.net>2018-09-13 23:30:10 -0400
committerWolfram Sang <wsa@the-dreams.de>2018-09-24 17:44:33 -0400
commit2be6b47211e17e6c90ead40d24d2a5cc815f2d5c (patch)
treec930c3c61c144056861b8869c8a65ea60f743022 /drivers/i2c
parent3e9efc3299dd78a0fa96515f0a453fab1ed4a1bd (diff)
i2c: aspeed: Acknowledge most interrupts early in interrupt handler
Commit 3e9efc3299dd ("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: 3e9efc3299dd ("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>
Diffstat (limited to 'drivers/i2c')
-rw-r--r--drivers/i2c/busses/i2c-aspeed.c9
1 files changed, 7 insertions, 2 deletions
diff --git a/drivers/i2c/busses/i2c-aspeed.c b/drivers/i2c/busses/i2c-aspeed.c
index c258c4d9a4c0..3d518e09369f 100644
--- a/drivers/i2c/busses/i2c-aspeed.c
+++ b/drivers/i2c/busses/i2c-aspeed.c
@@ -552,6 +552,9 @@ static irqreturn_t aspeed_i2c_bus_irq(int irq, void *dev_id)
552 552
553 spin_lock(&bus->lock); 553 spin_lock(&bus->lock);
554 irq_received = readl(bus->base + ASPEED_I2C_INTR_STS_REG); 554 irq_received = readl(bus->base + ASPEED_I2C_INTR_STS_REG);
555 /* Ack all interrupts except for Rx done */
556 writel(irq_received & ~ASPEED_I2CD_INTR_RX_DONE,
557 bus->base + ASPEED_I2C_INTR_STS_REG);
555 irq_remaining = irq_received; 558 irq_remaining = irq_received;
556 559
557#if IS_ENABLED(CONFIG_I2C_SLAVE) 560#if IS_ENABLED(CONFIG_I2C_SLAVE)
@@ -584,8 +587,10 @@ static irqreturn_t aspeed_i2c_bus_irq(int irq, void *dev_id)
584 "irq handled != irq. expected 0x%08x, but was 0x%08x\n", 587 "irq handled != irq. expected 0x%08x, but was 0x%08x\n",
585 irq_received, irq_handled); 588 irq_received, irq_handled);
586 589
587 /* Ack all interrupt bits. */ 590 /* Ack Rx done */
588 writel(irq_received, bus->base + ASPEED_I2C_INTR_STS_REG); 591 if (irq_received & ASPEED_I2CD_INTR_RX_DONE)
592 writel(ASPEED_I2CD_INTR_RX_DONE,
593 bus->base + ASPEED_I2C_INTR_STS_REG);
589 spin_unlock(&bus->lock); 594 spin_unlock(&bus->lock);
590 return irq_remaining ? IRQ_NONE : IRQ_HANDLED; 595 return irq_remaining ? IRQ_NONE : IRQ_HANDLED;
591} 596}