lkml.org 
[lkml]   [2013]   [May]   [22]   [last100]   RSS Feed
Views: [wrap][no wrap]   [headers]  [forward] 
 
Messages in this thread
    Patch in this message
    /
    From
    Subject[ 05/21] i2c: designware: fix RX FIFO overrun
    Date
    3.9-stable review patch.  If anyone has any objections, please let me know.

    ------------------

    From: Josef Ahmad <josef.ahmad@linux.intel.com>

    commit e6f34cea56f5b95498070eaa9f4aa3ba4a9e4f62 upstream.

    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>
    Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>

    ---
    drivers/i2c/busses/i2c-designware-core.c | 11 ++++++++++-
    drivers/i2c/busses/i2c-designware-core.h | 2 ++
    2 files changed, 12 insertions(+), 1 deletion(-)

    --- a/drivers/i2c/busses/i2c-designware-core.c
    +++ b/drivers/i2c/busses/i2c-designware-core.c
    @@ -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, st
    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)
    --- a/drivers/i2c/busses/i2c-designware-core.h
    +++ b/drivers/i2c/busses/i2c-designware-core.h
    @@ -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



    \
     
     \ /
      Last update: 2013-05-23 01:41    [W:4.255 / U:0.140 seconds]
    ©2003-2020 Jasper Spaans|hosted at Digital Ocean and TransIP|Read the blog|Advertise on this site