[PATCH] clk: give clock chance to change parent at setrate stage
From: Lei Wen <hidden>
Date: 2012-05-04 09:54:43
Subsystem:
common clk framework, the rest · Maintainers:
Michael Turquette, Stephen Boyd, Linus Torvalds
Possibly related (same subject, not in this thread)
- 2012-05-04 · [PATCH] clk: give clock chance to change parent at setrate stage · Lei Wen <hidden>
There is one clock rate changing requirement like those: 1. clock may need specify specific rate binded with specific parent 2. driver care only set_rate, don't care for which parent it is linked 3. It need registering notification to changing voltage 4. It want keep parent and rate changing in an atomic operation, which ? means for chaning parent, it only caching the mux chaning, and ? maintent the relationship in this tree, while do the real parent ? and rate chaning in the set_rate callback. And it requires ? recalculated rate reflect the true rate changing state to make the ? decision to whether sending notification (for voltage change). Base on those assumption, the logic in set_rate is changed a little, the round_rate should return not only best parent_rate, but also best parent. If best parent is changed, we would switch parent first, then do the sub rate chaning. Signed-off-by: Lei Wen <redacted> --- ?drivers/clk/clk.c ? ? ? ? ? ?| ? 22 +++++++++++++++++++++- ?include/linux/clk-provider.h | ? ?2 ++ ?2 files changed, 23 insertions(+), 1 deletions(-)
diff --git a/drivers/clk/clk.c b/drivers/clk/clk.c
index 9cf6f59..d207617 100644
--- a/drivers/clk/clk.c
+++ b/drivers/clk/clk.c@@ -23,6 +23,7 @@ static DEFINE_MUTEX(prepare_lock);?static HLIST_HEAD(clk_root_list); ?static HLIST_HEAD(clk_orphan_list); ?static LIST_HEAD(clk_notifier_list); +static int __clk_set_parent(struct clk *clk, struct clk *parent); ?/*** ? ? ? ?debugfs support ? ? ? ?***/
@@ -767,6 +768,7 @@ static struct clk *clk_calc_new_rates(struct clk*clk, unsigned long rate)
? ? ? ?struct clk *top = clk;
? ? ? ?unsigned long best_parent_rate = clk->parent->rate;
? ? ? ?unsigned long new_rate;
+ ? ? ? int ret;
? ? ? ?if (!clk->ops->round_rate && !(clk->flags & CLK_SET_RATE_PARENT)) {
? ? ? ? ? ? ? ?clk->new_rate = clk->rate;@@ -785,6 +787,21 @@ static struct clk *clk_calc_new_rates(struct clk*clk, unsigned long rate)
? ? ? ?else
? ? ? ? ? ? ? ?new_rate = clk->ops->round_rate(clk->hw, rate, NULL);
+ ? ? ? if (clk->hw->new_parent != clk->parent) {
+ ? ? ? ? ? ? ? if ((clk->flags & CLK_SET_PARENT_GATE) && clk->prepare_count)
+ ? ? ? ? ? ? ? ? ? ? ? ret = -EBUSY;
+ ? ? ? ? ? ? ? else
+ ? ? ? ? ? ? ? ? ? ? ? ret = __clk_set_parent(clk, clk->hw->new_parent);
+
+ ? ? ? ? ? ? ? if (ret) {
+ ? ? ? ? ? ? ? ? ? ? ? pr_debug("%s: %s set parent to %s failed\n", __func__,
+ ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? clk->name, clk->hw->new_parent->name);
+ ? ? ? ? ? ? ? ? ? ? ? return NULL;
+ ? ? ? ? ? ? ? }
+
+ ? ? ? ? ? ? ? __clk_reparent(clk, clk->hw->new_parent);
+ ? ? ? }
+
? ? ? ?if (best_parent_rate != clk->parent->rate) {
? ? ? ? ? ? ? ?top = clk_calc_new_rates(clk->parent, best_parent_rate);
@@ -1050,7 +1067,10 @@ out:
? ? ? ?clk->parent = new_parent; - ? ? ? __clk_recalc_rates(clk, POST_RATE_CHANGE); + ? ? ? if (!clk->hw->new_parent) + ? ? ? ? ? ? ? __clk_recalc_rates(clk, POST_RATE_CHANGE); + ? ? ? else + ? ? ? ? ? ? ? clk->hw->new_parent = NULL; ?} ?static int __clk_set_parent(struct clk *clk, struct clk *parent)
diff --git a/include/linux/clk-provider.h b/include/linux/clk-provider.h
index 5508897..54dad8a 100644
--- a/include/linux/clk-provider.h
+++ b/include/linux/clk-provider.h@@ -23,9 +23,11 @@?*
?* clk: pointer to the struct clk instance that points back to this struct
?* clk_hw instance
+ * new_parent: pointer for caching new parent position
?*/
?struct clk_hw {
? ? ? ?struct clk *clk;
+ ? ? ? struct clk *new_parent;
?};
?/*
--
1.7.5.4