Skip to contentSkip to conversation

i2c-designware: fix RX FIFO overrun

PATCH Applied Josef Ahmad 4 participants 17 messages Apr 19, 2013 – May 17, 2013

Subsystems (3)
lore
Share & tools
lore
#0 lore raw

PATCH 1/1 i2c-designware: fix RX FIFO overrun

Quoted 1 line

From a969728248c3b439dc97a69e7dac133b5efa34e7 Mon Sep 17 00:00:00 2001

From: Josef Ahmad <[email protected]>
Date: Fri, 19 Apr 2013 17:28:10 +0100
Subject: [PATCH] i2c-designware: fix RX FIFO overrun

i2c_dw_xfer_msg() pushes a number of bytes to transmit/receive to/from the bus into the TX FIFO.
For master-rx transactions, the maximum amount of data that can be received is calculated depending solely on TX and RX FIFO load.

This is racy - TX FIFO may contain master-rx data yet to be processed, which will eventually land into the RX FIFO. This data is not taken into account and the function may request more data than the controller is actually capable of storing.

This patch ensures the driver takes into account the outstanding master-rx data in TX FIFO to prevent RX FIFO overrun.

---
 drivers/i2c/busses/i2c-designware-core.c |   11 ++++++++++-
 drivers/i2c/busses/i2c-designware-core.h |    2 ++
 2 files changed, 12 insertions(+), 1 deletions(-)
drivers/i2c/busses/i2c-designware-core.c +10 -1
index 94fd818..8dbeef1 100644
@@ -426,8 +426,14 @@ i2c_dw_xfer_msg(struct dw_i2c_dev *dev)
 				cmd |= BIT(9);
 
 			if (msgs[dev->msg_write_idx].flags & I2C_M_RD) {
+
+				/* avoid rx buffer overrun */
+				if (rx_limit - dev->rx_outstanding <= 0)
+					break;
+
 				dw_writel(dev, cmd | 0x100, DW_IC_DATA_CMD);
 				rx_limit--;
+				dev->rx_outstanding++;
 			} else
 				dw_writel(dev, cmd | *buf++, DW_IC_DATA_CMD);
 			tx_limit--; buf_len--;
@@ -480,8 +486,10 @@ i2c_dw_read(struct dw_i2c_dev *dev)
 
 		rx_valid = dw_readl(dev, DW_IC_RXFLR);
 
