|
@@ -91,7 +91,9 @@
|
|
DW_IC_INTR_TX_ABRT | \
|
|
DW_IC_INTR_TX_ABRT | \
|
|
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_ERR_TX_ABRT 0x1
|
|
#define DW_IC_ERR_TX_ABRT 0x1
|
|
|
|
|
|
@@ -461,9 +463,25 @@ 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;
|
|
|
|
|
|
- /* Disable the adapter */
|
|
|
|
- __i2c_dw_enable_and_wait(dev, false);
|
|
|
|
|
|
+ enabled = dw_readl(dev, DW_IC_ENABLE_STATUS) & 1;
|
|
|
|
+
|
|
|
|
+ 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);
|
|
|
|
+ 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) {
|
|
@@ -493,8 +511,8 @@ 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);
|
|
|
|
|
|
- /* Enable the adapter */
|
|
|
|
- __i2c_dw_enable(dev, true);
|
|
|
|
|
|
+ if (!enabled)
|
|
|
|
+ __i2c_dw_enable(dev, true);
|
|
|
|
|
|
/* Clear and enable interrupts */
|
|
/* Clear and enable interrupts */
|
|
dw_readl(dev, DW_IC_CLR_INTR);
|
|
dw_readl(dev, DW_IC_CLR_INTR);
|
|
@@ -675,7 +693,8 @@ static int i2c_dw_handle_tx_abort(struct dw_i2c_dev *dev)
|
|
}
|
|
}
|
|
|
|
|
|
/*
|
|
/*
|
|
- * Prepare controller for a transaction and call i2c_dw_xfer_msg
|
|
|
|
|
|
+ * Prepare controller for a transaction and start transfer by calling
|
|
|
|
+ * 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)
|
|
@@ -718,16 +737,6 @@ 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;
|
|
@@ -864,9 +873,19 @@ 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)) || dev->msg_err)
|
|
|
|
|
|
+ if ((stat & (DW_IC_INTR_TX_ABRT | DW_IC_INTR_STOP_DET))
|
|
|
|
+ || 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);
|