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

Commit effe0500 authored by Piotr Wojtaszczyk's avatar Piotr Wojtaszczyk Committed by Greg Kroah-Hartman
Browse files

i2c: pnx: Fix potential deadlock warning from del_timer_sync() call in isr



[ Upstream commit f63b94be6942ba82c55343e196bd09b53227618e ]

When del_timer_sync() is called in an interrupt context it throws a warning
because of potential deadlock. The timer is used only to exit from
wait_for_completion() after a timeout so replacing the call with
wait_for_completion_timeout() allows to remove the problematic timer and
its related functions altogether.

Fixes: 41561f28 ("i2c: New Philips PNX bus driver")
Signed-off-by: default avatarPiotr Wojtaszczyk <piotr.wojtaszczyk@timesys.com>
Signed-off-by: default avatarAndi Shyti <andi.shyti@kernel.org>
Signed-off-by: default avatarSasha Levin <sashal@kernel.org>
parent 40945660
Loading
Loading
Loading
Loading
+10 −38
Original line number Original line Diff line number Diff line
@@ -15,7 +15,6 @@
#include <linux/ioport.h>
#include <linux/ioport.h>
#include <linux/delay.h>
#include <linux/delay.h>
#include <linux/i2c.h>
#include <linux/i2c.h>
#include <linux/timer.h>
#include <linux/completion.h>
#include <linux/completion.h>
#include <linux/platform_device.h>
#include <linux/platform_device.h>
#include <linux/io.h>
#include <linux/io.h>
@@ -32,7 +31,6 @@ struct i2c_pnx_mif {
	int			ret;		/* Return value */
	int			ret;		/* Return value */
	int			mode;		/* Interface mode */
	int			mode;		/* Interface mode */
	struct completion	complete;	/* I/O completion */
	struct completion	complete;	/* I/O completion */
	struct timer_list	timer;		/* Timeout */
	u8 *			buf;		/* Data buffer */
	u8 *			buf;		/* Data buffer */
	int			len;		/* Length of data buffer */
	int			len;		/* Length of data buffer */
	int			order;		/* RX Bytes to order via TX */
	int			order;		/* RX Bytes to order via TX */
@@ -117,24 +115,6 @@ static inline int wait_reset(struct i2c_pnx_algo_data *data)
	return (timeout <= 0);
	return (timeout <= 0);
}
}


static inline void i2c_pnx_arm_timer(struct i2c_pnx_algo_data *alg_data)
{
	struct timer_list *timer = &alg_data->mif.timer;
	unsigned long expires = msecs_to_jiffies(alg_data->timeout);

	if (expires <= 1)
		expires = 2;

	del_timer_sync(timer);

	dev_dbg(&alg_data->adapter.dev, "Timer armed at %lu plus %lu jiffies.\n",
		jiffies, expires);

	timer->expires = jiffies + expires;

	add_timer(timer);
}

/**
/**
 * i2c_pnx_start - start a device
 * i2c_pnx_start - start a device
 * @slave_addr:		slave address
 * @slave_addr:		slave address
@@ -259,8 +239,6 @@ static int i2c_pnx_master_xmit(struct i2c_pnx_algo_data *alg_data)
				~(mcntrl_afie | mcntrl_naie | mcntrl_drmie),
				~(mcntrl_afie | mcntrl_naie | mcntrl_drmie),
				  I2C_REG_CTL(alg_data));
				  I2C_REG_CTL(alg_data));


			del_timer_sync(&alg_data->mif.timer);

			dev_dbg(&alg_data->adapter.dev,
			dev_dbg(&alg_data->adapter.dev,
				"%s(): Waking up xfer routine.\n",
				"%s(): Waking up xfer routine.\n",
				__func__);
				__func__);
@@ -276,8 +254,6 @@ static int i2c_pnx_master_xmit(struct i2c_pnx_algo_data *alg_data)
			~(mcntrl_afie | mcntrl_naie | mcntrl_drmie),
			~(mcntrl_afie | mcntrl_naie | mcntrl_drmie),
			  I2C_REG_CTL(alg_data));
			  I2C_REG_CTL(alg_data));


		/* Stop timer. */
		del_timer_sync(&alg_data->mif.timer);
		dev_dbg(&alg_data->adapter.dev,
		dev_dbg(&alg_data->adapter.dev,
			"%s(): Waking up xfer routine after zero-xfer.\n",
			"%s(): Waking up xfer routine after zero-xfer.\n",
			__func__);
			__func__);
@@ -364,8 +340,6 @@ static int i2c_pnx_master_rcv(struct i2c_pnx_algo_data *alg_data)
				 mcntrl_drmie | mcntrl_daie);
				 mcntrl_drmie | mcntrl_daie);
			iowrite32(ctl, I2C_REG_CTL(alg_data));
			iowrite32(ctl, I2C_REG_CTL(alg_data));


			/* Kill timer. */
			del_timer_sync(&alg_data->mif.timer);
			complete(&alg_data->mif.complete);
			complete(&alg_data->mif.complete);
		}
		}
	}
	}
