summaryrefslogtreecommitdiff
path: root/drivers/i2c/busses/i2c-tegra.c
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/i2c/busses/i2c-tegra.c')
-rw-r--r--drivers/i2c/busses/i2c-tegra.c79
1 files changed, 67 insertions, 12 deletions
diff --git a/drivers/i2c/busses/i2c-tegra.c b/drivers/i2c/busses/i2c-tegra.c
index 0a6b21f8f077..d50c5e5db862 100644
--- a/drivers/i2c/busses/i2c-tegra.c
+++ b/drivers/i2c/busses/i2c-tegra.c
@@ -19,6 +19,7 @@
#include <linux/init.h>
#include <linux/platform_device.h>
#include <linux/clk.h>
+#include <linux/err.h>
#include <linux/i2c.h>
#include <linux/io.h>
#include <linux/interrupt.h>
@@ -44,8 +45,8 @@
#define I2C_SL_CNFG 0x020
#define I2C_SL_CNFG_NACK (1<<1)
#define I2C_SL_CNFG_NEWSL (1<<2)
-#define I2C_SL_ADDR1 0x02c
-#define I2C_SL_ADDR2 0x030
+#define I2C_SL_ADDR1 0x02c
+#define I2C_SL_ADDR2 0x030
#define I2C_TX_FIFO 0x050
#define I2C_RX_FIFO 0x054
#define I2C_PACKET_TRANSFER_STATUS 0x058
@@ -136,6 +137,7 @@ struct tegra_i2c_dev {
int msg_read;
int msg_transfer_complete;
struct i2c_msg *msgs;
+ int msg_add;
int msgs_num;
bool is_suspended;
int bus_count;
@@ -156,6 +158,20 @@ static u32 dvc_readl(struct tegra_i2c_dev *i2c_dev, unsigned long reg)
return readl(i2c_dev->base + reg);
}
+static void dvc_i2c_mask_irq(struct tegra_i2c_dev *i2c_dev, u32 mask)
+{
+ u32 int_mask = dvc_readl(i2c_dev, DVC_CTRL_REG3);
+ int_mask &= ~mask;
+ dvc_writel(i2c_dev, int_mask, DVC_CTRL_REG3);
+}
+
+static void dvc_i2c_unmask_irq(struct tegra_i2c_dev *i2c_dev, u32 mask)
+{
+ u32 int_mask = dvc_readl(i2c_dev, DVC_CTRL_REG3);
+ int_mask |= mask;
+ dvc_writel(i2c_dev, int_mask, DVC_CTRL_REG3);
+}
+
/* i2c_writel and i2c_readl will offset the register if necessary to talk
* to the I2C block inside the DVC block
*/
@@ -272,10 +288,15 @@ static int tegra_i2c_fill_tx_fifo(struct tegra_i2c_dev *i2c_dev)
for (word = 0; word < words_to_transfer; word++) {
val = get_unaligned_le32(buf);
- i2c_writel(i2c_dev, val, I2C_TX_FIFO);
+
+ /* Update the field before writing into Tx Fifo */
buf += BYTES_PER_FIFO_WORD;
buf_remaining -= BYTES_PER_FIFO_WORD;
tx_fifo_avail--;
+ i2c_dev->msg_buf_remaining = buf_remaining;
+ i2c_dev->msg_buf = buf;
+
+ i2c_writel(i2c_dev, val, I2C_TX_FIFO);
}
if (tx_fifo_avail > 0 && buf_remaining > 0) {
@@ -285,9 +306,14 @@ static int tegra_i2c_fill_tx_fifo(struct tegra_i2c_dev *i2c_dev)
val = 0;
for (byte = 0; byte < bytes_to_transfer; byte++)
val |= (*buf++) << (byte * 8);
- i2c_writel(i2c_dev, val, I2C_TX_FIFO);
+
+ /* Update the field before writing into Tx Fifo */
buf_remaining -= bytes_to_transfer;
tx_fifo_avail--;
+ i2c_dev->msg_buf_remaining = buf_remaining;
+ i2c_dev->msg_buf = buf;
+
+ i2c_writel(i2c_dev, val, I2C_TX_FIFO);
}
BUG_ON(tx_fifo_avail > 0 && buf_remaining > 0);
i2c_dev->msg_buf_remaining = buf_remaining;
@@ -306,7 +332,6 @@ static void tegra_dvc_init(struct tegra_i2c_dev *i2c_dev)
u32 val = 0;
val = dvc_readl(i2c_dev, DVC_CTRL_REG3);
val |= DVC_CTRL_REG3_SW_PROG;
- val |= DVC_CTRL_REG3_I2C_DONE_INTR_EN;
dvc_writel(i2c_dev, val, DVC_CTRL_REG3);
val = dvc_readl(i2c_dev, DVC_CTRL_REG1);
@@ -335,6 +360,10 @@ static int tegra_i2c_init(struct tegra_i2c_dev *i2c_dev)
clk_enable(i2c_dev->clk);
+ /* Interrupt generated before sending stop signal so
+ * wait for some time so that stop signal can be send proerly */
+ mdelay(1);
+
tegra_periph_reset_assert(i2c_dev->clk);
udelay(2);
tegra_periph_reset_deassert(i2c_dev->clk);
@@ -370,13 +399,14 @@ static int tegra_i2c_init(struct tegra_i2c_dev *i2c_dev)
static irqreturn_t tegra_i2c_isr(int irq, void *dev_id)
{
u32 status;
- const u32 status_err = I2C_INT_NO_ACK | I2C_INT_ARBITRATION_LOST;
+ const u32 status_err = I2C_INT_NO_ACK | I2C_INT_ARBITRATION_LOST | I2C_INT_TX_FIFO_OVERFLOW;
struct tegra_i2c_dev *i2c_dev = dev_id;
status = i2c_readl(i2c_dev, I2C_INT_STATUS);
if (status == 0) {
- dev_warn(i2c_dev->dev, "unknown interrupt\n");
+ dev_err(i2c_dev->dev, "unknown interrupt Add 0x%02x\n",
+ i2c_dev->msg_add);
i2c_dev->msg_err |= I2C_ERR_UNKNOWN_INTERRUPT;
if (!i2c_dev->irq_disabled) {
@@ -389,16 +419,30 @@ static irqreturn_t tegra_i2c_isr(int irq, void *dev_id)
}
if (unlikely(status & status_err)) {
+ dev_err(i2c_dev->dev, "I2c error status 0x%08x\n", status);
if (status & I2C_INT_NO_ACK) {
i2c_dev->msg_err |= I2C_ERR_NO_ACK;
- dev_warn(i2c_dev->dev, "no acknowledge\n");
+ dev_err(i2c_dev->dev, "no acknowledge from address"
+ " 0x%x\n", i2c_dev->msg_add);
+ dev_err(i2c_dev->dev, "Packet status 0x%08x\n",
+ i2c_readl(i2c_dev, I2C_PACKET_TRANSFER_STATUS));
}
if (status & I2C_INT_ARBITRATION_LOST) {
i2c_dev->msg_err |= I2C_ERR_ARBITRATION_LOST;
- dev_warn(i2c_dev->dev, "arbitration lost\n");
+ dev_err(i2c_dev->dev, "arbitration lost during "
+ " communicate to add 0x%x\n", i2c_dev->msg_add);
+ dev_err(i2c_dev->dev, "Packet status 0x%08x\n",
+ i2c_readl(i2c_dev, I2C_PACKET_TRANSFER_STATUS));
}
+ if (status & I2C_INT_TX_FIFO_OVERFLOW) {
+ i2c_dev->msg_err |= I2C_INT_TX_FIFO_OVERFLOW;
+ dev_warn(i2c_dev->dev, "Tx fifo overflow during "
+ " communicate to add 0x%x\n", i2c_dev->msg_add);
+ dev_warn(i2c_dev->dev, "Packet status 0x%08x\n",
+ i2c_readl(i2c_dev, I2C_PACKET_TRANSFER_STATUS));
+ }
complete(&i2c_dev->msg_complete);
goto err;
}
@@ -407,7 +451,7 @@ static irqreturn_t tegra_i2c_isr(int irq, void *dev_id)
&& (status == I2C_INT_TX_FIFO_DATA_REQ)
&& i2c_dev->msg_read
&& i2c_dev->msg_buf_remaining)) {
- dev_warn(i2c_dev->dev, "unexpected status\n");
+ dev_err(i2c_dev->dev, "unexpected status\n");
i2c_dev->msg_err |= I2C_ERR_UNEXPECTED_STATUS;
if (!i2c_dev->irq_disabled) {
@@ -470,10 +514,14 @@ err:
/* An error occured, mask all interrupts */
tegra_i2c_mask_irq(i2c_dev, I2C_INT_NO_ACK | I2C_INT_ARBITRATION_LOST |
I2C_INT_PACKET_XFER_COMPLETE | I2C_INT_TX_FIFO_DATA_REQ |
- I2C_INT_RX_FIFO_DATA_REQ);
+ I2C_INT_RX_FIFO_DATA_REQ | I2C_INT_TX_FIFO_OVERFLOW);
i2c_writel(i2c_dev, status, I2C_INT_STATUS);
+ /* An error occured, mask dvc interrupt */
+ if (i2c_dev->is_dvc)
+ dvc_i2c_mask_irq(i2c_dev, DVC_CTRL_REG3_I2C_DONE_INTR_EN);
+
if (i2c_dev->is_dvc)
dvc_writel(i2c_dev, DVC_STATUS_I2C_DONE_INTR, DVC_STATUS);
@@ -499,6 +547,7 @@ static int tegra_i2c_xfer_msg(struct tegra_i2c_bus *i2c_bus,
i2c_dev->msg_transfer_complete = 0;
i2c_dev->msg_read = (msg->flags & I2C_M_RD);
INIT_COMPLETION(i2c_dev->msg_complete);
+ i2c_dev->msg_add = msg->addr;
i2c_dev->packet_header = (0 << PACKET_HEADER0_HEADER_SIZE_SHIFT) |
PACKET_HEADER0_PROTOCOL_I2C |
@@ -524,7 +573,10 @@ static int tegra_i2c_xfer_msg(struct tegra_i2c_bus *i2c_bus,
if (!(msg->flags & I2C_M_RD))
tegra_i2c_fill_tx_fifo(i2c_dev);
- int_mask = I2C_INT_NO_ACK | I2C_INT_ARBITRATION_LOST;
+ if (i2c_dev->is_dvc)
+ dvc_i2c_unmask_irq(i2c_dev, DVC_CTRL_REG3_I2C_DONE_INTR_EN);
+
+ int_mask = I2C_INT_NO_ACK | I2C_INT_ARBITRATION_LOST | I2C_INT_TX_FIFO_OVERFLOW;
if (msg->flags & I2C_M_RD)
int_mask |= I2C_INT_RX_FIFO_DATA_REQ;
else if (i2c_dev->msg_buf_remaining)
@@ -536,6 +588,9 @@ static int tegra_i2c_xfer_msg(struct tegra_i2c_bus *i2c_bus,
TEGRA_I2C_TIMEOUT);
tegra_i2c_mask_irq(i2c_dev, int_mask);
+ if (i2c_dev->is_dvc)
+ dvc_i2c_mask_irq(i2c_dev, DVC_CTRL_REG3_I2C_DONE_INTR_EN);
+
if (WARN_ON(ret == 0)) {
dev_err(i2c_dev->dev,
"i2c transfer timed out, addr 0x%04x, data 0x%02x\n",