2013-05-20 13:29:15

by James Hogan

[permalink] [raw]
Subject: [PATCH v4 0/5] clk: implement remuxing during set_rate

This patchset adds support for automatic selection of the best parent
for a clock mux, i.e. the one which can provide the closest clock rate
to that requested. It can be disabled by a new CLK_SET_RATE_NO_REPARENT
flag (which is set for all uses of clk_register_mux().

This works by way of adding a new op, determine_rate, similar to
round_rate but with an extra parameter to allow the clock driver to
optionally select a different parent clock. This is used in
clk_calc_new_rates to decide whether to initiate a set_parent operation.

Changes in v4:

* rebased on clk-next ("clk: sun5i: Add compatibles for Allwinner A13").
* replace __clk_set_parent_no_recalc with __clk_set_parent.
* never pass NULL to determine_rate's best_parent_clk parameter, and
slight refactor of __clk_round_rate to use local copy of clk->parent.
* a few new comments around use of clk::new_child.
* new patch (patch 2) split out of patch 3 to avoid having to declare
static __clk_set_parent() at the top of clk.c, and to ease readability
of patch 3.

Changes in v3:

* rebased on v3.10-rc1.
* remove double underscore prefix from clk_get_parent_by_index()
* store new_parent_index in struct clk too (calculated from
clk_fetch_parent_index, and passed through __clk_set_parent_no_recalc
to __clk_set_parent).
* allow determine_rate to satisfy recalc_rate check in __clk_init.
* rename/invert CLK_SET_RATE_REMUX to CLK_SET_RATE_NO_REPARENT and move
to patch 3.
* patch 3: add CLK_SET_RATE_NO_REPARENT flag to all callers of
clk_register_mux. If you don't mind your clocks being reparented in
response to set_rate please let me know and I'll drop the relevant
portion of the patch.

Changes in v2:

I've moved the mux determine_rate implementation into a core helper, but
I haven't pushed it fully into the core, as I think it just wouldn't
work correctly for more complex clocks, e.g. if you (theoretically) had
a combined mux and divide, you'd want to intercept the determine_rate
and ask for a larger rate from the parent clocks, then return the
divided rate. This should be possible by wrapping the mux determine_rate
helper.

Patch 1 still exports the __clk_get_parent_by_index as it seems like it
might be a useful thing for clock implementations to have access to if
they ever wanted to do something more fancy with changing clock parents.

I haven't made any attempt to implement the atomic set_parent+set_rate
as I don't have hardware that could take proper advantage of it, but it
shouldn't be too difficult for others to implement if they wanted since
they're fairly close to one another (in clk_change_rate()).

* switched to using new determine_rate op rather than adding an argument
to round_rate.
* moved mux implementation into a single helper which should be usable
from more complex clocks which can mux.
* rewrite main implementation so that no changes are made until after
the PRE notifications have been sent, and in a way that should ensure
correct notifications without duplicates, and I think should be safe
in the event of a notification failing.
* various tidy ups and fixes.

James Hogan (5):
clk: abstract parent cache
clk: move some parent related functions upwards
clk: add support for clock reparent on set_rate
clk: add CLK_SET_RATE_NO_REPARENT flag
clk: clk-mux: implement remuxing on set_rate

Documentation/clk.txt | 4 +
arch/arm/mach-imx/clk.h | 5 +-
drivers/clk/clk-mux.c | 1 +
drivers/clk/clk.c | 416 ++++++++++++++++++++++-------------
drivers/clk/mmp/clk-mmp2.c | 39 ++--
drivers/clk/mmp/clk-pxa168.c | 40 ++--
drivers/clk/mmp/clk-pxa910.c | 31 ++-
drivers/clk/mxs/clk.h | 4 +-
drivers/clk/samsung/clk.h | 2 +-
drivers/clk/spear/spear1310_clock.c | 179 +++++++--------
drivers/clk/spear/spear1340_clock.c | 97 ++++----
drivers/clk/spear/spear3xx_clock.c | 57 +++--
drivers/clk/spear/spear6xx_clock.c | 35 +--
drivers/clk/sunxi/clk-sunxi.c | 3 +-
drivers/clk/tegra/clk-tegra114.c | 36 ++-
drivers/clk/tegra/clk-tegra20.c | 6 +-
drivers/clk/tegra/clk-tegra30.c | 33 ++-
drivers/clk/versatile/clk-vexpress.c | 4 +-
include/linux/clk-private.h | 3 +
include/linux/clk-provider.h | 12 +
20 files changed, 614 insertions(+), 393 deletions(-)

--
1.8.1.2


2013-05-20 13:29:19

by James Hogan

[permalink] [raw]
Subject: [PATCH v4 2/5] clk: move some parent related functions upwards

Move some parent related functions up in clk.c so they can be used by
the modifications in the following patch which enables clock reparenting
during set_rate. No other changes are made so this patch makes no
functional difference in isolation. This is separate from the following
patch primarily to ease readability of that patch.

Signed-off-by: James Hogan <[email protected]>
Cc: Mike Turquette <[email protected]>
Cc: [email protected]
---
Changes in v4:

* new patch (patch 2) split out of patch 3 to avoid having to declare
static __clk_set_parent() at the top of clk.c, and to ease readability
of patch 3.

drivers/clk/clk.c | 220 +++++++++++++++++++++++++++---------------------------
1 file changed, 110 insertions(+), 110 deletions(-)

diff --git a/drivers/clk/clk.c b/drivers/clk/clk.c
index f0ebada..a4cd6bc 100644
--- a/drivers/clk/clk.c
+++ b/drivers/clk/clk.c
@@ -1027,6 +1027,108 @@ unsigned long clk_get_rate(struct clk *clk)
}
EXPORT_SYMBOL_GPL(clk_get_rate);

+static u8 clk_fetch_parent_index(struct clk *clk, struct clk *parent)
+{
+ u8 i;
+
+ if (!clk->parents)
+ clk->parents = kzalloc((sizeof(struct clk*) * clk->num_parents),
+ GFP_KERNEL);
+
+ /*
+ * find index of new parent clock using cached parent ptrs,
+ * or if not yet cached, use string name comparison and cache
+ * them now to avoid future calls to __clk_lookup.
+ */
+ for (i = 0; i < clk->num_parents; i++) {
+ if (clk->parents && clk->parents[i] == parent)
+ break;
+ else if (!strcmp(clk->parent_names[i], parent->name)) {
+ if (clk->parents)
+ clk->parents[i] = __clk_lookup(parent->name);
+ break;
+ }
+ }
+
+ return i;
+}
+
+static void clk_reparent(struct clk *clk, struct clk *new_parent)
+{
+ hlist_del(&clk->child_node);
+
+ if (new_parent)
+ hlist_add_head(&clk->child_node, &new_parent->children);
+ else
+ hlist_add_head(&clk->child_node, &clk_orphan_list);
+
+ clk->parent = new_parent;
+}
+
+static int __clk_set_parent(struct clk *clk, struct clk *parent, u8 p_index)
+{
+ unsigned long flags;
+ int ret = 0;
+ struct clk *old_parent = clk->parent;
+
+ /*
+ * Migrate prepare state between parents and prevent race with
+ * clk_enable().
+ *
+ * If the clock is not prepared, then a race with
+ * clk_enable/disable() is impossible since we already have the
+ * prepare lock (future calls to clk_enable() need to be preceded by
+ * a clk_prepare()).
+ *
+ * If the clock is prepared, migrate the prepared state to the new
+ * parent and also protect against a race with clk_enable() by
+ * forcing the clock and the new parent on. This ensures that all
+ * future calls to clk_enable() are practically NOPs with respect to
+ * hardware and software states.
+ */
+ if (clk->prepare_count) {
+ __clk_prepare(parent);
+ clk_enable(parent);
+ clk_enable(clk);
+ }
+
+ /* update the clk tree topology */
+ flags = clk_enable_lock();
+ clk_reparent(clk, parent);
+ clk_enable_unlock(flags);
+
+ /* change clock input source */
+ if (parent && clk->ops->set_parent)
+ ret = clk->ops->set_parent(clk->hw, p_index);
+
+ if (ret) {
+ flags = clk_enable_lock();
+ clk_reparent(clk, old_parent);
+ clk_enable_unlock(flags);
+
+ if (clk->prepare_count) {
+ clk_disable(clk);
+ clk_disable(parent);
+ __clk_unprepare(parent);
+ }
+ return ret;
+ }
+
+ /*
+ * Finish the migration of prepare state and undo the changes done
+ * for preventing a race with clk_enable().
+ */
+ if (clk->prepare_count) {
+ clk_disable(clk);
+ clk_disable(old_parent);
+ __clk_unprepare(old_parent);
+ }
+
+ /* update debugfs with new clk tree topology */
+ clk_debug_reparent(clk, parent);
+ return 0;
+}
+
/**
* __clk_speculate_rates
* @clk: first clk in the subtree
@@ -1334,18 +1436,6 @@ out:
return ret;
}

-static void clk_reparent(struct clk *clk, struct clk *new_parent)
-{
- hlist_del(&clk->child_node);
-
- if (new_parent)
- hlist_add_head(&clk->child_node, &new_parent->children);
- else
- hlist_add_head(&clk->child_node, &clk_orphan_list);
-
- clk->parent = new_parent;
-}
-
void __clk_reparent(struct clk *clk, struct clk *new_parent)
{
clk_reparent(clk, new_parent);
@@ -1353,96 +1443,6 @@ void __clk_reparent(struct clk *clk, struct clk *new_parent)
__clk_recalc_rates(clk, POST_RATE_CHANGE);
}

-static u8 clk_fetch_parent_index(struct clk *clk, struct clk *parent)
-{
- u8 i;
-
- if (!clk->parents)
- clk->parents = kzalloc((sizeof(struct clk*) * clk->num_parents),
- GFP_KERNEL);
-
- /*
- * find index of new parent clock using cached parent ptrs,
- * or if not yet cached, use string name comparison and cache
- * them now to avoid future calls to __clk_lookup.
- */
- for (i = 0; i < clk->num_parents; i++) {
- if (clk->parents && clk->parents[i] == parent)
- break;
- else if (!strcmp(clk->parent_names[i], parent->name)) {
- if (clk->parents)
- clk->parents[i] = __clk_lookup(parent->name);
- break;
- }
- }
-
- return i;
-}
-
-static int __clk_set_parent(struct clk *clk, struct clk *parent, u8 p_index)
-{
- unsigned long flags;
- int ret = 0;
- struct clk *old_parent = clk->parent;
-
- /*
- * Migrate prepare state between parents and prevent race with
- * clk_enable().
- *
- * If the clock is not prepared, then a race with
- * clk_enable/disable() is impossible since we already have the
- * prepare lock (future calls to clk_enable() need to be preceded by
- * a clk_prepare()).
- *
- * If the clock is prepared, migrate the prepared state to the new
- * parent and also protect against a race with clk_enable() by
- * forcing the clock and the new parent on. This ensures that all
- * future calls to clk_enable() are practically NOPs with respect to
- * hardware and software states.
- */
- if (clk->prepare_count) {
- __clk_prepare(parent);
- clk_enable(parent);
- clk_enable(clk);
- }
-
- /* update the clk tree topology */
- flags = clk_enable_lock();
- clk_reparent(clk, parent);
- clk_enable_unlock(flags);
-
- /* change clock input source */
- if (parent && clk->ops->set_parent)
- ret = clk->ops->set_parent(clk->hw, p_index);
-
- if (ret) {
- flags = clk_enable_lock();
- clk_reparent(clk, old_parent);
- clk_enable_unlock(flags);
-
- if (clk->prepare_count) {
- clk_disable(clk);
- clk_disable(parent);
- __clk_unprepare(parent);
- }
- return ret;
- }
-
- /*
- * Finish the migration of prepare state and undo the changes done
- * for preventing a race with clk_enable().
- */
- if (clk->prepare_count) {
- clk_disable(clk);
- clk_disable(old_parent);
- __clk_unprepare(old_parent);
- }
-
- /* update debugfs with new clk tree topology */
- clk_debug_reparent(clk, parent);
- return 0;
-}
-
/**
* clk_set_parent - switch the parent of a mux clk
* @clk: the mux clk whose input we are switching
--
1.8.1.2

2013-05-20 13:29:17

by James Hogan

[permalink] [raw]
Subject: [PATCH v4 5/5] clk: clk-mux: implement remuxing on set_rate

Implement clk-mux remuxing if the CLK_SET_RATE_NO_REPARENT flag isn't
set. This implements determine_rate for clk-mux to propagate to each
parent and to choose the best one (like clk-divider this chooses the
parent which provides the fastest rate <= the requested rate).

The determine_rate op is implemented as a core helper function so that
it can be easily used by more complex clocks which incorporate muxes.

Signed-off-by: James Hogan <[email protected]>
Cc: Mike Turquette <[email protected]>
Cc: [email protected]
---
Changes in v4:

* never pass NULL to determine_rate's best_parent_clk parameter.

Changes in v3:

* rename/invert CLK_SET_RATE_REMUX to CLK_SET_RATE_NO_REPARENT and move
to patch 3.

drivers/clk/clk-mux.c | 1 +
drivers/clk/clk.c | 49 ++++++++++++++++++++++++++++++++++++++++++++
include/linux/clk-provider.h | 3 +++
3 files changed, 53 insertions(+)

diff --git a/drivers/clk/clk-mux.c b/drivers/clk/clk-mux.c
index 25b1734..cecfa01 100644
--- a/drivers/clk/clk-mux.c
+++ b/drivers/clk/clk-mux.c
@@ -100,6 +100,7 @@ static int clk_mux_set_parent(struct clk_hw *hw, u8 index)
const struct clk_ops clk_mux_ops = {
.get_parent = clk_mux_get_parent,
.set_parent = clk_mux_set_parent,
+ .determine_rate = __clk_mux_determine_rate,
};
EXPORT_SYMBOL_GPL(clk_mux_ops);

diff --git a/drivers/clk/clk.c b/drivers/clk/clk.c
index 3ce4810..85b661d 100644
--- a/drivers/clk/clk.c
+++ b/drivers/clk/clk.c
@@ -692,6 +692,55 @@ struct clk *__clk_lookup(const char *name)
return NULL;
}

+/*
+ * Helper for finding best parent to provide a given frequency. This can be used
+ * directly as a determine_rate callback (e.g. for a mux), or from a more
+ * complex clock that may combine a mux with other operations.
+ */
+long __clk_mux_determine_rate(struct clk_hw *hw, unsigned long rate,
+ unsigned long *best_parent_rate,
+ struct clk **best_parent_p)
+{
+ struct clk *clk = hw->clk, *parent, *best_parent = NULL;
+ int i, num_parents;
+ unsigned long parent_rate, best = 0;
+
+ /* if NO_REPARENT flag set, pass through to current parent */
+ if (clk->flags & CLK_SET_RATE_NO_REPARENT) {
+ parent = clk->parent;
+ if (clk->flags & CLK_SET_RATE_PARENT)
+ best = __clk_round_rate(parent, rate);
+ else if (parent)
+ best = __clk_get_rate(parent);
+ else
+ best = __clk_get_rate(clk);
+ goto out;
+ }
+
+ /* find the parent that can provide the fastest rate <= rate */
+ num_parents = clk->num_parents;
+ for (i = 0; i < num_parents; i++) {
+ parent = clk_get_parent_by_index(clk, i);
+ if (!parent)
+ continue;
+ if (clk->flags & CLK_SET_RATE_PARENT)
+ parent_rate = __clk_round_rate(parent, rate);
+ else
+ parent_rate = __clk_get_rate(parent);
+ if (parent_rate <= rate && parent_rate > best) {
+ best_parent = parent;
+ best = parent_rate;
+ }
+ }
+
+out:
+ if (best_parent)
+ *best_parent_p = best_parent;
+ *best_parent_rate = best;
+
+ return best;
+}
+
/*** clk api ***/

void __clk_unprepare(struct clk *clk)
diff --git a/include/linux/clk-provider.h b/include/linux/clk-provider.h
index bb2d812..d6057eb 100644
--- a/include/linux/clk-provider.h
+++ b/include/linux/clk-provider.h
@@ -419,6 +419,9 @@ unsigned long __clk_get_flags(struct clk *clk);
bool __clk_is_prepared(struct clk *clk);
bool __clk_is_enabled(struct clk *clk);
struct clk *__clk_lookup(const char *name);
+long __clk_mux_determine_rate(struct clk_hw *hw, unsigned long rate,
+ unsigned long *best_parent_rate,
+ struct clk **best_parent_p);

