Skip to content

Commit

Permalink
i2c-designware: i2c_dw_xfer_msg: Fix error handling procedures
Browse files Browse the repository at this point in the history
Current error handling procedures are not good in two respects:

* Forgot to mark dev->cmd_complete as "completed" on errors

  Once an I2C transaction is initiated, wait_for_completion_
  interruptible_timeout() waits for dev->cmd_complete to be completed.
  We have to take care of it whenever an error is detected, otherwise
  we will have a needless HZ timeout.

* Forgot to disable interrupts

  In the previous patch, interrupt mask operations have been changed.
  We don't disable interrupts at the end of the interrupt handler any
  more, and try to keep RX_FULL (and TX_EMPTY if required) enabled
  during the transaction so that we can send longer data than the size
  of Tx/Rx FIFO.

  If an error is detected, we need to disable interrupts before
  quitting current transaction.

We can work around above points using dev->msg_err effectively.

Signed-off-by: Shinya Kuribayashi <shinya.kuribayashi@necel.com>
Signed-off-by: Ben Dooks <ben-linux@fluff.org>
  • Loading branch information
Shinya Kuribayashi authored and Ben Dooks committed Dec 9, 2009
1 parent 69151e5 commit 8f588e4
Showing 1 changed file with 11 additions and 4 deletions.
15 changes: 11 additions & 4 deletions drivers/i2c/busses/i2c-designware.c
Original file line number Diff line number Diff line change
Expand Up @@ -380,14 +380,18 @@ i2c_dw_xfer_msg(struct dw_i2c_dev *dev)
* reprogram the target address in the i2c
* adapter when we are done with this transfer
*/
if (msgs[dev->msg_write_idx].addr != addr)
return;
if (msgs[dev->msg_write_idx].addr != addr) {
dev_err(dev->dev,
"%s: invalid target address\n", __func__);
dev->msg_err = -EINVAL;
break;
}

if (msgs[dev->msg_write_idx].len == 0) {
dev_err(dev->dev,
"%s: invalid message length\n", __func__);
dev->msg_err = -EINVAL;
return;
break;
}

if (!(dev->status & STATUS_WRITE_IN_PROGRESS)) {
Expand Down Expand Up @@ -426,6 +430,9 @@ i2c_dw_xfer_msg(struct dw_i2c_dev *dev)
if (dev->msg_write_idx == dev->msgs_num)
intr_mask &= ~DW_IC_INTR_TX_EMPTY;

if (dev->msg_err)
intr_mask = 0;

writel(intr_mask, dev->base + DW_IC_INTR_MASK);
}

Expand Down Expand Up @@ -628,7 +635,7 @@ static irqreturn_t i2c_dw_isr(int this_irq, void *dev_id)
* the current transmit status.
*/

if (stat & (DW_IC_INTR_TX_ABRT | DW_IC_INTR_STOP_DET))
if ((stat & (DW_IC_INTR_TX_ABRT | DW_IC_INTR_STOP_DET)) || dev->msg_err)
complete(&dev->cmd_complete);

return IRQ_HANDLED;
Expand Down

0 comments on commit 8f588e4

Please sign in to comment.