From patchwork Mon Aug 21 08:04:59 2017 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Marek Szyprowski X-Patchwork-Id: 110500 Delivered-To: patch@linaro.org Received: by 10.140.95.78 with SMTP id h72csp958906qge; Mon, 21 Aug 2017 01:05:48 -0700 (PDT) X-Received: by 10.84.232.5 with SMTP id h5mr10936591plk.433.1503302748285; Mon, 21 Aug 2017 01:05:48 -0700 (PDT) ARC-Seal: i=1; a=rsa-sha256; t=1503302748; cv=none; d=google.com; s=arc-20160816; b=udtISdRCjkGXQQ7958nA7b9VCDq/J7iGDT+4d7fPSEQduIfEKLw++dL3HJXYSs60W9 9Xjuj+b6WiYlNDGt+t7B3NUvPkn7Qy+mmLBXxBzX160WeTqfwQ2ezXSk055GXJV/6/s4 EKVUhDiJbNdTYmAzuEVoBfRt5Or2ZqdVE/HIE3ItMzbkw1Pn5VkeuLg9Wqi/TL91SV6q EWJdWSrr2ChSpdPM38wCYAL8y+A6pXPyA80yevR8m+Hm/a/sX86RWG751WLw8Gpq9244 pjlJITcq6JIAE3ntiiDBzZbx7NlR7Hed3XDIGzahCY8d14p9rcHg0OPNJbRaaxH9zDz9 rvEA== ARC-Message-Signature: i=1; a=rsa-sha256; c=relaxed/relaxed; d=google.com; s=arc-20160816; h=list-id:precedence:sender:references:cms-type:in-reply-to :message-id:date:subject:cc:to:from:arc-authentication-results; bh=bd4n04YMY0iPWInd+2qPs1ImU6aWwCvYis9vOY95YbM=; b=yRBYPcT91zTOyJQzDdUOHhw8M+YJmPOiPzb66Sh67odvnerBw4aqmWq6PATphQA9pA BD+TOoxYhSYCtff6SmLE56c4roSfQJb7rRtMHHPkw9BP7twTlm32bW9S+i6vzG1egsTu FQjkXzfT8gm+kTR223/eORweKznPtDN9anko1AAMxSzB5dJiitpJIQlioKZej+sRgKw7 KpuGBqYO7fjHwqrCEfcvLWHAJg4dMW0bg2NSmLR0eWHD/za94aOWyvZpOatlLH5qj8Xo DcJAvkxv9d9xV0XnjjrcKbPTtQgi+XJxXEUGubThNgbuEHr2+IrCgLDnG13wZfzbvbQc 25PQ== ARC-Authentication-Results: i=1; mx.google.com; spf=pass (google.com: best guess record for domain of linux-pm-owner@vger.kernel.org designates 209.132.180.67 as permitted sender) smtp.mailfrom=linux-pm-owner@vger.kernel.org; dmarc=fail (p=NONE sp=NONE dis=NONE) header.from=samsung.com Return-Path: Received: from vger.kernel.org (vger.kernel.org. [209.132.180.67]) by mx.google.com with ESMTP id 7si6960206pgt.528.2017.08.21.01.05.47; Mon, 21 Aug 2017 01:05:48 -0700 (PDT) Received-SPF: pass (google.com: best guess record for domain of linux-pm-owner@vger.kernel.org designates 209.132.180.67 as permitted sender) client-ip=209.132.180.67; Authentication-Results: mx.google.com; spf=pass (google.com: best guess record for domain of linux-pm-owner@vger.kernel.org designates 209.132.180.67 as permitted sender) smtp.mailfrom=linux-pm-owner@vger.kernel.org; dmarc=fail (p=NONE sp=NONE dis=NONE) header.from=samsung.com Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S1751959AbdHUIFp (ORCPT + 12 others); Mon, 21 Aug 2017 04:05:45 -0400 Received: from mailout2.w1.samsung.com ([210.118.77.12]:46189 "EHLO mailout2.w1.samsung.com" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S1751895AbdHUIFm (ORCPT ); Mon, 21 Aug 2017 04:05:42 -0400 Received: from eucas1p2.samsung.com (unknown [182.198.249.207]) by mailout2.w1.samsung.com (KnoxPortal) with ESMTP id 20170821080540euoutp02900565db0a1a2dc64e927fff83fdf25d~czeChuSRD1767817678euoutp027; Mon, 21 Aug 2017 08:05:40 +0000 (GMT) Received: from eusmges5.samsung.com (unknown [203.254.199.245]) by eucas1p2.samsung.com (KnoxPortal) with ESMTP id 20170821080539eucas1p2861b64c44004c849a5c614611da93a97~czeB0r_nQ1148811488eucas1p2T; Mon, 21 Aug 2017 08:05:39 +0000 (GMT) Received: from eucas1p1.samsung.com ( [182.198.249.206]) by eusmges5.samsung.com (EUCPMTA) with SMTP id 52.58.12743.3549A995; Mon, 21 Aug 2017 09:05:39 +0100 (BST) Received: from eusmgms2.samsung.com (unknown [182.198.249.180]) by eucas1p2.samsung.com (KnoxPortal) with ESMTP id 20170821080538eucas1p2fc5723914c314e6fe5da19bed55dd5bd~czeA8mB1k1148811488eucas1p2Q; Mon, 21 Aug 2017 08:05:38 +0000 (GMT) X-AuditID: cbfec7f5-f79d06d0000031c7-de-599a94535b93 Received: from eusync1.samsung.com ( [203.254.199.211]) by eusmgms2.samsung.com (EUCPMTA) with SMTP id AD.B4.20118.2549A995; Mon, 21 Aug 2017 09:05:38 +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 <0OV0009QWZT303A0@eusync1.samsung.com>; Mon, 21 Aug 2017 09:05:38 +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 v9 1/5] clk: Add support for runtime PM Date: Mon, 21 Aug 2017 10:04:59 +0200 Message-id: <1503302703-13801-2-git-send-email-m.szyprowski@samsung.com> X-Mailer: git-send-email 1.9.1 In-reply-to: <1503302703-13801-1-git-send-email-m.szyprowski@samsung.com> X-Brightmail-Tracker: H4sIAAAAAAAAA+NgFnrDIsWRmVeSWpSXmKPExsWy7djPc7rBU2ZFGsx5wW+xccZ6VovrX56z Wky6P4HF4vz5DewWmx5fY7X42HOP1eJz7xFGixnn9zFZrD1yl93i4ilXi8Nv2lktfpzpZrE4 vjbcgdfj/Y1Wdo/Lfb1MHptWdbJ53Lm2h81j85J6j74tqxg9Pm+SC2CP4rJJSc3JLEst0rdL 4Mr4+LKFpeCWb8X7O1OZGhhn2XUxcnJICJhITJw9nQ3CFpO4cG89mC0ksJRR4uTu4C5GLiD7 M6PEpZvT2bsYOcAabl2Gii9jlHj2+D0jhNPAJNH8djIzSDebgKFE19suNpCEiEATo8TEDetZ QRxmgSZmiWn7JjKBVAkLmEus62kD28cioCrxseMhM8gKXgEPiY+NlRAnyUmcPDaZFcTmFPCU 2DdjI9gcCYF17BLffk9nhjhJVmLTAWaIeheJbYuWsUPYwhKvjm+BsmUkLk/uZoGw+xklmlq1 IewZjBLn3vJC2NYSh49fBNvFLMAnMWkbzHheiY42IYgSD4m7/TvYIMKOEnsmSEP8PodRYm3f JfYJjDILGBlWMYqklhbnpqcWm+oVJ+YWl+al6yXn525iBKaB0/+Of93BuPSY1SFGAQ5GJR7e G/mzIoVYE8uKK3MPMUpwMCuJ8PJNmRkpxJuSWFmVWpQfX1Sak1p8iFGag0VJnNc2qi1SSCA9 sSQ1OzW1ILUIJsvEwSnVwBgpLG3cv2u7Vtza2Dbff1610teWLpx59vKZvUe/NEzY0XLN/4GJ 3J8dRd/mbn/2+dsS3Xs8Su8vtmuJObx/Oq38dw67zP27sVHnFrdK7mpaGxh7Ku9g7w/nsD+s qx48mX85ITGR27/ryvQpUT9q899PkQ0X+h2aymJ3kV1A/o2d55qtmvVZRTxKLMUZiYZazEXF iQCHdmqm/wIAAA== X-Brightmail-Tracker: H4sIAAAAAAAAA+NgFupjkeLIzCtJLcpLzFFi42I5/e/4Zd2gKbMiDU5OULbYOGM9q8X1L89Z LSbdn8Bicf78BnaLTY+vsVp87LnHavG59wijxYzz+5gs1h65y25x8ZSrxeE37awWP850s1gc XxvuwOvx/kYru8flvl4mj02rOtk87lzbw+axeUm9R9+WVYwenzfJBbBHudlkpCampBYppOYl 56dk5qXbKoWGuOlaKCnkJeam2ipF6PqGBCkplCXmlAJ5RgZowME5wD1YSd8uwS3j48sWloJb vhXv70xlamCcZdfFyMEhIWAicetycBcjJ5ApJnHh3nq2LkYuDiGBJYwSuy52MEM4TUwS174+ ZAKpYhMwlOh62wVWJSLQxCjxpG8aO4jDLNDGLPG/4ysjSJWwgLnEup42NhCbRUBV4mPHQ2aQ dbwCHhIfGysh1slJnDw2mRXE5hTwlNg3YyOYLQRUcrvjBuMERt4FjAyrGEVSS4tz03OLjfSK E3OLS/PS9ZLzczcxAuNi27GfW3Ywdr0LPsQowMGoxMN7I39WpBBrYllxZe4hRgkOZiURXr4p MyOFeFMSK6tSi/Lji0pzUosPMZoC3TSRWUo0OR8Ys3kl8YYmhuaWhkbGFhbmRkZK4rzql5si hQTSE0tSs1NTC1KLYPqYODilGhgDWjxrVmrb+tpVG7X4inh/SNz65v9hgYXSz2fHBpmvflL9 R6zWO7vhqPeZu7yXDKVU7mU8K/l6JcmMz9f7rOHPt5923Gqe0nHw9nSeOW+NLy9zvHg0/IG2 EOdFvmYeofbdM5h5NRKdLHxcnpoy/jxz2PnR4Zzf7yQXZjY+r9vDMH9/wDbXLXJKLMUZiYZa zEXFiQB4/5uFoQIAAA== X-MTR: 20000000000000000@CPGS X-CMS-MailID: 20170821080538eucas1p2fc5723914c314e6fe5da19bed55dd5bd X-Msg-Generator: CA X-Sender-IP: 182.198.249.180 X-Local-Sender: =?utf-8?q?Marek_Szyprowski=1BSRPOL-Kernel_=28TP=29=1B?= =?utf-8?b?7IK87ISx7KCE7J6QG1NlbmlvciBTb2Z0d2FyZSBFbmdpbmVlcg==?= X-Global-Sender: =?utf-8?q?Marek_Szyprowski=1BSRPOL-Kernel_=28TP=29=1BSam?= =?utf-8?q?sung_Electronics=1BSenior_Software_Engineer?= X-Sender-Code: =?utf-8?q?C10=1BEHQ=1BC10CD02CD027392?= CMS-TYPE: 201P X-CMS-RootMailID: 20170821080538eucas1p2fc5723914c314e6fe5da19bed55dd5bd X-RootMTR: 20170821080538eucas1p2fc5723914c314e6fe5da19bed55dd5bd References: <1503302703-13801-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 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 | 126 ++++++++++++++++++++++++++++++++++++++++++++++++------ 1 file changed, 112 insertions(+), 14 deletions(-) -- 1.9.1 diff --git a/drivers/clk/clk.c b/drivers/clk/clk.c index fc58c52a26b4..c5a4fb2687ab 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(); @@ -2350,7 +2440,7 @@ static inline void clk_debug_unregister(struct clk_core *core) */ static int __clk_core_init(struct clk_core *core) { - int i, ret = 0; + int i, ret; struct clk_core *orphan; struct hlist_node *tmp2; unsigned long rate; @@ -2360,6 +2450,10 @@ static int __clk_core_init(struct clk_core *core) clk_prepare_lock(); + ret = clk_pm_runtime_get(core); + if (ret) + goto unlock; + /* check to see if a clock with this name is already registered */ if (clk_core_lookup(core->name)) { pr_debug("%s: clk %s already initialized\n", @@ -2512,6 +2606,8 @@ static int __clk_core_init(struct clk_core *core) kref_init(&core->ref); out: + clk_pm_runtime_put(core); +unlock: clk_prepare_unlock(); if (!ret) @@ -2583,6 +2679,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;