/*
* FIXME clock api without lock protection
--
1.8.1.2

2013-05-20 13:29:33

by James Hogan

[permalink] [raw]
Subject: [PATCH v4 4/5] clk: add CLK_SET_RATE_NO_REPARENT flag

Add a CLK_SET_RATE_NO_REPARENT clock flag, which will prevent muxes
being reparented during clk_set_rate.

To avoid breaking existing platforms, all callers of clk_register_mux()
are adjusted to pass the new flag. Platform maintainers are encouraged
to remove the flag if they wish to allow mux reparenting on set_rate.

Signed-off-by: James Hogan <[email protected]>
Cc: Sascha Hauer <[email protected]>
Cc: Russell King <[email protected]>
Cc: Viresh Kumar <[email protected]>
Cc: Stephen Warren <[email protected]>
Cc: Mike Turquette <[email protected]>
Cc: Haojian Zhuang <[email protected]>
Cc: Chao Xie <[email protected]>
Cc: Arnd Bergmann <[email protected]>
Cc: "Emilio López" <[email protected]>
Cc: Gregory CLEMENT <[email protected]>
Cc: Maxime Ripard <[email protected]>
Cc: Prashant Gaikwad <[email protected]>
Cc: Thierry Reding <[email protected]>
Cc: Joseph Lo <[email protected]>
Cc: Peter De Schrijver <[email protected]>
Cc: Pawel Moll <[email protected]>
Cc: Catalin Marinas <[email protected]>
Cc: [email protected]
Cc: [email protected]
Cc: [email protected]
---
Changes in v3:

* rename/invert CLK_SET_RATE_REMUX to CLK_SET_RATE_NO_REPARENT and move
to this new patch.
* patch 3: add CLK_SET_RATE_NO_REPARENT flag to all callers of
clk_register_mux. If you don't mind your clocks being reparented in
response to set_rate please let me know and I'll drop the relevant
portion of the patch.

arch/arm/mach-imx/clk.h | 5 +-
drivers/clk/mmp/clk-mmp2.c | 39 +++++---
drivers/clk/mmp/clk-pxa168.c | 40 +++++---
drivers/clk/mmp/clk-pxa910.c | 31 +++---
drivers/clk/mxs/clk.h | 4 +-
drivers/clk/samsung/clk.h | 2 +-
drivers/clk/spear/spear1310_clock.c | 179 ++++++++++++++++++-----------------
drivers/clk/spear/spear1340_clock.c | 97 ++++++++++---------
drivers/clk/spear/spear3xx_clock.c | 57 +++++++----
drivers/clk/spear/spear6xx_clock.c | 35 +++----
drivers/clk/sunxi/clk-sunxi.c | 3 +-
drivers/clk/tegra/clk-tegra114.c | 36 ++++---
drivers/clk/tegra/clk-tegra20.c | 6 +-
drivers/clk/tegra/clk-tegra30.c | 33 ++++---
drivers/clk/versatile/clk-vexpress.c | 4 +-
include/linux/clk-provider.h | 1 +
16 files changed, 334 insertions(+), 238 deletions(-)

diff --git a/arch/arm/mach-imx/clk.h b/arch/arm/mach-imx/clk.h
index d9d9d9c..244db2b 100644
--- a/arch/arm/mach-imx/clk.h
+++ b/arch/arm/mach-imx/clk.h
@@ -77,7 +77,8 @@ static inline struct clk *imx_clk_gate(const char *name, const char *parent,
static inline struct clk *imx_clk_mux(const char *name, void __iomem *reg,
u8 shift, u8 width, const char **parents, int num_parents)
{
- return clk_register_mux(NULL, name, parents, num_parents, 0, reg, shift,
+ return clk_register_mux(NULL, name, parents, num_parents,
+ CLK_SET_RATE_NO_REPARENT, reg, shift,
width, 0, &imx_ccm_lock);
}

@@ -86,7 +87,7 @@ static inline struct clk *imx_clk_mux_flags(const char *name,
int num_parents, unsigned long flags)
{
return clk_register_mux(NULL, name, parents, num_parents,
- flags, reg, shift, width, 0,
+ flags | CLK_SET_RATE_NO_REPARENT, reg, shift, width, 0,
&imx_ccm_lock);
}

diff --git a/drivers/clk/mmp/clk-mmp2.c b/drivers/clk/mmp/clk-mmp2.c
index d1f1a19..b2721ca 100644
--- a/drivers/clk/mmp/clk-mmp2.c
+++ b/drivers/clk/mmp/clk-mmp2.c
@@ -248,7 +248,8 @@ void __init mmp2_clk_init(void)
clk_register_clkdev(clk, NULL, "mmp2-pwm.3");

clk = clk_register_mux(NULL, "uart0_mux", uart_parent,
- ARRAY_SIZE(uart_parent), CLK_SET_RATE_PARENT,
+ ARRAY_SIZE(uart_parent),
+ CLK_SET_RATE_PARENT | CLK_SET_RATE_NO_REPARENT,
apbc_base + APBC_UART0, 4, 3, 0, &clk_lock);
clk_set_parent(clk, vctcxo);
clk_register_clkdev(clk, "uart_mux.0", NULL);
@@ -258,7 +259,8 @@ void __init mmp2_clk_init(void)
clk_register_clkdev(clk, NULL, "pxa2xx-uart.0");

clk = clk_register_mux(NULL, "uart1_mux", uart_parent,
- ARRAY_SIZE(uart_parent), CLK_SET_RATE_PARENT,
+ ARRAY_SIZE(uart_parent),
+ CLK_SET_RATE_PARENT | CLK_SET_RATE_NO_REPARENT,
apbc_base + APBC_UART1, 4, 3, 0, &clk_lock);
clk_set_parent(clk, vctcxo);
clk_register_clkdev(clk, "uart_mux.1", NULL);
@@ -268,7 +270,8 @@ void __init mmp2_clk_init(void)
clk_register_clkdev(clk, NULL, "pxa2xx-uart.1");

clk = clk_register_mux(NULL, "uart2_mux", uart_parent,
- ARRAY_SIZE(uart_parent), CLK_SET_RATE_PARENT,
+ ARRAY_SIZE(uart_parent),
+ CLK_SET_RATE_PARENT | CLK_SET_RATE_NO_REPARENT,
apbc_base + APBC_UART2, 4, 3, 0, &clk_lock);
clk_set_parent(clk, vctcxo);
clk_register_clkdev(clk, "uart_mux.2", NULL);
@@ -278,7 +281,8 @@ void __init mmp2_clk_init(void)
clk_register_clkdev(clk, NULL, "pxa2xx-uart.2");

clk = clk_register_mux(NULL, "uart3_mux", uart_parent,
- ARRAY_SIZE(uart_parent), CLK_SET_RATE_PARENT,
+ ARRAY_SIZE(uart_parent),
+ CLK_SET_RATE_PARENT | CLK_SET_RATE_NO_REPARENT,
apbc_base + APBC_UART3, 4, 3, 0, &clk_lock);
clk_set_parent(clk, vctcxo);
clk_register_clkdev(clk, "uart_mux.3", NULL);
@@ -288,7 +292,8 @@ void __init mmp2_clk_init(void)
clk_register_clkdev(clk, NULL, "pxa2xx-uart.3");

clk = clk_register_mux(NULL, "ssp0_mux", ssp_parent,
- ARRAY_SIZE(ssp_parent), CLK_SET_RATE_PARENT,
+ ARRAY_SIZE(ssp_parent),
+ CLK_SET_RATE_PARENT | CLK_SET_RATE_NO_REPARENT,
apbc_base + APBC_SSP0, 4, 3, 0, &clk_lock);
clk_register_clkdev(clk, "uart_mux.0", NULL);

@@ -297,7 +302,8 @@ void __init mmp2_clk_init(void)
clk_register_clkdev(clk, NULL, "mmp-ssp.0");

clk = clk_register_mux(NULL, "ssp1_mux", ssp_parent,
- ARRAY_SIZE(ssp_parent), CLK_SET_RATE_PARENT,
+ ARRAY_SIZE(ssp_parent),
+ CLK_SET_RATE_PARENT | CLK_SET_RATE_NO_REPARENT,
apbc_base + APBC_SSP1, 4, 3, 0, &clk_lock);
clk_register_clkdev(clk, "ssp_mux.1", NULL);

@@ -306,7 +312,8 @@ void __init mmp2_clk_init(void)
clk_register_clkdev(clk, NULL, "mmp-ssp.1");

clk = clk_register_mux(NULL, "ssp2_mux", ssp_parent,
- ARRAY_SIZE(ssp_parent), CLK_SET_RATE_PARENT,
+ ARRAY_SIZE(ssp_parent),
+ CLK_SET_RATE_PARENT | CLK_SET_RATE_NO_REPARENT,
apbc_base + APBC_SSP2, 4, 3, 0, &clk_lock);
clk_register_clkdev(clk, "ssp_mux.2", NULL);

@@ -315,7 +322,8 @@ void __init mmp2_clk_init(void)
clk_register_clkdev(clk, NULL, "mmp-ssp.2");

clk = clk_register_mux(NULL, "ssp3_mux", ssp_parent,
- ARRAY_SIZE(ssp_parent), CLK_SET_RATE_PARENT,
+ ARRAY_SIZE(ssp_parent),
+ CLK_SET_RATE_PARENT | CLK_SET_RATE_NO_REPARENT,
apbc_base + APBC_SSP3, 4, 3, 0, &clk_lock);
clk_register_clkdev(clk, "ssp_mux.3", NULL);

@@ -324,7 +332,8 @@ void __init mmp2_clk_init(void)
clk_register_clkdev(clk, NULL, "mmp-ssp.3");

clk = clk_register_mux(NULL, "sdh_mux", sdh_parent,
- ARRAY_SIZE(sdh_parent), CLK_SET_RATE_PARENT,
+ ARRAY_SIZE(sdh_parent),
+ CLK_SET_RATE_PARENT | CLK_SET_RATE_NO_REPARENT,
apmu_base + APMU_SDH0, 8, 2, 0, &clk_lock);
clk_register_clkdev(clk, "sdh_mux", NULL);

@@ -354,7 +363,8 @@ void __init mmp2_clk_init(void)
clk_register_clkdev(clk, "usb_clk", NULL);

clk = clk_register_mux(NULL, "disp0_mux", disp_parent,
- ARRAY_SIZE(disp_parent), CLK_SET_RATE_PARENT,
+ ARRAY_SIZE(disp_parent),
+ CLK_SET_RATE_PARENT | CLK_SET_RATE_NO_REPARENT,
apmu_base + APMU_DISP0, 6, 2, 0, &clk_lock);
clk_register_clkdev(clk, "disp_mux.0", NULL);

@@ -376,7 +386,8 @@ void __init mmp2_clk_init(void)
clk_register_clkdev(clk, "disp_sphy.0", NULL);

clk = clk_register_mux(NULL, "disp1_mux", disp_parent,
- ARRAY_SIZE(disp_parent), CLK_SET_RATE_PARENT,
+ ARRAY_SIZE(disp_parent),
+ CLK_SET_RATE_PARENT | CLK_SET_RATE_NO_REPARENT,
apmu_base + APMU_DISP1, 6, 2, 0, &clk_lock);
clk_register_clkdev(clk, "disp_mux.1", NULL);

@@ -394,7 +405,8 @@ void __init mmp2_clk_init(void)
clk_register_clkdev(clk, "ccic_arbiter", NULL);

clk = clk_register_mux(NULL, "ccic0_mux", ccic_parent,
- ARRAY_SIZE(ccic_parent), CLK_SET_RATE_PARENT,
+ ARRAY_SIZE(ccic_parent),
+ CLK_SET_RATE_PARENT | CLK_SET_RATE_NO_REPARENT,
apmu_base + APMU_CCIC0, 6, 2, 0, &clk_lock);
clk_register_clkdev(clk, "ccic_mux.0", NULL);

@@ -421,7 +433,8 @@ void __init mmp2_clk_init(void)
clk_register_clkdev(clk, "sphyclk", "mmp-ccic.0");

clk = clk_register_mux(NULL, "ccic1_mux", ccic_parent,
- ARRAY_SIZE(ccic_parent), CLK_SET_RATE_PARENT,
+ ARRAY_SIZE(ccic_parent),
+ CLK_SET_RATE_PARENT | CLK_SET_RATE_NO_REPARENT,
apmu_base + APMU_CCIC1, 6, 2, 0, &clk_lock);
clk_register_clkdev(clk, "ccic_mux.1", NULL);

diff --git a/drivers/clk/mmp/clk-pxa168.c b/drivers/clk/mmp/clk-pxa168.c
index 28b3b51..014396b 100644
--- a/drivers/clk/mmp/clk-pxa168.c
+++ b/drivers/clk/mmp/clk-pxa168.c
@@ -199,7 +199,8 @@ void __init pxa168_clk_init(void)
clk_register_clkdev(clk, NULL, "pxa168-pwm.3");

clk = clk_register_mux(NULL, "uart0_mux", uart_parent,
- ARRAY_SIZE(uart_parent), CLK_SET_RATE_PARENT,
+ ARRAY_SIZE(uart_parent),
+ CLK_SET_RATE_PARENT | CLK_SET_RATE_NO_REPARENT,
apbc_base + APBC_UART0, 4, 3, 0, &clk_lock);
clk_set_parent(clk, uart_pll);
clk_register_clkdev(clk, "uart_mux.0", NULL);
@@ -209,7 +210,8 @@ void __init pxa168_clk_init(void)
clk_register_clkdev(clk, NULL, "pxa2xx-uart.0");

clk = clk_register_mux(NULL, "uart1_mux", uart_parent,
- ARRAY_SIZE(uart_parent), CLK_SET_RATE_PARENT,
+ ARRAY_SIZE(uart_parent),
+ CLK_SET_RATE_PARENT | CLK_SET_RATE_NO_REPARENT,
apbc_base + APBC_UART1, 4, 3, 0, &clk_lock);
clk_set_parent(clk, uart_pll);
clk_register_clkdev(clk, "uart_mux.1", NULL);
@@ -219,7 +221,8 @@ void __init pxa168_clk_init(void)
clk_register_clkdev(clk, NULL, "pxa2xx-uart.1");

clk = clk_register_mux(NULL, "uart2_mux", uart_parent,
- ARRAY_SIZE(uart_parent), CLK_SET_RATE_PARENT,
+ ARRAY_SIZE(uart_parent),
+ CLK_SET_RATE_PARENT | CLK_SET_RATE_NO_REPARENT,
apbc_base + APBC_UART2, 4, 3, 0, &clk_lock);
clk_set_parent(clk, uart_pll);
clk_register_clkdev(clk, "uart_mux.2", NULL);
@@ -229,7 +232,8 @@ void __init pxa168_clk_init(void)
clk_register_clkdev(clk, NULL, "pxa2xx-uart.2");

clk = clk_register_mux(NULL, "ssp0_mux", ssp_parent,
- ARRAY_SIZE(ssp_parent), CLK_SET_RATE_PARENT,
+ ARRAY_SIZE(ssp_parent),
+ CLK_SET_RATE_PARENT | CLK_SET_RATE_NO_REPARENT,
apbc_base + APBC_SSP0, 4, 3, 0, &clk_lock);
clk_register_clkdev(clk, "uart_mux.0", NULL);

@@ -238,7 +242,8 @@ void __init pxa168_clk_init(void)
clk_register_clkdev(clk, NULL, "mmp-ssp.0");

clk = clk_register_mux(NULL, "ssp1_mux", ssp_parent,
- ARRAY_SIZE(ssp_parent), CLK_SET_RATE_PARENT,
+ ARRAY_SIZE(ssp_parent),
+ CLK_SET_RATE_PARENT | CLK_SET_RATE_NO_REPARENT,
apbc_base + APBC_SSP1, 4, 3, 0, &clk_lock);
clk_register_clkdev(clk, "ssp_mux.1", NULL);

@@ -247,7 +252,8 @@ void __init pxa168_clk_init(void)
clk_register_clkdev(clk, NULL, "mmp-ssp.1");

clk = clk_register_mux(NULL, "ssp2_mux", ssp_parent,
- ARRAY_SIZE(ssp_parent), CLK_SET_RATE_PARENT,
+ ARRAY_SIZE(ssp_parent),
+ CLK_SET_RATE_PARENT | CLK_SET_RATE_NO_REPARENT,
apbc_base + APBC_SSP2, 4, 3, 0, &clk_lock);
clk_register_clkdev(clk, "ssp_mux.2", NULL);

@@ -256,7 +262,8 @@ void __init pxa168_clk_init(void)
clk_register_clkdev(clk, NULL, "mmp-ssp.2");

clk = clk_register_mux(NULL, "ssp3_mux", ssp_parent,
- ARRAY_SIZE(ssp_parent), CLK_SET_RATE_PARENT,
+ ARRAY_SIZE(ssp_parent),
+ CLK_SET_RATE_PARENT | CLK_SET_RATE_NO_REPARENT,
apbc_base + APBC_SSP3, 4, 3, 0, &clk_lock);
clk_register_clkdev(clk, "ssp_mux.3", NULL);

@@ -265,7 +272,8 @@ void __init pxa168_clk_init(void)
clk_register_clkdev(clk, NULL, "mmp-ssp.3");

clk = clk_register_mux(NULL, "ssp4_mux", ssp_parent,
- ARRAY_SIZE(ssp_parent), CLK_SET_RATE_PARENT,
+ ARRAY_SIZE(ssp_parent),
+ CLK_SET_RATE_PARENT | CLK_SET_RATE_NO_REPARENT,
apbc_base + APBC_SSP4, 4, 3, 0, &clk_lock);
clk_register_clkdev(clk, "ssp_mux.4", NULL);

@@ -278,7 +286,8 @@ void __init pxa168_clk_init(void)
clk_register_clkdev(clk, NULL, "pxa3xx-nand.0");

clk = clk_register_mux(NULL, "sdh0_mux", sdh_parent,
- ARRAY_SIZE(sdh_parent), CLK_SET_RATE_PARENT,
+ ARRAY_SIZE(sdh_parent),
+ CLK_SET_RATE_PARENT | CLK_SET_RATE_NO_REPARENT,
apmu_base + APMU_SDH0, 6, 1, 0, &clk_lock);
clk_register_clkdev(clk, "sdh0_mux", NULL);

@@ -287,7 +296,8 @@ void __init pxa168_clk_init(void)
clk_register_clkdev(clk, NULL, "sdhci-pxa.0");

clk = clk_register_mux(NULL, "sdh1_mux", sdh_parent,
- ARRAY_SIZE(sdh_parent), CLK_SET_RATE_PARENT,
+ ARRAY_SIZE(sdh_parent),
+ CLK_SET_RATE_PARENT | CLK_SET_RATE_NO_REPARENT,
apmu_base + APMU_SDH1, 6, 1, 0, &clk_lock);
clk_register_clkdev(clk, "sdh1_mux", NULL);

@@ -304,7 +314,8 @@ void __init pxa168_clk_init(void)
clk_register_clkdev(clk, "sph_clk", NULL);

clk = clk_register_mux(NULL, "disp0_mux", disp_parent,
- ARRAY_SIZE(disp_parent), CLK_SET_RATE_PARENT,
+ ARRAY_SIZE(disp_parent),
+ CLK_SET_RATE_PARENT | CLK_SET_RATE_NO_REPARENT,
apmu_base + APMU_DISP0, 6, 1, 0, &clk_lock);
clk_register_clkdev(clk, "disp_mux.0", NULL);

@@ -317,7 +328,8 @@ void __init pxa168_clk_init(void)
clk_register_clkdev(clk, "hclk", "mmp-disp.0");

clk = clk_register_mux(NULL, "ccic0_mux", ccic_parent,
- ARRAY_SIZE(ccic_parent), CLK_SET_RATE_PARENT,
+ ARRAY_SIZE(ccic_parent),
+ CLK_SET_RATE_PARENT | CLK_SET_RATE_NO_REPARENT,
apmu_base + APMU_CCIC0, 6, 1, 0, &clk_lock);
clk_register_clkdev(clk, "ccic_mux.0", NULL);

@@ -327,8 +339,8 @@ void __init pxa168_clk_init(void)

clk = clk_register_mux(NULL, "ccic0_phy_mux", ccic_phy_parent,
ARRAY_SIZE(ccic_phy_parent),
- CLK_SET_RATE_PARENT, apmu_base + APMU_CCIC0,
- 7, 1, 0, &clk_lock);
+ CLK_SET_RATE_PARENT | CLK_SET_RATE_NO_REPARENT,
+ apmu_base + APMU_CCIC0, 7, 1, 0, &clk_lock);
clk_register_clkdev(clk, "ccic_phy_mux.0", NULL);

clk = mmp_clk_register_apmu("ccic0_phy", "ccic0_phy_mux",
diff --git a/drivers/clk/mmp/clk-pxa910.c b/drivers/clk/mmp/clk-pxa910.c
index 6ec0569..9efc6a4 100644
--- a/drivers/clk/mmp/clk-pxa910.c
+++ b/drivers/clk/mmp/clk-pxa910.c
@@ -204,7 +204,8 @@ void __init pxa910_clk_init(void)
clk_register_clkdev(clk, NULL, "pxa910-pwm.3");

clk = clk_register_mux(NULL, "uart0_mux", uart_parent,
- ARRAY_SIZE(uart_parent), CLK_SET_RATE_PARENT,
+ ARRAY_SIZE(uart_parent),
+ CLK_SET_RATE_PARENT | CLK_SET_RATE_NO_REPARENT,
apbc_base + APBC_UART0, 4, 3, 0, &clk_lock);
clk_set_parent(clk, uart_pll);
clk_register_clkdev(clk, "uart_mux.0", NULL);
@@ -214,7 +215,8 @@ void __init pxa910_clk_init(void)
clk_register_clkdev(clk, NULL, "pxa2xx-uart.0");

clk = clk_register_mux(NULL, "uart1_mux", uart_parent,
- ARRAY_SIZE(uart_parent), CLK_SET_RATE_PARENT,
+ ARRAY_SIZE(uart_parent),
+ CLK_SET_RATE_PARENT | CLK_SET_RATE_NO_REPARENT,
apbc_base + APBC_UART1, 4, 3, 0, &clk_lock);
clk_set_parent(clk, uart_pll);
clk_register_clkdev(clk, "uart_mux.1", NULL);
@@ -224,7 +226,8 @@ void __init pxa910_clk_init(void)
clk_register_clkdev(clk, NULL, "pxa2xx-uart.1");

clk = clk_register_mux(NULL, "uart2_mux", uart_parent,
- ARRAY_SIZE(uart_parent), CLK_SET_RATE_PARENT,
+ ARRAY_SIZE(uart_parent),
+ CLK_SET_RATE_PARENT | CLK_SET_RATE_NO_REPARENT,
apbcp_base + APBCP_UART2, 4, 3, 0, &clk_lock);
clk_set_parent(clk, uart_pll);
clk_register_clkdev(clk, "uart_mux.2", NULL);
@@ -234,7 +237,8 @@ void __init pxa910_clk_init(void)
clk_register_clkdev(clk, NULL, "pxa2xx-uart.2");

clk = clk_register_mux(NULL, "ssp0_mux", ssp_parent,
- ARRAY_SIZE(ssp_parent), CLK_SET_RATE_PARENT,
+ ARRAY_SIZE(ssp_parent),
+ CLK_SET_RATE_PARENT | CLK_SET_RATE_NO_REPARENT,
apbc_base + APBC_SSP0, 4, 3, 0, &clk_lock);
clk_register_clkdev(clk, "uart_mux.0", NULL);

@@ -243,7 +247,8 @@ void __init pxa910_clk_init(void)
clk_register_clkdev(clk, NULL, "mmp-ssp.0");

clk = clk_register_mux(NULL, "ssp1_mux", ssp_parent,
- ARRAY_SIZE(ssp_parent), CLK_SET_RATE_PARENT,
+ ARRAY_SIZE(ssp_parent),
+ CLK_SET_RATE_PARENT | CLK_SET_RATE_NO_REPARENT,
apbc_base + APBC_SSP1, 4, 3, 0, &clk_lock);
clk_register_clkdev(clk, "ssp_mux.1", NULL);

@@ -256,7 +261,8 @@ void __init pxa910_clk_init(void)
clk_register_clkdev(clk, NULL, "pxa3xx-nand.0");

clk = clk_register_mux(NULL, "sdh0_mux", sdh_parent,
- ARRAY_SIZE(sdh_parent), CLK_SET_RATE_PARENT,
+ ARRAY_SIZE(sdh_parent),
+ CLK_SET_RATE_PARENT | CLK_SET_RATE_NO_REPARENT,
apmu_base + APMU_SDH0, 6, 1, 0, &clk_lock);
clk_register_clkdev(clk, "sdh0_mux", NULL);

@@ -265,7 +271,8 @@ void __init pxa910_clk_init(void)
clk_register_clkdev(clk, NULL, "sdhci-pxa.0");

clk = clk_register_mux(NULL, "sdh1_mux", sdh_parent,
- ARRAY_SIZE(sdh_parent), CLK_SET_RATE_PARENT,
+ ARRAY_SIZE(sdh_parent),
+ CLK_SET_RATE_PARENT | CLK_SET_RATE_NO_REPARENT,
apmu_base + APMU_SDH1, 6, 1, 0, &clk_lock);
clk_register_clkdev(clk, "sdh1_mux", NULL);

@@ -282,7 +289,8 @@ void __init pxa910_clk_init(void)
clk_register_clkdev(clk, "sph_clk", NULL);

clk = clk_register_mux(NULL, "disp0_mux", disp_parent,
- ARRAY_SIZE(disp_parent), CLK_SET_RATE_PARENT,
+ ARRAY_SIZE(disp_parent),
+ CLK_SET_RATE_PARENT | CLK_SET_RATE_NO_REPARENT,
apmu_base + APMU_DISP0, 6, 1, 0, &clk_lock);
clk_register_clkdev(clk, "disp_mux.0", NULL);

@@ -291,7 +299,8 @@ void __init pxa910_clk_init(void)
clk_register_clkdev(clk, NULL, "mmp-disp.0");

clk = clk_register_mux(NULL, "ccic0_mux", ccic_parent,
- ARRAY_SIZE(ccic_parent), CLK_SET_RATE_PARENT,
+ ARRAY_SIZE(ccic_parent),
+ CLK_SET_RATE_PARENT | CLK_SET_RATE_NO_REPARENT,
apmu_base + APMU_CCIC0, 6, 1, 0, &clk_lock);
clk_register_clkdev(clk, "ccic_mux.0", NULL);

@@ -301,8 +310,8 @@ void __init pxa910_clk_init(void)

clk = clk_register_mux(NULL, "ccic0_phy_mux", ccic_phy_parent,
ARRAY_SIZE(ccic_phy_parent),
- CLK_SET_RATE_PARENT, apmu_base + APMU_CCIC0,
- 7, 1, 0, &clk_lock);
+ CLK_SET_RATE_PARENT | CLK_SET_RATE_NO_REPARENT,
+ apmu_base + APMU_CCIC0, 7, 1, 0, &clk_lock);
clk_register_clkdev(clk, "ccic_phy_mux.0", NULL);

clk = mmp_clk_register_apmu("ccic0_phy", "ccic0_phy_mux",
diff --git a/drivers/clk/mxs/clk.h b/drivers/clk/mxs/clk.h
index 81421e2..ef10ad9 100644
--- a/drivers/clk/mxs/clk.h
+++ b/drivers/clk/mxs/clk.h
@@ -52,8 +52,8 @@ static inline struct clk *mxs_clk_mux(const char *name, void __iomem *reg,
u8 shift, u8 width, const char **parent_names, int num_parents)
{
return clk_register_mux(NULL, name, parent_names, num_parents,
- CLK_SET_RATE_PARENT, reg, shift, width,
- 0, &mxs_lock);
+ CLK_SET_RATE_PARENT | CLK_SET_RATE_NO_REPARENT,
+ reg, shift, width, 0, &mxs_lock);
}

static inline struct clk *mxs_clk_fixed_factor(const char *name,
diff --git a/drivers/clk/samsung/clk.h b/drivers/clk/samsung/clk.h
index e4ad6ea..eb672ba 100644
--- a/drivers/clk/samsung/clk.h
+++ b/drivers/clk/samsung/clk.h
@@ -127,7 +127,7 @@ struct samsung_mux_clock {
.name = cname, \
.parent_names = pnames, \
.num_parents = ARRAY_SIZE(pnames), \
- .flags = f, \
+ .flags = (f) | CLK_SET_RATE_NO_REPARENT, \
.offset = o, \
.shift = s, \
.width = w, \
diff --git a/drivers/clk/spear/spear1310_clock.c b/drivers/clk/spear/spear1310_clock.c
index aedbbe1..65894f7 100644
--- a/drivers/clk/spear/spear1310_clock.c
+++ b/drivers/clk/spear/spear1310_clock.c
@@ -416,9 +416,9 @@ void __init spear1310_clk_init(void __iomem *misc_base, void __iomem *ras_base)
/* clock derived from 24 or 25 MHz osc clk */
/* vco-pll */
clk = clk_register_mux(NULL, "vco1_mclk", vco_parents,
- ARRAY_SIZE(vco_parents), 0, SPEAR1310_PLL_CFG,
- SPEAR1310_PLL1_CLK_SHIFT, SPEAR1310_PLL_CLK_MASK, 0,
- &_lock);
+ ARRAY_SIZE(vco_parents), CLK_SET_RATE_NO_REPARENT,
+ SPEAR1310_PLL_CFG, SPEAR1310_PLL1_CLK_SHIFT,
+ SPEAR1310_PLL_CLK_MASK, 0, &_lock);
clk_register_clkdev(clk, "vco1_mclk", NULL);
clk = clk_register_vco_pll("vco1_clk", "pll1_clk", NULL, "vco1_mclk",
0, SPEAR1310_PLL1_CTR, SPEAR1310_PLL1_FRQ, pll_rtbl,
@@ -427,9 +427,9 @@ void __init spear1310_clk_init(void __iomem *misc_base, void __iomem *ras_base)
clk_register_clkdev(clk1, "pll1_clk", NULL);

clk = clk_register_mux(NULL, "vco2_mclk", vco_parents,
- ARRAY_SIZE(vco_parents), 0, SPEAR1310_PLL_CFG,
- SPEAR1310_PLL2_CLK_SHIFT, SPEAR1310_PLL_CLK_MASK, 0,
- &_lock);
+ ARRAY_SIZE(vco_parents), CLK_SET_RATE_NO_REPARENT,
+ SPEAR1310_PLL_CFG, SPEAR1310_PLL2_CLK_SHIFT,
+ SPEAR1310_PLL_CLK_MASK, 0, &_lock);
clk_register_clkdev(clk, "vco2_mclk", NULL);
clk = clk_register_vco_pll("vco2_clk", "pll2_clk", NULL, "vco2_mclk",
0, SPEAR1310_PLL2_CTR, SPEAR1310_PLL2_FRQ, pll_rtbl,
@@ -438,9 +438,9 @@ void __init spear1310_clk_init(void __iomem *misc_base, void __iomem *ras_base)
clk_register_clkdev(clk1, "pll2_clk", NULL);

clk = clk_register_mux(NULL, "vco3_mclk", vco_parents,
- ARRAY_SIZE(vco_parents), 0, SPEAR1310_PLL_CFG,
- SPEAR1310_PLL3_CLK_SHIFT, SPEAR1310_PLL_CLK_MASK, 0,
- &_lock);
+ ARRAY_SIZE(vco_parents), CLK_SET_RATE_NO_REPARENT,
+ SPEAR1310_PLL_CFG, SPEAR1310_PLL3_CLK_SHIFT,
+ SPEAR1310_PLL_CLK_MASK, 0, &_lock);
clk_register_clkdev(clk, "vco3_mclk", NULL);
clk = clk_register_vco_pll("vco3_clk", "pll3_clk", NULL, "vco3_mclk",
0, SPEAR1310_PLL3_CTR, SPEAR1310_PLL3_FRQ, pll_rtbl,
@@ -515,9 +515,9 @@ void __init spear1310_clk_init(void __iomem *misc_base, void __iomem *ras_base)

/* gpt clocks */
clk = clk_register_mux(NULL, "gpt0_mclk", gpt_parents,
- ARRAY_SIZE(gpt_parents), 0, SPEAR1310_PERIP_CLK_CFG,
- SPEAR1310_GPT0_CLK_SHIFT, SPEAR1310_GPT_CLK_MASK, 0,
- &_lock);
+ ARRAY_SIZE(gpt_parents), CLK_SET_RATE_NO_REPARENT,
+ SPEAR1310_PERIP_CLK_CFG, SPEAR1310_GPT0_CLK_SHIFT,
+ SPEAR1310_GPT_CLK_MASK, 0, &_lock);
clk_register_clkdev(clk, "gpt0_mclk", NULL);
clk = clk_register_gate(NULL, "gpt0_clk", "gpt0_mclk", 0,
SPEAR1310_PERIP1_CLK_ENB, SPEAR1310_GPT0_CLK_ENB, 0,
@@ -525,9 +525,9 @@ void __init spear1310_clk_init(void __iomem *misc_base, void __iomem *ras_base)
clk_register_clkdev(clk, NULL, "gpt0");

clk = clk_register_mux(NULL, "gpt1_mclk", gpt_parents,
- ARRAY_SIZE(gpt_parents), 0, SPEAR1310_PERIP_CLK_CFG,
- SPEAR1310_GPT1_CLK_SHIFT, SPEAR1310_GPT_CLK_MASK, 0,
- &_lock);
+ ARRAY_SIZE(gpt_parents), CLK_SET_RATE_NO_REPARENT,
+ SPEAR1310_PERIP_CLK_CFG, SPEAR1310_GPT1_CLK_SHIFT,
+ SPEAR1310_GPT_CLK_MASK, 0, &_lock);
clk_register_clkdev(clk, "gpt1_mclk", NULL);
clk = clk_register_gate(NULL, "gpt1_clk", "gpt1_mclk", 0,
SPEAR1310_PERIP1_CLK_ENB, SPEAR1310_GPT1_CLK_ENB, 0,
@@ -535,9 +535,9 @@ void __init spear1310_clk_init(void __iomem *misc_base, void __iomem *ras_base)
clk_register_clkdev(clk, NULL, "gpt1");

clk = clk_register_mux(NULL, "gpt2_mclk", gpt_parents,
- ARRAY_SIZE(gpt_parents), 0, SPEAR1310_PERIP_CLK_CFG,
- SPEAR1310_GPT2_CLK_SHIFT, SPEAR1310_GPT_CLK_MASK, 0,
- &_lock);
+ ARRAY_SIZE(gpt_parents), CLK_SET_RATE_NO_REPARENT,
+ SPEAR1310_PERIP_CLK_CFG, SPEAR1310_GPT2_CLK_SHIFT,
+ SPEAR1310_GPT_CLK_MASK, 0, &_lock);
clk_register_clkdev(clk, "gpt2_mclk", NULL);
clk = clk_register_gate(NULL, "gpt2_clk", "gpt2_mclk", 0,
SPEAR1310_PERIP2_CLK_ENB, SPEAR1310_GPT2_CLK_ENB, 0,
@@ -545,9 +545,9 @@ void __init spear1310_clk_init(void __iomem *misc_base, void __iomem *ras_base)
clk_register_clkdev(clk, NULL, "gpt2");

clk = clk_register_mux(NULL, "gpt3_mclk", gpt_parents,
- ARRAY_SIZE(gpt_parents), 0, SPEAR1310_PERIP_CLK_CFG,
- SPEAR1310_GPT3_CLK_SHIFT, SPEAR1310_GPT_CLK_MASK, 0,
- &_lock);
+ ARRAY_SIZE(gpt_parents), CLK_SET_RATE_NO_REPARENT,
+ SPEAR1310_PERIP_CLK_CFG, SPEAR1310_GPT3_CLK_SHIFT,
+ SPEAR1310_GPT_CLK_MASK, 0, &_lock);
clk_register_clkdev(clk, "gpt3_mclk", NULL);
clk = clk_register_gate(NULL, "gpt3_clk", "gpt3_mclk", 0,
SPEAR1310_PERIP2_CLK_ENB, SPEAR1310_GPT3_CLK_ENB, 0,
@@ -562,7 +562,8 @@ void __init spear1310_clk_init(void __iomem *misc_base, void __iomem *ras_base)
clk_register_clkdev(clk1, "uart_syn_gclk", NULL);

clk = clk_register_mux(NULL, "uart0_mclk", uart0_parents,
- ARRAY_SIZE(uart0_parents), CLK_SET_RATE_PARENT,
+ ARRAY_SIZE(uart0_parents),
+ CLK_SET_RATE_PARENT | CLK_SET_RATE_NO_REPARENT,
SPEAR1310_PERIP_CLK_CFG, SPEAR1310_UART_CLK_SHIFT,
SPEAR1310_UART_CLK_MASK, 0, &_lock);
clk_register_clkdev(clk, "uart0_mclk", NULL);
@@ -602,7 +603,8 @@ void __init spear1310_clk_init(void __iomem *misc_base, void __iomem *ras_base)
clk_register_clkdev(clk1, "c3_syn_gclk", NULL);

clk = clk_register_mux(NULL, "c3_mclk", c3_parents,
- ARRAY_SIZE(c3_parents), CLK_SET_RATE_PARENT,
+ ARRAY_SIZE(c3_parents),
+ CLK_SET_RATE_PARENT | CLK_SET_RATE_NO_REPARENT,
SPEAR1310_PERIP_CLK_CFG, SPEAR1310_C3_CLK_SHIFT,
SPEAR1310_C3_CLK_MASK, 0, &_lock);
clk_register_clkdev(clk, "c3_mclk", NULL);
@@ -614,8 +616,8 @@ void __init spear1310_clk_init(void __iomem *misc_base, void __iomem *ras_base)

/* gmac */
clk = clk_register_mux(NULL, "phy_input_mclk", gmac_phy_input_parents,
- ARRAY_SIZE(gmac_phy_input_parents), 0,
- SPEAR1310_GMAC_CLK_CFG,
+ ARRAY_SIZE(gmac_phy_input_parents),
+ CLK_SET_RATE_NO_REPARENT, SPEAR1310_GMAC_CLK_CFG,
SPEAR1310_GMAC_PHY_INPUT_CLK_SHIFT,
SPEAR1310_GMAC_PHY_INPUT_CLK_MASK, 0, &_lock);
clk_register_clkdev(clk, "phy_input_mclk", NULL);
@@ -627,15 +629,16 @@ void __init spear1310_clk_init(void __iomem *misc_base, void __iomem *ras_base)
clk_register_clkdev(clk1, "phy_syn_gclk", NULL);

clk = clk_register_mux(NULL, "phy_mclk", gmac_phy_parents,
- ARRAY_SIZE(gmac_phy_parents), 0,
+ ARRAY_SIZE(gmac_phy_parents), CLK_SET_RATE_NO_REPARENT,
SPEAR1310_PERIP_CLK_CFG, SPEAR1310_GMAC_PHY_CLK_SHIFT,
SPEAR1310_GMAC_PHY_CLK_MASK, 0, &_lock);
clk_register_clkdev(clk, "stmmacphy.0", NULL);

/* clcd */
clk = clk_register_mux(NULL, "clcd_syn_mclk", clcd_synth_parents,
- ARRAY_SIZE(clcd_synth_parents), 0,
- SPEAR1310_CLCD_CLK_SYNT, SPEAR1310_CLCD_SYNT_CLK_SHIFT,
+ ARRAY_SIZE(clcd_synth_parents),
+ CLK_SET_RATE_NO_REPARENT, SPEAR1310_CLCD_CLK_SYNT,
+ SPEAR1310_CLCD_SYNT_CLK_SHIFT,
SPEAR1310_CLCD_SYNT_CLK_MASK, 0, &_lock);
clk_register_clkdev(clk, "clcd_syn_mclk", NULL);

@@ -645,7 +648,8 @@ void __init spear1310_clk_init(void __iomem *misc_base, void __iomem *ras_base)
clk_register_clkdev(clk, "clcd_syn_clk", NULL);

clk = clk_register_mux(NULL, "clcd_pixel_mclk", clcd_pixel_parents,
- ARRAY_SIZE(clcd_pixel_parents), CLK_SET_RATE_PARENT,
+ ARRAY_SIZE(clcd_pixel_parents),
+ CLK_SET_RATE_PARENT | CLK_SET_RATE_NO_REPARENT,
SPEAR1310_PERIP_CLK_CFG, SPEAR1310_CLCD_CLK_SHIFT,
SPEAR1310_CLCD_CLK_MASK, 0, &_lock);
clk_register_clkdev(clk, "clcd_pixel_mclk", NULL);
@@ -657,9 +661,9 @@ void __init spear1310_clk_init(void __iomem *misc_base, void __iomem *ras_base)

/* i2s */
clk = clk_register_mux(NULL, "i2s_src_mclk", i2s_src_parents,
- ARRAY_SIZE(i2s_src_parents), 0, SPEAR1310_I2S_CLK_CFG,
- SPEAR1310_I2S_SRC_CLK_SHIFT, SPEAR1310_I2S_SRC_CLK_MASK,
- 0, &_lock);
+ ARRAY_SIZE(i2s_src_parents), CLK_SET_RATE_NO_REPARENT,
+ SPEAR1310_I2S_CLK_CFG, SPEAR1310_I2S_SRC_CLK_SHIFT,
+ SPEAR1310_I2S_SRC_CLK_MASK, 0, &_lock);
clk_register_clkdev(clk, "i2s_src_mclk", NULL);

