i2c: emev2: avoid race when unregistering slave client
authorWolfram Sang <wsa+renesas@sang-engineering.com>
Thu, 8 Aug 2019 19:54:17 +0000 (21:54 +0200)
committerGreg Kroah-Hartman <gregkh@linuxfoundation.org>
Fri, 6 Sep 2019 08:20:45 +0000 (10:20 +0200)
[ Upstream commit d7437fc0d8291181debe032671a289b6bd93f46f ]

After we disabled interrupts, there might still be an active one
running. Sync before clearing the pointer to the slave device.

Fixes: c31d0a00021d ("i2c: emev2: add slave support")
Reported-by: Krzysztof Adamski <krzysztof.adamski@nokia.com>
Signed-off-by: Wolfram Sang <wsa+renesas@sang-engineering.com>
Reviewed-by: Krzysztof Adamski <krzysztof.adamski@nokia.com>
Signed-off-by: Wolfram Sang <wsa@the-dreams.de>
Signed-off-by: Sasha Levin <sashal@kernel.org>
drivers/i2c/busses/i2c-emev2.c

index d2e84480fbe96093549666df92a55f4f7196259f..dd97e5d9f49a2275407ea399abe102f9092d8ee3 100644 (file)
@@ -72,6 +72,7 @@ struct em_i2c_device {
        struct completion msg_done;
        struct clk *sclk;
        struct i2c_client *slave;
+       int irq;
 };
 
 static inline void em_clear_set_bit(struct em_i2c_device *priv, u8 clear, u8 set, u8 reg)
@@ -342,6 +343,12 @@ static int em_i2c_unreg_slave(struct i2c_client *slave)
 
        writeb(0, priv->base + I2C_OFS_SVA0);
 
+       /*
+        * Wait for interrupt to finish. New slave irqs cannot happen because we
+        * cleared the slave address and, thus, only extension codes will be
+        * detected which do not use the slave ptr.
+        */
+       synchronize_irq(priv->irq);
        priv->slave = NULL;
 
        return 0;
@@ -358,7 +365,7 @@ static int em_i2c_probe(struct platform_device *pdev)
 {
        struct em_i2c_device *priv;
        struct resource *r;
-       int irq, ret;
+       int ret;
 
        priv = devm_kzalloc(&pdev->dev, sizeof(*priv), GFP_KERNEL);
        if (!priv)
@@ -393,8 +400,8 @@ static int em_i2c_probe(struct platform_device *pdev)
 
        em_i2c_reset(&priv->adap);
 
-       irq = platform_get_irq(pdev, 0);
-       ret = devm_request_irq(&pdev->dev, irq, em_i2c_irq_handler, 0,
+       priv->irq = platform_get_irq(pdev, 0);
+       ret = devm_request_irq(&pdev->dev, priv->irq, em_i2c_irq_handler, 0,
                                "em_i2c", priv);
        if (ret)
                goto err_clk;
@@ -404,7 +411,8 @@ static int em_i2c_probe(struct platform_device *pdev)
        if (ret)
                goto err_clk;
 
-       dev_info(&pdev->dev, "Added i2c controller %d, irq %d\n", priv->adap.nr, irq);
+       dev_info(&pdev->dev, "Added i2c controller %d, irq %d\n", priv->adap.nr,
+                priv->irq);
 
        return 0;