diff options
author | Jeremy Kerr <jk@ozlabs.org> | 2016-05-25 12:12:26 +0300 |
---|---|---|
committer | Joel Stanley <joel@jms.id.au> | 2016-06-06 23:57:21 +0300 |
commit | b951233aa15e85d43aed2421e0adac1a2abc5770 (patch) | |
tree | b44b85dcf5aeb515d20786035ccb237f89c43c30 | |
parent | b21b3ad5e1594bce21d418817518aef0b6f47a6a (diff) | |
download | linux-b951233aa15e85d43aed2421e0adac1a2abc5770.tar.xz |
drivers/i2c/i2c-aspeed: Only use IRQ status for command completion notificationopenbmc-20160606-1
We currently determine if an i2c command as complete by looking at the command
register (and comparing it with the pending set of commands). While this
may indicate that all pending commands have completed, we may still get
a further interrupt when multiple commands (like a read + stop) have
been queued.
This change uses the interrupt status to indicate command completion,
instead of the command register.
Signed-off-by: Jeremy Kerr <jk@ozlabs.org>
Signed-off-by: Joel Stanley <joel@jms.id.au>
-rw-r--r-- | drivers/i2c/busses/i2c-aspeed.c | 40 |
1 files changed, 31 insertions, 9 deletions
diff --git a/drivers/i2c/busses/i2c-aspeed.c b/drivers/i2c/busses/i2c-aspeed.c index b8e819996dcd..d0d8e7ff803a 100644 --- a/drivers/i2c/busses/i2c-aspeed.c +++ b/drivers/i2c/busses/i2c-aspeed.c @@ -216,12 +216,11 @@ /* Fast Mode = 400 kHz, Standard = 100 kHz */ //static int clock = 100; /* Default: 100 kHz */ +/* bitmask of commands that we wait for, in the cmd_pending mask */ #define AST_I2CD_CMDS (AST_I2CD_BUS_RECOVER_CMD_EN | \ AST_I2CD_M_STOP_CMD | \ - AST_I2CD_M_S_RX_CMD_LAST | \ AST_I2CD_M_RX_CMD | \ - AST_I2CD_M_TX_CMD | \ - AST_I2CD_M_START_CMD) + AST_I2CD_M_TX_CMD) static const int ast_i2c_n_busses = 14; @@ -338,7 +337,8 @@ static void ast_i2c_issue_cmd(struct ast_i2c_bus *bus, u32 cmd) { dev_dbg(bus->dev, "issuing cmd: %x\n", cmd); bus->cmd_err = 0; - bus->cmd_sent = bus->cmd_pending = cmd & AST_I2CD_CMDS; + bus->cmd_sent = cmd; + bus->cmd_pending = cmd & AST_I2CD_CMDS; ast_i2c_write(bus, cmd, I2C_CMD_REG); } @@ -554,18 +554,40 @@ static irqreturn_t ast_i2c_bus_irq(int irq, void *dev_id) ast_i2c_write(bus, sts, I2C_INTR_STS_REG); bus->cmd_err |= sts & errs; - bus->cmd_pending = cmd & AST_I2CD_CMDS; + + /** + * Mask-out pending commands that this interrupt has indicated are + * complete. These checks need to cover all of the possible bits set + * in the AST_I2CD_CMDS bitmask. + */ + if (sts & AST_I2CD_INTR_TX_ACK) + bus->cmd_pending &= ~AST_I2CD_M_TX_CMD; + + if (sts & AST_I2CD_INTR_RX_DONE) + bus->cmd_pending &= ~AST_I2CD_M_RX_CMD; + + if (sts & AST_I2CD_INTR_NORMAL_STOP) + bus->cmd_pending &= ~AST_I2CD_M_STOP_CMD; + + if (sts & AST_I2CD_INTR_BUS_RECOVER_DONE) + bus->cmd_pending &= ~AST_I2CD_BUS_RECOVER_CMD_EN; /* if we've seen an error, notify our waiter */ if (bus->cmd_err) { complete(&bus->cmd_complete); - /* We have a transfer in progress */ - } else if (bus->msg && !bus->cmd_pending) { + /* still have work to do? We'll wait for the corresponding IRQ(s) for + * that to complete. */ + } else if (bus->cmd_pending) { + dev_dbg(bus->dev, "cmds pending: 0x%x\n", bus->cmd_pending); + + /* message transfer complete */ + } else if (bus->msg) { ast_i2c_master_xfer_done(bus); - /* Other message queued: recovery, error stop. Notify waiters. */ - } else if (bus->cmd_sent && !bus->cmd_pending) { + /* other (non-message) command complete: recovery, error stop. Notify + * waiters. */ + } else if (bus->cmd_sent) { complete(&bus->cmd_complete); } else { |