From patchwork Wed Jul 15 07:34:27 2009 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Philby John X-Patchwork-Id: 35639 Received: from bear.ext.ti.com (bear.ext.ti.com [192.94.94.41]) by demeter.kernel.org (8.14.2/8.14.2) with ESMTP id n6F7Zxn5022139 for ; Wed, 15 Jul 2009 07:36:00 GMT Received: from dlep36.itg.ti.com ([157.170.170.91]) by bear.ext.ti.com (8.13.7/8.13.7) with ESMTP id n6F7YL8b016331; Wed, 15 Jul 2009 02:34:26 -0500 Received: from linux.omap.com (localhost [127.0.0.1]) by dlep36.itg.ti.com (8.13.8/8.13.8) with ESMTP id n6F7YKH5019629; Wed, 15 Jul 2009 02:34:20 -0500 (CDT) Received: from linux.omap.com (localhost [127.0.0.1]) by linux.omap.com (Postfix) with ESMTP id 6399880627; Wed, 15 Jul 2009 02:34:20 -0500 (CDT) X-Original-To: davinci-linux-open-source@linux.davincidsp.com Delivered-To: davinci-linux-open-source@linux.davincidsp.com Received: from dflp51.itg.ti.com (dflp51.itg.ti.com [128.247.22.94]) by linux.omap.com (Postfix) with ESMTP id A164480626 for ; Wed, 15 Jul 2009 02:34:18 -0500 (CDT) Received: from white.ext.ti.com (localhost [127.0.0.1]) by dflp51.itg.ti.com (8.13.7/8.13.7) with ESMTP id n6F7YIq1005865 for ; Wed, 15 Jul 2009 02:34:18 -0500 (CDT) Received: from mail62-sin-R.bigfish.com (mail-sin.bigfish.com [207.46.51.101]) by white.ext.ti.com (8.13.7/8.13.7) with ESMTP id n6F7YCfG025840 for ; Wed, 15 Jul 2009 02:34:17 -0500 Received: from mail62-sin (localhost.localdomain [127.0.0.1]) by mail62-sin-R.bigfish.com (Postfix) with ESMTP id A7BB64D8126 for ; Wed, 15 Jul 2009 07:34:11 +0000 (UTC) X-SpamScore: 0 X-BigFish: vps0(zzzz1202hzzz2dh6bh62h) X-Spam-TCS-SCL: 1:0 X-MS-Exchange-Organization-Antispam-Report: OrigIP: 63.81.120.155; Service: EHS Received: by mail62-sin (MessageSwitch) id 1247643094935628_444; Wed, 15 Jul 2009 07:31:34 +0000 (UCT) Received: from imap.sh.mvista.com (unknown [63.81.120.155]) by mail62-sin.bigfish.com (Postfix) with ESMTP id 2D22B630053 for ; Wed, 15 Jul 2009 07:31:34 +0000 (UTC) Received: from [10.161.2.200] (unknown [10.161.2.200]) by imap.sh.mvista.com (Postfix) with ESMTP id D5C693ECD; Wed, 15 Jul 2009 00:31:24 -0700 (PDT) From: Philby John To: linux-i2c@vger.kernel.org Organization: MontaVista Date: Wed, 15 Jul 2009 13:04:27 +0530 Message-Id: <1247643267.5981.82.camel@localhost.localdomain> Mime-Version: 1.0 X-Mailer: Evolution 2.24.5 (2.24.5-2.fc10) Cc: khali@linux-fr.org, davinci-linux-open-source@linux.davincidsp.com Subject: [PATCH/RFC 1/1] recover from davinci i2c time out conditions X-BeenThere: davinci-linux-open-source@linux.davincidsp.com X-Mailman-Version: 2.1.4 Precedence: list List-Id: davinci-linux-open-source.linux.davincidsp.com List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Sender: davinci-linux-open-source-bounces@linux.davincidsp.com Errors-To: davinci-linux-open-source-bounces@linux.davincidsp.com >From dbe7e824d576636bb15b82a20fd2557fddc9a8f7 Mon Sep 17 00:00:00 2001 From: Philby John Date: Tue, 14 Jul 2009 21:46:47 +0530 Subject: [PATCH] Reset i2c bus to come out of time out conditions Get out of i2c time out condition by resetting the i2c bus. The kernel must be robust enough to gracefully recover from i2c bus failure without having to reset the machine. This is done by first NACKing the slave and then resetting the i2c bus after a certain timeout. Signed-off-by: Philby John --- drivers/i2c/busses/i2c-davinci.c | 98 +++++++++++++++++++++++++++++++++++-- 1 files changed, 92 insertions(+), 6 deletions(-) diff --git a/drivers/i2c/busses/i2c-davinci.c b/drivers/i2c/busses/i2c-davinci.c index 17f2ee7..4ed1a4c 100755 --- a/drivers/i2c/busses/i2c-davinci.c +++ b/drivers/i2c/busses/i2c-davinci.c @@ -35,14 +35,18 @@ #include #include #include +#include #include #include +#include +#include /* ----- global defines ----------------------------------------------- */ #define DAVINCI_I2C_TIMEOUT (1*HZ) +#define DAVINCI_I2C_MAX_TRIES 2 #define I2C_DAVINCI_INTR_ALL (DAVINCI_I2C_IMR_AAS | \ DAVINCI_I2C_IMR_SCD | \ DAVINCI_I2C_IMR_ARDY | \ @@ -135,6 +139,50 @@ static inline u16 davinci_i2c_read_reg(struct davinci_i2c_dev *i2c_dev, int reg) } /* + * Configure the i2c data pin as a GPIO input and the i2c clock pin as a + * high GPIO output. + */ +static void disable_i2c_pins(void) +{ + unsigned long flags; + + local_irq_save(flags); + if (cpu_is_davinci_dm355()) { + gpio_direction_input(15); + gpio_direction_output(14, 0); + gpio_set_value(14, 1); + davinci_cfg_reg(DM355_I2C_SDA); + davinci_cfg_reg(DM355_I2C_SCL); + } + local_irq_restore(flags); +} + +/* Connect the i2c pins to the i2c controller. */ +static void enable_i2c_pins(void) +{ + unsigned long flags; + + local_irq_save(flags); + if (cpu_is_davinci_dm355()) { + davinci_cfg_reg(DM355_I2C_SDA); + davinci_cfg_reg(DM355_I2C_SCL); + } + local_irq_restore(flags); +} + + +/* Generate a pulse on the i2c clock pin. */ +static void pulse_i2c_clock(void) +{ + if (cpu_is_davinci_dm355()) { + gpio_set_value(14, 0); + udelay(20); + gpio_set_value(14, 1); + udelay(20); + } +} + +/* * This functions configures I2C and brings I2C out of reset. * This function is called during I2C init function. This function * also gets called if I2C encounters any errors. @@ -221,14 +269,36 @@ static int i2c_davinci_wait_bus_not_busy(struct davinci_i2c_dev *dev, char allow_sleep) { unsigned long timeout; + u16 i; + static u16 to_cnt = 0; + u32 flag = 0; timeout = jiffies + dev->adapter.timeout; while (davinci_i2c_read_reg(dev, DAVINCI_I2C_STR_REG) - & DAVINCI_I2C_STR_BB) { - if (time_after(jiffies, timeout)) { - dev_warn(dev->dev, - "timeout waiting for bus ready\n"); - return -ETIMEDOUT; + & DAVINCI_I2C_STR_BB) { + + if (to_cnt <= DAVINCI_I2C_MAX_TRIES) { + if (time_after(jiffies, timeout)) { + dev_warn(dev->dev, + "timeout waiting for bus ready\n"); + to_cnt++; + return -ETIMEDOUT; + } + } else if (cpu_is_davinci_dm644x() || cpu_is_davinci_dm355()) { + to_cnt = 0; + dev_err(dev->dev, "initiating i2c bus recovery\n"); + /* Send the NACK to the slave */ + flag = davinci_i2c_read_reg(dev, DAVINCI_I2C_IMR_REG); + flag |= DAVINCI_I2C_MDR_NACK; + /* write the data into mode register */ + davinci_i2c_write_reg(dev, DAVINCI_I2C_MDR_REG, flag); + /* Disable i2c */ + disable_i2c_pins(); + for (i = 0; i < 10; i++) + pulse_i2c_clock(); + /* Re-enable i2c */ + enable_i2c_pins(); + i2c_davinci_init(dev); } if (allow_sleep) schedule_timeout(1); @@ -309,7 +379,23 @@ i2c_davinci_xfer_msg(struct i2c_adapter *adap, struct i2c_msg *msg, int stop) r = wait_for_completion_interruptible_timeout(&dev->cmd_complete, dev->adapter.timeout); if (r == 0) { - dev_err(dev->dev, "controller timed out\n"); + u16 i; + u32 flag = 0; + if (cpu_is_davinci_dm644x() || cpu_is_davinci_dm355()) { + dev_err(dev->dev, "initiating i2c bus recovery\n"); + /* Send the NACK to the slave */ + flag = davinci_i2c_read_reg(dev, DAVINCI_I2C_IMR_REG); + flag |= DAVINCI_I2C_MDR_NACK; + /* write the data into mode register */ + davinci_i2c_write_reg(dev, DAVINCI_I2C_MDR_REG, flag); + /* Disable i2c */ + disable_i2c_pins(); + /* Send high and low on the SCL line */ + for (i = 0; i < 10; i++) + pulse_i2c_clock(); + /* Re-enable i2c */ + enable_i2c_pins(); + } i2c_davinci_init(dev); dev->buf_len = 0; return -ETIMEDOUT;