linux-kernel.vger.kernel.org archive mirror
 help / color / mirror / Atom feed
* [PATCH 0/4] UniPhier I2C fixes
@ 2018-12-06  3:55 Masahiro Yamada
  2018-12-06  3:55 ` [PATCH 1/4] i2c: uniphier-f: fix timeout error after reading 8 bytes Masahiro Yamada
                   ` (4 more replies)
  0 siblings, 5 replies; 6+ messages in thread
From: Masahiro Yamada @ 2018-12-06  3:55 UTC (permalink / raw)
  To: linux-i2c; +Cc: Masahiro Yamada, linux-arm-kernel, linux-kernel


Masahiro Yamada (4):
  i2c: uniphier-f: fix timeout error after reading 8 bytes
  i2c: uniphier-f: fill TX-FIFO only in IRQ handler for repeated START
  i2c: uniphier: fix violation of tLOW requirement for Fast-mode
  i2c: uniphier-f: fix violation of tLOW requirement for Fast-mode

 drivers/i2c/busses/i2c-uniphier-f.c | 49 +++++++++++++++++++++++++++++++------
 drivers/i2c/busses/i2c-uniphier.c   |  8 +++++-
 2 files changed, 48 insertions(+), 9 deletions(-)

-- 
2.7.4


^ permalink raw reply	[flat|nested] 6+ messages in thread

* [PATCH 1/4] i2c: uniphier-f: fix timeout error after reading 8 bytes
  2018-12-06  3:55 [PATCH 0/4] UniPhier I2C fixes Masahiro Yamada
@ 2018-12-06  3:55 ` Masahiro Yamada
  2018-12-06  3:55 ` [PATCH 2/4] i2c: uniphier-f: fill TX-FIFO only in IRQ handler for repeated START Masahiro Yamada
                   ` (3 subsequent siblings)
  4 siblings, 0 replies; 6+ messages in thread
From: Masahiro Yamada @ 2018-12-06  3:55 UTC (permalink / raw)
  To: linux-i2c; +Cc: Masahiro Yamada, linux-arm-kernel, linux-kernel

I was totally screwed up in commit eaba68785c2d ("i2c: uniphier-f:
fix race condition when IRQ is cleared"). Since that commit, if the
number of read bytes is multiple of the FIFO size (8, 16, 24... bytes),
the STOP condition could be issued twice, depending on the timing.
If this happens, the controller will go wrong, resulting in the timeout
error.

It was more than 3 years ago when I wrote this driver, so my memory
about this hardware was vague. Please let me correct the description
in the commit log of eaba68785c2d.

Clearing the IRQ status on exiting the IRQ handler is absolutely
fine. This controller makes a pause while any IRQ status is asserted.
If the IRQ status is cleared first, the hardware may start the next
transaction before the IRQ handler finishes what it supposed to do.

This partially reverts the bad commit with clear comments so that I
will never repeat this mistake.

I also investigated what is happening at the last moment of the read
mode. The UNIPHIER_FI2C_INT_RF interrupt is asserted a bit earlier
(by half a period of the clock cycle) than UNIPHIER_FI2C_INT_RB.

I consulted a hardware engineer, and I got the following information:

UNIPHIER_FI2C_INT_RF
    asserted at the falling edge of SCL at the 8th bit.

UNIPHIER_FI2C_INT_RB
    asserted at the rising edge of SCL at the 9th (ACK) bit.

In order to avoid calling uniphier_fi2c_stop() twice, check the latter
interrupt. I also commented this because it is obscure hardware internal.

Fixes: eaba68785c2d ("i2c: uniphier-f: fix race condition when IRQ is cleared")
Signed-off-by: Masahiro Yamada <yamada.masahiro@socionext.com>
---

 drivers/i2c/busses/i2c-uniphier-f.c | 17 ++++++++++++++---
 1 file changed, 14 insertions(+), 3 deletions(-)

diff --git a/drivers/i2c/busses/i2c-uniphier-f.c b/drivers/i2c/busses/i2c-uniphier-f.c
index dd38474..fad2b00 100644
--- a/drivers/i2c/busses/i2c-uniphier-f.c
+++ b/drivers/i2c/busses/i2c-uniphier-f.c
@@ -173,8 +173,6 @@ static irqreturn_t uniphier_fi2c_interrupt(int irq, void *dev_id)
 		"interrupt: enabled_irqs=%04x, irq_status=%04x\n",
 		priv->enabled_irqs, irq_status);
 
-	uniphier_fi2c_clear_irqs(priv, irq_status);
-
 	if (irq_status & UNIPHIER_FI2C_INT_STOP)
 		goto complete;
 
@@ -214,7 +212,13 @@ static irqreturn_t uniphier_fi2c_interrupt(int irq, void *dev_id)
 
 	if (irq_status & (UNIPHIER_FI2C_INT_RF | UNIPHIER_FI2C_INT_RB)) {
 		uniphier_fi2c_drain_rxfifo(priv);
-		if (!priv->len)
+		/*
+		 * If the number of bytes to read is multiple of the FIFO size
+		 * (msg->len == 8, 16, 24, ...), the INT_RF bit is set a little
+		 * earlier than INT_RB. We wait for INT_RB to confirm the
+		 * completion of the current message.
+		 */
+		if (!priv->len && (irq_status & UNIPHIER_FI2C_INT_RB))
 			goto data_done;
 
 		if (unlikely(priv->flags & UNIPHIER_FI2C_MANUAL_NACK)) {
@@ -253,6 +257,13 @@ static irqreturn_t uniphier_fi2c_interrupt(int irq, void *dev_id)
 	}
 
 handled:
+	/*
+	 * This controller makes a pause while any bit of the IRQ status is
+	 * asserted. Clear the asserted bit to kick the controller just before
+	 * exiting the handler.
+	 */
+	uniphier_fi2c_clear_irqs(priv, irq_status);
+
 	spin_unlock(&priv->lock);
 
 	return IRQ_HANDLED;
-- 
2.7.4


^ permalink raw reply related	[flat|nested] 6+ messages in thread

* [PATCH 2/4] i2c: uniphier-f: fill TX-FIFO only in IRQ handler for repeated START
  2018-12-06  3:55 [PATCH 0/4] UniPhier I2C fixes Masahiro Yamada
  2018-12-06  3:55 ` [PATCH 1/4] i2c: uniphier-f: fix timeout error after reading 8 bytes Masahiro Yamada
