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.c54
1 files changed, 31 insertions, 23 deletions
diff --git a/drivers/i2c/busses/i2c-tegra.c b/drivers/i2c/busses/i2c-tegra.c
index cbc2ad49043e..8280ac7cc1b7 100644
--- a/drivers/i2c/busses/i2c-tegra.c
+++ b/drivers/i2c/busses/i2c-tegra.c
@@ -123,10 +123,6 @@
#define I2C_THIGH_SHIFT 8
#define I2C_INTERFACE_TIMING_1 0x98
-#define I2C_STANDARD_MODE 100000
-#define I2C_FAST_MODE 400000
-#define I2C_FAST_PLUS_MODE 1000000
-
/* Packet header size in bytes */
#define I2C_PACKET_HEADER_SIZE 12
@@ -737,8 +733,8 @@ static int tegra_i2c_init(struct tegra_i2c_dev *i2c_dev, bool clk_reinit)
I2C_CLK_DIVISOR_STD_FAST_MODE_SHIFT;
i2c_writel(i2c_dev, clk_divisor, I2C_CLK_DIVISOR);
- if (i2c_dev->bus_clk_rate > I2C_STANDARD_MODE &&
- i2c_dev->bus_clk_rate <= I2C_FAST_PLUS_MODE) {
+ if (i2c_dev->bus_clk_rate > I2C_MAX_STANDARD_MODE_FREQ &&
+ i2c_dev->bus_clk_rate <= I2C_MAX_FAST_MODE_PLUS_FREQ) {
tlow = i2c_dev->hw->tlow_fast_fastplus_mode;
thigh = i2c_dev->hw->thigh_fast_fastplus_mode;
tsu_thd = i2c_dev->hw->setup_hold_time_fast_fast_plus_mode;
@@ -1000,14 +996,13 @@ tegra_i2c_poll_completion_timeout(struct tegra_i2c_dev *i2c_dev,
do {
u32 status = i2c_readl(i2c_dev, I2C_INT_STATUS);
- if (status) {
+ if (status)
tegra_i2c_isr(i2c_dev->irq, i2c_dev);
- if (completion_done(complete)) {
- s64 delta = ktime_ms_delta(ktimeout, ktime);
+ if (completion_done(complete)) {
+ s64 delta = ktime_ms_delta(ktimeout, ktime);
- return msecs_to_jiffies(delta) ?: 1;
- }
+ return msecs_to_jiffies(delta) ?: 1;
}
ktime = ktime_get();
@@ -1034,14 +1029,18 @@ tegra_i2c_wait_completion_timeout(struct tegra_i2c_dev *i2c_dev,
disable_irq(i2c_dev->irq);
/*
- * There is a chance that completion may happen after IRQ
- * synchronization, which is done by disable_irq().
+ * Under some rare circumstances (like running KASAN +
+ * NFS root) CPU, which handles interrupt, may stuck in
+ * uninterruptible state for a significant time. In this
+ * case we will get timeout if I2C transfer is running on
+ * a sibling CPU, despite of IRQ being raised.
+ *
+ * In order to handle this rare condition, the IRQ status
+ * needs to be checked after timeout.
*/
- if (ret == 0 && completion_done(complete)) {
- dev_warn(i2c_dev->dev,
- "completion done after timeout\n");
- ret = 1;
- }
+ if (ret == 0)
+ ret = tegra_i2c_poll_completion_timeout(i2c_dev,
+ complete, 0);
}
return ret;
@@ -1220,6 +1219,15 @@ static int tegra_i2c_xfer_msg(struct tegra_i2c_dev *i2c_dev,
time_left = tegra_i2c_wait_completion_timeout(
i2c_dev, &i2c_dev->dma_complete, xfer_time);
+ /*
+ * Synchronize DMA first, since dmaengine_terminate_sync()
+ * performs synchronization after the transfer's termination
+ * and we want to get a completion if transfer succeeded.
+ */
+ dmaengine_synchronize(i2c_dev->msg_read ?
+ i2c_dev->rx_dma_chan :
+ i2c_dev->tx_dma_chan);
+
dmaengine_terminate_sync(i2c_dev->msg_read ?
i2c_dev->rx_dma_chan :
i2c_dev->tx_dma_chan);
@@ -1341,7 +1349,7 @@ static void tegra_i2c_parse_dt(struct tegra_i2c_dev *i2c_dev)
ret = of_property_read_u32(np, "clock-frequency",
&i2c_dev->bus_clk_rate);
if (ret)
- i2c_dev->bus_clk_rate = 100000; /* default clock rate */
+ i2c_dev->bus_clk_rate = I2C_MAX_STANDARD_MODE_FREQ; /* default clock rate */
multi_mode = of_property_read_bool(np, "multi-master");
i2c_dev->is_multimaster_mode = multi_mode;
@@ -1640,12 +1648,12 @@ static int tegra_i2c_probe(struct platform_device *pdev)
}
}
- if (i2c_dev->bus_clk_rate > I2C_FAST_MODE &&
- i2c_dev->bus_clk_rate <= I2C_FAST_PLUS_MODE)
+ if (i2c_dev->bus_clk_rate > I2C_MAX_FAST_MODE_FREQ &&
+ i2c_dev->bus_clk_rate <= I2C_MAX_FAST_MODE_PLUS_FREQ)
i2c_dev->clk_divisor_non_hs_mode =
i2c_dev->hw->clk_divisor_fast_plus_mode;
- else if (i2c_dev->bus_clk_rate > I2C_STANDARD_MODE &&
- i2c_dev->bus_clk_rate <= I2C_FAST_MODE)
+ else if (i2c_dev->bus_clk_rate > I2C_MAX_STANDARD_MODE_FREQ &&
+ i2c_dev->bus_clk_rate <= I2C_MAX_FAST_MODE_FREQ)
i2c_dev->clk_divisor_non_hs_mode =
i2c_dev->hw->clk_divisor_fast_mode;
else