@@ -107,6 +107,7 @@ struct davinci_i2c_dev {
u8 *buf;
size_t buf_len;
int irq;
+ spinlock_t lock;
int stop;
u8 terminate;
struct i2c_adapter adapter;
@@ -312,6 +313,8 @@ i2c_davinci_xfer_msg(struct i2c_adapter *adap, struct i2c_msg *msg, int stop)
u32 flag;
u16 w;
int r;
+ unsigned long flags;
+ int preload = 0;
if (!pdata)
pdata = &davinci_i2c_platform_data_default;
@@ -347,6 +350,15 @@ i2c_davinci_xfer_msg(struct i2c_adapter *adap, struct i2c_msg *msg, int stop)
flag &= ~DAVINCI_I2C_MDR_STP;
}
+ /*
+ * When transmitting, lock ISR out to avoid it racing on the buffer and
+ * DXR register before we are done
+ */
+ if ((!(msg->flags & I2C_M_RD)) && dev->buf_len) {
+ preload = 1;
+ spin_lock_irqsave(&dev->lock, flags);
+ }
+
/* Enable receive or transmit interrupts */
w = davinci_i2c_read_reg(dev, DAVINCI_I2C_IMR_REG);
if (msg->flags & I2C_M_RD)
@@ -366,13 +378,15 @@ i2c_davinci_xfer_msg(struct i2c_adapter *adap, struct i2c_msg *msg, int stop)
* NACK-interrupt during sending of previous message and
* ICDXR may have wrong data
*/
- if ((!(msg->flags & I2C_M_RD)) && dev->buf_len) {
+ if (preload) {
davinci_i2c_write_reg(dev, DAVINCI_I2C_DXR_REG, *dev->buf++);
dev->buf_len--;
+ spin_unlock_irqrestore(&dev->lock, flags);
}
r = wait_for_completion_interruptible_timeout(&dev->cmd_complete,
dev->adapter.timeout);
+
if (r == 0) {
dev_err(dev->dev, "controller timed out\n");
i2c_recover_bus(dev);
@@ -490,6 +504,8 @@ static irqreturn_t i2c_davinci_isr(int this_irq, void *dev_id)
int count = 0;
u16 w;
+ spin_lock(&dev->lock);
+
while ((stat = davinci_i2c_read_reg(dev, DAVINCI_I2C_IVR_REG))) {
dev_dbg(dev->dev, "%s: stat=0x%x\n", __func__, stat);
if (count++ == 100) {
@@ -579,6 +595,8 @@ static irqreturn_t i2c_davinci_isr(int this_irq, void *dev_id)
}
}
+ spin_unlock(&dev->lock);
+
return count ? IRQ_HANDLED : IRQ_NONE;
}
@@ -662,6 +680,7 @@ static int davinci_i2c_probe(struct platform_device *pdev)
goto err_release_region;
}
+ spin_lock_init(&dev->lock);
init_completion(&dev->cmd_complete);
#ifdef CONFIG_CPU_FREQ
init_completion(&dev->xfr_complete);