From patchwork Fri Aug 26 13:09:04 2016 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Jon Hunter X-Patchwork-Id: 663105 X-Patchwork-Delegate: jonathanh@nvidia.com Return-Path: X-Original-To: incoming@patchwork.ozlabs.org Delivered-To: patchwork-incoming@bilbo.ozlabs.org Received: from vger.kernel.org (vger.kernel.org [209.132.180.67]) by ozlabs.org (Postfix) with ESMTP id 3sLM4r0pBPz9s4x for ; Fri, 26 Aug 2016 23:16:28 +1000 (AEST) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S1753659AbcHZNQJ (ORCPT ); Fri, 26 Aug 2016 09:16:09 -0400 Received: from hqemgate15.nvidia.com ([216.228.121.64]:16818 "EHLO hqemgate15.nvidia.com" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S1753222AbcHZNPz (ORCPT ); Fri, 26 Aug 2016 09:15:55 -0400 Received: from hqpgpgate101.nvidia.com (Not Verified[216.228.121.13]) by hqemgate15.nvidia.com id ; Fri, 26 Aug 2016 06:09:15 -0700 Received: from HQMAIL107.nvidia.com ([172.20.12.94]) by hqpgpgate101.nvidia.com (PGP Universal service); Fri, 26 Aug 2016 06:09:48 -0700 X-PGP-Universal: processed; by hqpgpgate101.nvidia.com on Fri, 26 Aug 2016 06:09:48 -0700 Received: from HQMAIL108.nvidia.com (172.18.146.13) by HQMAIL107.nvidia.com (172.20.187.13) with Microsoft SMTP Server (TLS) id 15.0.1210.3; Fri, 26 Aug 2016 13:09:43 +0000 Received: from hqnvemgw02.nvidia.com (172.16.227.111) by HQMAIL108.nvidia.com (172.18.146.13) with Microsoft SMTP Server id 15.0.1210.3 via Frontend Transport; Fri, 26 Aug 2016 13:09:43 +0000 Received: from jonathanh-lm.nvidia.com (Not Verified[10.26.11.251]) by hqnvemgw02.nvidia.com with Trustwave SEG (v7, 5, 5, 8150) id ; Fri, 26 Aug 2016 06:09:42 -0700 From: Jon Hunter To: Laxman Dewangan , Wolfram Sang CC: Stephen Warren , Thierry Reding , Alexandre Courbot , , , , Jon Hunter Subject: [PATCH V2 8/9] i2c: tegra: Add runtime power-management support Date: Fri, 26 Aug 2016 14:09:04 +0100 Message-ID: <1472216945-11818-9-git-send-email-jonathanh@nvidia.com> X-Mailer: git-send-email 2.1.4 In-Reply-To: <1472216945-11818-1-git-send-email-jonathanh@nvidia.com> References: <1472216945-11818-1-git-send-email-jonathanh@nvidia.com> X-NVConfidentiality: public MIME-Version: 1.0 Sender: linux-tegra-owner@vger.kernel.org Precedence: bulk List-ID: X-Mailing-List: linux-tegra@vger.kernel.org Update the Tegra I2C driver to use runtime PM and move the code in the tegra_i2c_clock_enable/disable() functions to the PM runtime resume and suspend callbacks, respectively. Note that given that CONFIG_PM is not mandatory for Tegra, if CONFIG_PM is not enabled and so runtime PM is not enabled, ensure that the I2C clocks are turned on during probe and kept on by calling the resume callback directly. In the function tegra_i2c_init(), the variable 'err' does not need to be initialised to zero in tegra_i2c_init() because it is initialised when pm_runtime_get_sync() is called. Furthermore, to ensure we only return 0 from tegra_i2c_init(), it is necessary to re-initialise 'err' to 0 after a successful call to pm_runtime_get_sync() because it can return a positive value on success. However, alternatively re-initialise 'err' by using the return value of the function tegra_i2c_flush_fifos() because it can only be 0 or -ETIMEDOUT. Signed-off-by: Jon Hunter Acked-by: Laxman Dewangan --- drivers/i2c/busses/i2c-tegra.c | 60 ++++++++++++++++++++++++++++++++---------- 1 file changed, 46 insertions(+), 14 deletions(-) diff --git a/drivers/i2c/busses/i2c-tegra.c b/drivers/i2c/busses/i2c-tegra.c index 3c27012fa96c..05e34dc29d5a 100644 --- a/drivers/i2c/busses/i2c-tegra.c +++ b/drivers/i2c/busses/i2c-tegra.c @@ -28,6 +28,7 @@ #include #include #include +#include #include @@ -401,8 +402,9 @@ static void tegra_dvc_init(struct tegra_i2c_dev *i2c_dev) dvc_writel(i2c_dev, val, DVC_CTRL_REG1); } -static inline int tegra_i2c_clock_enable(struct tegra_i2c_dev *i2c_dev) +static int tegra_i2c_runtime_resume(struct device *dev) { + struct tegra_i2c_dev *i2c_dev = dev_get_drvdata(dev); int ret; if (!i2c_dev->hw->has_single_clk_source) { @@ -413,32 +415,39 @@ static inline int tegra_i2c_clock_enable(struct tegra_i2c_dev *i2c_dev) return ret; } } + ret = clk_enable(i2c_dev->div_clk); if (ret < 0) { dev_err(i2c_dev->dev, "Enabling div clk failed, err %d\n", ret); clk_disable(i2c_dev->fast_clk); + return ret; } - return ret; + + return 0; } -static inline void tegra_i2c_clock_disable(struct tegra_i2c_dev *i2c_dev) +static int tegra_i2c_runtime_suspend(struct device *dev) { + struct tegra_i2c_dev *i2c_dev = dev_get_drvdata(dev); + clk_disable(i2c_dev->div_clk); if (!i2c_dev->hw->has_single_clk_source) clk_disable(i2c_dev->fast_clk); + + return 0; } static int tegra_i2c_init(struct tegra_i2c_dev *i2c_dev) { u32 val; - int err = 0; + int err; u32 clk_divisor; unsigned long timeout = jiffies + HZ; - err = tegra_i2c_clock_enable(i2c_dev); + err = pm_runtime_get_sync(i2c_dev->dev); if (err < 0) { - dev_err(i2c_dev->dev, "Clock enable failed %d\n", err); + dev_err(i2c_dev->dev, "runtime resume failed %d\n", err); return err; } @@ -477,8 +486,7 @@ static int tegra_i2c_init(struct tegra_i2c_dev *i2c_dev) 0 << I2C_FIFO_CONTROL_RX_TRIG_SHIFT; i2c_writel(i2c_dev, val, I2C_FIFO_CONTROL); - if (tegra_i2c_flush_fifos(i2c_dev)) - err = -ETIMEDOUT; + err = tegra_i2c_flush_fifos(i2c_dev); if (i2c_dev->is_multimaster_mode && i2c_dev->hw->has_slcg_override_reg) i2c_writel(i2c_dev, I2C_MST_CORE_CLKEN_OVR, I2C_CLKEN_OVERRIDE); @@ -502,7 +510,7 @@ static int tegra_i2c_init(struct tegra_i2c_dev *i2c_dev) } err: - tegra_i2c_clock_disable(i2c_dev); + pm_runtime_put(i2c_dev->dev); return err; } @@ -677,9 +685,9 @@ static int tegra_i2c_xfer(struct i2c_adapter *adap, struct i2c_msg msgs[], if (i2c_dev->is_suspended) return -EBUSY; - ret = tegra_i2c_clock_enable(i2c_dev); + ret = pm_runtime_get_sync(i2c_dev->dev); if (ret < 0) { - dev_err(i2c_dev->dev, "Clock enable failed %d\n", ret); + dev_err(i2c_dev->dev, "runtime resume failed %d\n", ret); return ret; } @@ -696,7 +704,9 @@ static int tegra_i2c_xfer(struct i2c_adapter *adap, struct i2c_msg msgs[], if (ret) break; } - tegra_i2c_clock_disable(i2c_dev); + + pm_runtime_put(i2c_dev->dev); + return ret ?: i; } @@ -902,12 +912,21 @@ static int tegra_i2c_probe(struct platform_device *pdev) goto unprepare_fast_clk; } + pm_runtime_enable(&pdev->dev); + if (!pm_runtime_enabled(&pdev->dev)) { + ret = tegra_i2c_runtime_resume(&pdev->dev); + if (ret < 0) { + dev_err(&pdev->dev, "runtime resume failed\n"); + goto unprepare_div_clk; + } + } + if (i2c_dev->is_multimaster_mode) { ret = clk_enable(i2c_dev->div_clk); if (ret < 0) { dev_err(i2c_dev->dev, "div_clk enable failed %d\n", ret); - goto unprepare_div_clk; + goto disable_rpm; } } @@ -943,6 +962,11 @@ disable_div_clk: if (i2c_dev->is_multimaster_mode) clk_disable(i2c_dev->div_clk); +disable_rpm: + pm_runtime_disable(&pdev->dev); + if (!pm_runtime_status_suspended(&pdev->dev)) + tegra_i2c_runtime_suspend(&pdev->dev); + unprepare_div_clk: clk_unprepare(i2c_dev->div_clk); @@ -962,6 +986,10 @@ static int tegra_i2c_remove(struct platform_device *pdev) if (i2c_dev->is_multimaster_mode) clk_disable(i2c_dev->div_clk); + pm_runtime_disable(&pdev->dev); + if (!pm_runtime_status_suspended(&pdev->dev)) + tegra_i2c_runtime_suspend(&pdev->dev); + clk_unprepare(i2c_dev->div_clk); if (!i2c_dev->hw->has_single_clk_source) clk_unprepare(i2c_dev->fast_clk); @@ -997,7 +1025,11 @@ static int tegra_i2c_resume(struct device *dev) return ret; } -static SIMPLE_DEV_PM_OPS(tegra_i2c_pm, tegra_i2c_suspend, tegra_i2c_resume); +static const struct dev_pm_ops tegra_i2c_pm = { + SET_RUNTIME_PM_OPS(tegra_i2c_runtime_suspend, tegra_i2c_runtime_resume, + NULL) + SET_SYSTEM_SLEEP_PM_OPS(tegra_i2c_suspend, tegra_i2c_resume) +}; #define TEGRA_I2C_PM (&tegra_i2c_pm) #else #define TEGRA_I2C_PM NULL