i2c-davinci: Fix signal handling bug
If wait_for_completion_interruptible_timeout exits due to a signal, the i2c bus was locking up. Signed-off-by: Troy Kisky <troy.kisky@boundarydevices.com> Signed-off-by: Kevin Hilman <khilman@mvista.com> Signed-off-by: Jean Delvare <khali@linux-fr.org>
This commit is contained in:
Родитель
0ab56e2067
Коммит
5a0d5f5ffa
|
@ -85,6 +85,7 @@
|
|||
#define DAVINCI_I2C_MDR_MST (1 << 10)
|
||||
#define DAVINCI_I2C_MDR_TRX (1 << 9)
|
||||
#define DAVINCI_I2C_MDR_XA (1 << 8)
|
||||
#define DAVINCI_I2C_MDR_RM (1 << 7)
|
||||
#define DAVINCI_I2C_MDR_IRS (1 << 5)
|
||||
|
||||
#define DAVINCI_I2C_IMR_AAS (1 << 6)
|
||||
|
@ -112,6 +113,7 @@ struct davinci_i2c_dev {
|
|||
u8 *buf;
|
||||
size_t buf_len;
|
||||
int irq;
|
||||
u8 terminate;
|
||||
struct i2c_adapter adapter;
|
||||
};
|
||||
|
||||
|
@ -283,20 +285,34 @@ i2c_davinci_xfer_msg(struct i2c_adapter *adap, struct i2c_msg *msg, int stop)
|
|||
MOD_REG_BIT(w, DAVINCI_I2C_IMR_XRDY, 1);
|
||||
davinci_i2c_write_reg(dev, DAVINCI_I2C_IMR_REG, w);
|
||||
|
||||
dev->terminate = 0;
|
||||
/* write the data into mode register */
|
||||
davinci_i2c_write_reg(dev, DAVINCI_I2C_MDR_REG, flag);
|
||||
|
||||
r = wait_for_completion_interruptible_timeout(&dev->cmd_complete,
|
||||
DAVINCI_I2C_TIMEOUT);
|
||||
dev->buf_len = 0;
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
if (r == 0) {
|
||||
dev_err(dev->dev, "controller timed out\n");
|
||||
i2c_davinci_init(dev);
|
||||
dev->buf_len = 0;
|
||||
return -ETIMEDOUT;
|
||||
}
|
||||
if (dev->buf_len) {
|
||||
/* This should be 0 if all bytes were transferred
|
||||
* or dev->cmd_err denotes an error.
|
||||
* A signal may have aborted the transfer.
|
||||
*/
|
||||
if (r >= 0) {
|
||||
dev_err(dev->dev, "abnormal termination buf_len=%i\n",
|
||||
dev->buf_len);
|
||||
r = -EREMOTEIO;
|
||||
}
|
||||
dev->terminate = 1;
|
||||
wmb();
|
||||
dev->buf_len = 0;
|
||||
}
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
/* no error */
|
||||
if (likely(!dev->cmd_err))
|
||||
|
@ -354,6 +370,27 @@ static u32 i2c_davinci_func(struct i2c_adapter *adap)
|
|||
return I2C_FUNC_I2C | (I2C_FUNC_SMBUS_EMUL & ~I2C_FUNC_SMBUS_QUICK);
|
||||
}
|
||||
|
||||
static void terminate_read(struct davinci_i2c_dev *dev)
|
||||
{
|
||||
u16 w = davinci_i2c_read_reg(dev, DAVINCI_I2C_MDR_REG);
|
||||
w |= DAVINCI_I2C_MDR_NACK;
|
||||
davinci_i2c_write_reg(dev, DAVINCI_I2C_MDR_REG, w);
|
||||
|
||||
/* Throw away data */
|
||||
davinci_i2c_read_reg(dev, DAVINCI_I2C_DRR_REG);
|
||||
if (!dev->terminate)
|
||||
dev_err(dev->dev, "RDR IRQ while no data requested\n");
|
||||
}
|
||||
static void terminate_write(struct davinci_i2c_dev *dev)
|
||||
{
|
||||
u16 w = davinci_i2c_read_reg(dev, DAVINCI_I2C_MDR_REG);
|
||||
w |= DAVINCI_I2C_MDR_RM | DAVINCI_I2C_MDR_STP;
|
||||
davinci_i2c_write_reg(dev, DAVINCI_I2C_MDR_REG, w);
|
||||
|
||||
if (!dev->terminate)
|
||||
dev_err(dev->dev, "TDR IRQ while no data to send\n");
|
||||
}
|
||||
|
||||
/*
|
||||
* Interrupt service routine. This gets called whenever an I2C interrupt
|
||||
* occurs.
|
||||
|
@ -374,12 +411,15 @@ static irqreturn_t i2c_davinci_isr(int this_irq, void *dev_id)
|
|||
|
||||
switch (stat) {
|
||||
case DAVINCI_I2C_IVR_AL:
|
||||
/* Arbitration lost, must retry */
|
||||
dev->cmd_err |= DAVINCI_I2C_STR_AL;
|
||||
dev->buf_len = 0;
|
||||
complete(&dev->cmd_complete);
|
||||
break;
|
||||
|
||||
case DAVINCI_I2C_IVR_NACK:
|
||||
dev->cmd_err |= DAVINCI_I2C_STR_NACK;
|
||||
dev->buf_len = 0;
|
||||
complete(&dev->cmd_complete);
|
||||
break;
|
||||
|
||||
|
@ -401,9 +441,10 @@ static irqreturn_t i2c_davinci_isr(int this_irq, void *dev_id)
|
|||
davinci_i2c_write_reg(dev,
|
||||
DAVINCI_I2C_STR_REG,
|
||||
DAVINCI_I2C_IMR_RRDY);
|
||||
} else
|
||||
dev_err(dev->dev, "RDR IRQ while no "
|
||||
"data requested\n");
|
||||
} else {
|
||||
/* signal can terminate transfer */
|
||||
terminate_read(dev);
|
||||
}
|
||||
break;
|
||||
|
||||
case DAVINCI_I2C_IVR_XRDY:
|
||||
|
@ -420,9 +461,10 @@ static irqreturn_t i2c_davinci_isr(int this_irq, void *dev_id)
|
|||
davinci_i2c_write_reg(dev,
|
||||
DAVINCI_I2C_IMR_REG,
|
||||
w);
|
||||
} else
|
||||
dev_err(dev->dev, "TDR IRQ while no data to "
|
||||
"send\n");
|
||||
} else {
|
||||
/* signal can terminate transfer */
|
||||
terminate_write(dev);
|
||||
}
|
||||
break;
|
||||
|
||||
case DAVINCI_I2C_IVR_SCD:
|
||||
|
|
Загрузка…
Ссылка в новой задаче