You cannot select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
openwrt/target/linux/ipq806x/patches-4.1/136-clk-Add-safe-switch-hoo...

165 lines
5.4 KiB
Diff

Content-Type: text/plain; charset="utf-8"
MIME-Version: 1.0
Content-Transfer-Encoding: 7bit
Subject: [v3,04/13] clk: Add safe switch hook
From: Stephen Boyd <sboyd@codeaurora.org>
X-Patchwork-Id: 6063211
Message-Id: <1426920332-9340-5-git-send-email-sboyd@codeaurora.org>
To: Mike Turquette <mturquette@linaro.org>, Stephen Boyd <sboyd@codeaurora.org>
Cc: linux-kernel@vger.kernel.org, linux-arm-msm@vger.kernel.org,
linux-pm@vger.kernel.org, linux-arm-kernel@lists.infradead.org,
Viresh Kumar <viresh.kumar@linaro.org>
Date: Fri, 20 Mar 2015 23:45:23 -0700
Sometimes clocks can't accept their parent source turning off
while the source is reprogrammed to a different rate. Most
notably CPU clocks require a way to switch away from the current
PLL they're running on, reprogram that PLL to a new rate, and
then switch back to the PLL with the new rate once they're done.
Add a hook that drivers can implement allowing them to return a
'safe parent' that they can switch their parent to while the
upstream source is reprogrammed to support this.
Signed-off-by: Stephen Boyd <sboyd@codeaurora.org>
---
This patch is good enough for Krait, but soon I'll need to
support a "safe rate" where we ask a clock what rate it needs to be running
at to be sure it's within voltage constraints. Right now safe parent
handles that problem on Krait, but on other platforms it won't work.
drivers/clk/clk.c | 61 ++++++++++++++++++++++++++++++++++++++------
include/linux/clk-provider.h | 1 +
2 files changed, 54 insertions(+), 8 deletions(-)
--- a/drivers/clk/clk.c
+++ b/drivers/clk/clk.c
@@ -56,9 +56,12 @@ struct clk_core {
struct clk_core **parents;
u8 num_parents;
u8 new_parent_index;
+ u8 safe_parent_index;
unsigned long rate;
unsigned long req_rate;
+ unsigned long old_rate;
unsigned long new_rate;
+ struct clk_core *safe_parent;
struct clk_core *new_parent;
struct clk_core *new_child;
unsigned long flags;
@@ -1596,7 +1599,8 @@ out:
static void clk_calc_subtree(struct clk_core *clk, unsigned long new_rate,
struct clk_core *new_parent, u8 p_index)
{
- struct clk_core *child;
+ struct clk_core *child, *parent;
+ struct clk_hw *parent_hw;
clk->new_rate = new_rate;
clk->new_parent = new_parent;
@@ -1606,6 +1610,18 @@ static void clk_calc_subtree(struct clk_
if (new_parent && new_parent != clk->parent)
new_parent->new_child = clk;
+ if (clk->ops->get_safe_parent) {
+ parent_hw = clk->ops->get_safe_parent(clk->hw);
+ if (parent_hw) {
+ parent = parent_hw->core;
+ p_index = clk_fetch_parent_index(clk, parent);
+ clk->safe_parent_index = p_index;
+ clk->safe_parent = parent;
+ }
+ } else {
+ clk->safe_parent = NULL;
+ }
+
hlist_for_each_entry(child, &clk->children, child_node) {
child->new_rate = clk_recalc(child, new_rate);
clk_calc_subtree(child, child->new_rate, NULL, 0);
@@ -1710,14 +1726,43 @@ static struct clk_core *clk_propagate_ra
unsigned long event)
{
struct clk_core *child, *tmp_clk, *fail_clk = NULL;
+ struct clk_core *old_parent;
int ret = NOTIFY_DONE;
- if (clk->rate == clk->new_rate)
+ if (clk->rate == clk->new_rate && event != POST_RATE_CHANGE)
return NULL;
+ switch (event) {
+ case PRE_RATE_CHANGE:
+ if (clk->safe_parent)
+ clk->ops->set_parent(clk->hw, clk->safe_parent_index);
+ clk->old_rate = clk->rate;
+ break;
+ case POST_RATE_CHANGE:
+ if (clk->safe_parent) {
+ old_parent = __clk_set_parent_before(clk,
+ clk->new_parent);
+ if (clk->ops->set_rate_and_parent) {
+ clk->ops->set_rate_and_parent(clk->hw,
+ clk->new_rate,
+ clk->new_parent ?
+ clk->new_parent->rate : 0,
+ clk->new_parent_index);
+ } else if (clk->ops->set_parent) {
+ clk->ops->set_parent(clk->hw,
+ clk->new_parent_index);
+ }
+ __clk_set_parent_after(clk, clk->new_parent,
+ old_parent);
+ }
+ break;
+ }
+
if (clk->notifier_count) {
- ret = __clk_notify(clk, event, clk->rate, clk->new_rate);
- if (ret & NOTIFY_STOP_MASK)
+ if (event != POST_RATE_CHANGE || clk->old_rate != clk->rate)
+ ret = __clk_notify(clk, event, clk->old_rate,
+ clk->new_rate);
+ if (ret & NOTIFY_STOP_MASK && event != POST_RATE_CHANGE)
fail_clk = clk;
}
@@ -1763,7 +1808,8 @@ clk_change_rate(struct clk_core *clk, un
old_rate = clk->rate;
- if (clk->new_parent && clk->new_parent != clk->parent) {
+ if (clk->new_parent && clk->new_parent != clk->parent &&
+ !clk->safe_parent) {
old_parent = __clk_set_parent_before(clk, clk->new_parent);
trace_clk_set_parent(clk, clk->new_parent);
@@ -1789,9 +1835,6 @@ clk_change_rate(struct clk_core *clk, un
clk->rate = clk->new_rate;
- if (clk->notifier_count && old_rate != clk->rate)
- __clk_notify(clk, POST_RATE_CHANGE, old_rate, clk->rate);
-
/*
* Use safe iteration, as change_rate can actually swap parents
* for certain clock types.
@@ -1851,6 +1894,8 @@ static int clk_core_set_rate_nolock(stru
clk->req_rate = req_rate;
+ clk_propagate_rate_change(top, POST_RATE_CHANGE);
+
return ret;
}
--- a/include/linux/clk-provider.h
+++ b/include/linux/clk-provider.h
@@ -183,6 +183,7 @@ struct clk_ops {
struct clk_hw **best_parent_hw);
int (*set_parent)(struct clk_hw *hw, u8 index);
u8 (*get_parent)(struct clk_hw *hw);
+ struct clk_hw *(*get_safe_parent)(struct clk_hw *hw);
int (*set_rate)(struct clk_hw *hw, unsigned long rate,
unsigned long parent_rate);
int (*set_rate_and_parent)(struct clk_hw *hw,