Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit f1fdcbbd authored by Masahiro Yamada's avatar Masahiro Yamada Committed by Wolfram Sang
Browse files

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: 6a62974b ("i2c: uniphier_f: add UniPhier FIFO-builtin I2C driver")
Signed-off-by: default avatarMasahiro Yamada <yamada.masahiro@socionext.com>
Signed-off-by: default avatarWolfram Sang <wsa@the-dreams.de>
parent a560ae55
Loading
Loading
Loading
Loading
+16 −1
Original line number Diff line number Diff line
@@ -98,6 +98,7 @@ struct uniphier_fi2c_priv {
	unsigned int flags;
	unsigned int busy_cnt;
	unsigned int clk_cycle;
	spinlock_t lock;	/* IRQ synchronization */
};

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)
	struct uniphier_fi2c_priv *priv = dev_id;
	u32 irq_status;

	spin_lock(&priv->lock);

	irq_status = readl(priv->membase + UNIPHIER_FI2C_INT);
	irq_status &= priv->enabled_irqs;

	dev_dbg(&priv->adap.dev,
		"interrupt: enabled_irqs=%04x, irq_status=%04x\n",
@@ -230,6 +234,8 @@ static irqreturn_t uniphier_fi2c_interrupt(int irq, void *dev_id)
		goto handled;
	}

	spin_unlock(&priv->lock);

	return IRQ_NONE;

data_done:
@@ -246,6 +252,8 @@ static irqreturn_t uniphier_fi2c_interrupt(int irq, void *dev_id)
handled:
	uniphier_fi2c_clear_irqs(priv);

	spin_unlock(&priv->lock);

	return IRQ_HANDLED;
}

@@ -311,7 +319,7 @@ static int uniphier_fi2c_master_xfer_one(struct i2c_adapter *adap,
{
	struct uniphier_fi2c_priv *priv = i2c_get_adapdata(adap);
	bool is_read = msg->flags & I2C_M_RD;
	unsigned long time_left;
	unsigned long time_left, flags;

	dev_dbg(&adap->dev, "%s: addr=0x%02x, len=%d, stop=%d\n",
		is_read ? "receive" : "transmit", msg->addr, msg->len, stop);
@@ -342,6 +350,12 @@ static int uniphier_fi2c_master_xfer_one(struct i2c_adapter *adap,
	       priv->membase + UNIPHIER_FI2C_CR);

	time_left = wait_for_completion_timeout(&priv->comp, adap->timeout);

	spin_lock_irqsave(&priv->lock, flags);
	priv->enabled_irqs = 0;
	uniphier_fi2c_set_irqs(priv);
	spin_unlock_irqrestore(&priv->lock, flags);

	if (!time_left) {
		dev_err(&adap->dev, "transaction timeout.\n");
		uniphier_fi2c_recover(priv);
@@ -529,6 +543,7 @@ static int uniphier_fi2c_probe(struct platform_device *pdev)

	priv->clk_cycle = clk_rate / bus_speed;
	init_completion(&priv->comp);
	spin_lock_init(&priv->lock);
	priv->adap.owner = THIS_MODULE;
	priv->adap.algo = &uniphier_fi2c_algo;
	priv->adap.dev.parent = dev;