summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorJeremy Kerr <jk@ozlabs.org>2016-05-25 12:12:26 +0300
committerJoel Stanley <joel@jms.id.au>2016-06-06 23:57:21 +0300
commitb951233aa15e85d43aed2421e0adac1a2abc5770 (patch)
treeb44b85dcf5aeb515d20786035ccb237f89c43c30
parentb21b3ad5e1594bce21d418817518aef0b6f47a6a (diff)
downloadlinux-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.c40
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 {