diff options
Diffstat (limited to 'drivers/clk/imx/clk-pfdv2.c')
-rw-r--r-- | drivers/clk/imx/clk-pfdv2.c | 61 |
1 files changed, 43 insertions, 18 deletions
diff --git a/drivers/clk/imx/clk-pfdv2.c b/drivers/clk/imx/clk-pfdv2.c index de93ce73101b..78e1f7641aaa 100644 --- a/drivers/clk/imx/clk-pfdv2.c +++ b/drivers/clk/imx/clk-pfdv2.c @@ -98,26 +98,45 @@ static unsigned long clk_pfdv2_recalc_rate(struct clk_hw *hw, return tmp; } -static long clk_pfdv2_round_rate(struct clk_hw *hw, unsigned long rate, - unsigned long *prate) +static int clk_pfdv2_determine_rate(struct clk_hw *hw, + struct clk_rate_request *req) { - u64 tmp = *prate; + unsigned long parent_rates[] = { + 480000000, + 528000000, + req->best_parent_rate + }; + unsigned long best_rate = -1UL, rate = req->rate; + unsigned long best_parent_rate = req->best_parent_rate; + u64 tmp; u8 frac; + int i; + + for (i = 0; i < ARRAY_SIZE(parent_rates); i++) { + tmp = parent_rates[i]; + tmp = tmp * 18 + rate / 2; + do_div(tmp, rate); + frac = tmp; + + if (frac < 12) + frac = 12; + else if (frac > 35) + frac = 35; + + tmp = parent_rates[i]; + tmp *= 18; + do_div(tmp, frac); + + if (abs(tmp - req->rate) < abs(best_rate - req->rate)) { + best_rate = tmp; + best_parent_rate = parent_rates[i]; + } + } - tmp = tmp * 18 + rate / 2; - do_div(tmp, rate); - frac = tmp; - - if (frac < 12) - frac = 12; - else if (frac > 35) - frac = 35; - - tmp = *prate; - tmp *= 18; - do_div(tmp, frac); + req->best_parent_rate = best_parent_rate; + req->rate = best_rate; - return tmp; + return 0; } static int clk_pfdv2_is_enabled(struct clk_hw *hw) @@ -139,6 +158,12 @@ static int clk_pfdv2_set_rate(struct clk_hw *hw, unsigned long rate, u32 val; u8 frac; + if (!rate) + return -EINVAL; + + /* PFD can NOT change rate without gating */ + WARN_ON(clk_pfdv2_is_enabled(hw)); + tmp = tmp * 18 + rate / 2; do_div(tmp, rate); frac = tmp; @@ -161,7 +186,7 @@ static const struct clk_ops clk_pfdv2_ops = { .enable = clk_pfdv2_enable, .disable = clk_pfdv2_disable, .recalc_rate = clk_pfdv2_recalc_rate, - .round_rate = clk_pfdv2_round_rate, + .determine_rate = clk_pfdv2_determine_rate, .set_rate = clk_pfdv2_set_rate, .is_enabled = clk_pfdv2_is_enabled, }; @@ -189,7 +214,7 @@ struct clk_hw *imx_clk_hw_pfdv2(const char *name, const char *parent_name, init.ops = &clk_pfdv2_ops; init.parent_names = &parent_name; init.num_parents = 1; - init.flags = CLK_SET_RATE_GATE; + init.flags = CLK_SET_RATE_GATE | CLK_SET_RATE_PARENT; pfd->hw.init = &init; |