-		for (; len > 0 && rx_valid > 0; len--, rx_valid--)
+		for (; len > 0 && rx_valid > 0; len--, rx_valid--) {
 			*buf++ = dw_readl(dev, DW_IC_DATA_CMD);
+			dev->rx_outstanding--;
+		}
 
 		if (len > 0) {
 			dev->status |= STATUS_READ_IN_PROGRESS;
@@ -539,6 +547,7 @@ i2c_dw_xfer(struct i2c_adapter *adap, struct i2c_msg msgs[], int num)
 	dev->msg_err = 0;
 	dev->status = STATUS_IDLE;
 	dev->abort_source = 0;
+	dev->rx_outstanding = 0;
 
 	ret = i2c_dw_wait_bus_not_busy(dev);
 	if (ret < 0)
drivers/i2c/busses/i2c-designware-core.h +2 -0
index 9c1840e..e761ad1 100644
@@ -60,6 +60,7 @@
  * @adapter: i2c subsystem adapter node
  * @tx_fifo_depth: depth of the hardware tx fifo
  * @rx_fifo_depth: depth of the hardware rx fifo
+ * @rx_outstanding: current master-rx elements in tx fifo
  */
 struct dw_i2c_dev {
 	struct device		*dev;
@@ -88,6 +89,7 @@ struct dw_i2c_dev {
 	u32			master_cfg;
 	unsigned int		tx_fifo_depth;
 	unsigned int		rx_fifo_depth;
+	int			rx_outstanding;
 };
 
 #define ACCESS_SWAP		0x00000001
Signature · 1 line
1.7.0.7
#1 lore raw

Josef.

This fixes a real bug for us does it not, some failure case with a sustained amount of traffic ?

Bryan

#2 lore raw

It does.
The bug appears with fairly-sized read transactions (in the order of kB) returning corrupted data.

Josef

Quoted 9 lines

Josef.

This fixes a real bug for us does it not, some failure case with a sustained amount of traffic ?

Bryan

maintainer
#3 lore raw
In reply to Josef Ahmad
Quoted 10 lines · Josef Ahmad
Quoted 1 line

From a969728248c3b439dc97a69e7dac133b5efa34e7 Mon Sep 17 00:00:00 2001

From: Josef Ahmad <[email protected]>
Date: Fri, 19 Apr 2013 17:28:10 +0100
Subject: [PATCH] i2c-designware: fix RX FIFO overrun

i2c_dw_xfer_msg() pushes a number of bytes to transmit/receive to/from the bus into the TX FIFO.
For master-rx transactions, the maximum amount of data that can be received is calculated depending solely on TX and RX FIFO load.

Quoted 7 lines

This is racy - TX FIFO may contain master-rx data yet to be processed, which will eventually land into the RX FIFO. This data is not taken into account and the function may request more data than the controller is actually capable of storing.

This patch ensures the driver takes into account the outstanding master-rx data in TX FIFO to prevent RX FIFO overrun.

Can you add something to the changelog to show what the error looks like (a dump from dmesg for example)?

Quoted 12 lines
---
 drivers/i2c/busses/i2c-designware-core.c |   11 ++++++++++-
 drivers/i2c/busses/i2c-designware-core.h |    2 ++
 2 files changed, 12 insertions(+), 1 deletions(-)
drivers/i2c/busses/i2c-designware-core.c +0 -0
index 94fd818..8dbeef1 100644
@@ -426,8 +426,14 @@ i2c_dw_xfer_msg(struct dw_i2c_dev *dev)
 				cmd |= BIT(9);

drivers/i2c/busses/i2c-designware-core.c i2c_dw_xfer_msg(struct dw_i2c_dev *dev) · line 436

Quoted 10 lines
drivers/i2c/busses/i2c-designware-core.c +6 -0
 
 			if (msgs[dev->msg_write_idx].flags & I2C_M_RD) {
+
+				/* avoid rx buffer overrun */
+				if (rx_limit - dev->rx_outstanding <= 0)
+					break;
+
 				dw_writel(dev, cmd | 0x100, DW_IC_DATA_CMD);
 				rx_limit--;
+				dev->rx_outstanding++;

Instead of adding a new variable, is there something preventing a use of DW_IC_STATUS bits RFNE and TFNF?

Quoted 23 lines
drivers/i2c/busses/i2c-designware-core.c +4 -1
 			} else
 				dw_writel(dev, cmd | *buf++, DW_IC_DATA_CMD);
 			tx_limit--; buf_len--;
@@ -480,8 +486,10 @@ i2c_dw_read(struct dw_i2c_dev *dev)
 
 		rx_valid = dw_readl(dev, DW_IC_RXFLR);
 
-		for (; len > 0 && rx_valid > 0; len--, rx_valid--)
+		for (; len > 0 && rx_valid > 0; len--, rx_valid--) {
 			*buf++ = dw_readl(dev, DW_IC_DATA_CMD);
+			dev->rx_outstanding--;
+		}
 
 		if (len > 0) {
 			dev->status |= STATUS_READ_IN_PROGRESS;
@@ -539,6 +547,7 @@ i2c_dw_xfer(struct i2c_adapter *adap, struct i2c_msg msgs[], int num)
 	dev->msg_err = 0;
 	dev->status = STATUS_IDLE;
 	dev->abort_source = 0;
+	dev->rx_outstanding = 0;
 
 	ret = i2c_dw_wait_bus_not_busy(dev);
 	if (ret < 0)
#4 lore raw
Quoted 16 lines · Josef Ahmad
Quoted 15 lines
Quoted 1 line

From a969728248c3b439dc97a69e7dac133b5efa34e7 Mon Sep 17 00:00:00 2001

From: Josef Ahmad <josef.ahmad-VuQAYsv1563Yd54FQh9/[email protected]>
Date: Fri, 19 Apr 2013 17:28:10 +0100
Subject: [PATCH] i2c-designware: fix RX FIFO overrun

i2c_dw_xfer_msg() pushes a number of bytes to transmit/receive to/from the bus into the TX FIFO.
For master-rx transactions, the maximum amount of data that can be received is calculated depending solely on TX and RX FIFO load.

This is racy - TX FIFO may contain master-rx data yet to be processed, which will eventually land into the RX FIFO. This data is not taken into account and the function may request more data than the controller is actually capable of storing.

Quoted 6 lines
Quoted 2 lines

This patch ensures the driver takes into account the outstanding master-rx data in TX FIFO to prevent RX FIFO overrun.

Can you add something to the changelog to show what the error looks like (a dump from dmesg for example)?

The issue is, the data is silently corrupted and not notified to the I2C core driver.
The master-rx transaction returns success and the RX buffer overflow is not reported by the driver, which will read dropped data.

FWIW, I have a simple test application receiving well-known data from a slave in a single transaction, and comparing received to expected data. Here's the outcome of three runs:

[19/03/13 20:30:14] i2c-error : rcv'd message != ref msg (first diff
@byte 33)
[19/03/13 20:30:43] i2c-error : rcv'd message != ref msg (first diff
@byte 108)
[19/03/13 20:31:24] i2c-error : rcv'd message != ref msg (first diff
@byte 133)