@ 2018-12-06  3:55 ` Masahiro Yamada
  2018-12-06  3:55 ` [PATCH 3/4] i2c: uniphier: fix violation of tLOW requirement for Fast-mode Masahiro Yamada
                   ` (2 subsequent siblings)
  4 siblings, 0 replies; 6+ messages in thread
From: Masahiro Yamada @ 2018-12-06  3:55 UTC (permalink / raw)
  To: linux-i2c; +Cc: Masahiro Yamada, linux-arm-kernel, linux-kernel

 - For a repeated START condition, this controller starts data transfer
   immediately after the slave address is written to the TX-FIFO.

 - Once the TX-FIFO empty interrupt is asserted, the controller makes
   a pause even if additional data are written to the TX-FIFO.

Given those circumstances, the data after a repeated START may not be
transferred if the interrupt is asserted while the TX-FIFO is being
filled up. A more reliable way is to append TX data only in the
interrupt handler.

Signed-off-by: Masahiro Yamada <yamada.masahiro@socionext.com>
---

 drivers/i2c/busses/i2c-uniphier-f.c | 13 +++++++++----
 1 file changed, 9 insertions(+), 4 deletions(-)

diff --git a/drivers/i2c/busses/i2c-uniphier-f.c b/drivers/i2c/busses/i2c-uniphier-f.c
index fad2b00..d8a5db14 100644
--- a/drivers/i2c/busses/i2c-uniphier-f.c
+++ b/drivers/i2c/busses/i2c-uniphier-f.c
@@ -269,7 +269,8 @@ static irqreturn_t uniphier_fi2c_interrupt(int irq, void *dev_id)
 	return IRQ_HANDLED;
 }
 
-static void uniphier_fi2c_tx_init(struct uniphier_fi2c_priv *priv, u16 addr)
+static void uniphier_fi2c_tx_init(struct uniphier_fi2c_priv *priv, u16 addr,
+				  bool repeat)
 {
 	priv->enabled_irqs |= UNIPHIER_FI2C_INT_TE;
 	uniphier_fi2c_set_irqs(priv);
@@ -279,8 +280,12 @@ static void uniphier_fi2c_tx_init(struct uniphier_fi2c_priv *priv, u16 addr)
 	/* set slave address */
 	writel(UNIPHIER_FI2C_DTTX_CMD | addr << 1,
 	       priv->membase + UNIPHIER_FI2C_DTTX);
-	/* first chunk of data */
-	uniphier_fi2c_fill_txfifo(priv, true);
+	/*
+	 * First chunk of data. For a repeated START condition, do not write
+	 * data to the TX fifo here to avoid the timing issue.
+	 */
+	if (!repeat)
+		uniphier_fi2c_fill_txfifo(priv, true);
 }
 
 static void uniphier_fi2c_rx_init(struct uniphier_fi2c_priv *priv, u16 addr)
@@ -361,7 +366,7 @@ static int uniphier_fi2c_master_xfer_one(struct i2c_adapter *adap,
 	if (is_read)
 		uniphier_fi2c_rx_init(priv, msg->addr);
 	else
-		uniphier_fi2c_tx_init(priv, msg->addr);
+		uniphier_fi2c_tx_init(priv, msg->addr, repeat);
 
 	dev_dbg(&adap->dev, "start condition\n");
 	/*
-- 
2.7.4


^ permalink raw reply related	[flat|nested] 6+ messages in thread

* [PATCH 3/4] i2c: uniphier: fix violation of tLOW requirement for Fast-mode
  2018-12-06  3:55 [PATCH 0/4] UniPhier I2C fixes Masahiro Yamada
  2018-12-06  3:55 ` [PATCH 1/4] i2c: uniphier-f: fix timeout error after reading 8 bytes Masahiro Yamada
  2018-12-06  3:55 ` [PATCH 2/4] i2c: uniphier-f: fill TX-FIFO only in IRQ handler for repeated START Masahiro Yamada
@ 2018-12-06  3:55 ` Masahiro Yamada
  2018-12-06  3:55 ` [PATCH 4/4] i2c: uniphier-f: " Masahiro Yamada
  2018-12-06 22:06 ` [PATCH 0/4] UniPhier I2C fixes Wolfram Sang
  4 siblings, 0 replies; 6+ messages in thread
