aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/i2c
diff options
context:
space:
mode:
authorJosef Ahmad <josef.ahmad@linux.intel.com>2013-04-19 12:28:10 -0400
committerWolfram Sang <wsa@the-dreams.de>2013-05-17 04:33:11 -0400
commite6f34cea56f5b95498070eaa9f4aa3ba4a9e4f62 (patch)
treee3b50a4ea7df97b564811788585759b2ae410dec /drivers/i2c
parentf722406faae2d073cc1d01063d1123c35425939e (diff)
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. 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
Diffstat (limited to 'drivers/i2c')
-rw-r--r--drivers/i2c/busses/i2c-designware-core.c11
-rw-r--r--drivers/i2c/busses/i2c-designware-core.h2
2 files changed, 12 insertions, 1 deletions
diff --git a/drivers/i2c/busses/i2c-designware-core.c b/drivers/i2c/busses/i2c-designware-core.c
index 21fbb340ad66..1f06c8e9934c 100644
--- a/drivers/i2c/busses/i2c-designware-core.c
+++ b/drivers/i2c/busses/i2c-designware-core.c
@@ -448,8 +448,14 @@ i2c_dw_xfer_msg(struct dw_i2c_dev *dev)
448 cmd |= BIT(9); 448 cmd |= BIT(9);
449 449
450 if (msgs[dev->msg_write_idx].flags & I2C_M_RD) { 450 if (msgs[dev->msg_write_idx].flags & I2C_M_RD) {
451
452 /* avoid rx buffer overrun */
453 if (rx_limit - dev->rx_outstanding <= 0)
454 break;
455
451 dw_writel(dev, cmd | 0x100, DW_IC_DATA_CMD); 456 dw_writel(dev, cmd | 0x100, DW_IC_DATA_CMD);
452 rx_limit--; 457 rx_limit--;
458 dev->rx_outstanding++;
453 } else 459 } else
454 dw_writel(dev, cmd | *buf++, DW_IC_DATA_CMD); 460 dw_writel(dev, cmd | *buf++, DW_IC_DATA_CMD);
455 tx_limit--; buf_len--; 461 tx_limit--; buf_len--;
@@ -502,8 +508,10 @@ i2c_dw_read(struct dw_i2c_dev *dev)
502 508
503 rx_valid = dw_readl(dev, DW_IC_RXFLR); 509 rx_valid = dw_readl(dev, DW_IC_RXFLR);
504 510
505 for (; len > 0 && rx_valid > 0; len--, rx_valid--) 511 for (; len > 0 && rx_valid > 0; len--, rx_valid--) {
506 *buf++ = dw_readl(dev, DW_IC_DATA_CMD); 512 *buf++ = dw_readl(dev, DW_IC_DATA_CMD);
513 dev->rx_outstanding--;
514 }
507 515
508 if (len > 0) { 516 if (len > 0) {
509 dev->status |= STATUS_READ_IN_PROGRESS; 517 dev->status |= STATUS_READ_IN_PROGRESS;
@@ -561,6 +569,7 @@ i2c_dw_xfer(struct i2c_adapter *adap, struct i2c_msg msgs[], int num)
561 dev->msg_err = 0; 569 dev->msg_err = 0;
562 dev->status = STATUS_IDLE; 570 dev->status = STATUS_IDLE;
563 dev->abort_source = 0; 571 dev->abort_source = 0;
572 dev->rx_outstanding = 0;
564 573
565 ret = i2c_dw_wait_bus_not_busy(dev); 574 ret = i2c_dw_wait_bus_not_busy(dev);
566 if (ret < 0) 575 if (ret < 0)
diff --git a/drivers/i2c/busses/i2c-designware-core.h b/drivers/i2c/busses/i2c-designware-core.h
index 9c1840ee09c7..e761ad18dd61 100644
--- a/drivers/i2c/busses/i2c-designware-core.h
+++ b/drivers/i2c/busses/i2c-designware-core.h
@@ -60,6 +60,7 @@
60 * @adapter: i2c subsystem adapter node 60 * @adapter: i2c subsystem adapter node
61 * @tx_fifo_depth: depth of the hardware tx fifo 61 * @tx_fifo_depth: depth of the hardware tx fifo
62 * @rx_fifo_depth: depth of the hardware rx fifo 62 * @rx_fifo_depth: depth of the hardware rx fifo
63 * @rx_outstanding: current master-rx elements in tx fifo
63 */ 64 */
64struct dw_i2c_dev { 65struct dw_i2c_dev {
65 struct device *dev; 66 struct device *dev;
@@ -88,6 +89,7 @@ struct dw_i2c_dev {
88 u32 master_cfg; 89 u32 master_cfg;
89 unsigned int tx_fifo_depth; 90 unsigned int tx_fifo_depth;
90 unsigned int rx_fifo_depth; 91 unsigned int rx_fifo_depth;
92 int rx_outstanding;
91}; 93};
92 94
93#define ACCESS_SWAP 0x00000001 95#define ACCESS_SWAP 0x00000001