Quoted 14 lines
Quoted 14 lines
---
 drivers/i2c/busses/i2c-designware-core.c |   11 ++++++++++-
 drivers/i2c/busses/i2c-designware-core.h |    2 ++
 2 files changed, 12 insertions(+), 1 deletions(-)
drivers/i2c/busses/i2c-designware-core.c +0 -0
index 94fd818..8dbeef1 100644
@@ -426,8 +426,14 @@ i2c_dw_xfer_msg(struct dw_i2c_dev *dev)
 				cmd |= BIT(9);
 
Quoted 13 lines

drivers/i2c/busses/i2c-designware-core.c i2c_dw_xfer_msg(struct dw_i2c_dev *dev) · line 436

Quoted 9 lines
drivers/i2c/busses/i2c-designware-core.c +6 -0
 			if (msgs[dev->msg_write_idx].flags & I2C_M_RD) {
+
+				/* avoid rx buffer overrun */
+				if (rx_limit - dev->rx_outstanding <= 0)
+					break;
+
 				dw_writel(dev, cmd | 0x100, DW_IC_DATA_CMD);
 				rx_limit--;
+				dev->rx_outstanding++;

Instead of adding a new variable, is there something preventing a use of DW_IC_STATUS bits RFNE and TFNF?

DW_IC_STATUS bits won't give information of the type of elements (read or write) that are in the fifos.
What we need here is more specific information, i.e. how many RX elements are currently in TX fifo. The register set doesn't provide this information to my knowledge, so I had to work it out externally with a status variable.

Consider this example with 8-byte fifos (E=empty, R=read, W=write elements):

State of the fifos:

        +-----------------+
TX  ->  | E E E E E W W R |
        +-----------------+
        +-----------------+
RX      | E E R R R R R R | <-
        +-----------------+

Now, say the transaction requires to pump 2 additional R elements into TX fifo. We need to ensure that at this stage only 1 of the 2 R elements is actually put into TX fifo: this way we we won't saturate the RX fifo. Failing to do so exposes a race condition: if we don't read RX quickly enough,
the R element + the new 2 R elements in the TX fifo will land into the RX, resulting in an element being dropped.

Quoted 25 lines
Quoted 24 lines
drivers/i2c/busses/i2c-designware-core.c +4 -1
 			} else
 				dw_writel(dev, cmd | *buf++, DW_IC_DATA_CMD);
 			tx_limit--; buf_len--;
@@ -480,8 +486,10 @@ i2c_dw_read(struct dw_i2c_dev *dev)
 
 		rx_valid = dw_readl(dev, DW_IC_RXFLR);
 
