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: 41561f28e7
("i2c: New Philips PNX bus driver")
Signed-off-by: Piotr Wojtaszczyk <piotr.wojtaszczyk@timesys.com>
Signed-off-by: Andi Shyti <andi.shyti@kernel.org>
Signed-off-by: Sasha Levin <sashal@kernel.org>
This commit is contained in:

committed by
Greg Kroah-Hartman

parent
2032e5dfae
commit
2849a1b747
@@ -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);
|
||||||
|
|
||||||
|
Reference in New Issue
Block a user