From patchwork Mon Jun 12 19:44:33 2017 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Jerome Brunet X-Patchwork-Id: 9782703 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 164AA602DA for ; Mon, 12 Jun 2017 19:45:31 +0000 (UTC) Received: from mail.wl.linuxfoundation.org (localhost [127.0.0.1]) by mail.wl.linuxfoundation.org (Postfix) with ESMTP id 062DA27F86 for ; Mon, 12 Jun 2017 19:45:31 +0000 (UTC) Received: by mail.wl.linuxfoundation.org (Postfix, from userid 486) id EEDC3284F1; Mon, 12 Jun 2017 19:45:30 +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=unavailable 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 3415C27F86 for ; Mon, 12 Jun 2017 19:45:30 +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=8FhALvf43ND0tsIx1oMO0sEKBLoVm9zS4zefT4ruvqI=; b=oo2BzR8KmFsFqVY+Q4n32iKsuV +v3i2/Efk9dxkyAVwcOndX72KJrwlG83vr6lkEOltfg2vYMYXsnzmmKdHk46TXlQj7mlrsrSgg6ms GT13LkwkFkifdQ79AnSg7DKdSoCCMMNSadR+EBN2CTiMICGjEU/teDgugAppG7Ivdqa1zDMQ/uIX/ 0B1b0Aso/7VHtaTF22pxXT7belQ3mo1+Rcl9v00qPnMHtSqSuRMOB/jsFEyYTwm+A0rgTaXWZoeun MGtWlsRvrr8l4wMBbhOktVPpv6zgBRg2eJl0Tkrnr1fBpqPz6l0y3pMkXWoIGL3TLpS1eVIAgoDpW eQVfbTfA==; 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 1dKVH8-0004s7-Jw; Mon, 12 Jun 2017 19:45:22 +0000 Received: from mail-wr0-x229.google.com ([2a00:1450:400c:c0c::229]) by bombadil.infradead.org with esmtps (Exim 4.87 #1 (Red Hat Linux)) id 1dKVGz-0003RM-7P for linux-amlogic@lists.infradead.org; Mon, 12 Jun 2017 19:45:18 +0000 Received: by mail-wr0-x229.google.com with SMTP id v111so110852049wrc.3 for ; Mon, 12 Jun 2017 12:44:54 -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=l8sYroLIVCELIkyeJvyHwylPPt9HnTo2yCG9xdauVhA=; b=KBSQ0Gj/wcMkq+xnlRXc0JbbQsNq5kSfTkcLsVekbJ+6rqps2Jpbgg3ERNTxsbU8ZJ ZUdCc8yGBUk22Z4+OI+l7DIyiC2RH+GjF5WkfwjvchGnRrsgPxd8csj+pJR76vQu9297 RSwCp2CmOzyoN4S/DjYa+H1LxoZsMnOq8mkS+WFOFzwVXaQgrofoFIVOk/3BRw4owAos FxeSITcJIZYoJDjHZaXrxHF4UoS5X23jbJvrg7KLpddP8I8Eww0BO43l+006M0cWB2NS jIrhynHIEXEX3ctLSwym56B3x7zg8HehR7h/OM9OzpLpfJYO4G71w6rJSR84VKYLy4Dx 6Qhg== 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=l8sYroLIVCELIkyeJvyHwylPPt9HnTo2yCG9xdauVhA=; b=WWR0BQGOX3W3HA4VEWPmDU+TrtB4cPCXAyy8dVR5dk1tplVpGy0TfMwAzvleNYL7Lu gSVpQTuI5kZ4PwFFG9VUf2bNPHGAKlBpk0AUFmwdqL8ohPjhxY8ZUaXHx9V6C+HrID95 di691QBYWtRMlTSEvxklBFLmapKgg2KxxUOBR8V+JzLSMzRdDtIRRe1GBTTXXOLWKA0V cSrqaSpt5aNrtEMCBIS9one/+zD11weEoxb8DM6PD8p/di6bDzqCFFrxT7nmWbbsLldK +DazIU+qCkQ+G2uOgWXrgh+JWR2Sac96KvLChqLneKDiQQLMemnAWXUjGtUcwfwsg7vs Vc3g== X-Gm-Message-State: AKS2vOzMQeJJqc9EXkYm7hIFjW/i9leXZPtMoq1giMRp05LwVHLZP3fH pTNiTiAwkeofHlgL X-Received: by 10.28.113.85 with SMTP id m82mr8868843wmc.21.1497296692802; Mon, 12 Jun 2017 12:44:52 -0700 (PDT) Received: from localhost.localdomain (cag06-3-82-243-161-21.fbx.proxad.net. [82.243.161.21]) by smtp.googlemail.com with ESMTPSA id 70sm13250797wmu.28.2017.06.12.12.44.51 (version=TLS1_2 cipher=ECDHE-RSA-AES128-GCM-SHA256 bits=128/128); Mon, 12 Jun 2017 12:44:52 -0700 (PDT) From: Jerome Brunet To: Michael Turquette , Stephen Boyd , linux-clk@vger.kernel.org Subject: [PATCH v3 05/10] clk: add support for clock protection Date: Mon, 12 Jun 2017 21:44:33 +0200 Message-Id: <20170612194438.12298-6-jbrunet@baylibre.com> X-Mailer: git-send-email 2.9.4 In-Reply-To: <20170612194438.12298-1-jbrunet@baylibre.com> References: <20170612194438.12298-1-jbrunet@baylibre.com> X-CRM114-Version: 20100106-BlameMichelson ( TRE 0.8.0 (BSD) ) MR-646709E3 X-CRM114-CacheID: sfid-20170612_124513_648921_593A279C X-CRM114-Status: GOOD ( 22.94 ) 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: Boris Brezillon , Kevin Hilman , Linus Walleij , Russell King , linux-amlogic@lists.infradead.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 | 205 +++++++++++++++++++++++++++++++++++++++++-- include/linux/clk-provider.h | 1 + include/linux/clk.h | 29 ++++++ 3 files changed, 229 insertions(+), 6 deletions(-) diff --git a/drivers/clk/clk.c b/drivers/clk/clk.c index 163cb9832f10..d688b8f59a59 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_count; struct hlist_node clks_node; }; @@ -148,6 +150,11 @@ static void clk_enable_unlock(unsigned long flags) spin_unlock_irqrestore(&enable_lock, flags); } +static bool clk_core_rate_is_protected(struct clk_core *core) +{ + return core->protect_count; +} + static bool clk_core_is_prepared(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_rate_is_protected(const struct clk_hw *hw) +{ + return clk_core_rate_is_protected(hw->core); +} + bool clk_hw_is_enabled(const struct clk_hw *hw) { return clk_core_is_enabled(hw->core); @@ -466,6 +478,102 @@ EXPORT_SYMBOL_GPL(__clk_mux_determine_rate_closest); /*** clk api ***/ +static void clk_core_rate_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_rate_unprotect(core->parent); +} + +/** + * clk_rate_unprotect - unprotect the rate of a clock source + * @clk: the clk being unprotected + * + * clk_unprotect completes a critical section during which the clock + * consumer cannot tolerate any change to the clock rate. If no other clock + * consumers have protected clocks in the parent chain, then calls to this + * function will allow the clocks in the parent chain to change rates + * freely. + * + * Unlike the clk_set_rate_range method, which allows the rate to change + * within a given range, protected clocks cannot have their rate changed, + * either directly or indirectly due to changes further up the parent chain + * of clocks. + * + * Calls to clk_unprotect must be balanced with calls to clk_protect. Calls + * to this function may sleep, and do not return error status. + */ +void clk_rate_unprotect(struct clk *clk) +{ + if (!clk) + return; + + clk_prepare_lock(); + + /* + * if there is something wrong with this consumer protect count, stop + * here before messing with the provider + */ + if (WARN_ON(clk->protect_count <= 0)) + goto out; + + clk_core_rate_unprotect(clk->core); + clk->protect_count--; +out: + clk_prepare_unlock(); +} +EXPORT_SYMBOL_GPL(clk_rate_unprotect); + +static void clk_core_rate_protect(struct clk_core *core) +{ + lockdep_assert_held(&prepare_lock); + + if (!core) + return; + + if (core->protect_count == 0) + clk_core_rate_protect(core->parent); + + core->protect_count++; +} + +/** + * clk_rate_protect - protect a clock source + * @clk: the clk being protected + * + * clk_protect begins a critical section during which the clock consumer + * cannot tolerate any change to the clock rate. This results in all clocks + * up the parent chain to also be rate-protected. + * + * Unlike the clk_set_rate_range method, which allows the rate to change + * within a given range, protected clocks cannot have their rate changed, + * either directly or indirectly due to changes further up the parent chain + * of clocks. + * + * Calls to clk_protect should be balanced with calls to clk_unprotect. + * Calls to this function may sleep, and do not return error status. + */ +void clk_rate_protect(struct clk *clk) +{ + if (!clk) + return; + + clk_prepare_lock(); + clk_core_rate_protect(clk->core); + clk->protect_count++; + clk_prepare_unlock(); +} +EXPORT_SYMBOL_GPL(clk_rate_protect); + static void clk_core_unprepare(struct clk_core *core) { lockdep_assert_held(&prepare_lock); @@ -838,7 +946,15 @@ static int clk_core_determine_round(struct clk_core *core, { long rate; - if (core->ops->determine_rate) { + /* + * At this point, core protection will be disabled if + * - if the provider is not protected at all + * - if the calling consumer is the only one protecting the + * provider (and only once) + */ + if (clk_core_rate_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, @@ -944,10 +1060,17 @@ long clk_round_rate(struct clk *clk, unsigned long rate) clk_prepare_lock(); + if (clk->protect_count) + clk_core_rate_unprotect(clk->core); + clk_core_get_boundaries(clk->core, &req.min_rate, &req.max_rate); req.rate = rate; ret = clk_core_round_rate_nolock(clk->core, &req); + + if (clk->protect_count) + clk_core_rate_protect(clk->core); + clk_prepare_unlock(); if (ret) @@ -1575,15 +1698,24 @@ static unsigned long clk_core_req_round_rate_nolock(struct clk_core *core, { int ret; struct clk_rate_request req; + unsigned int cnt = core->protect_count; if (!core) return 0; + /* simulate what the rate would be if it could be freely set */ + while (core->protect_count) + clk_core_rate_unprotect(core); + clk_core_get_boundaries(core, &req.min_rate, &req.max_rate); req.rate = req_rate; ret = clk_core_round_rate_nolock(core, &req); + /* restore the protection */ + while (core->protect_count < cnt) + clk_core_rate_protect(core); + return ret ? 0 : req.rate; } @@ -1602,6 +1734,10 @@ static int clk_core_set_rate_nolock(struct clk_core *core, if (rate == clk_core_get_rate_nolock(core)) return 0; + /* fail on a direct rate set of a protected provider */ + if (clk_core_rate_is_protected(core)) + return -EBUSY; + if ((core->flags & CLK_SET_RATE_GATE) && core->prepare_count) return -EBUSY; @@ -1658,8 +1794,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_count) + clk_core_rate_unprotect(clk->core); + ret = clk_core_set_rate_nolock(clk->core, rate); + if (clk->protect_count) + clk_core_rate_protect(clk->core); + clk_prepare_unlock(); return ret; @@ -1690,12 +1832,18 @@ int clk_set_rate_range(struct clk *clk, unsigned long min, unsigned long max) clk_prepare_lock(); + if (clk->protect_count) + clk_core_rate_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_count) + clk_core_rate_protect(clk->core); + clk_prepare_unlock(); return ret; @@ -1837,6 +1985,9 @@ static int clk_core_set_parent_nolock(struct clk_core *core, if ((core->flags & CLK_SET_PARENT_GATE) && core->prepare_count) return -EBUSY; + if (clk_core_rate_is_protected(core)) + return -EBUSY; + /* try finding the new parent index */ if (parent) { p_index = clk_fetch_parent_index(core, parent); @@ -1894,8 +2045,16 @@ int clk_set_parent(struct clk *clk, struct clk *parent) return 0; clk_prepare_lock(); + + if (clk->protect_count) + clk_core_rate_unprotect(clk->core); + ret = clk_core_set_parent_nolock(clk->core, parent ? parent->core : NULL); + + if (clk->protect_count) + clk_core_rate_protect(clk->core); + clk_prepare_unlock(); return ret; @@ -1909,6 +2068,9 @@ static int clk_core_set_phase_nolock(struct clk_core *core, int degrees) if (!core) return 0; + if (clk_core_rate_is_protected(core)) + return -EBUSY; + trace_clk_set_phase(core, degrees); if (core->ops->set_phase) @@ -1952,7 +2114,15 @@ int clk_set_phase(struct clk *clk, int degrees) degrees += 360; clk_prepare_lock(); + + if (clk->protect_count) + clk_core_rate_unprotect(clk->core); + ret = clk_core_set_phase_nolock(clk->core, degrees); + + if (clk->protect_count) + clk_core_rate_protect(clk->core); + clk_prepare_unlock(); return ret; @@ -2039,11 +2209,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, @@ -2065,8 +2236,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(); @@ -2101,6 +2272,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)); @@ -2231,6 +2403,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) @@ -2794,6 +2971,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(); @@ -2952,6 +3134,17 @@ void __clk_put(struct clk *clk) clk_prepare_lock(); + /* + * Before calling clk_put, all calls to clk_rate_protect from a given + * user must be balanced with calls to clk_rate_unprotect and by that + * same user + */ + WARN_ON(clk->protect_count); + + /* We voiced our concern, let's sanitize the situation */ + for (; clk->protect_count; clk->protect_count--) + clk_core_rate_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..ebd7df5f375f 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_rate_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 91bd464f4c9b..b60c36f2e6b0 100644 --- a/include/linux/clk.h +++ b/include/linux/clk.h @@ -331,6 +331,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_rate_protect - inform the system when the clock rate must 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_rate_protect(struct clk *clk); + +/** + * clk_rate_unprotect - release the protection of the clock source. + * @clk: clock source + * + * This function informs the system that the consumer previously protecting the + * clock rate can now deal with other consumer altering the clock source rate + * + * The caller must balance the number of rate_protect and rate_unprotect calls. + * + * Must not be called from within atomic context. + */ +void clk_rate_unprotect(struct clk *clk); /** * clk_enable - inform the system when the clock source should be running. @@ -583,6 +607,11 @@ static inline void clk_bulk_put(int num_clks, struct clk_bulk_data *clks) {} 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;