From patchwork Fri Feb 8 16:45:14 2019 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Sowjanya Komatineni X-Patchwork-Id: 1038753 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="Kug9MVbO"; dkim-atps=neutral Received: from vger.kernel.org (vger.kernel.org [209.132.180.67]) by ozlabs.org (Postfix) with ESMTP id 43x1JZ69q5z9sNG for ; Sat, 9 Feb 2019 03:45:34 +1100 (AEDT) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S1727639AbfBHQp1 (ORCPT ); Fri, 8 Feb 2019 11:45:27 -0500 Received: from hqemgate16.nvidia.com ([216.228.121.65]:1653 "EHLO hqemgate16.nvidia.com" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S1726679AbfBHQp0 (ORCPT ); Fri, 8 Feb 2019 11:45:26 -0500 Received: from hqpgpgate101.nvidia.com (Not Verified[216.228.121.13]) by hqemgate16.nvidia.com (using TLS: TLSv1.2, DES-CBC3-SHA) id ; Fri, 08 Feb 2019 08:45:05 -0800 Received: from hqmail.nvidia.com ([172.20.161.6]) by hqpgpgate101.nvidia.com (PGP Universal service); Fri, 08 Feb 2019 08:45:24 -0800 X-PGP-Universal: processed; by hqpgpgate101.nvidia.com on Fri, 08 Feb 2019 08:45:24 -0800 Received: from HQMAIL104.nvidia.com (172.18.146.11) by HQMAIL104.nvidia.com (172.18.146.11) with Microsoft SMTP Server (TLS) id 15.0.1395.4; Fri, 8 Feb 2019 16:45:24 +0000 Received: from hqnvemgw01.nvidia.com (172.20.150.20) by HQMAIL104.nvidia.com (172.18.146.11) with Microsoft SMTP Server (TLS) id 15.0.1395.4 via Frontend Transport; Fri, 8 Feb 2019 16:45:24 +0000 Received: from skomatineni-linux.nvidia.com (Not Verified[10.2.171.121]) by hqnvemgw01.nvidia.com with Trustwave SEG (v7, 5, 8, 10121) id ; Fri, 08 Feb 2019 08:45:24 -0800 From: Sowjanya Komatineni To: , , , , CC: , , , , Sowjanya Komatineni Subject: [PATCH V16 6/6] i2c: tegra: add i2c interface timing support Date: Fri, 8 Feb 2019 08:45:14 -0800 Message-ID: <1549644314-1481-6-git-send-email-skomatineni@nvidia.com> X-Mailer: git-send-email 2.7.4 In-Reply-To: <1549644314-1481-1-git-send-email-skomatineni@nvidia.com> References: <1549644314-1481-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=1549644305; bh=8oMz2KJHermtrmDUj0QIErNaOCPMut3MKeUhYdwTdoU=; h=X-PGP-Universal:From:To:CC:Subject:Date:Message-ID:X-Mailer: In-Reply-To:References:X-NVConfidentiality:MIME-Version: Content-Type; b=Kug9MVbOURV1tgsa3rfXKSk4Qk7LXuWLQWS3Yi71SghmYGRfolN5R/ci6EHnarlzG w7Znzc1NOSYOCT1ORnt3mLEeGZ4hJKWWyuuJm5SIU0fuQdIWUcDp/vIeM5bjI2sp3t zytLdtjGfha3sKCBJRtztZS26Tlv4PJNeFd0oL+vAW/pD6UUVck/7Uc8tQ0MUR3H1J u6xDe4dr8IkkgYggb+wYhGJ2TmBvp+77q/gYE4DB9K6kKMQMMA1iIZY3a8gHNAEJS6 IlPZho5rjLhlSVwCHgevO78220i3grcjX9mHf3nC6j4CeLaduivv8ayVVo/yV8oUcs DK1veNDayPUsA== 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 Reviewed-by: Dmitry Osipenko Tested-by: Dmitry Osipenko --- [V16] : Minor changes [V15] : Moved Tegra186 hw feature structure to DMA patch of this series. [V14] : INTERFACE_TIMING registers doesnt exist on Tegra114 and prior. Added flag to hw feature to identify this and program only on supported tegra chips. [V13] : Same as V12 [V12] : Fixed strict coding style check issues [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, 159 insertions(+), 30 deletions(-) diff --git a/drivers/i2c/busses/i2c-tegra.c b/drivers/i2c/busses/i2c-tegra.c index 9cba84be1571..55082d1db896 100644 --- a/drivers/i2c/busses/i2c-tegra.c +++ b/drivers/i2c/busses/i2c-tegra.c @@ -126,6 +126,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 @@ -160,7 +169,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 @@ -178,6 +190,18 @@ 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. + * @has_interface_timing_reg: Has interface timing register to program the tuned + * timing settings. */ struct tegra_i2c_hw_feature { bool has_continue_xfer_support; @@ -185,7 +209,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; @@ -193,6 +218,14 @@ 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; + bool has_interface_timing_reg; }; /** @@ -660,11 +693,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) { @@ -694,6 +729,41 @@ 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; + } + + if (i2c_dev->hw->has_interface_timing_reg) { + val = (thigh << I2C_THIGH_SHIFT) | tlow; + i2c_writel(i2c_dev, val, I2C_INTERFACE_TIMING_0); + } + + /* + * configure setup and hold times only when tsu_thd is non-zero. + * otherwise, preserve the chip default values + */ + if (i2c_dev->hw->has_interface_timing_reg && 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); @@ -1098,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; } @@ -1124,7 +1194,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; } @@ -1136,7 +1206,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) @@ -1232,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, @@ -1241,6 +1312,14 @@ 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, + .has_interface_timing_reg = false, }; static const struct tegra_i2c_hw_feature tegra30_i2c_hw = { @@ -1248,7 +1327,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, @@ -1257,6 +1337,14 @@ 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, + .has_interface_timing_reg = false, }; static const struct tegra_i2c_hw_feature tegra114_i2c_hw = { @@ -1264,7 +1352,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, @@ -1273,6 +1362,14 @@ 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, + .has_interface_timing_reg = false, }; static const struct tegra_i2c_hw_feature tegra124_i2c_hw = { @@ -1280,7 +1377,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, @@ -1289,6 +1387,14 @@ 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, + .has_interface_timing_reg = true, }; static const struct tegra_i2c_hw_feature tegra210_i2c_hw = { @@ -1296,7 +1402,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, @@ -1305,6 +1412,14 @@ 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, + .has_interface_timing_reg = true, }; static const struct tegra_i2c_hw_feature tegra186_i2c_hw = { @@ -1312,7 +1427,8 @@ static const struct tegra_i2c_hw_feature tegra186_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 = 0x16, + .clk_divisor_fast_mode = 0x19, .clk_divisor_fast_plus_mode = 0x10, .has_config_load_reg = true, .has_multi_master_mode = true, @@ -1321,6 +1437,14 @@ static const struct tegra_i2c_hw_feature tegra186_i2c_hw = { .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, + .has_interface_timing_reg = true, }; static const struct tegra_i2c_hw_feature tegra194_i2c_hw = { @@ -1328,8 +1452,9 @@ static const struct tegra_i2c_hw_feature tegra194_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_fast_plus_mode = 0x10, + .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, @@ -1337,6 +1462,14 @@ static const struct tegra_i2c_hw_feature tegra194_i2c_hw = { .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, + .has_interface_timing_reg = true, }; /* Match table for of_platform binding */ @@ -1363,7 +1496,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; @@ -1434,20 +1566,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) { @@ -1477,7 +1606,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 release_dma;