]> git.proxmox.com Git - mirror_ubuntu-bionic-kernel.git/commitdiff
i2c: uniphier-f: fix race condition when IRQ is cleared
authorMasahiro Yamada <yamada.masahiro@socionext.com>
Tue, 16 Oct 2018 03:01:49 +0000 (12:01 +0900)
committerMarcelo Henrique Cerri <marcelo.cerri@canonical.com>
Fri, 17 Jan 2020 17:20:57 +0000 (14:20 -0300)
BugLink: https://bugs.launchpad.net/bugs/1854975
[ Upstream commit eaba68785c2d24ebf1f0d46c24e11b79cc2f94c7 ]

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: 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>
Signed-off-by: Sasha Levin <sashal@kernel.org>
Signed-off-by: Kamal Mostafa <kamal@canonical.com>
Signed-off-by: Kleber Sacilotto de Souza <kleber.souza@canonical.com>
drivers/i2c/busses/i2c-uniphier-f.c

index bbd5b137aa2163346701d2a4dee3903e7944ee91..928ea9930d17eb14c2e031c3354a6b758694f17c 100644 (file)
@@ -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)
@@ -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;
 
@@ -250,8 +253,6 @@ complete:
        }
 
 handled:
-       uniphier_fi2c_clear_irqs(priv);
-
        spin_unlock(&priv->lock);
 
        return IRQ_HANDLED;
@@ -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 */