From patchwork Fri Aug 4 09:34:51 2017 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Marek Szyprowski X-Patchwork-Id: 9880911 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 BB7EB602B8 for ; Fri, 4 Aug 2017 09:36:39 +0000 (UTC) Received: from mail.wl.linuxfoundation.org (localhost [127.0.0.1]) by mail.wl.linuxfoundation.org (Postfix) with ESMTP id AB0822883D for ; Fri, 4 Aug 2017 09:36:39 +0000 (UTC) Received: by mail.wl.linuxfoundation.org (Postfix, from userid 486) id 9FA99289B5; Fri, 4 Aug 2017 09:36:39 +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=-1.9 required=2.0 tests=BAYES_00,DKIM_SIGNED, DKIM_VALID,RCVD_IN_DNSWL_NONE autolearn=ham version=3.3.1 Received: from bombadil.infradead.org (bombadil.infradead.org [65.50.211.133]) (using TLSv1.2 with cipher AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by mail.wl.linuxfoundation.org (Postfix) with ESMTPS id BF78D2883D for ; Fri, 4 Aug 2017 09:36:38 +0000 (UTC) DKIM-Signature: v=1; a=rsa-sha256; q=dns/txt; c=relaxed/relaxed; d=lists.infradead.org; s=bombadil.20170209; h=Sender: Content-Transfer-Encoding:Content-Type:MIME-Version:Cc:List-Subscribe: List-Help:List-Post:List-Archive:List-Unsubscribe:List-Id:References: In-reply-to:Message-id:Date:Subject:To:From:Reply-To:Content-ID: Content-Description:Resent-Date:Resent-From:Resent-Sender:Resent-To:Resent-Cc :Resent-Message-ID:List-Owner; bh=gPuOolGDXAopRsJFO6c4Fym4mLFpXtnxjqXgX89B5AQ=; b=u0WZzu95Qd6xl/HL+1Qbs3b77t hGgydCdvOHWam6Zud6WG4wJHEyKk/ddA1WkecMd27w44EdbSNatpNW6GpNuOWS/kHSFLHp3mlrd7E QaHR9nTzJp8JqBedWrR+5aFELMj3wmUrci0j7eW7WxFJh7XEsiU04jpuyR+SiJkugPUOKq0guqoaE zPkeGJPgkTURqfNC77q0ePkjBN7sM5WaJ/U6mBsy2/+ksOclFfijn+IaJB73L5kQqR77Vs6zdIkTd nH/dihA2hx/gFmjRsPpwmHkAURn08lO5wQ0d0VDmEi+lw7ViwUgc7udxNyYr10NIBo8YhLQdUrwiY P00jwdsQ==; Received: from localhost ([127.0.0.1] helo=bombadil.infradead.org) by bombadil.infradead.org with esmtp (Exim 4.87 #1 (Red Hat Linux)) id 1ddZ22-0000gY-ST; Fri, 04 Aug 2017 09:36:35 +0000 Received: from mailout3.w1.samsung.com ([210.118.77.13]) by bombadil.infradead.org with esmtps (Exim 4.87 #1 (Red Hat Linux)) id 1ddZ1U-0008Tr-Qd for linux-arm-kernel@lists.infradead.org; Fri, 04 Aug 2017 09:36:10 +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 <0OU500F4OMMSJX50@mailout3.w1.samsung.com> for linux-arm-kernel@lists.infradead.org; Fri, 04 Aug 2017 10:35:16 +0100 (BST) Received: from eusmges5.samsung.com (unknown [203.254.199.245]) by eucas1p1.samsung.com (KnoxPortal) with ESMTP id 20170804093515eucas1p10a1261ca29389fe60884a398d831a77b~XmuaVPT2_2001820018eucas1p1C; Fri, 4 Aug 2017 09:35:15 +0000 (GMT) Received: from eucas1p2.samsung.com ( [182.198.249.207]) by eusmges5.samsung.com (EUCPMTA) with SMTP id 66.0A.25577.3DF34895; Fri, 4 Aug 2017 10:35:15 +0100 (BST) Received: from eusmgms1.samsung.com (unknown [182.198.249.179]) by eucas1p2.samsung.com (KnoxPortal) with ESMTP id 20170804093515eucas1p2226e597c155e5c4ca4ffea95c9e00392~XmuZn2PWN2766327663eucas1p2n; Fri, 4 Aug 2017 09:35:15 +0000 (GMT) X-AuditID: cbfec7f5-f792f6d0000063e9-d3-59843fd302d5 Received: from eusync3.samsung.com ( [203.254.199.213]) by eusmgms1.samsung.com (EUCPMTA) with SMTP id 4B.2F.17452.3DF34895; Fri, 4 Aug 2017 10:35:15 +0100 (BST) Received: from AMDC2765.digital.local ([106.116.147.25]) by eusync3.samsung.com (Oracle Communications Messaging Server 7.0.5.31.0 64bit (built May 5 2014)) with ESMTPA id <0OU50004IMMJTPA0@eusync3.samsung.com>; Fri, 04 Aug 2017 10:35:15 +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 Subject: [PATCH v7 1/4] clk: Add support for runtime PM Date: Fri, 04 Aug 2017 11:34:51 +0200 Message-id: <1501839294-478-2-git-send-email-m.szyprowski@samsung.com> X-Mailer: git-send-email 1.9.1 In-reply-to: <1501839294-478-1-git-send-email-m.szyprowski@samsung.com> X-Brightmail-Tracker: H4sIAAAAAAAAA+NgFnrLIsWRmVeSWpSXmKPExsWy7djP87qX7VsiDU5vVrHYOGM9q8X1L89Z LSbdn8Bicf78BnaLTY+vsVp87LnHavG59wijxYzz+5gs1h65y25x8ZSrxeE37awWP850s1gc XxvuwOvx/kYru8flvl4mj02rOtk87lzbw+axeUm9R9+WVYwenzfJBbBHcdmkpOZklqUW6dsl cGXM3/GMqeC0T0XrlmlsDYwrbbsYOTkkBEwkjvZuYoawxSQu3FvP1sXIxSEksJRR4tCj+0wQ zmdGiW39K4AyHGAdG6fKQ8SXMUp8/7OHCaRbSKCBSeLvL0YQm03AUKLrbRfYJBGBJkaJiRvW s4I4zAJNzBLT9k1kApkkLGAu8fcKC0gDi4CqxO/Lz8EG8Qq4SVxY18IOcZKcxMljk1lBbE4B d4mZvxrBhkoIrGOXWPN5KTvERbISmw5AveAicWrrdChbWOLV8S1Qc2QkOjsOMkHY/YwSTa3a EPYMRolzb3khbGuJw8cvgu1iFuCTmLQNZA7IeF6JjjYhiBIPia13v0KNcZTY8XwhKyQgZjFK /Fm4iWUCo8wCRoZVjCKppcW56anFpnrFibnFpXnpesn5uZsYgYng9L/jX3cwLj1mdYhRgINR iYfXYEZzpBBrYllxZe4hRgkOZiURXkaVlkgh3pTEyqrUovz4otKc1OJDjNIcLErivFynrkUI CaQnlqRmp6YWpBbBZJk4OKUaGPfu9tj/c/KxT8yXu+W2HdH58uJIWknJ9TXSl+sMxO4e/Ve8 1Gjuytyn7e5xS6yllhzXd8+P3c6U+KBp0tRlqXPm/5Ve5Vvl0HXBcRWPxc6Jq8Tm2F5Qe2G1 enZY36I2rbUeM//GCf6N37iCdXlWlvpM9UWnzCc0LBJ48oXjnbht20nlK6fau+qUWIozEg21 mIuKEwEmiFbHAAMAAA== X-Brightmail-Tracker: H4sIAAAAAAAAA+NgFuplkeLIzCtJLcpLzFFi42I5/e/4Vd3L9i2RBv8fsVpsnLGe1eL6l+es FpPuT2CxOH9+A7vFpsfXWC0+9txjtfjce4TRYsb5fUwWa4/cZbe4eMrV4vCbdlaLH2e6WSyO rw134PV4f6OV3eNyXy+Tx6ZVnWwed67tYfPYvKTeo2/LKkaPz5vkAtij3GwyUhNTUosUUvOS 81My89JtlUJD3HQtlBTyEnNTbZUidH1DgpQUyhJzSoE8IwM04OAc4B6spG+X4JYxf8czpoLT PhWtW6axNTCutO1i5OCQEDCR2DhVvouRE8gUk7hwbz0biC0ksIRRYuUEgS5GLiC7iUlixfsj YAk2AUOJrrddbCAJEYEmRoknfdPYQRxmgTZmif8dXxlBpgoLmEv8vcIC0sAioCrx+/JzJhCb V8BN4sK6FnaIbXISJ49NZgWxOQXcJWb+aoTa7Cax6cIDxgmMvAsYGVYxiqSWFuem5xYb6hUn 5haX5qXrJefnbmIERsW2Yz8372C8tDH4EKMAB6MSD6/BjOZIIdbEsuLK3EOMEhzMSiK8jCot kUK8KYmVValF+fFFpTmpxYcYTYGOmsgsJZqcD4zYvJJ4QxNDc0tDI2MLC3MjIyVxXvXLTZFC AumJJanZqakFqUUwfUwcnFINjDJzVjxgu2Q2Y8qy34VXFnRE3DpRcvPr2fjJgbZydZeT5rrV s59XOrrlzOIbC3l+Xq4Mt7I5mPeB85FXofykCcuL2DxemZgsC6sxqfO+6BQaod338vX9baEh nduzhL02/mH3K5jlrzNn1crlPaGb3T3n3Jv37lrFPSMGlhfnqw07Nke5qvjV1CmxFGckGmox FxUnAgDhKtpioAIAAA== X-MTR: 20000000000000000@CPGS X-CMS-MailID: 20170804093515eucas1p2226e597c155e5c4ca4ffea95c9e00392 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: 20170804093515eucas1p2226e597c155e5c4ca4ffea95c9e00392 X-RootMTR: 20170804093515eucas1p2226e597c155e5c4ca4ffea95c9e00392 References: <1501839294-478-1-git-send-email-m.szyprowski@samsung.com> X-CRM114-Version: 20100106-BlameMichelson ( TRE 0.8.0 (BSD) ) MR-646709E3 X-CRM114-CacheID: sfid-20170804_023601_597130_03163315 X-CRM114-Status: GOOD ( 26.60 ) X-BeenThere: linux-arm-kernel@lists.infradead.org X-Mailman-Version: 2.1.21 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_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);