Commit e3480312 authored by Linus Torvalds's avatar Linus Torvalds

Merge branch 'i2c/for-current' of git://git.kernel.org/pub/scm/linux/kernel/git/wsa/linux

Pull i2c fixes from Wolfram Sang:
 "Here is a revert and two bugfixes for the I2C designware driver.

  Please note that we are still hunting down a regression for the
  i2c-octeon driver. While there is a fix pending, we have unclear
  feedback from the testers currently. An rc8 would be quite helpful
  for this case"

* 'i2c/for-current' of git://git.kernel.org/pub/scm/linux/kernel/git/wsa/linux:
  Revert "i2c: designware: do not disable adapter after transfer"
  i2c: designware: fix rx fifo depth tracking
  i2c: designware: report short transfers
parents a56f3eb2 89119f08
...@@ -92,8 +92,6 @@ ...@@ -92,8 +92,6 @@
DW_IC_INTR_STOP_DET) DW_IC_INTR_STOP_DET)
#define DW_IC_STATUS_ACTIVITY 0x1 #define DW_IC_STATUS_ACTIVITY 0x1
#define DW_IC_STATUS_TFE BIT(2)
#define DW_IC_STATUS_MST_ACTIVITY BIT(5)
#define DW_IC_SDA_HOLD_RX_SHIFT 16 #define DW_IC_SDA_HOLD_RX_SHIFT 16
#define DW_IC_SDA_HOLD_RX_MASK GENMASK(23, DW_IC_SDA_HOLD_RX_SHIFT) #define DW_IC_SDA_HOLD_RX_MASK GENMASK(23, DW_IC_SDA_HOLD_RX_SHIFT)
...@@ -478,25 +476,9 @@ static void i2c_dw_xfer_init(struct dw_i2c_dev *dev) ...@@ -478,25 +476,9 @@ static void i2c_dw_xfer_init(struct dw_i2c_dev *dev)
{ {
struct i2c_msg *msgs = dev->msgs; struct i2c_msg *msgs = dev->msgs;
u32 ic_tar = 0; u32 ic_tar = 0;
bool enabled;
enabled = dw_readl(dev, DW_IC_ENABLE_STATUS) & 1; /* Disable the adapter */
if (enabled) {
u32 ic_status;
/*
* Only disable adapter if ic_tar and ic_con can't be
* dynamically updated
*/
ic_status = dw_readl(dev, DW_IC_STATUS);
if (!dev->dynamic_tar_update_enabled ||
(ic_status & DW_IC_STATUS_MST_ACTIVITY) ||
!(ic_status & DW_IC_STATUS_TFE)) {
__i2c_dw_enable_and_wait(dev, false); __i2c_dw_enable_and_wait(dev, false);
enabled = false;
}
}
/* if the slave address is ten bit address, enable 10BITADDR */ /* if the slave address is ten bit address, enable 10BITADDR */
if (dev->dynamic_tar_update_enabled) { if (dev->dynamic_tar_update_enabled) {
...@@ -526,7 +508,7 @@ static void i2c_dw_xfer_init(struct dw_i2c_dev *dev) ...@@ -526,7 +508,7 @@ static void i2c_dw_xfer_init(struct dw_i2c_dev *dev)
/* enforce disabled interrupts (due to HW issues) */ /* enforce disabled interrupts (due to HW issues) */
i2c_dw_disable_int(dev); i2c_dw_disable_int(dev);
if (!enabled) /* Enable the adapter */
__i2c_dw_enable(dev, true); __i2c_dw_enable(dev, true);
/* Clear and enable interrupts */ /* Clear and enable interrupts */
...@@ -611,7 +593,7 @@ i2c_dw_xfer_msg(struct dw_i2c_dev *dev) ...@@ -611,7 +593,7 @@ i2c_dw_xfer_msg(struct dw_i2c_dev *dev)
if (msgs[dev->msg_write_idx].flags & I2C_M_RD) { if (msgs[dev->msg_write_idx].flags & I2C_M_RD) {
/* avoid rx buffer overrun */ /* avoid rx buffer overrun */
if (rx_limit - dev->rx_outstanding <= 0) if (dev->rx_outstanding >= dev->rx_fifo_depth)
break; break;
dw_writel(dev, cmd | 0x100, DW_IC_DATA_CMD); dw_writel(dev, cmd | 0x100, DW_IC_DATA_CMD);
...@@ -708,8 +690,7 @@ static int i2c_dw_handle_tx_abort(struct dw_i2c_dev *dev) ...@@ -708,8 +690,7 @@ static int i2c_dw_handle_tx_abort(struct dw_i2c_dev *dev)
} }
/* /*
* Prepare controller for a transaction and start transfer by calling * Prepare controller for a transaction and call i2c_dw_xfer_msg
* i2c_dw_xfer_init()
*/ */
static int static int
i2c_dw_xfer(struct i2c_adapter *adap, struct i2c_msg msgs[], int num) i2c_dw_xfer(struct i2c_adapter *adap, struct i2c_msg msgs[], int num)
...@@ -752,13 +733,23 @@ i2c_dw_xfer(struct i2c_adapter *adap, struct i2c_msg msgs[], int num) ...@@ -752,13 +733,23 @@ i2c_dw_xfer(struct i2c_adapter *adap, struct i2c_msg msgs[], int num)
goto done; goto done;
} }
/*
* We must disable the adapter before returning and signaling the end
* of the current transfer. Otherwise the hardware might continue
* generating interrupts which in turn causes a race condition with
* the following transfer. Needs some more investigation if the
* additional interrupts are a hardware bug or this driver doesn't
* handle them correctly yet.
*/
__i2c_dw_enable(dev, false);
if (dev->msg_err) { if (dev->msg_err) {
ret = dev->msg_err; ret = dev->msg_err;
goto done; goto done;
} }
/* no error */ /* no error */
if (likely(!dev->cmd_err)) { if (likely(!dev->cmd_err && !dev->status)) {
ret = num; ret = num;
goto done; goto done;
} }
...@@ -768,6 +759,11 @@ i2c_dw_xfer(struct i2c_adapter *adap, struct i2c_msg msgs[], int num) ...@@ -768,6 +759,11 @@ i2c_dw_xfer(struct i2c_adapter *adap, struct i2c_msg msgs[], int num)
ret = i2c_dw_handle_tx_abort(dev); ret = i2c_dw_handle_tx_abort(dev);
goto done; goto done;
} }
if (dev->status)
dev_err(dev->dev,
"transfer terminated early - interrupt latency too high?\n");
ret = -EIO; ret = -EIO;
done: done:
...@@ -888,19 +884,9 @@ static irqreturn_t i2c_dw_isr(int this_irq, void *dev_id) ...@@ -888,19 +884,9 @@ static irqreturn_t i2c_dw_isr(int this_irq, void *dev_id)
*/ */
tx_aborted: tx_aborted:
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)
|| dev->msg_err) {
/*
* We must disable interruts before returning and signaling
* the end of the current transfer. Otherwise the hardware
* might continue generating interrupts for non-existent
* transfers.
*/
i2c_dw_disable_int(dev);
dw_readl(dev, DW_IC_CLR_INTR);
complete(&dev->cmd_complete); complete(&dev->cmd_complete);
} else if (unlikely(dev->accessor_flags & ACCESS_INTR_MASK)) { else if (unlikely(dev->accessor_flags & ACCESS_INTR_MASK)) {
/* workaround to trigger pending interrupt */ /* workaround to trigger pending interrupt */
stat = dw_readl(dev, DW_IC_INTR_MASK); stat = dw_readl(dev, DW_IC_INTR_MASK);
i2c_dw_disable_int(dev); i2c_dw_disable_int(dev);
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment