[PATCH] clk: give clock chance to change parent at setrate stage
Lei Wen
adrian.wenl at gmail.com
Fri May 4 05:54:43 EDT 2012
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 <leiwen at marvell.com>
---
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
More information about the linux-arm-kernel
mailing list