From patchwork Fri Apr 19 18:05:30 2013 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Josef Ahmad X-Patchwork-Id: 238088 Return-Path: X-Original-To: incoming@patchwork.ozlabs.org Delivered-To: patchwork-incoming@bilbo.ozlabs.org Received: from vger.kernel.org (vger.kernel.org [209.132.180.67]) by ozlabs.org (Postfix) with ESMTP id 5B61D2C021F for ; Sat, 20 Apr 2013 05:03:54 +1000 (EST) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S1753275Ab3DSTDT (ORCPT ); Fri, 19 Apr 2013 15:03:19 -0400 Received: from mga11.intel.com ([192.55.52.93]:2768 "EHLO mga11.intel.com" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S1753246Ab3DSTDQ (ORCPT ); Fri, 19 Apr 2013 15:03:16 -0400 Received: from fmsmga002.fm.intel.com ([10.253.24.26]) by fmsmga102.fm.intel.com with ESMTP; 19 Apr 2013 12:03:15 -0700 X-ExtLoop1: 1 X-IronPort-AV: E=Sophos;i="4.87,510,1363158000"; d="scan'208,223";a="325043314" Received: from localhost.ir.intel.com ([163.33.230.42]) by fmsmga002.fm.intel.com with ESMTP; 19 Apr 2013 12:03:12 -0700 Date: Fri, 19 Apr 2013 19:05:30 +0100 (IST) From: Josef Ahmad X-X-Sender: joey@localhost To: Wolfram Sang , Ben Dooks , Jean Delvare , Stefan Roese , Axel Lin , Mika Westerberg , linux-i2c@vger.kernel.org, linux-kernel@vger.kernel.org cc: Dirk Brandewie Subject: [PATCH] i2c-designware: fix RX FIFO overrun Message-ID: User-Agent: Alpine 2.02 (DEB 1266 2009-07-14) MIME-Version: 1.0 Sender: linux-i2c-owner@vger.kernel.org Precedence: bulk List-ID: X-Mailing-List: linux-i2c@vger.kernel.org From a969728248c3b439dc97a69e7dac133b5efa34e7 Mon Sep 17 00:00:00 2001 From: Josef Ahmad 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. Signed-off-by: Josef Ahmad Acked-by: Mika Westerberg --- drivers/i2c/busses/i2c-designware-core.c | 11 ++++++++++- drivers/i2c/busses/i2c-designware-core.h | 2 ++ 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 94fd818..8dbeef1 100644 --- 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, 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) diff --git a/drivers/i2c/busses/i2c-designware-core.h b/drivers/i2c/busses/i2c-designware-core.h index 9c1840e..e761ad1 100644 --- 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