Skip to content

Commit

Permalink
i2c: uniphier-f: fix race condition when IRQ is cleared
Browse files Browse the repository at this point in the history
The current IRQ handler clears all the IRQ status bits when it bails
out. This is dangerous because it might clear away the status bits
that have just been set while processing the current handler. If this
happens, the IRQ event for the latest transfer is lost forever.

The IRQ status bits must be cleared *before* the next transfer is
kicked.

Fixes: 6a62974 ("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>
  • Loading branch information
Masahiro Yamada authored and Wolfram Sang committed Oct 29, 2018
1 parent 39226aa commit eaba687
Showing 1 changed file with 6 additions and 5 deletions.
11 changes: 6 additions & 5 deletions drivers/i2c/busses/i2c-uniphier-f.c
Original file line number Diff line number Diff line change
Expand Up @@ -143,9 +143,10 @@ static void uniphier_fi2c_set_irqs(struct uniphier_fi2c_priv *priv)
writel(priv->enabled_irqs, priv->membase + UNIPHIER_FI2C_IE);
}

static void uniphier_fi2c_clear_irqs(struct uniphier_fi2c_priv *priv)
static void uniphier_fi2c_clear_irqs(struct uniphier_fi2c_priv *priv,
u32 mask)
{
writel(-1, priv->membase + UNIPHIER_FI2C_IC);
writel(mask, priv->membase + UNIPHIER_FI2C_IC);
}

static void uniphier_fi2c_stop(struct uniphier_fi2c_priv *priv)
Expand All @@ -172,6 +173,8 @@ static irqreturn_t uniphier_fi2c_interrupt(int irq, void *dev_id)
"interrupt: enabled_irqs=%04x, irq_status=%04x\n",
priv->enabled_irqs, irq_status);

uniphier_fi2c_clear_irqs(priv, irq_status);

if (irq_status & UNIPHIER_FI2C_INT_STOP)
goto complete;

Expand Down Expand Up @@ -250,8 +253,6 @@ static irqreturn_t uniphier_fi2c_interrupt(int irq, void *dev_id)
}

handled:
uniphier_fi2c_clear_irqs(priv);

spin_unlock(&priv->lock);

return IRQ_HANDLED;
Expand Down Expand Up @@ -340,7 +341,7 @@ static int uniphier_fi2c_master_xfer_one(struct i2c_adapter *adap,
priv->flags |= UNIPHIER_FI2C_STOP;

reinit_completion(&priv->comp);
uniphier_fi2c_clear_irqs(priv);
uniphier_fi2c_clear_irqs(priv, U32_MAX);
writel(UNIPHIER_FI2C_RST_TBRST | UNIPHIER_FI2C_RST_RBRST,
priv->membase + UNIPHIER_FI2C_RST); /* reset TX/RX FIFO */

Expand Down

0 comments on commit eaba687

Please sign in to comment.