From patchwork Mon Sep 19 10:55:21 2016 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Marek Szyprowski X-Patchwork-Id: 9338969 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 7624A607D0 for ; Mon, 19 Sep 2016 10:55:57 +0000 (UTC) Received: from mail.wl.linuxfoundation.org (localhost [127.0.0.1]) by mail.wl.linuxfoundation.org (Postfix) with ESMTP id 6612129125 for ; Mon, 19 Sep 2016 10:55:57 +0000 (UTC) Received: by mail.wl.linuxfoundation.org (Postfix, from userid 486) id 5A44E29142; Mon, 19 Sep 2016 10:55:57 +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 B083C29130 for ; Mon, 19 Sep 2016 10:55:56 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S1754962AbcISKzz (ORCPT ); Mon, 19 Sep 2016 06:55:55 -0400 Received: from mailout3.w1.samsung.com ([210.118.77.13]:56958 "EHLO mailout3.w1.samsung.com" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S1752725AbcISKzx (ORCPT ); Mon, 19 Sep 2016 06:55:53 -0400 Received: from eucas1p2.samsung.com (unknown [182.198.249.207]) by mailout3.w1.samsung.com (Oracle Communications Messaging Server 7.0.5.31.0 64bit (built May 5 2014)) with ESMTP id <0ODQ00CYUZP1YX90@mailout3.w1.samsung.com>; Mon, 19 Sep 2016 11:55:50 +0100 (BST) Received: from eusmges4.samsung.com (unknown [203.254.199.244]) by eucas1p1.samsung.com (KnoxPortal) with ESMTP id 20160919105549eucas1p105a6c93ed88dd5f14684ce83a88f0a74~1tCrsnjcG3185531855eucas1p1L; Mon, 19 Sep 2016 10:55:49 +0000 (GMT) Received: from eucas1p1.samsung.com ( [182.198.249.206]) by eusmges4.samsung.com (EUCPMTA) with SMTP id FA.96.28332.534CFD75; Mon, 19 Sep 2016 11:55:49 +0100 (BST) Received: from eusmgms2.samsung.com (unknown [182.198.249.180]) by eucas1p2.samsung.com (KnoxPortal) with ESMTP id 20160919105548eucas1p24b28143fab25eb47f5629d31f817b0bc~1tCq5JZv72047720477eucas1p2i; Mon, 19 Sep 2016 10:55:48 +0000 (GMT) X-AuditID: cbfec7f4-f791c6d000006eac-32-57dfc43512e9 Received: from eusync1.samsung.com ( [203.254.199.211]) by eusmgms2.samsung.com (EUCPMTA) with SMTP id DE.77.10494.604CFD75; Mon, 19 Sep 2016 11:55:02 +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 <0ODQ00LJ5ZOPIK20@eusync1.samsung.com>; Mon, 19 Sep 2016 11:55:48 +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 v2 1/5] clk: add support for runtime pm Date: Mon, 19 Sep 2016 12:55:21 +0200 Message-id: <1474282525-30441-2-git-send-email-m.szyprowski@samsung.com> X-Mailer: git-send-email 1.9.1 In-reply-to: <1474282525-30441-1-git-send-email-m.szyprowski@samsung.com> X-Brightmail-Tracker: H4sIAAAAAAAAA+NgFnrOIsWRmVeSWpSXmKPExsWy7djPc7qmR+6HG/xZwmuxccZ6VovrX56z Wky6P4HF4vULQ4tNj6+xWnzsucdq8bn3CKPFjPP7mCzWHrnLbnHxlKvF4TftrBY/znSzWBxf G+7A6/H+Riu7x+W+XiaPO9f2sHlsXlLv0bdlFaPH501yAWxRXDYpqTmZZalF+nYJXBnvm5Yy Fhx2q7j/1r+BcY1FFyMnh4SAicTqy+9ZIWwxiQv31rN1MXJxCAksZZS4sfsfC4TzmVHi7PSP jDAdux4cYYZILGOUeLBlEVRLA5PEuuYrbCBVbAKGEl1vu8ASIgJNjBITN6xnBXGYBbqYJR6e +gA2S1jAXGLnrF9MIDaLgKrEvCVzgGwODl4BD4k1z4Ig1slJnDw2GexATgFPidkT/4LdJCGw iF1i03UQhwPIkZXYdIAZot5FYvfTDqiHhCVeHd/CDmHLSFye3M0CYfczSjS1akPYMxglzr3l hbCtJQ4fvwjWyyzAJzFp23RmiPG8Eh1tQhAlHhKTVp1jhAg7Smya4QXx+xxgCM1dyjaBUWYB I8MqRpHU0uLc9NRiE73ixNzi0rx0veT83E2MwOg//e/4lx2Mi49ZHWIU4GBU4uFlCLgfLsSa WFZcmXuIUYKDWUmE9+keoBBvSmJlVWpRfnxRaU5q8SFGaQ4WJXHePQuuhAsJpCeWpGanphak FsFkmTg4pRoYmWanstf01lV6HPgselmL+1WAukaSq8nxxfzLp352+BpU9HNLGPOEjKNtWvvm nvwr/0vH5/hnpoUHlMu1nMrsnXQzs1+ss3u8ml/hiv+8T58ESiqDl7Ukn/dxSVrfp9fpw+wl ZFZooMPS+zbw1p3XszY6FhvOil/8bkNp6b2jWx1LzGsPNlxSYinOSDTUYi4qTgQACSlFi/oC AAA= X-Brightmail-Tracker: H4sIAAAAAAAAA+NgFupgkeLIzCtJLcpLzFFi42I5/e/4ZV22I/fDDdqecFlsnLGe1eL6l+es FpPuT2CxeP3C0GLT42usFh977rFafO49wmgx4/w+Jou1R+6yW1w85Wpx+E07q8WPM90sFsfX hjvwery/0crucbmvl8njzrU9bB6bl9R79G1ZxejxeZNcAFuUm01GamJKapFCal5yfkpmXrqt UmiIm66FkkJeYm6qrVKErm9IkJJCWWJOKZBnZIAGHJwD3IOV9O0S3DLeNy1lLDjsVnH/rX8D 4xqLLkZODgkBE4ldD44wQ9hiEhfurWfrYuTiEBJYwijx4sNsJginiUli14Fp7CBVbAKGEl1v u8CqRASaGCWe9IEkuDiYBfqYJVqnTQerEhYwl9g56xcTiM0ioCoxb8kcIJuDg1fAQ2LNsyCI dXISJ49NZgWxOQU8JWZP/MsCYgsBlbzZupl1AiPvAkaGVYwiqaXFuem5xUZ6xYm5xaV56XrJ +bmbGIGxsO3Yzy07GLveBR9iFOBgVOLhZQi4Hy7EmlhWXJl7iFGCg1lJhPfpHqAQb0piZVVq UX58UWlOavEhRlOgmyYyS4km5wPjNK8k3tDE0NzS0MjYwsLcyEhJnHfqhyvhQgLpiSWp2amp BalFMH1MHJxSDYxG04Merha40GHanfXqQkVM4VQW/0KGku7bP2Y7cfk0Rv8LfV7koTlxR8nH hZzbylZ+UsiaUH98zcTrE1cJLNebtinL0fTik8Sl82QnyeeZrDy2/+3kOibt6BVuEzSVN3nN 23zqlun9FYvndX2Tc6jI2/zO/NbzIyuvHZvTp7HsrWYoj5bCUws5JZbijERDLeai4kQAi39S E5sCAAA= X-MTR: 20000000000000000@CPGS X-CMS-MailID: 20160919105548eucas1p24b28143fab25eb47f5629d31f817b0bc 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: 20160919105548eucas1p24b28143fab25eb47f5629d31f817b0bc X-RootMTR: 20160919105548eucas1p24b28143fab25eb47f5629d31f817b0bc References: <1474282525-30441-1-git-send-email-m.szyprowski@samsung.com> Sender: linux-samsung-soc-owner@vger.kernel.org Precedence: bulk List-ID: X-Mailing-List: linux-samsung-soc@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 and set CLK_RUNTIME_PM flags for its clocks. 2. It needs to enable runtime PM for that device. 3. It needs to make sure the runtime PM status of the controller device reflects the HW state. Signed-off-by: Marek Szyprowski --- drivers/clk/clk.c | 107 +++++++++++++++++++++++++++++++++++++++---- include/linux/clk-provider.h | 1 + 2 files changed, 98 insertions(+), 10 deletions(-) diff --git a/drivers/clk/clk.c b/drivers/clk/clk.c index 820a939fb6bb..096a199b8e46 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,17 @@ 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); + clk_pm_runtime_get(core); + status = core->ops->is_prepared(core->hw); + clk_pm_runtime_put(core); + + 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 +199,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 runtime pm is enabled 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 place. + */ + if (core->dev) { + pm_runtime_get_noresume(core->dev); + if (pm_runtime_suspended(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 +541,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 +584,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 +599,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 +806,9 @@ static void clk_unprepare_unused_subtree(struct clk_core *core) if (core->flags & CLK_IGNORE_UNUSED) return; + if (clk_pm_runtime_get(core) != 0) + return; + if (clk_core_is_prepared(core)) { trace_clk_unprepare(core); if (core->ops->unprepare_unused) @@ -753,6 +817,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 +834,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) != 0) + return; + flags = clk_enable_lock(); if (core->enable_count) @@ -794,6 +863,8 @@ unlock_out: clk_enable_unlock(flags); if (core->flags & CLK_OPS_PARENT_ENABLE) clk_core_disable_unprepare(core->parent); + + clk_pm_runtime_put(core); } static bool clk_ignore_unused; @@ -1563,6 +1634,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 +1651,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 +1903,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 +1925,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(); @@ -2546,6 +2631,8 @@ struct clk *clk_register(struct device *dev, struct clk_hw *hw) goto fail_name; } core->ops = hw->init->ops; + if (dev && (hw->init->flags & CLK_RUNTIME_PM)) + core->dev = dev; if (dev && dev->driver) core->owner = dev->driver->owner; core->hw = hw; diff --git a/include/linux/clk-provider.h b/include/linux/clk-provider.h index a39c0c530778..8a131eb71fdf 100644 --- a/include/linux/clk-provider.h +++ b/include/linux/clk-provider.h @@ -35,6 +35,7 @@ #define CLK_IS_CRITICAL BIT(11) /* do not gate, ever */ /* parents need enable during gate/ungate, set rate and re-parent */ #define CLK_OPS_PARENT_ENABLE BIT(12) +#define CLK_RUNTIME_PM BIT(13) struct clk; struct clk_hw;