From patchwork Wed Mar 22 11:35:40 2017 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Marek Szyprowski X-Patchwork-Id: 95711 Delivered-To: patch@linaro.org Received: by 10.140.89.233 with SMTP id v96csp188337qgd; Wed, 22 Mar 2017 04:39:35 -0700 (PDT) X-Received: by 10.99.167.74 with SMTP id w10mr43860917pgo.2.1490182775812; Wed, 22 Mar 2017 04:39:35 -0700 (PDT) Return-Path: Received: from vger.kernel.org (vger.kernel.org. [209.132.180.67]) by mx.google.com with ESMTP id 189si1511825pgh.105.2017.03.22.04.39.35; Wed, 22 Mar 2017 04:39:35 -0700 (PDT) Received-SPF: pass (google.com: best guess record for domain of linux-pm-owner@vger.kernel.org designates 209.132.180.67 as permitted sender) client-ip=209.132.180.67; Authentication-Results: mx.google.com; spf=pass (google.com: best guess record for domain of linux-pm-owner@vger.kernel.org designates 209.132.180.67 as permitted sender) smtp.mailfrom=linux-pm-owner@vger.kernel.org Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S1758830AbdCVLje (ORCPT + 13 others); Wed, 22 Mar 2017 07:39:34 -0400 Received: from mailout4.w1.samsung.com ([210.118.77.14]:63795 "EHLO mailout4.w1.samsung.com" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S1758843AbdCVLjd (ORCPT ); Wed, 22 Mar 2017 07:39:33 -0400 Received: from eucas1p2.samsung.com (unknown [182.198.249.207]) by mailout4.w1.samsung.com (Oracle Communications Messaging Server 7.0.5.31.0 64bit (built May 5 2014)) with ESMTP id <0ON70068MS7VIP20@mailout4.w1.samsung.com>; Wed, 22 Mar 2017 11:35:55 +0000 (GMT) Received: from eusmges1.samsung.com (unknown [203.254.199.239]) by eucas1p2.samsung.com (KnoxPortal) with ESMTP id 20170322113555eucas1p22a31369fe19ce33553c485fa5a4247dd~uMSOLyItC2778827788eucas1p2y; Wed, 22 Mar 2017 11:35:55 +0000 (GMT) Received: from eucas1p2.samsung.com ( [182.198.249.207]) by eusmges1.samsung.com (EUCPMTA) with SMTP id 6B.81.16908.D9162D85; Wed, 22 Mar 2017 11:35:57 +0000 (GMT) Received: from eusmgms2.samsung.com (unknown [182.198.249.180]) by eucas1p2.samsung.com (KnoxPortal) with ESMTP id 20170322113554eucas1p27471f0de278c55d5dce7fdf120395e09~uMSNeqjce1589415894eucas1p2R; Wed, 22 Mar 2017 11:35:54 +0000 (GMT) X-AuditID: cbfec7ef-f79d26d00000420c-b4-58d2619d4027 Received: from eusync1.samsung.com ( [203.254.199.211]) by eusmgms2.samsung.com (EUCPMTA) with SMTP id 47.D4.10233.9A162D85; Wed, 22 Mar 2017 11:36:09 +0000 (GMT) Received: from AMDC2765.digital.local ([106.116.147.25]) by eusync1.samsung.com (Oracle Communications Messaging Server 7.0.5.31.0 64bit (built May 5 2014)) with ESMTPA id <0ON7005SKS7QHC90@eusync1.samsung.com>; Wed, 22 Mar 2017 11:35:54 +0000 (GMT) From: Marek Szyprowski To: linux-clk@vger.kernel.org, linux-pm@vger.kernel.org, linux-samsung-soc@vger.kernel.org, linux-arm-kernel@lists.infradead.org Cc: Marek Szyprowski , Stephen Boyd , Michael Turquette , Ulf Hansson , Sylwester Nawrocki , Chanwoo Choi , Inki Dae , Krzysztof Kozlowski , Bartlomiej Zolnierkiewicz Subject: [PATCH v6 1/4] clk: Add support for runtime PM Date: Wed, 22 Mar 2017 12:35:40 +0100 Message-id: <1490182543-28550-2-git-send-email-m.szyprowski@samsung.com> X-Mailer: git-send-email 1.9.1 In-reply-to: <1490182543-28550-1-git-send-email-m.szyprowski@samsung.com> X-Brightmail-Tracker: H4sIAAAAAAAAA+NgFnrPIsWRmVeSWpSXmKPExsWy7djP87pzEy9FGLT0mVtsnLGe1eL6l+es FpPuT2CxOH9+A7vFpsfXWC0+9txjtfjce4TRYsb5fUwWa4/cZbe4eMrV4vCbdlaLH2e6WSyO rw134PV4f6OV3eNyXy+Tx6ZVnWwed67tYfPYvKTeo2/LKkaPz5vkAtijuGxSUnMyy1KL9O0S uDI+7GtiLzjtU3Fm8jK2BsaVtl2MnBwSAiYSLx9PZoWwxSQu3FvP1sXIxSEksIxRYktDPyuE 85lR4vPRfSwwHXd+TEKoWv5uGyOE08Akcf7RJrAqNgFDia63XWBVIgJNjBITN6wHm8Us0MQs MW3fRCaQKmEBc4kJC54yg9gsAqoSx1/9YAOxeQU8JGa374a6Sk7i5DGICzkFPCXmfp7KAjJI QmAZu8SUu/eBmjmAHFmJTQegTBeJid+TIFqFJV4d38IOYctIXJ7cDfVCP6NEU6s2hD2DUeLc W14I21ri8PGLYKuYBfgkJm2bDjWSV6KjTQiixEOi7fwPqMscJZZOfMwM8fwcRokXi38xTmCU WcDIsIpRJLW0ODc9tdhQrzgxt7g0L10vOT93EyMwGZz+d/z9DsanzSGHGAU4GJV4eBXkL0YI sSaWFVfmHmKU4GBWEuFdFnEpQog3JbGyKrUoP76oNCe1+BCjNAeLkjjv3gVXwoUE0hNLUrNT UwtSi2CyTBycUg2M/rzNxc1Wse189d6MH7f2nc0XSy6wWH9rUv3768s971ysmrtZ4ulR/wy/ DXwCOTIFM9Pffirlq3bgvRB6/ZnDqvpD2y4Ja0gV3uCeX5TokrOqfnbU0sm8Jfu+ZiZ+cdia UmSyfsXGbXnRbNskoxZXye1aH1Pxu7fFZe6L5nfPEh896j7nH79RiaU4I9FQi7moOBEAe54T xAIDAAA= X-Brightmail-Tracker: H4sIAAAAAAAAA+NgFprAIsWRmVeSWpSXmKPExsVy+t/xy7orEy9FGHxdZGCxccZ6VovrX56z Wky6P4HF4vz5DewWmx5fY7X42HOP1eJz7xFGixnn9zFZrD1yl93i4ilXi8Nv2lktfpzpZrE4 vjbcgdfj/Y1Wdo/Lfb1MHptWdbJ53Lm2h81j85J6j74tqxg9Pm+SC2CPcrPJSE1MSS1SSM1L zk/JzEu3VQoNcdO1UFLIS8xNtVWK0PUNCVJSKEvMKQXyjAzQgINzgHuwkr5dglvGh31N7AWn fSrOTF7G1sC40raLkZNDQsBE4s6PSWwQtpjEhXvrgWwuDiGBJYwSv5Z2QDlNTBKNe64wgVSx CRhKdL3tAkuICDQxSjzpm8YO4jALtDFL/O/4yghSJSxgLjFhwVNmEJtFQFXi+KsfYDt4BTwk ZrfvZoXYJydx8thkMJtTwFNi7uepLCC2EFDN4zmzmSYw8i5gZFjFKJJaWpybnltspFecmFtc mpeul5yfu4kRGBvbjv3csoOx613wIUYBDkYlHl4F+YsRQqyJZcWVuYcYJTiYlUR4l0VcihDi TUmsrEotyo8vKs1JLT7EaAp01ERmKdHkfGDc5pXEG5oYmlsaGhlbWJgbGSmJ8079cCVcSCA9 sSQ1OzW1ILUIpo+Jg1OqgZFjXuZi5ZBjxyVqW14Lhye4qfx64j439L3U4vipCyd7FH059P6F weVTEy5mdDlvCYiWZW0x3dAatrmFb/OKJNfN3Qv79KfZlx994WXJ/q7eTdza9NOvW3/lAqvj 7A1fXKveuE6xkDvWmIeZ/TKLpZ55+r7l5xhshRd+EOfS2XlLc1uqVHdOqxJLcUaioRZzUXEi AIJZgnyjAgAA X-MTR: 20000000000000000@CPGS X-CMS-MailID: 20170322113554eucas1p27471f0de278c55d5dce7fdf120395e09 X-Msg-Generator: CA X-Sender-IP: 182.198.249.180 X-Local-Sender: =?utf-8?q?Marek_Szyprowski=1BSRPOL-Kernel_=28TP=29=1B?= =?utf-8?b?7IK87ISx7KCE7J6QG1NlbmlvciBTb2Z0d2FyZSBFbmdpbmVlcg==?= X-Global-Sender: =?utf-8?q?Marek_Szyprowski=1BSRPOL-Kernel_=28TP=29=1BSam?= =?utf-8?q?sung_Electronics=1BSenior_Software_Engineer?= X-Sender-Code: =?utf-8?q?C10=1BEHQ=1BC10CD02CD027392?= CMS-TYPE: 201P X-HopCount: 7 X-CMS-RootMailID: 20170322113554eucas1p27471f0de278c55d5dce7fdf120395e09 X-RootMTR: 20170322113554eucas1p27471f0de278c55d5dce7fdf120395e09 References: <1490182543-28550-1-git-send-email-m.szyprowski@samsung.com> Sender: linux-pm-owner@vger.kernel.org Precedence: bulk List-ID: X-Mailing-List: linux-pm@vger.kernel.org Registers for some clocks might be located in the SOC area, which are under the power domain. To enable access to those registers respective domain has to be turned on. Additionally, registers for such clocks will usually loose its contents when power domain is turned off, so additional saving and restoring of them might be needed in the clock controller driver. This patch adds basic infrastructure in the clocks core to allow implementing driver for such clocks under power domains. Clock provider can supply a struct device pointer, which is the used by clock core for tracking and managing clock's controller runtime pm state. Each clk_prepare() operation will first call pm_runtime_get_sync() on the supplied device, while clk_unprepare() will do pm_runtime_put_sync() at the end. Additional calls to pm_runtime_get/put functions are required to ensure that any register access (like calculating/changing clock rates and unpreparing/disabling unused clocks on boot) will be done with clock controller in runtime resumend state. When one wants to register clock controller, which make use of this feature, he has to: 1. Provide a struct device to the core when registering the provider. 2. Ensure to enable runtime PM for that device before registering clocks. 3. Make sure that the runtime PM status of the controller device reflects the HW state. Signed-off-by: Marek Szyprowski Reviewed-by: Ulf Hansson --- drivers/clk/clk.c | 129 +++++++++++++++++++++++++++++++++++++++++++++++------- 1 file changed, 114 insertions(+), 15 deletions(-) -- 1.9.1 diff --git a/drivers/clk/clk.c b/drivers/clk/clk.c index 0fb39fe217d1..a6001b6e49a7 100644 --- a/drivers/clk/clk.c +++ b/drivers/clk/clk.c @@ -21,6 +21,7 @@ #include #include #include +#include #include #include @@ -46,6 +47,7 @@ struct clk_core { const struct clk_ops *ops; struct clk_hw *hw; struct module *owner; + struct device *dev; struct clk_core *parent; const char **parent_names; struct clk_core **parents; @@ -87,6 +89,26 @@ struct clk { struct hlist_node clks_node; }; +/*** runtime pm ***/ +static int clk_pm_runtime_get(struct clk_core *core) +{ + int ret = 0; + + if (!core->dev) + return 0; + + ret = pm_runtime_get_sync(core->dev); + return ret < 0 ? ret : 0; +} + +static void clk_pm_runtime_put(struct clk_core *core) +{ + if (!core->dev) + return; + + pm_runtime_put_sync(core->dev); +} + /*** locking ***/ static void clk_prepare_lock(void) { @@ -150,6 +172,8 @@ static void clk_enable_unlock(unsigned long flags) static bool clk_core_is_prepared(struct clk_core *core) { + bool ret = false; + /* * .is_prepared is optional for clocks that can prepare * fall back to software usage counter if it is missing @@ -157,11 +181,18 @@ static bool clk_core_is_prepared(struct clk_core *core) if (!core->ops->is_prepared) return core->prepare_count; - return core->ops->is_prepared(core->hw); + if (!clk_pm_runtime_get(core)) { + ret = core->ops->is_prepared(core->hw); + clk_pm_runtime_put(core); + } + + return ret; } static bool clk_core_is_enabled(struct clk_core *core) { + bool ret = false; + /* * .is_enabled is only mandatory for clocks that gate * fall back to software usage counter if .is_enabled is missing @@ -169,7 +200,29 @@ static bool clk_core_is_enabled(struct clk_core *core) if (!core->ops->is_enabled) return core->enable_count; - return core->ops->is_enabled(core->hw); + /* + * Check if clock controller's device is runtime active before + * calling .is_enabled callback. If not, assume that clock is + * disabled, because we might be called from atomic context, from + * which pm_runtime_get() is not allowed. + * This function is called mainly from clk_disable_unused_subtree, + * which ensures proper runtime pm activation of controller before + * taking enable spinlock, but the below check is needed if one tries + * to call it from other places. + */ + if (core->dev) { + pm_runtime_get_noresume(core->dev); + if (!pm_runtime_active(core->dev)) { + ret = false; + goto done; + } + } + + ret = core->ops->is_enabled(core->hw); +done: + clk_pm_runtime_put(core); + + return ret; } /*** helper functions ***/ @@ -489,6 +542,8 @@ static void clk_core_unprepare(struct clk_core *core) if (core->ops->unprepare) core->ops->unprepare(core->hw); + clk_pm_runtime_put(core); + trace_clk_unprepare_complete(core); clk_core_unprepare(core->parent); } @@ -530,10 +585,14 @@ static int clk_core_prepare(struct clk_core *core) return 0; if (core->prepare_count == 0) { - ret = clk_core_prepare(core->parent); + ret = clk_pm_runtime_get(core); if (ret) return ret; + ret = clk_core_prepare(core->parent); + if (ret) + goto runtime_put; + trace_clk_prepare(core); if (core->ops->prepare) @@ -541,15 +600,18 @@ static int clk_core_prepare(struct clk_core *core) trace_clk_prepare_complete(core); - if (ret) { - clk_core_unprepare(core->parent); - return ret; - } + if (ret) + goto unprepare; } core->prepare_count++; return 0; +unprepare: + clk_core_unprepare(core->parent); +runtime_put: + clk_pm_runtime_put(core); + return ret; } static int clk_core_prepare_lock(struct clk_core *core) @@ -745,6 +807,9 @@ static void clk_unprepare_unused_subtree(struct clk_core *core) if (core->flags & CLK_IGNORE_UNUSED) return; + if (clk_pm_runtime_get(core)) + return; + if (clk_core_is_prepared(core)) { trace_clk_unprepare(core); if (core->ops->unprepare_unused) @@ -753,6 +818,8 @@ static void clk_unprepare_unused_subtree(struct clk_core *core) core->ops->unprepare(core->hw); trace_clk_unprepare_complete(core); } + + clk_pm_runtime_put(core); } static void clk_disable_unused_subtree(struct clk_core *core) @@ -768,6 +835,9 @@ static void clk_disable_unused_subtree(struct clk_core *core) if (core->flags & CLK_OPS_PARENT_ENABLE) clk_core_prepare_enable(core->parent); + if (clk_pm_runtime_get(core)) + goto unprepare_out; + flags = clk_enable_lock(); if (core->enable_count) @@ -792,6 +862,8 @@ static void clk_disable_unused_subtree(struct clk_core *core) unlock_out: clk_enable_unlock(flags); + clk_pm_runtime_put(core); +unprepare_out: if (core->flags & CLK_OPS_PARENT_ENABLE) clk_core_disable_unprepare(core->parent); } @@ -1036,9 +1108,13 @@ long clk_get_accuracy(struct clk *clk) static unsigned long clk_recalc(struct clk_core *core, unsigned long parent_rate) { - if (core->ops->recalc_rate) - return core->ops->recalc_rate(core->hw, parent_rate); - return parent_rate; + unsigned long rate = parent_rate; + + if (core->ops->recalc_rate && !clk_pm_runtime_get(core)) { + rate = core->ops->recalc_rate(core->hw, parent_rate); + clk_pm_runtime_put(core); + } + return rate; } /** @@ -1563,6 +1639,7 @@ static int clk_core_set_rate_nolock(struct clk_core *core, { struct clk_core *top, *fail_clk; unsigned long rate = req_rate; + int ret = 0; if (!core) return 0; @@ -1579,21 +1656,28 @@ static int clk_core_set_rate_nolock(struct clk_core *core, if (!top) return -EINVAL; + ret = clk_pm_runtime_get(core); + if (ret) + return ret; + /* notify that we are about to change rates */ fail_clk = clk_propagate_rate_change(top, PRE_RATE_CHANGE); if (fail_clk) { pr_debug("%s: failed to set %s rate\n", __func__, fail_clk->name); clk_propagate_rate_change(top, ABORT_RATE_CHANGE); - return -EBUSY; + ret = -EBUSY; + goto err; } /* change the rates */ clk_change_rate(top); core->req_rate = req_rate; +err: + clk_pm_runtime_put(core); - return 0; + return ret; } /** @@ -1824,12 +1908,16 @@ static int clk_core_set_parent(struct clk_core *core, struct clk_core *parent) p_rate = parent->rate; } + ret = clk_pm_runtime_get(core); + if (ret) + goto out; + /* propagate PRE_RATE_CHANGE notifications */ ret = __clk_speculate_rates(core, p_rate); /* abort if a driver objects */ if (ret & NOTIFY_STOP_MASK) - goto out; + goto runtime_put; /* do the re-parent */ ret = __clk_set_parent(core, parent, p_index); @@ -1842,6 +1930,8 @@ static int clk_core_set_parent(struct clk_core *core, struct clk_core *parent) __clk_recalc_accuracies(core); } +runtime_put: + clk_pm_runtime_put(core); out: clk_prepare_unlock(); @@ -2549,6 +2639,12 @@ struct clk *clk_register(struct device *dev, struct clk_hw *hw) goto fail_name; } core->ops = hw->init->ops; + if (dev && pm_runtime_enabled(dev)) { + core->dev = dev; + ret = clk_pm_runtime_get(core); + if (ret) + goto fail_pm; + } if (dev && dev->driver) core->owner = dev->driver->owner; core->hw = hw; @@ -2595,12 +2691,13 @@ struct clk *clk_register(struct device *dev, struct clk_hw *hw) } ret = __clk_core_init(core); - if (!ret) + if (!ret) { + clk_pm_runtime_put(core); return hw->clk; + } __clk_free_clk(hw->clk); hw->clk = NULL; - fail_parents: kfree(core->parents); fail_parent_names_copy: @@ -2608,6 +2705,8 @@ struct clk *clk_register(struct device *dev, struct clk_hw *hw) kfree_const(core->parent_names[i]); kfree(core->parent_names); fail_parent_names: + clk_pm_runtime_put(core); +fail_pm: kfree_const(core->name); fail_name: kfree(core);