summaryrefslogtreecommitdiffstats
path: root/drivers/i2c/busses
diff options
context:
space:
mode:
authorDmitry Osipenko <digetx@gmail.com>2020-03-24 20:12:16 +0100
committerThierry Reding <treding@nvidia.com>2020-05-08 12:42:33 +0200
commit566c05f7cd9a5c6a257d19d4fb91ce35b3b27fb6 (patch)
tree513442eb53fa2d181d0ab1c195ad2f0dfb952ced /drivers/i2c/busses
parenti2c: tegra: Keep IRQs enabled during suspend/resume (diff)
downloadlinux-566c05f7cd9a5c6a257d19d4fb91ce35b3b27fb6.tar.xz
linux-566c05f7cd9a5c6a257d19d4fb91ce35b3b27fb6.zip
i2c: tegra: Better handle case where CPU0 is busy for a long time
Boot CPU0 always handle I2C interrupt and under some rare circumstances (like running KASAN + NFS root) it 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 completion timeout. Signed-off-by: Dmitry Osipenko <digetx@gmail.com> Signed-off-by: Thierry Reding <treding@nvidia.com>
Diffstat (limited to 'drivers/i2c/busses')
-rw-r--r--drivers/i2c/busses/i2c-tegra.c27
1 files changed, 15 insertions, 12 deletions
diff --git a/drivers/i2c/busses/i2c-tegra.c b/drivers/i2c/busses/i2c-tegra.c
index 26673be867bc..9232038ccb40 100644
--- a/drivers/i2c/busses/i2c-tegra.c
+++ b/drivers/i2c/busses/i2c-tegra.c
@@ -996,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();
@@ -1030,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;