diff options
| author | Masahiro Yamada <yamada.masahiro@socionext.com> | 2018-10-15 23:01:47 -0400 |
|---|---|---|
| committer | Wolfram Sang <wsa@the-dreams.de> | 2018-10-29 14:53:37 -0400 |
| commit | f1fdcbbdf45d9609f3d4063b67e9ea941ba3a58f (patch) | |
| tree | 2e30f3ef1dd453403186443b6ba7580c580aef22 /drivers/i2c | |
| parent | a560ae55dcc3ae43c0c5764c745d3d1f0257beee (diff) | |
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>
Diffstat (limited to 'drivers/i2c')
| -rw-r--r-- | drivers/i2c/busses/i2c-uniphier-f.c | 17 |
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 { | |||
| 98 | unsigned int flags; | 98 | unsigned int flags; |
| 99 | unsigned int busy_cnt; | 99 | unsigned int busy_cnt; |
| 100 | unsigned int clk_cycle; | 100 | unsigned int clk_cycle; |
| 101 | spinlock_t lock; /* IRQ synchronization */ | ||
| 101 | }; | 102 | }; |
| 102 | 103 | ||
| 103 | static void uniphier_fi2c_fill_txfifo(struct uniphier_fi2c_priv *priv, | 104 | 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) | |||
| 162 | struct uniphier_fi2c_priv *priv = dev_id; | 163 | struct uniphier_fi2c_priv *priv = dev_id; |
| 163 | u32 irq_status; | 164 | u32 irq_status; |
| 164 | 165 | ||
| 166 | spin_lock(&priv->lock); | ||
| 167 | |||
| 165 | irq_status = readl(priv->membase + UNIPHIER_FI2C_INT); | 168 | irq_status = readl(priv->membase + UNIPHIER_FI2C_INT); |
| 169 | irq_status &= priv->enabled_irqs; | ||
| 166 | 170 | ||
| 167 | dev_dbg(&priv->adap.dev, | 171 | dev_dbg(&priv->adap.dev, |
| 168 | "interrupt: enabled_irqs=%04x, irq_status=%04x\n", | 172 | "interrupt: enabled_irqs=%04x, irq_status=%04x\n", |
| @@ -230,6 +234,8 @@ static irqreturn_t uniphier_fi2c_interrupt(int irq, void *dev_id) | |||
| 230 | goto handled; | 234 | goto handled; |
| 231 | } | 235 | } |
| 232 | 236 | ||
| 237 | spin_unlock(&priv->lock); | ||
| 238 | |||
| 233 | return IRQ_NONE; | 239 | return IRQ_NONE; |
| 234 | 240 | ||
| 235 | data_done: | 241 | data_done: |
| @@ -246,6 +252,8 @@ complete: | |||
| 246 | handled: | 252 | handled: |
| 247 | uniphier_fi2c_clear_irqs(priv); | 253 | uniphier_fi2c_clear_irqs(priv); |
| 248 | 254 | ||
| 255 | spin_unlock(&priv->lock); | ||
| 256 | |||
| 249 | return IRQ_HANDLED; | 257 | return IRQ_HANDLED; |
| 250 | } | 258 | } |
| 251 | 259 | ||
| @@ -311,7 +319,7 @@ static int uniphier_fi2c_master_xfer_one(struct i2c_adapter *adap, | |||
| 311 | { | 319 | { |
| 312 | struct uniphier_fi2c_priv *priv = i2c_get_adapdata(adap); | 320 | struct uniphier_fi2c_priv *priv = i2c_get_adapdata(adap); |
| 313 | bool is_read = msg->flags & I2C_M_RD; | 321 | bool is_read = msg->flags & I2C_M_RD; |
| 314 | unsigned long time_left; | 322 | unsigned long time_left, flags; |
| 315 | 323 | ||
| 316 | dev_dbg(&adap->dev, "%s: addr=0x%02x, len=%d, stop=%d\n", | 324 | dev_dbg(&adap->dev, "%s: addr=0x%02x, len=%d, stop=%d\n", |
| 317 | is_read ? "receive" : "transmit", msg->addr, msg->len, stop); | 325 | is_read ? "receive" : "transmit", msg->addr, msg->len, stop); |
| @@ -342,6 +350,12 @@ static int uniphier_fi2c_master_xfer_one(struct i2c_adapter *adap, | |||
| 342 | priv->membase + UNIPHIER_FI2C_CR); | 350 | priv->membase + UNIPHIER_FI2C_CR); |
| 343 | 351 | ||
| 344 | time_left = wait_for_completion_timeout(&priv->comp, adap->timeout); | 352 | time_left = wait_for_completion_timeout(&priv->comp, adap->timeout); |
| 353 | |||
| 354 | spin_lock_irqsave(&priv->lock, flags); | ||
| 355 | priv->enabled_irqs = 0; | ||
| 356 | uniphier_fi2c_set_irqs(priv); | ||
| 357 | spin_unlock_irqrestore(&priv->lock, flags); | ||
| 358 | |||
| 345 | if (!time_left) { | 359 | if (!time_left) { |
| 346 | dev_err(&adap->dev, "transaction timeout.\n"); | 360 | dev_err(&adap->dev, "transaction timeout.\n"); |
| 347 | uniphier_fi2c_recover(priv); | 361 | uniphier_fi2c_recover(priv); |
| @@ -529,6 +543,7 @@ static int uniphier_fi2c_probe(struct platform_device *pdev) | |||
| 529 | 543 | ||
| 530 | priv->clk_cycle = clk_rate / bus_speed; | 544 | priv->clk_cycle = clk_rate / bus_speed; |
| 531 | init_completion(&priv->comp); | 545 | init_completion(&priv->comp); |
| 546 | spin_lock_init(&priv->lock); | ||
| 532 | priv->adap.owner = THIS_MODULE; | 547 | priv->adap.owner = THIS_MODULE; |
| 533 | priv->adap.algo = &uniphier_fi2c_algo; | 548 | priv->adap.algo = &uniphier_fi2c_algo; |
| 534 | priv->adap.dev.parent = dev; | 549 | priv->adap.dev.parent = dev; |
