From patchwork Wed Aug 9 10:35:03 2017 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Marek Szyprowski X-Patchwork-Id: 9890267 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 8A01560363 for ; Wed, 9 Aug 2017 10:35:26 +0000 (UTC) Received: from mail.wl.linuxfoundation.org (localhost [127.0.0.1]) by mail.wl.linuxfoundation.org (Postfix) with ESMTP id 9973E289BB for ; Wed, 9 Aug 2017 10:35:26 +0000 (UTC) Received: by mail.wl.linuxfoundation.org (Postfix, from userid 486) id 8E0B828A49; Wed, 9 Aug 2017 10:35:26 +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 D8AEB289BB for ; Wed, 9 Aug 2017 10:35:25 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S1752191AbdHIKfX (ORCPT ); Wed, 9 Aug 2017 06:35:23 -0400 Received: from mailout4.w1.samsung.com ([210.118.77.14]:42277 "EHLO mailout4.w1.samsung.com" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S1751890AbdHIKfR (ORCPT ); Wed, 9 Aug 2017 06:35:17 -0400 Received: from eucas1p1.samsung.com (unknown [182.198.249.206]) by mailout4.w1.samsung.com (Oracle Communications Messaging Server 7.0.5.31.0 64bit (built May 5 2014)) with ESMTP id <0OUE002Z8YQQM860@mailout4.w1.samsung.com>; Wed, 09 Aug 2017 11:35:14 +0100 (BST) Received: from eusmges5.samsung.com (unknown [203.254.199.245]) by eucas1p2.samsung.com (KnoxPortal) with ESMTP id 20170809103513eucas1p268fa00c7a0ce7a1c767932e34bd3a7b8~ZJxMWbsJs2192121921eucas1p2K; Wed, 9 Aug 2017 10:35:13 +0000 (GMT) Received: from eucas1p1.samsung.com ( [182.198.249.206]) by eusmges5.samsung.com (EUCPMTA) with SMTP id 65.46.12743.165EA895; Wed, 9 Aug 2017 11:35:13 +0100 (BST) Received: from eusmgms2.samsung.com (unknown [182.198.249.180]) by eucas1p2.samsung.com (KnoxPortal) with ESMTP id 20170809103512eucas1p2eb66401b357d82bed91182a5a1b1fea9~ZJxLt19EJ2192821928eucas1p2L; Wed, 9 Aug 2017 10:35:12 +0000 (GMT) X-AuditID: cbfec7f5-f79d06d0000031c7-43-598ae561f40f Received: from eusync2.samsung.com ( [203.254.199.212]) by eusmgms2.samsung.com (EUCPMTA) with SMTP id DC.75.20118.065EA895; Wed, 9 Aug 2017 11:35:12 +0100 (BST) Received: from AMDC2765.digital.local ([106.116.147.25]) by eusync2.samsung.com (Oracle Communications Messaging Server 7.0.5.31.0 64bit (built May 5 2014)) with ESMTPA id <0OUE00IH6YQKHB00@eusync2.samsung.com>; Wed, 09 Aug 2017 11:35:12 +0100 (BST) 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 v8 1/5] clk: Add support for runtime PM Date: Wed, 09 Aug 2017 12:35:03 +0200 Message-id: <1502274907-11931-2-git-send-email-m.szyprowski@samsung.com> X-Mailer: git-send-email 1.9.1 In-reply-to: <1502274907-11931-1-git-send-email-m.szyprowski@samsung.com> X-Brightmail-Tracker: H4sIAAAAAAAAA+NgFnrPIsWRmVeSWpSXmKPExsWy7djPc7qJT7siDRZtFbPYOGM9q8X1L89Z LSbdn8Bicf78BnaLTY+vsVp87LnHavG59wijxYzz+5gs1h65y25x8ZSrxeE37awWP850s1gc XxvuwOvx/kYru8flvl4mj02rOtk87lzbw+axeUm9R9+WVYwenzfJBbBHcdmkpOZklqUW6dsl cGXMb/zKUvDHp2Jyz3bmBsartl2MnBwSAiYS3afPsUDYYhIX7q1n62Lk4hASWMoosfnGPWYI 5zOjxPf9O4CqOMA6lv5JgIgvY5S4u/M6K4TTwCSxetMJsFFsAoYSXW+7wEaJCDQxSkzcsB6s ilmgiVli2r6JTCBVwgLmEjdvbwHrYBFQlVi+dgFYnFfAQ+LrjsnsEEfJSZw8NpkVxOYU8JRY P+cW2E0SAuvYJfqmNEDdJCux6QAzRL2LxPGF2xghbGGJV8e3QM2Rkbg8uRvq0X5GiaZWbQh7 BqPEube8ELa1xOHjF8F2MQvwSUzaNp0ZYjyvREebEESJh8T295ugxjhKrJ6/ngXi+zmMEge2 HmGfwCizgJFhFaNIamlxbnpqsalecWJucWleul5yfu4mRmAyOP3v+NcdjEuPWR1iFOBgVOLh vbGnM1KINbGsuDL3EKMEB7OSCO/zx12RQrwpiZVVqUX58UWlOanFhxilOViUxHlto9oihQTS E0tSs1NTC1KLYLJMHJxSDYxS57xz0xTs2kJcAntkCiqmbdn8remhyuaVrvq73zCHL1ocV7t1 4e388J+1+67U6cpoV7VeKtWs+v8kcs3jNa8cAratXHrbc1/A3IydiorPZdQvm/1a2/oytrWm UfXN/OnMzNu6DJ8f5FXZnzXfeUJqM8vuk1Uml70trXTe/So347+938PkxQElluKMREMt5qLi RAA6wXAfAgMAAA== X-Brightmail-Tracker: H4sIAAAAAAAAA+NgFupnkeLIzCtJLcpLzFFi42I5/e/4Fd2Ep12RBvPuW1hsnLGe1eL6l+es FpPuT2CxOH9+A7vFpsfXWC0+9txjtfjce4TRYsb5fUwWa4/cZbe4eMrV4vCbdlaLH2e6WSyO rw134PV4f6OV3eNyXy+Tx6ZVnWwed67tYfPYvKTeo2/LKkaPz5vkAtij3GwyUhNTUosUUvOS 81My89JtlUJD3HQtlBTyEnNTbZUidH1DgpQUyhJzSoE8IwM04OAc4B6spG+X4JYxv/ErS8Ef n4rJPduZGxiv2nYxcnBICJhILP2T0MXICWSKSVy4t56ti5GLQ0hgCaPEu+b5jBBOE5PE7Ltz GUGq2AQMJbredoFViQg0MUo86ZvGDuIwC7QxS/zv+ApWJSxgLnHz9hYWEJtFQFVi+doFTCA2 r4CHxNcdk9kh9slJnDw2mRXE5hTwlFg/5xYziC0EVHPv2CaWCYy8CxgZVjGKpJYW56bnFhvp FSfmFpfmpesl5+duYgRGxrZjP7fsYOx6F3yIUYCDUYmH98aezkgh1sSy4srcQ4wSHMxKIrxi wLgS4k1JrKxKLcqPLyrNSS0+xGgKdNREZinR5Hxg1OaVxBuaGJpbGhoZW1iYGxkpifOqX26K FBJITyxJzU5NLUgtgulj4uCUamAM/iXp/HDruW83ta9UNWw6KHhfWX5d1pzpJ8x6VpmfMqp9 IvHFKmr/zjsyL8X3Mr4Vu1XwuejuN++MY0KmupP/VTj8+xdR5qSgtW7mPveFKxXiyrhTVOYo G715ltI38bGXzlK38ixzN6EtLks9nvefeau8cMcH9QOm3rVXJppuPtErkJL92fmtEktxRqKh FnNRcSIAqRm9KaICAAA= X-MTR: 20000000000000000@CPGS X-CMS-MailID: 20170809103512eucas1p2eb66401b357d82bed91182a5a1b1fea9 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: 20170809103512eucas1p2eb66401b357d82bed91182a5a1b1fea9 X-RootMTR: 20170809103512eucas1p2eb66401b357d82bed91182a5a1b1fea9 References: <1502274907-11931-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 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_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 Acked-by: Krzysztof Kozlowski --- drivers/clk/clk.c | 129 +++++++++++++++++++++++++++++++++++++++++++++++------- 1 file changed, 114 insertions(+), 15 deletions(-) diff --git a/drivers/clk/clk.c b/drivers/clk/clk.c index fc58c52a26b4..eb11a6a0e1d0 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); } @@ -1038,9 +1110,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; } /** @@ -1565,6 +1641,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; @@ -1581,21 +1658,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; } /** @@ -1826,12 +1910,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); @@ -1844,6 +1932,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(); @@ -2583,6 +2673,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; @@ -2629,12 +2725,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: @@ -2642,6 +2739,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);