diff options
Diffstat (limited to 'drivers/clk/renesas/rcar-gen3-cpg.c')
-rw-r--r-- | drivers/clk/renesas/rcar-gen3-cpg.c | 64 |
1 files changed, 35 insertions, 29 deletions
diff --git a/drivers/clk/renesas/rcar-gen3-cpg.c b/drivers/clk/renesas/rcar-gen3-cpg.c index d25c8ba00a65..c97b647db9b6 100644 --- a/drivers/clk/renesas/rcar-gen3-cpg.c +++ b/drivers/clk/renesas/rcar-gen3-cpg.c @@ -114,18 +114,24 @@ static unsigned long cpg_z_clk_recalc_rate(struct clk_hw *hw, 32 * zclk->fixed_div); } -static long cpg_z_clk_round_rate(struct clk_hw *hw, unsigned long rate, - unsigned long *parent_rate) +static int cpg_z_clk_determine_rate(struct clk_hw *hw, + struct clk_rate_request *req) { struct cpg_z_clk *zclk = to_z_clk(hw); + unsigned int min_mult, max_mult, mult; unsigned long prate; - unsigned int mult; - prate = *parent_rate / zclk->fixed_div; - mult = div_u64(rate * 32ULL, prate); - mult = clamp(mult, 1U, 32U); + prate = req->best_parent_rate / zclk->fixed_div; + min_mult = max(div64_ul(req->min_rate * 32ULL, prate), 1ULL); + max_mult = min(div64_ul(req->max_rate * 32ULL, prate), 32ULL); + if (max_mult < min_mult) + return -EINVAL; + + mult = div64_ul(req->rate * 32ULL, prate); + mult = clamp(mult, min_mult, max_mult); - return (u64)prate * mult / 32; + req->rate = div_u64((u64)prate * mult, 32); + return 0; } static int cpg_z_clk_set_rate(struct clk_hw *hw, unsigned long rate, @@ -172,7 +178,7 @@ static int cpg_z_clk_set_rate(struct clk_hw *hw, unsigned long rate, static const struct clk_ops cpg_z_clk_ops = { .recalc_rate = cpg_z_clk_recalc_rate, - .round_rate = cpg_z_clk_round_rate, + .determine_rate = cpg_z_clk_determine_rate, .set_rate = cpg_z_clk_set_rate, }; @@ -309,44 +315,44 @@ static unsigned long cpg_sd_clock_recalc_rate(struct clk_hw *hw, clock->div_table[clock->cur_div_idx].div); } -static unsigned int cpg_sd_clock_calc_div(struct sd_clock *clock, - unsigned long rate, - unsigned long parent_rate) +static int cpg_sd_clock_determine_rate(struct clk_hw *hw, + struct clk_rate_request *req) { - unsigned long calc_rate, diff, diff_min = ULONG_MAX; - unsigned int i, best_div = 0; + unsigned long best_rate = ULONG_MAX, diff_min = ULONG_MAX; + struct sd_clock *clock = to_sd_clock(hw); + unsigned long calc_rate, diff; + unsigned int i; for (i = 0; i < clock->div_num; i++) { - calc_rate = DIV_ROUND_CLOSEST(parent_rate, + calc_rate = DIV_ROUND_CLOSEST(req->best_parent_rate, clock->div_table[i].div); - diff = calc_rate > rate ? calc_rate - rate : rate - calc_rate; + if (calc_rate < req->min_rate || calc_rate > req->max_rate) + continue; + + diff = calc_rate > req->rate ? calc_rate - req->rate + : req->rate - calc_rate; if (diff < diff_min) { - best_div = clock->div_table[i].div; + best_rate = calc_rate; diff_min = diff; } } - return best_div; -} - -static long cpg_sd_clock_round_rate(struct clk_hw *hw, unsigned long rate, - unsigned long *parent_rate) -{ - struct sd_clock *clock = to_sd_clock(hw); - unsigned int div = cpg_sd_clock_calc_div(clock, rate, *parent_rate); + if (best_rate == ULONG_MAX) + return -EINVAL; - return DIV_ROUND_CLOSEST(*parent_rate, div); + req->rate = best_rate; + return 0; } static int cpg_sd_clock_set_rate(struct clk_hw *hw, unsigned long rate, - unsigned long parent_rate) + unsigned long parent_rate) { struct sd_clock *clock = to_sd_clock(hw); - unsigned int div = cpg_sd_clock_calc_div(clock, rate, parent_rate); unsigned int i; for (i = 0; i < clock->div_num; i++) - if (div == clock->div_table[i].div) + if (rate == DIV_ROUND_CLOSEST(parent_rate, + clock->div_table[i].div)) break; if (i >= clock->div_num) @@ -366,7 +372,7 @@ static const struct clk_ops cpg_sd_clock_ops = { .disable = cpg_sd_clock_disable, .is_enabled = cpg_sd_clock_is_enabled, .recalc_rate = cpg_sd_clock_recalc_rate, - .round_rate = cpg_sd_clock_round_rate, + .determine_rate = cpg_sd_clock_determine_rate, .set_rate = cpg_sd_clock_set_rate, }; |