Skip to content

Commit

Permalink
i2c: designware: fix RX FIFO overrun
Browse files Browse the repository at this point in the history
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.

Signed-off-by: Josef Ahmad <josef.ahmad@linux.intel.com>
Acked-by: Mika Westerberg <mika.westerberg@linux.intel.com>
Signed-off-by: Wolfram Sang <wsa@the-dreams.de>
Cc: stable@kernel.org
  • Loading branch information
Josef Ahmad authored and Wolfram Sang committed May 17, 2013
1 parent f722406 commit e6f34ce
Show file tree
Hide file tree
Showing 2 changed files with 12 additions and 1 deletion.
11 changes: 10 additions & 1 deletion drivers/i2c/busses/i2c-designware-core.c
Original file line number Diff line number Diff line change
Expand Up @@ -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--;
Expand Down Expand Up @@ -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;
Expand Down Expand Up @@ -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)
Expand Down
2 changes: 2 additions & 0 deletions drivers/i2c/busses/i2c-designware-core.h
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -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
Expand Down

0 comments on commit e6f34ce

Please sign in to comment.