summaryrefslogtreecommitdiff
path: root/drivers/i2c/busses/i2c-tegra.c
diff options
context:
space:
mode:
authorAlok Chauhan <alokc@nvidia.com>2011-05-05 10:32:20 +0530
committerDan Willemsen <dwillemsen@nvidia.com>2011-11-30 21:41:52 -0800
commit579ecde73f6dbc5d79985eb5f10bc58b6e96367e (patch)
tree8408232c4b78afd96725356ff49a885b12f94a3d /drivers/i2c/busses/i2c-tegra.c
parent4268cd392fdb3bf94a33fadaaf06e7bb19c5cb94 (diff)
i2c: tegra: Making proper synchronization between isr and caller
Following are the changes: - Added a mechanism to handle synchronization between caller thread and isr so that isr should not get called when clock is disable or controller is in reset state. - Handling tx fifo overflow error. - Added delay before resetting the controller which is required if there is any error in previous transaction so proper stop signal will be generated. Original-Change-Id: I7ac121e056193cd4601f032cdabab59e7e0946ea Reviewed-on: http://git-master/r/30457 Reviewed-by: Laxman Dewangan <ldewangan@nvidia.com> Reviewed-by: Alok Chauhan <alokc@nvidia.com> Tested-by: Alok Chauhan <alokc@nvidia.com> Tested-by: Laxman Dewangan <ldewangan@nvidia.com> Reviewed-by: Danielle Sun <dsun@nvidia.com> Tested-by: Danielle Sun <dsun@nvidia.com> Reviewed-by: Bo Yan <byan@nvidia.com> Reviewed-by: Scott Williams <scwilliams@nvidia.com> Rebase-Id: R0e7b428de2b0b1567a23bcac3c53aa5cc9d8d4a9
Diffstat (limited to 'drivers/i2c/busses/i2c-tegra.c')
-rw-r--r--drivers/i2c/busses/i2c-tegra.c65
1 files changed, 54 insertions, 11 deletions
diff --git a/drivers/i2c/busses/i2c-tegra.c b/drivers/i2c/busses/i2c-tegra.c
index 420397626155..58cf7030860f 100644
--- a/drivers/i2c/busses/i2c-tegra.c
+++ b/drivers/i2c/busses/i2c-tegra.c
@@ -32,6 +32,7 @@
#include <linux/slab.h>
#include <linux/i2c-tegra.h>
#include <linux/of_i2c.h>
+#include <linux/spinlock.h>
#include <asm/unaligned.h>
@@ -145,10 +146,12 @@ struct tegra_i2c_dev {
struct clk *clk;
struct resource *iomem;
struct rt_mutex dev_lock;
+ spinlock_t clk_lock;
void __iomem *base;
int cont_id;
int irq;
bool irq_disabled;
+ bool controller_enabled;
int is_dvc;
bool is_slave;
struct completion msg_complete;
@@ -391,6 +394,10 @@ static int tegra_i2c_init(struct tegra_i2c_dev *i2c_dev)
if (!i2c_dev->is_clkon_always)
clk_enable(i2c_dev->clk);
+ /* Interrupt generated before sending stop signal so
+ * wait for some time so that stop signal can be send proerly */
+ udelay(100);
+
tegra_periph_reset_assert(i2c_dev->clk);
udelay(2);
tegra_periph_reset_deassert(i2c_dev->clk);
@@ -437,8 +444,16 @@ 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;
+ unsigned long flags;
+
+ spin_lock_irqsave(&i2c_dev->clk_lock, flags);
+ if (!i2c_dev->controller_enabled) {
+ dev_warn(i2c_dev->dev, "Controller not enabled\n");
+ spin_unlock_irqrestore(&i2c_dev->clk_lock, flags);
+ return IRQ_NONE;
+ }
status = i2c_readl(i2c_dev, I2C_INT_STATUS);
@@ -452,7 +467,6 @@ static irqreturn_t tegra_i2c_isr(int irq, void *dev_id)
i2c_dev->irq_disabled = 1;
}
- complete(&i2c_dev->msg_complete);
goto err;
}
@@ -474,7 +488,13 @@ static irqreturn_t tegra_i2c_isr(int irq, void *dev_id)
i2c_readl(i2c_dev, I2C_PACKET_TRANSFER_STATUS));
}
- complete(&i2c_dev->msg_complete);
+ 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));
+ }
goto err;
}
@@ -490,7 +510,6 @@ static irqreturn_t tegra_i2c_isr(int irq, void *dev_id)
i2c_dev->irq_disabled = 1;
}
- complete(&i2c_dev->msg_complete);
goto err;
}
@@ -508,16 +527,17 @@ static irqreturn_t tegra_i2c_isr(int irq, void *dev_id)
tegra_i2c_mask_irq(i2c_dev, I2C_INT_TX_FIFO_DATA_REQ);
}
- if (status & I2C_INT_PACKET_XFER_COMPLETE) {
- BUG_ON(i2c_dev->msg_buf_remaining);
- complete(&i2c_dev->msg_complete);
- }
-
i2c_writel(i2c_dev, status, I2C_INT_STATUS);
if (i2c_dev->is_dvc)
dvc_writel(i2c_dev, DVC_STATUS_I2C_DONE_INTR, DVC_STATUS);
+ if (status & I2C_INT_PACKET_XFER_COMPLETE) {
+ BUG_ON(i2c_dev->msg_buf_remaining);
+ complete(&i2c_dev->msg_complete);
+ }
+
+ spin_unlock_irqrestore(&i2c_dev->clk_lock, flags);
return IRQ_HANDLED;
err:
@@ -544,13 +564,15 @@ err:
/* An error occurred, 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);
if (i2c_dev->is_dvc)
dvc_writel(i2c_dev, DVC_STATUS_I2C_DONE_INTR, DVC_STATUS);
+ complete(&i2c_dev->msg_complete);
+ spin_unlock_irqrestore(&i2c_dev->clk_lock, flags);
return IRQ_HANDLED;
}
@@ -560,6 +582,7 @@ static int tegra_i2c_xfer_msg(struct tegra_i2c_bus *i2c_bus,
struct tegra_i2c_dev *i2c_dev = i2c_bus->dev;
u32 int_mask;
int ret;
+ unsigned long flags;
tegra_i2c_flush_fifos(i2c_dev);
@@ -598,7 +621,7 @@ 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;
+ 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)
@@ -616,7 +639,10 @@ static int tegra_i2c_xfer_msg(struct tegra_i2c_bus *i2c_bus,
"i2c transfer timed out, addr 0x%04x, data 0x%02x\n",
msg->addr, msg->buf[0]);
+ spin_lock_irqsave(&i2c_dev->clk_lock, flags);
+ i2c_dev->controller_enabled = false;
tegra_i2c_init(i2c_dev);
+ spin_unlock_irqrestore(&i2c_dev->clk_lock, flags);
return -ETIMEDOUT;
}
@@ -626,7 +652,11 @@ static int tegra_i2c_xfer_msg(struct tegra_i2c_bus *i2c_bus,
if (likely(i2c_dev->msg_err == I2C_ERR_NONE))
return 0;
+ spin_lock_irqsave(&i2c_dev->clk_lock, flags);
+ i2c_dev->controller_enabled = false;
tegra_i2c_init(i2c_dev);
+ spin_unlock_irqrestore(&i2c_dev->clk_lock, flags);
+
if (i2c_dev->msg_err == I2C_ERR_NO_ACK) {
if (msg->flags & I2C_M_IGNORE_NAK)
return 0;
@@ -646,6 +676,7 @@ static int tegra_i2c_xfer(struct i2c_adapter *adap, struct i2c_msg msgs[],
struct tegra_i2c_dev *i2c_dev = i2c_bus->dev;
int i;
int ret = 0;
+ unsigned long flags;
if (i2c_dev->is_suspended)
return -EBUSY;
@@ -671,12 +702,22 @@ static int tegra_i2c_xfer(struct i2c_adapter *adap, struct i2c_msg msgs[],
if (!i2c_dev->is_clkon_always)
clk_enable(i2c_dev->clk);
+
+ spin_lock_irqsave(&i2c_dev->clk_lock, flags);
+ i2c_dev->controller_enabled = true;
+ spin_unlock_irqrestore(&i2c_dev->clk_lock, flags);
+
for (i = 0; i < num; i++) {
int stop = (i == (num - 1)) ? 1 : 0;
ret = tegra_i2c_xfer_msg(i2c_bus, &msgs[i], stop);
if (ret)
break;
}
+
+ spin_lock_irqsave(&i2c_dev->clk_lock, flags);
+ i2c_dev->controller_enabled = false;
+ spin_unlock_irqrestore(&i2c_dev->clk_lock, flags);
+
if (!i2c_dev->is_clkon_always)
clk_disable(i2c_dev->clk);
@@ -786,7 +827,9 @@ static int tegra_i2c_probe(struct platform_device *pdev)
i2c_dev->msgs = NULL;
i2c_dev->msgs_num = 0;
+ i2c_dev->controller_enabled = false;
rt_mutex_init(&i2c_dev->dev_lock);
+ spin_lock_init(&i2c_dev->clk_lock);
i2c_dev->slave_addr = plat->slave_addr;
i2c_dev->is_dvc = plat->is_dvc;