ARM: OMAP2+: clock: allow omap2_dpll_round_rate() to round to next-lowest rate
Change the behavior of omap2_dpll_round_rate() to round to either the exact rate requested, or the next lowest rate that the clock is able to provide. This is not an ideal fix, but is intended to provide a relatively safe way for drivers to set PLL rates, until a better solution can be implemented. For the time being, omap3_noncore_dpll_set_rate() is still allowed to set its rate to something other than what the caller requested; but will warn when this occurs. Cc: Tomi Valkeinen <tomi.valkeinen@ti.com> Cc: Mike Turquette <mturquette@linaro.org> Signed-off-by: Paul Walmsley <paul@pwsan.com>
This commit is contained in:
Родитель
a497c3ba1d
Коммит
0a26344440
|
@ -293,10 +293,13 @@ long omap2_dpll_round_rate(struct clk_hw *hw, unsigned long target_rate,
|
|||
{
|
||||
struct clk_hw_omap *clk = to_clk_hw_omap(hw);
|
||||
int m, n, r, scaled_max_m;
|
||||
int min_delta_m = INT_MAX, min_delta_n = INT_MAX;
|
||||
unsigned long scaled_rt_rp;
|
||||
unsigned long new_rate = 0;
|
||||
struct dpll_data *dd;
|
||||
unsigned long ref_rate;
|
||||
long delta;
|
||||
long prev_min_delta = LONG_MAX;
|
||||
const char *clk_name;
|
||||
|
||||
if (!clk || !clk->dpll_data)
|
||||
|
@ -342,23 +345,34 @@ long omap2_dpll_round_rate(struct clk_hw *hw, unsigned long target_rate,
|
|||
if (r == DPLL_MULT_UNDERFLOW)
|
||||
continue;
|
||||
|
||||
/* skip rates above our target rate */
|
||||
delta = target_rate - new_rate;
|
||||
if (delta < 0)
|
||||
continue;
|
||||
|
||||
if (delta < prev_min_delta) {
|
||||
prev_min_delta = delta;
|
||||
min_delta_m = m;
|
||||
min_delta_n = n;
|
||||
}
|
||||
|
||||
pr_debug("clock: %s: m = %d: n = %d: new_rate = %lu\n",
|
||||
clk_name, m, n, new_rate);
|
||||
|
||||
if (target_rate == new_rate) {
|
||||
dd->last_rounded_m = m;
|
||||
dd->last_rounded_n = n;
|
||||
dd->last_rounded_rate = target_rate;
|
||||
if (delta == 0)
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (target_rate != new_rate) {
|
||||
if (prev_min_delta == LONG_MAX) {
|
||||
pr_debug("clock: %s: cannot round to rate %lu\n",
|
||||
clk_name, target_rate);
|
||||
return ~0;
|
||||
}
|
||||
|
||||
return target_rate;
|
||||
dd->last_rounded_m = min_delta_m;
|
||||
dd->last_rounded_n = min_delta_n;
|
||||
dd->last_rounded_rate = target_rate - prev_min_delta;
|
||||
|
||||
return dd->last_rounded_rate;
|
||||
}
|
||||
|
||||
|
|
|
@ -478,6 +478,7 @@ int omap3_noncore_dpll_set_rate(struct clk_hw *hw, unsigned long rate,
|
|||
{
|
||||
struct clk_hw_omap *clk = to_clk_hw_omap(hw);
|
||||
struct clk *new_parent = NULL;
|
||||
unsigned long rrate;
|
||||
u16 freqsel = 0;
|
||||
struct dpll_data *dd;
|
||||
int ret;
|
||||
|
@ -505,8 +506,16 @@ int omap3_noncore_dpll_set_rate(struct clk_hw *hw, unsigned long rate,
|
|||
__clk_prepare(dd->clk_ref);
|
||||
clk_enable(dd->clk_ref);
|
||||
|
||||
if (dd->last_rounded_rate != rate)
|
||||
rate = __clk_round_rate(hw->clk, rate);
|
||||
/* XXX this check is probably pointless in the CCF context */
|
||||
if (dd->last_rounded_rate != rate) {
|
||||
rrate = __clk_round_rate(hw->clk, rate);
|
||||
if (rrate != rate) {
|
||||
pr_warn("%s: %s: final rate %lu does not match desired rate %lu\n",
|
||||
__func__, __clk_get_name(hw->clk),
|
||||
rrate, rate);
|
||||
rate = rrate;
|
||||
}
|
||||
}
|
||||
|
||||
if (dd->last_rounded_rate == 0)
|
||||
return -EINVAL;
|
||||
|
|
Загрузка…
Ссылка в новой задаче