From patchwork Wed Jan 25 11:53:11 2017 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Marek Szyprowski X-Patchwork-Id: 9536911 Return-Path: Received: from mail.wl.linuxfoundation.org (pdx-wl-mail.web.codeaurora.org [172.30.200.125]) by pdx-korg-patchwork.web.codeaurora.org (Postfix) with ESMTP id 85D78604A0 for ; Wed, 25 Jan 2017 11:53:45 +0000 (UTC) Received: from mail.wl.linuxfoundation.org (localhost [127.0.0.1]) by mail.wl.linuxfoundation.org (Postfix) with ESMTP id 7593A26E94 for ; Wed, 25 Jan 2017 11:53:45 +0000 (UTC) Received: by mail.wl.linuxfoundation.org (Postfix, from userid 486) id 6A6FF27D9B; Wed, 25 Jan 2017 11:53:45 +0000 (UTC) X-Spam-Checker-Version: SpamAssassin 3.3.1 (2010-03-16) on pdx-wl-mail.web.codeaurora.org X-Spam-Level: X-Spam-Status: No, score=-6.9 required=2.0 tests=BAYES_00,RCVD_IN_DNSWL_HI autolearn=unavailable version=3.3.1 Received: from vger.kernel.org (vger.kernel.org [209.132.180.67]) by mail.wl.linuxfoundation.org (Postfix) with ESMTP id 9F62B26E94 for ; Wed, 25 Jan 2017 11:53:44 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S1751440AbdAYLxm (ORCPT ); Wed, 25 Jan 2017 06:53:42 -0500 Received: from mailout2.w1.samsung.com ([210.118.77.12]:57605 "EHLO mailout2.w1.samsung.com" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S1751418AbdAYLxb (ORCPT ); Wed, 25 Jan 2017 06:53:31 -0500 Received: from eucas1p1.samsung.com (unknown [182.198.249.206]) by mailout2.w1.samsung.com (Oracle Communications Messaging Server 7.0.5.31.0 64bit (built May 5 2014)) with ESMTP id <0OKC00EG53P5G110@mailout2.w1.samsung.com>; Wed, 25 Jan 2017 11:53:29 +0000 (GMT) Received: from eusmges3.samsung.com (unknown [203.254.199.242]) by eucas1p2.samsung.com (KnoxPortal) with ESMTP id 20170125115329eucas1p2a11b81b51b4338f37f4945dd268b0e2a~dAZkqNH901514715147eucas1p2F; Wed, 25 Jan 2017 11:53:29 +0000 (GMT) Received: from eucas1p2.samsung.com ( [182.198.249.207]) by eusmges3.samsung.com (EUCPMTA) with SMTP id C0.04.09557.9B198885; Wed, 25 Jan 2017 11:53:29 +0000 (GMT) Received: from eusmgms2.samsung.com (unknown [182.198.249.180]) by eucas1p1.samsung.com (KnoxPortal) with ESMTP id 20170125115328eucas1p1d97fac29286805b58039f8aec2b9bfe6~dAZkC3Qv-1866418664eucas1p1k; Wed, 25 Jan 2017 11:53:28 +0000 (GMT) X-AuditID: cbfec7f2-f790f6d000002555-2f-588891b9a345 Received: from eusync4.samsung.com ( [203.254.199.214]) by eusmgms2.samsung.com (EUCPMTA) with SMTP id 51.FE.10233.5B198885; Wed, 25 Jan 2017 11:53:25 +0000 (GMT) Received: from AMDC2765.digital.local ([106.116.147.25]) by eusync4.samsung.com (Oracle Communications Messaging Server 7.0.5.31.0 64bit (built May 5 2014)) with ESMTPA id <0OKC006PE3OZJVA0@eusync4.samsung.com>; Wed, 25 Jan 2017 11:53:28 +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 v5 1/4] clk: Add support for runtime PM Date: Wed, 25 Jan 2017 12:53:11 +0100 Message-id: <1485345194-3196-2-git-send-email-m.szyprowski@samsung.com> X-Mailer: git-send-email 1.9.1 In-reply-to: <1485345194-3196-1-git-send-email-m.szyprowski@samsung.com> X-Brightmail-Tracker: H4sIAAAAAAAAA+NgFnrPIsWRmVeSWpSXmKPExsWy7djP87o7J3ZEGPTe47bYOGM9q8X1L89Z LSbdn8Bicf78BnaLTY+vsVp87LnHavG59wijxYzz+5gs1h65y25x8ZSrxeE37awWP850s1gc XxvuwOvx/kYru8flvl4mj02rOtk87lzbw+axeUm9R9+WVYwenzfJBbBHcdmkpOZklqUW6dsl cGWc3bqcreC8T8XEk+fZGxgX2XYxcnJICJhITPv9kx3CFpO4cG89WxcjF4eQwFJGiVnvlzJD OJ8ZJXpmLGGF6Zj/7AoLRGIZo8S6PxOgnAYmicmL5jKBVLEJGEp0ve0CmyUi0MQoMXHDelYQ h1mgiVli2r6JYFXCAuYS8+dcBtvOIqAq0XX1HNAoDg5eAXeJK52OEOvkJE4emwy2mlPAQ2LG 9tVgcyQE1rFLPFpxiRmkXkJAVmLTAWaIeheJG62LoB4Slnh1fAuULSNxeXI3C4TdzyjR1KoN Yc9glDj3lhfCtpY4fPwi2C5mAT6JSdumQ43nlehoE4Io8ZD4evYWE4TtKDFjzglGiOdnM0rM +HWBaQKjzAJGhlWMIqmlxbnpqcXGesWJucWleel6yfm5mxiByeD0v+OfdjB+PWF1iFGAg1GJ h/dFSnuEEGtiWXFl7iFGCQ5mJRFeaWAqEeJNSaysSi3Kjy8qzUktPsQozcGiJM67Z8GVcCGB 9MSS1OzU1ILUIpgsEwenVAOj/5rfMlu1zF99c3v/6aZU0NToFz5yjH+rzCIsuBvMfz/Z7PCv 8P0Lj/nhqWUPYg5aPetZdZFRqs4+5327o/mrx+lqeb/m7A5uDF5TnvLkHU+D0rxsZ0ZDg0P7 2avDcif2Tbg88Uv6F2PR08m9TknpBodmtTst/pvzfq9Q8FKVfUqfphz0S/dWYinOSDTUYi4q TgQALEqF2gIDAAA= X-Brightmail-Tracker: H4sIAAAAAAAAA+NgFupjkeLIzCtJLcpLzFFi42I5/e/4Nd2tEzsiDBr2GlhsnLGe1eL6l+es FpPuT2CxOH9+A7vFpsfXWC0+9txjtfjce4TRYsb5fUwWa4/cZbe4eMrV4vCbdlaLH2e6WSyO rw134PV4f6OV3eNyXy+Tx6ZVnWwed67tYfPYvKTeo2/LKkaPz5vkAtij3GwyUhNTUosUUvOS 81My89JtlUJD3HQtlBTyEnNTbZUidH1DgpQUyhJzSoE8IwM04OAc4B6spG+X4JZxdutytoLz PhUTT55nb2BcZNvFyMkhIWAiMf/ZFRYIW0ziwr31bCC2kMASRoltS/27GLmA7CYmiQU/zzOB JNgEDCW63naxgSREBJoYJZ70TWMHcZgF2pgl/nd8ZQSpEhYwl5g/5zI7iM0ioCrRdfUc0AoO Dl4Bd4krnY4Q2+QkTh6bzApicwp4SMzYvpoVYrO7xOUlCxknMPIuYGRYxSiSWlqcm55bbKRX nJhbXJqXrpecn7uJERgX24793LKDsetd8CFGAQ5GJR7eCUntEUKsiWXFlbmHGCU4mJVEeB1a OiKEeFMSK6tSi/Lji0pzUosPMZoC3TSRWUo0OR8Ys3kl8YYmhuaWhkbGFhbmRkZK4rxTP1wJ FxJITyxJzU5NLUgtgulj4uCUamDMKuOIueolVrrif6rU+iX3XeesP7hYXHtijcWNUJ79kqoG i26Ezn+VWn6ptNlvVVTEvRealjEzvGf/4eubf8186pW8qyn1Djf530WKzVudJ9nYsm/BJg52 Qfu91l0iwcH38qc+fSi2ZhrjguSsY+0S1tn2hw6EXCu848Baar7V5kCXrKSlmJASS3FGoqEW c1FxIgC4/TZXoQIAAA== X-MTR: 20000000000000000@CPGS X-CMS-MailID: 20170125115328eucas1p1d97fac29286805b58039f8aec2b9bfe6 X-Msg-Generator: CA X-Sender-IP: 182.198.249.180 X-Local-Sender: =?UTF-8?B?TWFyZWsgU3p5cHJvd3NraRtTUlBPTC1LZXJuZWwgKFRQKRs=?= =?UTF-8?B?7IK87ISx7KCE7J6QG1NlbmlvciBTb2Z0d2FyZSBFbmdpbmVlcg==?= X-Global-Sender: =?UTF-8?B?TWFyZWsgU3p5cHJvd3NraRtTUlBPTC1LZXJuZWwgKFRQKRtT?= =?UTF-8?B?YW1zdW5nIEVsZWN0cm9uaWNzG1NlbmlvciBTb2Z0d2FyZSBFbmdpbmVlcg==?= X-Sender-Code: =?UTF-8?B?QzEwG0VIURtDMTBDRDAyQ0QwMjczOTI=?= CMS-TYPE: 201P X-HopCount: 7 X-CMS-RootMailID: 20170125115328eucas1p1d97fac29286805b58039f8aec2b9bfe6 X-RootMTR: 20170125115328eucas1p1d97fac29286805b58039f8aec2b9bfe6 References: <1485345194-3196-1-git-send-email-m.szyprowski@samsung.com> Sender: linux-clk-owner@vger.kernel.org Precedence: bulk List-ID: X-Mailing-List: linux-clk@vger.kernel.org X-Virus-Scanned: ClamAV using ClamSMTP 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() 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 --- drivers/clk/clk.c | 132 +++++++++++++++++++++++++++++++++++++++++++++++------- 1 file changed, 117 insertions(+), 15 deletions(-) diff --git a/drivers/clk/clk.c b/drivers/clk/clk.c index 0fb39fe217d1..5b9d52c8185b 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(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 status; + /* * .is_prepared is optional for clocks that can prepare * fall back to software usage counter if it is missing @@ -157,11 +181,20 @@ 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) == 0) { + status = core->ops->is_prepared(core->hw); + clk_pm_runtime_put(core); + } else { + status = false; + } + + return status; } static bool clk_core_is_enabled(struct clk_core *core) { + bool status; + /* * .is_enabled is only mandatory for clocks that gate * fall back to software usage counter if .is_enabled is missing @@ -169,7 +202,30 @@ 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)) { + status = false; + goto done; + } + } + + status = core->ops->is_enabled(core->hw); +done: + if (core->dev) + pm_runtime_put(core->dev); + + return status; } /*** helper functions ***/ @@ -489,6 +545,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 +588,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 +603,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 +810,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 +821,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 +838,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 +865,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 +1111,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) == 0) { + rate = core->ops->recalc_rate(core->hw, parent_rate); + clk_pm_runtime_put(core); + } + return rate; } /** @@ -1563,6 +1642,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 +1659,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 +1911,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 +1933,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 +2642,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 +2694,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 +2708,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);