From patchwork Fri Jan 26 12:09:50 2018 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Andrzej Hajda X-Patchwork-Id: 866274 Return-Path: X-Original-To: incoming@patchwork.ozlabs.org Delivered-To: patchwork-incoming@bilbo.ozlabs.org Authentication-Results: ozlabs.org; spf=none (mailfrom) smtp.mailfrom=vger.kernel.org (client-ip=209.132.180.67; helo=vger.kernel.org; envelope-from=linux-i2c-owner@vger.kernel.org; receiver=) Authentication-Results: ozlabs.org; dkim=pass (1024-bit key; unprotected) header.d=samsung.com header.i=@samsung.com header.b="PKwfDZaU"; dkim-atps=neutral Received: from vger.kernel.org (vger.kernel.org [209.132.180.67]) by ozlabs.org (Postfix) with ESMTP id 3zSd5D6b8Rz9s8J for ; Fri, 26 Jan 2018 23:10:08 +1100 (AEDT) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S1751819AbeAZMKH (ORCPT ); Fri, 26 Jan 2018 07:10:07 -0500 Received: from mailout2.w1.samsung.com ([210.118.77.12]:45099 "EHLO mailout2.w1.samsung.com" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S1751814AbeAZMKF (ORCPT ); Fri, 26 Jan 2018 07:10:05 -0500 Received: from eucas1p2.samsung.com (unknown [182.198.249.207]) by mailout2.w1.samsung.com (KnoxPortal) with ESMTP id 20180126121003euoutp02dc3446bf6ba1f394b5ef247d54fc55d8~NWuhdVKL_2410924109euoutp02U; Fri, 26 Jan 2018 12:10:03 +0000 (GMT) DKIM-Filter: OpenDKIM Filter v2.11.0 mailout2.w1.samsung.com 20180126121003euoutp02dc3446bf6ba1f394b5ef247d54fc55d8~NWuhdVKL_2410924109euoutp02U DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=samsung.com; s=mail20170921; t=1516968603; bh=Q3wJQ3t7s14YaFav+4egV2XuUE2tLu5XNaz9BT2o0mA=; h=From:To:Cc:Subject:Date:References:From; b=PKwfDZaUzpZ/YINoLSTHeXaUm+27QH/LE/Wsorm/VcjSI+vsw8J+CyXkVVk6r8CTL ta9mbOyEUamuX5E3a3n8dwbHDlXDnTJwdIn6M63ZZk+SYrQaGhXzzLyehXIxtZeAoV pzZS2vdwwhpR0AgVOHUhDceKCTOczEP2skxwmpJk= Received: from eusmges5.samsung.com (unknown [203.254.199.245]) by eucas1p1.samsung.com (KnoxPortal) with ESMTP id 20180126121002eucas1p15b15a6cdef2311b5456053b2df804a3a~NWuggqARy2033420334eucas1p1s; Fri, 26 Jan 2018 12:10:02 +0000 (GMT) Received: from eucas1p1.samsung.com ( [182.198.249.206]) by eusmges5.samsung.com (EUCPMTA) with SMTP id 0A.EA.12743.A9A1B6A5; Fri, 26 Jan 2018 12:10:02 +0000 (GMT) Received: from eusmgms1.samsung.com (unknown [182.198.249.179]) by eucas1p2.samsung.com (KnoxPortal) with ESMTP id 20180126121001eucas1p2931738af133ad3e5bf1cd824058ba8d0~NWufZWMLo1058010580eucas1p2T; Fri, 26 Jan 2018 12:10:01 +0000 (GMT) X-AuditID: cbfec7f5-f79d06d0000031c7-33-5a6b1a9a13e3 Received: from eusync4.samsung.com ( [203.254.199.214]) by eusmgms1.samsung.com (EUCPMTA) with SMTP id 0B.08.18832.99A1B6A5; Fri, 26 Jan 2018 12:10:01 +0000 (GMT) Received: from AMDC2768.DIGITAL.local ([106.120.43.17]) by eusync4.samsung.com (Oracle Communications Messaging Server 7.0.5.31.0 64bit (built May 5 2014)) with ESMTPA id <0P3500HATWGOSA80@eusync4.samsung.com>; Fri, 26 Jan 2018 12:10:01 +0000 (GMT) From: Andrzej Hajda To: Wolfram Sang , Andi Shyti Cc: Andrzej Hajda , Bartlomiej Zolnierkiewicz , Marek Szyprowski , linux-i2c@vger.kernel.org (open list:I2C SUBSYSTEM), linux-samsung-soc@vger.kernel.org (moderated list:ARM/SAMSUNG EXYNOS ARM ARCHITECTURES) Subject: [PATCH] i2c: exynos5: rework HSI2C_MASTER_ST_LOSE state handling Date: Fri, 26 Jan 2018 13:09:50 +0100 Message-id: <20180126120950.9934-1-a.hajda@samsung.com> X-Mailer: git-send-email 2.15.1 X-Brightmail-Tracker: H4sIAAAAAAAAA+NgFlrBIsWRmVeSWpSXmKPExsWy7djPc7qzpLKjDF5MZLa4te4cq8X2I89Y LTbOWM9q0fH3C6PFjPP7mCzWHrnLbrHyxCxmB3aPvi2rGD1OnnrC4vF5k1wAcxSXTUpqTmZZ apG+XQJXxq5td9kLnihWnJ9wjb2B8ap0FyMnh4SAicSl9z2MELaYxIV769lAbCGBpYwS148Z QtifGSU+fSiAqZ93fDNLFyMXUHwZo8SB00vZIJz/jBIXVnSDTWIT0JT4u/km2CQRAU+JW59b wDqYBRYwSby43QOWEAZKzJr+lwnEZhFQlTgwvQGsmVfAXGLL6jlQJ8lLTNjSBNYsIfCSVeJu Ww8rRMJFon/aFagiYYlXx7ewQ9gyEp0dB5kgGrqB7u4/wQ7hTGGU+PdhBjNElbXE4eMXwSYx C/BJTNo2HSjOARTnlehoE4Io8ZDo+nIIqtxR4uDe1dCAiZW4c3MPywRGqQWMDKsYRVJLi3PT U4tN9YoTc4tL89L1kvNzNzEC4+/0v+NfdzAuPWZ1iFGAg1GJhzeDPytKiDWxrLgy9xCjBAez kgivGWd2lBBvSmJlVWpRfnxRaU5q8SFGaQ4WJXFe26i2SCGB9MSS1OzU1ILUIpgsEwenVAMj Z7Hj9nss27LUhftkL/y7XSYncTJCYHNRhIhwa1jpjef+SkVTHsU9n/r0222GrddjGa/pdZ7/ y6rCzX1aeOPfOJfHPmGea2cvZE6/Lu+faGP/v/tJ/4rDeyxe1E4+k1/81PmFS8pupohIJ+s0 t8oqrS1lBjHlUWlzAyzPfpO5IPhgu4jFqTIlluKMREMt5qLiRAC06QTTuwIAAA== X-Brightmail-Tracker: H4sIAAAAAAAAA+NgFvrEJMWRmVeSWpSXmKPExsVy+t/xa7ozpbKjDI4uFLK4te4cq8X2I89Y LTbOWM9q0fH3C6PFjPP7mCzWHrnLbrHyxCxmB3aPvi2rGD1OnnrC4vF5k1wAcxSXTUpqTmZZ apG+XQJXxq5td9kLnihWnJ9wjb2B8ap0FyMnh4SAicS845tZIGwxiQv31rN1MXJxCAksYZSY +/YuI4TTyCTROfcIWBWbgKbE38032UBsEQFPiVufW1hAipgFFjFJnO3ayw6SEAZKzJr+lwnE ZhFQlTgwvYERxOYVMJfYsnoOI8Q6eYkJW5pYJjByL2BkWMUoklpanJueW2yoV5yYW1yal66X nJ+7iREYEtuO/dy8g/HSxuBDjAIcjEo8vAb3MqOEWBPLiitzDzFKcDArifCacWZHCfGmJFZW pRblxxeV5qQWH2KU5mBREuft3bM6UkggPbEkNTs1tSC1CCbLxMEp1cCYU+y/fELyvMKlnTW3 Pl9c+upmqsbKsIP52yZxnOb9f6ZWz2iXp6jes+Z/azs9c8R/vjmzcZ/eHKPXK2aYTomJM7eO fzi9MvGikoOe/qxCsY0XissDyllkqhrcMitZrzUpXsi18d+a1yzk6CG84OaTZbHa/XGfC2Yv /9255qeJm8N2uZ8PeJqUWIozEg21mIuKEwH1TOv7BQIAAA== X-CMS-MailID: 20180126121001eucas1p2931738af133ad3e5bf1cd824058ba8d0 X-Msg-Generator: CA CMS-TYPE: 201P X-CMS-RootMailID: 20180126121001eucas1p2931738af133ad3e5bf1cd824058ba8d0 X-RootMTR: 20180126121001eucas1p2931738af133ad3e5bf1cd824058ba8d0 References: Sender: linux-i2c-owner@vger.kernel.org Precedence: bulk List-ID: X-Mailing-List: linux-i2c@vger.kernel.org HSI2C_MASTER_ST_LOSE state is not documented properly, extensive tests show that hardware is usually able to recover from this state without interrupting the transfer. Moreover documentation says that such state can be caused by slave clock stretching, and should not be treated as an error during transaction. The only place it indicates an error is just before starting transaction. In such case bus recovery procedure should be performed - master should pulse SCL line nine times and then send STOP condition, it can be repeated until SDA goes high. The procedure can be performed using manual commands HSI2C_CMD_READ_DATA and HSI2C_CMD_SEND_STOP. Signed-off-by: Andrzej Hajda Reviewed-by: Andi Shyti Tested-by: Andi Shyti --- Hi Wolfram, This patch integrates and invalidates my previous patches [1][2]. I hope inline comments are sufficient. [1]: i2c: exynos5: implement bus recovery functionality [2]: i2c: exynos5: do not check TRANS_STATUS in case of Exynos7 variant Regards Andrzej drivers/i2c/busses/i2c-exynos5.c | 67 ++++++++++++++++++++++++++++++++++++---- 1 file changed, 61 insertions(+), 6 deletions(-) diff --git a/drivers/i2c/busses/i2c-exynos5.c b/drivers/i2c/busses/i2c-exynos5.c index b02428498f6d..8dcc36835a1a 100644 --- a/drivers/i2c/busses/i2c-exynos5.c +++ b/drivers/i2c/busses/i2c-exynos5.c @@ -128,6 +128,10 @@ #define HSI2C_TIMEOUT_EN (1u << 31) #define HSI2C_TIMEOUT_MASK 0xff +/* I2C_MANUAL_CMD register bits */ +#define HSI2C_CMD_READ_DATA (1u << 4) +#define HSI2C_CMD_SEND_STOP (1u << 2) + /* I2C_TRANS_STATUS register bits */ #define HSI2C_MASTER_BUSY (1u << 17) #define HSI2C_SLAVE_BUSY (1u << 16) @@ -441,12 +445,6 @@ static irqreturn_t exynos5_i2c_irq(int irqno, void *dev_id) i2c->state = -ETIMEDOUT; goto stop; } - - trans_status = readl(i2c->regs + HSI2C_TRANS_STATUS); - if ((trans_status & HSI2C_MASTER_ST_MASK) == HSI2C_MASTER_ST_LOSE) { - i2c->state = -EAGAIN; - goto stop; - } } else if (int_status & HSI2C_INT_I2C) { trans_status = readl(i2c->regs + HSI2C_TRANS_STATUS); if (trans_status & HSI2C_NO_DEV_ACK) { @@ -544,6 +542,61 @@ static int exynos5_i2c_wait_bus_idle(struct exynos5_i2c *i2c) return -EBUSY; } +static void exynos5_i2c_bus_recover(struct exynos5_i2c *i2c) +{ + u32 val; + + dev_dbg(i2c->dev, "%s: start\n", __func__); + + val = readl(i2c->regs + HSI2C_CTL) | HSI2C_RXCHON; + writel(val, i2c->regs + HSI2C_CTL); + val = readl(i2c->regs + HSI2C_CONF) & ~HSI2C_AUTO_MODE; + writel(val, i2c->regs + HSI2C_CONF); + + /* + * Specification says master should send nine clock pulses. It can be + * emulated by sending manual read command (nine pulses for read eight + * bits + one pulse for NACK). + */ + writel(HSI2C_CMD_READ_DATA, i2c->regs + HSI2C_MANUAL_CMD); + exynos5_i2c_wait_bus_idle(i2c); + writel(HSI2C_CMD_SEND_STOP, i2c->regs + HSI2C_MANUAL_CMD); + exynos5_i2c_wait_bus_idle(i2c); + + val = readl(i2c->regs + HSI2C_CTL) & ~HSI2C_RXCHON; + writel(val, i2c->regs + HSI2C_CTL); + val = readl(i2c->regs + HSI2C_CONF) | HSI2C_AUTO_MODE; + writel(val, i2c->regs + HSI2C_CONF); + + dev_dbg(i2c->dev, "%s: end\n", __func__); +} + +static void exynos5_i2c_bus_check(struct exynos5_i2c *i2c) +{ + unsigned long timeout; + + if (i2c->variant->hw != HSI2C_EXYNOS7) + return; + + /* + * HSI2C_MASTER_ST_LOSE state in EXYNOS7 variant before transaction + * indicates that bus is stuck (SDA is low). In such case bus recovery + * can be performed. + */ + timeout = jiffies + msecs_to_jiffies(100); + for (;;) { + u32 st = readl(i2c->regs + HSI2C_TRANS_STATUS); + + if ((st & HSI2C_MASTER_ST_MASK) != HSI2C_MASTER_ST_LOSE) + return; + + if (time_is_before_jiffies(timeout)) + return; + + exynos5_i2c_bus_recover(i2c); + } +} + /* * exynos5_i2c_message_start: Configures the bus and starts the xfer * i2c: struct exynos5_i2c pointer for the current bus @@ -598,6 +651,8 @@ static void exynos5_i2c_message_start(struct exynos5_i2c *i2c, int stop) writel(fifo_ctl, i2c->regs + HSI2C_FIFO_CTL); writel(i2c_ctl, i2c->regs + HSI2C_CTL); + exynos5_i2c_bus_check(i2c); + /* * Enable interrupts before starting the transfer so that we don't * miss any INT_I2C interrupts.