i2c: aspeed: Handle the coalesced stop conditions with the start conditions.
[ Upstream commit b4cc1cbba5195a4dd497cf2f8f09e7807977d543 ]
Some masters may drive the transfers with low enough latency between
the nak/stop phase of the current command and the start/address phase
of the following command that the interrupts are coalesced by the
time we process them.
Handle the stop conditions before processing SLAVE_MATCH to fix the
complaints that sometimes occur below.
"aspeed-i2c-bus 1e78a040.i2c-bus: irq handled != irq. Expected
0x00000086, but was 0x00000084"
Fixes: f9eb91350b
("i2c: aspeed: added slave support for Aspeed I2C driver")
Signed-off-by: Quan Nguyen <quan@os.amperecomputing.com>
Reviewed-by: Andrew Jeffery <andrew@codeconstruct.com.au>
Reviewed-by: Andi Shyti <andi.shyti@kernel.org>
Signed-off-by: Wolfram Sang <wsa@kernel.org>
Signed-off-by: Sasha Levin <sashal@kernel.org>
This commit is contained in:
Родитель
c87ab6e306
Коммит
f0c52b790a
|
@ -250,18 +250,46 @@ static u32 aspeed_i2c_slave_irq(struct aspeed_i2c_bus *bus, u32 irq_status)
|
|||
if (!slave)
|
||||
return 0;
|
||||
|
||||
command = readl(bus->base + ASPEED_I2C_CMD_REG);
|
||||
/*
|
||||
* Handle stop conditions early, prior to SLAVE_MATCH. Some masters may drive
|
||||
* transfers with low enough latency between the nak/stop phase of the current
|
||||
* command and the start/address phase of the following command that the
|
||||
* interrupts are coalesced by the time we process them.
|
||||
*/
|
||||
if (irq_status & ASPEED_I2CD_INTR_NORMAL_STOP) {
|
||||
irq_handled |= ASPEED_I2CD_INTR_NORMAL_STOP;
|
||||
bus->slave_state = ASPEED_I2C_SLAVE_STOP;
|
||||
}
|
||||
|
||||
/* Slave was requested, restart state machine. */
|
||||
if (irq_status & ASPEED_I2CD_INTR_TX_NAK &&
|
||||
bus->slave_state == ASPEED_I2C_SLAVE_READ_PROCESSED) {
|
||||
irq_handled |= ASPEED_I2CD_INTR_TX_NAK;
|
||||
bus->slave_state = ASPEED_I2C_SLAVE_STOP;
|
||||
}
|
||||
|
||||
/* Propagate any stop conditions to the slave implementation. */
|
||||
if (bus->slave_state == ASPEED_I2C_SLAVE_STOP) {
|
||||
i2c_slave_event(slave, I2C_SLAVE_STOP, &value);
|
||||
bus->slave_state = ASPEED_I2C_SLAVE_INACTIVE;
|
||||
}
|
||||
|
||||
/*
|
||||
* Now that we've dealt with any potentially coalesced stop conditions,
|
||||
* address any start conditions.
|
||||
*/
|
||||
if (irq_status & ASPEED_I2CD_INTR_SLAVE_MATCH) {
|
||||
irq_handled |= ASPEED_I2CD_INTR_SLAVE_MATCH;
|
||||
bus->slave_state = ASPEED_I2C_SLAVE_START;
|
||||
}
|
||||
|
||||
/* Slave is not currently active, irq was for someone else. */
|
||||
/*
|
||||
* If the slave has been stopped and not started then slave interrupt
|
||||
* handling is complete.
|
||||
*/
|
||||
if (bus->slave_state == ASPEED_I2C_SLAVE_INACTIVE)
|
||||
return irq_handled;
|
||||
|
||||
command = readl(bus->base + ASPEED_I2C_CMD_REG);
|
||||
dev_dbg(bus->dev, "slave irq status 0x%08x, cmd 0x%08x\n",
|
||||
irq_status, command);
|
||||
|
||||
|
@ -280,17 +308,6 @@ static u32 aspeed_i2c_slave_irq(struct aspeed_i2c_bus *bus, u32 irq_status)
|
|||
irq_handled |= ASPEED_I2CD_INTR_RX_DONE;
|
||||
}
|
||||
|
||||
/* Slave was asked to stop. */
|
||||
if (irq_status & ASPEED_I2CD_INTR_NORMAL_STOP) {
|
||||
irq_handled |= ASPEED_I2CD_INTR_NORMAL_STOP;
|
||||
bus->slave_state = ASPEED_I2C_SLAVE_STOP;
|
||||
}
|
||||
if (irq_status & ASPEED_I2CD_INTR_TX_NAK &&
|
||||
bus->slave_state == ASPEED_I2C_SLAVE_READ_PROCESSED) {
|
||||
irq_handled |= ASPEED_I2CD_INTR_TX_NAK;
|
||||
bus->slave_state = ASPEED_I2C_SLAVE_STOP;
|
||||
}
|
||||
|
||||
switch (bus->slave_state) {
|
||||
case ASPEED_I2C_SLAVE_READ_REQUESTED:
|
||||
if (unlikely(irq_status & ASPEED_I2CD_INTR_TX_ACK))
|
||||
|
@ -319,8 +336,7 @@ static u32 aspeed_i2c_slave_irq(struct aspeed_i2c_bus *bus, u32 irq_status)
|
|||
i2c_slave_event(slave, I2C_SLAVE_WRITE_RECEIVED, &value);
|
||||
break;
|
||||
case ASPEED_I2C_SLAVE_STOP:
|
||||
i2c_slave_event(slave, I2C_SLAVE_STOP, &value);
|
||||
bus->slave_state = ASPEED_I2C_SLAVE_INACTIVE;
|
||||
/* Stop event handling is done early. Unreachable. */
|
||||
break;
|
||||
case ASPEED_I2C_SLAVE_START:
|
||||
/* Slave was just started. Waiting for the next event. */;
|
||||
|
|
Загрузка…
Ссылка в новой задаче