clk = clk_register_aux("i2s_prs1_clk", NULL, "i2s_src_mclk", 0,
@@ -668,7 +672,8 @@ void __init spear1310_clk_init(void __iomem *misc_base, void __iomem *ras_base)
clk_register_clkdev(clk, "i2s_prs1_clk", NULL);

clk = clk_register_mux(NULL, "i2s_ref_mclk", i2s_ref_parents,
- ARRAY_SIZE(i2s_ref_parents), CLK_SET_RATE_PARENT,
+ ARRAY_SIZE(i2s_ref_parents),
+ CLK_SET_RATE_PARENT | CLK_SET_RATE_NO_REPARENT,
SPEAR1310_I2S_CLK_CFG, SPEAR1310_I2S_REF_SHIFT,
SPEAR1310_I2S_REF_SEL_MASK, 0, &_lock);
clk_register_clkdev(clk, "i2s_ref_mclk", NULL);
@@ -806,13 +811,15 @@ void __init spear1310_clk_init(void __iomem *misc_base, void __iomem *ras_base)

/* RAS clks */
clk = clk_register_mux(NULL, "gen_syn0_1_mclk", gen_synth0_1_parents,
- ARRAY_SIZE(gen_synth0_1_parents), 0, SPEAR1310_PLL_CFG,
+ ARRAY_SIZE(gen_synth0_1_parents),
+ CLK_SET_RATE_NO_REPARENT, SPEAR1310_PLL_CFG,
SPEAR1310_RAS_SYNT0_1_CLK_SHIFT,
SPEAR1310_RAS_SYNT_CLK_MASK, 0, &_lock);
clk_register_clkdev(clk, "gen_syn0_1_clk", NULL);

clk = clk_register_mux(NULL, "gen_syn2_3_mclk", gen_synth2_3_parents,
- ARRAY_SIZE(gen_synth2_3_parents), 0, SPEAR1310_PLL_CFG,
+ ARRAY_SIZE(gen_synth2_3_parents),
+ CLK_SET_RATE_NO_REPARENT, SPEAR1310_PLL_CFG,
SPEAR1310_RAS_SYNT2_3_CLK_SHIFT,
SPEAR1310_RAS_SYNT_CLK_MASK, 0, &_lock);
clk_register_clkdev(clk, "gen_syn2_3_clk", NULL);
@@ -929,8 +936,8 @@ void __init spear1310_clk_init(void __iomem *misc_base, void __iomem *ras_base)

clk = clk_register_mux(NULL, "smii_rgmii_phy_mclk",
smii_rgmii_phy_parents,
- ARRAY_SIZE(smii_rgmii_phy_parents), 0,
- SPEAR1310_RAS_CTRL_REG1,
+ ARRAY_SIZE(smii_rgmii_phy_parents),
+ CLK_SET_RATE_NO_REPARENT, SPEAR1310_RAS_CTRL_REG1,
SPEAR1310_SMII_RGMII_PHY_CLK_SHIFT,
SPEAR1310_PHY_CLK_MASK, 0, &_lock);
clk_register_clkdev(clk, "stmmacphy.1", NULL);
@@ -938,15 +945,15 @@ void __init spear1310_clk_init(void __iomem *misc_base, void __iomem *ras_base)
clk_register_clkdev(clk, "stmmacphy.4", NULL);

clk = clk_register_mux(NULL, "rmii_phy_mclk", rmii_phy_parents,
- ARRAY_SIZE(rmii_phy_parents), 0,
+ ARRAY_SIZE(rmii_phy_parents), CLK_SET_RATE_NO_REPARENT,
SPEAR1310_RAS_CTRL_REG1, SPEAR1310_RMII_PHY_CLK_SHIFT,
SPEAR1310_PHY_CLK_MASK, 0, &_lock);
clk_register_clkdev(clk, "stmmacphy.3", NULL);

clk = clk_register_mux(NULL, "uart1_mclk", uart_parents,
- ARRAY_SIZE(uart_parents), 0, SPEAR1310_RAS_CTRL_REG0,
- SPEAR1310_UART1_CLK_SHIFT, SPEAR1310_RAS_UART_CLK_MASK,
- 0, &_lock);
+ ARRAY_SIZE(uart_parents), CLK_SET_RATE_NO_REPARENT,
+ SPEAR1310_RAS_CTRL_REG0, SPEAR1310_UART1_CLK_SHIFT,
+ SPEAR1310_RAS_UART_CLK_MASK, 0, &_lock);
clk_register_clkdev(clk, "uart1_mclk", NULL);

clk = clk_register_gate(NULL, "uart1_clk", "uart1_mclk", 0,
@@ -955,9 +962,9 @@ void __init spear1310_clk_init(void __iomem *misc_base, void __iomem *ras_base)
clk_register_clkdev(clk, NULL, "5c800000.serial");

clk = clk_register_mux(NULL, "uart2_mclk", uart_parents,
- ARRAY_SIZE(uart_parents), 0, SPEAR1310_RAS_CTRL_REG0,
- SPEAR1310_UART2_CLK_SHIFT, SPEAR1310_RAS_UART_CLK_MASK,
- 0, &_lock);
+ ARRAY_SIZE(uart_parents), CLK_SET_RATE_NO_REPARENT,
+ SPEAR1310_RAS_CTRL_REG0, SPEAR1310_UART2_CLK_SHIFT,
+ SPEAR1310_RAS_UART_CLK_MASK, 0, &_lock);
clk_register_clkdev(clk, "uart2_mclk", NULL);

clk = clk_register_gate(NULL, "uart2_clk", "uart2_mclk", 0,
@@ -966,9 +973,9 @@ void __init spear1310_clk_init(void __iomem *misc_base, void __iomem *ras_base)
clk_register_clkdev(clk, NULL, "5c900000.serial");

clk = clk_register_mux(NULL, "uart3_mclk", uart_parents,
- ARRAY_SIZE(uart_parents), 0, SPEAR1310_RAS_CTRL_REG0,
- SPEAR1310_UART3_CLK_SHIFT, SPEAR1310_RAS_UART_CLK_MASK,
- 0, &_lock);
+ ARRAY_SIZE(uart_parents), CLK_SET_RATE_NO_REPARENT,
+ SPEAR1310_RAS_CTRL_REG0, SPEAR1310_UART3_CLK_SHIFT,
+ SPEAR1310_RAS_UART_CLK_MASK, 0, &_lock);
clk_register_clkdev(clk, "uart3_mclk", NULL);

clk = clk_register_gate(NULL, "uart3_clk", "uart3_mclk", 0,
@@ -977,9 +984,9 @@ void __init spear1310_clk_init(void __iomem *misc_base, void __iomem *ras_base)
clk_register_clkdev(clk, NULL, "5ca00000.serial");

clk = clk_register_mux(NULL, "uart4_mclk", uart_parents,
- ARRAY_SIZE(uart_parents), 0, SPEAR1310_RAS_CTRL_REG0,
- SPEAR1310_UART4_CLK_SHIFT, SPEAR1310_RAS_UART_CLK_MASK,
- 0, &_lock);
+ ARRAY_SIZE(uart_parents), CLK_SET_RATE_NO_REPARENT,
+ SPEAR1310_RAS_CTRL_REG0, SPEAR1310_UART4_CLK_SHIFT,
+ SPEAR1310_RAS_UART_CLK_MASK, 0, &_lock);
clk_register_clkdev(clk, "uart4_mclk", NULL);

clk = clk_register_gate(NULL, "uart4_clk", "uart4_mclk", 0,
@@ -988,9 +995,9 @@ void __init spear1310_clk_init(void __iomem *misc_base, void __iomem *ras_base)
clk_register_clkdev(clk, NULL, "5cb00000.serial");

clk = clk_register_mux(NULL, "uart5_mclk", uart_parents,
- ARRAY_SIZE(uart_parents), 0, SPEAR1310_RAS_CTRL_REG0,
- SPEAR1310_UART5_CLK_SHIFT, SPEAR1310_RAS_UART_CLK_MASK,
- 0, &_lock);
+ ARRAY_SIZE(uart_parents), CLK_SET_RATE_NO_REPARENT,
+ SPEAR1310_RAS_CTRL_REG0, SPEAR1310_UART5_CLK_SHIFT,
+ SPEAR1310_RAS_UART_CLK_MASK, 0, &_lock);
clk_register_clkdev(clk, "uart5_mclk", NULL);

clk = clk_register_gate(NULL, "uart5_clk", "uart5_mclk", 0,
@@ -999,9 +1006,9 @@ void __init spear1310_clk_init(void __iomem *misc_base, void __iomem *ras_base)
clk_register_clkdev(clk, NULL, "5cc00000.serial");

clk = clk_register_mux(NULL, "i2c1_mclk", i2c_parents,
- ARRAY_SIZE(i2c_parents), 0, SPEAR1310_RAS_CTRL_REG0,
- SPEAR1310_I2C1_CLK_SHIFT, SPEAR1310_I2C_CLK_MASK, 0,
- &_lock);
+ ARRAY_SIZE(i2c_parents), CLK_SET_RATE_NO_REPARENT,
+ SPEAR1310_RAS_CTRL_REG0, SPEAR1310_I2C1_CLK_SHIFT,
+ SPEAR1310_I2C_CLK_MASK, 0, &_lock);
clk_register_clkdev(clk, "i2c1_mclk", NULL);

clk = clk_register_gate(NULL, "i2c1_clk", "i2c1_mclk", 0,
@@ -1010,9 +1017,9 @@ void __init spear1310_clk_init(void __iomem *misc_base, void __iomem *ras_base)
clk_register_clkdev(clk, NULL, "5cd00000.i2c");

clk = clk_register_mux(NULL, "i2c2_mclk", i2c_parents,
- ARRAY_SIZE(i2c_parents), 0, SPEAR1310_RAS_CTRL_REG0,
- SPEAR1310_I2C2_CLK_SHIFT, SPEAR1310_I2C_CLK_MASK, 0,
- &_lock);
+ ARRAY_SIZE(i2c_parents), CLK_SET_RATE_NO_REPARENT,
+ SPEAR1310_RAS_CTRL_REG0, SPEAR1310_I2C2_CLK_SHIFT,
+ SPEAR1310_I2C_CLK_MASK, 0, &_lock);
clk_register_clkdev(clk, "i2c2_mclk", NULL);

clk = clk_register_gate(NULL, "i2c2_clk", "i2c2_mclk", 0,
@@ -1021,9 +1028,9 @@ void __init spear1310_clk_init(void __iomem *misc_base, void __iomem *ras_base)
clk_register_clkdev(clk, NULL, "5ce00000.i2c");

clk = clk_register_mux(NULL, "i2c3_mclk", i2c_parents,
- ARRAY_SIZE(i2c_parents), 0, SPEAR1310_RAS_CTRL_REG0,
- SPEAR1310_I2C3_CLK_SHIFT, SPEAR1310_I2C_CLK_MASK, 0,
- &_lock);
+ ARRAY_SIZE(i2c_parents), CLK_SET_RATE_NO_REPARENT,
+ SPEAR1310_RAS_CTRL_REG0, SPEAR1310_I2C3_CLK_SHIFT,
+ SPEAR1310_I2C_CLK_MASK, 0, &_lock);
clk_register_clkdev(clk, "i2c3_mclk", NULL);

clk = clk_register_gate(NULL, "i2c3_clk", "i2c3_mclk", 0,
@@ -1032,9 +1039,9 @@ void __init spear1310_clk_init(void __iomem *misc_base, void __iomem *ras_base)
clk_register_clkdev(clk, NULL, "5cf00000.i2c");

clk = clk_register_mux(NULL, "i2c4_mclk", i2c_parents,
- ARRAY_SIZE(i2c_parents), 0, SPEAR1310_RAS_CTRL_REG0,
- SPEAR1310_I2C4_CLK_SHIFT, SPEAR1310_I2C_CLK_MASK, 0,
- &_lock);
+ ARRAY_SIZE(i2c_parents), CLK_SET_RATE_NO_REPARENT,
+ SPEAR1310_RAS_CTRL_REG0, SPEAR1310_I2C4_CLK_SHIFT,
+ SPEAR1310_I2C_CLK_MASK, 0, &_lock);
clk_register_clkdev(clk, "i2c4_mclk", NULL);

clk = clk_register_gate(NULL, "i2c4_clk", "i2c4_mclk", 0,
@@ -1043,9 +1050,9 @@ void __init spear1310_clk_init(void __iomem *misc_base, void __iomem *ras_base)
clk_register_clkdev(clk, NULL, "5d000000.i2c");

clk = clk_register_mux(NULL, "i2c5_mclk", i2c_parents,
- ARRAY_SIZE(i2c_parents), 0, SPEAR1310_RAS_CTRL_REG0,
- SPEAR1310_I2C5_CLK_SHIFT, SPEAR1310_I2C_CLK_MASK, 0,
- &_lock);
+ ARRAY_SIZE(i2c_parents), CLK_SET_RATE_NO_REPARENT,
+ SPEAR1310_RAS_CTRL_REG0, SPEAR1310_I2C5_CLK_SHIFT,
+ SPEAR1310_I2C_CLK_MASK, 0, &_lock);
clk_register_clkdev(clk, "i2c5_mclk", NULL);

clk = clk_register_gate(NULL, "i2c5_clk", "i2c5_mclk", 0,
@@ -1054,9 +1061,9 @@ void __init spear1310_clk_init(void __iomem *misc_base, void __iomem *ras_base)
clk_register_clkdev(clk, NULL, "5d100000.i2c");

clk = clk_register_mux(NULL, "i2c6_mclk", i2c_parents,
- ARRAY_SIZE(i2c_parents), 0, SPEAR1310_RAS_CTRL_REG0,
- SPEAR1310_I2C6_CLK_SHIFT, SPEAR1310_I2C_CLK_MASK, 0,
- &_lock);
+ ARRAY_SIZE(i2c_parents), CLK_SET_RATE_NO_REPARENT,
+ SPEAR1310_RAS_CTRL_REG0, SPEAR1310_I2C6_CLK_SHIFT,
+ SPEAR1310_I2C_CLK_MASK, 0, &_lock);
clk_register_clkdev(clk, "i2c6_mclk", NULL);

clk = clk_register_gate(NULL, "i2c6_clk", "i2c6_mclk", 0,
@@ -1065,9 +1072,9 @@ void __init spear1310_clk_init(void __iomem *misc_base, void __iomem *ras_base)
clk_register_clkdev(clk, NULL, "5d200000.i2c");

clk = clk_register_mux(NULL, "i2c7_mclk", i2c_parents,
- ARRAY_SIZE(i2c_parents), 0, SPEAR1310_RAS_CTRL_REG0,
- SPEAR1310_I2C7_CLK_SHIFT, SPEAR1310_I2C_CLK_MASK, 0,
- &_lock);
+ ARRAY_SIZE(i2c_parents), CLK_SET_RATE_NO_REPARENT,
+ SPEAR1310_RAS_CTRL_REG0, SPEAR1310_I2C7_CLK_SHIFT,
+ SPEAR1310_I2C_CLK_MASK, 0, &_lock);
clk_register_clkdev(clk, "i2c7_mclk", NULL);

clk = clk_register_gate(NULL, "i2c7_clk", "i2c7_mclk", 0,
@@ -1076,9 +1083,9 @@ void __init spear1310_clk_init(void __iomem *misc_base, void __iomem *ras_base)
clk_register_clkdev(clk, NULL, "5d300000.i2c");

clk = clk_register_mux(NULL, "ssp1_mclk", ssp1_parents,
- ARRAY_SIZE(ssp1_parents), 0, SPEAR1310_RAS_CTRL_REG0,
- SPEAR1310_SSP1_CLK_SHIFT, SPEAR1310_SSP1_CLK_MASK, 0,
- &_lock);
+ ARRAY_SIZE(ssp1_parents), CLK_SET_RATE_NO_REPARENT,
+ SPEAR1310_RAS_CTRL_REG0, SPEAR1310_SSP1_CLK_SHIFT,
+ SPEAR1310_SSP1_CLK_MASK, 0, &_lock);
clk_register_clkdev(clk, "ssp1_mclk", NULL);

