diff options
Diffstat (limited to 'target/linux/ipq806x/patches/0126-clk-Add-safe-switch-hook.patch')
-rw-r--r-- | target/linux/ipq806x/patches/0126-clk-Add-safe-switch-hook.patch | 155 |
1 files changed, 155 insertions, 0 deletions
diff --git a/target/linux/ipq806x/patches/0126-clk-Add-safe-switch-hook.patch b/target/linux/ipq806x/patches/0126-clk-Add-safe-switch-hook.patch new file mode 100644 index 0000000000..bda409363e --- /dev/null +++ b/target/linux/ipq806x/patches/0126-clk-Add-safe-switch-hook.patch @@ -0,0 +1,155 @@ +From b7b3ceec506179d59e46e3a8ea8b370872cc7fcb Mon Sep 17 00:00:00 2001 +From: Stephen Boyd <sboyd@codeaurora.org> +Date: Mon, 31 Mar 2014 16:52:29 -0700 +Subject: [PATCH 126/182] clk: Add safe switch hook + +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> +--- + drivers/clk/clk.c | 53 ++++++++++++++++++++++++++++++++++++------ + include/linux/clk-private.h | 2 ++ + include/linux/clk-provider.h | 1 + + 3 files changed, 49 insertions(+), 7 deletions(-) + +diff --git a/drivers/clk/clk.c b/drivers/clk/clk.c +index b94a311..0582068 100644 +--- a/drivers/clk/clk.c ++++ b/drivers/clk/clk.c +@@ -1356,6 +1356,7 @@ static void clk_calc_subtree(struct clk *clk, unsigned long new_rate, + struct clk *new_parent, u8 p_index) + { + struct clk *child; ++ struct clk *parent; + + clk->new_rate = new_rate; + clk->new_parent = new_parent; +@@ -1365,6 +1366,17 @@ static void clk_calc_subtree(struct clk *clk, unsigned long new_rate, + if (new_parent && new_parent != clk->parent) + new_parent->new_child = clk; + ++ if (clk->ops->get_safe_parent) { ++ parent = clk->ops->get_safe_parent(clk->hw); ++ if (parent) { ++ 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) { + if (child->ops->recalc_rate) + child->new_rate = child->ops->recalc_rate(child->hw, new_rate); +@@ -1450,14 +1462,42 @@ out: + static struct clk *clk_propagate_rate_change(struct clk *clk, unsigned long event) + { + struct clk *child, *tmp_clk, *fail_clk = NULL; ++ struct clk *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); ++ 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) ++ ret = __clk_notify(clk, event, clk->rate, ++ clk->new_rate); ++ if (ret & NOTIFY_STOP_MASK && event != POST_RATE_CHANGE) + fail_clk = clk; + } + +@@ -1499,7 +1539,8 @@ static void clk_change_rate(struct clk *clk) + else if (clk->parent) + best_parent_rate = clk->parent->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); + + if (clk->ops->set_rate_and_parent) { +@@ -1522,9 +1563,6 @@ static void clk_change_rate(struct clk *clk) + else + clk->rate = best_parent_rate; + +- if (clk->notifier_count && old_rate != clk->rate) +- __clk_notify(clk, POST_RATE_CHANGE, old_rate, clk->rate); +- + hlist_for_each_entry(child, &clk->children, child_node) { + /* Skip children who will be reparented to another clock */ + if (child->new_parent && child->new_parent != clk) +@@ -1598,6 +1636,7 @@ int clk_set_rate(struct clk *clk, unsigned long rate) + /* change the rates */ + clk_change_rate(top); + ++ clk_propagate_rate_change(top, POST_RATE_CHANGE); + out: + clk_prepare_unlock(); + +diff --git a/include/linux/clk-private.h b/include/linux/clk-private.h +index efbf70b..f48684a 100644 +--- a/include/linux/clk-private.h ++++ b/include/linux/clk-private.h +@@ -38,8 +38,10 @@ struct clk { + struct clk **parents; + u8 num_parents; + u8 new_parent_index; ++ u8 safe_parent_index; + unsigned long rate; + unsigned long new_rate; ++ struct clk *safe_parent; + struct clk *new_parent; + struct clk *new_child; + unsigned long flags; +diff --git a/include/linux/clk-provider.h b/include/linux/clk-provider.h +index 939533d..300fcb8 100644 +--- a/include/linux/clk-provider.h ++++ b/include/linux/clk-provider.h +@@ -157,6 +157,7 @@ struct clk_ops { + struct clk **best_parent_clk); + int (*set_parent)(struct clk_hw *hw, u8 index); + u8 (*get_parent)(struct clk_hw *hw); ++ struct clk *(*get_safe_parent)(struct clk_hw *hw); + int (*set_rate)(struct clk_hw *hw, unsigned long, + unsigned long); + int (*set_rate_and_parent)(struct clk_hw *hw, +-- +1.7.10.4 + |