From: Masahiro Yamada @ 2018-12-06  3:55 UTC (permalink / raw)
  To: linux-i2c; +Cc: Masahiro Yamada, linux-arm-kernel, linux-kernel

Currently, the clock duty is set as tLOW/tHIGH = 1/1. For Fast-mode,
tLOW is set to 1.25 us while the I2C spec requires tLOW >= 1.3 us.

tLOW/tHIGH = 5/4 would meet both Standard-mode and Fast-mode:
  Standard-mode: tLOW = 5.56 us, tHIGH = 4.44 us
  Fast-mode:     tLOW = 1.39 us, tHIGH = 1.11 us

Signed-off-by: Masahiro Yamada <yamada.masahiro@socionext.com>
---

 drivers/i2c/busses/i2c-uniphier.c | 8 +++++++-
 1 file changed, 7 insertions(+), 1 deletion(-)

diff --git a/drivers/i2c/busses/i2c-uniphier.c b/drivers/i2c/busses/i2c-uniphier.c
index 454f914..c488e55 100644
--- a/drivers/i2c/busses/i2c-uniphier.c
+++ b/drivers/i2c/busses/i2c-uniphier.c
@@ -320,7 +320,13 @@ static void uniphier_i2c_hw_init(struct uniphier_i2c_priv *priv)
 
 	uniphier_i2c_reset(priv, true);
 
-	writel((cyc / 2 << 16) | cyc, priv->membase + UNIPHIER_I2C_CLK);
+	/*
+	 * Bit30-16: clock cycles of tLOW.
+	 *  Standard-mode: tLOW = 4.7 us, tHIGH = 4.0 us
+	 *  Fast-mode:     tLOW = 1.3 us, tHIGH = 0.6 us
+	 * "tLow/tHIGH = 5/4" meets both.
+	 */
+	writel((cyc * 5 / 9 << 16) | cyc, priv->membase + UNIPHIER_I2C_CLK);
 
 	uniphier_i2c_reset(priv, false);
 }
-- 
2.7.4


^ permalink raw reply related	[flat|nested] 6+ messages in thread

* [PATCH 4/4] i2c: uniphier-f: fix violation of tLOW requirement for Fast-mode
  2018-12-06  3:55 [PATCH 0/4] UniPhier I2C fixes Masahiro Yamada
                   ` (2 preceding siblings ...)
  2018-12-06  3:55 ` [PATCH 3/4] i2c: uniphier: fix violation of tLOW requirement for Fast-mode Masahiro Yamada
@ 2018-12-06  3:55 ` Masahiro Yamada
  2018-12-06 22:06 ` [PATCH 0/4] UniPhier I2C fixes Wolfram Sang
  4 siblings, 0 replies; 6+ messages in thread
From: Masahiro Yamada @ 2018-12-06  3:55 UTC (permalink / raw)
  To: linux-i2c; +Cc: Masahiro Yamada, linux-arm-kernel, linux-kernel

Currently, the clock duty is set as tLOW/tHIGH = 1/1. For Fast-mode,
tLOW is set to 1.25 us while the I2C spec requires tLOW >= 1.3 us.

tLOW/tHIGH = 5/4 would meet both Standard-mode and Fast-mode:
  Standard-mode: tLOW = 5.56 us, tHIGH = 4.44 us
  Fast-mode:     tLOW = 1.39 us, tHIGH = 1.11 us

