diff options
author | Chaitanya Bandi <bandik@nvidia.com> | 2013-02-27 15:13:35 +0530 |
---|---|---|
committer | Riham Haidar <rhaidar@nvidia.com> | 2013-03-13 16:22:54 -0700 |
commit | 6ccd093d5282dbddb5c590d6c1ebd4cc5d097a12 (patch) | |
tree | 172e8576c04bd1061a4267baf6e225ebc78d0e73 /drivers/i2c | |
parent | 84d79a515e3074d390a91755f931def47ff383c7 (diff) |
i2c: tegra: Use ALL_PACKETS_XFER_COMPLETE interrupt
The issue is that some clock stretching is observed
in case of coupled Write-Read transaction after the write
transaction because we wait for PACKET_XFER_COMPLETE.
Using ALL_PACKETS_XFER_COMPLETE will prevent that clock
stretching. In case of non-coupled transactions,
PACKET_XFER_COMPLETE is used.
Bug 1234504
Change-Id: I7e51bc0cc674bc91ca63b15e3d4b8696de82e1cb
Signed-off-by: Chaitanya Bandi <bandik@nvidia.com>
Reviewed-on: http://git-master/r/204520
Reviewed-by: Laxman Dewangan <ldewangan@nvidia.com>
Diffstat (limited to 'drivers/i2c')
-rw-r--r-- | drivers/i2c/busses/i2c-tegra.c | 42 |
1 files changed, 24 insertions, 18 deletions
diff --git a/drivers/i2c/busses/i2c-tegra.c b/drivers/i2c/busses/i2c-tegra.c index 9a42f6423ace..45b40332beeb 100644 --- a/drivers/i2c/busses/i2c-tegra.c +++ b/drivers/i2c/busses/i2c-tegra.c @@ -197,7 +197,6 @@ struct tegra_i2c_dev { bool irq_disabled; int is_dvc; struct completion msg_complete; - bool has_next_msg; int msg_err; int next_msg_err; u8 *msg_buf; @@ -225,6 +224,7 @@ struct tegra_i2c_dev { bool is_clkon_always; bool is_high_speed_enable; u16 hs_master_code; + bool use_single_xfer_complete; int (*arb_recovery)(int scl_gpio, int sda_gpio); struct tegra_i2c_chipdata *chipdata; struct tegra_i2c_bus busses[1]; @@ -567,7 +567,6 @@ static int tegra_i2c_init(struct tegra_i2c_dev *i2c_dev) } static int tegra_i2c_copy_next_to_current(struct tegra_i2c_dev *i2c_dev) { - i2c_dev->has_next_msg = 0; i2c_dev->msg_buf = i2c_dev->next_msg_buf; i2c_dev->msg_buf_remaining = i2c_dev->next_msg_buf_remaining; i2c_dev->msg_err = i2c_dev->next_msg_err; @@ -679,14 +678,15 @@ static irqreturn_t tegra_i2c_isr(int irq, void *dev_id) if (i2c_dev->is_dvc) dvc_writel(i2c_dev, DVC_STATUS_I2C_DONE_INTR, DVC_STATUS); - if (status & I2C_INT_PACKET_XFER_COMPLETE) { + if (status & I2C_INT_ALL_PACKETS_XFER_COMPLETE) { BUG_ON(i2c_dev->msg_buf_remaining); - if (i2c_dev->has_next_msg) { - tegra_i2c_copy_next_to_current(i2c_dev); - } - else - complete(&i2c_dev->msg_complete); + complete(&i2c_dev->msg_complete); + } else if ((status & I2C_INT_PACKET_XFER_COMPLETE) + && i2c_dev->use_single_xfer_complete) { + BUG_ON(i2c_dev->msg_buf_remaining); + complete(&i2c_dev->msg_complete); } + return IRQ_HANDLED; err: @@ -739,7 +739,6 @@ err: static int tegra_i2c_send_next_read_msg_pkt_header(struct tegra_i2c_dev *i2c_dev, struct i2c_msg *next_msg, enum msg_end_type end_state) { - i2c_dev->has_next_msg = true; i2c_dev->next_msg_buf = next_msg->buf; i2c_dev->next_msg_buf_remaining = next_msg->len; i2c_dev->next_msg_err = I2C_ERR_NONE; @@ -817,7 +816,11 @@ static int tegra_i2c_xfer_msg(struct tegra_i2c_bus *i2c_bus, i2c_dev->payload_size = msg->len - 1; i2c_writel(i2c_dev, i2c_dev->payload_size, I2C_TX_FIFO); - i2c_dev->io_header = I2C_HEADER_IE_ENABLE; + i2c_dev->use_single_xfer_complete = true; + i2c_dev->io_header = 0; + if (next_msg == NULL) + i2c_dev->io_header = I2C_HEADER_IE_ENABLE; + if (end_state == MSG_END_CONTINUE) i2c_dev->io_header |= I2C_HEADER_CONTINUE_XFER; else if (end_state == MSG_END_REPEAT_START) @@ -840,7 +843,6 @@ static int tegra_i2c_xfer_msg(struct tegra_i2c_bus *i2c_bus, } i2c_writel(i2c_dev, i2c_dev->io_header, I2C_TX_FIFO); - if (!(msg->flags & I2C_M_RD)) tegra_i2c_fill_tx_fifo(i2c_dev); @@ -852,23 +854,27 @@ static int tegra_i2c_xfer_msg(struct tegra_i2c_bus *i2c_bus, if (i2c_dev->chipdata->has_xfer_complete_interrupt) int_mask |= I2C_INT_PACKET_XFER_COMPLETE; + if (i2c_dev->chipdata->has_xfer_complete_interrupt) + int_mask |= I2C_INT_ALL_PACKETS_XFER_COMPLETE; + if (msg->flags & I2C_M_RD) int_mask |= I2C_INT_RX_FIFO_DATA_REQ; else if (i2c_dev->msg_buf_remaining) int_mask |= I2C_INT_TX_FIFO_DATA_REQ; - i2c_dev->has_next_msg = 0; - if (next_msg != NULL) { tegra_i2c_send_next_read_msg_pkt_header(i2c_dev, next_msg, next_msg_end_state); + tegra_i2c_copy_next_to_current(i2c_dev); int_mask |= I2C_INT_RX_FIFO_DATA_REQ; + i2c_dev->use_single_xfer_complete = false; } if (!i2c_dev->chipdata->has_xfer_complete_interrupt) spin_unlock_irqrestore(&i2c_dev->fifo_lock, flags); tegra_i2c_unmask_irq(i2c_dev, int_mask); + dev_dbg(i2c_dev->dev, "unmasked irq: %02x\n", i2c_readl(i2c_dev, I2C_INT_MASK)); @@ -1011,18 +1017,18 @@ static int tegra_i2c_xfer(struct i2c_adapter *adap, struct i2c_msg msgs[], else next_msg_end_type = MSG_END_REPEAT_START; } - if ((!(msgs[i].flags & I2C_M_RD)) && (msgs[i].len <= 8) && (msgs[i+1].flags & I2C_M_RD)) { + if ((!(msgs[i].flags & I2C_M_RD)) && (msgs[i].len <= 8) && (msgs[i+1].flags & I2C_M_RD) + && (next_msg_end_type != MSG_END_CONTINUE) && (end_type == MSG_END_REPEAT_START)) { ret = tegra_i2c_xfer_msg(i2c_bus, &msgs[i], end_type, &msgs[i+1], next_msg_end_type); if (ret) break; i++; - } - else + } else { ret = tegra_i2c_xfer_msg(i2c_bus, &msgs[i], end_type, NULL, next_msg_end_type); if (ret) break; - } - else { + } + } else { ret = tegra_i2c_xfer_msg(i2c_bus, &msgs[i], end_type, NULL, next_msg_end_type); if (ret) break; |