Skip to content

Commit 2550d96

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 5c11f63 commit 2550d96

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
@@ -249,18 +249,46 @@ static u32 aspeed_i2c_slave_irq(struct aspeed_i2c_bus *bus, u32 irq_status)
249249
if (!slave)
250250
return 0;
251251

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

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

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

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

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

282-
/* Slave was asked to stop. */
283-
if (irq_status & ASPEED_I2CD_INTR_NORMAL_STOP) {
284-
irq_handled |= ASPEED_I2CD_INTR_NORMAL_STOP;
285-
bus->slave_state = ASPEED_I2C_SLAVE_STOP;
286-
}
287-
if (irq_status & ASPEED_I2CD_INTR_TX_NAK &&
288-
bus->slave_state == ASPEED_I2C_SLAVE_READ_PROCESSED) {
289-
irq_handled |= ASPEED_I2CD_INTR_TX_NAK;
290-
bus->slave_state = ASPEED_I2C_SLAVE_STOP;
291-
}
292-
293310
switch (bus->slave_state) {
294311
case ASPEED_I2C_SLAVE_READ_REQUESTED:
295312
if (unlikely(irq_status & ASPEED_I2CD_INTR_TX_ACK))
@@ -324,8 +341,7 @@ static u32 aspeed_i2c_slave_irq(struct aspeed_i2c_bus *bus, u32 irq_status)
324341
i2c_slave_event(slave, I2C_SLAVE_WRITE_RECEIVED, &value);
325342
break;
326343
case ASPEED_I2C_SLAVE_STOP:
327-
i2c_slave_event(slave, I2C_SLAVE_STOP, &value);
328-
bus->slave_state = ASPEED_I2C_SLAVE_INACTIVE;
344+
/* Stop event handling is done early. Unreachable. */
329345
break;
330346
case ASPEED_I2C_SLAVE_START:
331347
/* Slave was just started. Waiting for the next event. */;

0 commit comments

Comments
 (0)