-		for (; len > 0 && rx_valid > 0; len--, rx_valid--)
+		for (; len > 0 && rx_valid > 0; len--, rx_valid--) {
 			*buf++ = dw_readl(dev, DW_IC_DATA_CMD);
+			dev->rx_outstanding--;
+		}
 
 		if (len > 0) {
 			dev->status |= STATUS_READ_IN_PROGRESS;
@@ -539,6 +547,7 @@ i2c_dw_xfer(struct i2c_adapter *adap, struct i2c_msg
 msgs[], int num)
 	dev->msg_err = 0;
 	dev->status = STATUS_IDLE;
 	dev->abort_source = 0;
+	dev->rx_outstanding = 0;
 
 	ret = i2c_dw_wait_bus_not_busy(dev);

if (ret < 0)

maintainer Acked-by
#5 lore raw
Quoted 19 lines · Josef Ahmad
Quoted 19 lines · Josef Ahmad
Quoted 17 lines
Quoted 1 line

From a969728248c3b439dc97a69e7dac133b5efa34e7 Mon Sep 17 00:00:00 2001

From: Josef Ahmad <[email protected]>
Date: Fri, 19 Apr 2013 17:28:10 +0100
Subject: [PATCH] i2c-designware: fix RX FIFO overrun

i2c_dw_xfer_msg() pushes a number of bytes to transmit/receive to/from the bus into the TX FIFO.
For master-rx transactions, the maximum amount of data that can be received is calculated depending solely on TX and RX FIFO load.

This is racy - TX FIFO may contain master-rx data yet to be processed, which will eventually land into the RX FIFO. This data is not taken into account and the function may request more data than the controller is actually capable of storing.

This patch ensures the driver takes into account the outstanding master-rx data in TX FIFO to prevent RX FIFO overrun.

Quoted 8 lines
Quoted 3 lines

Can you add something to the changelog to show what the error looks like (a dump from dmesg for example)?

The issue is, the data is silently corrupted and not notified to the I2C core driver.
The master-rx transaction returns success and the RX buffer overflow is not reported by the driver, which will read dropped data.

OK.

Quoted 55 lines

FWIW, I have a simple test application receiving well-known data from a slave in a single transaction, and comparing received to expected data. Here's the outcome of three runs:

[19/03/13 20:30:14] i2c-error : rcv'd message != ref msg (first diff
@byte 33)
[19/03/13 20:30:43] i2c-error : rcv'd message != ref msg (first diff
@byte 108)
[19/03/13 20:31:24] i2c-error : rcv'd message != ref msg (first diff
@byte 133)

Quoted 14 lines
Quoted 14 lines
---
 drivers/i2c/busses/i2c-designware-core.c |   11 ++++++++++-
 drivers/i2c/busses/i2c-designware-core.h |    2 ++
 2 files changed, 12 insertions(+), 1 deletions(-)
drivers/i2c/busses/i2c-designware-core.c +0 -0
index 94fd818..8dbeef1 100644
@@ -426,8 +426,14 @@ i2c_dw_xfer_msg(struct dw_i2c_dev *dev)
 				cmd |= BIT(9);
 
Quoted 13 lines

drivers/i2c/busses/i2c-designware-core.c i2c_dw_xfer_msg(struct dw_i2c_dev *dev) · line 436

