diff options
author | Uwe Kleine-König <u.kleine-koenig@pengutronix.de> | 2015-02-21 11:40:24 +0100 |
---|---|---|
committer | Greg Kroah-Hartman <gregkh@linuxfoundation.org> | 2015-04-19 10:10:26 +0200 |
commit | 765c20456c868989e179b14640ebe65347f2f57a (patch) | |
tree | 2ce9a464982b069aaebbb08331a4d4802f9a9d4c | |
parent | 4858bcfdbcddd3e319f7621bb8044ebec2bacbeb (diff) | |
download | linux-stable-765c20456c868989e179b14640ebe65347f2f57a.tar.gz linux-stable-765c20456c868989e179b14640ebe65347f2f57a.tar.bz2 linux-stable-765c20456c868989e179b14640ebe65347f2f57a.zip |
clk: divider: fix selection of divider when rounding to closest
commit 26bac95aa88c2b1747808c0b885abe7814c0165d upstream.
It's an invalid approach to assume that among two divider values
the one nearer the exact divider is the better one.
Assume a parent rate of 1000 Hz, a divider with CLK_DIVIDER_POWER_OF_TWO
and a target rate of 89 Hz. The exact divider is ~ 11.236 so 8 and 16
are the candidates to choose from yielding rates 125 Hz and 62.5 Hz
respectivly. While 8 is nearer to 11.236 than 16 is, the latter is still
the better divider as 62.5 is nearer to 89 than 125 is.
Fixes: 774b514390b1 (clk: divider: Add round to closest divider)
Signed-off-by: Uwe Kleine-König <u.kleine-koenig@pengutronix.de>
Acked-by: Sascha Hauer <s.hauer@pengutronix.de>
Acked-by: Maxime Coquelin <maxime.coquelin@st.com>
Signed-off-by: Michael Turquette <mturquette@linaro.org>
Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
-rw-r--r-- | drivers/clk/clk-divider.c | 6 |
1 files changed, 5 insertions, 1 deletions
diff --git a/drivers/clk/clk-divider.c b/drivers/clk/clk-divider.c index c0a842b335c5..0c0a865c1266 100644 --- a/drivers/clk/clk-divider.c +++ b/drivers/clk/clk-divider.c @@ -208,6 +208,7 @@ static int _div_round_closest(struct clk_divider *divider, unsigned long parent_rate, unsigned long rate) { int up, down, div; + unsigned long up_rate, down_rate; up = down = div = DIV_ROUND_CLOSEST(parent_rate, rate); @@ -219,7 +220,10 @@ static int _div_round_closest(struct clk_divider *divider, down = _round_down_table(divider->table, div); } - return (up - div) <= (div - down) ? up : down; + up_rate = DIV_ROUND_UP(parent_rate, up); + down_rate = DIV_ROUND_UP(parent_rate, down); + + return (rate - up_rate) <= (down_rate - rate) ? up : down; } static int _div_round(struct clk_divider *divider, unsigned long parent_rate, |