From patchwork Tue Feb 5 08:56:21 2019 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Sowjanya Komatineni X-Patchwork-Id: 1036504 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-tegra-owner@vger.kernel.org; receiver=) Authentication-Results: ozlabs.org; dmarc=pass (p=none dis=none) header.from=nvidia.com Authentication-Results: ozlabs.org; dkim=pass (2048-bit key; unprotected) header.d=nvidia.com header.i=@nvidia.com header.b="PoV1y1RA"; dkim-atps=neutral Received: from vger.kernel.org (vger.kernel.org [209.132.180.67]) by ozlabs.org (Postfix) with ESMTP id 43tz2k3kNhz9s4V for ; Tue, 5 Feb 2019 19:56:30 +1100 (AEDT) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S1728036AbfBEI43 (ORCPT ); Tue, 5 Feb 2019 03:56:29 -0500 Received: from hqemgate14.nvidia.com ([216.228.121.143]:10562 "EHLO hqemgate14.nvidia.com" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S1725898AbfBEI43 (ORCPT ); Tue, 5 Feb 2019 03:56:29 -0500 Received: from hqpgpgate102.nvidia.com (Not Verified[216.228.121.13]) by hqemgate14.nvidia.com (using TLS: TLSv1.2, DES-CBC3-SHA) id ; Tue, 05 Feb 2019 00:56:31 -0800 Received: from hqmail.nvidia.com ([172.20.161.6]) by hqpgpgate102.nvidia.com (PGP Universal service); Tue, 05 Feb 2019 00:56:28 -0800 X-PGP-Universal: processed; by hqpgpgate102.nvidia.com on Tue, 05 Feb 2019 00:56:28 -0800 Received: from HQMAIL112.nvidia.com (172.18.146.18) by HQMAIL103.nvidia.com (172.20.187.11) with Microsoft SMTP Server (TLS) id 15.0.1395.4; Tue, 5 Feb 2019 08:56:27 +0000 Received: from HQMAIL108.nvidia.com (172.18.146.13) by HQMAIL112.nvidia.com (172.18.146.18) with Microsoft SMTP Server (TLS) id 15.0.1395.4; Tue, 5 Feb 2019 08:56:28 +0000 Received: from hqnvemgw01.nvidia.com (172.20.150.20) by HQMAIL108.nvidia.com (172.18.146.13) with Microsoft SMTP Server (TLS) id 15.0.1395.4 via Frontend Transport; Tue, 5 Feb 2019 08:56:28 +0000 Received: from skomatineni-linux.nvidia.com (Not Verified[10.2.175.36]) by hqnvemgw01.nvidia.com with Trustwave SEG (v7, 5, 8, 10121) id ; Tue, 05 Feb 2019 00:56:27 -0800 From: Sowjanya Komatineni To: , , , , CC: , , , , "Sowjanya Komatineni" Subject: [PATCH V11 1/5] i2c: tegra: sort all the include headers alphabetically Date: Tue, 5 Feb 2019 00:56:21 -0800 Message-ID: <1549356985-25726-1-git-send-email-skomatineni@nvidia.com> X-Mailer: git-send-email 2.7.4 X-NVConfidentiality: public MIME-Version: 1.0 DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=nvidia.com; s=n1; t=1549356991; bh=R27/nAX0FDPhNLFmdgIsheCagv0n7NryJjjxsPpw/o0=; h=X-PGP-Universal:From:To:CC:Subject:Date:Message-ID:X-Mailer: X-NVConfidentiality:MIME-Version:Content-Type; b=PoV1y1RALSUUFBTVREyjC+uk1k+L3SknwddGltpywKEui0EobAmWywIo7/ON/BhEe axpB/qNOg+TKYWuIHPn2Ufj+uvn6WvA1FMgBVkQOSkNEGexfIRIYl04Dt9OAalzxxy 6J1g2kfsonHTgby091ymSozDBqQq4LD0tIN2h1WdG8K9v9B/4fTv2fZM7TSUVOK6uF ROear/WDMFwOoYrKiAVRmSLFLbYqPlx/UFIdgtBLbdBTluv2P8fyaqyQxqT748oRhv R5fSX1otK8kOGaiD8v4pzWnQImzvw0gCRRLBIyInIAV8IxbY4NPvRmp+KcDh7zAvyD 8iFgCWvfoIeSw== Sender: linux-tegra-owner@vger.kernel.org Precedence: bulk List-ID: X-Mailing-List: linux-tegra@vger.kernel.org This patch sorts all the include headers alphabetically for the I2C Tegra driver. Acked-by: Thierry Reding Reviewed-by: Dmitry Osipenko Signed-off-by: Sowjanya Komatineni --- [V9/V10/V11] : Rebased to 5.0-rc4 [V3/V4/V5/V7/V8] : Removed unsued headers in tegra I2C [V2] : Added this in V2 to sort the headers in tegra I2C drivers/i2c/busses/i2c-tegra.c | 19 ++++++++----------- 1 file changed, 8 insertions(+), 11 deletions(-) diff --git a/drivers/i2c/busses/i2c-tegra.c b/drivers/i2c/busses/i2c-tegra.c index c77adbbea0c7..79c6aa87499b 100644 --- a/drivers/i2c/busses/i2c-tegra.c +++ b/drivers/i2c/busses/i2c-tegra.c @@ -6,24 +6,21 @@ * Author: Colin Cross */ -#include -#include -#include #include +#include #include #include -#include +#include #include -#include -#include -#include +#include +#include +#include #include -#include +#include #include +#include #include -#include - -#include +#include #define TEGRA_I2C_TIMEOUT (msecs_to_jiffies(1000)) #define BYTES_PER_FIFO_WORD 4 From patchwork Tue Feb 5 08:56:22 2019 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Sowjanya Komatineni X-Patchwork-Id: 1036505 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-tegra-owner@vger.kernel.org; receiver=) Authentication-Results: ozlabs.org; dmarc=pass (p=none dis=none) header.from=nvidia.com Authentication-Results: ozlabs.org; dkim=pass (2048-bit key; unprotected) header.d=nvidia.com header.i=@nvidia.com header.b="OM6moW9z"; dkim-atps=neutral Received: from vger.kernel.org (vger.kernel.org [209.132.180.67]) by ozlabs.org (Postfix) with ESMTP id 43tz2p4GZdz9sN9 for ; Tue, 5 Feb 2019 19:56:34 +1100 (AEDT) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S1728254AbfBEI4d (ORCPT ); Tue, 5 Feb 2019 03:56:33 -0500 Received: from hqemgate16.nvidia.com ([216.228.121.65]:11388 "EHLO hqemgate16.nvidia.com" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S1728244AbfBEI4c (ORCPT ); Tue, 5 Feb 2019 03:56:32 -0500 Received: from hqpgpgate101.nvidia.com (Not Verified[216.228.121.13]) by hqemgate16.nvidia.com (using TLS: TLSv1.2, DES-CBC3-SHA) id ; Tue, 05 Feb 2019 00:56:20 -0800 Received: from hqmail.nvidia.com ([172.20.161.6]) by hqpgpgate101.nvidia.com (PGP Universal service); Tue, 05 Feb 2019 00:56:29 -0800 X-PGP-Universal: processed; by hqpgpgate101.nvidia.com on Tue, 05 Feb 2019 00:56:29 -0800 Received: from HQMAIL101.nvidia.com (172.20.187.10) by HQMAIL108.nvidia.com (172.18.146.13) with Microsoft SMTP Server (TLS) id 15.0.1395.4; Tue, 5 Feb 2019 08:56:29 +0000 Received: from hqnvemgw01.nvidia.com (172.20.150.20) by HQMAIL101.nvidia.com (172.20.187.10) with Microsoft SMTP Server (TLS) id 15.0.1395.4 via Frontend Transport; Tue, 5 Feb 2019 08:56:29 +0000 Received: from skomatineni-linux.nvidia.com (Not Verified[10.2.175.36]) by hqnvemgw01.nvidia.com with Trustwave SEG (v7, 5, 8, 10121) id ; Tue, 05 Feb 2019 00:56:29 -0800 From: Sowjanya Komatineni To: , , , , CC: , , , , Sowjanya Komatineni Subject: [PATCH V11 2/5] i2c: tegra: add bus clear Master Support Date: Tue, 5 Feb 2019 00:56:22 -0800 Message-ID: <1549356985-25726-2-git-send-email-skomatineni@nvidia.com> X-Mailer: git-send-email 2.7.4 In-Reply-To: <1549356985-25726-1-git-send-email-skomatineni@nvidia.com> References: <1549356985-25726-1-git-send-email-skomatineni@nvidia.com> X-NVConfidentiality: public MIME-Version: 1.0 DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=nvidia.com; s=n1; t=1549356980; bh=mmdP3T7admRvAtWZ2ZF7LY2Ms6tEcjPReoRdGjKNbHY=; h=X-PGP-Universal:From:To:CC:Subject:Date:Message-ID:X-Mailer: In-Reply-To:References:X-NVConfidentiality:MIME-Version: Content-Type; b=OM6moW9zt8R0nd5Bl/HmgT2QqLwA/lIZBI9oV8PtR7jdgHttiFRoqk6lcCfCqTPYH POtxSNr2dyQEeI+iS6yj4kzqD58M3MvGazjlpW4WpuA6RUTO4FUPgXyVDREU53ix4S F2ctYy4dq8su+gkNvkZKt4h6YoSLlZJDKK49FA1yjh3fXrhgEw2v/N95N/i4TYQQtF KdXmGhhH764Jq9imt+A1RF8N+1Gyt8wMmXdln4kdDUUPZbYfh8Q3Odk5A3dFRgYHGG ckY43b040af7RytWvFO7x1UBxmD4P0io83HsluhM0lLhrSpaoRUbCZjIokLwz26TdF miqg4UlaxUMvQ== Sender: linux-tegra-owner@vger.kernel.org Precedence: bulk List-ID: X-Mailing-List: linux-tegra@vger.kernel.org Bus clear feature of Tegra I2C controller helps to recover from bus hang when I2C master loses the bus arbitration due to the slave device holding SDA LOW continuously for some unknown reasons. Per I2C specification, the device that held the bus LOW should release it within 9 clock pulses. During bus clear operation, Tegra I2C controller sends 9 clock pulses and terminates the transaction with STOP condition. Upon successful bus clear operation, bus goes to idle state and driver retries the transaction. Acked-by: Thierry Reding Reviewed-by: Dmitry Osipenko Signed-off-by: Sowjanya Komatineni --- [V9/V10/V11] : Rebased to 5.0-rc4 [V5/V6/V7/V8]: Same as V4 [V4]: Added I2C Bus Clear support patch to this version of series. drivers/i2c/busses/i2c-tegra.c | 73 ++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 73 insertions(+) diff --git a/drivers/i2c/busses/i2c-tegra.c b/drivers/i2c/busses/i2c-tegra.c index 79c6aa87499b..118b7023a0f4 100644 --- a/drivers/i2c/busses/i2c-tegra.c +++ b/drivers/i2c/busses/i2c-tegra.c @@ -51,6 +51,7 @@ #define I2C_FIFO_STATUS_RX_SHIFT 0 #define I2C_INT_MASK 0x064 #define I2C_INT_STATUS 0x068 +#define I2C_INT_BUS_CLR_DONE BIT(11) #define I2C_INT_PACKET_XFER_COMPLETE BIT(7) #define I2C_INT_ALL_PACKETS_XFER_COMPLETE BIT(6) #define I2C_INT_TX_FIFO_OVERFLOW BIT(5) @@ -93,6 +94,15 @@ #define I2C_HEADER_MASTER_ADDR_SHIFT 12 #define I2C_HEADER_SLAVE_ADDR_SHIFT 1 +#define I2C_BUS_CLEAR_CNFG 0x084 +#define I2C_BC_SCLK_THRESHOLD 9 +#define I2C_BC_SCLK_THRESHOLD_SHIFT 16 +#define I2C_BC_STOP_COND BIT(2) +#define I2C_BC_TERMINATE BIT(1) +#define I2C_BC_ENABLE BIT(0) +#define I2C_BUS_CLEAR_STATUS 0x088 +#define I2C_BC_STATUS BIT(0) + #define I2C_CONFIG_LOAD 0x08C #define I2C_MSTR_CONFIG_LOAD BIT(0) #define I2C_SLV_CONFIG_LOAD BIT(1) @@ -154,6 +164,8 @@ enum msg_end_type { * be transferred in one go. * @quirks: i2c adapter quirks for limiting write/read transfer size and not * allowing 0 length transfers. + * @supports_bus_clear: Bus Clear support to recover from bus hang during + * SDA stuck low from device for some unknown reasons. */ struct tegra_i2c_hw_feature { bool has_continue_xfer_support; @@ -167,6 +179,7 @@ struct tegra_i2c_hw_feature { bool has_slcg_override_reg; bool has_mst_fifo; const struct i2c_adapter_quirks *quirks; + bool supports_bus_clear; }; /** @@ -640,6 +653,13 @@ static irqreturn_t tegra_i2c_isr(int irq, void *dev_id) goto err; } + /* + * I2C transfer is terminated during the bus clear so skip + * processing the other interrupts. + */ + if (i2c_dev->hw->supports_bus_clear && (status & I2C_INT_BUS_CLR_DONE)) + goto err; + if (i2c_dev->msg_read && (status & I2C_INT_RX_FIFO_DATA_REQ)) { if (i2c_dev->msg_buf_remaining) tegra_i2c_empty_rx_fifo(i2c_dev); @@ -668,6 +688,8 @@ static irqreturn_t tegra_i2c_isr(int irq, void *dev_id) tegra_i2c_mask_irq(i2c_dev, I2C_INT_NO_ACK | I2C_INT_ARBITRATION_LOST | I2C_INT_PACKET_XFER_COMPLETE | I2C_INT_TX_FIFO_DATA_REQ | I2C_INT_RX_FIFO_DATA_REQ); + if (i2c_dev->hw->supports_bus_clear) + tegra_i2c_mask_irq(i2c_dev, I2C_INT_BUS_CLR_DONE); i2c_writel(i2c_dev, status, I2C_INT_STATUS); if (i2c_dev->is_dvc) dvc_writel(i2c_dev, DVC_STATUS_I2C_DONE_INTR, DVC_STATUS); @@ -678,6 +700,43 @@ static irqreturn_t tegra_i2c_isr(int irq, void *dev_id) return IRQ_HANDLED; } +static int tegra_i2c_issue_bus_clear(struct tegra_i2c_dev *i2c_dev) +{ + int err; + unsigned long time_left; + u32 reg; + + if (i2c_dev->hw->supports_bus_clear) { + reinit_completion(&i2c_dev->msg_complete); + reg = (I2C_BC_SCLK_THRESHOLD << I2C_BC_SCLK_THRESHOLD_SHIFT) | + I2C_BC_STOP_COND | I2C_BC_TERMINATE; + i2c_writel(i2c_dev, reg, I2C_BUS_CLEAR_CNFG); + if (i2c_dev->hw->has_config_load_reg) { + err = tegra_i2c_wait_for_config_load(i2c_dev); + if (err) + return err; + } + reg |= I2C_BC_ENABLE; + i2c_writel(i2c_dev, reg, I2C_BUS_CLEAR_CNFG); + tegra_i2c_unmask_irq(i2c_dev, I2C_INT_BUS_CLR_DONE); + + time_left = wait_for_completion_timeout(&i2c_dev->msg_complete, + TEGRA_I2C_TIMEOUT); + if (time_left == 0) { + dev_err(i2c_dev->dev, "timed out for bus clear\n"); + return -ETIMEDOUT; + } + reg = i2c_readl(i2c_dev, I2C_BUS_CLEAR_STATUS); + if (!(reg & I2C_BC_STATUS)) { + dev_err(i2c_dev->dev, + "Un-recovered arbitration lost\n"); + return -EIO; + } + } + + return -EAGAIN; +} + static int tegra_i2c_xfer_msg(struct tegra_i2c_dev *i2c_dev, struct i2c_msg *msg, enum msg_end_type end_state) { @@ -759,6 +818,13 @@ static int tegra_i2c_xfer_msg(struct tegra_i2c_dev *i2c_dev, return 0; tegra_i2c_init(i2c_dev); + /* start recovery upon arbitration loss in single master mode */ + if (i2c_dev->msg_err == I2C_ERR_ARBITRATION_LOST) { + if (!i2c_dev->is_multimaster_mode) + return tegra_i2c_issue_bus_clear(i2c_dev); + return -EAGAIN; + } + if (i2c_dev->msg_err == I2C_ERR_NO_ACK) { if (msg->flags & I2C_M_IGNORE_NAK) return 0; @@ -853,6 +919,7 @@ static const struct tegra_i2c_hw_feature tegra20_i2c_hw = { .has_slcg_override_reg = false, .has_mst_fifo = false, .quirks = &tegra_i2c_quirks, + .supports_bus_clear = false, }; static const struct tegra_i2c_hw_feature tegra30_i2c_hw = { @@ -867,6 +934,7 @@ static const struct tegra_i2c_hw_feature tegra30_i2c_hw = { .has_slcg_override_reg = false, .has_mst_fifo = false, .quirks = &tegra_i2c_quirks, + .supports_bus_clear = false, }; static const struct tegra_i2c_hw_feature tegra114_i2c_hw = { @@ -881,6 +949,7 @@ static const struct tegra_i2c_hw_feature tegra114_i2c_hw = { .has_slcg_override_reg = false, .has_mst_fifo = false, .quirks = &tegra_i2c_quirks, + .supports_bus_clear = true, }; static const struct tegra_i2c_hw_feature tegra124_i2c_hw = { @@ -895,6 +964,7 @@ static const struct tegra_i2c_hw_feature tegra124_i2c_hw = { .has_slcg_override_reg = true, .has_mst_fifo = false, .quirks = &tegra_i2c_quirks, + .supports_bus_clear = true, }; static const struct tegra_i2c_hw_feature tegra210_i2c_hw = { @@ -909,6 +979,7 @@ static const struct tegra_i2c_hw_feature tegra210_i2c_hw = { .has_slcg_override_reg = true, .has_mst_fifo = false, .quirks = &tegra_i2c_quirks, + .supports_bus_clear = true, }; static const struct tegra_i2c_hw_feature tegra194_i2c_hw = { @@ -923,6 +994,7 @@ static const struct tegra_i2c_hw_feature tegra194_i2c_hw = { .has_slcg_override_reg = true, .has_mst_fifo = true, .quirks = &tegra194_i2c_quirks, + .supports_bus_clear = true, }; /* Match table for of_platform binding */ @@ -974,6 +1046,7 @@ static int tegra_i2c_probe(struct platform_device *pdev) i2c_dev->base = base; i2c_dev->div_clk = div_clk; i2c_dev->adapter.algo = &tegra_i2c_algo; + i2c_dev->adapter.retries = 1; i2c_dev->irq = irq; i2c_dev->cont_id = pdev->id; i2c_dev->dev = &pdev->dev; From patchwork Tue Feb 5 08:56:23 2019 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Sowjanya Komatineni X-Patchwork-Id: 1036510 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-tegra-owner@vger.kernel.org; receiver=) Authentication-Results: ozlabs.org; dmarc=pass (p=none dis=none) header.from=nvidia.com Authentication-Results: ozlabs.org; dkim=pass (2048-bit key; unprotected) header.d=nvidia.com header.i=@nvidia.com header.b="ogZzTSoZ"; dkim-atps=neutral Received: from vger.kernel.org (vger.kernel.org [209.132.180.67]) by ozlabs.org (Postfix) with ESMTP id 43tz395j1Dz9sMl for ; Tue, 5 Feb 2019 19:56:53 +1100 (AEDT) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S1728288AbfBEI4m (ORCPT ); Tue, 5 Feb 2019 03:56:42 -0500 Received: from hqemgate15.nvidia.com ([216.228.121.64]:18199 "EHLO hqemgate15.nvidia.com" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S1725898AbfBEI4d (ORCPT ); Tue, 5 Feb 2019 03:56:33 -0500 Received: from hqpgpgate101.nvidia.com (Not Verified[216.228.121.13]) by hqemgate15.nvidia.com (using TLS: TLSv1.2, DES-CBC3-SHA) id ; Tue, 05 Feb 2019 00:56:00 -0800 Received: from hqmail.nvidia.com ([172.20.161.6]) by hqpgpgate101.nvidia.com (PGP Universal service); Tue, 05 Feb 2019 00:56:31 -0800 X-PGP-Universal: processed; by hqpgpgate101.nvidia.com on Tue, 05 Feb 2019 00:56:31 -0800 Received: from HQMAIL110.nvidia.com (172.18.146.15) by HQMAIL107.nvidia.com (172.20.187.13) with Microsoft SMTP Server (TLS) id 15.0.1395.4; Tue, 5 Feb 2019 08:56:31 +0000 Received: from HQMAIL106.nvidia.com (172.18.146.12) by hqmail110.nvidia.com (172.18.146.15) with Microsoft SMTP Server (TLS) id 15.0.1395.4; Tue, 5 Feb 2019 08:56:30 +0000 Received: from hqnvemgw01.nvidia.com (172.20.150.20) by HQMAIL106.nvidia.com (172.18.146.12) with Microsoft SMTP Server (TLS) id 15.0.1395.4 via Frontend Transport; Tue, 5 Feb 2019 08:56:30 +0000 Received: from skomatineni-linux.nvidia.com (Not Verified[10.2.175.36]) by hqnvemgw01.nvidia.com with Trustwave SEG (v7, 5, 8, 10121) id ; Tue, 05 Feb 2019 00:56:30 -0800 From: Sowjanya Komatineni To: , , , , CC: , , , , "Sowjanya Komatineni" Subject: [PATCH V11 3/5] i2c: tegra: Add DMA support Date: Tue, 5 Feb 2019 00:56:23 -0800 Message-ID: <1549356985-25726-3-git-send-email-skomatineni@nvidia.com> X-Mailer: git-send-email 2.7.4 In-Reply-To: <1549356985-25726-1-git-send-email-skomatineni@nvidia.com> References: <1549356985-25726-1-git-send-email-skomatineni@nvidia.com> X-NVConfidentiality: public MIME-Version: 1.0 DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=nvidia.com; s=n1; t=1549356960; bh=CHtYBi2D8qUbLN8Ex4duUQ36wlqIEZqTdFjV67fGnPo=; h=X-PGP-Universal:From:To:CC:Subject:Date:Message-ID:X-Mailer: In-Reply-To:References:X-NVConfidentiality:MIME-Version: Content-Type; b=ogZzTSoZa9AlGxKIHsNxGFQeCiIFipVjkazEhkbN1xBwwAI4696lVU7ox9+krFsIR DM/B45xp+TiJNnCpqnYdgllaxn1QCow5NSVwikzh95b/bKlufwEt9j8J1mrhHrJT4s tEQxpNoYOjAQkcFocVMKO9kVOradqSUHERqHZ3ESz6BaBmVUFoCcutODckaQ5DOhEv y0j9FVWYrEnfUBv0KPBewSN5++7FN2RG0pUQgoA8F1Mi0TuLeBv67UR2NoaL/jgHd4 7CistzJRfoSk3t9n+p/YiaiZFCxRLGBl8eitLgEnc57O4GNV1ahQUnfbS8AWuw6bhJ we1+/XVJ8QERQ== Sender: linux-tegra-owner@vger.kernel.org Precedence: bulk List-ID: X-Mailing-List: linux-tegra@vger.kernel.org This patch adds DMA support for Tegra I2C. Tegra I2C TX and RX FIFO depth is 8 words. PIO mode is used for transfer size of the max FIFO depth and DMA mode is used for transfer size higher than max FIFO depth to save CPU overhead. PIO mode needs full intervention of CPU to fill or empty FIFO's and also need to service multiple data requests interrupt for the same transaction. This adds delay between data bytes of the same transfer when CPU is fully loaded and some slave devices has internal timeout for no bus activity and stops transaction to avoid bus hang. DMA mode is helpful in such cases. DMA mode is also helpful for Large transfers during downloading or uploading FW over I2C to some external devices. Signed-off-by: Sowjanya Komatineni --- [V11] : Replaced deprecated dmaengine_terminate_all with dmaengine_termine_async from non-atomic context and dmaengine_terminate_sync from atomic context. Fixed to program fifo trigger levels properly when transfer falls back to pio mode in case of dma slave configuration failure and other minor fixes. [V10] : APBDMA is replaced with GPCDMA on Tegra186 and Tegra194 designs. Added apbdma hw support flag to now allow Tegra186 and later use APBDMA driver. Added explicit flow control enable for DMA slave config and error handling. Moved releasing DMA resources to seperate function to reuse in multiple places. Updated to register tegra_i2c_driver from module level rather than subsys level. Other minor feedback [V9] : Rebased to 5.0-rc4 Removed dependency of APB DMA in Kconfig and added conditional check in I2C driver to decide on using DMA mode. Changed back the allocation of dma buffer during i2c probe. Fixed FIFO triggers depending on DMA Vs PIO. [V8] : Moved back dma init to i2c probe, removed ALL_PACKETS_XFER_COMPLETE interrupt and using PACKETS_XFER_COMPLETE interrupt only and some other fixes Updated Kconfig for APB_DMA dependency [V7] : Same as V6 [V6] : Updated for proper buffer allocation/freeing, channel release. Updated to use exact xfer size for syncing dma buffer. [V5] : Same as V4 [V4] : Updated to allocate DMA buffer only when DMA mode. Updated to fall back to PIO mode when DMA channel request or buffer allocation fails. [V3] : Updated without additional buffer allocation. [V2] : Updated based on V1 review feedback along with code cleanup for proper implementation of DMA. drivers/i2c/busses/i2c-tegra.c | 412 ++++++++++++++++++++++++++++++++++++----- 1 file changed, 368 insertions(+), 44 deletions(-) diff --git a/drivers/i2c/busses/i2c-tegra.c b/drivers/i2c/busses/i2c-tegra.c index 118b7023a0f4..267a6b3084bf 100644 --- a/drivers/i2c/busses/i2c-tegra.c +++ b/drivers/i2c/busses/i2c-tegra.c @@ -8,6 +8,9 @@ #include #include +#include +#include +#include #include #include #include @@ -44,6 +47,8 @@ #define I2C_FIFO_CONTROL_RX_FLUSH BIT(0) #define I2C_FIFO_CONTROL_TX_TRIG_SHIFT 5 #define I2C_FIFO_CONTROL_RX_TRIG_SHIFT 2 +#define I2C_FIFO_CONTROL_TX_TRIG(x) (((x) - 1) << 5) +#define I2C_FIFO_CONTROL_RX_TRIG(x) (((x) - 1) << 2) #define I2C_FIFO_STATUS 0x060 #define I2C_FIFO_STATUS_TX_MASK 0xF0 #define I2C_FIFO_STATUS_TX_SHIFT 4 @@ -125,6 +130,19 @@ #define I2C_MST_FIFO_STATUS_TX_MASK 0xff0000 #define I2C_MST_FIFO_STATUS_TX_SHIFT 16 +/* Packet header size in bytes */ +#define I2C_PACKET_HEADER_SIZE 12 + +#define DATA_DMA_DIR_TX (1 << 0) +#define DATA_DMA_DIR_RX (1 << 1) + +/* + * Upto I2C_PIO_MODE_MAX_LEN bytes, controller will use PIO mode, + * above this, controller will use DMA to fill FIFO. + * MAX PIO len is 20 bytes excluding packet header. + */ +#define I2C_PIO_MODE_MAX_LEN 32 + /* * msg_end_type: The bus control which need to be send at end of transfer. * @MSG_END_STOP: Send stop pulse at end of transfer. @@ -166,6 +184,7 @@ enum msg_end_type { * allowing 0 length transfers. * @supports_bus_clear: Bus Clear support to recover from bus hang during * SDA stuck low from device for some unknown reasons. + * @has_apb_dma: Support of APBDMA on corresponding Tegra chip. */ struct tegra_i2c_hw_feature { bool has_continue_xfer_support; @@ -180,6 +199,7 @@ struct tegra_i2c_hw_feature { bool has_mst_fifo; const struct i2c_adapter_quirks *quirks; bool supports_bus_clear; + bool has_apb_dma; }; /** @@ -191,6 +211,7 @@ struct tegra_i2c_hw_feature { * @fast_clk: clock reference for fast clock of I2C controller * @rst: reset control for the I2C controller * @base: ioremapped registers cookie + * @base_phys: Physical base address of the I2C controller * @cont_id: I2C controller ID, used for packet header * @irq: IRQ number of transfer complete interrupt * @irq_disabled: used to track whether or not the interrupt is enabled @@ -204,6 +225,13 @@ struct tegra_i2c_hw_feature { * @clk_divisor_non_hs_mode: clock divider for non-high-speed modes * @is_multimaster_mode: track if I2C controller is in multi-master mode * @xfer_lock: lock to serialize transfer submission and processing + * @tx_dma_chan: DMA transmit channel + * @rx_dma_chan: DMA receive channel + * @dma_phys: handle to DMA resources + * @dma_buf: pointer to allocated DMA buffer + * @dma_buf_size: DMA buffer size + * @is_curr_dma_xfer: indicates active DMA transfer + * @dma_complete: DMA completion notifier */ struct tegra_i2c_dev { struct device *dev; @@ -213,6 +241,7 @@ struct tegra_i2c_dev { struct clk *fast_clk; struct reset_control *rst; void __iomem *base; + phys_addr_t base_phys; int cont_id; int irq; bool irq_disabled; @@ -226,6 +255,13 @@ struct tegra_i2c_dev { u16 clk_divisor_non_hs_mode; bool is_multimaster_mode; spinlock_t xfer_lock; + struct dma_chan *tx_dma_chan; + struct dma_chan *rx_dma_chan; + dma_addr_t dma_phys; + u32 *dma_buf; + unsigned int dma_buf_size; + bool is_curr_dma_xfer; + struct completion dma_complete; }; static void dvc_writel(struct tegra_i2c_dev *i2c_dev, u32 val, @@ -294,6 +330,109 @@ static void tegra_i2c_unmask_irq(struct tegra_i2c_dev *i2c_dev, u32 mask) i2c_writel(i2c_dev, int_mask, I2C_INT_MASK); } +static void tegra_i2c_dma_complete(void *args) +{ + struct tegra_i2c_dev *i2c_dev = args; + + complete(&i2c_dev->dma_complete); +} + +static int tegra_i2c_dma_submit(struct tegra_i2c_dev *i2c_dev, size_t len) +{ + struct dma_async_tx_descriptor *dma_desc; + enum dma_transfer_direction dir; + struct dma_chan *chan; + + dev_dbg(i2c_dev->dev, "starting DMA for length: %zu\n", len); + reinit_completion(&i2c_dev->dma_complete); + dir = i2c_dev->msg_read ? DMA_DEV_TO_MEM : DMA_MEM_TO_DEV; + chan = i2c_dev->msg_read ? i2c_dev->rx_dma_chan : i2c_dev->tx_dma_chan; + dma_desc = dmaengine_prep_slave_single(chan, i2c_dev->dma_phys, + len, dir, DMA_PREP_INTERRUPT | + DMA_CTRL_ACK); + if (!dma_desc) { + dev_err(i2c_dev->dev, "failed to get DMA descriptor\n"); + return -EIO; + } + + dma_desc->callback = tegra_i2c_dma_complete; + dma_desc->callback_param = i2c_dev; + dmaengine_submit(dma_desc); + dma_async_issue_pending(chan); + return 0; +} + +static void tegra_i2c_release_dma(struct tegra_i2c_dev *i2c_dev) +{ + if (i2c_dev->dma_buf) { + dma_free_coherent(i2c_dev->dev, i2c_dev->dma_buf_size, + i2c_dev->dma_buf, i2c_dev->dma_phys); + i2c_dev->dma_buf = NULL; + } + + if (i2c_dev->tx_dma_chan) { + dma_release_channel(i2c_dev->tx_dma_chan); + i2c_dev->tx_dma_chan = NULL; + } + + if (i2c_dev->rx_dma_chan) { + dma_release_channel(i2c_dev->rx_dma_chan); + i2c_dev->rx_dma_chan = NULL; + } +} + +static int tegra_i2c_init_dma(struct tegra_i2c_dev *i2c_dev) +{ + struct dma_chan *chan; + u32 *dma_buf; + dma_addr_t dma_phys; + int err = 0; + + if (!IS_ENABLED(CONFIG_TEGRA20_APB_DMA) || + !i2c_dev->hw->has_apb_dma) { + err = -ENODEV; + goto err_out; + } + + chan = dma_request_slave_channel_reason(i2c_dev->dev, "rx"); + if (IS_ERR(chan)) { + err = PTR_ERR(chan); + goto err_out; + } + + i2c_dev->rx_dma_chan = chan; + + chan = dma_request_slave_channel_reason(i2c_dev->dev, "tx"); + if (IS_ERR(chan)) { + err = PTR_ERR(chan); + goto err_out; + } + + i2c_dev->tx_dma_chan = chan; + + dma_buf = dma_alloc_coherent(i2c_dev->dev, i2c_dev->dma_buf_size, + &dma_phys, GFP_KERNEL | __GFP_NOWARN); + if (!dma_buf) { + dev_err(i2c_dev->dev, "failed to allocate the DMA buffer\n"); + err = -ENOMEM; + goto err_out; + } + + i2c_dev->dma_buf = dma_buf; + i2c_dev->dma_phys = dma_phys; + return 0; + +err_out: + tegra_i2c_release_dma(i2c_dev); + if (err != -EPROBE_DEFER) { + dev_err(i2c_dev->dev, "can't use DMA, err: %d, using PIO\n", + err); + return 0; + } + + return err; +} + static int tegra_i2c_flush_fifos(struct tegra_i2c_dev *i2c_dev) { unsigned long timeout = jiffies + HZ; @@ -571,16 +710,6 @@ static int tegra_i2c_init(struct tegra_i2c_dev *i2c_dev) i2c_writel(i2c_dev, 0x00, I2C_SL_ADDR2); } - if (i2c_dev->hw->has_mst_fifo) { - val = I2C_MST_FIFO_CONTROL_TX_TRIG(8) | - I2C_MST_FIFO_CONTROL_RX_TRIG(1); - i2c_writel(i2c_dev, val, I2C_MST_FIFO_CONTROL); - } else { - val = 7 << I2C_FIFO_CONTROL_TX_TRIG_SHIFT | - 0 << I2C_FIFO_CONTROL_RX_TRIG_SHIFT; - i2c_writel(i2c_dev, val, I2C_FIFO_CONTROL); - } - err = tegra_i2c_flush_fifos(i2c_dev); if (err) goto err; @@ -660,25 +789,37 @@ static irqreturn_t tegra_i2c_isr(int irq, void *dev_id) if (i2c_dev->hw->supports_bus_clear && (status & I2C_INT_BUS_CLR_DONE)) goto err; - if (i2c_dev->msg_read && (status & I2C_INT_RX_FIFO_DATA_REQ)) { - if (i2c_dev->msg_buf_remaining) - tegra_i2c_empty_rx_fifo(i2c_dev); - else - BUG(); - } + if (!i2c_dev->is_curr_dma_xfer) { + if (i2c_dev->msg_read && (status & I2C_INT_RX_FIFO_DATA_REQ)) { + if (i2c_dev->msg_buf_remaining) + tegra_i2c_empty_rx_fifo(i2c_dev); + else + BUG(); + } - if (!i2c_dev->msg_read && (status & I2C_INT_TX_FIFO_DATA_REQ)) { - if (i2c_dev->msg_buf_remaining) - tegra_i2c_fill_tx_fifo(i2c_dev); - else - tegra_i2c_mask_irq(i2c_dev, I2C_INT_TX_FIFO_DATA_REQ); + if (!i2c_dev->msg_read && (status & I2C_INT_TX_FIFO_DATA_REQ)) { + if (i2c_dev->msg_buf_remaining) + tegra_i2c_fill_tx_fifo(i2c_dev); + else + tegra_i2c_mask_irq(i2c_dev, + I2C_INT_TX_FIFO_DATA_REQ); + } } i2c_writel(i2c_dev, status, I2C_INT_STATUS); if (i2c_dev->is_dvc) dvc_writel(i2c_dev, DVC_STATUS_I2C_DONE_INTR, DVC_STATUS); + /* + * During message read XFER_COMPLETE interrupt is triggered prior to + * DMA completion and during message write XFER_COMPLETE interrupt is + * triggered after DMA completion. + * PACKETS_XFER_COMPLETE indicates completion of all bytes of transfer. + * so forcing msg_buf_remaining to 0 in DMA mode. + */ if (status & I2C_INT_PACKET_XFER_COMPLETE) { + if (i2c_dev->is_curr_dma_xfer) + i2c_dev->msg_buf_remaining = 0; BUG_ON(i2c_dev->msg_buf_remaining); complete(&i2c_dev->msg_complete); } @@ -694,12 +835,89 @@ static irqreturn_t tegra_i2c_isr(int irq, void *dev_id) if (i2c_dev->is_dvc) dvc_writel(i2c_dev, DVC_STATUS_I2C_DONE_INTR, DVC_STATUS); + if (i2c_dev->is_curr_dma_xfer) { + if (i2c_dev->msg_read) + dmaengine_terminate_async(i2c_dev->rx_dma_chan); + else + dmaengine_terminate_async(i2c_dev->tx_dma_chan); + + complete(&i2c_dev->dma_complete); + } + complete(&i2c_dev->msg_complete); done: spin_unlock(&i2c_dev->xfer_lock); return IRQ_HANDLED; } +static int tegra_i2c_config_fifo_trig(struct tegra_i2c_dev *i2c_dev, + size_t len) +{ + u32 val, reg; + u8 dma_burst = 0; + struct dma_slave_config slv_config = {0}; + struct dma_chan *chan; + int ret = 0; + + if (i2c_dev->hw->has_mst_fifo) + reg = I2C_MST_FIFO_CONTROL; + else + reg = I2C_FIFO_CONTROL; + val = i2c_readl(i2c_dev, reg); + + if (i2c_dev->is_curr_dma_xfer) { + if (len & 0xF) + dma_burst = 1; + else if (len & 0x10) + dma_burst = 4; + else + dma_burst = 8; + + if (i2c_dev->msg_read) { + chan = i2c_dev->rx_dma_chan; + slv_config.src_addr = i2c_dev->base_phys + I2C_RX_FIFO; + slv_config.src_addr_width = DMA_SLAVE_BUSWIDTH_4_BYTES; + slv_config.src_maxburst = dma_burst; + + if (i2c_dev->hw->has_mst_fifo) + val |= I2C_MST_FIFO_CONTROL_RX_TRIG(dma_burst); + else + val |= I2C_FIFO_CONTROL_RX_TRIG(dma_burst); + } else { + chan = i2c_dev->tx_dma_chan; + slv_config.dst_addr = i2c_dev->base_phys + I2C_TX_FIFO; + slv_config.dst_addr_width = DMA_SLAVE_BUSWIDTH_4_BYTES; + slv_config.dst_maxburst = dma_burst; + + if (i2c_dev->hw->has_mst_fifo) + val |= I2C_MST_FIFO_CONTROL_TX_TRIG(dma_burst); + else + val |= I2C_FIFO_CONTROL_TX_TRIG(dma_burst); + } + + slv_config.device_fc = true; + ret = dmaengine_slave_config(chan, &slv_config); + if (ret < 0) { + dev_err(i2c_dev->dev, + "DMA slave config failed, err: %d using PIO\n", + ret); + tegra_i2c_release_dma(i2c_dev); + } else { + goto out; + } + } + + if (i2c_dev->hw->has_mst_fifo) + val = I2C_MST_FIFO_CONTROL_TX_TRIG(8) | + I2C_MST_FIFO_CONTROL_RX_TRIG(1); + else + val = I2C_FIFO_CONTROL_TX_TRIG(8) | + I2C_FIFO_CONTROL_RX_TRIG(1); +out: + i2c_writel(i2c_dev, val, reg); + return ret; +} + static int tegra_i2c_issue_bus_clear(struct tegra_i2c_dev *i2c_dev) { int err; @@ -744,6 +962,10 @@ static int tegra_i2c_xfer_msg(struct tegra_i2c_dev *i2c_dev, u32 int_mask; unsigned long time_left; unsigned long flags; + size_t xfer_size; + u32 *buffer = NULL; + int err = 0; + bool dma = false; tegra_i2c_flush_fifos(i2c_dev); @@ -753,19 +975,59 @@ static int tegra_i2c_xfer_msg(struct tegra_i2c_dev *i2c_dev, i2c_dev->msg_read = (msg->flags & I2C_M_RD); reinit_completion(&i2c_dev->msg_complete); + if (i2c_dev->msg_read) + xfer_size = msg->len; + else + xfer_size = msg->len + I2C_PACKET_HEADER_SIZE; + + xfer_size = ALIGN(xfer_size, BYTES_PER_FIFO_WORD); + dma = (xfer_size > I2C_PIO_MODE_MAX_LEN) && i2c_dev->dma_buf; + i2c_dev->is_curr_dma_xfer = dma; + spin_lock_irqsave(&i2c_dev->xfer_lock, flags); int_mask = I2C_INT_NO_ACK | I2C_INT_ARBITRATION_LOST; tegra_i2c_unmask_irq(i2c_dev, int_mask); + err = tegra_i2c_config_fifo_trig(i2c_dev, xfer_size); + if (err < 0) + i2c_dev->is_curr_dma_xfer = dma = false; + + if (dma) { + if (i2c_dev->msg_read) { + dma_sync_single_for_device(i2c_dev->dev, + i2c_dev->dma_phys, + xfer_size, + DMA_FROM_DEVICE); + err = tegra_i2c_dma_submit(i2c_dev, xfer_size); + if (err < 0) { + dev_err(i2c_dev->dev, + "starting RX DMA failed, err %d\n", + err); + goto unlock; + } + } else { + dma_sync_single_for_cpu(i2c_dev->dev, + i2c_dev->dma_phys, + xfer_size, + DMA_TO_DEVICE); + buffer = i2c_dev->dma_buf; + } + } packet_header = (0 << PACKET_HEADER0_HEADER_SIZE_SHIFT) | PACKET_HEADER0_PROTOCOL_I2C | (i2c_dev->cont_id << PACKET_HEADER0_CONT_ID_SHIFT) | (1 << PACKET_HEADER0_PACKET_ID_SHIFT); - i2c_writel(i2c_dev, packet_header, I2C_TX_FIFO); + if (dma && !i2c_dev->msg_read) + *buffer++ = packet_header; + else + i2c_writel(i2c_dev, packet_header, I2C_TX_FIFO); packet_header = msg->len - 1; - i2c_writel(i2c_dev, packet_header, I2C_TX_FIFO); + if (dma && !i2c_dev->msg_read) + *buffer++ = packet_header; + else + i2c_writel(i2c_dev, packet_header, I2C_TX_FIFO); packet_header = I2C_HEADER_IE_ENABLE; if (end_state == MSG_END_CONTINUE) @@ -782,23 +1044,79 @@ static int tegra_i2c_xfer_msg(struct tegra_i2c_dev *i2c_dev, packet_header |= I2C_HEADER_CONT_ON_NAK; if (msg->flags & I2C_M_RD) packet_header |= I2C_HEADER_READ; - i2c_writel(i2c_dev, packet_header, I2C_TX_FIFO); - - if (!(msg->flags & I2C_M_RD)) - tegra_i2c_fill_tx_fifo(i2c_dev); + if (dma && !i2c_dev->msg_read) + *buffer++ = packet_header; + else + i2c_writel(i2c_dev, packet_header, I2C_TX_FIFO); + + if (!msg->flags & I2C_M_RD) { + if (dma) { + memcpy(buffer, msg->buf, msg->len); + dma_sync_single_for_device(i2c_dev->dev, + i2c_dev->dma_phys, + xfer_size, + DMA_TO_DEVICE); + err = tegra_i2c_dma_submit(i2c_dev, xfer_size); + if (err < 0) { + dev_err(i2c_dev->dev, + "starting TX DMA failed, err %d\n", + err); + goto unlock; + } + } else { + tegra_i2c_fill_tx_fifo(i2c_dev); + } + } if (i2c_dev->hw->has_per_pkt_xfer_complete_irq) int_mask |= I2C_INT_PACKET_XFER_COMPLETE; - if (msg->flags & I2C_M_RD) - int_mask |= I2C_INT_RX_FIFO_DATA_REQ; - else if (i2c_dev->msg_buf_remaining) - int_mask |= I2C_INT_TX_FIFO_DATA_REQ; + + if (!dma) { + if (msg->flags & I2C_M_RD) + int_mask |= I2C_INT_RX_FIFO_DATA_REQ; + else if (i2c_dev->msg_buf_remaining) + int_mask |= I2C_INT_TX_FIFO_DATA_REQ; + } tegra_i2c_unmask_irq(i2c_dev, int_mask); - spin_unlock_irqrestore(&i2c_dev->xfer_lock, flags); dev_dbg(i2c_dev->dev, "unmasked irq: %02x\n", i2c_readl(i2c_dev, I2C_INT_MASK)); +unlock: + spin_unlock_irqrestore(&i2c_dev->xfer_lock, flags); + + if (dma) { + if (err) + return err; + + time_left = wait_for_completion_timeout( + &i2c_dev->dma_complete, + TEGRA_I2C_TIMEOUT); + + if (time_left == 0) { + dev_err(i2c_dev->dev, "DMA transfer timeout\n"); + dmaengine_terminate_sync(i2c_dev->msg_read ? + i2c_dev->rx_dma_chan : + i2c_dev->tx_dma_chan); + tegra_i2c_init(i2c_dev); + return -ETIMEDOUT; + } + + if (i2c_dev->msg_read && (i2c_dev->msg_err == I2C_ERR_NONE)) { + dma_sync_single_for_cpu(i2c_dev->dev, + i2c_dev->dma_phys, + xfer_size, + DMA_FROM_DEVICE); + memcpy(i2c_dev->msg_buf, i2c_dev->dma_buf, + msg->len); + } + if (i2c_dev->msg_err != I2C_ERR_NONE) { + dmaengine_synchronize(i2c_dev->msg_read ? + i2c_dev->rx_dma_chan : + i2c_dev->tx_dma_chan); + } + } + time_left = wait_for_completion_timeout(&i2c_dev->msg_complete, TEGRA_I2C_TIMEOUT); tegra_i2c_mask_irq(i2c_dev, int_mask); @@ -814,6 +1132,7 @@ static int tegra_i2c_xfer_msg(struct tegra_i2c_dev *i2c_dev, time_left, completion_done(&i2c_dev->msg_complete), i2c_dev->msg_err); + i2c_dev->is_curr_dma_xfer = false; if (likely(i2c_dev->msg_err == I2C_ERR_NONE)) return 0; @@ -920,6 +1239,7 @@ static const struct tegra_i2c_hw_feature tegra20_i2c_hw = { .has_mst_fifo = false, .quirks = &tegra_i2c_quirks, .supports_bus_clear = false, + .has_apb_dma = true, }; static const struct tegra_i2c_hw_feature tegra30_i2c_hw = { @@ -935,6 +1255,7 @@ static const struct tegra_i2c_hw_feature tegra30_i2c_hw = { .has_mst_fifo = false, .quirks = &tegra_i2c_quirks, .supports_bus_clear = false, + .has_apb_dma = true, }; static const struct tegra_i2c_hw_feature tegra114_i2c_hw = { @@ -950,6 +1271,7 @@ static const struct tegra_i2c_hw_feature tegra114_i2c_hw = { .has_mst_fifo = false, .quirks = &tegra_i2c_quirks, .supports_bus_clear = true, + .has_apb_dma = true, }; static const struct tegra_i2c_hw_feature tegra124_i2c_hw = { @@ -965,6 +1287,7 @@ static const struct tegra_i2c_hw_feature tegra124_i2c_hw = { .has_mst_fifo = false, .quirks = &tegra_i2c_quirks, .supports_bus_clear = true, + .has_apb_dma = true, }; static const struct tegra_i2c_hw_feature tegra210_i2c_hw = { @@ -980,6 +1303,7 @@ static const struct tegra_i2c_hw_feature tegra210_i2c_hw = { .has_mst_fifo = false, .quirks = &tegra_i2c_quirks, .supports_bus_clear = true, + .has_apb_dma = true, }; static const struct tegra_i2c_hw_feature tegra194_i2c_hw = { @@ -995,6 +1319,7 @@ static const struct tegra_i2c_hw_feature tegra194_i2c_hw = { .has_mst_fifo = true, .quirks = &tegra194_i2c_quirks, .supports_bus_clear = true, + .has_apb_dma = false, }; /* Match table for of_platform binding */ @@ -1017,11 +1342,13 @@ static int tegra_i2c_probe(struct platform_device *pdev) struct clk *div_clk; struct clk *fast_clk; void __iomem *base; + phys_addr_t base_phys; int irq; int ret = 0; int clk_multiplier = I2C_CLK_MULTIPLIER_STD_FAST_MODE; res = platform_get_resource(pdev, IORESOURCE_MEM, 0); + base_phys = res->start; base = devm_ioremap_resource(&pdev->dev, res); if (IS_ERR(base)) return PTR_ERR(base); @@ -1044,6 +1371,7 @@ static int tegra_i2c_probe(struct platform_device *pdev) return -ENOMEM; i2c_dev->base = base; + i2c_dev->base_phys = base_phys; i2c_dev->div_clk = div_clk; i2c_dev->adapter.algo = &tegra_i2c_algo; i2c_dev->adapter.retries = 1; @@ -1063,7 +1391,9 @@ static int tegra_i2c_probe(struct platform_device *pdev) i2c_dev->is_dvc = of_device_is_compatible(pdev->dev.of_node, "nvidia,tegra20-i2c-dvc"); i2c_dev->adapter.quirks = i2c_dev->hw->quirks; + i2c_dev->dma_buf_size = i2c_dev->adapter.quirks->max_write_len; init_completion(&i2c_dev->msg_complete); + init_completion(&i2c_dev->dma_complete); spin_lock_init(&i2c_dev->xfer_lock); if (!i2c_dev->hw->has_single_clk_source) { @@ -1124,6 +1454,10 @@ static int tegra_i2c_probe(struct platform_device *pdev) } } + ret = tegra_i2c_init_dma(i2c_dev); + if (ret < 0) + goto disable_div_clk; + ret = tegra_i2c_init(i2c_dev); if (ret) { dev_err(&pdev->dev, "Failed to initialize i2c controller\n"); @@ -1188,6 +1522,7 @@ static int tegra_i2c_remove(struct platform_device *pdev) if (!i2c_dev->hw->has_single_clk_source) clk_unprepare(i2c_dev->fast_clk); + tegra_i2c_release_dma(i2c_dev); return 0; } @@ -1211,18 +1546,7 @@ static struct platform_driver tegra_i2c_driver = { }, }; -static int __init tegra_i2c_init_driver(void) -{ - return platform_driver_register(&tegra_i2c_driver); -} - -static void __exit tegra_i2c_exit_driver(void) -{ - platform_driver_unregister(&tegra_i2c_driver); -} - -subsys_initcall(tegra_i2c_init_driver); -module_exit(tegra_i2c_exit_driver); +module_platform_driver(tegra_i2c_driver); MODULE_DESCRIPTION("nVidia Tegra2 I2C Bus Controller driver"); MODULE_AUTHOR("Colin Cross"); From patchwork Tue Feb 5 08:56:24 2019 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Sowjanya Komatineni X-Patchwork-Id: 1036508 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-tegra-owner@vger.kernel.org; receiver=) Authentication-Results: ozlabs.org; dmarc=pass (p=none dis=none) header.from=nvidia.com Authentication-Results: ozlabs.org; dkim=pass (2048-bit key; unprotected) header.d=nvidia.com header.i=@nvidia.com header.b="TtNDrV2z"; dkim-atps=neutral Received: from vger.kernel.org (vger.kernel.org [209.132.180.67]) by ozlabs.org (Postfix) with ESMTP id 43tz380yQQz9sMl for ; Tue, 5 Feb 2019 19:56:52 +1100 (AEDT) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S1728352AbfBEI4m (ORCPT ); Tue, 5 Feb 2019 03:56:42 -0500 Received: from hqemgate15.nvidia.com ([216.228.121.64]:18201 "EHLO hqemgate15.nvidia.com" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S1728219AbfBEI4d (ORCPT ); Tue, 5 Feb 2019 03:56:33 -0500 Received: from hqpgpgate101.nvidia.com (Not Verified[216.228.121.13]) by hqemgate15.nvidia.com (using TLS: TLSv1.2, DES-CBC3-SHA) id ; Tue, 05 Feb 2019 00:56:01 -0800 Received: from hqmail.nvidia.com ([172.20.161.6]) by hqpgpgate101.nvidia.com (PGP Universal service); Tue, 05 Feb 2019 00:56:32 -0800 X-PGP-Universal: processed; by hqpgpgate101.nvidia.com on Tue, 05 Feb 2019 00:56:32 -0800 Received: from HQMAIL105.nvidia.com (172.20.187.12) by HQMAIL105.nvidia.com (172.20.187.12) with Microsoft SMTP Server (TLS) id 15.0.1395.4; Tue, 5 Feb 2019 08:56:31 +0000 Received: from hqnvemgw01.nvidia.com (172.20.150.20) by HQMAIL105.nvidia.com (172.20.187.12) with Microsoft SMTP Server (TLS) id 15.0.1395.4 via Frontend Transport; Tue, 5 Feb 2019 08:56:31 +0000 Received: from skomatineni-linux.nvidia.com (Not Verified[10.2.175.36]) by hqnvemgw01.nvidia.com with Trustwave SEG (v7, 5, 8, 10121) id ; Tue, 05 Feb 2019 00:56:31 -0800 From: Sowjanya Komatineni To: , , , , CC: , , , , Sowjanya Komatineni Subject: [PATCH V11 4/5] i2c: tegra: update transfer timeout Date: Tue, 5 Feb 2019 00:56:24 -0800 Message-ID: <1549356985-25726-4-git-send-email-skomatineni@nvidia.com> X-Mailer: git-send-email 2.7.4 In-Reply-To: <1549356985-25726-1-git-send-email-skomatineni@nvidia.com> References: <1549356985-25726-1-git-send-email-skomatineni@nvidia.com> X-NVConfidentiality: public MIME-Version: 1.0 DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=nvidia.com; s=n1; t=1549356961; bh=qlHiwoEfLdbbjVV/nKuGflABEazMsHd/XO0TcQuiT9k=; h=X-PGP-Universal:From:To:CC:Subject:Date:Message-ID:X-Mailer: In-Reply-To:References:X-NVConfidentiality:MIME-Version: Content-Type; b=TtNDrV2znNt8XMG45eqq8fo/Bn1S2IaoBZTSSceeQpv6InZOPjcg1+vFBB4MWbL4M bAcPxZSPAjOMj8pAwVtDcOnBsE2+Kn1+J0JTzNYf/G3txPvnYWF83hrCmBxurZhblr +oIH8sA4C/mHcZMds1shUs+JTYukmlOBop+rVOaRwu/ZL5uQLKmMQnqVAOTnsiUWkz jqNgdNF9Bhu0MA1dBOppYOMVOyy9oTF9J8ZwR8sBgATrFKLEl9o7ibhfECesyUvjET S0qVb8P3xr6u/WB0HyFzn3RdRpTDCy6OkgMbiVMwgo1fuX0uFe8u08U4ZVv08ji93p DDPGcq5IQYILA== Sender: linux-tegra-owner@vger.kernel.org Precedence: bulk List-ID: X-Mailing-List: linux-tegra@vger.kernel.org Tegra194 allows max of 64K bytes and Tegra186 and prior allows max of 4K bytes of transfer per packet. one sec timeout is not enough for transfers more than 10K bytes at STD bus rate. This patch updates I2C transfer timeout based on the transfer size and I2C bus rate to allow enough time during max transfer size at lower bus speed. Acked-by: Thierry Reding Reviewed-by: Dmitry Osipenko Signed-off-by: Sowjanya Komatineni --- [V10/V11] : Reduced the timeout for bus clear operation Added adapter timeout to cover worst case transfer rate (max transfer size at STD speed) incase if ARB LOST happens during middle/end of the transaction. [V9] : Rebased to 5.0-rc4 Minor updates for readability of xfer time [V8] : Added comment with explaination of xfer time calculation [V5/V6/V7] : Same as V4 [V4] : V4 series includes bus clear support and this patch is updated with fixed timeout of 1sec for bus clear operation. [V3] : Same as V2 [V2] : Added this patch in V2 series to allow enough time for data transfer to happen. This patch has dependency with DMA patch as TEGRA_I2C_TIMEOUT define takes argument with this patch. drivers/i2c/busses/i2c-tegra.c | 22 ++++++++++++++++------ 1 file changed, 16 insertions(+), 6 deletions(-) diff --git a/drivers/i2c/busses/i2c-tegra.c b/drivers/i2c/busses/i2c-tegra.c index 267a6b3084bf..f7584f0ea7da 100644 --- a/drivers/i2c/busses/i2c-tegra.c +++ b/drivers/i2c/busses/i2c-tegra.c @@ -25,7 +25,6 @@ #include #include -#define TEGRA_I2C_TIMEOUT (msecs_to_jiffies(1000)) #define BYTES_PER_FIFO_WORD 4 #define I2C_CNFG 0x000 @@ -938,8 +937,9 @@ static int tegra_i2c_issue_bus_clear(struct tegra_i2c_dev *i2c_dev) i2c_writel(i2c_dev, reg, I2C_BUS_CLEAR_CNFG); tegra_i2c_unmask_irq(i2c_dev, I2C_INT_BUS_CLR_DONE); - time_left = wait_for_completion_timeout(&i2c_dev->msg_complete, - TEGRA_I2C_TIMEOUT); + time_left = wait_for_completion_timeout( + &i2c_dev->msg_complete, + msecs_to_jiffies(50)); if (time_left == 0) { dev_err(i2c_dev->dev, "timed out for bus clear\n"); return -ETIMEDOUT; @@ -966,6 +966,7 @@ static int tegra_i2c_xfer_msg(struct tegra_i2c_dev *i2c_dev, u32 *buffer = NULL; int err = 0; bool dma = false; + u16 xfer_time = 100; tegra_i2c_flush_fifos(i2c_dev); @@ -984,6 +985,13 @@ static int tegra_i2c_xfer_msg(struct tegra_i2c_dev *i2c_dev, dma = (xfer_size > I2C_PIO_MODE_MAX_LEN) && i2c_dev->dma_buf; i2c_dev->is_curr_dma_xfer = dma; + /* + * Transfer time in mSec = Total bits / transfer rate + * Total bits = 9 bits per byte (including ACK bit) + Start & stop bits + */ + xfer_time += DIV_ROUND_CLOSEST(((xfer_size * 9) + 2) * MSEC_PER_SEC, + i2c_dev->bus_clk_rate); + spin_lock_irqsave(&i2c_dev->xfer_lock, flags); int_mask = I2C_INT_NO_ACK | I2C_INT_ARBITRATION_LOST; @@ -1091,7 +1099,7 @@ static int tegra_i2c_xfer_msg(struct tegra_i2c_dev *i2c_dev, time_left = wait_for_completion_timeout( &i2c_dev->dma_complete, - TEGRA_I2C_TIMEOUT); + msecs_to_jiffies(xfer_time)); if (time_left == 0) { dev_err(i2c_dev->dev, "DMA transfer timeout\n"); @@ -1117,8 +1125,9 @@ static int tegra_i2c_xfer_msg(struct tegra_i2c_dev *i2c_dev, } } - time_left = wait_for_completion_timeout(&i2c_dev->msg_complete, - TEGRA_I2C_TIMEOUT); + time_left = wait_for_completion_timeout( + &i2c_dev->msg_complete, + msecs_to_jiffies(xfer_time)); tegra_i2c_mask_irq(i2c_dev, int_mask); if (time_left == 0) { @@ -1375,6 +1384,7 @@ static int tegra_i2c_probe(struct platform_device *pdev) i2c_dev->div_clk = div_clk; i2c_dev->adapter.algo = &tegra_i2c_algo; i2c_dev->adapter.retries = 1; + i2c_dev->adapter.timeout = 6 * HZ; i2c_dev->irq = irq; i2c_dev->cont_id = pdev->id; i2c_dev->dev = &pdev->dev; From patchwork Tue Feb 5 08:56:25 2019 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Sowjanya Komatineni X-Patchwork-Id: 1036506 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-tegra-owner@vger.kernel.org; receiver=) Authentication-Results: ozlabs.org; dmarc=pass (p=none dis=none) header.from=nvidia.com Authentication-Results: ozlabs.org; dkim=pass (2048-bit key; unprotected) header.d=nvidia.com header.i=@nvidia.com header.b="R0suUYfx"; dkim-atps=neutral Received: from vger.kernel.org (vger.kernel.org [209.132.180.67]) by ozlabs.org (Postfix) with ESMTP id 43tz2y1BjKz9sN9 for ; Tue, 5 Feb 2019 19:56:42 +1100 (AEDT) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S1728310AbfBEI4f (ORCPT ); Tue, 5 Feb 2019 03:56:35 -0500 Received: from hqemgate16.nvidia.com ([216.228.121.65]:11391 "EHLO hqemgate16.nvidia.com" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S1728288AbfBEI4e (ORCPT ); Tue, 5 Feb 2019 03:56:34 -0500 Received: from hqpgpgate102.nvidia.com (Not Verified[216.228.121.13]) by hqemgate16.nvidia.com (using TLS: TLSv1.2, DES-CBC3-SHA) id ; Tue, 05 Feb 2019 00:56:24 -0800 Received: from hqmail.nvidia.com ([172.20.161.6]) by hqpgpgate102.nvidia.com (PGP Universal service); Tue, 05 Feb 2019 00:56:33 -0800 X-PGP-Universal: processed; by hqpgpgate102.nvidia.com on Tue, 05 Feb 2019 00:56:33 -0800 Received: from HQMAIL108.nvidia.com (172.18.146.13) by HQMAIL106.nvidia.com (172.18.146.12) with Microsoft SMTP Server (TLS) id 15.0.1395.4; Tue, 5 Feb 2019 08:56:33 +0000 Received: from hqnvemgw01.nvidia.com (172.20.150.20) by HQMAIL108.nvidia.com (172.18.146.13) with Microsoft SMTP Server (TLS) id 15.0.1395.4 via Frontend Transport; Tue, 5 Feb 2019 08:56:33 +0000 Received: from skomatineni-linux.nvidia.com (Not Verified[10.2.175.36]) by hqnvemgw01.nvidia.com with Trustwave SEG (v7, 5, 8, 10121) id ; Tue, 05 Feb 2019 00:56:33 -0800 From: Sowjanya Komatineni To: , , , , CC: , , , , Sowjanya Komatineni Subject: [PATCH V11 5/5] i2c: tegra: add i2c interface timing support Date: Tue, 5 Feb 2019 00:56:25 -0800 Message-ID: <1549356985-25726-5-git-send-email-skomatineni@nvidia.com> X-Mailer: git-send-email 2.7.4 In-Reply-To: <1549356985-25726-1-git-send-email-skomatineni@nvidia.com> References: <1549356985-25726-1-git-send-email-skomatineni@nvidia.com> X-NVConfidentiality: public MIME-Version: 1.0 DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=nvidia.com; s=n1; t=1549356984; bh=+rS707ZFqF9Lkm5+M/rPF/IVcVIrAl1aOoT3Z67guOE=; h=X-PGP-Universal:From:To:CC:Subject:Date:Message-ID:X-Mailer: In-Reply-To:References:X-NVConfidentiality:MIME-Version: Content-Type; b=R0suUYfxug3o7hKwf9l/sQ8A8h5qjO1s701OBGmaCXRrCneZIAxK29wBdu4MOD8wJ hdtNCEyH9R7ISYOkasfvL3UreMP0Eypkn+S79voriB0li2wjRA1F0tY/1xvnouGBOp rh3gMgdGu98vCeQpSFNzQZOiaYk1ygGeOsdO82UB/yd3+fc/bWTpUXPB4x66hT65Aa ITcDuOW4LXjhhF5RamvlPuR3aUQ0T80ylfRofx1HDjSl0TsmEsfHTAf2zU224CBQTX 8tGet9ED7QRsRBuoS6xjY3Ucwly6xudUA/SYdJu/CZJbiKKGLKE79gjP1DWS7XHeKg 5MZvlTPyrzbzw== Sender: linux-tegra-owner@vger.kernel.org Precedence: bulk List-ID: X-Mailing-List: linux-tegra@vger.kernel.org This patch adds I2C interface timing registers support for proper bus rate configuration along with meeting the i2c spec setup and hold times based on the tuning performed on Tegra210, Tegra186 and Tegra194 platforms. I2C_INTERFACE_TIMING_0 register contains TLOW and THIGH field and Tegra I2C controller design uses them as a part of internal clock divisor. I2C_INTERFACE_TIMING_1 register contains the setup and hold times for start and stop conditions. Acked-by: Thierry Reding Signed-off-by: Sowjanya Komatineni --- [V9/V10/V11] : Same as V8 [V8] : Updated to handle timing implementation within tegra_i2c_init directly [V7] : Minor updates to timing implementation [V5/V6] : Added this Interface timing patch in V5 of the patch series. drivers/i2c/busses/i2c-tegra.c | 189 ++++++++++++++++++++++++++++++++++------- 1 file changed, 160 insertions(+), 29 deletions(-) diff --git a/drivers/i2c/busses/i2c-tegra.c b/drivers/i2c/busses/i2c-tegra.c index f7584f0ea7da..841cad6ccb57 100644 --- a/drivers/i2c/busses/i2c-tegra.c +++ b/drivers/i2c/busses/i2c-tegra.c @@ -129,6 +129,15 @@ #define I2C_MST_FIFO_STATUS_TX_MASK 0xff0000 #define I2C_MST_FIFO_STATUS_TX_SHIFT 16 +#define I2C_INTERFACE_TIMING_0 0x94 +#define I2C_THIGH_SHIFT 8 +#define I2C_INTERFACE_TIMING_1 0x98 + +#define I2C_STANDARD_MODE 100000 +#define I2C_FAST_MODE 400000 +#define I2C_FAST_PLUS_MODE 1000000 +#define I2C_HS_MODE 3500000 + /* Packet header size in bytes */ #define I2C_PACKET_HEADER_SIZE 12 @@ -166,7 +175,10 @@ enum msg_end_type { * @has_config_load_reg: Has the config load register to load the new * configuration. * @clk_divisor_hs_mode: Clock divisor in HS mode. - * @clk_divisor_std_fast_mode: Clock divisor in standard/fast mode. It is + * @clk_divisor_std_mode: Clock divisor in standard mode. It is + * applicable if there is no fast clock source i.e. single clock + * source. + * @clk_divisor_fast_mode: Clock divisor in fast mode. It is * applicable if there is no fast clock source i.e. single clock * source. * @clk_divisor_fast_plus_mode: Clock divisor in fast mode plus. It is @@ -184,6 +196,16 @@ enum msg_end_type { * @supports_bus_clear: Bus Clear support to recover from bus hang during * SDA stuck low from device for some unknown reasons. * @has_apb_dma: Support of APBDMA on corresponding Tegra chip. + * @tlow_std_mode: Low period of the clock in standard mode. + * @thigh_std_mode: High period of the clock in standard mode. + * @tlow_fast_fastplus_mode: Low period of the clock in fast/fast-plus modes. + * @thigh_fast_fastplus_mode: High period of the clock in fast/fast-plus modes. + * @setup_hold_time_std_mode: Setup and hold time for start and stop conditions + * in standard mode. + * @setup_hold_time_fast_fast_plus_mode: Setup and hold time for start and stop + * conditions in fast/fast-plus modes. + * @setup_hold_time_hs_mode: Setup and hold time for start and stop conditions + * in HS mode. */ struct tegra_i2c_hw_feature { bool has_continue_xfer_support; @@ -191,7 +213,8 @@ struct tegra_i2c_hw_feature { bool has_single_clk_source; bool has_config_load_reg; int clk_divisor_hs_mode; - int clk_divisor_std_fast_mode; + int clk_divisor_std_mode; + int clk_divisor_fast_mode; u16 clk_divisor_fast_plus_mode; bool has_multi_master_mode; bool has_slcg_override_reg; @@ -199,6 +222,13 @@ struct tegra_i2c_hw_feature { const struct i2c_adapter_quirks *quirks; bool supports_bus_clear; bool has_apb_dma; + u8 tlow_std_mode; + u8 thigh_std_mode; + u8 tlow_fast_fastplus_mode; + u8 thigh_fast_fastplus_mode; + u32 setup_hold_time_std_mode; + u32 setup_hold_time_fast_fast_plus_mode; + u32 setup_hold_time_hs_mode; }; /** @@ -666,11 +696,13 @@ static int tegra_i2c_wait_for_config_load(struct tegra_i2c_dev *i2c_dev) return 0; } -static int tegra_i2c_init(struct tegra_i2c_dev *i2c_dev) +static int tegra_i2c_init(struct tegra_i2c_dev *i2c_dev, bool clk_reinit) { u32 val; int err; - u32 clk_divisor; + u32 clk_divisor, clk_multiplier; + u32 tsu_thd = 0; + u8 tlow, thigh; err = pm_runtime_get_sync(i2c_dev->dev); if (err < 0) { @@ -700,6 +732,36 @@ static int tegra_i2c_init(struct tegra_i2c_dev *i2c_dev) I2C_CLK_DIVISOR_STD_FAST_MODE_SHIFT; i2c_writel(i2c_dev, clk_divisor, I2C_CLK_DIVISOR); + if ((i2c_dev->bus_clk_rate > I2C_STANDARD_MODE) && + (i2c_dev->bus_clk_rate <= I2C_FAST_PLUS_MODE)) { + tlow = i2c_dev->hw->tlow_fast_fastplus_mode; + thigh = i2c_dev->hw->thigh_fast_fastplus_mode; + tsu_thd = i2c_dev->hw->setup_hold_time_fast_fast_plus_mode; + } else { + tlow = i2c_dev->hw->tlow_std_mode; + thigh = i2c_dev->hw->thigh_std_mode; + tsu_thd = i2c_dev->hw->setup_hold_time_std_mode; + } + + val = (thigh << I2C_THIGH_SHIFT) | tlow; + if (val) + i2c_writel(i2c_dev, val, I2C_INTERFACE_TIMING_0); + + if (tsu_thd) + i2c_writel(i2c_dev, tsu_thd, I2C_INTERFACE_TIMING_1); + + if (!clk_reinit) { + clk_multiplier = (tlow + thigh + 2); + clk_multiplier *= (i2c_dev->clk_divisor_non_hs_mode + 1); + err = clk_set_rate(i2c_dev->div_clk, + i2c_dev->bus_clk_rate * clk_multiplier); + if (err) { + dev_err(i2c_dev->dev, + "failed changing clock rate: %d\n", err); + goto err; + } + } + if (!i2c_dev->is_dvc) { u32 sl_cfg = i2c_readl(i2c_dev, I2C_SL_CNFG); @@ -1106,7 +1168,7 @@ static int tegra_i2c_xfer_msg(struct tegra_i2c_dev *i2c_dev, dmaengine_terminate_sync(i2c_dev->msg_read ? i2c_dev->rx_dma_chan : i2c_dev->tx_dma_chan); - tegra_i2c_init(i2c_dev); + tegra_i2c_init(i2c_dev, true); return -ETIMEDOUT; } @@ -1133,7 +1195,7 @@ static int tegra_i2c_xfer_msg(struct tegra_i2c_dev *i2c_dev, if (time_left == 0) { dev_err(i2c_dev->dev, "i2c transfer timed out\n"); - tegra_i2c_init(i2c_dev); + tegra_i2c_init(i2c_dev, true); return -ETIMEDOUT; } @@ -1145,7 +1207,7 @@ static int tegra_i2c_xfer_msg(struct tegra_i2c_dev *i2c_dev, if (likely(i2c_dev->msg_err == I2C_ERR_NONE)) return 0; - tegra_i2c_init(i2c_dev); + tegra_i2c_init(i2c_dev, true); /* start recovery upon arbitration loss in single master mode */ if (i2c_dev->msg_err == I2C_ERR_ARBITRATION_LOST) { if (!i2c_dev->is_multimaster_mode) @@ -1240,7 +1302,8 @@ static const struct tegra_i2c_hw_feature tegra20_i2c_hw = { .has_per_pkt_xfer_complete_irq = false, .has_single_clk_source = false, .clk_divisor_hs_mode = 3, - .clk_divisor_std_fast_mode = 0, + .clk_divisor_std_mode = 0, + .clk_divisor_fast_mode = 0, .clk_divisor_fast_plus_mode = 0, .has_config_load_reg = false, .has_multi_master_mode = false, @@ -1249,6 +1312,13 @@ static const struct tegra_i2c_hw_feature tegra20_i2c_hw = { .quirks = &tegra_i2c_quirks, .supports_bus_clear = false, .has_apb_dma = true, + .tlow_std_mode = 0x4, + .thigh_std_mode = 0x2, + .tlow_fast_fastplus_mode = 0x4, + .thigh_fast_fastplus_mode = 0x2, + .setup_hold_time_std_mode = 0x0, + .setup_hold_time_fast_fast_plus_mode = 0x0, + .setup_hold_time_hs_mode = 0x0, }; static const struct tegra_i2c_hw_feature tegra30_i2c_hw = { @@ -1256,7 +1326,8 @@ static const struct tegra_i2c_hw_feature tegra30_i2c_hw = { .has_per_pkt_xfer_complete_irq = false, .has_single_clk_source = false, .clk_divisor_hs_mode = 3, - .clk_divisor_std_fast_mode = 0, + .clk_divisor_std_mode = 0, + .clk_divisor_fast_mode = 0, .clk_divisor_fast_plus_mode = 0, .has_config_load_reg = false, .has_multi_master_mode = false, @@ -1265,6 +1336,13 @@ static const struct tegra_i2c_hw_feature tegra30_i2c_hw = { .quirks = &tegra_i2c_quirks, .supports_bus_clear = false, .has_apb_dma = true, + .tlow_std_mode = 0x4, + .thigh_std_mode = 0x2, + .tlow_fast_fastplus_mode = 0x4, + .thigh_fast_fastplus_mode = 0x2, + .setup_hold_time_std_mode = 0x0, + .setup_hold_time_fast_fast_plus_mode = 0x0, + .setup_hold_time_hs_mode = 0x0, }; static const struct tegra_i2c_hw_feature tegra114_i2c_hw = { @@ -1272,7 +1350,8 @@ static const struct tegra_i2c_hw_feature tegra114_i2c_hw = { .has_per_pkt_xfer_complete_irq = true, .has_single_clk_source = true, .clk_divisor_hs_mode = 1, - .clk_divisor_std_fast_mode = 0x19, + .clk_divisor_std_mode = 0x19, + .clk_divisor_fast_mode = 0x19, .clk_divisor_fast_plus_mode = 0x10, .has_config_load_reg = false, .has_multi_master_mode = false, @@ -1281,6 +1360,13 @@ static const struct tegra_i2c_hw_feature tegra114_i2c_hw = { .quirks = &tegra_i2c_quirks, .supports_bus_clear = true, .has_apb_dma = true, + .tlow_std_mode = 0x4, + .thigh_std_mode = 0x2, + .tlow_fast_fastplus_mode = 0x4, + .thigh_fast_fastplus_mode = 0x2, + .setup_hold_time_std_mode = 0x0, + .setup_hold_time_fast_fast_plus_mode = 0x0, + .setup_hold_time_hs_mode = 0x0, }; static const struct tegra_i2c_hw_feature tegra124_i2c_hw = { @@ -1288,7 +1374,8 @@ static const struct tegra_i2c_hw_feature tegra124_i2c_hw = { .has_per_pkt_xfer_complete_irq = true, .has_single_clk_source = true, .clk_divisor_hs_mode = 1, - .clk_divisor_std_fast_mode = 0x19, + .clk_divisor_std_mode = 0x19, + .clk_divisor_fast_mode = 0x19, .clk_divisor_fast_plus_mode = 0x10, .has_config_load_reg = true, .has_multi_master_mode = false, @@ -1297,6 +1384,13 @@ static const struct tegra_i2c_hw_feature tegra124_i2c_hw = { .quirks = &tegra_i2c_quirks, .supports_bus_clear = true, .has_apb_dma = true, + .tlow_std_mode = 0x4, + .thigh_std_mode = 0x2, + .tlow_fast_fastplus_mode = 0x4, + .thigh_fast_fastplus_mode = 0x2, + .setup_hold_time_std_mode = 0x0, + .setup_hold_time_fast_fast_plus_mode = 0x0, + .setup_hold_time_hs_mode = 0x0, }; static const struct tegra_i2c_hw_feature tegra210_i2c_hw = { @@ -1304,7 +1398,8 @@ static const struct tegra_i2c_hw_feature tegra210_i2c_hw = { .has_per_pkt_xfer_complete_irq = true, .has_single_clk_source = true, .clk_divisor_hs_mode = 1, - .clk_divisor_std_fast_mode = 0x19, + .clk_divisor_std_mode = 0x19, + .clk_divisor_fast_mode = 0x19, .clk_divisor_fast_plus_mode = 0x10, .has_config_load_reg = true, .has_multi_master_mode = true, @@ -1313,27 +1408,67 @@ static const struct tegra_i2c_hw_feature tegra210_i2c_hw = { .quirks = &tegra_i2c_quirks, .supports_bus_clear = true, .has_apb_dma = true, + .tlow_std_mode = 0x4, + .thigh_std_mode = 0x2, + .tlow_fast_fastplus_mode = 0x4, + .thigh_fast_fastplus_mode = 0x2, + .setup_hold_time_std_mode = 0, + .setup_hold_time_fast_fast_plus_mode = 0, + .setup_hold_time_hs_mode = 0, }; -static const struct tegra_i2c_hw_feature tegra194_i2c_hw = { +static const struct tegra_i2c_hw_feature tegra186_i2c_hw = { .has_continue_xfer_support = true, .has_per_pkt_xfer_complete_irq = true, .has_single_clk_source = true, .clk_divisor_hs_mode = 1, - .clk_divisor_std_fast_mode = 0x19, + .clk_divisor_std_mode = 0x16, + .clk_divisor_fast_mode = 0x19, .clk_divisor_fast_plus_mode = 0x10, .has_config_load_reg = true, .has_multi_master_mode = true, .has_slcg_override_reg = true, .has_mst_fifo = true, + .quirks = &tegra_i2c_quirks, + .supports_bus_clear = true, + .has_apb_dma = false, + .tlow_std_mode = 0x4, + .thigh_std_mode = 0x3, + .tlow_fast_fastplus_mode = 0x4, + .thigh_fast_fastplus_mode = 0x2, + .setup_hold_time_std_mode = 0, + .setup_hold_time_fast_fast_plus_mode = 0, + .setup_hold_time_hs_mode = 0, +}; + +static const struct tegra_i2c_hw_feature tegra194_i2c_hw = { + .has_continue_xfer_support = true, + .has_per_pkt_xfer_complete_irq = true, + .has_single_clk_source = true, + .clk_divisor_hs_mode = 1, + .clk_divisor_std_mode = 0x4f, + .clk_divisor_fast_mode = 0x3c, + .clk_divisor_fast_plus_mode = 0x16, + .has_config_load_reg = true, + .has_multi_master_mode = true, + .has_slcg_override_reg = true, + .has_mst_fifo = true, .quirks = &tegra194_i2c_quirks, .supports_bus_clear = true, .has_apb_dma = false, + .tlow_std_mode = 0x8, + .thigh_std_mode = 0x7, + .tlow_fast_fastplus_mode = 0x2, + .thigh_fast_fastplus_mode = 0x2, + .setup_hold_time_std_mode = 0x08080808, + .setup_hold_time_fast_fast_plus_mode = 0x02020202, + .setup_hold_time_hs_mode = 0x090909, }; /* Match table for of_platform binding */ static const struct of_device_id tegra_i2c_of_match[] = { { .compatible = "nvidia,tegra194-i2c", .data = &tegra194_i2c_hw, }, + { .compatible = "nvidia,tegra186-i2c", .data = &tegra186_i2c_hw, }, { .compatible = "nvidia,tegra210-i2c", .data = &tegra210_i2c_hw, }, { .compatible = "nvidia,tegra124-i2c", .data = &tegra124_i2c_hw, }, { .compatible = "nvidia,tegra114-i2c", .data = &tegra114_i2c_hw, }, @@ -1354,7 +1489,6 @@ static int tegra_i2c_probe(struct platform_device *pdev) phys_addr_t base_phys; int irq; int ret = 0; - int clk_multiplier = I2C_CLK_MULTIPLIER_STD_FAST_MODE; res = platform_get_resource(pdev, IORESOURCE_MEM, 0); base_phys = res->start; @@ -1425,20 +1559,17 @@ static int tegra_i2c_probe(struct platform_device *pdev) } } - i2c_dev->clk_divisor_non_hs_mode = - i2c_dev->hw->clk_divisor_std_fast_mode; - if (i2c_dev->hw->clk_divisor_fast_plus_mode && - (i2c_dev->bus_clk_rate == 1000000)) + if ((i2c_dev->bus_clk_rate > I2C_FAST_MODE) && + (i2c_dev->bus_clk_rate <= I2C_FAST_PLUS_MODE)) i2c_dev->clk_divisor_non_hs_mode = - i2c_dev->hw->clk_divisor_fast_plus_mode; - - clk_multiplier *= (i2c_dev->clk_divisor_non_hs_mode + 1); - ret = clk_set_rate(i2c_dev->div_clk, - i2c_dev->bus_clk_rate * clk_multiplier); - if (ret) { - dev_err(i2c_dev->dev, "Clock rate change failed %d\n", ret); - goto unprepare_fast_clk; - } + i2c_dev->hw->clk_divisor_fast_plus_mode; + else if ((i2c_dev->bus_clk_rate > I2C_STANDARD_MODE) && + (i2c_dev->bus_clk_rate <= I2C_FAST_MODE)) + i2c_dev->clk_divisor_non_hs_mode = + i2c_dev->hw->clk_divisor_fast_mode; + else + i2c_dev->clk_divisor_non_hs_mode = + i2c_dev->hw->clk_divisor_std_mode; ret = clk_prepare(i2c_dev->div_clk); if (ret < 0) { @@ -1468,7 +1599,7 @@ static int tegra_i2c_probe(struct platform_device *pdev) if (ret < 0) goto disable_div_clk; - ret = tegra_i2c_init(i2c_dev); + ret = tegra_i2c_init(i2c_dev, false); if (ret) { dev_err(&pdev->dev, "Failed to initialize i2c controller\n"); goto disable_div_clk;