Signed-off-by: Masahiro Yamada <yamada.masahiro@socionext.com>
---

 drivers/i2c/busses/i2c-uniphier-f.c | 19 ++++++++++++++++++-
 1 file changed, 18 insertions(+), 1 deletion(-)

diff --git a/drivers/i2c/busses/i2c-uniphier-f.c b/drivers/i2c/busses/i2c-uniphier-f.c
index d8a5db14..03da4a5 100644
--- a/drivers/i2c/busses/i2c-uniphier-f.c
+++ b/drivers/i2c/busses/i2c-uniphier-f.c
@@ -518,9 +518,26 @@ static void uniphier_fi2c_hw_init(struct uniphier_fi2c_priv *priv)
 
 	uniphier_fi2c_reset(priv);
 
+	/*
+	 *  Standard-mode: tLOW + tHIGH = 10 us
+	 *  Fast-mode:     tLOW + tHIGH = 2.5 us
+	 */
 	writel(cyc, priv->membase + UNIPHIER_FI2C_CYC);
-	writel(cyc / 2, priv->membase + UNIPHIER_FI2C_LCTL);
+	/*
+	 *  Standard-mode: tLOW = 4.7 us, tHIGH = 4.0 us, tBUF = 4.7 us
+	 *  Fast-mode:     tLOW = 1.3 us, tHIGH = 0.6 us, tBUF = 1.3 us
+	 * "tLow/tHIGH = 5/4" meets both.
+	 */
+	writel(cyc * 5 / 9, priv->membase + UNIPHIER_FI2C_LCTL);
+	/*
+	 *  Standard-mode: tHD;STA = 4.0 us, tSU;STA = 4.7 us, tSU;STO = 4.0 us
+	 *  Fast-mode:     tHD;STA = 0.6 us, tSU;STA = 0.6 us, tSU;STO = 0.6 us
+	 */
 	writel(cyc / 2, priv->membase + UNIPHIER_FI2C_SSUT);
+	/*
+	 *  Standard-mode: tSU;DAT = 250 ns
+	 *  Fast-mode:     tSU;DAT = 100 ns
+	 */
 	writel(cyc / 16, priv->membase + UNIPHIER_FI2C_DSUT);
 
 	uniphier_fi2c_prepare_operation(priv);
-- 
2.7.4


^ permalink raw reply related	[flat|nested] 6+ messages in thread

* Re: [PATCH 0/4] UniPhier I2C fixes
  2018-12-06  3:55 [PATCH 0/4] UniPhier I2C fixes Masahiro Yamada
                   ` (3 preceding siblings ...)
  2018-12-06  3:55 ` [PATCH 4/4] i2c: uniphier-f: " Masahiro Yamada
@ 2018-12-06 22:06 ` Wolfram Sang
  4 siblings, 0 replies; 6+ messages in thread
From: Wolfram Sang @ 2018-12-06 22:06 UTC (permalink / raw)
  To: Masahiro Yamada; +Cc: linux-i2c, linux-arm-kernel, linux-kernel

[-- Attachment #1: Type: text/plain, Size: 443 bytes --]

On Thu, Dec 06, 2018 at 12:55:24PM +0900, Masahiro Yamada wrote:
> 
> Masahiro Yamada (4):
>   i2c: uniphier-f: fix timeout error after reading 8 bytes
>   i2c: uniphier-f: fill TX-FIFO only in IRQ handler for repeated START
>   i2c: uniphier: fix violation of tLOW requirement for Fast-mode
>   i2c: uniphier-f: fix violation of tLOW requirement for Fast-mode

They all look like bugfixes to me, so applied to for-current, thanks!


[-- Attachment #2: signature.asc --]
[-- Type: application/pgp-signature, Size: 833 bytes --]

^ permalink raw reply	[flat|nested] 6+ messages in thread

end of thread, other threads:[~2018-12-06 22:06 UTC | newest]

Thread overview: 6+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2018-12-06  3:55 [PATCH 0/4] UniPhier I2C fixes Masahiro Yamada
2018-12-06  3:55 ` [PATCH 1/4] i2c: uniphier-f: fix timeout error after reading 8 bytes Masahiro Yamada
2018-12-06  3:55 ` [PATCH 2/4] i2c: uniphier-f: fill TX-FIFO only in IRQ handler for repeated START Masahiro Yamada
2018-12-06  3:55 ` [PATCH 3/4] i2c: uniphier: fix violation of tLOW requirement for Fast-mode Masahiro Yamada
2018-12-06  3:55 ` [PATCH 4/4] i2c: uniphier-f: " Masahiro Yamada
2018-12-06 22:06 ` [PATCH 0/4] UniPhier I2C fixes Wolfram Sang

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).