diff options
Diffstat (limited to 'drivers/i2c/busses/i2c-axxia.c')
-rw-r--r-- | drivers/i2c/busses/i2c-axxia.c | 57 |
1 files changed, 19 insertions, 38 deletions
diff --git a/drivers/i2c/busses/i2c-axxia.c b/drivers/i2c/busses/i2c-axxia.c index bf564391091f..1c7b41f45c83 100644 --- a/drivers/i2c/busses/i2c-axxia.c +++ b/drivers/i2c/busses/i2c-axxia.c @@ -99,6 +99,7 @@ * @adapter: core i2c abstraction * @i2c_clk: clock reference for i2c input clock * @bus_clk_rate: current i2c bus clock rate + * @last: a flag indicating is this is last message in transfer */ struct axxia_i2c_dev { void __iomem *base; @@ -112,6 +113,7 @@ struct axxia_i2c_dev { struct i2c_adapter adapter; struct clk *i2c_clk; u32 bus_clk_rate; + bool last; }; static void i2c_int_disable(struct axxia_i2c_dev *idev, u32 mask) @@ -324,15 +326,14 @@ static irqreturn_t axxia_i2c_isr(int irq, void *_dev) /* Stop completed */ i2c_int_disable(idev, ~MST_STATUS_TSS); complete(&idev->msg_complete); - } else if (status & MST_STATUS_SNS) { + } else if (status & (MST_STATUS_SNS | MST_STATUS_SS)) { /* Transfer done */ - i2c_int_disable(idev, ~MST_STATUS_TSS); + int mask = idev->last ? ~0 : ~MST_STATUS_TSS; + + i2c_int_disable(idev, mask); if (i2c_m_rd(idev->msg_r) && idev->msg_xfrd_r < idev->msg_r->len) axxia_i2c_empty_rx_fifo(idev); complete(&idev->msg_complete); - } else if (status & MST_STATUS_SS) { - /* Auto/Sequence transfer done */ - complete(&idev->msg_complete); } else if (status & MST_STATUS_TSS) { /* Transfer timeout */ idev->msg_err = -ETIMEDOUT; @@ -405,6 +406,7 @@ static int axxia_i2c_xfer_seq(struct axxia_i2c_dev *idev, struct i2c_msg msgs[]) idev->msg_r = &msgs[1]; idev->msg_xfrd = 0; idev->msg_xfrd_r = 0; + idev->last = true; axxia_i2c_fill_tx_fifo(idev); writel(CMD_SEQUENCE, idev->base + MST_COMMAND); @@ -415,10 +417,6 @@ static int axxia_i2c_xfer_seq(struct axxia_i2c_dev *idev, struct i2c_msg msgs[]) time_left = wait_for_completion_timeout(&idev->msg_complete, I2C_XFER_TIMEOUT); - i2c_int_disable(idev, int_mask); - - axxia_i2c_empty_rx_fifo(idev); - if (idev->msg_err == -ENXIO) { if (axxia_i2c_handle_seq_nak(idev)) axxia_i2c_init(idev); @@ -438,9 +436,10 @@ static int axxia_i2c_xfer_seq(struct axxia_i2c_dev *idev, struct i2c_msg msgs[]) return idev->msg_err; } -static int axxia_i2c_xfer_msg(struct axxia_i2c_dev *idev, struct i2c_msg *msg) +static int axxia_i2c_xfer_msg(struct axxia_i2c_dev *idev, struct i2c_msg *msg, + bool last) { - u32 int_mask = MST_STATUS_ERR | MST_STATUS_SNS; + u32 int_mask = MST_STATUS_ERR; u32 rx_xfer, tx_xfer; unsigned long time_left; unsigned int wt_value; @@ -449,6 +448,7 @@ static int axxia_i2c_xfer_msg(struct axxia_i2c_dev *idev, struct i2c_msg *msg) idev->msg_r = msg; idev->msg_xfrd = 0; idev->msg_xfrd_r = 0; + idev->last = last; reinit_completion(&idev->msg_complete); axxia_i2c_set_addr(idev, msg); @@ -478,8 +478,13 @@ static int axxia_i2c_xfer_msg(struct axxia_i2c_dev *idev, struct i2c_msg *msg) if (idev->msg_err) goto out; - /* Start manual mode */ - writel(CMD_MANUAL, idev->base + MST_COMMAND); + if (!last) { + writel(CMD_MANUAL, idev->base + MST_COMMAND); + int_mask |= MST_STATUS_SNS; + } else { + writel(CMD_AUTO, idev->base + MST_COMMAND); + int_mask |= MST_STATUS_SS; + } writel(WT_EN | wt_value, idev->base + WAIT_TIMER_CONTROL); @@ -507,28 +512,6 @@ out: return idev->msg_err; } -static int axxia_i2c_stop(struct axxia_i2c_dev *idev) -{ - u32 int_mask = MST_STATUS_ERR | MST_STATUS_SCC | MST_STATUS_TSS; - unsigned long time_left; - - reinit_completion(&idev->msg_complete); - - /* Issue stop */ - writel(0xb, idev->base + MST_COMMAND); - i2c_int_enable(idev, int_mask); - time_left = wait_for_completion_timeout(&idev->msg_complete, - I2C_STOP_TIMEOUT); - i2c_int_disable(idev, int_mask); - if (time_left == 0) - return -ETIMEDOUT; - - if (readl(idev->base + MST_COMMAND) & CMD_BUSY) - dev_warn(idev->dev, "busy after stop\n"); - - return 0; -} - /* This function checks if the msgs[] array contains messages compatible with * Sequence mode of operation. This mode assumes there will be exactly one * write of non-zero length followed by exactly one read of non-zero length, @@ -558,9 +541,7 @@ axxia_i2c_xfer(struct i2c_adapter *adap, struct i2c_msg msgs[], int num) i2c_int_enable(idev, MST_STATUS_TSS); for (i = 0; ret == 0 && i < num; ++i) - ret = axxia_i2c_xfer_msg(idev, &msgs[i]); - - axxia_i2c_stop(idev); + ret = axxia_i2c_xfer_msg(idev, &msgs[i], i == (num - 1)); return ret ? : i; } |