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

Commit 2133e30f authored by Atsushi Nemoto's avatar Atsushi Nemoto Committed by Greg Kroah-Hartman
Browse files

i2c: altera: Fix race between xfer_msg and isr thread



[ Upstream commit 5d4c7977499a736f3f80826bdc9744344ad55589 ]

Use a mutex to protect access to idev->msg_len, idev->buf, etc. which
are modified by both altr_i2c_xfer_msg() and altr_i2c_isr().

This is the minimal fix for easy backporting. A cleanup to remove the
spinlock will be added later.

Signed-off-by: default avatarAtsushi Nemoto <atsushi.nemoto@sord.co.jp>
Acked-by: default avatarThor Thayer <thor.thayer@linux.intel.com>
[wsa: updated commit message]
Signed-off-by: default avatarWolfram Sang <wsa@kernel.org>
Signed-off-by: default avatarSasha Levin <sashal@kernel.org>
parent a0006477
Loading
Loading
Loading
Loading
+9 −1
Original line number Diff line number Diff line
@@ -81,6 +81,7 @@
 * @isr_mask: cached copy of local ISR enables.
 * @isr_status: cached copy of local ISR status.
 * @lock: spinlock for IRQ synchronization.
 * @isr_mutex: mutex for IRQ thread.
 */
struct altr_i2c_dev {
	void __iomem *base;
@@ -97,6 +98,7 @@ struct altr_i2c_dev {
	u32 isr_mask;
	u32 isr_status;
	spinlock_t lock;	/* IRQ synchronization */
	struct mutex isr_mutex;
};

static void
@@ -256,10 +258,11 @@ static irqreturn_t altr_i2c_isr(int irq, void *_dev)
	struct altr_i2c_dev *idev = _dev;
	u32 status = idev->isr_status;

	mutex_lock(&idev->isr_mutex);
	if (!idev->msg) {
		dev_warn(idev->dev, "unexpected interrupt\n");
		altr_i2c_int_clear(idev, ALTR_I2C_ALL_IRQ);
		return IRQ_HANDLED;
		goto out;
	}
	read = (idev->msg->flags & I2C_M_RD) != 0;

@@ -312,6 +315,8 @@ static irqreturn_t altr_i2c_isr(int irq, void *_dev)
		complete(&idev->msg_complete);
		dev_dbg(idev->dev, "Message Complete\n");
	}
out:
	mutex_unlock(&idev->isr_mutex);

	return IRQ_HANDLED;
}
@@ -323,6 +328,7 @@ static int altr_i2c_xfer_msg(struct altr_i2c_dev *idev, struct i2c_msg *msg)
	u32 value;
	u8 addr = i2c_8bit_addr_from_msg(msg);

	mutex_lock(&idev->isr_mutex);
	idev->msg = msg;
	idev->msg_len = msg->len;
	idev->buf = msg->buf;
@@ -347,6 +353,7 @@ static int altr_i2c_xfer_msg(struct altr_i2c_dev *idev, struct i2c_msg *msg)
		altr_i2c_int_enable(idev, imask, true);
		altr_i2c_fill_tx_fifo(idev);
	}
	mutex_unlock(&idev->isr_mutex);

	time_left = wait_for_completion_timeout(&idev->msg_complete,
						ALTR_I2C_XFER_TIMEOUT);
@@ -420,6 +427,7 @@ static int altr_i2c_probe(struct platform_device *pdev)
	idev->dev = &pdev->dev;
	init_completion(&idev->msg_complete);
	spin_lock_init(&idev->lock);
	mutex_init(&idev->isr_mutex);

	ret = device_property_read_u32(idev->dev, "fifo-size",
				       &idev->fifo_size);