Commit e366fdd7 authored by James Hogan's avatar James Hogan Committed by Mike Turquette

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: default avatarJames Hogan <james.hogan@imgtec.com>
Reviewed-by: default avatarStephen Boyd <sboyd@codeaurora.org>
Cc: Mike Turquette <mturquette@linaro.org>
Cc: linux-arm-kernel@lists.infradead.org
Signed-off-by: default avatarMike Turquette <mturquette@linaro.org>
parent 819c1de3
...@@ -104,6 +104,7 @@ static int clk_mux_set_parent(struct clk_hw *hw, u8 index) ...@@ -104,6 +104,7 @@ static int clk_mux_set_parent(struct clk_hw *hw, u8 index)
const struct clk_ops clk_mux_ops = { const struct clk_ops clk_mux_ops = {
.get_parent = clk_mux_get_parent, .get_parent = clk_mux_get_parent,
.set_parent = clk_mux_set_parent, .set_parent = clk_mux_set_parent,
.determine_rate = __clk_mux_determine_rate,
}; };
EXPORT_SYMBOL_GPL(clk_mux_ops); EXPORT_SYMBOL_GPL(clk_mux_ops);
......
...@@ -692,6 +692,55 @@ struct clk *__clk_lookup(const char *name) ...@@ -692,6 +692,55 @@ struct clk *__clk_lookup(const char *name)
return NULL; 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 ***/ /*** clk api ***/
void __clk_unprepare(struct clk *clk) void __clk_unprepare(struct clk *clk)
......
...@@ -436,6 +436,9 @@ unsigned long __clk_get_flags(struct clk *clk); ...@@ -436,6 +436,9 @@ unsigned long __clk_get_flags(struct clk *clk);
bool __clk_is_prepared(struct clk *clk); bool __clk_is_prepared(struct clk *clk);
bool __clk_is_enabled(struct clk *clk); bool __clk_is_enabled(struct clk *clk);
struct clk *__clk_lookup(const char *name); 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 * FIXME clock api without lock protection
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment