From patchwork Tue Mar 21 18:33:27 2017 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Jerome Brunet X-Patchwork-Id: 9637325 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 E39986020B for ; Tue, 21 Mar 2017 18:34:18 +0000 (UTC) Received: from mail.wl.linuxfoundation.org (localhost [127.0.0.1]) by mail.wl.linuxfoundation.org (Postfix) with ESMTP id D61BC28304 for ; Tue, 21 Mar 2017 18:34:18 +0000 (UTC) Received: by mail.wl.linuxfoundation.org (Postfix, from userid 486) id CA4152841A; Tue, 21 Mar 2017 18:34:18 +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 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 EED2028304 for ; Tue, 21 Mar 2017 18:34:15 +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=KxYn/MdhbhS0H5U/xXF+WhNVR/CsjgW2ScmmORkZC0I=; b=Y2CByEC3WtMe191EtE0pi0B7av pvYSDFui52Kr2xC40gqqJsJtMR89NR0LlEhSgDl9ZLscUFyXTro/2R+kVObRJJzScWfdz8MV4yQIy UKvrN2SJLL2AQSoeo9IF2jc7+Xdo2stOkpPpWXl8XUyt43b7hSLUd6LFiRzJVmpkNUyeI8tMPBPZ6 RlCIJt7pxAahhS9b92xPpF6pEZMBuZ8skXe+/675U+CYbpJ2nQNt5dcxJ28SXNIe6ta92N3bu0l1e m1X91JnudJFaIXkgs9JulHUyB6prqtpJ3lXU17J4T7jhy7/KXRFy5CJtkyMmXIz2VQ0VcYi0LDU0U P8D8GdDg==; 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 1cqObh-0000IQ-Ep; Tue, 21 Mar 2017 18:34:09 +0000 Received: from mail-wr0-x22f.google.com ([2a00:1450:400c:c0c::22f]) by bombadil.infradead.org with esmtps (Exim 4.87 #1 (Red Hat Linux)) id 1cqObX-00006N-Ha for linux-amlogic@lists.infradead.org; Tue, 21 Mar 2017 18:34:04 +0000 Received: by mail-wr0-x22f.google.com with SMTP id g10so117437801wrg.2 for ; Tue, 21 Mar 2017 11:33:40 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=baylibre-com.20150623.gappssmtp.com; s=20150623; h=from:to:cc:subject:date:message-id:in-reply-to:references; bh=pyWzEjy25+pxxjmxIbh7lqS3KkZDRM7wWaDXMVUFEMg=; b=ISBSGptk65PNezJlqhQMVnlKteDBwv6WIkGUP2oxsspFJuqx/8mFFR/2b5iNXmFEqN q3hxmmatsWWd190gJn/O5gGl0ZYgsHZ3mHEX5P7b9TxUSAbdBFAkmhJu0I8wzlYDmN+i Zo/fARP76NOM5LAMOoIZJFlH2cK9MuxOBUOooAX6BeeDnPS0x0G+YyPYMBPjY5c/GxJG s6Q7yPbgiatsRMgjQIu5B9Pavi4ZgLhbB8urPltgme1/kwUJg91RtBFonXc/UM0bkPQ3 stJnH9wAwRAnJATynhSKbsV5Mi9Vg6guulx/KkHnzjaOLFTTilZ0etYdM0EUPkDWp2JX cwgg== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20161025; h=x-gm-message-state:from:to:cc:subject:date:message-id:in-reply-to :references; bh=pyWzEjy25+pxxjmxIbh7lqS3KkZDRM7wWaDXMVUFEMg=; b=lAebiKbH5lPNOkJaI33DkayHvmHDTGe1VZARryY1nDyjY25fe9JzF5PzbZQrsLMQG6 QpVYyYetXd/4REW408/DB6pMqpgRkqcvBCIt1Fs/nu2ydU/dj8HC5UJHP5P5TLaaM+l1 AeGbWbcBGDTMEpMyVfD/in187bfe7VRzwWzK2YVNCbpvmeutR3JPqXtutJRSwguGSWo4 SEUHX5YxtGcYYQ9Rgugj0WuHKmGwdBDwn8o0jk+Vp/V258VsEhX5TMWVYaRzq/IeFg9f t3JPowBx1o9JQo86/ZzElhOwKo32WoTGCI/yhQLYuRkt333Pdw5KcFOmlVG3flv8gtpS 0AMg== X-Gm-Message-State: AFeK/H3XLKix8Rdx46pBRpsIwP9EmL+T2eE2Vy6StJwH0YN+lSD8fwKDX3O+MBhXcrRF8lT0 X-Received: by 10.223.136.66 with SMTP id e2mr32587593wre.14.1490121219135; Tue, 21 Mar 2017 11:33:39 -0700 (PDT) Received: from localhost.localdomain ([90.63.244.31]) by smtp.googlemail.com with ESMTPSA id q12sm18667504wmd.8.2017.03.21.11.33.38 (version=TLS1_2 cipher=ECDHE-RSA-AES128-GCM-SHA256 bits=128/128); Tue, 21 Mar 2017 11:33:38 -0700 (PDT) From: Jerome Brunet To: Michael Turquette , Stephen Boyd , Kevin Hilman Subject: [RFC 4/7] clk: add support for clock protection Date: Tue, 21 Mar 2017 19:33:27 +0100 Message-Id: <20170321183330.26722-5-jbrunet@baylibre.com> X-Mailer: git-send-email 2.9.3 In-Reply-To: <20170321183330.26722-1-jbrunet@baylibre.com> References: <20170321183330.26722-1-jbrunet@baylibre.com> X-CRM114-Version: 20100106-BlameMichelson ( TRE 0.8.0 (BSD) ) MR-646709E3 X-CRM114-CacheID: sfid-20170321_113359_935414_E5CD8C9F X-CRM114-Status: GOOD ( 21.12 ) X-BeenThere: linux-amlogic@lists.infradead.org X-Mailman-Version: 2.1.21 Precedence: list List-Id: List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Cc: linux-amlogic@lists.infradead.org, linux-clk@vger.kernel.org, Jerome Brunet MIME-Version: 1.0 Sender: "linux-amlogic" Errors-To: linux-amlogic-bounces+patchwork-linux-amlogic=patchwork.kernel.org@lists.infradead.org X-Virus-Scanned: ClamAV using ClamSMTP The patch adds clk_protect and clk_unprotect to the CCF API. These functions allow a consumer to inform the system that the rate of clock is critical to for its operations and it can't tolerate other consumers changing the rate or introducing glitches while the clock is protected. Signed-off-by: Jerome Brunet --- drivers/clk/clk.c | 177 ++++++++++++++++++++++++++++++++++++++++--- include/linux/clk-provider.h | 1 + include/linux/clk.h | 29 +++++++ 3 files changed, 197 insertions(+), 10 deletions(-) diff --git a/drivers/clk/clk.c b/drivers/clk/clk.c index fa77a1841e0f..69db8cc15063 100644 --- a/drivers/clk/clk.c +++ b/drivers/clk/clk.c @@ -60,6 +60,7 @@ struct clk_core { bool orphan; unsigned int enable_count; unsigned int prepare_count; + unsigned int protect_count; unsigned long min_rate; unsigned long max_rate; unsigned long accuracy; @@ -84,6 +85,7 @@ struct clk { const char *con_id; unsigned long min_rate; unsigned long max_rate; + unsigned long protect_ucount; struct hlist_node clks_node; }; @@ -160,6 +162,11 @@ static bool clk_core_is_prepared(struct clk_core *core) return core->ops->is_prepared(core->hw); } +static bool clk_core_is_protected(struct clk_core *core) +{ + return core->protect_count; +} + static bool clk_core_is_enabled(struct clk_core *core) { /* @@ -328,6 +335,11 @@ bool clk_hw_is_prepared(const struct clk_hw *hw) return clk_core_is_prepared(hw->core); } +bool clk_hw_is_protected(const struct clk_hw *hw) +{ + return clk_core_is_protected(hw->core); +} + bool clk_hw_is_enabled(const struct clk_hw *hw) { return clk_core_is_enabled(hw->core); @@ -584,6 +596,89 @@ int clk_prepare(struct clk *clk) } EXPORT_SYMBOL_GPL(clk_prepare); +static void clk_core_unprotect(struct clk_core *core) +{ + lockdep_assert_held(&prepare_lock); + + if (!core) + return; + + if (WARN_ON(core->protect_count == 0)) + return; + + if (--core->protect_count > 0) + return; + + clk_core_unprotect(core->parent); +} + +/** + * clk_unprotect - unprotect a clock source + * @clk: the clk being unprotected + * + * clk_unprotect shall be used when a consumer no longer depends on the clock + * rate and can tolerate glitches. As with clk_unprepare and clk_enable, calls + * to clk_unprotect must be balanced with clk_protect. + * clk_protect may sleep + */ +void clk_unprotect(struct clk *clk) +{ + if (!clk) + return; + + clk_prepare_lock(); + + if (WARN_ON(clk->protect_ucount <= 0)) { + /* + * There is something wrong with this consumer protect count. + * Stop here before messing with the provider + */ + clk_prepare_unlock(); + return; + } + + clk_core_unprotect(clk->core); + clk->protect_ucount--; + clk_prepare_unlock(); +} +EXPORT_SYMBOL_GPL(clk_unprotect); + +static void clk_core_protect(struct clk_core *core) +{ + lockdep_assert_held(&prepare_lock); + + if (!core) + return; + + if (core->protect_count == 0) + clk_core_protect(core->parent); + + core->protect_count++; +} + +/** + * clk_protect - protect a clock source + * @clk: the clk being protected + * + * clk_protect can be used when a consumer depends on the clock rate and can't + * tolerate any glitches. The consumer protecting the clock can still make + * adjustment to clock, if it is the only one protecting the clock. Other + * consumers can still use the clock but won't be able to adjust the rate or + * reparent the clock while it is protected. + * clk_protect may sleep. + */ +void clk_protect(struct clk *clk) +{ + if (!clk) + return; + + clk_prepare_lock(); + clk_core_protect(clk->core); + clk->protect_ucount++; + clk_prepare_unlock(); +} +EXPORT_SYMBOL_GPL(clk_protect); + static void clk_core_disable(struct clk_core *core) { lockdep_assert_held(&enable_lock); @@ -838,7 +933,9 @@ static int clk_core_determine_round(struct clk_core *core, { long rate; - if (core->ops->determine_rate) { + if (clk_core_is_protected(core)) { + req->rate = core->rate; + } else if (core->ops->determine_rate) { return core->ops->determine_rate(core->hw, req); } else if (core->ops->round_rate) { rate = core->ops->round_rate(core->hw, req->rate, @@ -1381,7 +1478,7 @@ static struct clk_core *clk_calc_new_rates(struct clk_core *core, req.min_rate = min_rate; req.max_rate = max_rate; - clk_core_init_rate_req(core, req); + clk_core_init_rate_req(core, &req); ret = clk_core_determine_round(core, &req); if (ret < 0) @@ -1637,8 +1734,14 @@ int clk_set_rate(struct clk *clk, unsigned long rate) /* prevent racing with updates to the clock topology */ clk_prepare_lock(); + if (clk->protect_ucount) + clk_core_unprotect(clk->core); + ret = clk_core_set_rate_nolock(clk->core, rate); + if (clk->protect_ucount) + clk_core_protect(clk->core); + clk_prepare_unlock(); return ret; @@ -1669,12 +1772,18 @@ int clk_set_rate_range(struct clk *clk, unsigned long min, unsigned long max) clk_prepare_lock(); + if (clk->protect_ucount) + clk_core_unprotect(clk->core); + if (min != clk->min_rate || max != clk->max_rate) { clk->min_rate = min; clk->max_rate = max; ret = clk_core_set_rate_nolock(clk->core, clk->core->req_rate); } + if (clk->protect_ucount) + clk_core_protect(clk->core); + clk_prepare_unlock(); return ret; @@ -1815,6 +1924,9 @@ static int clk_core_set_parent(struct clk_core *core, struct clk_core *parent) if ((core->flags & CLK_SET_PARENT_GATE) && core->prepare_count) return -EBUSY; + if (clk_core_is_protected(core)) + return -EBUSY; + /* try finding the new parent index */ if (parent) { p_index = clk_fetch_parent_index(core, parent); @@ -1878,11 +1990,24 @@ static int clk_core_set_parent_lock(struct clk_core *core, */ int clk_set_parent(struct clk *clk, struct clk *parent) { + int ret; + if (!clk) return 0; - return clk_core_set_parent_lock(clk->core, - parent ? parent->core : NULL); + clk_prepare_lock(); + + if (clk->protect_ucount) + clk_core_unprotect(clk->core); + + ret = clk_core_set_parent(clk->core, parent ? parent->core : NULL); + + if (clk->protect_ucount) + clk_core_protect(clk->core); + + clk_prepare_unlock(); + + return ret; } EXPORT_SYMBOL_GPL(clk_set_parent); @@ -1893,7 +2018,10 @@ static int clk_core_set_phase(struct clk_core *core, int degrees) if (!core) return 0; - trace_clk_set_phase(clk->core, degrees); + if (clk_core_is_protected(core)) + return -EBUSY; + + trace_clk_set_phase(core, degrees); if (core->ops->set_phase) ret = core->ops->set_phase(core->hw, degrees); @@ -1936,7 +2064,15 @@ int clk_set_phase(struct clk *clk, int degrees) degrees += 360; clk_prepare_lock(); + + if (clk->protect_ucount) + clk_core_unprotect(clk->core); + ret = clk_core_set_phase(clk->core, degrees); + + if (clk->protect_ucount) + clk_core_protect(clk->core); + clk_prepare_unlock(); return ret; @@ -2023,11 +2159,12 @@ static void clk_summary_show_one(struct seq_file *s, struct clk_core *c, if (!c) return; - seq_printf(s, "%*s%-*s %11d %12d %11lu %10lu %-3d\n", + seq_printf(s, "%*s%-*s %11d %12d %12d %11lu %10lu %-3d\n", level * 3 + 1, "", 30 - level * 3, c->name, - c->enable_count, c->prepare_count, clk_core_get_rate(c), - clk_core_get_accuracy(c), clk_core_get_phase(c)); + c->enable_count, c->prepare_count, c->protect_count, + clk_core_get_rate(c), clk_core_get_accuracy(c), + clk_core_get_phase(c)); } static void clk_summary_show_subtree(struct seq_file *s, struct clk_core *c, @@ -2049,8 +2186,8 @@ static int clk_summary_show(struct seq_file *s, void *data) struct clk_core *c; struct hlist_head **lists = (struct hlist_head **)s->private; - seq_puts(s, " clock enable_cnt prepare_cnt rate accuracy phase\n"); - seq_puts(s, "----------------------------------------------------------------------------------------\n"); + seq_puts(s, " clock enable_cnt prepare_cnt protect_cnt rate accuracy phase\n"); + seq_puts(s, "----------------------------------------------------------------------------------------------------\n"); clk_prepare_lock(); @@ -2085,6 +2222,7 @@ static void clk_dump_one(struct seq_file *s, struct clk_core *c, int level) seq_printf(s, "\"%s\": { ", c->name); seq_printf(s, "\"enable_count\": %d,", c->enable_count); seq_printf(s, "\"prepare_count\": %d,", c->prepare_count); + seq_printf(s, "\"protect_count\": %d,", c->protect_count); seq_printf(s, "\"rate\": %lu,", clk_core_get_rate(c)); seq_printf(s, "\"accuracy\": %lu,", clk_core_get_accuracy(c)); seq_printf(s, "\"phase\": %d", clk_core_get_phase(c)); @@ -2191,6 +2329,11 @@ static int clk_debug_create_one(struct clk_core *core, struct dentry *pdentry) if (!d) goto err_out; + d = debugfs_create_u32("clk_protect_count", S_IRUGO, core->dentry, + (u32 *)&core->protect_count); + if (!d) + goto err_out; + d = debugfs_create_u32("clk_notifier_count", S_IRUGO, core->dentry, (u32 *)&core->notifier_count); if (!d) @@ -2747,6 +2890,11 @@ void clk_unregister(struct clk *clk) if (clk->core->prepare_count) pr_warn("%s: unregistering prepared clock: %s\n", __func__, clk->core->name); + + if (clk->core->protect_count) + pr_warn("%s: unregistering protected clock: %s\n", + __func__, clk->core->name); + kref_put(&clk->core->ref, __clk_release); unlock: clk_prepare_unlock(); @@ -2905,6 +3053,15 @@ void __clk_put(struct clk *clk) clk_prepare_lock(); + /* Protect count not balanced: warn and sanitize */ + if (clk->protect_ucount) { + pr_warn("%s: releasing protected clock: %s\n", + __func__, clk->core->name); + + for (; clk->protect_ucount; clk->protect_ucount--) + clk_core_unprotect(clk->core); + } + hlist_del(&clk->clks_node); if (clk->min_rate > clk->core->req_rate || clk->max_rate < clk->core->req_rate) diff --git a/include/linux/clk-provider.h b/include/linux/clk-provider.h index a428aec36ace..705a158d9b8f 100644 --- a/include/linux/clk-provider.h +++ b/include/linux/clk-provider.h @@ -739,6 +739,7 @@ unsigned long clk_hw_get_rate(const struct clk_hw *hw); unsigned long __clk_get_flags(struct clk *clk); unsigned long clk_hw_get_flags(const struct clk_hw *hw); bool clk_hw_is_prepared(const struct clk_hw *hw); +bool clk_hw_is_protected(const struct clk_hw *hw); bool clk_hw_is_enabled(const struct clk_hw *hw); bool __clk_is_enabled(struct clk *clk); struct clk *__clk_lookup(const char *name); diff --git a/include/linux/clk.h b/include/linux/clk.h index e9d36b3e49de..90b72ead4411 100644 --- a/include/linux/clk.h +++ b/include/linux/clk.h @@ -265,6 +265,30 @@ struct clk *devm_clk_get(struct device *dev, const char *id); */ struct clk *devm_get_clk_from_child(struct device *dev, struct device_node *np, const char *con_id); +/** + * clk_protect - inform the system when the clock source should be protected. + * @clk: clock source + * + * This function informs the system that the consumer protecting the clock + * depends on the rate of the clock source and can't tolerate any glitches + * introduced by further clock rate change or re-parenting of the clock source. + * + * Must not be called from within atomic context. + */ +void clk_protect(struct clk *clk); + +/** + * clk_unprotect - release the protection of the clock source. + * @clk: clock source + * + * This function informs the system that the consumer previously protecting the + * clock source can now deal with other consumer altering the clock source. + * + * The caller must balance the number of protect and unprotect calls. + * + * Must not be called from within atomic context. + */ +void clk_unprotect(struct clk *clk); /** * clk_enable - inform the system when the clock source should be running. @@ -460,6 +484,11 @@ static inline void clk_put(struct clk *clk) {} static inline void devm_clk_put(struct device *dev, struct clk *clk) {} + +static inline void clk_protect(struct clk *clk) {} + +static inline void clk_unprotect(struct clk *clk) {} + static inline int clk_enable(struct clk *clk) { return 0;