@@ -400,8 +374,6 @@ static irqreturn_t i2c_pnx_interrupt(int irq, void *dev_id)
			 mcntrl_drmie);
			 mcntrl_drmie);
		iowrite32(ctl, I2C_REG_CTL(alg_data));
		iowrite32(ctl, I2C_REG_CTL(alg_data));


		/* Stop timer, to prevent timeout. */
		del_timer_sync(&alg_data->mif.timer);
		complete(&alg_data->mif.complete);
		complete(&alg_data->mif.complete);
	} else if (stat & mstatus_nai) {
	} else if (stat & mstatus_nai) {
		/* Slave did not acknowledge, generate a STOP */
		/* Slave did not acknowledge, generate a STOP */
@@ -419,8 +391,6 @@ static irqreturn_t i2c_pnx_interrupt(int irq, void *dev_id)
		/* Our return value. */
		/* Our return value. */
		alg_data->mif.ret = -EIO;
		alg_data->mif.ret = -EIO;


		/* Stop timer, to prevent timeout. */
		del_timer_sync(&alg_data->mif.timer);
		complete(&alg_data->mif.complete);
		complete(&alg_data->mif.complete);
	} else {
	} else {
		/*
		/*
@@ -453,9 +423,8 @@ static irqreturn_t i2c_pnx_interrupt(int irq, void *dev_id)
	return IRQ_HANDLED;
	return IRQ_HANDLED;
}
}


static void i2c_pnx_timeout(struct timer_list *t)
static void i2c_pnx_timeout(struct i2c_pnx_algo_data *alg_data)
{
{
	struct i2c_pnx_algo_data *alg_data = from_timer(alg_data, t, mif.timer);
	u32 ctl;
	u32 ctl;


	dev_err(&alg_data->adapter.dev,
	dev_err(&alg_data->adapter.dev,
@@ -472,7 +441,6 @@ static void i2c_pnx_timeout(struct timer_list *t)
	iowrite32(ctl, I2C_REG_CTL(alg_data));
	iowrite32(ctl, I2C_REG_CTL(alg_data));
	wait_reset(alg_data);
	wait_reset(alg_data);
	alg_data->mif.ret = -EIO;
	alg_data->mif.ret = -EIO;
	complete(&alg_data->mif.complete);
}
}


static inline void bus_reset_if_active(struct i2c_pnx_algo_data *alg_data)
static inline void bus_reset_if_active(struct i2c_pnx_algo_data *alg_data)
@@ -514,6 +482,7 @@ i2c_pnx_xfer(struct i2c_adapter *adap, struct i2c_msg *msgs, int num)
	struct i2c_msg *pmsg;
	struct i2c_msg *pmsg;
	int rc = 0, completed = 0, i;
	int rc = 0, completed = 0, i;
	struct i2c_pnx_algo_data *alg_data = adap->algo_data;
	struct i2c_pnx_algo_data *alg_data = adap->algo_data;
	unsigned long time_left;
	u32 stat;
	u32 stat;


	dev_dbg(&alg_data->adapter.dev,
	dev_dbg(&alg_data->adapter.dev,
@@ -548,7 +517,6 @@ i2c_pnx_xfer(struct i2c_adapter *adap, struct i2c_msg *msgs, int num)
		dev_dbg(&alg_data->adapter.dev, "%s(): mode %d, %d bytes\n",
		dev_dbg(&alg_data->adapter.dev, "%s(): mode %d, %d bytes\n",
			__func__, alg_data->mif.mode, alg_data->mif.len);
			__func__, alg_data->mif.mode, alg_data->mif.len);


		i2c_pnx_arm_timer(alg_data);


		/* initialize the completion var */
		/* initialize the completion var */
		init_completion(&alg_data->mif.complete);
		init_completion(&alg_data->mif.complete);
@@ -564,7 +532,10 @@ i2c_pnx_xfer(struct i2c_adapter *adap, struct i2c_msg *msgs, int num)
			break;
			break;


		/* Wait for completion */
		/* Wait for completion */
		wait_for_completion(&alg_data->mif.complete);
		time_left = wait_for_completion_timeout(&alg_data->mif.complete,
							alg_data->timeout);
		if (time_left == 0)
			i2c_pnx_timeout(alg_data);


		if (!(rc = alg_data->mif.ret))
		if (!(rc = alg_data->mif.ret))
			completed++;
			completed++;
@@ -657,7 +628,10 @@ static int i2c_pnx_probe(struct platform_device *pdev)
	alg_data->adapter.algo_data = alg_data;
	alg_data->adapter.algo_data = alg_data;
	alg_data->adapter.nr = pdev->id;
	alg_data->adapter.nr = pdev->id;


	alg_data->timeout = I2C_PNX_TIMEOUT_DEFAULT;
	alg_data->timeout = msecs_to_jiffies(I2C_PNX_TIMEOUT_DEFAULT);
	if (alg_data->timeout <= 1)
		alg_data->timeout = 2;

#ifdef CONFIG_OF
#ifdef CONFIG_OF
	alg_data->adapter.dev.of_node = of_node_get(pdev->dev.of_node);
	alg_data->adapter.dev.of_node = of_node_get(pdev->dev.of_node);
	if (pdev->dev.of_node) {
	if (pdev->dev.of_node) {
@@ -677,8 +651,6 @@ static int i2c_pnx_probe(struct platform_device *pdev)
	if (IS_ERR(alg_data->clk))
	if (IS_ERR(alg_data->clk))
		return PTR_ERR(alg_data->clk);
		return PTR_ERR(alg_data->clk);


	timer_setup(&alg_data->mif.timer, i2c_pnx_timeout, 0);

	snprintf(alg_data->adapter.name, sizeof(alg_data->adapter.name),
	snprintf(alg_data->adapter.name, sizeof(alg_data->adapter.name),
		 "%s", pdev->name);
		 "%s", pdev->name);