From: James Hogan <james.hogan@imgtec.com>
To: Mike Turquette <mturquette@linaro.org>,
<linux-arm-kernel@lists.infradead.org>,
<linux-kernel@vger.kernel.org>
Cc: Chao Xie <xiechao.mail@gmail.com>,
Sascha Hauer <s.hauer@pengutronix.de>,
James Hogan <james.hogan@imgtec.com>
Subject: [RFC PATCH v1 2/3] clk: add support for clock remuxing
Date: Fri, 22 Mar 2013 15:43:50 +0000 [thread overview]
Message-ID: <1363967031-22781-3-git-send-email-james.hogan@imgtec.com> (raw)
In-Reply-To: <1363967031-22781-1-git-send-email-james.hogan@imgtec.com>
Add support for automatic selection of the best parent for a mux, i.e.
the one which can provide the closest clock rate to that requested. This
is by way of adding a parameter to the round_rate clock op which allows
the clock to optionally select a different parent index. This is used in
clk_calc_new_rates to decide whether to initiate a set_parent operation.
This splits out the mutex protected portion of clk_set_parent() into a
separate __clk_set_parent_notify() which takes care of sending clock
change notifications.
A new clock flag is also added called CLK_SET_RATE_REMUX to indicate
that the clock can have it's parent changed automatically in response to
a set_rate. It isn't used yet, but will be used within clock mux
drivers.
Signed-off-by: James Hogan <james.hogan@imgtec.com>
---
drivers/clk/clk.c | 94 +++++++++++++++++++++++++++++++-------------
include/linux/clk-provider.h | 6 ++-
2 files changed, 71 insertions(+), 29 deletions(-)
diff --git a/drivers/clk/clk.c b/drivers/clk/clk.c
index 79d5deb..3886ccd 100644
--- a/drivers/clk/clk.c
+++ b/drivers/clk/clk.c
@@ -36,6 +36,8 @@ static struct dentry *rootdir;
static struct dentry *orphandir;
static int inited = 0;
+static int __clk_set_parent_notify(struct clk *clk, struct clk *parent);
+
static void clk_summary_show_one(struct seq_file *s, struct clk *c, int level)
{
if (!c)
@@ -737,7 +739,7 @@ unsigned long __clk_round_rate(struct clk *clk, unsigned long rate)
if (clk->parent)
parent_rate = clk->parent->rate;
- return clk->ops->round_rate(clk->hw, rate, &parent_rate);
+ return clk->ops->round_rate(clk->hw, rate, &parent_rate, NULL);
}
/**
@@ -928,8 +930,10 @@ static void clk_calc_subtree(struct clk *clk, unsigned long new_rate)
static struct clk *clk_calc_new_rates(struct clk *clk, unsigned long rate)
{
struct clk *top = clk;
+ struct clk *parent;
unsigned long best_parent_rate = 0;
unsigned long new_rate;
+ u8 old_parent = 0, best_parent;
/* sanity */
if (IS_ERR_OR_NULL(clk))
@@ -939,13 +943,21 @@ static struct clk *clk_calc_new_rates(struct clk *clk, unsigned long rate)
if (clk->parent)
best_parent_rate = clk->parent->rate;
+ /* by default don't change the parent */
+ if (clk->ops->get_parent)
+ old_parent = clk->ops->get_parent(clk->hw);
+ best_parent = old_parent;
+ parent = clk->parent;
+
/* never propagate up to the parent */
if (!(clk->flags & CLK_SET_RATE_PARENT)) {
if (!clk->ops->round_rate) {
clk->new_rate = clk->rate;
return NULL;
}
- new_rate = clk->ops->round_rate(clk->hw, rate, &best_parent_rate);
+ new_rate = clk->ops->round_rate(clk->hw, rate,
+ &best_parent_rate,
+ &best_parent);
goto out;
}
@@ -962,15 +974,36 @@ static struct clk *clk_calc_new_rates(struct clk *clk, unsigned long rate)
goto out;
}
- new_rate = clk->ops->round_rate(clk->hw, rate, &best_parent_rate);
+ new_rate = clk->ops->round_rate(clk->hw, rate, &best_parent_rate,
+ &best_parent);
- if (best_parent_rate != clk->parent->rate) {
- top = clk_calc_new_rates(clk->parent, best_parent_rate);
+ if (best_parent != old_parent) {
+ parent = __clk_get_parent_by_index(clk, best_parent);
+ /* couldn't find requested parent */
+ if (!parent)
+ return NULL;
+ }
+
+ if (best_parent_rate != parent->rate) {
+ top = clk_calc_new_rates(parent, best_parent_rate);
goto out;
}
out:
+ /* the parent may have changed */
+ if (best_parent != old_parent) {
+ parent = __clk_get_parent_by_index(clk, best_parent);
+ /* couldn't find requested parent */
+ if (!parent) {
+ parent = clk->parent;
+ best_parent = old_parent;
+ }
+ if (best_parent != old_parent)
+ if (__clk_set_parent_notify(clk, parent))
+ return NULL;
+ }
+
clk_calc_subtree(clk, new_rate);
return top;
@@ -1270,31 +1303,10 @@ out:
return ret;
}
-/**
- * clk_set_parent - switch the parent of a mux clk
- * @clk: the mux clk whose input we are switching
- * @parent: the new input to clk
- *
- * Re-parent clk to use parent as it's new input source. If clk has the
- * CLK_SET_PARENT_GATE flag set then clk must be gated for this
- * operation to succeed. After successfully changing clk's parent
- * clk_set_parent will update the clk topology, sysfs topology and
- * propagate rate recalculation via __clk_recalc_rates. Returns 0 on
- * success, -EERROR otherwise.
- */
-int clk_set_parent(struct clk *clk, struct clk *parent)
+static int __clk_set_parent_notify(struct clk *clk, struct clk *parent)
{
int ret = 0;
- if (!clk || !clk->ops)
- return -EINVAL;
-
- if (!clk->ops->set_parent)
- return -ENOSYS;
-
- /* prevent racing with updates to the clock topology */
- mutex_lock(&prepare_lock);
-
if (clk->parent == parent)
goto out;
@@ -1322,6 +1334,34 @@ int clk_set_parent(struct clk *clk, struct clk *parent)
__clk_reparent(clk, parent);
out:
+ return ret;
+}
+
+/**
+ * clk_set_parent - switch the parent of a mux clk
+ * @clk: the mux clk whose input we are switching
+ * @parent: the new input to clk
+ *
+ * Re-parent clk to use parent as it's new input source. If clk has the
+ * CLK_SET_PARENT_GATE flag set then clk must be gated for this
+ * operation to succeed. After successfully changing clk's parent
+ * clk_set_parent will update the clk topology, sysfs topology and
+ * propagate rate recalculation via __clk_recalc_rates. Returns 0 on
+ * success, -EERROR otherwise.
+ */
+int clk_set_parent(struct clk *clk, struct clk *parent)
+{
+ int ret;
+
+ if (!clk || !clk->ops)
+ return -EINVAL;
+
+ if (!clk->ops->set_parent)
+ return -ENOSYS;
+
+ /* prevent racing with updates to the clock topology */
+ mutex_lock(&prepare_lock);
+ ret = __clk_set_parent_notify(clk, parent);
mutex_unlock(&prepare_lock);
return ret;
diff --git a/include/linux/clk-provider.h b/include/linux/clk-provider.h
index 4e0b634..cdad3ce 100644
--- a/include/linux/clk-provider.h
+++ b/include/linux/clk-provider.h
@@ -27,6 +27,7 @@
#define CLK_IS_ROOT BIT(4) /* root clk, has no parent */
#define CLK_IS_BASIC BIT(5) /* Basic clk, can't do a to_clk_foo() */
#define CLK_GET_RATE_NOCACHE BIT(6) /* do not use the cached clk rate */
+#define CLK_SET_RATE_REMUX BIT(7) /* find best parent for rate change */
struct clk_hw;
@@ -69,7 +70,8 @@ struct clk_hw;
* this op is not set then clock rate will be initialized to 0.
*
* @round_rate: Given a target rate as input, returns the closest rate actually
- * supported by the clock.
+ * supported by the clock, and optionally the index of the parent
+ * that should be used to provide the clock rate.
*
* @get_parent: Queries the hardware to determine the parent of a clock. The
* return value is a u8 which specifies the index corresponding to
@@ -115,7 +117,7 @@ struct clk_ops {
unsigned long (*recalc_rate)(struct clk_hw *hw,
unsigned long parent_rate);
long (*round_rate)(struct clk_hw *hw, unsigned long,
- unsigned long *);
+ unsigned long *, u8 *index);
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,
--
1.8.1.2
next prev parent reply other threads:[~2013-03-22 15:45 UTC|newest]
Thread overview: 8+ messages / expand[flat|nested] mbox.gz Atom feed top
2013-03-22 15:43 [RFC PATCH v1 0/3] clk: implement remuxing during set_rate James Hogan
2013-03-22 15:43 ` [RFC PATCH v1 1/3] clk: abstract parent cache James Hogan
2013-03-22 15:43 ` James Hogan [this message]
2013-03-22 15:43 ` [RFC PATCH v1 3/3] clk: clk-mux: implement remuxing James Hogan
2013-04-03 2:06 ` [RFC PATCH v1 0/3] clk: implement remuxing during set_rate Stephen Boyd
2013-04-03 21:08 ` James Hogan
2013-04-03 21:34 ` Mike Turquette
2013-04-19 16:41 ` James Hogan
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=1363967031-22781-3-git-send-email-james.hogan@imgtec.com \
--to=james.hogan@imgtec.com \
--cc=linux-arm-kernel@lists.infradead.org \
--cc=linux-kernel@vger.kernel.org \
--cc=mturquette@linaro.org \
--cc=s.hauer@pengutronix.de \
--cc=xiechao.mail@gmail.com \
/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
all inboxes | Powered by JetHome®