Skip to content

Commit

Permalink
i2c: rcar: avoid race when unregistering slave client
Browse files Browse the repository at this point in the history
After we disabled interrupts, there might still be an active one
running. Sync before clearing the pointer to the slave device.

Fixes: de20d18 ("i2c: rcar: 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>
  • Loading branch information
Wolfram Sang authored and Wolfram Sang committed Aug 14, 2019
1 parent 8fc3ae3 commit 7b814d8
Showing 1 changed file with 7 additions and 4 deletions.
11 changes: 7 additions & 4 deletions drivers/i2c/busses/i2c-rcar.c
Original file line number Diff line number Diff line change
Expand Up @@ -139,6 +139,7 @@ struct rcar_i2c_priv {
enum dma_data_direction dma_direction;

struct reset_control *rstc;
int irq;
};

#define rcar_i2c_priv_to_dev(p) ((p)->adap.dev.parent)
Expand Down Expand Up @@ -861,9 +862,11 @@ static int rcar_unreg_slave(struct i2c_client *slave)

WARN_ON(!priv->slave);

/* disable irqs and ensure none is running before clearing ptr */
rcar_i2c_write(priv, ICSIER, 0);
rcar_i2c_write(priv, ICSCR, 0);

synchronize_irq(priv->irq);
priv->slave = NULL;

pm_runtime_put(rcar_i2c_priv_to_dev(priv));
Expand Down Expand Up @@ -918,7 +921,7 @@ static int rcar_i2c_probe(struct platform_device *pdev)
struct i2c_adapter *adap;
struct device *dev = &pdev->dev;
struct i2c_timings i2c_t;
int irq, ret;
int ret;

/* Otherwise logic will break because some bytes must always use PIO */
BUILD_BUG_ON_MSG(RCAR_MIN_DMA_LEN < 3, "Invalid min DMA length");
Expand Down Expand Up @@ -984,10 +987,10 @@ static int rcar_i2c_probe(struct platform_device *pdev)
pm_runtime_put(dev);


irq = platform_get_irq(pdev, 0);
ret = devm_request_irq(dev, irq, rcar_i2c_irq, 0, dev_name(dev), priv);
priv->irq = platform_get_irq(pdev, 0);
ret = devm_request_irq(dev, priv->irq, rcar_i2c_irq, 0, dev_name(dev), priv);
if (ret < 0) {
dev_err(dev, "cannot get irq %d\n", irq);
dev_err(dev, "cannot get irq %d\n", priv->irq);
goto out_pm_disable;
}

Expand Down

0 comments on commit 7b814d8

Please sign in to comment.