From f284d6f5d542ad65d010353fe219024bd2e23363 Mon Sep 17 00:00:00 2001
From: "Wu, Qiming" <qi-ming.wu@intel.com>
Date: Thu, 8 Mar 2018 06:23:52 +0100
Subject: [PATCH] Merge pull request #237 in SW_PON/linux from
 bugfix/DRVLIB_SW-446-clone-i2c-failures to xrx500

* commit '92c026af8cdb77665a84a3993917c2436034134f':
  Remove all inlines from i2c-lantiq
  Change ltq i2c tx transmission size to 4-bytes
  Add i2c-ltq recovery in case of underflow or bus error
---
 drivers/i2c/busses/i2c-lantiq.c | 74 ++++++++++++++++++++++++---------
 drivers/i2c/busses/i2c-lantiq.h | 14 ++++++-
 2 files changed, 68 insertions(+), 20 deletions(-)

diff --git a/drivers/i2c/busses/i2c-lantiq.c b/drivers/i2c/busses/i2c-lantiq.c
index 907d53edd..a0ff6a613 100644
--- a/drivers/i2c/busses/i2c-lantiq.c
+++ b/drivers/i2c/busses/i2c-lantiq.c
@@ -330,7 +330,7 @@ static irqreturn_t ltq_i2c_isr(int irq, void *dev_id);
  * handler). For this reason we avoid using last-burst in rx, and rely on
  * tx-end instead to end transfer.
  */
-static inline void enable_burst_irq(struct ltq_i2c *priv)
+static void enable_burst_irq(struct ltq_i2c *priv)
 {
 	int mask;
 
@@ -342,7 +342,8 @@ static inline void enable_burst_irq(struct ltq_i2c *priv)
 
 	i2c_w32_mask(0, mask, imsc);
 }
