From patchwork Fri Feb 28 10:32:05 2014 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Mike Looijmans X-Patchwork-Id: 3739911 Return-Path: X-Original-To: patchwork-davinci@patchwork.kernel.org Delivered-To: patchwork-parsemail@patchwork1.web.kernel.org Received: from mail.kernel.org (mail.kernel.org [198.145.19.201]) by patchwork1.web.kernel.org (Postfix) with ESMTP id E993C9F35F for ; Fri, 28 Feb 2014 10:33:36 +0000 (UTC) Received: from mail.kernel.org (localhost [127.0.0.1]) by mail.kernel.org (Postfix) with ESMTP id DC74020265 for ; Fri, 28 Feb 2014 10:33:35 +0000 (UTC) Received: from devils.ext.ti.com (devils.ext.ti.com [198.47.26.153]) (using TLSv1 with cipher DHE-RSA-AES256-SHA (256/256 bits)) (No client certificate requested) by mail.kernel.org (Postfix) with ESMTPS id 02B1C20179 for ; Fri, 28 Feb 2014 10:33:33 +0000 (UTC) Received: from dlelxv90.itg.ti.com ([172.17.2.17]) by devils.ext.ti.com (8.13.7/8.13.7) with ESMTP id s1SAWGQ5017344; Fri, 28 Feb 2014 04:32:16 -0600 Received: from DFLE73.ent.ti.com (dfle73.ent.ti.com [128.247.5.110]) by dlelxv90.itg.ti.com (8.14.3/8.13.8) with ESMTP id s1SAWEW3026781; Fri, 28 Feb 2014 04:32:15 -0600 Received: from dlep33.itg.ti.com (157.170.170.75) by DFLE73.ent.ti.com (128.247.5.110) with Microsoft SMTP Server id 14.3.174.1; Fri, 28 Feb 2014 04:32:15 -0600 Received: from linux.omap.com (dlelxs01.itg.ti.com [157.170.227.31]) by dlep33.itg.ti.com (8.14.3/8.13.8) with ESMTP id s1SAWDFl018326; Fri, 28 Feb 2014 04:32:13 -0600 Received: from linux.omap.com (localhost [127.0.0.1]) by linux.omap.com (Postfix) with ESMTP id C33E780627; Fri, 28 Feb 2014 04:32:13 -0600 (CST) X-Original-To: davinci-linux-open-source@linux.davincidsp.com Delivered-To: davinci-linux-open-source@linux.davincidsp.com Received: from dflxv17.itg.ti.com (dflxv17.itg.ti.com [128.247.5.93]) by linux.omap.com (Postfix) with ESMTP id 8CD5080626 for ; Fri, 28 Feb 2014 04:32:12 -0600 (CST) Received: from medina.ext.ti.com (medina.ext.ti.com [192.91.81.31]) by dflxv17.itg.ti.com (8.14.3/8.13.8) with ESMTP id s1SAWCnx025950 for ; Fri, 28 Feb 2014 04:32:12 -0600 Received: from mail6.bemta8.messagelabs.com (mail6.bemta8.messagelabs.com [216.82.243.55]) by medina.ext.ti.com (8.13.7/8.13.7) with ESMTP id s1SAWBaq027134 for ; Fri, 28 Feb 2014 04:32:12 -0600 Received: from [216.82.241.195:37185] by server-8.bemta-8.messagelabs.com id 37/22-18877-BA560135; Fri, 28 Feb 2014 10:32:11 +0000 X-Env-Sender: mike.looijmans@topic.nl X-Msg-Ref: server-6.tower-119.messagelabs.com!1393583530!22989135!1 X-Originating-IP: [194.134.41.34] X-SpamReason: No, hits=0.0 required=7.0 tests=sa_preprocessor: VHJ1c3RlZCBJUDogMTk0LjEzNC40MS4zNCA9PiAxNjQ4ODk=\n X-StarScan-Received: X-StarScan-Version: 6.9.16; banners=-,-,- X-VirusChecked: Checked Received: (qmail 26535 invoked from network); 28 Feb 2014 10:32:11 -0000 Received: from smtp04.online.nl (HELO smtp04.online.nl) (194.134.41.34) by server-6.tower-119.messagelabs.com with SMTP; 28 Feb 2014 10:32:11 -0000 Received: from smtp04.online.nl (localhost [127.0.0.1]) by smtp04.online.nl (Postfix) with ESMTP id 5EAF4E538; Fri, 28 Feb 2014 11:32:09 +0100 (CET) Received: from localhost.localdomain (s55969068.adsl.online.nl [85.150.144.104]) by smtp04.online.nl (Postfix) with ESMTP; Fri, 28 Feb 2014 11:32:09 +0100 (CET) From: To: , , Subject: [PATCH] i2c-davinci: Implement a bus recovery that actually works Date: Fri, 28 Feb 2014 11:32:05 +0100 Message-ID: <1393583525-9555-1-git-send-email-mike.looijmans@topic.nl> X-Mailer: git-send-email 1.7.9.5 X-Online-Scanned: by Cloudmark authority (on smtp04.online.nl) CC: , Mike Looijmans , , X-BeenThere: davinci-linux-open-source@linux.davincidsp.com X-Mailman-Version: 2.1.12 Precedence: list List-Id: List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , MIME-Version: 1.0 Sender: Errors-To: davinci-linux-open-source-bounces@linux.davincidsp.com X-Spam-Status: No, score=-6.9 required=5.0 tests=BAYES_00, RCVD_IN_DNSWL_HI, UNPARSEABLE_RELAY autolearn=unavailable version=3.3.1 X-Spam-Checker-Version: SpamAssassin 3.3.1 (2010-03-16) on mail.kernel.org X-Virus-Scanned: ClamAV using ClamSMTP From: Mike Looijmans Having a board where the I2C bus locks up occasionally made it clear that the bus recovery in the i2c-davinci driver will only work on some boards, because on regular boards, this will only toggle GPIO lines that aren't muxed to the actual pins. The I2C controller has the built-in capability to bit-bang its lines. Use this to implement a generic recovery routine that puts the controller in GPIO mode and pulse the clk lines until both SDA and SCL return to a high state. Because the controller must be held in reset while doing so, the recovery routine must re-init the controller. Since this was already being done after each call to i2c_recover_bus, move that call into the recovery routine as well. Tested on a custom board with OMAP-L138, and after this change, the board can recover from chips keeping SDA low. Note: This is an adapted port from 2.6.37 code, and was only tested with that kernel. --- drivers/i2c/busses/i2c-davinci.c | 83 ++++++++++++++++++++------------------ 1 file changed, 44 insertions(+), 39 deletions(-) diff --git a/drivers/i2c/busses/i2c-davinci.c b/drivers/i2c/busses/i2c-davinci.c index af0b583..1ee04c4 100644 --- a/drivers/i2c/busses/i2c-davinci.c +++ b/drivers/i2c/busses/i2c-davinci.c @@ -64,6 +64,10 @@ #define DAVINCI_I2C_IVR_REG 0x28 #define DAVINCI_I2C_EMDR_REG 0x2c #define DAVINCI_I2C_PSC_REG 0x30 +#define DAVINCI_I2C_FUNC_REG 0x48 +#define DAVINCI_I2C_DIR_REG 0x4c +#define DAVINCI_I2C_DIN_REG 0x50 +#define DAVINCI_I2C_DOUT_REG 0x54 #define DAVINCI_I2C_IVR_AAS 0x07 #define DAVINCI_I2C_IVR_SCD 0x06 @@ -133,43 +137,6 @@ static inline u16 davinci_i2c_read_reg(struct davinci_i2c_dev *i2c_dev, int reg) return readw_relaxed(i2c_dev->base + reg); } -/* Generate a pulse on the i2c clock pin. */ -static void davinci_i2c_clock_pulse(unsigned int scl_pin) -{ - u16 i; - - if (scl_pin) { - /* Send high and low on the SCL line */ - for (i = 0; i < 9; i++) { - gpio_set_value(scl_pin, 0); - udelay(20); - gpio_set_value(scl_pin, 1); - udelay(20); - } - } -} - -/* This routine does i2c bus recovery as specified in the - * i2c protocol Rev. 03 section 3.16 titled "Bus clear" - */ -static void davinci_i2c_recover_bus(struct davinci_i2c_dev *dev) -{ - u32 flag = 0; - struct davinci_i2c_platform_data *pdata = dev->pdata; - - dev_err(dev->dev, "initiating i2c bus recovery\n"); - /* Send NACK to the slave */ - flag = davinci_i2c_read_reg(dev, DAVINCI_I2C_MDR_REG); - flag |= DAVINCI_I2C_MDR_NACK; - /* write the data into mode register */ - davinci_i2c_write_reg(dev, DAVINCI_I2C_MDR_REG, flag); - davinci_i2c_clock_pulse(pdata->scl_pin); - /* Send STOP */ - flag = davinci_i2c_read_reg(dev, DAVINCI_I2C_MDR_REG); - flag |= DAVINCI_I2C_MDR_STP; - davinci_i2c_write_reg(dev, DAVINCI_I2C_MDR_REG, flag); -} - static inline void davinci_i2c_reset_ctrl(struct davinci_i2c_dev *i2c_dev, int val) { @@ -266,6 +233,46 @@ static int i2c_davinci_init(struct davinci_i2c_dev *dev) return 0; } +/* This routine does i2c bus recovery as specified in the + * i2c protocol Rev. 03 section 3.16 titled "Bus clear" + */ +static void i2c_recover_bus(struct davinci_i2c_dev *dev) +{ + u32 flag = 0; + int i; + struct davinci_i2c_platform_data *pdata = dev->dev->platform_data; + flag = davinci_i2c_read_reg(dev, DAVINCI_I2C_DIN_REG); + if ((flag & 0x01) == 0) + dev_err(dev->dev, "SCL stuck at zero.\n"); + dev_err(dev->dev, "initiating i2c bus recovery\n"); + /* Disable interrupts */ + davinci_i2c_write_reg(dev, DAVINCI_I2C_IMR_REG, 0); + /* put I2C into reset */ + davinci_i2c_reset_ctrl(dev, 0); + /* Set GPIO mode */ + davinci_i2c_write_reg(dev, DAVINCI_I2C_FUNC_REG, 0x1); + /* Set scl=1 sda=1 */ + davinci_i2c_write_reg(dev, DAVINCI_I2C_DOUT_REG, 0x03); + /* Set SCL pin as output, SDA as input */ + davinci_i2c_write_reg(dev, DAVINCI_I2C_DIR_REG, 0x1); + /* Send up to 9 clock pulses until SDA is high */ + for (i = 0; i < 9; ++i) { + davinci_i2c_write_reg(dev, DAVINCI_I2C_DOUT_REG, 0x02); /* scl=0 sda=1 */ + udelay(10); + davinci_i2c_write_reg(dev, DAVINCI_I2C_DOUT_REG, 0x03); + udelay(10); + /* Register DIN reads actual state on line */ + flag = davinci_i2c_read_reg(dev, DAVINCI_I2C_DIN_REG); + if ((flag & 0x3) == 0x3) { + dev_info(dev->dev, "SDA and SCL high again (i=%d), resume.\n", i); + break; + } + } + /* Resume operation */ + davinci_i2c_write_reg(dev, DAVINCI_I2C_FUNC_REG, 0x0); + i2c_davinci_init(dev); +} + /* * Waiting for bus not busy */ @@ -287,7 +294,6 @@ static int i2c_davinci_wait_bus_not_busy(struct davinci_i2c_dev *dev, } else { to_cnt = 0; davinci_i2c_recover_bus(dev); - i2c_davinci_init(dev); } } if (allow_sleep) @@ -377,7 +383,6 @@ i2c_davinci_xfer_msg(struct i2c_adapter *adap, struct i2c_msg *msg, int stop) if (r == 0) { dev_err(dev->dev, "controller timed out\n"); davinci_i2c_recover_bus(dev); - i2c_davinci_init(dev); dev->buf_len = 0; return -ETIMEDOUT; }