clk = clk_register_gate(NULL, "ssp1_clk", "ssp1_mclk", 0,
@@ -1087,9 +1094,9 @@ void __init spear1310_clk_init(void __iomem *misc_base, void __iomem *ras_base)
clk_register_clkdev(clk, NULL, "5d400000.spi");

clk = clk_register_mux(NULL, "pci_mclk", pci_parents,
- ARRAY_SIZE(pci_parents), 0, SPEAR1310_RAS_CTRL_REG0,
- SPEAR1310_PCI_CLK_SHIFT, SPEAR1310_PCI_CLK_MASK, 0,
- &_lock);
+ ARRAY_SIZE(pci_parents), CLK_SET_RATE_NO_REPARENT,
+ SPEAR1310_RAS_CTRL_REG0, SPEAR1310_PCI_CLK_SHIFT,
+ SPEAR1310_PCI_CLK_MASK, 0, &_lock);
clk_register_clkdev(clk, "pci_mclk", NULL);

clk = clk_register_gate(NULL, "pci_clk", "pci_mclk", 0,
@@ -1098,9 +1105,9 @@ void __init spear1310_clk_init(void __iomem *misc_base, void __iomem *ras_base)
clk_register_clkdev(clk, NULL, "pci");

clk = clk_register_mux(NULL, "tdm1_mclk", tdm_parents,
- ARRAY_SIZE(tdm_parents), 0, SPEAR1310_RAS_CTRL_REG0,
- SPEAR1310_TDM1_CLK_SHIFT, SPEAR1310_TDM_CLK_MASK, 0,
- &_lock);
+ ARRAY_SIZE(tdm_parents), CLK_SET_RATE_NO_REPARENT,
+ SPEAR1310_RAS_CTRL_REG0, SPEAR1310_TDM1_CLK_SHIFT,
+ SPEAR1310_TDM_CLK_MASK, 0, &_lock);
clk_register_clkdev(clk, "tdm1_mclk", NULL);

clk = clk_register_gate(NULL, "tdm1_clk", "tdm1_mclk", 0,
@@ -1109,9 +1116,9 @@ void __init spear1310_clk_init(void __iomem *misc_base, void __iomem *ras_base)
clk_register_clkdev(clk, NULL, "tdm_hdlc.0");

clk = clk_register_mux(NULL, "tdm2_mclk", tdm_parents,
- ARRAY_SIZE(tdm_parents), 0, SPEAR1310_RAS_CTRL_REG0,
- SPEAR1310_TDM2_CLK_SHIFT, SPEAR1310_TDM_CLK_MASK, 0,
- &_lock);
+ ARRAY_SIZE(tdm_parents), CLK_SET_RATE_NO_REPARENT,
+ SPEAR1310_RAS_CTRL_REG0, SPEAR1310_TDM2_CLK_SHIFT,
+ SPEAR1310_TDM_CLK_MASK, 0, &_lock);
clk_register_clkdev(clk, "tdm2_mclk", NULL);

clk = clk_register_gate(NULL, "tdm2_clk", "tdm2_mclk", 0,
diff --git a/drivers/clk/spear/spear1340_clock.c b/drivers/clk/spear/spear1340_clock.c
index 9d0b394..fe835c1 100644
--- a/drivers/clk/spear/spear1340_clock.c
+++ b/drivers/clk/spear/spear1340_clock.c
@@ -473,9 +473,9 @@ void __init spear1340_clk_init(void __iomem *misc_base)
/* clock derived from 24 or 25 MHz osc clk */
/* vco-pll */
clk = clk_register_mux(NULL, "vco1_mclk", vco_parents,
- ARRAY_SIZE(vco_parents), 0, SPEAR1340_PLL_CFG,
- SPEAR1340_PLL1_CLK_SHIFT, SPEAR1340_PLL_CLK_MASK, 0,
- &_lock);
+ ARRAY_SIZE(vco_parents), CLK_SET_RATE_NO_REPARENT,
+ SPEAR1340_PLL_CFG, SPEAR1340_PLL1_CLK_SHIFT,
+ SPEAR1340_PLL_CLK_MASK, 0, &_lock);
clk_register_clkdev(clk, "vco1_mclk", NULL);
clk = clk_register_vco_pll("vco1_clk", "pll1_clk", NULL, "vco1_mclk", 0,
SPEAR1340_PLL1_CTR, SPEAR1340_PLL1_FRQ, pll_rtbl,
@@ -484,9 +484,9 @@ void __init spear1340_clk_init(void __iomem *misc_base)
clk_register_clkdev(clk1, "pll1_clk", NULL);

clk = clk_register_mux(NULL, "vco2_mclk", vco_parents,
- ARRAY_SIZE(vco_parents), 0, SPEAR1340_PLL_CFG,
- SPEAR1340_PLL2_CLK_SHIFT, SPEAR1340_PLL_CLK_MASK, 0,
- &_lock);
+ ARRAY_SIZE(vco_parents), CLK_SET_RATE_NO_REPARENT,
+ SPEAR1340_PLL_CFG, SPEAR1340_PLL2_CLK_SHIFT,
+ SPEAR1340_PLL_CLK_MASK, 0, &_lock);
clk_register_clkdev(clk, "vco2_mclk", NULL);
clk = clk_register_vco_pll("vco2_clk", "pll2_clk", NULL, "vco2_mclk", 0,
SPEAR1340_PLL2_CTR, SPEAR1340_PLL2_FRQ, pll_rtbl,
@@ -495,9 +495,9 @@ void __init spear1340_clk_init(void __iomem *misc_base)
clk_register_clkdev(clk1, "pll2_clk", NULL);

clk = clk_register_mux(NULL, "vco3_mclk", vco_parents,
- ARRAY_SIZE(vco_parents), 0, SPEAR1340_PLL_CFG,
- SPEAR1340_PLL3_CLK_SHIFT, SPEAR1340_PLL_CLK_MASK, 0,
- &_lock);
+ ARRAY_SIZE(vco_parents), CLK_SET_RATE_NO_REPARENT,
+ SPEAR1340_PLL_CFG, SPEAR1340_PLL3_CLK_SHIFT,
+ SPEAR1340_PLL_CLK_MASK, 0, &_lock);
clk_register_clkdev(clk, "vco3_mclk", NULL);
clk = clk_register_vco_pll("vco3_clk", "pll3_clk", NULL, "vco3_mclk", 0,
SPEAR1340_PLL3_CTR, SPEAR1340_PLL3_FRQ, pll_rtbl,
@@ -561,8 +561,8 @@ void __init spear1340_clk_init(void __iomem *misc_base)
clk_register_clkdev(clk, "amba_syn_clk", NULL);

clk = clk_register_mux(NULL, "sys_mclk", sys_parents,
- ARRAY_SIZE(sys_parents), 0, SPEAR1340_SYS_CLK_CTRL,
- SPEAR1340_SCLK_SRC_SEL_SHIFT,
+ ARRAY_SIZE(sys_parents), CLK_SET_RATE_NO_REPARENT,
+ SPEAR1340_SYS_CLK_CTRL, SPEAR1340_SCLK_SRC_SEL_SHIFT,
SPEAR1340_SCLK_SRC_SEL_MASK, 0, &_lock);
clk_register_clkdev(clk, "sys_mclk", NULL);

@@ -583,8 +583,8 @@ void __init spear1340_clk_init(void __iomem *misc_base)
clk_register_clkdev(clk, NULL, "smp_twd");

clk = clk_register_mux(NULL, "ahb_clk", ahb_parents,
- ARRAY_SIZE(ahb_parents), 0, SPEAR1340_SYS_CLK_CTRL,
- SPEAR1340_HCLK_SRC_SEL_SHIFT,
+ ARRAY_SIZE(ahb_parents), CLK_SET_RATE_NO_REPARENT,
+ SPEAR1340_SYS_CLK_CTRL, SPEAR1340_HCLK_SRC_SEL_SHIFT,
SPEAR1340_HCLK_SRC_SEL_MASK, 0, &_lock);
clk_register_clkdev(clk, "ahb_clk", NULL);

@@ -594,9 +594,9 @@ void __init spear1340_clk_init(void __iomem *misc_base)

/* gpt clocks */
clk = clk_register_mux(NULL, "gpt0_mclk", gpt_parents,
- ARRAY_SIZE(gpt_parents), 0, SPEAR1340_PERIP_CLK_CFG,
- SPEAR1340_GPT0_CLK_SHIFT, SPEAR1340_GPT_CLK_MASK, 0,
- &_lock);
+ ARRAY_SIZE(gpt_parents), CLK_SET_RATE_NO_REPARENT,
+ SPEAR1340_PERIP_CLK_CFG, SPEAR1340_GPT0_CLK_SHIFT,
+ SPEAR1340_GPT_CLK_MASK, 0, &_lock);
clk_register_clkdev(clk, "gpt0_mclk", NULL);
clk = clk_register_gate(NULL, "gpt0_clk", "gpt0_mclk", 0,
SPEAR1340_PERIP1_CLK_ENB, SPEAR1340_GPT0_CLK_ENB, 0,
@@ -604,9 +604,9 @@ void __init spear1340_clk_init(void __iomem *misc_base)
clk_register_clkdev(clk, NULL, "gpt0");

clk = clk_register_mux(NULL, "gpt1_mclk", gpt_parents,
- ARRAY_SIZE(gpt_parents), 0, SPEAR1340_PERIP_CLK_CFG,
- SPEAR1340_GPT1_CLK_SHIFT, SPEAR1340_GPT_CLK_MASK, 0,
- &_lock);
+ ARRAY_SIZE(gpt_parents), CLK_SET_RATE_NO_REPARENT,
+ SPEAR1340_PERIP_CLK_CFG, SPEAR1340_GPT1_CLK_SHIFT,
+ SPEAR1340_GPT_CLK_MASK, 0, &_lock);
clk_register_clkdev(clk, "gpt1_mclk", NULL);
clk = clk_register_gate(NULL, "gpt1_clk", "gpt1_mclk", 0,
SPEAR1340_PERIP1_CLK_ENB, SPEAR1340_GPT1_CLK_ENB, 0,
@@ -614,9 +614,9 @@ void __init spear1340_clk_init(void __iomem *misc_base)
clk_register_clkdev(clk, NULL, "gpt1");

clk = clk_register_mux(NULL, "gpt2_mclk", gpt_parents,
- ARRAY_SIZE(gpt_parents), 0, SPEAR1340_PERIP_CLK_CFG,
- SPEAR1340_GPT2_CLK_SHIFT, SPEAR1340_GPT_CLK_MASK, 0,
- &_lock);
+ ARRAY_SIZE(gpt_parents), CLK_SET_RATE_NO_REPARENT,
+ SPEAR1340_PERIP_CLK_CFG, SPEAR1340_GPT2_CLK_SHIFT,
+ SPEAR1340_GPT_CLK_MASK, 0, &_lock);
clk_register_clkdev(clk, "gpt2_mclk", NULL);
clk = clk_register_gate(NULL, "gpt2_clk", "gpt2_mclk", 0,
SPEAR1340_PERIP2_CLK_ENB, SPEAR1340_GPT2_CLK_ENB, 0,
@@ -624,9 +624,9 @@ void __init spear1340_clk_init(void __iomem *misc_base)
clk_register_clkdev(clk, NULL, "gpt2");

clk = clk_register_mux(NULL, "gpt3_mclk", gpt_parents,
- ARRAY_SIZE(gpt_parents), 0, SPEAR1340_PERIP_CLK_CFG,
- SPEAR1340_GPT3_CLK_SHIFT, SPEAR1340_GPT_CLK_MASK, 0,
- &_lock);
+ ARRAY_SIZE(gpt_parents), CLK_SET_RATE_NO_REPARENT,
+ SPEAR1340_PERIP_CLK_CFG, SPEAR1340_GPT3_CLK_SHIFT,
+ SPEAR1340_GPT_CLK_MASK, 0, &_lock);
clk_register_clkdev(clk, "gpt3_mclk", NULL);
clk = clk_register_gate(NULL, "gpt3_clk", "gpt3_mclk", 0,
SPEAR1340_PERIP2_CLK_ENB, SPEAR1340_GPT3_CLK_ENB, 0,
@@ -641,7 +641,8 @@ void __init spear1340_clk_init(void __iomem *misc_base)
clk_register_clkdev(clk1, "uart0_syn_gclk", NULL);

clk = clk_register_mux(NULL, "uart0_mclk", uart0_parents,
- ARRAY_SIZE(uart0_parents), CLK_SET_RATE_PARENT,
+ ARRAY_SIZE(uart0_parents),
+ CLK_SET_RATE_PARENT | CLK_SET_RATE_NO_REPARENT,
SPEAR1340_PERIP_CLK_CFG, SPEAR1340_UART0_CLK_SHIFT,
SPEAR1340_UART_CLK_MASK, 0, &_lock);
clk_register_clkdev(clk, "uart0_mclk", NULL);
@@ -658,9 +659,9 @@ void __init spear1340_clk_init(void __iomem *misc_base)
clk_register_clkdev(clk1, "uart1_syn_gclk", NULL);

clk = clk_register_mux(NULL, "uart1_mclk", uart1_parents,
- ARRAY_SIZE(uart1_parents), 0, SPEAR1340_PERIP_CLK_CFG,
- SPEAR1340_UART1_CLK_SHIFT, SPEAR1340_UART_CLK_MASK, 0,
- &_lock);
+ ARRAY_SIZE(uart1_parents), CLK_SET_RATE_NO_REPARENT,
+ SPEAR1340_PERIP_CLK_CFG, SPEAR1340_UART1_CLK_SHIFT,
+ SPEAR1340_UART_CLK_MASK, 0, &_lock);
clk_register_clkdev(clk, "uart1_mclk", NULL);

clk = clk_register_gate(NULL, "uart1_clk", "uart1_mclk", 0,
@@ -698,7 +699,8 @@ void __init spear1340_clk_init(void __iomem *misc_base)
clk_register_clkdev(clk1, "c3_syn_gclk", NULL);

clk = clk_register_mux(NULL, "c3_mclk", c3_parents,
- ARRAY_SIZE(c3_parents), CLK_SET_RATE_PARENT,
+ ARRAY_SIZE(c3_parents),
+ CLK_SET_RATE_PARENT | CLK_SET_RATE_NO_REPARENT,
SPEAR1340_PERIP_CLK_CFG, SPEAR1340_C3_CLK_SHIFT,
SPEAR1340_C3_CLK_MASK, 0, &_lock);
clk_register_clkdev(clk, "c3_mclk", NULL);
@@ -710,8 +712,8 @@ void __init spear1340_clk_init(void __iomem *misc_base)

/* gmac */
clk = clk_register_mux(NULL, "phy_input_mclk", gmac_phy_input_parents,
- ARRAY_SIZE(gmac_phy_input_parents), 0,
- SPEAR1340_GMAC_CLK_CFG,
+ ARRAY_SIZE(gmac_phy_input_parents),
+ CLK_SET_RATE_NO_REPARENT, SPEAR1340_GMAC_CLK_CFG,
SPEAR1340_GMAC_PHY_INPUT_CLK_SHIFT,
SPEAR1340_GMAC_PHY_INPUT_CLK_MASK, 0, &_lock);
clk_register_clkdev(clk, "phy_input_mclk", NULL);
@@ -723,15 +725,16 @@ void __init spear1340_clk_init(void __iomem *misc_base)
clk_register_clkdev(clk1, "phy_syn_gclk", NULL);

clk = clk_register_mux(NULL, "phy_mclk", gmac_phy_parents,
- ARRAY_SIZE(gmac_phy_parents), 0,
+ ARRAY_SIZE(gmac_phy_parents), CLK_SET_RATE_NO_REPARENT,
SPEAR1340_PERIP_CLK_CFG, SPEAR1340_GMAC_PHY_CLK_SHIFT,
SPEAR1340_GMAC_PHY_CLK_MASK, 0, &_lock);
clk_register_clkdev(clk, "stmmacphy.0", NULL);

/* clcd */
clk = clk_register_mux(NULL, "clcd_syn_mclk", clcd_synth_parents,
- ARRAY_SIZE(clcd_synth_parents), 0,
- SPEAR1340_CLCD_CLK_SYNT, SPEAR1340_CLCD_SYNT_CLK_SHIFT,
+ ARRAY_SIZE(clcd_synth_parents),
+ CLK_SET_RATE_NO_REPARENT, SPEAR1340_CLCD_CLK_SYNT,
+ SPEAR1340_CLCD_SYNT_CLK_SHIFT,
SPEAR1340_CLCD_SYNT_CLK_MASK, 0, &_lock);
clk_register_clkdev(clk, "clcd_syn_mclk", NULL);

@@ -741,7 +744,8 @@ void __init spear1340_clk_init(void __iomem *misc_base)
clk_register_clkdev(clk, "clcd_syn_clk", NULL);

clk = clk_register_mux(NULL, "clcd_pixel_mclk", clcd_pixel_parents,
- ARRAY_SIZE(clcd_pixel_parents), CLK_SET_RATE_PARENT,
+ ARRAY_SIZE(clcd_pixel_parents),
+ CLK_SET_RATE_PARENT | CLK_SET_RATE_NO_REPARENT,
SPEAR1340_PERIP_CLK_CFG, SPEAR1340_CLCD_CLK_SHIFT,
SPEAR1340_CLCD_CLK_MASK, 0, &_lock);
clk_register_clkdev(clk, "clcd_pixel_mclk", NULL);
@@ -753,9 +757,9 @@ void __init spear1340_clk_init(void __iomem *misc_base)

/* i2s */
clk = clk_register_mux(NULL, "i2s_src_mclk", i2s_src_parents,
- ARRAY_SIZE(i2s_src_parents), 0, SPEAR1340_I2S_CLK_CFG,
- SPEAR1340_I2S_SRC_CLK_SHIFT, SPEAR1340_I2S_SRC_CLK_MASK,
- 0, &_lock);
+ ARRAY_SIZE(i2s_src_parents), CLK_SET_RATE_NO_REPARENT,
+ SPEAR1340_I2S_CLK_CFG, SPEAR1340_I2S_SRC_CLK_SHIFT,
+ SPEAR1340_I2S_SRC_CLK_MASK, 0, &_lock);
clk_register_clkdev(clk, "i2s_src_mclk", NULL);

clk = clk_register_aux("i2s_prs1_clk", NULL, "i2s_src_mclk",
@@ -765,7 +769,8 @@ void __init spear1340_clk_init(void __iomem *misc_base)
clk_register_clkdev(clk, "i2s_prs1_clk", NULL);

clk = clk_register_mux(NULL, "i2s_ref_mclk", i2s_ref_parents,
- ARRAY_SIZE(i2s_ref_parents), CLK_SET_RATE_PARENT,
+ ARRAY_SIZE(i2s_ref_parents),
+ CLK_SET_RATE_PARENT | CLK_SET_RATE_NO_REPARENT,
SPEAR1340_I2S_CLK_CFG, SPEAR1340_I2S_REF_SHIFT,
SPEAR1340_I2S_REF_SEL_MASK, 0, &_lock);
clk_register_clkdev(clk, "i2s_ref_mclk", NULL);
@@ -891,13 +896,15 @@ void __init spear1340_clk_init(void __iomem *misc_base)

/* RAS clks */
clk = clk_register_mux(NULL, "gen_syn0_1_mclk", gen_synth0_1_parents,
- ARRAY_SIZE(gen_synth0_1_parents), 0, SPEAR1340_PLL_CFG,
+ ARRAY_SIZE(gen_synth0_1_parents),
+ CLK_SET_RATE_NO_REPARENT, SPEAR1340_PLL_CFG,
SPEAR1340_GEN_SYNT0_1_CLK_SHIFT,
SPEAR1340_GEN_SYNT_CLK_MASK, 0, &_lock);
clk_register_clkdev(clk, "gen_syn0_1_mclk", NULL);

clk = clk_register_mux(NULL, "gen_syn2_3_mclk", gen_synth2_3_parents,
- ARRAY_SIZE(gen_synth2_3_parents), 0, SPEAR1340_PLL_CFG,
+ ARRAY_SIZE(gen_synth2_3_parents),
+ CLK_SET_RATE_NO_REPARENT, SPEAR1340_PLL_CFG,
SPEAR1340_GEN_SYNT2_3_CLK_SHIFT,
SPEAR1340_GEN_SYNT_CLK_MASK, 0, &_lock);
clk_register_clkdev(clk, "gen_syn2_3_mclk", NULL);
@@ -938,7 +945,8 @@ void __init spear1340_clk_init(void __iomem *misc_base)
clk_register_clkdev(clk, NULL, "spear_cec.1");

clk = clk_register_mux(NULL, "spdif_out_mclk", spdif_out_parents,
- ARRAY_SIZE(spdif_out_parents), CLK_SET_RATE_PARENT,
+ ARRAY_SIZE(spdif_out_parents),
+ CLK_SET_RATE_PARENT | CLK_SET_RATE_NO_REPARENT,
SPEAR1340_PERIP_CLK_CFG, SPEAR1340_SPDIF_OUT_CLK_SHIFT,
SPEAR1340_SPDIF_CLK_MASK, 0, &_lock);
clk_register_clkdev(clk, "spdif_out_mclk", NULL);
@@ -949,7 +957,8 @@ void __init spear1340_clk_init(void __iomem *misc_base)
clk_register_clkdev(clk, NULL, "d0000000.spdif-out");

clk = clk_register_mux(NULL, "spdif_in_mclk", spdif_in_parents,
- ARRAY_SIZE(spdif_in_parents), CLK_SET_RATE_PARENT,
+ ARRAY_SIZE(spdif_in_parents),
+ CLK_SET_RATE_PARENT | CLK_SET_RATE_NO_REPARENT,
SPEAR1340_PERIP_CLK_CFG, SPEAR1340_SPDIF_IN_CLK_SHIFT,
SPEAR1340_SPDIF_CLK_MASK, 0, &_lock);
clk_register_clkdev(clk, "spdif_in_mclk", NULL);
diff --git a/drivers/clk/spear/spear3xx_clock.c b/drivers/clk/spear/spear3xx_clock.c
index f9ec43f..4e3bb9e 100644
--- a/drivers/clk/spear/spear3xx_clock.c
+++ b/drivers/clk/spear/spear3xx_clock.c
@@ -294,7 +294,8 @@ static void __init spear320_clk_init(void __iomem *soc_config_base)
clk_register_clkdev(clk, NULL, "a9400000.i2s");

clk = clk_register_mux(NULL, "i2s_ref_clk", i2s_ref_parents,
- ARRAY_SIZE(i2s_ref_parents), CLK_SET_RATE_PARENT,
+ ARRAY_SIZE(i2s_ref_parents),
+ CLK_SET_RATE_PARENT | CLK_SET_RATE_NO_REPARENT,
SPEAR320_CONTROL_REG, I2S_REF_PCLK_SHIFT,
I2S_REF_PCLK_MASK, 0, &_lock);
clk_register_clkdev(clk, "i2s_ref_clk", NULL);
@@ -313,57 +314,66 @@ static void __init spear320_clk_init(void __iomem *soc_config_base)
clk_register_clkdev(clk, "hclk", "ab000000.eth");

clk = clk_register_mux(NULL, "rs485_clk", uartx_parents,
- ARRAY_SIZE(uartx_parents), CLK_SET_RATE_PARENT,
+ ARRAY_SIZE(uartx_parents),
+ CLK_SET_RATE_PARENT | CLK_SET_RATE_NO_REPARENT,
SPEAR320_EXT_CTRL_REG, SPEAR320_RS485_PCLK_SHIFT,
SPEAR320_UARTX_PCLK_MASK, 0, &_lock);
clk_register_clkdev(clk, NULL, "a9300000.serial");

clk = clk_register_mux(NULL, "sdhci_clk", sdhci_parents,
- ARRAY_SIZE(sdhci_parents), CLK_SET_RATE_PARENT,
+ ARRAY_SIZE(sdhci_parents),
+ CLK_SET_RATE_PARENT | CLK_SET_RATE_NO_REPARENT,
SPEAR320_CONTROL_REG, SDHCI_PCLK_SHIFT, SDHCI_PCLK_MASK,
0, &_lock);
clk_register_clkdev(clk, NULL, "70000000.sdhci");

clk = clk_register_mux(NULL, "smii_pclk", smii0_parents,
- ARRAY_SIZE(smii0_parents), 0, SPEAR320_CONTROL_REG,
- SMII_PCLK_SHIFT, SMII_PCLK_MASK, 0, &_lock);
+ ARRAY_SIZE(smii0_parents), CLK_SET_RATE_NO_REPARENT,
+ SPEAR320_CONTROL_REG, SMII_PCLK_SHIFT, SMII_PCLK_MASK,
+ 0, &_lock);
clk_register_clkdev(clk, NULL, "smii_pclk");

clk = clk_register_fixed_factor(NULL, "smii_clk", "smii_pclk", 0, 1, 1);
clk_register_clkdev(clk, NULL, "smii");