-static inline void disable_burst_irq(struct ltq_i2c *priv)
+
+static void disable_burst_irq(struct ltq_i2c *priv)
 {
 	i2c_w32_mask(I2C_IMSC_LBREQ_INT_EN |
 		I2C_IMSC_BREQ_INT_EN |
@@ -479,8 +480,8 @@ static int ltq_i2c_hw_init(struct i2c_adapter *adap)
 	/* configure fifo */
 	i2c_w32(I2C_FIFO_CFG_TXFC | /* tx fifo as flow controller */
 		I2C_FIFO_CFG_RXFC | /* rx fifo as flow controller */
-		I2C_FIFO_CFG_TXFA_TXFA2 | /* tx fifo 4-byte aligned */
-		I2C_FIFO_CFG_RXFA_RXFA2 | /* rx fifo 4-byte aligned */
+		I2C_FIFO_CFG_TXFA_TXFA0 | /* tx fifo byte aligned */
+		I2C_FIFO_CFG_RXFA_RXFA0 | /* rx fifo byte aligned */
 		I2C_FIFO_CFG_TXBS_TXBS0 | /* tx fifo burst size is 1 word */
 		I2C_FIFO_CFG_RXBS_RXBS0,  /* rx fifo burst size is 1 word */
 		fifo_cfg);
@@ -530,21 +531,57 @@ static int ltq_i2c_wait_bus_not_busy(struct ltq_i2c *priv)
 	return -ETIMEDOUT;
 }
 
-static inline void ltq_i2c_tx(struct ltq_i2c *priv)
+/**
+ * Send addr and tx in word (LSB first)
+ *
+ * Address and tx data are sent together in word (4-bytes), to eliminate/reduce
+ * the dependency on tx interrupt.
+ */
+static void ltq_i2c_tx(struct ltq_i2c *priv)
 {
+	struct i2c_msg *msg = priv->current_msg;
+	u16 addr = msg->addr;
+	u32 word = 0;
+	u32 tmp;
+	const int bytes_per_word = 4;
+	int i = 0;
+
+	if (priv->status == STATUS_ADDR) {
+		/* new i2c_msg */
+		priv->msg_buf = msg->buf;
+		priv->msg_buf_len = msg->len;
+		pr_debug("ADDR: msg->flags[%d] addr 0x%x\n", msg->flags, addr);
+
+		/* send slave address */
+		if (msg->flags & I2C_M_TEN) {
+			word = 0xf0 | ((addr & 0x300) >> 7);
+			word |= ((addr & 0xff) << 8);
+			i += 2;
+		} else {
+			word = (addr & 0x7f) << 1;
+			i += 1;
+		}
+		priv->status = STATUS_WRITE;
+	}
 
-	if (priv->msg_buf_len && priv->msg_buf) {
-		i2c_w32(*priv->msg_buf, txd);
-		if (--priv->msg_buf_len)
-			priv->msg_buf++;
-		else
-			priv->msg_buf = NULL;
-	} else
-		priv->last_burst = LAST_BURST;
+	for (; i < bytes_per_word; i++) {
+		if (priv->msg_buf_len && priv->msg_buf) {
+			tmp = ((u32)*priv->msg_buf) << (8 * i);
+			word |= tmp;
+			if (--priv->msg_buf_len)
+				priv->msg_buf++;
+			else
+				priv->msg_buf = NULL;
+		} else {
+			priv->last_burst = LAST_BURST;
+		}
+	}
+
+	/* write as word */
+	i2c_w32(word, txd);
 
 	if (priv->last_burst)
 		disable_burst_irq(priv);
-
 }
 
 /**
@@ -554,7 +591,7 @@ static inline void ltq_i2c_tx(struct ltq_i2c *priv)
  * sometimes ffs increased without rps being increased. Therefore here we
  * ignore rps and rely solely on ffs.
  */
-static inline int ltq_i2c_rx(struct ltq_i2c *priv)
+static int ltq_i2c_rx(struct ltq_i2c *priv)
 {
 	u32 fifo_filled;
 	int ret = 0;
@@ -683,6 +720,7 @@ static int ltq_i2c_xfer(struct i2c_adapter *adap, struct i2c_msg msgs[],
 	ret = ltq_i2c_wait_bus_not_busy(priv);
 	if (ret) {
 		dev_err(priv->dev, "%s: bus is busy %x\n", __func__, ret);
+		ltq_i2c_hw_init(adap);
 		goto done;
 	}
 
@@ -716,6 +754,7 @@ static int ltq_i2c_xfer(struct i2c_adapter *adap, struct i2c_msg msgs[],
 					dev_err(priv->dev, " rx underflow\n");
 
 				ret = -EREMOTEIO;
+				ltq_i2c_hw_init(adap);
 			}
 
 			goto done;
@@ -805,9 +844,6 @@ static irqreturn_t ltq_i2c_isr_burst(int irq, void *dev_id)
 	} else {
 		switch (priv->status) {
 		case STATUS_ADDR:
-			pr_debug("===x===\n");
-			prepare_msg_send_addr(priv);
-			break;
 		case STATUS_WRITE:
 			pr_debug("===w===\n");
 			ltq_i2c_tx(priv);
@@ -823,7 +859,7 @@ static irqreturn_t ltq_i2c_isr_burst(int irq, void *dev_id)
 	return ret;
 }
 
-static inline irqreturn_t ltq_i2c_isr_prot(struct ltq_i2c *priv)
+static irqreturn_t ltq_i2c_isr_prot(struct ltq_i2c *priv)
 {
 	u32 i_pro = i2c_r32(p_irqss);
 	int ret = IRQ_HANDLED;
diff --git a/drivers/i2c/busses/i2c-lantiq.h b/drivers/i2c/busses/i2c-lantiq.h
index 9e1e3bb9e..7f84dcdd5 100644
--- a/drivers/i2c/busses/i2c-lantiq.h
+++ b/drivers/i2c/busses/i2c-lantiq.h
@@ -173,10 +173,22 @@ struct lantiq_reg_i2c {
 #define I2C_FIFO_CFG_TXFC 0x00020000
 /* RX FIFO Flow Control */
 #define I2C_FIFO_CFG_RXFC 0x00010000
+/* Byte aligned (character alignment) */
+#define I2C_FIFO_CFG_TXFA_TXFA0 0x00000000
+/* Half word aligned (character alignment of two characters) */
+#define I2C_FIFO_CFG_TXFA_TXFA1 0x00001000
 /* Word aligned (character alignment of four characters) */
 #define I2C_FIFO_CFG_TXFA_TXFA2 0x00002000
+/* Double word aligned (character alignment of eight */
+#define I2C_FIFO_CFG_TXFA_TXFA3 0x00003000
+/* Byte aligned (character alignment) */
+#define I2C_FIFO_CFG_RXFA_RXFA0 0x00000000
+/* Half word aligned (character alignment of two characters) */
+#define I2C_FIFO_CFG_RXFA_RXFA1 0x00000100
 /* Word aligned (character alignment of four characters) */
-#define I2C_FIFO_CFG_RXFA_RXFA2 0x00000000
+#define I2C_FIFO_CFG_RXFA_RXFA2 0x00000200
+/* Double word aligned (character alignment of eight */
+#define I2C_FIFO_CFG_RXFA_RXFA3 0x00000300
 /* 1 word: 0 for 1 word, 2 for 4 words, prevent txunder */
 #define I2C_FIFO_CFG_TXBS_TXBS0 0x00000000
 
-- 
GitLab