linux-kernel.vger.kernel.org archive mirror
 help / color / mirror / Atom feed
From: Derek Basehore <dbasehore@chromium.org>
To: linux-kernel@vger.kernel.org
Cc: linux-clk@vger.kernel.org, linux-arm-kernel@lists.infradead.org,
	linux-rockchip@lists.infradead.org, linux-doc@vger.kernel.org,
	sboyd@kernel.org, mturquette@baylibre.com, heiko@sntech.de,
	aisheng.dong@nxp.com, mchehab+samsung@kernel.org, corbet@lwn.net,
	Derek Basehore <dbasehore@chromium.org>
Subject: [PATCH 4/6] clk: add pre clk changes support
Date: Tue, 23 Oct 2018 18:31:30 -0700	[thread overview]
Message-ID: <20181024013132.115907-5-dbasehore@chromium.org> (raw)
In-Reply-To: <20181024013132.115907-1-dbasehore@chromium.org>

This adds a new clk_op, pre_rate_req. It allows clks to setup an
intermediate state when clk rates are changed. One use case for this
is when a clk needs to switch to a safe parent when its PLL ancestor
changes rates. This is needed when a PLL cannot guarantee that it will
not exceed the new rate before it locks. The set_rate, set_parent, and
set_rate_and_parent callbacks are used with the pre_rate_req callback.

Signed-off-by: Derek Basehore <dbasehore@chromium.org>
---
 drivers/clk/clk.c            | 136 +++++++++++++++++++++++++++++++++--
 include/linux/clk-provider.h |  10 +++
 2 files changed, 139 insertions(+), 7 deletions(-)

diff --git a/drivers/clk/clk.c b/drivers/clk/clk.c
index 1db44b4e46b0..36a2f929ab8d 100644
--- a/drivers/clk/clk.c
+++ b/drivers/clk/clk.c
@@ -39,6 +39,7 @@ static int enable_refcnt;
 static HLIST_HEAD(clk_root_list);
 static HLIST_HEAD(clk_orphan_list);
 static LIST_HEAD(clk_notifier_list);
+static LIST_HEAD(pre_change_free_list);
 
 /***    private data structures    ***/
 
@@ -1896,6 +1897,74 @@ static struct clk_core *clk_propagate_rate_change(struct clk_core *core,
 	return fail_clk;
 }
 
+static int clk_pre_rate_req(struct list_head *pre_list, struct clk_core *core)
+{
+	struct clk_core *child, *parent = core->parent;
+	struct clk_rate_request next, pre;
+	struct clk_change *change;
+	int ret;
+
+	hlist_for_each_entry(child, &core->children, child_node) {
+		ret = clk_pre_rate_req(pre_list, child);
+		if (ret)
+			return ret;
+	}
+
+	if (core->new_child) {
+		ret = clk_pre_rate_req(pre_list, child);
+		if (ret)
+			return ret;
+	}
+
+	if (!core->ops->pre_rate_req)
+		return 0;
+
+	if (core->change.parent)
+		parent = core->change.parent;
+
+	if (parent) {
+		next.best_parent_hw = parent->hw;
+		next.best_parent_rate = parent->change.rate;
+	}
+
+	next.rate = core->change.rate;
+	clk_core_get_boundaries(core, &next.min_rate, &next.max_rate);
+
+	ret = core->ops->pre_rate_req(core->hw, &next, &pre);
+	if (ret < 0)
+		return ret;
+	else if (!ret)
+		goto out;
+
+	/*
+	 * We allocate a change for each clk with the pre_rate_req op. If we run
+	 * out, that's because we wrapped around to a clk again in the
+	 * pre_rate_req step which is not allowed.
+	 */
+	change = list_first_entry(&pre_change_free_list, struct clk_change,
+				  change_list);
+	if (IS_ERR_OR_NULL(change)) {
+		pr_err("%s: pre_rate_req loop detected on clk %s. All pre_rate_req clk_change structs are used\n",
+				__func__, core->name);
+		return -EDEADLK;
+	}
+
+	change->core = core;
+	change->rate = pre.rate;
+	change->parent = pre.best_parent_hw ? pre.best_parent_hw->core : NULL;
+	list_move(&change->change_list, pre_list);
+
+out:
+	/* If the pre req req pulls in a new parent, add it to the call chain */
+	if (parent != change->parent) {
+		ret = clk_pre_rate_req(pre_list, change->parent);
+		if (ret)
+			return ret;
+	}
+
+	return 0;
+}
+
 /*
  * walk down a subtree and set the new rates notifying the rate
  * change on the way
@@ -2065,6 +2134,8 @@ static int clk_core_set_rate_nolock(struct clk_core *core,
 	struct clk_core *top, *fail_clk;
 	struct clk_change *change, *tmp;
 	unsigned long rate;
+	LIST_HEAD(pre_changes);
+	LIST_HEAD(post_changes);
 	LIST_HEAD(changes);
 	int ret = 0;
 
@@ -2092,6 +2163,14 @@ static int clk_core_set_rate_nolock(struct clk_core *core,
 
 	clk_calc_subtree(core);
 
+	/* We need a separate list for these changes due to error handling. */
+	ret = clk_pre_rate_req(&pre_changes, top);
+	if (ret) {
+		pr_debug("%s: failed pre_rate_req via top clk %s: %d\n",
+				__func__, top->name, ret);
+		goto pre_rate_req;
+	}
+
 	/* Construct the list of changes */
 	clk_prepare_changes(&changes, top);
 
