From patchwork Fri Dec 30 13:33:33 2016 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Marek Szyprowski X-Patchwork-Id: 9492149 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 4C02F60417 for ; Fri, 30 Dec 2016 13:36:54 +0000 (UTC) Received: from mail.wl.linuxfoundation.org (localhost [127.0.0.1]) by mail.wl.linuxfoundation.org (Postfix) with ESMTP id 41F3B1FF8F for ; Fri, 30 Dec 2016 13:36:54 +0000 (UTC) Received: by mail.wl.linuxfoundation.org (Postfix, from userid 486) id 36AF622689; Fri, 30 Dec 2016 13:36:54 +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=-4.2 required=2.0 tests=BAYES_00, RCVD_IN_DNSWL_MED autolearn=ham version=3.3.1 Received: from bombadil.infradead.org (bombadil.infradead.org [198.137.202.9]) (using TLSv1.2 with cipher AES128-GCM-SHA256 (128/128 bits)) (No client certificate requested) by mail.wl.linuxfoundation.org (Postfix) with ESMTPS id 766AE1FF8F for ; Fri, 30 Dec 2016 13:36:53 +0000 (UTC) Received: from localhost ([127.0.0.1] helo=bombadil.infradead.org) by bombadil.infradead.org with esmtp (Exim 4.85_2 #1 (Red Hat Linux)) id 1cMxKK-00012h-4S; Fri, 30 Dec 2016 13:34:32 +0000 Received: from mailout3.w1.samsung.com ([210.118.77.13]) by bombadil.infradead.org with esmtps (Exim 4.85_2 #1 (Red Hat Linux)) id 1cMxJx-0000qq-Vv for linux-arm-kernel@lists.infradead.org; Fri, 30 Dec 2016 13:34:12 +0000 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 <0OJ0008U930AG590@mailout3.w1.samsung.com> for linux-arm-kernel@lists.infradead.org; Fri, 30 Dec 2016 13:33:46 +0000 (GMT) Received: from eusmges4.samsung.com (unknown [203.254.199.244]) by eucas1p2.samsung.com (KnoxPortal) with ESMTP id 20161230133345eucas1p2603a35e21e6ea0cc3104b8549778f321~VC-s3f1iY1338613386eucas1p2l; Fri, 30 Dec 2016 13:33:45 +0000 (GMT) Received: from eucas1p2.samsung.com ( [182.198.249.207]) by eusmges4.samsung.com (EUCPMTA) with SMTP id F3.C5.20460.93266685; Fri, 30 Dec 2016 13:33:45 +0000 (GMT) Received: from eusmgms2.samsung.com (unknown [182.198.249.180]) by eucas1p2.samsung.com (KnoxPortal) with ESMTP id 20161230133345eucas1p2b88ad403789265915db45ce48ab40453~VC-sFXkXf1937819378eucas1p2z; Fri, 30 Dec 2016 13:33:45 +0000 (GMT) X-AuditID: cbfec7f4-f79b96d000004fec-25-586662394818 Received: from eusync4.samsung.com ( [203.254.199.214]) by eusmgms2.samsung.com (EUCPMTA) with SMTP id 66.6B.06573.C2266685; Fri, 30 Dec 2016 13:33:32 +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 <0OJ00051Q304AG90@eusync4.samsung.com>; Fri, 30 Dec 2016 13:33:44 +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 Subject: [PATCH v4 1/4] clk: Add support for runtime PM Date: Fri, 30 Dec 2016 14:33:33 +0100 Message-id: <1483104816-20885-2-git-send-email-m.szyprowski@samsung.com> X-Mailer: git-send-email 1.9.1 In-reply-to: <1483104816-20885-1-git-send-email-m.szyprowski@samsung.com> X-Brightmail-Tracker: H4sIAAAAAAAAA+NgFnrLIsWRmVeSWpSXmKPExsWy7djP87qWSWkRBm9WKVtsnLGe1eL6l+es FpPuT2CxOH9+A7vFpsfXWC0+9txjtfjce4TRYsb5fUwWa4/cZbe4eMrV4vCbdlaLH2e6WSyO rw134PV4f6OV3eNyXy+Tx6ZVnWwed67tYfPYvKTeo2/LKkaPz5vkAtijuGxSUnMyy1KL9O0S uDI676xmLNjsXPH4/TrmBsanpl2MnBwSAiYSPbdfs0DYYhIX7q1n62Lk4hASWMoosfrfLEYI 5zOjxIW/mxlhOpbM2c8KkVjGKNE9ZTVYQkiggUliz04bEJtNwFCi620X2CgRgSZGiYkb1oN1 MAs0MUtM2zeRCaRKWMBcoqV7PhuIzSKgKvHk3ENWEJtXwEPi16q5bBDr5CROHpsMFucU8JRo +fkdbKqEwCp2iSsXnwIN4gByZCU2HWCGMF0k+ndA/SYs8er4FnYIW0bi8uRuqD/7GSWaWrUh 7BmMEufe8kLY1hKHj18EW8UswCcxadt0qJG8Eh1tQhAlHhInl++HBoSjxJs1V9khfp8DDK4t ChMYZRYwMqxiFEktLc5NTy020StOzC0uzUvXS87P3cQITASn/x3/soNx8TGrQ4wCHIxKPLw/ ylIjhFgTy4orcw8xSnAwK4nw7ohPixDiTUmsrEotyo8vKs1JLT7EKM3BoiTOu2fBlXAhgfTE ktTs1NSC1CKYLBMHp1QDo7rfi7DMorVrFe4UTYtu2uGlKmP5eKmjRQDfrYerjb7Hf9q8mJsv lIl5+8q/J2JP5R05HP/15bIC9o0+V6xkjoudlv/yUD/W7reWmpPYv8/m/5Y4bLqrUhSx9Y90 RcoNrmkvGi85tTx/VhHvuaTvg3Z2+ZRbv7t+eDayfX0cdM/qYNjDU42qnEosxRmJhlrMRcWJ AFQ+Zc4AAwAA X-Brightmail-Tracker: H4sIAAAAAAAAA+NgFupnkeLIzCtJLcpLzFFi42I5/e/4NV2dpLQIg/VXtCw2zljPanH9y3NW i0n3J7BYnD+/gd1i0+NrrBYfe+6xWnzuPcJoMeP8PiaLtUfusltcPOVqcfhNO6vFjzPdLBbH 14Y78Hq8v9HK7nG5r5fJY9OqTjaPO9f2sHlsXlLv0bdlFaPH501yAexRbjYZqYkpqUUKqXnJ +SmZeem2SqEhbroWSgp5ibmptkoRur4hQUoKZYk5pUCekQEacHAOcA9W0rdLcMvovLOasWCz c8Xj9+uYGxifmnYxcnJICJhILJmznxXCFpO4cG89WxcjF4eQwBJGifZDO9hAEkICTUwSB5vr QGw2AUOJrrddYEUiAk2MEk/6prGDOMwCbcwS/zu+MoJUCQuYS7R0zwfrZhFQlXhy7iHYCl4B D4lfq+ayQayTkzh5bDJYnFPAU6Ll53eobR4S8998YJzAyLuAkWEVo0hqaXFuem6xkV5xYm5x aV66XnJ+7iZGYGRsO/Zzyw7GrnfBhxgFOBiVeHgXlKRGCLEmlhVX5h5ilOBgVhLh3RGfFiHE m5JYWZValB9fVJqTWnyI0RToqInMUqLJ+cCozSuJNzQxNLc0NDK2sDA3MlIS55364Uq4kEB6 YklqdmpqQWoRTB8TB6dUA6P7h95/spPND4ufPsB8+0FVtF+Kzrcd+VzrLp2QOb1X/g9bH2+7 Seflz3V/TGzOuVc78mozy//09Di5aYL+4ZNXBc5vWh0h6l0/b/5F3cX7ZjE8vOgsGLJ/4Wz/ iLRbTi+CJ/1QXyhu4X3GKNL2Sa6H0xbGOrUPjJd+23vt2vr967kdae8FDk9UYinOSDTUYi4q TgQAup1roaICAAA= X-MTR: 20000000000000000@CPGS X-CMS-MailID: 20161230133345eucas1p2b88ad403789265915db45ce48ab40453 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: 20161230133345eucas1p2b88ad403789265915db45ce48ab40453 X-RootMTR: 20161230133345eucas1p2b88ad403789265915db45ce48ab40453 References: <1483104816-20885-1-git-send-email-m.szyprowski@samsung.com> X-CRM114-Version: 20100106-BlameMichelson ( TRE 0.8.0 (BSD) ) MR-646709E3 X-CRM114-CacheID: sfid-20161230_053410_426999_132EAEF7 X-CRM114-Status: GOOD ( 25.64 ) X-BeenThere: linux-arm-kernel@lists.infradead.org X-Mailman-Version: 2.1.20 Precedence: list List-Id: List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Cc: Ulf Hansson , Bartlomiej Zolnierkiewicz , Michael Turquette , Stephen Boyd , Krzysztof Kozlowski , Inki Dae , Chanwoo Choi , Sylwester Nawrocki , Marek Szyprowski MIME-Version: 1.0 Sender: "linux-arm-kernel" Errors-To: linux-arm-kernel-bounces+patchwork-linux-arm=patchwork.kernel.org@lists.infradead.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;