From patchwork Mon Oct 24 12:12:06 2016 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Marek Szyprowski X-Patchwork-Id: 9391929 X-Patchwork-Delegate: sboyd@codeaurora.org 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 6605F6077A for ; Mon, 24 Oct 2016 12:12:42 +0000 (UTC) Received: from mail.wl.linuxfoundation.org (localhost [127.0.0.1]) by mail.wl.linuxfoundation.org (Postfix) with ESMTP id 5659B28FBB for ; Mon, 24 Oct 2016 12:12:42 +0000 (UTC) Received: by mail.wl.linuxfoundation.org (Postfix, from userid 486) id 4A50728FBE; Mon, 24 Oct 2016 12:12:42 +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=ham 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 F2C6B28FC5 for ; Mon, 24 Oct 2016 12:12:39 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S936480AbcJXMMi (ORCPT ); Mon, 24 Oct 2016 08:12:38 -0400 Received: from mailout1.w1.samsung.com ([210.118.77.11]:38422 "EHLO mailout1.w1.samsung.com" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S936266AbcJXMMb (ORCPT ); Mon, 24 Oct 2016 08:12:31 -0400 Received: from eucas1p1.samsung.com (unknown [182.198.249.206]) by mailout1.w1.samsung.com (Oracle Communications Messaging Server 7.0.5.31.0 64bit (built May 5 2014)) with ESMTP id <0OFJ001RMWKR0550@mailout1.w1.samsung.com>; Mon, 24 Oct 2016 13:12:27 +0100 (BST) Received: from eusmges3.samsung.com (unknown [203.254.199.242]) by eucas1p1.samsung.com (KnoxPortal) with ESMTP id 20161024121226eucas1p10e5b390f7feb28bac5788b377f98dc7b~AdqkvMfmH1322413224eucas1p1A; Mon, 24 Oct 2016 12:12:26 +0000 (GMT) Received: from eucas1p2.samsung.com ( [182.198.249.207]) by eusmges3.samsung.com (EUCPMTA) with SMTP id 02.9E.11330.AAAFD085; Mon, 24 Oct 2016 13:12:26 +0100 (BST) Received: from eusmgms1.samsung.com (unknown [182.198.249.179]) by eucas1p1.samsung.com (KnoxPortal) with ESMTP id 20161024121226eucas1p15f985a5780ea27bde4ec77db0054ec92~AdqkH6jHb0116901169eucas1p1X; Mon, 24 Oct 2016 12:12:26 +0000 (GMT) X-AuditID: cbfec7f2-f79556d000002c42-08-580dfaaaae17 Received: from eusync1.samsung.com ( [203.254.199.211]) by eusmgms1.samsung.com (EUCPMTA) with SMTP id C8.39.07726.D9AFD085; Mon, 24 Oct 2016 13:12:13 +0100 (BST) 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 <0OFJ00C6YWKJ1FB0@eusync1.samsung.com>; Mon, 24 Oct 2016 13:12:26 +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 v3 1/5] clk: add support for runtime pm Date: Mon, 24 Oct 2016 14:12:06 +0200 Message-id: <1477311130-6534-2-git-send-email-m.szyprowski@samsung.com> X-Mailer: git-send-email 1.9.1 In-reply-to: <1477311130-6534-1-git-send-email-m.szyprowski@samsung.com> X-Brightmail-Tracker: H4sIAAAAAAAAA+NgFnrLIsWRmVeSWpSXmKPExsWy7djP87qrfvFGGHw4LW+xccZ6VovrX56z Wky6P4HF4vz5DewWmx5fY7X42HOP1eJz7xFGixnn9zFZrD1yl93i4ilXi8Nv2lktfpzpZrE4 vjbcgdfj/Y1Wdo/Lfb1MHptWdbJ53Lm2h81j85J6j74tqxg9Pm+SC2CP4rJJSc3JLEst0rdL 4MrovLOasWCzc8Xj9+uYGxifmnYxcnBICJhInHvq1cXICWSKSVy4t56ti5GLQ0hgKaPE3RO9 TBDOZ0aJIw96GSGqTCTeHb3LDGILCSxjlPi1VBqiqIFJYlnnHlaQBJuAoUTX2y6wUSICTYwS EzesZwVxmAWamCWm7ZvIBFIlLGAuca61CWwsi4CqxNVdT9hBbF4Bd4ldb3ayQayTkzh5bDLY VE4BD4mVK5eA3SQhsIpd4vfOVhaIJ2QlNh1ghjBdJKZMFoVoFZZ4dXwLO4QtI9HZcZAJwu5n lGhq1YawZzBKnHvLC2FbSxw+fhFsFbMAn8SkbdOhRvJKdLQJQZR4SLza9wbqMkeJR9NWQUNo NqPEnD3X2CYwyixgZFjFKJJaWpybnlpsrFecmFtcmpeul5yfu4kRmAhO/zv+aQfj1xNWhxgF OBiVeHgVrvJECLEmlhVX5h5ilOBgVhLh3f6TN0KINyWxsiq1KD++qDQntfgQozQHi5I4754F V8KFBNITS1KzU1MLUotgskwcnFINjKoFSzfZP9/73CTdYo/o6r1mvkcisucc3Vm59+SsAA4r NxdOFqFEb5U/yXbVqyLTHyo8WHFOvsswwE42zSrH8dXn9PeX6udIf5SWYNLgf7++1vKQ4/Sz VwTvXojK2pjleT//3mr3FFf3hqeuKvqeRde5Xy6IOueysS7/XH+racKiGb2RP/qXKbEUZyQa ajEXFScCAM6iYUkAAwAA X-Brightmail-Tracker: H4sIAAAAAAAAA+NgFprAIsWRmVeSWpSXmKPExsVy+t/xy7pzf/FGGGybImKxccZ6VovrX56z Wky6P4HF4vz5DewWmx5fY7X42HOP1eJz7xFGixnn9zFZrD1yl93i4ilXi8Nv2lktfpzpZrE4 vjbcgdfj/Y1Wdo/Lfb1MHptWdbJ53Lm2h81j85J6j74tqxg9Pm+SC2CPcrPJSE1MSS1SSM1L zk/JzEu3VQoNcdO1UFLIS8xNtVWK0PUNCVJSKEvMKQXyjAzQgINzgHuwkr5dgltG553VjAWb nSsev1/H3MD41LSLkZNDQsBE4t3Ru8wQtpjEhXvr2boYuTiEBJYwSsz6NYERwmlikth37gwT SBWbgKFE19susCoRgSZGiSd909hBHGaBNmaJ/x1fGUGqhAXMJc61NoHZLAKqEld3PWEHsXkF 3CV2vdnJBrFPTuLkscmsIDangIfEypVLwDYIAdXc6XjNMoGRdwEjwypGkdTS4tz03GJDveLE 3OLSvHS95PzcTYzA2Nh27OfmHYyXNgYfYhTgYFTi4T1xiSdCiDWxrLgy9xCjBAezkgjv9p+8 EUK8KYmVValF+fFFpTmpxYcYTYGOmsgsJZqcD4zbvJJ4QxNDc0tDI2MLC3MjIyVx3pIPV8KF BNITS1KzU1MLUotg+pg4OKUaGBvDrPTXb/ZYw6Y/R7vn+CNB4/qoqj+B2yUj4tWUdjIz1lRI /hQp2Zn07FHF8065Wjb983eePxaOzXTf2H95p3TbqXazb/YKD2YJWfKJe5w7wXZ8YlzjDLn3 nh//sh56vvW5kLaQ99MZRxLtZp2wyWK/N/NGy6I3HaX5f415WU+5hm8Tu53ZrMRSnJFoqMVc VJwIAJvBJ4ijAgAA X-MTR: 20000000000000000@CPGS X-CMS-MailID: 20161024121226eucas1p15f985a5780ea27bde4ec77db0054ec92 X-Msg-Generator: CA X-Sender-IP: 182.198.249.179 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: 20161024121226eucas1p15f985a5780ea27bde4ec77db0054ec92 X-RootMTR: 20161024121226eucas1p15f985a5780ea27bde4ec77db0054ec92 References: <1477311130-6534-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 | 111 +++++++++++++++++++++++++++++++++++++++++++++++++----- 1 file changed, 101 insertions(+), 10 deletions(-) diff --git a/drivers/clk/clk.c b/drivers/clk/clk.c index 0fb39fe217d1..3cf202ccf5a9 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); } @@ -1563,6 +1638,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 +1655,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 +1907,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 +1929,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 +2638,8 @@ 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; if (dev && dev->driver) core->owner = dev->driver->owner; core->hw = hw;