@@ -2100,11 +2179,19 @@ static int clk_core_set_rate_nolock(struct clk_core *core,
 	if (fail_clk) {
 		pr_debug("%s: failed to set %s rate\n", __func__,
 				fail_clk->name);
-		clk_propagate_rate_change(top, ABORT_RATE_CHANGE);
 		ret = -EBUSY;
-		goto err;
+		goto prop_rate;
+	}
+
+	ret = clk_change_rates(&pre_changes);
+	if (ret) {
+		pr_debug("%s: rate rate changes failed via top clk %s: %d\n",
+				__func__, top->name, ret);
+		goto pre_rate_req;
 	}
 
+	list_splice_tail(&post_changes, &changes);
+
 	/* change the rates */
 	ret = clk_change_rates(&changes);
 	list_for_each_entry_safe(change, tmp, &changes, change_list) {
@@ -2112,16 +2199,28 @@ static int clk_core_set_rate_nolock(struct clk_core *core,
 		change->parent = NULL;
 		list_del_init(&change->change_list);
 	}
-
 	if (ret) {
 		pr_debug("%s: failed to set %s rate via top clk %s\n", __func__,
 				core->name, top->name);
-		clk_propagate_rate_change(top, ABORT_RATE_CHANGE);
-		goto err;
+		goto change_rates;
 	}
 
+	list_splice(&pre_changes, &pre_change_free_list);
 	core->req_rate = req_rate;
-err:
+
+	return 0;
+
+change_rates:
+	WARN_ON(clk_change_rates(&pre_changes));
+pre_rate_req:
+	list_splice(&pre_changes, &pre_change_free_list);
+prop_rate:
+	clk_propagate_rate_change(top, ABORT_RATE_CHANGE);
+	list_for_each_entry_safe(change, tmp, &changes, change_list) {
+		change->rate = 0;
+		change->parent = NULL;
+		list_del_init(&change->change_list);
+	}
 	clk_pm_runtime_put(core);
 
 	return ret;
@@ -3139,7 +3238,9 @@ static int __clk_core_init(struct clk_core *core)
 
 	/* check that clk_ops are sane.  See Documentation/driver-api/clk.rst */
 	if (core->ops->set_rate &&
-	    !((core->ops->round_rate || core->ops->determine_rate) &&
+	    !((core->ops->round_rate ||
+	       core->ops->determine_rate ||
+	       core->ops->pre_rate_req) &&
 	      core->ops->recalc_rate)) {
 		pr_err("%s: %s must implement .round_rate or .determine_rate in addition to .recalc_rate\n",
 		       __func__, core->name);
@@ -3175,6 +3276,20 @@ static int __clk_core_init(struct clk_core *core)
 				"%s: invalid NULL in %s's .parent_names\n",
 				__func__, core->name);
 
+	/* Allocate a clk_change struct for pre_rate_reqs */
+	if (core->ops->pre_rate_req) {
+		struct clk_change *change = kzalloc(sizeof(*change),
+				GFP_KERNEL);
+		if (!change) {
+			ret = -ENOMEM;
+			kfree(core->parents);
+			goto out;
+		}
+
+		INIT_LIST_HEAD(&change->change_list);
+		list_add(&pre_change_free_list, &change->change_list);
+	}
+
 	core->parent = __clk_init_parent(core);
 
 	/*
@@ -3476,6 +3591,13 @@ static void __clk_release(struct kref *ref)
 	while (--i >= 0)
 		kfree_const(core->parent_names[i]);
 
+	if (core->ops->pre_rate_req) {
+		struct clk_change *change =
+			list_first_entry(&pre_change_free_list,
+					struct clk_change, change_list);
+		list_del(&change->change_list);
+		kfree(change);
+	}
 	kfree(core->parent_names);
 	kfree_const(core->name);
 	kfree(core);
diff --git a/include/linux/clk-provider.h b/include/linux/clk-provider.h
index 60c51871b04b..98a65c6c326d 100644
--- a/include/linux/clk-provider.h
+++ b/include/linux/clk-provider.h
@@ -138,6 +138,13 @@ struct clk_duty {
  *		actually supported by the clock, and optionally the parent clock
  *		that should be used to provide the clock rate.
  *
+ * @pre_rate_req: Given the next state that the clk will enter via a
+ *		clk_rate_request struct, next, fill in another clk_rate_request
+ *		struct, pre, with any desired intermediate state to change to
+ *		before the state in next is applied. Returns positive to request
+ *		an intermediate state transition, 0 for no transition, and
+ *		-EERROR otherwise.
+ *
  * @set_parent:	Change the input source of this clock; for clocks with multiple
  *		possible parents specify a new parent by passing in the index
  *		as a u8 corresponding to the parent in either the .parent_names
@@ -236,6 +243,9 @@ struct clk_ops {
 					unsigned long *parent_rate);
 	int		(*determine_rate)(struct clk_hw *hw,
 					  struct clk_rate_request *req);
+	int		(*pre_rate_req)(struct clk_hw *hw,
+					const struct clk_rate_request *next,
+					struct clk_rate_request *pre);
 	int		(*set_parent)(struct clk_hw *hw, u8 index);
 	u8		(*get_parent)(struct clk_hw *hw);
 	int		(*set_rate)(struct clk_hw *hw, unsigned long rate,
-- 
2.19.1.568.g152ad8e336-goog


  parent reply	other threads:[~2018-10-24  3:09 UTC|newest]

Thread overview: 21+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2018-10-24  1:31 [PATCH 0/6] Coordinated Clks Derek Basehore
2018-10-24  1:31 ` [PATCH 1/6] clk: Remove recursion in clk_core_{prepare,enable}() Derek Basehore
2018-10-24  9:51   ` Jerome Brunet
2018-10-24 20:15     ` dbasehore .
2018-10-24 20:23       ` dbasehore .
2018-10-24 20:50       ` dbasehore .
2018-10-25  8:57         ` Jerome Brunet
2018-10-24 20:36     ` dbasehore .
2018-10-25  8:12       ` Jerome Brunet
2018-10-24 13:07   ` Stephen Boyd
2018-10-24 20:09     ` dbasehore .
2018-10-24  1:31 ` [PATCH 2/6] clk: fix clk_calc_subtree compute duplications Derek Basehore
2018-11-01  2:58   ` dbasehore .
2018-10-24  1:31 ` [PATCH 3/6] clk: change rates via list iteration Derek Basehore
2018-10-26  3:29   ` dbasehore .
2018-10-24  1:31 ` Derek Basehore [this message]
2018-10-24  1:31 ` [PATCH 5/6] docs: driver-api: add pre_rate_req to clk documentation Derek Basehore
2018-10-24  1:31 ` [PATCH 6/6] clk: rockchip: use pre_rate_req for cpuclk Derek Basehore
2018-10-24  4:06   ` dbasehore .
2018-12-20 21:15 ` [PATCH 0/6] Coordinated Clks Stephen Boyd
2018-12-20 23:20   ` dbasehore .

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=20181024013132.115907-5-dbasehore@chromium.org \
    --to=dbasehore@chromium.org \
    --cc=aisheng.dong@nxp.com \
    --cc=corbet@lwn.net \
    --cc=heiko@sntech.de \
    --cc=linux-arm-kernel@lists.infradead.org \
    --cc=linux-clk@vger.kernel.org \
    --cc=linux-doc@vger.kernel.org \
    --cc=linux-kernel@vger.kernel.org \
    --cc=linux-rockchip@lists.infradead.org \
    --cc=mchehab+samsung@kernel.org \
    --cc=mturquette@baylibre.com \
    --cc=sboyd@kernel.org \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).