Quoted 9 lines
drivers/i2c/busses/i2c-designware-core.c +6 -0
 			if (msgs[dev->msg_write_idx].flags & I2C_M_RD) {
+
+				/* avoid rx buffer overrun */
+				if (rx_limit - dev->rx_outstanding <= 0)
+					break;
+
 				dw_writel(dev, cmd | 0x100, DW_IC_DATA_CMD);
 				rx_limit--;
+				dev->rx_outstanding++;

Instead of adding a new variable, is there something preventing a use of DW_IC_STATUS bits RFNE and TFNF?

DW_IC_STATUS bits won't give information of the type of elements (read or write) that are in the fifos.
What we need here is more specific information, i.e. how many RX elements are currently in TX fifo. The register set doesn't provide this information to my knowledge, so I had to work it out externally with a status variable.

Consider this example with 8-byte fifos (E=empty, R=read, W=write elements):

State of the fifos:

        +-----------------+
TX  ->  | E E E E E W W R |
        +-----------------+
        +-----------------+
RX      | E E R R R R R R | <-
        +-----------------+
Quoted 7 lines

Now, say the transaction requires to pump 2 additional R elements into TX fifo. We need to ensure that at this stage only 1 of the 2 R elements is actually put into TX fifo: this way we we won't saturate the RX fifo. Failing to do so exposes a race condition: if we don't read RX quickly enough,
the R element + the new 2 R elements in the TX fifo will land into the RX, resulting in an element being dropped.

Thanks for the explanation. Makes sense to me now.

Feel free to add my

author Acked-by Mika Westerberg
#6 lore raw
Quoted 1 line

From a969728248c3b439dc97a69e7dac133b5efa34e7 Mon Sep 17 00:00:00 2001

From: Josef Ahmad <josef.ahmad-VuQAYsv1563Yd54FQh9/[email protected]>
Date: Fri, 19 Apr 2013 17:28:10 +0100
Subject: [PATCH] i2c-designware: fix RX FIFO overrun

i2c_dw_xfer_msg() pushes a number of bytes to transmit/receive to/from the bus into the TX FIFO.
For master-rx transactions, the maximum amount of data that can be received is calculated depending solely on TX and RX FIFO load.

This is racy - TX FIFO may contain master-rx data yet to be processed, which will eventually land into the RX FIFO. This data is not taken into account and the function may request more data than the controller is actually capable of storing.

This patch ensures the driver takes into account the outstanding master-rx data in TX FIFO to prevent RX FIFO overrun.

---
 drivers/i2c/busses/i2c-designware-core.c |   11 ++++++++++-
 drivers/i2c/busses/i2c-designware-core.h |    2 ++
 2 files changed, 12 insertions(+), 1 deletions(-)
drivers/i2c/busses/i2c-designware-core.c +10 -1
index 94fd818..8dbeef1 100644
@@ -426,8 +426,14 @@ i2c_dw_xfer_msg(struct dw_i2c_dev *dev)
 				cmd |= BIT(9);
 
 			if (msgs[dev->msg_write_idx].flags & I2C_M_RD) {
+
+				/* avoid rx buffer overrun */
+				if (rx_limit - dev->rx_outstanding <= 0)
+					break;
+
 				dw_writel(dev, cmd | 0x100, DW_IC_DATA_CMD);
 				rx_limit--;
+				dev->rx_outstanding++;
 			} else
 				dw_writel(dev, cmd | *buf++, DW_IC_DATA_CMD);
 			tx_limit--; buf_len--;
@@ -480,8 +486,10 @@ i2c_dw_read(struct dw_i2c_dev *dev)
 
 		rx_valid = dw_readl(dev, DW_IC_RXFLR);
 
-		for (; len > 0 && rx_valid > 0; len--, rx_valid--)
+		for (; len > 0 && rx_valid > 0; len--, rx_valid--) {
 			*buf++ = dw_readl(dev, DW_IC_DATA_CMD);
+			dev->rx_outstanding--;
+		}
 
 		if (len > 0) {
 			dev->status |= STATUS_READ_IN_PROGRESS;
@@ -539,6 +547,7 @@ i2c_dw_xfer(struct i2c_adapter *adap, struct i2c_msg msgs[], int num)
 	dev->msg_err = 0;
 	dev->status = STATUS_IDLE;
 	dev->abort_source = 0;
+	dev->rx_outstanding = 0;
 
 	ret = i2c_dw_wait_bus_not_busy(dev);
 	if (ret < 0)
drivers/i2c/busses/i2c-designware-core.h +2 -0
index 9c1840e..e761ad1 100644
@@ -60,6 +60,7 @@
  * @adapter: i2c subsystem adapter node
  * @tx_fifo_depth: depth of the hardware tx fifo
  * @rx_fifo_depth: depth of the hardware rx fifo
+ * @rx_outstanding: current master-rx elements in tx fifo
  */
 struct dw_i2c_dev {
 	struct device		*dev;
@@ -88,6 +89,7 @@ struct dw_i2c_dev {
 	u32			master_cfg;
 	unsigned int		tx_fifo_depth;
 	unsigned int		rx_fifo_depth;
+	int			rx_outstanding;
 };
 
 #define ACCESS_SWAP		0x00000001
Signature · 1 line
1.7.0.7
#7 lore raw
Quoted 15 lines · Josef Ahmad

From a969728248c3b439dc97a69e7dac133b5efa34e7 Mon Sep 17 00:00:00 2001
From: Josef Ahmad <josef.ahmad-VuQAYsv1563Yd54FQh9/[email protected]>
Date: Fri, 19 Apr 2013 17:28:10 +0100
Subject: [PATCH] i2c-designware: fix RX FIFO overrun

i2c_dw_xfer_msg() pushes a number of bytes to transmit/receive to/from the bus into the TX FIFO.
For master-rx transactions, the maximum amount of data that can be received is calculated depending solely on TX and RX FIFO load.

This is racy - TX FIFO may contain master-rx data yet to be processed, which will eventually land into the RX FIFO. This data is not taken into account and the function may request more data than the controller is actually capable of storing.

Quoted 5 lines

This patch ensures the driver takes into account the outstanding master-rx data in TX FIFO to prevent RX FIFO overrun.

This driver had a major cleanup meanwhile. Could you rebase your patch on top of my for-next branch? I guess this version should make it into stable?

#8 lore raw
Quoted 19 lines · Josef Ahmad
Quoted 18 lines

From a969728248c3b439dc97a69e7dac133b5efa34e7 Mon Sep 17 00:00:00 2001
From: Josef Ahmad <josef.ahmad-VuQAYsv1563Yd54FQh9/[email protected]>
Date: Fri, 19 Apr 2013 17:28:10 +0100
Subject: [PATCH] i2c-designware: fix RX FIFO overrun

i2c_dw_xfer_msg() pushes a number of bytes to transmit/receive to/from the bus into the TX FIFO.
For master-rx transactions, the maximum amount of data that can be received is calculated depending solely on TX and RX FIFO load.

This is racy - TX FIFO may contain master-rx data yet to be processed, which will eventually land into the RX FIFO. This data is not taken into account and the function may request more data than the controller is actually capable of storing.

This patch ensures the driver takes into account the outstanding master-rx data in TX FIFO to prevent RX FIFO overrun.

Quoted 7 lines
Quoted 2 lines

This driver had a major cleanup meanwhile. Could you rebase your patch on top of my for-next branch? I guess this version should make it into stable?

Hi Wolfram

This patch applies just fine on top of your i2c-embedded/for-next branch.

#9 lore raw
Quoted 7 lines
Quoted 5 lines

This driver had a major cleanup meanwhile. Could you rebase your patch on top of my for-next branch? I guess this version should make it into stable?

This patch applies just fine on top of your i2c-embedded/for-next branch.

i2c-embedded? Ah, this one is outdated. Please check
git://git.kernel.org/pub/scm/linux/kernel/git/wsa/linux.git i2c/for-next.

Thanks,

Wolfram

author Acked-by Mika Westerberg
#10 lore raw
Quoted 1 line

From 8a4773d0c0df6fe2e816ad37fde30a2d90a1ad31 Mon Sep 17 00:00:00 2001

From: Josef Ahmad <josef.ahmad-VuQAYsv1563Yd54FQh9/[email protected]>
Date: Fri, 19 Apr 2013 17:28:10 +0100
Subject: [PATCH] i2c-designware: fix RX FIFO overrun

i2c_dw_xfer_msg() pushes a number of bytes to transmit/receive to/from the bus into the TX FIFO.
For master-rx transactions, the maximum amount of data that can be received is calculated depending solely on TX and RX FIFO load.

This is racy - TX FIFO may contain master-rx data yet to be processed, which will eventually land into the RX FIFO. This data is not taken into account and the function may request more data than the controller is actually capable of storing.

This patch ensures the driver takes into account the outstanding master-rx data in TX FIFO to prevent RX FIFO overrun.

---
 drivers/i2c/busses/i2c-designware-core.c |   11 ++++++++++-
 drivers/i2c/busses/i2c-designware-core.h |    2 ++
 2 files changed, 12 insertions(+), 1 deletions(-)
drivers/i2c/busses/i2c-designware-core.c +10 -1
index 21fbb34..1f06c8e 100644
@@ -448,8 +448,14 @@ i2c_dw_xfer_msg(struct dw_i2c_dev *dev)
 				cmd |= BIT(9);
 
 			if (msgs[dev->msg_write_idx].flags & I2C_M_RD) {
+
+				/* avoid rx buffer overrun */
+				if (rx_limit - dev->rx_outstanding <= 0)
+					break;
+
 				dw_writel(dev, cmd | 0x100, DW_IC_DATA_CMD);
 				rx_limit--;
+				dev->rx_outstanding++;
 			} else
 				dw_writel(dev, cmd | *buf++, DW_IC_DATA_CMD);
 			tx_limit--; buf_len--;
@@ -502,8 +508,10 @@ i2c_dw_read(struct dw_i2c_dev *dev)
 
 		rx_valid = dw_readl(dev, DW_IC_RXFLR);
 
-		for (; len > 0 && rx_valid > 0; len--, rx_valid--)
+		for (; len > 0 && rx_valid > 0; len--, rx_valid--) {
 			*buf++ = dw_readl(dev, DW_IC_DATA_CMD);
+			dev->rx_outstanding--;
+		}
 
 		if (len > 0) {
 			dev->status |= STATUS_READ_IN_PROGRESS;
@@ -561,6 +569,7 @@ i2c_dw_xfer(struct i2c_adapter *adap, struct i2c_msg msgs[], int num)
 	dev->msg_err = 0;
 	dev->status = STATUS_IDLE;
 	dev->abort_source = 0;
+	dev->rx_outstanding = 0;
 
 	ret = i2c_dw_wait_bus_not_busy(dev);
 	if (ret < 0)
drivers/i2c/busses/i2c-designware-core.h +2 -0
index 9c1840e..e761ad1 100644
@@ -60,6 +60,7 @@
  * @adapter: i2c subsystem adapter node
  * @tx_fifo_depth: depth of the hardware tx fifo
  * @rx_fifo_depth: depth of the hardware rx fifo
+ * @rx_outstanding: current master-rx elements in tx fifo
  */
 struct dw_i2c_dev {
 	struct device		*dev;
@@ -88,6 +89,7 @@ struct dw_i2c_dev {
 	u32			master_cfg;
 	unsigned int		tx_fifo_depth;
 	unsigned int		rx_fifo_depth;
+	int			rx_outstanding;
 };
 
 #define ACCESS_SWAP		0x00000001
Signature · 1 line
1.7.0.7
#11 lore raw
Quoted 15 lines · Josef Ahmad

From 8a4773d0c0df6fe2e816ad37fde30a2d90a1ad31 Mon Sep 17 00:00:00 2001
From: Josef Ahmad <josef.ahmad-VuQAYsv1563Yd54FQh9/[email protected]>
Date: Fri, 19 Apr 2013 17:28:10 +0100
Subject: [PATCH] i2c-designware: fix RX FIFO overrun

i2c_dw_xfer_msg() pushes a number of bytes to transmit/receive to/from the bus into the TX FIFO.
For master-rx transactions, the maximum amount of data that can be received is calculated depending solely on TX and RX FIFO load.

This is racy - TX FIFO may contain master-rx data yet to be processed, which will eventually land into the RX FIFO. This data is not taken into account and the function may request more data than the controller is actually capable of storing.

Quoted 5 lines

This patch ensures the driver takes into account the outstanding master-rx data in TX FIFO to prevent RX FIFO overrun.

Still can't apply :( Whitespace problems in the patch context :(

#12 lore raw

Sorry for late reply.
Bad UA configuration. Will resend straight away.

Thanks,

Josef

Quoted 3 lines · Wolfram Sang

Still can't apply :( Whitespace problems in the patch context :(

author Acked-by Mika Westerberg
#13 lore raw
In reply to Wolfram Sang
Quoted 1 line

From 8a4773d0c0df6fe2e816ad37fde30a2d90a1ad31 Mon Sep 17 00:00:00 2001

From: Josef Ahmad <[email protected]>
Date: Fri, 19 Apr 2013 17:28:10 +0100
Subject: [PATCH] i2c-designware: fix RX FIFO overrun

i2c_dw_xfer_msg() pushes a number of bytes to transmit/receive to/from the bus into the TX FIFO.
For master-rx transactions, the maximum amount of data that can be received is calculated depending solely on TX and RX FIFO load.

This is racy - TX FIFO may contain master-rx data yet to be processed, which will eventually land into the RX FIFO. This data is not taken into account and the function may request more data than the controller is actually capable of storing.

This patch ensures the driver takes into account the outstanding master-rx data in TX FIFO to prevent RX FIFO overrun.

---
 drivers/i2c/busses/i2c-designware-core.c |   11 ++++++++++-
 drivers/i2c/busses/i2c-designware-core.h |    2 ++
 2 files changed, 12 insertions(+), 1 deletions(-)
drivers/i2c/busses/i2c-designware-core.c +10 -1
index 21fbb34..1f06c8e 100644
@@ -448,8 +448,14 @@ i2c_dw_xfer_msg(struct dw_i2c_dev *dev)
 				cmd |= BIT(9);
 
 			if (msgs[dev->msg_write_idx].flags & I2C_M_RD) {
+
+				/* avoid rx buffer overrun */
+				if (rx_limit - dev->rx_outstanding <= 0)
+					break;
+
 				dw_writel(dev, cmd | 0x100, DW_IC_DATA_CMD);
 				rx_limit--;
+				dev->rx_outstanding++;
 			} else
 				dw_writel(dev, cmd | *buf++, DW_IC_DATA_CMD);
 			tx_limit--; buf_len--;
@@ -502,8 +508,10 @@ i2c_dw_read(struct dw_i2c_dev *dev)
 
 		rx_valid = dw_readl(dev, DW_IC_RXFLR);
 
-		for (; len > 0 && rx_valid > 0; len--, rx_valid--)
+		for (; len > 0 && rx_valid > 0; len--, rx_valid--) {
 			*buf++ = dw_readl(dev, DW_IC_DATA_CMD);
+			dev->rx_outstanding--;
+		}
 
 		if (len > 0) {
 			dev->status |= STATUS_READ_IN_PROGRESS;
@@ -561,6 +569,7 @@ i2c_dw_xfer(struct i2c_adapter *adap, struct i2c_msg msgs[], int num)
 	dev->msg_err = 0;
 	dev->status = STATUS_IDLE;
 	dev->abort_source = 0;
+	dev->rx_outstanding = 0;
 
 	ret = i2c_dw_wait_bus_not_busy(dev);
 	if (ret < 0)
drivers/i2c/busses/i2c-designware-core.h +2 -0
index 9c1840e..e761ad1 100644
@@ -60,6 +60,7 @@
  * @adapter: i2c subsystem adapter node
  * @tx_fifo_depth: depth of the hardware tx fifo
  * @rx_fifo_depth: depth of the hardware rx fifo
+ * @rx_outstanding: current master-rx elements in tx fifo
  */
 struct dw_i2c_dev {
 	struct device		*dev;
@@ -88,6 +89,7 @@ struct dw_i2c_dev {
 	u32			master_cfg;
 	unsigned int		tx_fifo_depth;
 	unsigned int		rx_fifo_depth;
+	int			rx_outstanding;
 };
 
 #define ACCESS_SWAP		0x00000001
Signature · 1 line
1.7.0.7
Applied
#14 lore raw
Quoted 15 lines · Josef Ahmad

From 8a4773d0c0df6fe2e816ad37fde30a2d90a1ad31 Mon Sep 17 00:00:00 2001
From: Josef Ahmad <[email protected]>
Date: Fri, 19 Apr 2013 17:28:10 +0100
Subject: [PATCH] i2c-designware: fix RX FIFO overrun

i2c_dw_xfer_msg() pushes a number of bytes to transmit/receive to/from the bus into the TX FIFO.
For master-rx transactions, the maximum amount of data that can be received is calculated depending solely on TX and RX FIFO load.

This is racy - TX FIFO may contain master-rx data yet to be processed, which will eventually land into the RX FIFO. This data is not taken into account and the function may request more data than the controller is actually capable of storing.

Quoted 5 lines

This patch ensures the driver takes into account the outstanding master-rx data in TX FIFO to prevent RX FIFO overrun.

Applied to for-current, thanks!

#15 lore raw
Quoted 1 line

Applied to for-current, thanks!

Added stable, too.

#16 lore raw
Quoted 4 lines
Quoted 1 line

Applied to for-current, thanks!

Added stable, too.

Thanks Wolfram.