Skip to content

Commit 0b85149

Browse files
qnguyen-amperegregkh
authored andcommitted
i2c: aspeed: Handle the coalesced stop conditions with the start conditions.
[ Upstream commit b4cc1cb ] 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: f9eb913 ("i2c: aspeed: added slave support for Aspeed I2C driver") Signed-off-by: Quan Nguyen <[email protected]> Reviewed-by: Andrew Jeffery <[email protected]> Reviewed-by: Andi Shyti <[email protected]> Signed-off-by: Wolfram Sang <[email protected]> Signed-off-by: Sasha Levin <[email protected]>
1 parent 3dce7a5 commit 0b85149

File tree

1 file changed

+32
-16
lines changed

1 file changed

+32
-16
lines changed

drivers/i2c/busses/i2c-aspeed.c

Lines changed: 32 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -250,18 +250,46 @@ static u32 aspeed_i2c_slave_irq(struct aspeed_i2c_bus *bus, u32 irq_status)
250250
if (!slave)
251251
return 0;
252252

253-
command = readl(bus->base + ASPEED_I2C_CMD_REG);
253+
/*
254+
* Handle stop conditions early, prior to SLAVE_MATCH. Some masters may drive
255+
* transfers with low enough latency between the nak/stop phase of the current
256+
* command and the start/address phase of the following command that the
257+
* interrupts are coalesced by the time we process them.
258+
*/
259+
if (irq_status & ASPEED_I2CD_INTR_NORMAL_STOP) {
260+
irq_handled |= ASPEED_I2CD_INTR_NORMAL_STOP;
261+
bus->slave_state = ASPEED_I2C_SLAVE_STOP;
262+
}
263+
264+
if (irq_status & ASPEED_I2CD_INTR_TX_NAK &&
265+
bus->slave_state == ASPEED_I2C_SLAVE_READ_PROCESSED) {
266+
irq_handled |= ASPEED_I2CD_INTR_TX_NAK;
267+
bus->slave_state = ASPEED_I2C_SLAVE_STOP;
268+
}
269+
270+
/* Propagate any stop conditions to the slave implementation. */
271+
if (bus->slave_state == ASPEED_I2C_SLAVE_STOP) {
272+
i2c_slave_event(slave, I2C_SLAVE_STOP, &value);
273+
bus->slave_state = ASPEED_I2C_SLAVE_INACTIVE;
274+
}
254275

255-
/* Slave was requested, restart state machine. */
276+
/*
277+
* Now that we've dealt with any potentially coalesced stop conditions,
278+
* address any start conditions.
279+
*/
256280
if (irq_status & ASPEED_I2CD_INTR_SLAVE_MATCH) {
257281
irq_handled |= ASPEED_I2CD_INTR_SLAVE_MATCH;
258282
bus->slave_state = ASPEED_I2C_SLAVE_START;
259283
}
260284

261-
/* Slave is not currently active, irq was for someone else. */
285+
/*
286+
* If the slave has been stopped and not started then slave interrupt
287+
* handling is complete.
288+
*/
262289
if (bus->slave_state == ASPEED_I2C_SLAVE_INACTIVE)
263290
return irq_handled;
264291

292+
command = readl(bus->base + ASPEED_I2C_CMD_REG);
265293
dev_dbg(bus->dev, "slave irq status 0x%08x, cmd 0x%08x\n",
266294
irq_status, command);
267295

@@ -280,17 +308,6 @@ static u32 aspeed_i2c_slave_irq(struct aspeed_i2c_bus *bus, u32 irq_status)
280308
irq_handled |= ASPEED_I2CD_INTR_RX_DONE;
281309
}
282310

283-
/* Slave was asked to stop. */
284-
if (irq_status & ASPEED_I2CD_INTR_NORMAL_STOP) {
285-
irq_handled |= ASPEED_I2CD_INTR_NORMAL_STOP;
286-
bus->slave_state = ASPEED_I2C_SLAVE_STOP;
287-
}
288-
if (irq_status & ASPEED_I2CD_INTR_TX_NAK &&
289-
bus->slave_state == ASPEED_I2C_SLAVE_READ_PROCESSED) {
290-
irq_handled |= ASPEED_I2CD_INTR_TX_NAK;
291-
bus->slave_state = ASPEED_I2C_SLAVE_STOP;
292-
}
293-
294311
switch (bus->slave_state) {
295312
case ASPEED_I2C_SLAVE_READ_REQUESTED:
296313
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)
319336
i2c_slave_event(slave, I2C_SLAVE_WRITE_RECEIVED, &value);
320337
break;
321338
case ASPEED_I2C_SLAVE_STOP:
322-
i2c_slave_event(slave, I2C_SLAVE_STOP, &value);
323-
bus->slave_state = ASPEED_I2C_SLAVE_INACTIVE;
339+
/* Stop event handling is done early. Unreachable. */
324340
break;
325341
case ASPEED_I2C_SLAVE_START:
326342
/* Slave was just started. Waiting for the next event. */;

0 commit comments

Comments
 (0)