From patchwork Fri Jan 4 00:23:38 2019 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Sowjanya Komatineni X-Patchwork-Id: 1020552 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; 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="oE4RHraG"; dkim-atps=neutral Received: from vger.kernel.org (vger.kernel.org [209.132.180.67]) by ozlabs.org (Postfix) with ESMTP id 43W59r6Lv1z9s7T for ; Fri, 4 Jan 2019 11:23:44 +1100 (AEDT) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S1727355AbfADAXn (ORCPT ); Thu, 3 Jan 2019 19:23:43 -0500 Received: from hqemgate15.nvidia.com ([216.228.121.64]:6930 "EHLO hqemgate15.nvidia.com" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S1726034AbfADAXn (ORCPT ); Thu, 3 Jan 2019 19:23:43 -0500 Received: from hqpgpgate101.nvidia.com (Not Verified[216.228.121.13]) by hqemgate15.nvidia.com (using TLS: TLSv1.2, DES-CBC3-SHA) id ; Thu, 03 Jan 2019 16:23:26 -0800 Received: from hqmail.nvidia.com ([172.20.161.6]) by hqpgpgate101.nvidia.com (PGP Universal service); Thu, 03 Jan 2019 16:23:40 -0800 X-PGP-Universal: processed; by hqpgpgate101.nvidia.com on Thu, 03 Jan 2019 16:23:40 -0800 Received: from HQMAIL111.nvidia.com (172.20.187.18) by HQMAIL103.nvidia.com (172.20.187.11) with Microsoft SMTP Server (TLS) id 15.0.1395.4; Fri, 4 Jan 2019 00:23:40 +0000 Received: from HQMAIL104.nvidia.com (172.18.146.11) by HQMAIL111.nvidia.com (172.20.187.18) with Microsoft SMTP Server (TLS) id 15.0.1395.4; Fri, 4 Jan 2019 00:23:40 +0000 Received: from hqnvemgw02.nvidia.com (172.16.227.111) by HQMAIL104.nvidia.com (172.18.146.11) with Microsoft SMTP Server (TLS) id 15.0.1395.4 via Frontend Transport; Fri, 4 Jan 2019 00:23:40 +0000 Received: from skomatineni-linux.nvidia.com (Not Verified[10.110.103.52]) by hqnvemgw02.nvidia.com with Trustwave SEG (v7, 5, 8, 10121) id ; Thu, 03 Jan 2019 16:23:39 -0800 From: Sowjanya Komatineni To: , , , , CC: , , , Sowjanya Komatineni Subject: [PATCH V1] i2c: tegra: Add Bus Clear Master Support Date: Thu, 3 Jan 2019 16:23:38 -0800 Message-ID: <1546561418-10230-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=1546561406; bh=iv7oaOmSHG33WoAhMC9KCzVgoCsvsBzLzTsKk0iVkRA=; h=X-PGP-Universal:From:To:CC:Subject:Date:Message-ID:X-Mailer: X-NVConfidentiality:MIME-Version:Content-Type; b=oE4RHraGP4fYsyxhvrRwaePzZJcgxwJ+dXJMy404N8kw3KpkNKy1qgIsj6CMzuhyD slzmNncNYO3gZqtgy4+x7XtjKONDJVrIjgcAubQMZWIB5+hbMQbmdvhnEMz2kGBVdE LM3+082XqOqhgBE5fEXdVLqj2OdbUJEeBp/rdh4jyE2UkPv1xI8JgkRv3v4Ugh0CPB iQ1lDvJEO3ZTlp/eS4qOrVaT+EqK8cZPvx3Ds6+T7+cWmbznDRj7yPpy1YCaXKWPbl sWAMjBY8+co/7JQmcuYEzX+qRAOa3+k+S986nOVgJ1mHD3ornH9qHj4rBY/BHBhOME UVXXQ7qSZQ7UQ== Sender: linux-i2c-owner@vger.kernel.org Precedence: bulk List-ID: X-Mailing-List: linux-i2c@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. Signed-off-by: Sowjanya Komatineni --- drivers/i2c/busses/i2c-tegra.c | 65 ++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 65 insertions(+) diff --git a/drivers/i2c/busses/i2c-tegra.c b/drivers/i2c/busses/i2c-tegra.c index 437294ea2f0a..89a0c9fdc99e 100644 --- a/drivers/i2c/busses/i2c-tegra.c +++ b/drivers/i2c/busses/i2c-tegra.c @@ -63,6 +63,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) @@ -105,6 +106,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 { * @clk_divisor_std_fast_mode: Clock divisor in standard/fast mode. It is * applicable if there is no fast clock source i.e. single clock * source. + * @has_bus_clr_support: Bus Clear support to recover from bus hang during + * SDA stuck low from device for some unknown reasons. */ struct tegra_i2c_hw_feature { @@ -167,6 +179,7 @@ struct tegra_i2c_hw_feature { bool has_multi_master_mode; bool has_slcg_override_reg; bool has_mst_fifo; + bool has_bus_clr_support; }; /** @@ -636,6 +649,9 @@ static irqreturn_t tegra_i2c_isr(int irq, void *dev_id) goto err; } + if (i2c_dev->hw->has_bus_clr_support && (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); @@ -664,6 +680,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->has_bus_clr_support) + 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); @@ -674,6 +692,41 @@ 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 time_left, err; + u32 reg; + + if (i2c_dev->hw->has_bus_clr_support) { + 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 Arb 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) { @@ -755,6 +808,12 @@ 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; @@ -844,6 +903,7 @@ static const struct tegra_i2c_hw_feature tegra20_i2c_hw = { .has_multi_master_mode = false, .has_slcg_override_reg = false, .has_mst_fifo = false, + .has_bus_clr_support = false, }; static const struct tegra_i2c_hw_feature tegra30_i2c_hw = { @@ -857,6 +917,7 @@ static const struct tegra_i2c_hw_feature tegra30_i2c_hw = { .has_multi_master_mode = false, .has_slcg_override_reg = false, .has_mst_fifo = false, + .has_bus_clr_support = false, }; static const struct tegra_i2c_hw_feature tegra114_i2c_hw = { @@ -870,6 +931,7 @@ static const struct tegra_i2c_hw_feature tegra114_i2c_hw = { .has_multi_master_mode = false, .has_slcg_override_reg = false, .has_mst_fifo = false, + .has_bus_clr_support = true, }; static const struct tegra_i2c_hw_feature tegra124_i2c_hw = { @@ -883,6 +945,7 @@ static const struct tegra_i2c_hw_feature tegra124_i2c_hw = { .has_multi_master_mode = false, .has_slcg_override_reg = true, .has_mst_fifo = false, + .has_bus_clr_support = true, }; static const struct tegra_i2c_hw_feature tegra210_i2c_hw = { @@ -896,6 +959,7 @@ static const struct tegra_i2c_hw_feature tegra210_i2c_hw = { .has_multi_master_mode = true, .has_slcg_override_reg = true, .has_mst_fifo = false, + .has_bus_clr_support = true, }; static const struct tegra_i2c_hw_feature tegra194_i2c_hw = { @@ -909,6 +973,7 @@ static const struct tegra_i2c_hw_feature tegra194_i2c_hw = { .has_multi_master_mode = true, .has_slcg_override_reg = true, .has_mst_fifo = true, + .has_bus_clr_support = true, }; /* Match table for of_platform binding */