From patchwork Thu Apr 14 14:04:56 2016 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Jisheng Zhang X-Patchwork-Id: 8837061 Return-Path: X-Original-To: patchwork-linux-arm@patchwork.kernel.org Delivered-To: patchwork-parsemail@patchwork2.web.kernel.org Received: from mail.kernel.org (mail.kernel.org [198.145.29.136]) by patchwork2.web.kernel.org (Postfix) with ESMTP id B6418C0553 for ; Thu, 14 Apr 2016 14:11:18 +0000 (UTC) Received: from mail.kernel.org (localhost [127.0.0.1]) by mail.kernel.org (Postfix) with ESMTP id B2AF720270 for ; Thu, 14 Apr 2016 14:11:17 +0000 (UTC) Received: from bombadil.infradead.org (bombadil.infradead.org [198.137.202.9]) (using TLSv1.2 with cipher AES128-GCM-SHA256 (128/128 bits)) (No client certificate requested) by mail.kernel.org (Postfix) with ESMTPS id A4E882026C for ; Thu, 14 Apr 2016 14:11:16 +0000 (UTC) Received: from localhost ([127.0.0.1] helo=bombadil.infradead.org) by bombadil.infradead.org with esmtp (Exim 4.80.1 #2 (Red Hat Linux)) id 1aqhxi-0002Uq-Cn; Thu, 14 Apr 2016 14:09:38 +0000 Received: from mx0a-0016f401.pphosted.com ([67.231.148.174]) by bombadil.infradead.org with esmtps (Exim 4.80.1 #2 (Red Hat Linux)) id 1aqhxf-0002Qx-Op for linux-arm-kernel@lists.infradead.org; Thu, 14 Apr 2016 14:09:36 +0000 Received: from pps.filterd (m0045849.ppops.net [127.0.0.1]) by mx0a-0016f401.pphosted.com (8.16.0.11/8.16.0.11) with SMTP id u3EE7Hte006603; Thu, 14 Apr 2016 07:08:48 -0700 Received: from sc-exch04.marvell.com ([199.233.58.184]) by mx0a-0016f401.pphosted.com with ESMTP id 226y3kspq2-1 (version=TLSv1.2 cipher=ECDHE-RSA-AES256-SHA384 bits=256 verify=NOT); Thu, 14 Apr 2016 07:08:47 -0700 Received: from SC-EXCH03.marvell.com (10.93.176.83) by SC-EXCH04.marvell.com (10.93.176.84) with Microsoft SMTP Server (TLS) id 15.0.1104.5; Thu, 14 Apr 2016 07:08:46 -0700 Received: from maili.marvell.com (10.93.176.43) by SC-EXCH03.marvell.com (10.93.176.83) with Microsoft SMTP Server id 15.0.1104.5 via Frontend Transport; Thu, 14 Apr 2016 07:08:46 -0700 Received: from xhacker.marvell.com (unknown [10.37.130.135]) by maili.marvell.com (Postfix) with ESMTP id 7C38D3F703F; Thu, 14 Apr 2016 07:08:44 -0700 (PDT) From: Jisheng Zhang To: , , , , , , , , Subject: [PATCH] i2c: designware-platdrv: implement bus recovery Date: Thu, 14 Apr 2016 22:04:56 +0800 Message-ID: <1460642696-1858-1-git-send-email-jszhang@marvell.com> X-Mailer: git-send-email 2.8.0.rc3 MIME-Version: 1.0 X-Proofpoint-Virus-Version: vendor=fsecure engine=2.50.10432:, , definitions=2016-04-14_08:, , signatures=0 X-Proofpoint-Details: rule=outbound_notspam policy=outbound score=0 spamscore=0 suspectscore=0 malwarescore=0 phishscore=0 adultscore=0 bulkscore=0 classifier=spam adjust=0 reason=mlx scancount=1 engine=8.0.1-1603180000 definitions=main-1604140200 X-CRM114-Version: 20100106-BlameMichelson ( TRE 0.8.0 (BSD) ) MR-646709E3 X-CRM114-CacheID: sfid-20160414_070935_834059_A10DEF47 X-CRM114-Status: GOOD ( 15.68 ) X-Spam-Score: -2.6 (--) X-BeenThere: linux-arm-kernel@lists.infradead.org X-Mailman-Version: 2.1.20 Precedence: list List-Id: List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Cc: Jisheng Zhang , devicetree@vger.kernel.org, linux-i2c@vger.kernel.org, linux-arm-kernel@lists.infradead.org, linux-kernel@vger.kernel.org Sender: "linux-arm-kernel" Errors-To: linux-arm-kernel-bounces+patchwork-linux-arm=patchwork.kernel.org@lists.infradead.org X-Spam-Status: No, score=-5.2 required=5.0 tests=BAYES_00, RCVD_IN_DNSWL_MED, RP_MATCHES_RCVD, 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 Implement bus recovery methods for i2c designware so we can recover from situations where SCL/SDA are stuck low. The recovery method is similar as i2c-imx: "config the i2c pinctrl to gpio mode by calling pinctrl sleep set function, and then use GPIO to emulate the i2c protocol to send nine dummy clock to recover i2c device. After recovery, set i2c pinctrl to default group setting. Signed-off-by: Jisheng Zhang --- depends on runtime pm patches http://lists.infradead.org/pipermail/linux-arm-kernel/2016-April/422202.html .../devicetree/bindings/i2c/i2c-designware.txt | 12 ++++++ drivers/i2c/busses/i2c-designware-core.c | 6 ++- drivers/i2c/busses/i2c-designware-core.h | 4 ++ drivers/i2c/busses/i2c-designware-platdrv.c | 50 ++++++++++++++++++++++ 4 files changed, 71 insertions(+), 1 deletion(-) diff --git a/Documentation/devicetree/bindings/i2c/i2c-designware.txt b/Documentation/devicetree/bindings/i2c/i2c-designware.txt index fee26dc..51a55c6 100644 --- a/Documentation/devicetree/bindings/i2c/i2c-designware.txt +++ b/Documentation/devicetree/bindings/i2c/i2c-designware.txt @@ -20,6 +20,13 @@ Optional properties : - i2c-sda-falling-time-ns : should contain the SDA falling time in nanoseconds. This value which is by default 300ns is used to compute the tHIGH period. + - scl-gpios: specify the gpio related to SCL pin + + - sda-gpios: specify the gpio related to SDA pin + + - pinctrl: add extra pinctrl to configure i2c pins to gpio function for i2c + bus recovery, call it "gpio" state + Example : i2c@f0000 { @@ -42,4 +49,9 @@ Example : i2c-sda-hold-time-ns = <300>; i2c-sda-falling-time-ns = <300>; i2c-scl-falling-time-ns = <300>; + pinctrl-names = "default", "gpio"; + pinctrl-0 = <&pinctrl_i2c1_default>; + pinctrl-1 = <&pinctrl_i2c1_gpio>; + scl-gpios = <&porta 26 GPIO_ACTIVE_HIGH>; + sda-gpios = <&porta 27 GPIO_ACTIVE_HIGH>; }; diff --git a/drivers/i2c/busses/i2c-designware-core.c b/drivers/i2c/busses/i2c-designware-core.c index 4255eaa..be40de1 100644 --- a/drivers/i2c/busses/i2c-designware-core.c +++ b/drivers/i2c/busses/i2c-designware-core.c @@ -399,7 +399,10 @@ static int i2c_dw_wait_bus_not_busy(struct dw_i2c_dev *dev) while (dw_readl(dev, DW_IC_STATUS) & DW_IC_STATUS_ACTIVITY) { if (timeout <= 0) { dev_warn(dev->dev, "timeout waiting for bus ready\n"); - return -ETIMEDOUT; + i2c_recover_bus(&dev->adapter); + if (dw_readl(dev, DW_IC_STATUS) + & DW_IC_STATUS_ACTIVITY) + return -ETIMEDOUT; } timeout--; usleep_range(1000, 1100); @@ -665,6 +668,7 @@ i2c_dw_xfer(struct i2c_adapter *adap, struct i2c_msg msgs[], int num) /* wait for tx to complete */ if (!wait_for_completion_timeout(&dev->cmd_complete, HZ)) { dev_err(dev->dev, "controller timed out\n"); + i2c_recover_bus(&dev->adapter); /* i2c_dw_init implicitly disables the adapter */ i2c_dw_init(dev); ret = -ETIMEDOUT; diff --git a/drivers/i2c/busses/i2c-designware-core.h b/drivers/i2c/busses/i2c-designware-core.h index cd409e7..26c84ee 100644 --- a/drivers/i2c/busses/i2c-designware-core.h +++ b/drivers/i2c/busses/i2c-designware-core.h @@ -104,6 +104,10 @@ struct dw_i2c_dev { u16 fs_lcnt; int (*acquire_lock)(struct dw_i2c_dev *dev); void (*release_lock)(struct dw_i2c_dev *dev); + struct i2c_bus_recovery_info rinfo; + struct pinctrl *pinctrl; + struct pinctrl_state *pinctrl_pins_default; + struct pinctrl_state *pinctrl_pins_gpio; bool pm_runtime_disabled; }; diff --git a/drivers/i2c/busses/i2c-designware-platdrv.c b/drivers/i2c/busses/i2c-designware-platdrv.c index 1488cea..62bded9 100644 --- a/drivers/i2c/busses/i2c-designware-platdrv.c +++ b/drivers/i2c/busses/i2c-designware-platdrv.c @@ -33,6 +33,7 @@ #include #include #include +#include #include #include #include @@ -148,6 +149,49 @@ static int i2c_dw_plat_prepare_clk(struct dw_i2c_dev *i_dev, bool prepare) return 0; } +static void i2c_dw_plat_prepare_recovery(struct i2c_adapter *adap) +{ + struct dw_i2c_dev *dev = i2c_get_adapdata(adap); + + pinctrl_select_state(dev->pinctrl, dev->pinctrl_pins_gpio); +} + +static void i2c_dw_plat_unprepare_recovery(struct i2c_adapter *adap) +{ + struct dw_i2c_dev *dev = i2c_get_adapdata(adap); + + pinctrl_select_state(dev->pinctrl, dev->pinctrl_pins_default); +} + +static void i2c_dw_plat_init_recovery_info(struct dw_i2c_dev *dev, + struct platform_device *pdev) +{ + struct i2c_bus_recovery_info *rinfo = &dev->rinfo; + + dev->pinctrl_pins_default = pinctrl_lookup_state(dev->pinctrl, + PINCTRL_STATE_DEFAULT); + dev->pinctrl_pins_gpio = pinctrl_lookup_state(dev->pinctrl, + "gpio"); + rinfo->sda_gpio = of_get_named_gpio(pdev->dev.of_node, "sda-gpios", 0); + rinfo->scl_gpio = of_get_named_gpio(pdev->dev.of_node, "scl-gpios", 0); + + if (!gpio_is_valid(rinfo->sda_gpio) || + !gpio_is_valid(rinfo->scl_gpio) || + IS_ERR(dev->pinctrl_pins_default) || + IS_ERR(dev->pinctrl_pins_gpio)) { + dev_dbg(&pdev->dev, "recovery information incomplete\n"); + return; + } + + dev_dbg(&pdev->dev, "using scl-gpio %d and sda-gpio %d for recovery\n", + rinfo->sda_gpio, rinfo->scl_gpio); + + rinfo->prepare_recovery = i2c_dw_plat_prepare_recovery; + rinfo->unprepare_recovery = i2c_dw_plat_unprepare_recovery; + rinfo->recover_bus = i2c_generic_gpio_recovery; + dev->adapter.bus_recovery_info = rinfo; +} + static int dw_i2c_plat_probe(struct platform_device *pdev) { struct dw_i2c_platform_data *pdata = dev_get_platdata(&pdev->dev); @@ -165,6 +209,12 @@ static int dw_i2c_plat_probe(struct platform_device *pdev) if (!dev) return -ENOMEM; + dev->pinctrl = devm_pinctrl_get(&pdev->dev); + if (IS_ERR(dev->pinctrl)) + return PTR_ERR(dev->pinctrl); + + i2c_dw_plat_init_recovery_info(dev, pdev); + mem = platform_get_resource(pdev, IORESOURCE_MEM, 0); dev->base = devm_ioremap_resource(&pdev->dev, mem); if (IS_ERR(dev->base))