clk = clk_register_mux(NULL, "uart1_clk", uartx_parents,
- ARRAY_SIZE(uartx_parents), CLK_SET_RATE_PARENT,
+ ARRAY_SIZE(uartx_parents),
+ CLK_SET_RATE_PARENT | CLK_SET_RATE_NO_REPARENT,
SPEAR320_CONTROL_REG, UART1_PCLK_SHIFT, UART1_PCLK_MASK,
0, &_lock);
clk_register_clkdev(clk, NULL, "a3000000.serial");

clk = clk_register_mux(NULL, "uart2_clk", uartx_parents,
- ARRAY_SIZE(uartx_parents), CLK_SET_RATE_PARENT,
+ ARRAY_SIZE(uartx_parents),
+ CLK_SET_RATE_PARENT | CLK_SET_RATE_NO_REPARENT,
SPEAR320_EXT_CTRL_REG, SPEAR320_UART2_PCLK_SHIFT,
SPEAR320_UARTX_PCLK_MASK, 0, &_lock);
clk_register_clkdev(clk, NULL, "a4000000.serial");

clk = clk_register_mux(NULL, "uart3_clk", uartx_parents,
- ARRAY_SIZE(uartx_parents), CLK_SET_RATE_PARENT,
+ ARRAY_SIZE(uartx_parents),
+ CLK_SET_RATE_PARENT | CLK_SET_RATE_NO_REPARENT,
SPEAR320_EXT_CTRL_REG, SPEAR320_UART3_PCLK_SHIFT,
SPEAR320_UARTX_PCLK_MASK, 0, &_lock);
clk_register_clkdev(clk, NULL, "a9100000.serial");

clk = clk_register_mux(NULL, "uart4_clk", uartx_parents,
- ARRAY_SIZE(uartx_parents), CLK_SET_RATE_PARENT,
+ ARRAY_SIZE(uartx_parents),
+ CLK_SET_RATE_PARENT | CLK_SET_RATE_NO_REPARENT,
SPEAR320_EXT_CTRL_REG, SPEAR320_UART4_PCLK_SHIFT,
SPEAR320_UARTX_PCLK_MASK, 0, &_lock);
clk_register_clkdev(clk, NULL, "a9200000.serial");

clk = clk_register_mux(NULL, "uart5_clk", uartx_parents,
- ARRAY_SIZE(uartx_parents), CLK_SET_RATE_PARENT,
+ ARRAY_SIZE(uartx_parents),
+ CLK_SET_RATE_PARENT | CLK_SET_RATE_NO_REPARENT,
SPEAR320_EXT_CTRL_REG, SPEAR320_UART5_PCLK_SHIFT,
SPEAR320_UARTX_PCLK_MASK, 0, &_lock);
clk_register_clkdev(clk, NULL, "60000000.serial");

