summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorMasahiro Yamada <yamada.masahiro@socionext.com>2018-10-16 12:01:47 +0900
committerWolfram Sang <wsa@the-dreams.de>2018-10-29 18:53:37 +0000
commitf1fdcbbdf45d9609f3d4063b67e9ea941ba3a58f (patch)
tree2e30f3ef1dd453403186443b6ba7580c580aef22
parenta560ae55dcc3ae43c0c5764c745d3d1f0257beee (diff)
downloadlinux-f1fdcbbdf45d9609f3d4063b67e9ea941ba3a58f.tar.bz2
i2c: uniphier-f: make driver robust against concurrency
This is unlikely to happen, but it is possible for a CPU to enter the interrupt handler just after wait_for_completion_timeout() has expired. If this happens, the hardware is accessed from multiple contexts concurrently. Disable the IRQ after wait_for_completion_timeout(), and do nothing from the handler when the IRQ is disabled. Fixes: 6a62974b667f ("i2c: uniphier_f: add UniPhier FIFO-builtin I2C driver") Signed-off-by: Masahiro Yamada <yamada.masahiro@socionext.com> Signed-off-by: Wolfram Sang <wsa@the-dreams.de>
-rw-r--r--drivers/i2c/busses/i2c-uniphier-f.c17
1 files changed, 16 insertions, 1 deletions
diff --git a/drivers/i2c/busses/i2c-uniphier-f.c b/drivers/i2c/busses/i2c-uniphier-f.c
index a403e8579b65..200dfd1771af 100644
--- a/drivers/i2c/busses/i2c-uniphier-f.c
+++ b/drivers/i2c/busses/i2c-uniphier-f.c
@@ -98,6 +98,7 @@ struct uniphier_fi2c_priv {
unsigned int flags;
unsigned int busy_cnt;
unsigned int clk_cycle;
+ spinlock_t lock; /* IRQ synchronization */
};
static void uniphier_fi2c_fill_txfifo(struct uniphier_fi2c_priv *priv,
@@ -162,7 +163,10 @@ static irqreturn_t uniphier_fi2c_interrupt(int irq, void *dev_id)
struct uniphier_fi2c_priv *priv = dev_id;
u32 irq_status;
+ spin_lock(&priv->lock);
+
irq_status = readl(priv->membase + UNIPHIER_FI2C_INT);
+ irq_status &= priv->enabled_irqs;
dev_dbg(&priv->adap.dev,
"interrupt: enabled_irqs=%04x, irq_status=%04x\n",
@@ -230,6 +234,8 @@ static irqreturn_t uniphier_fi2c_interrupt(int irq, void *dev_id)
goto handled;
}
+ spin_unlock(&priv->lock);
+
return IRQ_NONE;
data_done:
@@ -246,6 +252,8 @@ complete:
handled:
uniphier_fi2c_clear_irqs(priv);
+ spin_unlock(&priv->lock);
+
return IRQ_HANDLED;
}
@@ -311,7 +319,7 @@ static int uniphier_fi2c_master_xfer_one(struct i2c_adapter *adap,
{
struct uniphier_fi2c_priv *priv = i2c_get_adapdata(adap);
bool is_read = msg->flags & I2C_M_RD;
- unsigned long time_left;
+ unsigned long time_left, flags;
dev_dbg(&adap->dev, "%s: addr=0x%02x, len=%d, stop=%d\n",
is_read ? "receive" : "transmit", msg->addr, msg->len, stop);
@@ -342,6 +350,12 @@ static int uniphier_fi2c_master_xfer_one(struct i2c_adapter *adap,
priv->membase + UNIPHIER_FI2C_CR);
time_left = wait_for_completion_timeout(&priv->comp, adap->timeout);
+
+ spin_lock_irqsave(&priv->lock, flags);
+ priv->enabled_irqs = 0;
+ uniphier_fi2c_set_irqs(priv);
+ spin_unlock_irqrestore(&priv->lock, flags);
+
if (!time_left) {
dev_err(&adap->dev, "transaction timeout.\n");
uniphier_fi2c_recover(priv);
@@ -529,6 +543,7 @@ static int uniphier_fi2c_probe(struct platform_device *pdev)
priv->clk_cycle = clk_rate / bus_speed;
init_completion(&priv->comp);
+ spin_lock_init(&priv->lock);
priv->adap.owner = THIS_MODULE;
priv->adap.algo = &uniphier_fi2c_algo;
priv->adap.dev.parent = dev;