diff options
Diffstat (limited to 'drivers/clk/clk.c')
-rw-r--r-- | drivers/clk/clk.c | 179 |
1 files changed, 99 insertions, 80 deletions
diff --git a/drivers/clk/clk.c b/drivers/clk/clk.c index 7873151a7ff8..bd6dfbe04cf0 100644 --- a/drivers/clk/clk.c +++ b/drivers/clk/clk.c @@ -436,28 +436,31 @@ static bool mux_is_better_rate(unsigned long rate, unsigned long now, return now <= rate && now > best; } -static long -clk_mux_determine_rate_flags(struct clk_hw *hw, unsigned long rate, - unsigned long min_rate, - unsigned long max_rate, - unsigned long *best_parent_rate, - struct clk_hw **best_parent_p, +static int +clk_mux_determine_rate_flags(struct clk_hw *hw, struct clk_rate_request *req, unsigned long flags) { struct clk_core *core = hw->core, *parent, *best_parent = NULL; - int i, num_parents; - unsigned long parent_rate, best = 0; + int i, num_parents, ret; + unsigned long best = 0; + struct clk_rate_request parent_req = *req; /* if NO_REPARENT flag set, pass through to current parent */ if (core->flags & CLK_SET_RATE_NO_REPARENT) { parent = core->parent; - if (core->flags & CLK_SET_RATE_PARENT) - best = __clk_determine_rate(parent ? parent->hw : NULL, - rate, min_rate, max_rate); - else if (parent) + if (core->flags & CLK_SET_RATE_PARENT) { + ret = __clk_determine_rate(parent ? parent->hw : NULL, + &parent_req); + if (ret) + return ret; + + best = parent_req.rate; + } else if (parent) { best = clk_core_get_rate_nolock(parent); - else + } else { best = clk_core_get_rate_nolock(core); + } + goto out; } @@ -467,24 +470,33 @@ clk_mux_determine_rate_flags(struct clk_hw *hw, unsigned long rate, parent = clk_core_get_parent_by_index(core, i); if (!parent) continue; - if (core->flags & CLK_SET_RATE_PARENT) - parent_rate = __clk_determine_rate(parent->hw, rate, - min_rate, - max_rate); - else - parent_rate = clk_core_get_rate_nolock(parent); - if (mux_is_better_rate(rate, parent_rate, best, flags)) { + + if (core->flags & CLK_SET_RATE_PARENT) { + parent_req = *req; + ret = __clk_determine_rate(parent->hw, &parent_req); + if (ret) + continue; + } else { + parent_req.rate = clk_core_get_rate_nolock(parent); + } + + if (mux_is_better_rate(req->rate, parent_req.rate, + best, flags)) { best_parent = parent; - best = parent_rate; + best = parent_req.rate; } } + if (!best_parent) + return -EINVAL; + out: if (best_parent) - *best_parent_p = best_parent->hw; - *best_parent_rate = best; + req->best_parent_hw = best_parent->hw; + req->best_parent_rate = best; + req->rate = best; - return best; + return 0; } struct clk *__clk_lookup(const char *name) @@ -515,28 +527,17 @@ static void clk_core_get_boundaries(struct clk_core *core, * 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 min_rate, - unsigned long max_rate, - unsigned long *best_parent_rate, - struct clk_hw **best_parent_p) +int __clk_mux_determine_rate(struct clk_hw *hw, + struct clk_rate_request *req) { - return clk_mux_determine_rate_flags(hw, rate, min_rate, max_rate, - best_parent_rate, - best_parent_p, 0); + return clk_mux_determine_rate_flags(hw, req, 0); } EXPORT_SYMBOL_GPL(__clk_mux_determine_rate); -long __clk_mux_determine_rate_closest(struct clk_hw *hw, unsigned long rate, - unsigned long min_rate, - unsigned long max_rate, - unsigned long *best_parent_rate, - struct clk_hw **best_parent_p) +int __clk_mux_determine_rate_closest(struct clk_hw *hw, + struct clk_rate_request *req) { - return clk_mux_determine_rate_flags(hw, rate, min_rate, max_rate, - best_parent_rate, - best_parent_p, - CLK_MUX_ROUND_CLOSEST); + return clk_mux_determine_rate_flags(hw, req, CLK_MUX_ROUND_CLOSEST); } EXPORT_SYMBOL_GPL(__clk_mux_determine_rate_closest); @@ -759,14 +760,11 @@ int clk_enable(struct clk *clk) } EXPORT_SYMBOL_GPL(clk_enable); -static unsigned long clk_core_round_rate_nolock(struct clk_core *core, - unsigned long rate, - unsigned long min_rate, - unsigned long max_rate) +static int clk_core_round_rate_nolock(struct clk_core *core, + struct clk_rate_request *req) { - unsigned long parent_rate = 0; struct clk_core *parent; - struct clk_hw *parent_hw; + long rate; lockdep_assert_held(&prepare_lock); @@ -774,21 +772,30 @@ static unsigned long clk_core_round_rate_nolock(struct clk_core *core, return 0; parent = core->parent; - if (parent) - parent_rate = parent->rate; + if (parent) { + req->best_parent_hw = parent->hw; + req->best_parent_rate = parent->rate; + } else { + req->best_parent_hw = NULL; + req->best_parent_rate = 0; + } if (core->ops->determine_rate) { - parent_hw = parent ? parent->hw : NULL; - return core->ops->determine_rate(core->hw, rate, - min_rate, max_rate, - &parent_rate, &parent_hw); - } else if (core->ops->round_rate) - return core->ops->round_rate(core->hw, rate, &parent_rate); - else if (core->flags & CLK_SET_RATE_PARENT) - return clk_core_round_rate_nolock(core->parent, rate, min_rate, - max_rate); - else - return core->rate; + return core->ops->determine_rate(core->hw, req); + } else if (core->ops->round_rate) { + rate = core->ops->round_rate(core->hw, req->rate, + &req->best_parent_rate); + if (rate < 0) + return rate; + + req->rate = rate; + } else if (core->flags & CLK_SET_RATE_PARENT) { + return clk_core_round_rate_nolock(parent, req); + } else { + req->rate = core->rate; + } + + return 0; } /** @@ -800,15 +807,14 @@ static unsigned long clk_core_round_rate_nolock(struct clk_core *core, * * Useful for clk_ops such as .set_rate and .determine_rate. */ -unsigned long __clk_determine_rate(struct clk_hw *hw, - unsigned long rate, - unsigned long min_rate, - unsigned long max_rate) +int __clk_determine_rate(struct clk_hw *hw, struct clk_rate_request *req) { - if (!hw) + if (!hw) { + req->rate = 0; return 0; + } - return clk_core_round_rate_nolock(hw->core, rate, min_rate, max_rate); + return clk_core_round_rate_nolock(hw->core, req); } EXPORT_SYMBOL_GPL(__clk_determine_rate); @@ -821,15 +827,20 @@ EXPORT_SYMBOL_GPL(__clk_determine_rate); */ unsigned long __clk_round_rate(struct clk *clk, unsigned long rate) { - unsigned long min_rate; - unsigned long max_rate; + struct clk_rate_request req; + int ret; if (!clk) return 0; - clk_core_get_boundaries(clk->core, &min_rate, &max_rate); + clk_core_get_boundaries(clk->core, &req.min_rate, &req.max_rate); + req.rate = rate; + + ret = clk_core_round_rate_nolock(clk->core, &req); + if (ret) + return 0; - return clk_core_round_rate_nolock(clk->core, rate, min_rate, max_rate); + return req.rate; } EXPORT_SYMBOL_GPL(__clk_round_rate); @@ -1249,7 +1260,6 @@ static struct clk_core *clk_calc_new_rates(struct clk_core *core, { struct clk_core *top = core; struct clk_core *old_parent, *parent; - struct clk_hw *parent_hw; unsigned long best_parent_rate = 0; unsigned long new_rate; unsigned long min_rate; @@ -1270,20 +1280,29 @@ static struct clk_core *clk_calc_new_rates(struct clk_core *core, /* find the closest rate and parent clk/rate */ if (core->ops->determine_rate) { - parent_hw = parent ? parent->hw : NULL; - ret = core->ops->determine_rate(core->hw, rate, - min_rate, - max_rate, - &best_parent_rate, - &parent_hw); + struct clk_rate_request req; + + req.rate = rate; + req.min_rate = min_rate; + req.max_rate = max_rate; + if (parent) { + req.best_parent_hw = parent->hw; + req.best_parent_rate = parent->rate; + } else { + req.best_parent_hw = NULL; + req.best_parent_rate = 0; + } + + ret = core->ops->determine_rate(core->hw, &req); if (ret < 0) return NULL; - new_rate = ret; - parent = parent_hw ? parent_hw->core : NULL; + best_parent_rate = req.best_parent_rate; + new_rate = req.rate; + parent = req.best_parent_hw ? req.best_parent_hw->core : NULL; } else if (core->ops->round_rate) { ret = core->ops->round_rate(core->hw, rate, - &best_parent_rate); + &best_parent_rate); if (ret < 0) return NULL; |