clk = clk_register_mux(NULL, "uart6_clk", uartx_parents,
- ARRAY_SIZE(uartx_parents), CLK_SET_RATE_PARENT,
+ ARRAY_SIZE(uartx_parents),
+ CLK_SET_RATE_PARENT | CLK_SET_RATE_NO_REPARENT,
SPEAR320_EXT_CTRL_REG, SPEAR320_UART6_PCLK_SHIFT,
SPEAR320_UARTX_PCLK_MASK, 0, &_lock);
clk_register_clkdev(clk, NULL, "60100000.serial");
@@ -427,7 +437,8 @@ void __init spear3xx_clk_init(void __iomem *misc_base, void __iomem *soc_config_
clk_register_clkdev(clk1, "uart_syn_gclk", NULL);

clk = clk_register_mux(NULL, "uart0_mclk", uart0_parents,
- ARRAY_SIZE(uart0_parents), CLK_SET_RATE_PARENT,
+ ARRAY_SIZE(uart0_parents),
+ CLK_SET_RATE_PARENT | CLK_SET_RATE_NO_REPARENT,
PERIP_CLK_CFG, UART_CLK_SHIFT, UART_CLK_MASK, 0,
&_lock);
clk_register_clkdev(clk, "uart0_mclk", NULL);
@@ -444,7 +455,8 @@ void __init spear3xx_clk_init(void __iomem *misc_base, void __iomem *soc_config_
clk_register_clkdev(clk1, "firda_syn_gclk", NULL);

clk = clk_register_mux(NULL, "firda_mclk", firda_parents,
- ARRAY_SIZE(firda_parents), CLK_SET_RATE_PARENT,
+ ARRAY_SIZE(firda_parents),
+ CLK_SET_RATE_PARENT | CLK_SET_RATE_NO_REPARENT,
PERIP_CLK_CFG, FIRDA_CLK_SHIFT, FIRDA_CLK_MASK, 0,
&_lock);
clk_register_clkdev(clk, "firda_mclk", NULL);
@@ -458,14 +470,16 @@ void __init spear3xx_clk_init(void __iomem *misc_base, void __iomem *soc_config_
clk_register_gpt("gpt0_syn_clk", "pll1_clk", 0, PRSC0_CLK_CFG, gpt_rtbl,
ARRAY_SIZE(gpt_rtbl), &_lock);
clk = clk_register_mux(NULL, "gpt0_clk", gpt0_parents,
- ARRAY_SIZE(gpt0_parents), CLK_SET_RATE_PARENT,
+ ARRAY_SIZE(gpt0_parents),
+ CLK_SET_RATE_PARENT | CLK_SET_RATE_NO_REPARENT,
PERIP_CLK_CFG, GPT0_CLK_SHIFT, GPT_CLK_MASK, 0, &_lock);
clk_register_clkdev(clk, NULL, "gpt0");

clk_register_gpt("gpt1_syn_clk", "pll1_clk", 0, PRSC1_CLK_CFG, gpt_rtbl,
ARRAY_SIZE(gpt_rtbl), &_lock);
clk = clk_register_mux(NULL, "gpt1_mclk", gpt1_parents,
- ARRAY_SIZE(gpt1_parents), CLK_SET_RATE_PARENT,
+ ARRAY_SIZE(gpt1_parents),
+ CLK_SET_RATE_PARENT | CLK_SET_RATE_NO_REPARENT,
PERIP_CLK_CFG, GPT1_CLK_SHIFT, GPT_CLK_MASK, 0, &_lock);
clk_register_clkdev(clk, "gpt1_mclk", NULL);
clk = clk_register_gate(NULL, "gpt1_clk", "gpt1_mclk",
@@ -476,7 +490,8 @@ void __init spear3xx_clk_init(void __iomem *misc_base, void __iomem *soc_config_
clk_register_gpt("gpt2_syn_clk", "pll1_clk", 0, PRSC2_CLK_CFG, gpt_rtbl,
ARRAY_SIZE(gpt_rtbl), &_lock);
clk = clk_register_mux(NULL, "gpt2_mclk", gpt2_parents,
- ARRAY_SIZE(gpt2_parents), CLK_SET_RATE_PARENT,
+ ARRAY_SIZE(gpt2_parents),
+ CLK_SET_RATE_PARENT | CLK_SET_RATE_NO_REPARENT,
PERIP_CLK_CFG, GPT2_CLK_SHIFT, GPT_CLK_MASK, 0, &_lock);
clk_register_clkdev(clk, "gpt2_mclk", NULL);
clk = clk_register_gate(NULL, "gpt2_clk", "gpt2_mclk",
@@ -498,9 +513,9 @@ void __init spear3xx_clk_init(void __iomem *misc_base, void __iomem *soc_config_
clk_register_clkdev(clk1, "gen1_syn_gclk", NULL);

clk = clk_register_mux(NULL, "gen2_3_par_clk", gen2_3_parents,
- ARRAY_SIZE(gen2_3_parents), 0, CORE_CLK_CFG,
- GEN_SYNTH2_3_CLK_SHIFT, GEN_SYNTH2_3_CLK_MASK, 0,
- &_lock);
+ ARRAY_SIZE(gen2_3_parents), CLK_SET_RATE_NO_REPARENT,
+ CORE_CLK_CFG, GEN_SYNTH2_3_CLK_SHIFT,
+ GEN_SYNTH2_3_CLK_MASK, 0, &_lock);
clk_register_clkdev(clk, "gen2_3_par_clk", NULL);

clk = clk_register_aux("gen2_syn_clk", "gen2_syn_gclk",
@@ -540,8 +555,8 @@ void __init spear3xx_clk_init(void __iomem *misc_base, void __iomem *soc_config_
clk_register_clkdev(clk, "ahbmult2_clk", NULL);

clk = clk_register_mux(NULL, "ddr_clk", ddr_parents,
- ARRAY_SIZE(ddr_parents), 0, PLL_CLK_CFG, MCTR_CLK_SHIFT,
- MCTR_CLK_MASK, 0, &_lock);
+ ARRAY_SIZE(ddr_parents), CLK_SET_RATE_NO_REPARENT,
+ PLL_CLK_CFG, MCTR_CLK_SHIFT, MCTR_CLK_MASK, 0, &_lock);
clk_register_clkdev(clk, "ddr_clk", NULL);

clk = clk_register_divider(NULL, "apb_clk", "ahb_clk",
diff --git a/drivers/clk/spear/spear6xx_clock.c b/drivers/clk/spear/spear6xx_clock.c
index 9406f24..4f649c9 100644
--- a/drivers/clk/spear/spear6xx_clock.c
+++ b/drivers/clk/spear/spear6xx_clock.c
@@ -169,8 +169,9 @@ void __init spear6xx_clk_init(void __iomem *misc_base)
clk_register_clkdev(clk1, "uart_syn_gclk", NULL);

clk = clk_register_mux(NULL, "uart_mclk", uart_parents,
- ARRAY_SIZE(uart_parents), 0, PERIP_CLK_CFG,
- UART_CLK_SHIFT, UART_CLK_MASK, 0, &_lock);
+ ARRAY_SIZE(uart_parents), CLK_SET_RATE_NO_REPARENT,
+ PERIP_CLK_CFG, UART_CLK_SHIFT, UART_CLK_MASK, 0,
+ &_lock);
clk_register_clkdev(clk, "uart_mclk", NULL);

clk = clk_register_gate(NULL, "uart0", "uart_mclk", 0, PERIP1_CLK_ENB,
@@ -188,8 +189,9 @@ void __init spear6xx_clk_init(void __iomem *misc_base)
clk_register_clkdev(clk1, "firda_syn_gclk", NULL);

clk = clk_register_mux(NULL, "firda_mclk", firda_parents,
- ARRAY_SIZE(firda_parents), 0, PERIP_CLK_CFG,
- FIRDA_CLK_SHIFT, FIRDA_CLK_MASK, 0, &_lock);
+ ARRAY_SIZE(firda_parents), CLK_SET_RATE_NO_REPARENT,
+ PERIP_CLK_CFG, FIRDA_CLK_SHIFT, FIRDA_CLK_MASK, 0,
+ &_lock);
clk_register_clkdev(clk, "firda_mclk", NULL);

clk = clk_register_gate(NULL, "firda_clk", "firda_mclk", 0,
@@ -203,8 +205,9 @@ void __init spear6xx_clk_init(void __iomem *misc_base)
clk_register_clkdev(clk1, "clcd_syn_gclk", NULL);

clk = clk_register_mux(NULL, "clcd_mclk", clcd_parents,
- ARRAY_SIZE(clcd_parents), 0, PERIP_CLK_CFG,
- CLCD_CLK_SHIFT, CLCD_CLK_MASK, 0, &_lock);
+ ARRAY_SIZE(clcd_parents), CLK_SET_RATE_NO_REPARENT,
+ PERIP_CLK_CFG, CLCD_CLK_SHIFT, CLCD_CLK_MASK, 0,
+ &_lock);
clk_register_clkdev(clk, "clcd_mclk", NULL);

clk = clk_register_gate(NULL, "clcd_clk", "clcd_mclk", 0,
@@ -217,13 +220,13 @@ void __init spear6xx_clk_init(void __iomem *misc_base)
clk_register_clkdev(clk, "gpt0_1_syn_clk", NULL);

clk = clk_register_mux(NULL, "gpt0_mclk", gpt0_1_parents,
- ARRAY_SIZE(gpt0_1_parents), 0, PERIP_CLK_CFG,
- GPT0_CLK_SHIFT, GPT_CLK_MASK, 0, &_lock);
+ ARRAY_SIZE(gpt0_1_parents), CLK_SET_RATE_NO_REPARENT,
+ PERIP_CLK_CFG, GPT0_CLK_SHIFT, GPT_CLK_MASK, 0, &_lock);
clk_register_clkdev(clk, NULL, "gpt0");

clk = clk_register_mux(NULL, "gpt1_mclk", gpt0_1_parents,
- ARRAY_SIZE(gpt0_1_parents), 0, PERIP_CLK_CFG,
- GPT1_CLK_SHIFT, GPT_CLK_MASK, 0, &_lock);
+ ARRAY_SIZE(gpt0_1_parents), CLK_SET_RATE_NO_REPARENT,
+ PERIP_CLK_CFG, GPT1_CLK_SHIFT, GPT_CLK_MASK, 0, &_lock);
clk_register_clkdev(clk, "gpt1_mclk", NULL);

clk = clk_register_gate(NULL, "gpt1_clk", "gpt1_mclk", 0,
@@ -235,8 +238,8 @@ void __init spear6xx_clk_init(void __iomem *misc_base)
clk_register_clkdev(clk, "gpt2_syn_clk", NULL);

clk = clk_register_mux(NULL, "gpt2_mclk", gpt2_parents,
- ARRAY_SIZE(gpt2_parents), 0, PERIP_CLK_CFG,
- GPT2_CLK_SHIFT, GPT_CLK_MASK, 0, &_lock);
+ ARRAY_SIZE(gpt2_parents), CLK_SET_RATE_NO_REPARENT,
+ PERIP_CLK_CFG, GPT2_CLK_SHIFT, GPT_CLK_MASK, 0, &_lock);
clk_register_clkdev(clk, "gpt2_mclk", NULL);

clk = clk_register_gate(NULL, "gpt2_clk", "gpt2_mclk", 0,
@@ -248,8 +251,8 @@ void __init spear6xx_clk_init(void __iomem *misc_base)
clk_register_clkdev(clk, "gpt3_syn_clk", NULL);

clk = clk_register_mux(NULL, "gpt3_mclk", gpt3_parents,
- ARRAY_SIZE(gpt3_parents), 0, PERIP_CLK_CFG,
- GPT3_CLK_SHIFT, GPT_CLK_MASK, 0, &_lock);
+ ARRAY_SIZE(gpt3_parents), CLK_SET_RATE_NO_REPARENT,
+ PERIP_CLK_CFG, GPT3_CLK_SHIFT, GPT_CLK_MASK, 0, &_lock);
clk_register_clkdev(clk, "gpt3_mclk", NULL);

clk = clk_register_gate(NULL, "gpt3_clk", "gpt3_mclk", 0,
@@ -277,8 +280,8 @@ void __init spear6xx_clk_init(void __iomem *misc_base)
clk_register_clkdev(clk, "ahbmult2_clk", NULL);

clk = clk_register_mux(NULL, "ddr_clk", ddr_parents,
- ARRAY_SIZE(ddr_parents), 0, PLL_CLK_CFG, MCTR_CLK_SHIFT,
- MCTR_CLK_MASK, 0, &_lock);
+ ARRAY_SIZE(ddr_parents), CLK_SET_RATE_NO_REPARENT,
+ PLL_CLK_CFG, MCTR_CLK_SHIFT, MCTR_CLK_MASK, 0, &_lock);
clk_register_clkdev(clk, "ddr_clk", NULL);

clk = clk_register_divider(NULL, "apb_clk", "ahb_clk",
diff --git a/drivers/clk/sunxi/clk-sunxi.c b/drivers/clk/sunxi/clk-sunxi.c
index 930d36f..f47df26 100644
--- a/drivers/clk/sunxi/clk-sunxi.c
+++ b/drivers/clk/sunxi/clk-sunxi.c
@@ -261,7 +261,8 @@ static void __init sunxi_mux_clk_setup(struct device_node *node,
while (i < 5 && (parents[i] = of_clk_get_parent_name(node, i)) != NULL)
i++;

- clk = clk_register_mux(NULL, clk_name, parents, i, 0, reg,
+ clk = clk_register_mux(NULL, clk_name, parents, i,
+ CLK_SET_RATE_NO_REPARENT, reg,
data->shift, SUNXI_MUX_GATE_WIDTH,
0, &clk_lock);

diff --git a/drivers/clk/tegra/clk-tegra114.c b/drivers/clk/tegra/clk-tegra114.c
index d78e16e..21aa7bb 100644
--- a/drivers/clk/tegra/clk-tegra114.c
+++ b/drivers/clk/tegra/clk-tegra114.c
@@ -1435,7 +1435,8 @@ static void __init tegra114_audio_clk_init(void __iomem *clk_base)

/* audio0 */
clk = clk_register_mux(NULL, "audio0_mux", mux_audio_sync_clk,
- ARRAY_SIZE(mux_audio_sync_clk), 0,
+ ARRAY_SIZE(mux_audio_sync_clk),
+ CLK_SET_RATE_NO_REPARENT,
clk_base + AUDIO_SYNC_CLK_I2S0, 0, 3, 0,
NULL);
clks[audio0_mux] = clk;
@@ -1447,7 +1448,8 @@ static void __init tegra114_audio_clk_init(void __iomem *clk_base)

/* audio1 */
clk = clk_register_mux(NULL, "audio1_mux", mux_audio_sync_clk,
- ARRAY_SIZE(mux_audio_sync_clk), 0,
+ ARRAY_SIZE(mux_audio_sync_clk),
+ CLK_SET_RATE_NO_REPARENT,
clk_base + AUDIO_SYNC_CLK_I2S1, 0, 3, 0,
NULL);
clks[audio1_mux] = clk;
@@ -1459,7 +1461,8 @@ static void __init tegra114_audio_clk_init(void __iomem *clk_base)

/* audio2 */
clk = clk_register_mux(NULL, "audio2_mux", mux_audio_sync_clk,
- ARRAY_SIZE(mux_audio_sync_clk), 0,
+ ARRAY_SIZE(mux_audio_sync_clk),
+ CLK_SET_RATE_NO_REPARENT,
clk_base + AUDIO_SYNC_CLK_I2S2, 0, 3, 0,
NULL);
clks[audio2_mux] = clk;
@@ -1471,7 +1474,8 @@ static void __init tegra114_audio_clk_init(void __iomem *clk_base)

/* audio3 */
clk = clk_register_mux(NULL, "audio3_mux", mux_audio_sync_clk,
- ARRAY_SIZE(mux_audio_sync_clk), 0,
+ ARRAY_SIZE(mux_audio_sync_clk),
+ CLK_SET_RATE_NO_REPARENT,
clk_base + AUDIO_SYNC_CLK_I2S3, 0, 3, 0,
NULL);
clks[audio3_mux] = clk;
@@ -1483,7 +1487,8 @@ static void __init tegra114_audio_clk_init(void __iomem *clk_base)

/* audio4 */
clk = clk_register_mux(NULL, "audio4_mux", mux_audio_sync_clk,
- ARRAY_SIZE(mux_audio_sync_clk), 0,
+ ARRAY_SIZE(mux_audio_sync_clk),
+ CLK_SET_RATE_NO_REPARENT,
clk_base + AUDIO_SYNC_CLK_I2S4, 0, 3, 0,
NULL);
clks[audio4_mux] = clk;
@@ -1495,7 +1500,8 @@ static void __init tegra114_audio_clk_init(void __iomem *clk_base)

/* spdif */
clk = clk_register_mux(NULL, "spdif_mux", mux_audio_sync_clk,
- ARRAY_SIZE(mux_audio_sync_clk), 0,
+ ARRAY_SIZE(mux_audio_sync_clk),
+ CLK_SET_RATE_NO_REPARENT,
clk_base + AUDIO_SYNC_CLK_SPDIF, 0, 3, 0,
NULL);
clks[spdif_mux] = clk;
@@ -1590,7 +1596,8 @@ static void __init tegra114_pmc_clk_init(void __iomem *pmc_base)

/* clk_out_1 */
clk = clk_register_mux(NULL, "clk_out_1_mux", clk_out1_parents,
- ARRAY_SIZE(clk_out1_parents), 0,
+ ARRAY_SIZE(clk_out1_parents),
+ CLK_SET_RATE_NO_REPARENT,
pmc_base + PMC_CLK_OUT_CNTRL, 6, 3, 0,
&clk_out_lock);
clks[clk_out_1_mux] = clk;
@@ -1602,7 +1609,8 @@ static void __init tegra114_pmc_clk_init(void __iomem *pmc_base)

/* clk_out_2 */
clk = clk_register_mux(NULL, "clk_out_2_mux", clk_out2_parents,
- ARRAY_SIZE(clk_out1_parents), 0,
+ ARRAY_SIZE(clk_out1_parents),
+ CLK_SET_RATE_NO_REPARENT,
pmc_base + PMC_CLK_OUT_CNTRL, 14, 3, 0,
&clk_out_lock);
clks[clk_out_2_mux] = clk;
@@ -1614,7 +1622,8 @@ static void __init tegra114_pmc_clk_init(void __iomem *pmc_base)

/* clk_out_3 */
clk = clk_register_mux(NULL, "clk_out_3_mux", clk_out3_parents,
- ARRAY_SIZE(clk_out1_parents), 0,
+ ARRAY_SIZE(clk_out1_parents),
+ CLK_SET_RATE_NO_REPARENT,
pmc_base + PMC_CLK_OUT_CNTRL, 22, 3, 0,
&clk_out_lock);
clks[clk_out_3_mux] = clk;
@@ -1928,7 +1937,8 @@ static __init void tegra114_periph_clk_init(void __iomem *clk_base)

/* dsia */
clk = clk_register_mux(NULL, "dsia_mux", mux_plld_out0_plld2_out0,
- ARRAY_SIZE(mux_plld_out0_plld2_out0), 0,
+ ARRAY_SIZE(mux_plld_out0_plld2_out0),
+ CLK_SET_RATE_NO_REPARENT,
clk_base + PLLD_BASE, 25, 1, 0, &pll_d_lock);
clks[dsia_mux] = clk;
clk = tegra_clk_register_periph_gate("dsia", "dsia_mux", 0, clk_base,
@@ -1938,7 +1948,8 @@ static __init void tegra114_periph_clk_init(void __iomem *clk_base)

/* dsib */
clk = clk_register_mux(NULL, "dsib_mux", mux_plld_out0_plld2_out0,
- ARRAY_SIZE(mux_plld_out0_plld2_out0), 0,
+ ARRAY_SIZE(mux_plld_out0_plld2_out0),
+ CLK_SET_RATE_NO_REPARENT,
clk_base + PLLD2_BASE, 25, 1, 0, &pll_d2_lock);
clks[dsib_mux] = clk;
clk = tegra_clk_register_periph_gate("dsib", "dsib_mux", 0, clk_base,
@@ -1975,7 +1986,8 @@ static __init void tegra114_periph_clk_init(void __iomem *clk_base)

/* emc */
clk = clk_register_mux(NULL, "emc_mux", mux_pllmcp_clkm,
- ARRAY_SIZE(mux_pllmcp_clkm), 0,
+ ARRAY_SIZE(mux_pllmcp_clkm),
+ CLK_SET_RATE_NO_REPARENT,
clk_base + CLK_SOURCE_EMC,
29, 3, 0, NULL);
clk = tegra_clk_register_periph_gate("emc", "emc_mux", 0, clk_base,
diff --git a/drivers/clk/tegra/clk-tegra20.c b/drivers/clk/tegra/clk-tegra20.c
index 8292a00..48868deb 100644
--- a/drivers/clk/tegra/clk-tegra20.c
+++ b/drivers/clk/tegra/clk-tegra20.c
@@ -778,7 +778,8 @@ static void __init tegra20_audio_clk_init(void)

/* audio */
clk = clk_register_mux(NULL, "audio_mux", audio_parents,
- ARRAY_SIZE(audio_parents), 0,
+ ARRAY_SIZE(audio_parents),
+ CLK_SET_RATE_NO_REPARENT,
clk_base + AUDIO_SYNC_CLK, 0, 3, 0, NULL);
clk = clk_register_gate(NULL, "audio", "audio_mux", 0,
clk_base + AUDIO_SYNC_CLK, 4,
@@ -933,7 +934,8 @@ static void __init tegra20_periph_clk_init(void)

/* emc */
clk = clk_register_mux(NULL, "emc_mux", mux_pllmcp_clkm,
- ARRAY_SIZE(mux_pllmcp_clkm), 0,
+ ARRAY_SIZE(mux_pllmcp_clkm),
+ CLK_SET_RATE_NO_REPARENT,
clk_base + CLK_SOURCE_EMC,
30, 2, 0, NULL);
clk = tegra_clk_register_periph_gate("emc", "emc_mux", 0, clk_base, 0,
diff --git a/drivers/clk/tegra/clk-tegra30.c b/drivers/clk/tegra/clk-tegra30.c
index c6921f5..b4abfa5 100644
--- a/drivers/clk/tegra/clk-tegra30.c
+++ b/drivers/clk/tegra/clk-tegra30.c
@@ -1008,7 +1008,8 @@ static void __init tegra30_pll_init(void)

/* PLLE */
clk = clk_register_mux(NULL, "pll_e_mux", pll_e_parents,
- ARRAY_SIZE(pll_e_parents), 0,
+ ARRAY_SIZE(pll_e_parents),
+ CLK_SET_RATE_NO_REPARENT,
clk_base + PLLE_AUX, 2, 1, 0, NULL);
clk = tegra_clk_register_plle("pll_e", "pll_e_mux", clk_base, pmc_base,
CLK_GET_RATE_NOCACHE, 100000000, &pll_e_params,
@@ -1068,7 +1069,8 @@ static void __init tegra30_audio_clk_init(void)

/* audio0 */
clk = clk_register_mux(NULL, "audio0_mux", mux_audio_sync_clk,
- ARRAY_SIZE(mux_audio_sync_clk), 0,
+ ARRAY_SIZE(mux_audio_sync_clk),
+ CLK_SET_RATE_NO_REPARENT,
clk_base + AUDIO_SYNC_CLK_I2S0, 0, 3, 0, NULL);
clk = clk_register_gate(NULL, "audio0", "audio0_mux", 0,
clk_base + AUDIO_SYNC_CLK_I2S0, 4,
@@ -1078,7 +1080,8 @@ static void __init tegra30_audio_clk_init(void)

/* audio1 */
clk = clk_register_mux(NULL, "audio1_mux", mux_audio_sync_clk,
- ARRAY_SIZE(mux_audio_sync_clk), 0,
+ ARRAY_SIZE(mux_audio_sync_clk),
+ CLK_SET_RATE_NO_REPARENT,
clk_base + AUDIO_SYNC_CLK_I2S1, 0, 3, 0, NULL);
clk = clk_register_gate(NULL, "audio1", "audio1_mux", 0,
clk_base + AUDIO_SYNC_CLK_I2S1, 4,
@@ -1088,7 +1091,8 @@ static void __init tegra30_audio_clk_init(void)

/* audio2 */
clk = clk_register_mux(NULL, "audio2_mux", mux_audio_sync_clk,
- ARRAY_SIZE(mux_audio_sync_clk), 0,
+ ARRAY_SIZE(mux_audio_sync_clk),
+ CLK_SET_RATE_NO_REPARENT,
clk_base + AUDIO_SYNC_CLK_I2S2, 0, 3, 0, NULL);
clk = clk_register_gate(NULL, "audio2", "audio2_mux", 0,
clk_base + AUDIO_SYNC_CLK_I2S2, 4,
@@ -1098,7 +1102,8 @@ static void __init tegra30_audio_clk_init(void)

/* audio3 */
clk = clk_register_mux(NULL, "audio3_mux", mux_audio_sync_clk,
- ARRAY_SIZE(mux_audio_sync_clk), 0,
+ ARRAY_SIZE(mux_audio_sync_clk),
+ CLK_SET_RATE_NO_REPARENT,
clk_base + AUDIO_SYNC_CLK_I2S3, 0, 3, 0, NULL);
clk = clk_register_gate(NULL, "audio3", "audio3_mux", 0,
clk_base + AUDIO_SYNC_CLK_I2S3, 4,
@@ -1108,7 +1113,8 @@ static void __init tegra30_audio_clk_init(void)

/* audio4 */
clk = clk_register_mux(NULL, "audio4_mux", mux_audio_sync_clk,
- ARRAY_SIZE(mux_audio_sync_clk), 0,
+ ARRAY_SIZE(mux_audio_sync_clk),
+ CLK_SET_RATE_NO_REPARENT,
clk_base + AUDIO_SYNC_CLK_I2S4, 0, 3, 0, NULL);
clk = clk_register_gate(NULL, "audio4", "audio4_mux", 0,
clk_base + AUDIO_SYNC_CLK_I2S4, 4,
@@ -1118,7 +1124,8 @@ static void __init tegra30_audio_clk_init(void)

/* spdif */
clk = clk_register_mux(NULL, "spdif_mux", mux_audio_sync_clk,
- ARRAY_SIZE(mux_audio_sync_clk), 0,
+ ARRAY_SIZE(mux_audio_sync_clk),
+ CLK_SET_RATE_NO_REPARENT,
clk_base + AUDIO_SYNC_CLK_SPDIF, 0, 3, 0, NULL);
clk = clk_register_gate(NULL, "spdif", "spdif_mux", 0,
clk_base + AUDIO_SYNC_CLK_SPDIF, 4,
@@ -1211,7 +1218,8 @@ static void __init tegra30_pmc_clk_init(void)

/* clk_out_1 */
clk = clk_register_mux(NULL, "clk_out_1_mux", clk_out1_parents,
- ARRAY_SIZE(clk_out1_parents), 0,
+ ARRAY_SIZE(clk_out1_parents),
+ CLK_SET_RATE_NO_REPARENT,
pmc_base + PMC_CLK_OUT_CNTRL, 6, 3, 0,
&clk_out_lock);
clks[clk_out_1_mux] = clk;
@@ -1223,7 +1231,8 @@ static void __init tegra30_pmc_clk_init(void)

/* clk_out_2 */
clk = clk_register_mux(NULL, "clk_out_2_mux", clk_out2_parents,
- ARRAY_SIZE(clk_out1_parents), 0,
+ ARRAY_SIZE(clk_out1_parents),
+ CLK_SET_RATE_NO_REPARENT,
pmc_base + PMC_CLK_OUT_CNTRL, 14, 3, 0,
&clk_out_lock);
clk = clk_register_gate(NULL, "clk_out_2", "clk_out_2_mux", 0,
@@ -1234,7 +1243,8 @@ static void __init tegra30_pmc_clk_init(void)

/* clk_out_3 */
clk = clk_register_mux(NULL, "clk_out_3_mux", clk_out3_parents,
- ARRAY_SIZE(clk_out1_parents), 0,
+ ARRAY_SIZE(clk_out1_parents),
+ CLK_SET_RATE_NO_REPARENT,
pmc_base + PMC_CLK_OUT_CNTRL, 22, 3, 0,
&clk_out_lock);
clk = clk_register_gate(NULL, "clk_out_3", "clk_out_3_mux", 0,
@@ -1655,7 +1665,8 @@ static void __init tegra30_periph_clk_init(void)

/* emc */
clk = clk_register_mux(NULL, "emc_mux", mux_pllmcp_clkm,
- ARRAY_SIZE(mux_pllmcp_clkm), 0,
+ ARRAY_SIZE(mux_pllmcp_clkm),
+ CLK_SET_RATE_NO_REPARENT,
clk_base + CLK_SOURCE_EMC,
30, 2, 0, NULL);
clk = tegra_clk_register_periph_gate("emc", "emc_mux", 0, clk_base, 0,
diff --git a/drivers/clk/versatile/clk-vexpress.c b/drivers/clk/versatile/clk-vexpress.c
index a4a728d..2d5e1b4 100644
--- a/drivers/clk/versatile/clk-vexpress.c
+++ b/drivers/clk/versatile/clk-vexpress.c
@@ -37,8 +37,8 @@ static void __init vexpress_sp810_init(void __iomem *base)
snprintf(name, ARRAY_SIZE(name), "timerclken%d", i);

vexpress_sp810_timerclken[i] = clk_register_mux(NULL, name,
- parents, 2, 0, base + SCCTRL,
- SCCTRL_TIMERENnSEL_SHIFT(i), 1,
+ parents, 2, CLK_SET_RATE_NO_REPARENT,
+ base + SCCTRL, SCCTRL_TIMERENnSEL_SHIFT(i), 1,
0, &vexpress_sp810_lock);

if (WARN_ON(IS_ERR(vexpress_sp810_timerclken[i])))
diff --git a/include/linux/clk-provider.h b/include/linux/clk-provider.h
index 24cb2ee..bb2d812 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_NO_REPARENT BIT(7) /* don't re-parent on rate change */

struct clk_hw;

--
1.8.1.2

2013-05-20 13:29:59

by James Hogan

[permalink] [raw]
Subject: [PATCH v4 1/5] clk: abstract parent cache

Abstract access to the clock parent cache by defining
clk_get_parent_by_index(clk, index). This allows access to parent
clocks from clock drivers.

Signed-off-by: James Hogan <[email protected]>
Reviewed-by: Stephen Boyd <[email protected]>
Cc: Mike Turquette <[email protected]>
Cc: [email protected]
---
Changes in v3:

* remove double underscore prefix from clk_get_parent_by_index()

drivers/clk/clk.c | 21 ++++++++++++++-------
include/linux/clk-provider.h | 1 +
2 files changed, 15 insertions(+), 7 deletions(-)

diff --git a/drivers/clk/clk.c b/drivers/clk/clk.c
index 74e5ab2..f0ebada 100644
--- a/drivers/clk/clk.c
+++ b/drivers/clk/clk.c
@@ -559,6 +559,19 @@ struct clk *__clk_get_parent(struct clk *clk)
return !clk ? NULL : clk->parent;
}

+struct clk *clk_get_parent_by_index(struct clk *clk, u8 index)
+{
+ if (!clk || index >= clk->num_parents)
+ return NULL;
+ else if (!clk->parents)
+ return __clk_lookup(clk->parent_names[index]);
+ else if (!clk->parents[index])
+ return clk->parents[index] =
+ __clk_lookup(clk->parent_names[index]);
+ else
+ return clk->parents[index];
+}
+
unsigned int __clk_get_enable_count(struct clk *clk)
{
return !clk ? 0 : clk->enable_count;
@@ -1315,13 +1328,7 @@ static struct clk *__clk_init_parent(struct clk *clk)
kzalloc((sizeof(struct clk*) * clk->num_parents),
GFP_KERNEL);

- if (!clk->parents)
- ret = __clk_lookup(clk->parent_names[index]);
- else if (!clk->parents[index])
- ret = clk->parents[index] =
- __clk_lookup(clk->parent_names[index]);
- else
- ret = clk->parents[index];
+ ret = clk_get_parent_by_index(clk, index);

out:
return ret;
diff --git a/include/linux/clk-provider.h b/include/linux/clk-provider.h
index 1186098..76b1667 100644
--- a/include/linux/clk-provider.h
+++ b/include/linux/clk-provider.h
@@ -403,6 +403,7 @@ const char *__clk_get_name(struct clk *clk);
struct clk_hw *__clk_get_hw(struct clk *clk);
u8 __clk_get_num_parents(struct clk *clk);
struct clk *__clk_get_parent(struct clk *clk);
+struct clk *clk_get_parent_by_index(struct clk *clk, u8 index);
unsigned int __clk_get_enable_count(struct clk *clk);
unsigned int __clk_get_prepare_count(struct clk *clk);
unsigned long __clk_get_rate(struct clk *clk);
--
1.8.1.2

2013-05-20 13:30:31

by James Hogan

[permalink] [raw]
Subject: [PATCH v4 3/5] clk: add support for clock reparent on set_rate

Add core support to allow clock implementations to select the best
parent clock when rounding a rate, e.g. the one which can provide the
closest clock rate to that requested. This is by way of adding a new
clock op, determine_rate(), which is like round_rate() but has an extra
parameter to allow the clock implementation to optionally select a
different parent clock. The core then takes care of reparenting the
clock when setting the rate.

The parent change takes place with the help of some new private data
members. struct clk::new_parent specifies a clock's new parent (NULL
indicates no change), and struct clk::new_child specifies a clock's new
child (whose new_parent member points back to it). The purpose of these
are to allow correct walking of the future tree for notifications prior
to actually reparenting any clocks, specifically to skip child clocks
who are being reparented to another clock (they will be notified via the
new parent), and to include any new child clock. These pointers are set
by clk_calc_subtree(), and the new_child pointer gets cleared when a
child is actually reparented to avoid duplicate POST_RATE_CHANGE
notifications.

Each place where round_rate() is called, determine_rate() is checked
first and called in preference. This restructures a few of the call
sites to simplify the logic into if/else blocks.

Signed-off-by: James Hogan <[email protected]>
Cc: Mike Turquette <[email protected]>
Cc: [email protected]
---
Changes in v4:

* (not included Stephen Boyd's Reviewed-by due to changes)
* replace __clk_set_parent_no_recalc with __clk_set_parent.
* never pass NULL to determine_rate's best_parent_clk parameter, and
slight refactor of __clk_round_rate to use local copy of clk->parent.
* a few new comments around use of clk::new_child.

Changes in v3:

* rebased on v3.10-rc1.
* store new_parent_index in struct clk too (calculated from
clk_fetch_parent_index, and passed through __clk_set_parent_no_recalc
to __clk_set_parent).
* allow determine_rate to satisfy recalc_rate check in __clk_init.

Documentation/clk.txt | 4 ++
drivers/clk/clk.c | 142 +++++++++++++++++++++++++++++--------------
include/linux/clk-private.h | 3 +
include/linux/clk-provider.h | 7 +++
4 files changed, 110 insertions(+), 46 deletions(-)

diff --git a/Documentation/clk.txt b/Documentation/clk.txt
index b9911c2..3110ba4 100644
--- a/Documentation/clk.txt
+++ b/Documentation/clk.txt
@@ -70,6 +70,10 @@ the operations defined in clk.h:
unsigned long parent_rate);
long (*round_rate)(struct clk_hw *hw, unsigned long,
unsigned long *);
+ long (*determine_rate)(struct clk_hw *hw,
+ unsigned long rate,
+ unsigned long *best_parent_rate,
+ struct clk **best_parent_clk);
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);
diff --git a/drivers/clk/clk.c b/drivers/clk/clk.c
index a4cd6bc..3ce4810 100644
--- a/drivers/clk/clk.c
+++ b/drivers/clk/clk.c
@@ -888,21 +888,24 @@ EXPORT_SYMBOL_GPL(clk_enable);
unsigned long __clk_round_rate(struct clk *clk, unsigned long rate)
{
unsigned long parent_rate = 0;
+ struct clk *parent;

if (!clk)
return 0;

- if (!clk->ops->round_rate) {
- if (clk->flags & CLK_SET_RATE_PARENT)
- return __clk_round_rate(clk->parent, rate);
- else
- return clk->rate;
- }
-
- if (clk->parent)
- parent_rate = clk->parent->rate;
-
- return clk->ops->round_rate(clk->hw, rate, &parent_rate);
+ parent = clk->parent;
+ if (parent)
+ parent_rate = parent->rate;
+
+ if (clk->ops->determine_rate)
+ return clk->ops->determine_rate(clk->hw, rate, &parent_rate,
+ &parent);
+ else if (clk->ops->round_rate)
+ return clk->ops->round_rate(clk->hw, rate, &parent_rate);
+ else if (clk->flags & CLK_SET_RATE_PARENT)
+ return __clk_round_rate(clk->parent, rate);
+ else
+ return clk->rate;
}

/**
@@ -1055,6 +1058,10 @@ static u8 clk_fetch_parent_index(struct clk *clk, struct clk *parent)

static void clk_reparent(struct clk *clk, struct clk *new_parent)
{
+ /* avoid duplicate POST_RATE_CHANGE notifications */
+ if (new_parent->new_child == clk)
+ new_parent->new_child = NULL;
+
hlist_del(&clk->child_node);

if (new_parent)
@@ -1173,18 +1180,25 @@ out:
return ret;
}

-static void clk_calc_subtree(struct clk *clk, unsigned long new_rate)
+static void clk_calc_subtree(struct clk *clk, unsigned long new_rate,
+ struct clk *new_parent, u8 p_index)
{
struct clk *child;

clk->new_rate = new_rate;
+ clk->new_parent = new_parent;
+ clk->new_parent_index = p_index;
+ /* include clk in new parent's PRE_RATE_CHANGE notifications */
+ clk->new_child = NULL;
+ if (new_parent && new_parent != clk->parent)
+ new_parent->new_child = clk;

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);
else
child->new_rate = new_rate;
- clk_calc_subtree(child, child->new_rate);
+ clk_calc_subtree(child, child->new_rate, NULL, 0);
}
}

@@ -1195,50 +1209,63 @@ 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 *old_parent, *parent;
unsigned long best_parent_rate = 0;
unsigned long new_rate;
+ u8 p_index = 0;

/* sanity */
if (IS_ERR_OR_NULL(clk))
return NULL;

/* save parent rate, if it exists */
- if (clk->parent)
- best_parent_rate = clk->parent->rate;
-
- /* 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);
+ parent = old_parent = clk->parent;
+ if (parent)
+ best_parent_rate = parent->rate;
+
+ /* find the closest rate and parent clk/rate */
+ if (clk->ops->determine_rate) {
+ new_rate = clk->ops->determine_rate(clk->hw, rate,
+ &best_parent_rate,
+ &parent);
+ } else if (clk->ops->round_rate) {
+ new_rate = clk->ops->round_rate(clk->hw, rate,
+ &best_parent_rate);
+ } else if (!parent || !(clk->flags & CLK_SET_RATE_PARENT)) {
+ /* pass-through clock without adjustable parent */
+ clk->new_rate = clk->rate;
+ return NULL;
+ } else {
+ /* pass-through clock with adjustable parent */
+ top = clk_calc_new_rates(parent, rate);
+ new_rate = parent->new_rate;
goto out;
}

- /* need clk->parent from here on out */
- if (!clk->parent) {
- pr_debug("%s: %s has NULL parent\n", __func__, clk->name);
+ /* some clocks must be gated to change parent */
+ if (parent != old_parent &&
+ (clk->flags & CLK_SET_PARENT_GATE) && clk->prepare_count) {
+ pr_debug("%s: %s not gated but wants to reparent\n",
+ __func__, clk->name);
return NULL;
}

- if (!clk->ops->round_rate) {
- top = clk_calc_new_rates(clk->parent, rate);
- new_rate = clk->parent->new_rate;
-
- goto out;
+ /* try finding the new parent index */
+ if (parent) {
+ p_index = clk_fetch_parent_index(clk, parent);
+ if (p_index == clk->num_parents) {
+ pr_debug("%s: clk %s can not be parent of clk %s\n",
+ __func__, parent->name, clk->name);
+ return NULL;
+ }
}

- new_rate = clk->ops->round_rate(clk->hw, rate, &best_parent_rate);
-
- if (best_parent_rate != clk->parent->rate) {
- top = clk_calc_new_rates(clk->parent, best_parent_rate);
-
- goto out;
- }
+ if ((clk->flags & CLK_SET_RATE_PARENT) && parent &&
+ best_parent_rate != parent->rate)
+ top = clk_calc_new_rates(parent, best_parent_rate);

out:
- clk_calc_subtree(clk, new_rate);
+ clk_calc_subtree(clk, new_rate, parent, p_index);

return top;
}
@@ -1250,7 +1277,7 @@ out:
*/
static struct clk *clk_propagate_rate_change(struct clk *clk, unsigned long event)
{
- struct clk *child, *fail_clk = NULL;
+ struct clk *child, *tmp_clk, *fail_clk = NULL;
int ret = NOTIFY_DONE;

if (clk->rate == clk->new_rate)
@@ -1263,9 +1290,19 @@ static struct clk *clk_propagate_rate_change(struct clk *clk, unsigned long even
}

hlist_for_each_entry(child, &clk->children, child_node) {
- clk = clk_propagate_rate_change(child, event);
- if (clk)
- fail_clk = clk;
+ /* Skip children who will be reparented to another clock */
+ if (child->new_parent && child->new_parent != clk)
+ continue;
+ tmp_clk = clk_propagate_rate_change(child, event);
+ if (tmp_clk)
+ fail_clk = tmp_clk;
+ }
+
+ /* handle the new child who might not be in clk->children yet */
+ if (clk->new_child) {
+ tmp_clk = clk_propagate_rate_change(clk->new_child, event);
+ if (tmp_clk)
+ fail_clk = tmp_clk;
}

return fail_clk;
@@ -1283,6 +1320,10 @@ static void clk_change_rate(struct clk *clk)

old_rate = clk->rate;

+ /* set parent */
+ if (clk->new_parent && clk->new_parent != clk->parent)
+ __clk_set_parent(clk, clk->new_parent, clk->new_parent_index);
+
if (clk->parent)
best_parent_rate = clk->parent->rate;

@@ -1297,8 +1338,16 @@ static void clk_change_rate(struct clk *clk)
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)
+ 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)
+ continue;
clk_change_rate(child);
+ }
+
+ /* handle the new child who might not be in clk->children yet */
+ if (clk->new_child)
+ clk_change_rate(clk->new_child);
}

/**
@@ -1545,8 +1594,9 @@ int __clk_init(struct device *dev, struct clk *clk)

/* check that clk_ops are sane. See Documentation/clk.txt */
if (clk->ops->set_rate &&
- !(clk->ops->round_rate && clk->ops->recalc_rate)) {
- pr_warning("%s: %s must implement .round_rate & .recalc_rate\n",
+ !((clk->ops->round_rate || clk->ops->determine_rate) &&
+ clk->ops->recalc_rate)) {
+ pr_warning("%s: %s must implement .round_rate or .determine_rate in addition to .recalc_rate\n",
__func__, clk->name);
ret = -EINVAL;
goto out;
diff --git a/include/linux/clk-private.h b/include/linux/clk-private.h
index dd7adff..8138c94 100644
--- a/include/linux/clk-private.h
+++ b/include/linux/clk-private.h
@@ -33,8 +33,11 @@ struct clk {
const char **parent_names;
struct clk **parents;
u8 num_parents;
+ u8 new_parent_index;
unsigned long rate;
unsigned long new_rate;
+ struct clk *new_parent;
+ struct clk *new_child;
unsigned long flags;
unsigned int enable_count;
unsigned int prepare_count;
diff --git a/include/linux/clk-provider.h b/include/linux/clk-provider.h
index 76b1667..24cb2ee 100644
--- a/include/linux/clk-provider.h
+++ b/include/linux/clk-provider.h
@@ -79,6 +79,10 @@ struct clk_hw;
* @round_rate: Given a target rate as input, returns the closest rate actually
* supported by the clock.
*
+ * @determine_rate: Given a target rate as input, returns the closest rate
+ * actually supported by the clock, and optionally the parent clock
+ * 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
* the parent clock. This index can be applied to either the
@@ -126,6 +130,9 @@ struct clk_ops {
unsigned long parent_rate);
long (*round_rate)(struct clk_hw *hw, unsigned long,
unsigned long *);
+ long (*determine_rate)(struct clk_hw *hw, unsigned long rate,
+ unsigned long *best_parent_rate,
+ struct clk **best_parent_clk);
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

2013-05-20 20:44:15

by Stephen Warren

[permalink] [raw]
Subject: Re: [PATCH v4 4/5] clk: add CLK_SET_RATE_NO_REPARENT flag

On 05/20/2013 07:28 AM, James Hogan wrote:
> Add a CLK_SET_RATE_NO_REPARENT clock flag, which will prevent muxes
> being reparented during clk_set_rate.
>
> To avoid breaking existing platforms, all callers of clk_register_mux()
> are adjusted to pass the new flag. Platform maintainers are encouraged
> to remove the flag if they wish to allow mux reparenting on set_rate.

The Tegra parts,
Acked-by: Stephen Warren <[email protected]>

2013-05-20 21:16:47

by Stephen Boyd

[permalink] [raw]
Subject: Re: [PATCH v4 2/5] clk: move some parent related functions upwards

On 05/20/13 06:28, James Hogan wrote:
> Move some parent related functions up in clk.c so they can be used by
> the modifications in the following patch which enables clock reparenting
> during set_rate. No other changes are made so this patch makes no
> functional difference in isolation. This is separate from the following
> patch primarily to ease readability of that patch.
>
> Signed-off-by: James Hogan <[email protected]>
> Cc: Mike Turquette <[email protected]>
> Cc: [email protected]

Reviewed-by: Stephen Boyd <[email protected]>

--
Qualcomm Innovation Center, Inc. is a member of Code Aurora Forum,
hosted by The Linux Foundation

2013-05-20 21:17:18

by Stephen Boyd

[permalink] [raw]
Subject: Re: [PATCH v4 3/5] clk: add support for clock reparent on set_rate

On 05/20/13 06:28, James Hogan wrote:
> diff --git a/Documentation/clk.txt b/Documentation/clk.txt
> index b9911c2..3110ba4 100644
> --- a/Documentation/clk.txt
> +++ b/Documentation/clk.txt
> @@ -70,6 +70,10 @@ the operations defined in clk.h:
> unsigned long parent_rate);
> long (*round_rate)(struct clk_hw *hw, unsigned long,
> unsigned long *);
> + long (*determine_rate)(struct clk_hw *hw,
> + unsigned long rate,
> + unsigned long *best_parent_rate,
> + struct clk **best_parent_clk);
> 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);

Can you update the clock hardware characteristics table as well?

--
Qualcomm Innovation Center, Inc. is a member of Code Aurora Forum,
hosted by The Linux Foundation

2013-05-21 04:44:40

by Saravana Kannan

[permalink] [raw]
Subject: Re: [PATCH v4 5/5] clk: clk-mux: implement remuxing on set_rate

On 05/20/2013 06:28 AM, James Hogan wrote:
> Implement clk-mux remuxing if the CLK_SET_RATE_NO_REPARENT flag isn't
> set. This implements determine_rate for clk-mux to propagate to each
> parent and to choose the best one (like clk-divider this chooses the
> parent which provides the fastest rate <= the requested rate).
>
> The determine_rate op is implemented as a core helper function so that
> it can be easily used by more complex clocks which incorporate muxes.
>
> Signed-off-by: James Hogan <[email protected]>
> Cc: Mike Turquette <[email protected]>
> Cc: [email protected]
> ---
> Changes in v4:
>
> * never pass NULL to determine_rate's best_parent_clk parameter.
>
> Changes in v3:
>
> * rename/invert CLK_SET_RATE_REMUX to CLK_SET_RATE_NO_REPARENT and move
> to patch 3.
>
> drivers/clk/clk-mux.c | 1 +
> drivers/clk/clk.c | 49 ++++++++++++++++++++++++++++++++++++++++++++
> include/linux/clk-provider.h | 3 +++
> 3 files changed, 53 insertions(+)
>
> diff --git a/drivers/clk/clk-mux.c b/drivers/clk/clk-mux.c
> index 25b1734..cecfa01 100644
> --- a/drivers/clk/clk-mux.c
> +++ b/drivers/clk/clk-mux.c
> @@ -100,6 +100,7 @@ static int clk_mux_set_parent(struct clk_hw *hw, u8 index)
> const struct clk_ops clk_mux_ops = {
> .get_parent = clk_mux_get_parent,
> .set_parent = clk_mux_set_parent,
> + .determine_rate = __clk_mux_determine_rate,
> };
> EXPORT_SYMBOL_GPL(clk_mux_ops);
>
> diff --git a/drivers/clk/clk.c b/drivers/clk/clk.c
> index 3ce4810..85b661d 100644
> --- a/drivers/clk/clk.c
> +++ b/drivers/clk/clk.c
> @@ -692,6 +692,55 @@ struct clk *__clk_lookup(const char *name)
> return NULL;
> }
>
> +/*
> + * Helper for finding best parent to provide a given frequency. This can be used
> + * directly as a determine_rate callback (e.g. for a mux), or from a more
> + * complex clock that may combine a mux with other operations.
> + */
> +long __clk_mux_determine_rate(struct clk_hw *hw, unsigned long rate,
> + unsigned long *best_parent_rate,
> + struct clk **best_parent_p)
> +{
> + struct clk *clk = hw->clk, *parent, *best_parent = NULL;
> + int i, num_parents;
> + unsigned long parent_rate, best = 0;
> +
> + /* if NO_REPARENT flag set, pass through to current parent */
> + if (clk->flags & CLK_SET_RATE_NO_REPARENT) {
> + parent = clk->parent;
> + if (clk->flags & CLK_SET_RATE_PARENT)
> + best = __clk_round_rate(parent, rate);
> + else if (parent)
> + best = __clk_get_rate(parent);
> + else
> + best = __clk_get_rate(clk);
> + goto out;
> + }
> +
> + /* find the parent that can provide the fastest rate <= rate */
> + num_parents = clk->num_parents;
> + for (i = 0; i < num_parents; i++) {

While writing a similar code for our internal tree, I quickly came to
the realization that, "all parents are equal, but some are more equal
than others". The simplest example is a clock tree like this:

Source -> Divider -> Mux
Source -> Mux

A rate of Y can be achieved for Mux by either running Source at Y and
picking that input or running Source at Y * 2 and Divider set to div-2
and picking the Divider input.

The solution seems to be a priority list of parents. I'm sure there
would be other reason (jitter, clock quality, etc) for a mux to pick one
parent vs. another when both of them can provide the required rate.

I think this loop should loop over parents based on their priority
order. So, parents should become a struct of { clk, index } and have the
parents listed in the order of priority. Well, at least in that long run
that would be better to avoid messing up parent/index values. But for
now, you could just have a priority array of index or parents.

It might not fit 100% of the cases where two parents can provide the
same rate, but it should fit most use cases.

> + parent = clk_get_parent_by_index(clk, i);
> + if (!parent)
> + continue;
> + if (clk->flags & CLK_SET_RATE_PARENT)
> + parent_rate = __clk_round_rate(parent, rate);
> + else
> + parent_rate = __clk_get_rate(parent);
> + if (parent_rate <= rate && parent_rate > best) {
> + best_parent = parent;
> + best = parent_rate;
> + }
> + }
> +
> +out:
> + if (best_parent)
> + *best_parent_p = best_parent;
> + *best_parent_rate = best;
> +
> + return best;
> +}
> +

Thanks,
Saravana

--
The Qualcomm Innovation Center, Inc. is a member of the Code Aurora Forum,
hosted by The Linux Foundation

2013-05-21 05:10:57

by Saravana Kannan

[permalink] [raw]
Subject: Re: [PATCH v4 3/5] clk: add support for clock reparent on set_rate

On 05/20/2013 06:28 AM, James Hogan wrote:
> Add core support to allow clock implementations to select the best
> parent clock when rounding a rate, e.g. the one which can provide the
> closest clock rate to that requested. This is by way of adding a new
> clock op, determine_rate(), which is like round_rate() but has an extra
> parameter to allow the clock implementation to optionally select a
> different parent clock. The core then takes care of reparenting the
> clock when setting the rate.
>
> The parent change takes place with the help of some new private data
> members. struct clk::new_parent specifies a clock's new parent (NULL
> indicates no change), and struct clk::new_child specifies a clock's new
> child (whose new_parent member points back to it). The purpose of these
> are to allow correct walking of the future tree for notifications prior
> to actually reparenting any clocks, specifically to skip child clocks
> who are being reparented to another clock (they will be notified via the
> new parent), and to include any new child clock. These pointers are set
> by clk_calc_subtree(), and the new_child pointer gets cleared when a
> child is actually reparented to avoid duplicate POST_RATE_CHANGE
> notifications.
>
> Each place where round_rate() is called, determine_rate() is checked
> first and called in preference. This restructures a few of the call
> sites to simplify the logic into if/else blocks.
>
> Signed-off-by: James Hogan <[email protected]>
> Cc: Mike Turquette <[email protected]>
> Cc: [email protected]
> ---
> Changes in v4:
>
> * (not included Stephen Boyd's Reviewed-by due to changes)
> * replace __clk_set_parent_no_recalc with __clk_set_parent.
> * never pass NULL to determine_rate's best_parent_clk parameter, and
> slight refactor of __clk_round_rate to use local copy of clk->parent.
> * a few new comments around use of clk::new_child.
>
> Changes in v3:
>
> * rebased on v3.10-rc1.
> * store new_parent_index in struct clk too (calculated from
> clk_fetch_parent_index, and passed through __clk_set_parent_no_recalc
> to __clk_set_parent).
> * allow determine_rate to satisfy recalc_rate check in __clk_init.
>
> Documentation/clk.txt | 4 ++
> drivers/clk/clk.c | 142 +++++++++++++++++++++++++++++--------------
> include/linux/clk-private.h | 3 +
> include/linux/clk-provider.h | 7 +++
> 4 files changed, 110 insertions(+), 46 deletions(-)
>
> diff --git a/Documentation/clk.txt b/Documentation/clk.txt
> index b9911c2..3110ba4 100644
> --- a/Documentation/clk.txt
> +++ b/Documentation/clk.txt
> @@ -70,6 +70,10 @@ the operations defined in clk.h:
> unsigned long parent_rate);
> long (*round_rate)(struct clk_hw *hw, unsigned long,
> unsigned long *);
> + long (*determine_rate)(struct clk_hw *hw,
> + unsigned long rate,
> + unsigned long *best_parent_rate,
> + struct clk **best_parent_clk);
> 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);
> diff --git a/drivers/clk/clk.c b/drivers/clk/clk.c
> index a4cd6bc..3ce4810 100644
> --- a/drivers/clk/clk.c
> +++ b/drivers/clk/clk.c
> @@ -888,21 +888,24 @@ EXPORT_SYMBOL_GPL(clk_enable);
> unsigned long __clk_round_rate(struct clk *clk, unsigned long rate)
> {
> unsigned long parent_rate = 0;
> + struct clk *parent;
>
> if (!clk)
> return 0;
>
> - if (!clk->ops->round_rate) {
> - if (clk->flags & CLK_SET_RATE_PARENT)
> - return __clk_round_rate(clk->parent, rate);
> - else
> - return clk->rate;
> - }
> -
> - if (clk->parent)
> - parent_rate = clk->parent->rate;
> -
> - return clk->ops->round_rate(clk->hw, rate, &parent_rate);
> + parent = clk->parent;
> + if (parent)
> + parent_rate = parent->rate;
> +
> + if (clk->ops->determine_rate)
> + return clk->ops->determine_rate(clk->hw, rate, &parent_rate,
> + &parent);
> + else if (clk->ops->round_rate)
> + return clk->ops->round_rate(clk->hw, rate, &parent_rate);
> + else if (clk->flags & CLK_SET_RATE_PARENT)
> + return __clk_round_rate(clk->parent, rate);
> + else
> + return clk->rate;
> }
>
> /**
> @@ -1055,6 +1058,10 @@ static u8 clk_fetch_parent_index(struct clk *clk, struct clk *parent)
>
> static void clk_reparent(struct clk *clk, struct clk *new_parent)
> {
> + /* avoid duplicate POST_RATE_CHANGE notifications */
> + if (new_parent->new_child == clk)
> + new_parent->new_child = NULL;
> +
> hlist_del(&clk->child_node);
>
> if (new_parent)
> @@ -1173,18 +1180,25 @@ out:
> return ret;
> }
>
> -static void clk_calc_subtree(struct clk *clk, unsigned long new_rate)
> +static void clk_calc_subtree(struct clk *clk, unsigned long new_rate,
> + struct clk *new_parent, u8 p_index)
> {
> struct clk *child;
>
> clk->new_rate = new_rate;
> + clk->new_parent = new_parent;
> + clk->new_parent_index = p_index;
> + /* include clk in new parent's PRE_RATE_CHANGE notifications */
> + clk->new_child = NULL;
> + if (new_parent && new_parent != clk->parent)
> + new_parent->new_child = clk;
>
> 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);
> else
> child->new_rate = new_rate;
> - clk_calc_subtree(child, child->new_rate);
> + clk_calc_subtree(child, child->new_rate, NULL, 0);
> }
> }
>
> @@ -1195,50 +1209,63 @@ 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 *old_parent, *parent;
> unsigned long best_parent_rate = 0;
> unsigned long new_rate;
> + u8 p_index = 0;
>
> /* sanity */
> if (IS_ERR_OR_NULL(clk))
> return NULL;
>
> /* save parent rate, if it exists */
> - if (clk->parent)
> - best_parent_rate = clk->parent->rate;
> -
> - /* 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);
> + parent = old_parent = clk->parent;
> + if (parent)
> + best_parent_rate = parent->rate;
> +
> + /* find the closest rate and parent clk/rate */
> + if (clk->ops->determine_rate) {
> + new_rate = clk->ops->determine_rate(clk->hw, rate,
> + &best_parent_rate,
> + &parent);
> + } else if (clk->ops->round_rate) {
> + new_rate = clk->ops->round_rate(clk->hw, rate,
> + &best_parent_rate);
> + } else if (!parent || !(clk->flags & CLK_SET_RATE_PARENT)) {
> + /* pass-through clock without adjustable parent */
> + clk->new_rate = clk->rate;
> + return NULL;
> + } else {
> + /* pass-through clock with adjustable parent */
> + top = clk_calc_new_rates(parent, rate);
> + new_rate = parent->new_rate;
> goto out;
> }
>
> - /* need clk->parent from here on out */
> - if (!clk->parent) {
> - pr_debug("%s: %s has NULL parent\n", __func__, clk->name);
> + /* some clocks must be gated to change parent */
> + if (parent != old_parent &&
> + (clk->flags & CLK_SET_PARENT_GATE) && clk->prepare_count) {
> + pr_debug("%s: %s not gated but wants to reparent\n",
> + __func__, clk->name);
> return NULL;
> }
>
> - if (!clk->ops->round_rate) {
> - top = clk_calc_new_rates(clk->parent, rate);
> - new_rate = clk->parent->new_rate;
> -
> - goto out;
> + /* try finding the new parent index */
> + if (parent) {
> + p_index = clk_fetch_parent_index(clk, parent);
> + if (p_index == clk->num_parents) {
> + pr_debug("%s: clk %s can not be parent of clk %s\n",
> + __func__, parent->name, clk->name);
> + return NULL;
> + }
> }
>
> - new_rate = clk->ops->round_rate(clk->hw, rate, &best_parent_rate);
> -
> - if (best_parent_rate != clk->parent->rate) {
> - top = clk_calc_new_rates(clk->parent, best_parent_rate);
> -
> - goto out;
> - }
> + if ((clk->flags & CLK_SET_RATE_PARENT) && parent &&
> + best_parent_rate != parent->rate)
> + top = clk_calc_new_rates(parent, best_parent_rate);
>
> out:
> - clk_calc_subtree(clk, new_rate);
> + clk_calc_subtree(clk, new_rate, parent, p_index);
>
> return top;
> }
> @@ -1250,7 +1277,7 @@ out:
> */
> static struct clk *clk_propagate_rate_change(struct clk *clk, unsigned long event)
> {
> - struct clk *child, *fail_clk = NULL;
> + struct clk *child, *tmp_clk, *fail_clk = NULL;
> int ret = NOTIFY_DONE;
>
> if (clk->rate == clk->new_rate)
> @@ -1263,9 +1290,19 @@ static struct clk *clk_propagate_rate_change(struct clk *clk, unsigned long even
> }
>
> hlist_for_each_entry(child, &clk->children, child_node) {
> - clk = clk_propagate_rate_change(child, event);
> - if (clk)
> - fail_clk = clk;
> + /* Skip children who will be reparented to another clock */
> + if (child->new_parent && child->new_parent != clk)
> + continue;
> + tmp_clk = clk_propagate_rate_change(child, event);
> + if (tmp_clk)
> + fail_clk = tmp_clk;
> + }
> +
> + /* handle the new child who might not be in clk->children yet */
> + if (clk->new_child) {
> + tmp_clk = clk_propagate_rate_change(clk->new_child, event);
> + if (tmp_clk)
> + fail_clk = tmp_clk;
> }
>
> return fail_clk;
> @@ -1283,6 +1320,10 @@ static void clk_change_rate(struct clk *clk)
>
> old_rate = clk->rate;
>
> + /* set parent */
> + if (clk->new_parent && clk->new_parent != clk->parent)
> + __clk_set_parent(clk, clk->new_parent, clk->new_parent_index);
> +
> if (clk->parent)
> best_parent_rate = clk->parent->rate;
>
> @@ -1297,8 +1338,16 @@ static void clk_change_rate(struct clk *clk)
> 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)
> + 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)
> + continue;
> clk_change_rate(child);
> + }
> +
> + /* handle the new child who might not be in clk->children yet */
> + if (clk->new_child)
> + clk_change_rate(clk->new_child);
> }
>
> /**
> @@ -1545,8 +1594,9 @@ int __clk_init(struct device *dev, struct clk *clk)
>
> /* check that clk_ops are sane. See Documentation/clk.txt */
> if (clk->ops->set_rate &&
> - !(clk->ops->round_rate && clk->ops->recalc_rate)) {
> - pr_warning("%s: %s must implement .round_rate & .recalc_rate\n",
> + !((clk->ops->round_rate || clk->ops->determine_rate) &&
> + clk->ops->recalc_rate)) {
> + pr_warning("%s: %s must implement .round_rate or .determine_rate in addition to .recalc_rate\n",
> __func__, clk->name);
> ret = -EINVAL;
> goto out;
> diff --git a/include/linux/clk-private.h b/include/linux/clk-private.h
> index dd7adff..8138c94 100644
> --- a/include/linux/clk-private.h
> +++ b/include/linux/clk-private.h
> @@ -33,8 +33,11 @@ struct clk {
> const char **parent_names;
> struct clk **parents;
> u8 num_parents;
> + u8 new_parent_index;

Why do you need this? Given the new_parent, can't the specific clock
implementation just look it up when set_rate() is called? Wouldn't that
be the only time you would actually need the index?

If it's just for optimization of some error cases, I think we should
drop this to keep the code simpler. One less state to keep track of when
reading, writing or reviewing the clock framework.

I would like to do a more thorough review of this patch, but it's been a
while since I have looked at clk.c. So, I might not be able to do it
soon enough. But I can't ask to hold off this patch just because I don't
have time. So, I'll see what I can do.

-Saravana




--
The Qualcomm Innovation Center, Inc. is a member of the Code Aurora Forum,
hosted by The Linux Foundation

2013-05-22 10:13:30

by James Hogan

[permalink] [raw]
Subject: Re: [PATCH v4 3/5] clk: add support for clock reparent on set_rate

On 21/05/13 06:10, Saravana Kannan wrote:
> On 05/20/2013 06:28 AM, James Hogan wrote:
>> diff --git a/include/linux/clk-private.h b/include/linux/clk-private.h
>> index dd7adff..8138c94 100644
>> --- a/include/linux/clk-private.h
>> +++ b/include/linux/clk-private.h
>> @@ -33,8 +33,11 @@ struct clk {
>> const char **parent_names;
>> struct clk **parents;
>> u8 num_parents;
>> + u8 new_parent_index;
>
> Why do you need this? Given the new_parent, can't the specific clock
> implementation just look it up when set_rate() is called? Wouldn't that
> be the only time you would actually need the index?
>
> If it's just for optimization of some error cases, I think we should
> drop this to keep the code simpler. One less state to keep track of when
> reading, writing or reviewing the clock framework.

clk_change_rate cannot currently return an error condition so I had
assumed it was better to check that the requested parent clock has a
valid parent index prior to starting to change any clock rates or firing
off any notifications.

Cheers
James

2013-05-22 15:52:28

by Maxime Ripard

[permalink] [raw]
Subject: Re: [PATCH v4 4/5] clk: add CLK_SET_RATE_NO_REPARENT flag

Hi James,

On Mon, May 20, 2013 at 02:28:26PM +0100, James Hogan wrote:
> Add a CLK_SET_RATE_NO_REPARENT clock flag, which will prevent muxes
> being reparented during clk_set_rate.
>
> To avoid breaking existing platforms, all callers of clk_register_mux()
> are adjusted to pass the new flag. Platform maintainers are encouraged
> to remove the flag if they wish to allow mux reparenting on set_rate.

For the sunxi part,
Acked-by: Maxime Ripard <[email protected]>

--
Maxime Ripard, Free Electrons
Embedded Linux, Kernel and Android engineering
http://free-electrons.com

2013-06-06 01:39:50

by Haojian Zhuang

[permalink] [raw]
Subject: Re: [PATCH v4 0/5] clk: implement remuxing during set_rate

On Mon, May 20, 2013 at 9:28 PM, James Hogan <[email protected]> wrote:
> This patchset adds support for automatic selection of the best parent
> for a clock mux, i.e. the one which can provide the closest clock rate
> to that requested. It can be disabled by a new CLK_SET_RATE_NO_REPARENT
> flag (which is set for all uses of clk_register_mux().
>
> This works by way of adding a new op, determine_rate, similar to
> round_rate but with an extra parameter to allow the clock driver to
> optionally select a different parent clock. This is used in
> clk_calc_new_rates to decide whether to initiate a set_parent operation.
>
> Changes in v4:
>
> * rebased on clk-next ("clk: sun5i: Add compatibles for Allwinner A13").
> * replace __clk_set_parent_no_recalc with __clk_set_parent.
> * never pass NULL to determine_rate's best_parent_clk parameter, and
> slight refactor of __clk_round_rate to use local copy of clk->parent.
> * a few new comments around use of clk::new_child.
> * new patch (patch 2) split out of patch 3 to avoid having to declare
> static __clk_set_parent() at the top of clk.c, and to ease readability
> of patch 3.
>
> Changes in v3:
>
> * rebased on v3.10-rc1.
> * remove double underscore prefix from clk_get_parent_by_index()
> * store new_parent_index in struct clk too (calculated from
> clk_fetch_parent_index, and passed through __clk_set_parent_no_recalc
> to __clk_set_parent).
> * allow determine_rate to satisfy recalc_rate check in __clk_init.
> * rename/invert CLK_SET_RATE_REMUX to CLK_SET_RATE_NO_REPARENT and move
> to patch 3.
> * patch 3: add CLK_SET_RATE_NO_REPARENT flag to all callers of
> clk_register_mux. If you don't mind your clocks being reparented in
> response to set_rate please let me know and I'll drop the relevant
> portion of the patch.
>
> Changes in v2:
>
> I've moved the mux determine_rate implementation into a core helper, but
> I haven't pushed it fully into the core, as I think it just wouldn't
> work correctly for more complex clocks, e.g. if you (theoretically) had
> a combined mux and divide, you'd want to intercept the determine_rate
> and ask for a larger rate from the parent clocks, then return the
> divided rate. This should be possible by wrapping the mux determine_rate
> helper.
>
> Patch 1 still exports the __clk_get_parent_by_index as it seems like it
> might be a useful thing for clock implementations to have access to if
> they ever wanted to do something more fancy with changing clock parents.
>
> I haven't made any attempt to implement the atomic set_parent+set_rate
> as I don't have hardware that could take proper advantage of it, but it
> shouldn't be too difficult for others to implement if they wanted since
> they're fairly close to one another (in clk_change_rate()).
>
> * switched to using new determine_rate op rather than adding an argument
> to round_rate.
> * moved mux implementation into a single helper which should be usable
> from more complex clocks which can mux.
> * rewrite main implementation so that no changes are made until after
> the PRE notifications have been sent, and in a way that should ensure
> correct notifications without duplicates, and I think should be safe
> in the event of a notification failing.
> * various tidy ups and fixes.
>
> James Hogan (5):
> clk: abstract parent cache
> clk: move some parent related functions upwards
> clk: add support for clock reparent on set_rate
> clk: add CLK_SET_RATE_NO_REPARENT flag
> clk: clk-mux: implement remuxing on set_rate
>
> Documentation/clk.txt | 4 +
> arch/arm/mach-imx/clk.h | 5 +-
> drivers/clk/clk-mux.c | 1 +
> drivers/clk/clk.c | 416 ++++++++++++++++++++++-------------
> drivers/clk/mmp/clk-mmp2.c | 39 ++--
> drivers/clk/mmp/clk-pxa168.c | 40 ++--
> drivers/clk/mmp/clk-pxa910.c | 31 ++-
> drivers/clk/mxs/clk.h | 4 +-
> drivers/clk/samsung/clk.h | 2 +-
> drivers/clk/spear/spear1310_clock.c | 179 +++++++--------
> drivers/clk/spear/spear1340_clock.c | 97 ++++----
> drivers/clk/spear/spear3xx_clock.c | 57 +++--
> drivers/clk/spear/spear6xx_clock.c | 35 +--
> drivers/clk/sunxi/clk-sunxi.c | 3 +-
> drivers/clk/tegra/clk-tegra114.c | 36 ++-
> drivers/clk/tegra/clk-tegra20.c | 6 +-
> drivers/clk/tegra/clk-tegra30.c | 33 ++-
> drivers/clk/versatile/clk-vexpress.c | 4 +-
> include/linux/clk-private.h | 3 +
> include/linux/clk-provider.h | 12 +
> 20 files changed, 614 insertions(+), 393 deletions(-)
>
> --
> 1.8.1.2

Tested-by: Haojian Zhuang <[email protected]>

Pass test on MMP & Hisilicon SoC.

Regards
Haojian

2013-06-12 01:01:05

by Douglas Anderson

[permalink] [raw]
Subject: Re: [PATCH v4 5/5] clk: clk-mux: implement remuxing on set_rate

Hi,

Mike pointed me at this series since I'm running into parenting
problems at the moment as well...

On Mon, May 20, 2013 at 9:44 PM, Saravana Kannan <[email protected]> wrote:
> While writing a similar code for our internal tree, I quickly came to the
> realization that, "all parents are equal, but some are more equal than
> others". The simplest example is a clock tree like this:
>
> Source -> Divider -> Mux
> Source -> Mux
>
> A rate of Y can be achieved for Mux by either running Source at Y and
> picking that input or running Source at Y * 2 and Divider set to div-2 and
> picking the Divider input.
>
> The solution seems to be a priority list of parents. I'm sure there would be
> other reason (jitter, clock quality, etc) for a mux to pick one parent vs.
> another when both of them can provide the required rate.
>
> I think this loop should loop over parents based on their priority order.
> So, parents should become a struct of { clk, index } and have the parents
> listed in the order of priority. Well, at least in that long run that would
> be better to avoid messing up parent/index values. But for now, you could
> just have a priority array of index or parents.
>
> It might not fit 100% of the cases where two parents can provide the same
> rate, but it should fit most use cases.

I'm slightly worried about similar problems, but I don't have a really
great solution.

In my case I'm working on exynos5 hardware which has a bunch of PLLs
and a crazy number of muxing options. Many of the muxing options are
not really meant to be used but seem to have been added to the SoC as
a "backup plan" of some sort. :-/ Most of the PLLs are intended to
be used for one purpose and one purpose only though muxing options
allow them to be used all over the place.

For instance if I look at my current bootup of exynos5250-snow, I see:

fout_apll/clk_rate:1700000000
fout_bpll/clk_rate:800000000
fout_cpll/clk_rate:333000000
fout_epll/clk_rate:45158401
fout_gpll/clk_rate:533000000
fout_mpll/clk_rate:1600000000

* APLL is intended to be the parent of the 2 ARM cores and changes due
to cpu load
* EPLL is intended to be the parent for audio and changes dpending on
audio playback rates.
* GPLL is intended to be the parent of the GPU and changes due to gpu load
* VPLL is intended to be the parent for video related things and could
change depending on the LCD.
* MPLL doesn't change a lot and is intended to be the parent for most things.
* In some systems BPLL can be used for memory or GPU

My main concern here is the CCF will end up deciding at some point
that it should reparent some clock onto a PLL that is going to change
a whole lot. Maybe the user will plug in an SD card that requests a
frequency of 52MHz and at the moment we'll be running EPLL at 104 MHz
so it will be a perfect match! ...but then the user wants to play
audio at a different rate. The audio code assumes that it can mess
with its clock and we've got code setup to call CLK_SET_RATE_PARENT
all the way up to EPLL. That will really mess with the SD card.
Really we'd rather just have the SD card clock always parented on the
stable MPLL and it's OK if 52MHz gets rounded down to 50MHz.

Of course, on another board maybe they don't have an audio codec and
aren't using epll for audio and have realized that EPLL would be a
perfect way to get their SD card to run 4% faster. It ought to work.


I guess to summarize the above:

* It seems like much of the muxing on exynos5250 is just too
complicated to leave it to an simple automated algorithm.
* It seems like we can't make muxing decisions on the SoC level.
* Your automatic muxing patches don't hurt me and could be useful for
_some_ of the muxing options, just not the top PLL ones.

...but the only place that leaves me for my muxing needs is the device
tree. ...and as Mike pointed out on IRC the device tree should
describe hardware, not policy. Ick.

-Doug

2013-06-12 17:45:07

by Mike Turquette

[permalink] [raw]
Subject: Re: [PATCH v4 5/5] clk: clk-mux: implement remuxing on set_rate

Quoting Doug Anderson (2013-06-11 18:01:01)
> Hi,
>
> Mike pointed me at this series since I'm running into parenting
> problems at the moment as well...
>
> On Mon, May 20, 2013 at 9:44 PM, Saravana Kannan <[email protected]> wrote:
> > While writing a similar code for our internal tree, I quickly came to the
> > realization that, "all parents are equal, but some are more equal than
> > others". The simplest example is a clock tree like this:
> >
> > Source -> Divider -> Mux
> > Source -> Mux
> >
> > A rate of Y can be achieved for Mux by either running Source at Y and
> > picking that input or running Source at Y * 2 and Divider set to div-2 and
> > picking the Divider input.
> >
> > The solution seems to be a priority list of parents. I'm sure there would be
> > other reason (jitter, clock quality, etc) for a mux to pick one parent vs.
> > another when both of them can provide the required rate.
> >
> > I think this loop should loop over parents based on their priority order.
> > So, parents should become a struct of { clk, index } and have the parents
> > listed in the order of priority. Well, at least in that long run that would
> > be better to avoid messing up parent/index values. But for now, you could
> > just have a priority array of index or parents.
> >
> > It might not fit 100% of the cases where two parents can provide the same
> > rate, but it should fit most use cases.
>
> I'm slightly worried about similar problems, but I don't have a really
> great solution.
>
> In my case I'm working on exynos5 hardware which has a bunch of PLLs
> and a crazy number of muxing options. Many of the muxing options are
> not really meant to be used but seem to have been added to the SoC as
> a "backup plan" of some sort. :-/ Most of the PLLs are intended to
> be used for one purpose and one purpose only though muxing options
> allow them to be used all over the place.
>
> For instance if I look at my current bootup of exynos5250-snow, I see:
>
> fout_apll/clk_rate:1700000000
> fout_bpll/clk_rate:800000000
> fout_cpll/clk_rate:333000000
> fout_epll/clk_rate:45158401
> fout_gpll/clk_rate:533000000
> fout_mpll/clk_rate:1600000000
>
> * APLL is intended to be the parent of the 2 ARM cores and changes due
> to cpu load
> * EPLL is intended to be the parent for audio and changes dpending on
> audio playback rates.
> * GPLL is intended to be the parent of the GPU and changes due to gpu load
> * VPLL is intended to be the parent for video related things and could
> change depending on the LCD.
> * MPLL doesn't change a lot and is intended to be the parent for most things.
> * In some systems BPLL can be used for memory or GPU
>
> My main concern here is the CCF will end up deciding at some point
> that it should reparent some clock onto a PLL that is going to change
> a whole lot. Maybe the user will plug in an SD card that requests a
> frequency of 52MHz and at the moment we'll be running EPLL at 104 MHz
> so it will be a perfect match! ...but then the user wants to play
> audio at a different rate. The audio code assumes that it can mess
> with its clock and we've got code setup to call CLK_SET_RATE_PARENT
> all the way up to EPLL. That will really mess with the SD card.
> Really we'd rather just have the SD card clock always parented on the
> stable MPLL and it's OK if 52MHz gets rounded down to 50MHz.
>
> Of course, on another board maybe they don't have an audio codec and
> aren't using epll for audio and have realized that EPLL would be a
> perfect way to get their SD card to run 4% faster. It ought to work.
>
>
> I guess to summarize the above:
>
> * It seems like much of the muxing on exynos5250 is just too
> complicated to leave it to an simple automated algorithm.

I'd wager this will be true at the PLL-level for most complex SoCs.
Having a general re-muxing algorithm makes a lot more sense for simpler
cases like a fast 100MHz parent and a slow 32KHz parent, and the
requested rate makes it painfully obvious which one to pick. This is
less true for PLLs that can provide a very wide range of frequencies and
have lots of children.

This is analogous to lots of the other basic clock types where we try to
have common code that benefits a wide range of users, but if you need
something custom then you implement a new clock type/driver. If you
have a limited set of combinations that you want to support out of many
possible combinations (e.g. your "backup muxing options") then it might
be better for you to skip the remuxing algorithm and instead call
clk_set_parent explicitly from your clock driver, perhaps in your
clock's .set_rate and/or .set_parent callbacks.

> * It seems like we can't make muxing decisions on the SoC level.
> * Your automatic muxing patches don't hurt me and could be useful for
> _some_ of the muxing options, just not the top PLL ones.

For the time being you won't be affected by this until you start using
.determine_rate. Even then we have the flag which disables this
behavior.

>
> ...but the only place that leaves me for my muxing needs is the device
> tree. ...and as Mike pointed out on IRC the device tree should
> describe hardware, not policy. Ick.

This sounds like another vote for configtree ;-)

Regards,
Mike

>
> -Doug

2013-06-12 17:55:39

by Douglas Anderson

[permalink] [raw]
Subject: Re: [PATCH v4 5/5] clk: clk-mux: implement remuxing on set_rate

Mike,

On Wed, Jun 12, 2013 at 10:45 AM, Mike Turquette <[email protected]> wrote:

>> * It seems like we can't make muxing decisions on the SoC level.
>> * Your automatic muxing patches don't hurt me and could be useful for
>> _some_ of the muxing options, just not the top PLL ones.
>
> For the time being you won't be affected by this until you start using
> .determine_rate. Even then we have the flag which disables this
> behavior.

Yup, exactly! :) So I have no objections to the auto remuxing, it
just doesn't solve all of my problems.


>> ...but the only place that leaves me for my muxing needs is the device
>> tree. ...and as Mike pointed out on IRC the device tree should
>> describe hardware, not policy. Ick.
>
> This sounds like another vote for configtree ;-)

Yes. It sounds like for now we're just going to carry some patches to
setup our clocks, but a configtree seems like it would solve this type
of problem.

One question to raise: if we're going to need to come up with a
solution for defining parents for things like PLLs, does it decrease
the need for the auto-remuxing patches? AKA: if we use some type of
mechanism like configtree to specify muxing, would that be enough? I
don't know the answer, but just thought I'd raise the question...

-Doug

2013-06-12 18:07:54

by Mike Turquette

[permalink] [raw]
Subject: Re: [PATCH v4 5/5] clk: clk-mux: implement remuxing on set_rate

On Wed, Jun 12, 2013 at 10:55 AM, Doug Anderson <[email protected]> wrote:
> Mike,
>
> On Wed, Jun 12, 2013 at 10:45 AM, Mike Turquette <[email protected]> wrote:
>
>>> * It seems like we can't make muxing decisions on the SoC level.
>>> * Your automatic muxing patches don't hurt me and could be useful for
>>> _some_ of the muxing options, just not the top PLL ones.
>>
>> For the time being you won't be affected by this until you start using
>> .determine_rate. Even then we have the flag which disables this
>> behavior.
>
> Yup, exactly! :) So I have no objections to the auto remuxing, it
> just doesn't solve all of my problems.
>
>
>>> ...but the only place that leaves me for my muxing needs is the device
>>> tree. ...and as Mike pointed out on IRC the device tree should
>>> describe hardware, not policy. Ick.
>>
>> This sounds like another vote for configtree ;-)
>
> Yes. It sounds like for now we're just going to carry some patches to
> setup our clocks, but a configtree seems like it would solve this type
> of problem.
>
> One question to raise: if we're going to need to come up with a
> solution for defining parents for things like PLLs, does it decrease
> the need for the auto-remuxing patches? AKA: if we use some type of
> mechanism like configtree to specify muxing, would that be enough? I
> don't know the answer, but just thought I'd raise the question...

It's a good question. I can think of some cases where auto-muxing
would still be helpful. There are drivers out there that don't need to
know the details of the clock tree hierarchy and automagic muxing will
help remove those details from some of the drivers.

However I think that boot-time configuration of muxes that don't
switch (configtree), combined with custom mux clock code that
implements recommendations from HW engineers and the data sheet will
probably be more common than lots and lots of generic re-muxing. I
think that implementing those recommendations on which parent to use
is the problem Saravana was trying to solve with the priority list of
parents

How to encode that information in DT is a bit of a problem. I guess
one approach could be to create a mux table in DT and instead of
ordering items in that list based on bitfield index (which feel
natural to most programmers), instead let the order imply priority.
This is a tricky hack since we're *technically* only describing the
hardware with the mux table, but the priority encoding is silently
encoded. E.g:

clock: clock@4a008100 {
#clock-cells = <0>;
compatible = "mux-clock";
clocks = <&clock_foo>, <&clock_bar>, <&clock_baz>;
reg = <0x4a008100 0x4>;
mask = <0x3>;
shift = <0>;
table = <&clock_baz 0x2>, <&clock_foo 0x0>, <&clock_bar 0x1>;
};

Thoughts?

Regards,
Mike

>
> -Doug

2013-06-13 01:35:17

by Stephen Boyd

[permalink] [raw]
Subject: Re: [PATCH v4 0/5] clk: implement remuxing during set_rate

On 05/20/13 06:28, James Hogan wrote:
> This patchset adds support for automatic selection of the best parent
> for a clock mux, i.e. the one which can provide the closest clock rate
> to that requested. It can be disabled by a new CLK_SET_RATE_NO_REPARENT
> flag (which is set for all uses of clk_register_mux().
>
> This works by way of adding a new op, determine_rate, similar to
> round_rate but with an extra parameter to allow the clock driver to
> optionally select a different parent clock. This is used in
> clk_calc_new_rates to decide whether to initiate a set_parent operation.

I'd like to see this go into 3.11. We can tackle the larger problem that
Saravana brought up later on since we're no worse off than we already
are without these patches.

Feel free to add my reviewed-by to all patches.

Reviewed-by: Stephen Boyd <[email protected]>

--
Qualcomm Innovation Center, Inc. is a member of Code Aurora Forum,
hosted by The Linux Foundation

2013-06-13 14:31:58

by James Hogan

[permalink] [raw]
Subject: Re: [PATCH v4 3/5] clk: add support for clock reparent on set_rate

On 20/05/13 22:17, Stephen Boyd wrote:
> On 05/20/13 06:28, James Hogan wrote:
>> diff --git a/Documentation/clk.txt b/Documentation/clk.txt
>> index b9911c2..3110ba4 100644
>> --- a/Documentation/clk.txt
>> +++ b/Documentation/clk.txt
>> @@ -70,6 +70,10 @@ the operations defined in clk.h:
>> unsigned long parent_rate);
>> long (*round_rate)(struct clk_hw *hw, unsigned long,
>> unsigned long *);
>> + long (*determine_rate)(struct clk_hw *hw,
>> + unsigned long rate,
>> + unsigned long *best_parent_rate,
>> + struct clk **best_parent_clk);
>> 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);
>
> Can you update the clock hardware characteristics table as well?
>

Yep, I'll add the hunk below to the patch in the next version.

Cheers
James

@@ -179,26 +183,28 @@ mandatory, a cell marked as "n" implies that either including that
callback is invalid or otherwise unnecessary. Empty cells are either
optional or must be evaluated on a case-by-case basis.

- clock hardware characteristics
- -----------------------------------------------------------
- | gate | change rate | single parent | multiplexer | root |
- |------|-------------|---------------|-------------|------|
-.prepare | | | | | |
-.unprepare | | | | | |
- | | | | | |
-.enable | y | | | | |
-.disable | y | | | | |
-.is_enabled | y | | | | |
- | | | | | |
-.recalc_rate | | y | | | |
-.round_rate | | y | | | |
-.set_rate | | y | | | |
- | | | | | |
-.set_parent | | | n | y | n |
-.get_parent | | | n | y | n |
- | | | | | |
-.init | | | | | |
- -----------------------------------------------------------
+ clock hardware characteristics
+ -----------------------------------------------------------
+ | gate | change rate | single parent | multiplexer | root |
+ |------|-------------|---------------|-------------|------|
+.prepare | | | | | |
+.unprepare | | | | | |
+ | | | | | |
+.enable | y | | | | |
+.disable | y | | | | |
+.is_enabled | y | | | | |
+ | | | | | |
+.recalc_rate | | y | | | |
+.round_rate | | y [1] | | | |
+.determine_rate | | y [1] | | | |
+.set_rate | | y | | | |
+ | | | | | |
+.set_parent | | | n | y | n |
+.get_parent | | | n | y | n |
+ | | | | | |
+.init | | | | | |
+ -----------------------------------------------------------
+[1] either one of round_rate or determine_rate is required.

Finally, register your clock at run-time with a hardware-specific
registration function. This function simply populates struct clk_foo's

2013-06-13 15:02:33

by James Hogan

[permalink] [raw]
Subject: Re: [PATCH v4 5/5] clk: clk-mux: implement remuxing on set_rate

On 21/05/13 05:44, Saravana Kannan wrote:
> On 05/20/2013 06:28 AM, James Hogan wrote:
>> Implement clk-mux remuxing if the CLK_SET_RATE_NO_REPARENT flag isn't
>> set. This implements determine_rate for clk-mux to propagate to each
>> parent and to choose the best one (like clk-divider this chooses the
>> parent which provides the fastest rate <= the requested rate).
>>
>> The determine_rate op is implemented as a core helper function so that
>> it can be easily used by more complex clocks which incorporate muxes.
>>
>> Signed-off-by: James Hogan <[email protected]>
>> Cc: Mike Turquette <[email protected]>
>> Cc: [email protected]
>> ---
>> Changes in v4:
>>
>> * never pass NULL to determine_rate's best_parent_clk parameter.
>>
>> Changes in v3:
>>
>> * rename/invert CLK_SET_RATE_REMUX to CLK_SET_RATE_NO_REPARENT and move
>> to patch 3.
>>
>> drivers/clk/clk-mux.c | 1 +
>> drivers/clk/clk.c | 49
>> ++++++++++++++++++++++++++++++++++++++++++++
>> include/linux/clk-provider.h | 3 +++
>> 3 files changed, 53 insertions(+)
>>
>> diff --git a/drivers/clk/clk-mux.c b/drivers/clk/clk-mux.c
>> index 25b1734..cecfa01 100644
>> --- a/drivers/clk/clk-mux.c
>> +++ b/drivers/clk/clk-mux.c
>> @@ -100,6 +100,7 @@ static int clk_mux_set_parent(struct clk_hw *hw,
>> u8 index)
>> const struct clk_ops clk_mux_ops = {
>> .get_parent = clk_mux_get_parent,
>> .set_parent = clk_mux_set_parent,
>> + .determine_rate = __clk_mux_determine_rate,
>> };
>> EXPORT_SYMBOL_GPL(clk_mux_ops);
>>
>> diff --git a/drivers/clk/clk.c b/drivers/clk/clk.c
>> index 3ce4810..85b661d 100644
>> --- a/drivers/clk/clk.c
>> +++ b/drivers/clk/clk.c
>> @@ -692,6 +692,55 @@ struct clk *__clk_lookup(const char *name)
>> return NULL;
>> }
>>
>> +/*
>> + * Helper for finding best parent to provide a given frequency. This
>> can be used
>> + * directly as a determine_rate callback (e.g. for a mux), or from a
>> more
>> + * complex clock that may combine a mux with other operations.
>> + */
>> +long __clk_mux_determine_rate(struct clk_hw *hw, unsigned long rate,
>> + unsigned long *best_parent_rate,
>> + struct clk **best_parent_p)
>> +{
>> + struct clk *clk = hw->clk, *parent, *best_parent = NULL;
>> + int i, num_parents;
>> + unsigned long parent_rate, best = 0;
>> +
>> + /* if NO_REPARENT flag set, pass through to current parent */
>> + if (clk->flags & CLK_SET_RATE_NO_REPARENT) {
>> + parent = clk->parent;
>> + if (clk->flags & CLK_SET_RATE_PARENT)
>> + best = __clk_round_rate(parent, rate);
>> + else if (parent)
>> + best = __clk_get_rate(parent);
>> + else
>> + best = __clk_get_rate(clk);
>> + goto out;
>> + }
>> +
>> + /* find the parent that can provide the fastest rate <= rate */
>> + num_parents = clk->num_parents;
>> + for (i = 0; i < num_parents; i++) {
>
> While writing a similar code for our internal tree, I quickly came to
> the realization that, "all parents are equal, but some are more equal
> than others". The simplest example is a clock tree like this:
>
> Source -> Divider -> Mux
> Source -> Mux
>
> A rate of Y can be achieved for Mux by either running Source at Y and
> picking that input or running Source at Y * 2 and Divider set to div-2
> and picking the Divider input.
>
> The solution seems to be a priority list of parents. I'm sure there
> would be other reason (jitter, clock quality, etc) for a mux to pick one
> parent vs. another when both of them can provide the required rate.
>
> I think this loop should loop over parents based on their priority
> order. So, parents should become a struct of { clk, index } and have the
> parents listed in the order of priority. Well, at least in that long run
> that would be better to avoid messing up parent/index values. But for
> now, you could just have a priority array of index or parents.
>
> It might not fit 100% of the cases where two parents can provide the
> same rate, but it should fit most use cases.

Sorry for the delay replying.

What you say sounds reasonable. As Stephen Boyd suggested though, I'd
like to omit this from this patchset as its something that can be
tackled later.

Cheers
James

2013-06-13 15:19:05

by James Hogan

[permalink] [raw]
Subject: Re: [PATCH v4 5/5] clk: clk-mux: implement remuxing on set_rate

On 12/06/13 02:01, Doug Anderson wrote:
> Hi,
>
> Mike pointed me at this series since I'm running into parenting
> problems at the moment as well...
>
> On Mon, May 20, 2013 at 9:44 PM, Saravana Kannan <[email protected]> wrote:
>> While writing a similar code for our internal tree, I quickly came to the
>> realization that, "all parents are equal, but some are more equal than
>> others". The simplest example is a clock tree like this:
>>
>> Source -> Divider -> Mux
>> Source -> Mux
>>
>> A rate of Y can be achieved for Mux by either running Source at Y and
>> picking that input or running Source at Y * 2 and Divider set to div-2 and
>> picking the Divider input.
>>
>> The solution seems to be a priority list of parents. I'm sure there would be
>> other reason (jitter, clock quality, etc) for a mux to pick one parent vs.
>> another when both of them can provide the required rate.
>>
>> I think this loop should loop over parents based on their priority order.
>> So, parents should become a struct of { clk, index } and have the parents
>> listed in the order of priority. Well, at least in that long run that would
>> be better to avoid messing up parent/index values. But for now, you could
>> just have a priority array of index or parents.
>>
>> It might not fit 100% of the cases where two parents can provide the same
>> rate, but it should fit most use cases.
>
> I'm slightly worried about similar problems, but I don't have a really
> great solution.
>
> In my case I'm working on exynos5 hardware which has a bunch of PLLs
> and a crazy number of muxing options. Many of the muxing options are
> not really meant to be used but seem to have been added to the SoC as
> a "backup plan" of some sort. :-/ Most of the PLLs are intended to
> be used for one purpose and one purpose only though muxing options
> allow them to be used all over the place.
>
> For instance if I look at my current bootup of exynos5250-snow, I see:
>
> fout_apll/clk_rate:1700000000
> fout_bpll/clk_rate:800000000
> fout_cpll/clk_rate:333000000
> fout_epll/clk_rate:45158401
> fout_gpll/clk_rate:533000000
> fout_mpll/clk_rate:1600000000
>
> * APLL is intended to be the parent of the 2 ARM cores and changes due
> to cpu load
> * EPLL is intended to be the parent for audio and changes dpending on
> audio playback rates.
> * GPLL is intended to be the parent of the GPU and changes due to gpu load
> * VPLL is intended to be the parent for video related things and could
> change depending on the LCD.
> * MPLL doesn't change a lot and is intended to be the parent for most things.
> * In some systems BPLL can be used for memory or GPU
>
> My main concern here is the CCF will end up deciding at some point
> that it should reparent some clock onto a PLL that is going to change
> a whole lot. Maybe the user will plug in an SD card that requests a
> frequency of 52MHz and at the moment we'll be running EPLL at 104 MHz
> so it will be a perfect match! ...but then the user wants to play
> audio at a different rate. The audio code assumes that it can mess
> with its clock and we've got code setup to call CLK_SET_RATE_PARENT
> all the way up to EPLL. That will really mess with the SD card.
> Really we'd rather just have the SD card clock always parented on the
> stable MPLL and it's OK if 52MHz gets rounded down to 50MHz.

One possible approach to handling this is by making drivers register
clock rate change notifications, which can reject a clock rate change,
however I believe at the moment this would simply make the clock rate
change fail rather than finding an alternative change which isn't
rejected, so it may have limited use at the moment.

> Of course, on another board maybe they don't have an audio codec and
> aren't using epll for audio and have realized that EPLL would be a
> perfect way to get their SD card to run 4% faster. It ought to work.
>
>
> I guess to summarize the above:
>
> * It seems like much of the muxing on exynos5250 is just too
> complicated to leave it to an simple automated algorithm.
> * It seems like we can't make muxing decisions on the SoC level.
> * Your automatic muxing patches don't hurt me and could be useful for
> _some_ of the muxing options, just not the top PLL ones.
>
> ...but the only place that leaves me for my muxing needs is the device
> tree. ...and as Mike pointed out on IRC the device tree should
> describe hardware, not policy. Ick.

Yes, we have a similar situation with the TZ1090, except it may be worse
as some of the clocks/devices may be driven by different non-Linux cores
or hardware threads (e.g. the WiFi/DAB firmware drives the I2C/SPI bus
the tuner chip is connected to, and related clocks), in which case Linux
literally has no knowledge of whether the hardware is already in use
aside from some form of application-specific configuration (currently
munged into device tree due to the lack of an alternative).

Cheers
James