diff options
author | Maxime COQUELIN <maxime.coquelin@st.com> | 2014-01-29 11:24:07 -0500 |
---|---|---|
committer | Mike Turquette <mturquette@linaro.org> | 2014-04-30 14:51:26 -0400 |
commit | 774b514390b1eb8476bc759262790762bd1ef45a (patch) | |
tree | b283823eca82e2e5a71fbd90b203da950fd96764 /drivers/clk/clk-divider.c | |
parent | 874f224cc52d64c912087e68e3724be95ad80ee7 (diff) |
clk: divider: Add round to closest divider
In some cases, we want to be able to round the divider to the closest one,
instead than rounding up.
This patch adds a new CLK_DIVIDER_ROUND_CLOSEST flag to specify the divider
has to round to closest div, keeping rounding up as de default behaviour.
Signed-off-by: Maxime Coquelin <maxime.coquelin@st.com>
Signed-off-by: Mike Turquette <mturquette@linaro.org>
Diffstat (limited to 'drivers/clk/clk-divider.c')
-rw-r--r-- | drivers/clk/clk-divider.c | 69 |
1 files changed, 67 insertions, 2 deletions
diff --git a/drivers/clk/clk-divider.c b/drivers/clk/clk-divider.c index 4637697c139f..c57294563a98 100644 --- a/drivers/clk/clk-divider.c +++ b/drivers/clk/clk-divider.c | |||
@@ -43,6 +43,17 @@ static unsigned int _get_table_maxdiv(const struct clk_div_table *table) | |||
43 | return maxdiv; | 43 | return maxdiv; |
44 | } | 44 | } |
45 | 45 | ||
46 | static unsigned int _get_table_mindiv(const struct clk_div_table *table) | ||
47 | { | ||
48 | unsigned int mindiv = UINT_MAX; | ||
49 | const struct clk_div_table *clkt; | ||
50 | |||
51 | for (clkt = table; clkt->div; clkt++) | ||
52 | if (clkt->div < mindiv) | ||
53 | mindiv = clkt->div; | ||
54 | return mindiv; | ||
55 | } | ||
56 | |||
46 | static unsigned int _get_maxdiv(struct clk_divider *divider) | 57 | static unsigned int _get_maxdiv(struct clk_divider *divider) |
47 | { | 58 | { |
48 | if (divider->flags & CLK_DIVIDER_ONE_BASED) | 59 | if (divider->flags & CLK_DIVIDER_ONE_BASED) |
@@ -162,6 +173,24 @@ static int _round_up_table(const struct clk_div_table *table, int div) | |||
162 | return up; | 173 | return up; |
163 | } | 174 | } |
164 | 175 | ||
176 | static int _round_down_table(const struct clk_div_table *table, int div) | ||
177 | { | ||
178 | const struct clk_div_table *clkt; | ||
179 | int down = _get_table_mindiv(table); | ||
180 | |||
181 | for (clkt = table; clkt->div; clkt++) { | ||
182 | if (clkt->div == div) | ||
183 | return clkt->div; | ||
184 | else if (clkt->div > div) | ||
185 | continue; | ||
186 | |||
187 | if ((div - clkt->div) < (div - down)) | ||
188 | down = clkt->div; | ||
189 | } | ||
190 | |||
191 | return down; | ||
192 | } | ||
193 | |||
165 | static int _div_round_up(struct clk_divider *divider, | 194 | static int _div_round_up(struct clk_divider *divider, |
166 | unsigned long parent_rate, unsigned long rate) | 195 | unsigned long parent_rate, unsigned long rate) |
167 | { | 196 | { |
@@ -175,6 +204,42 @@ static int _div_round_up(struct clk_divider *divider, | |||
175 | return div; | 204 | return div; |
176 | } | 205 | } |
177 | 206 | ||
207 | static int _div_round_closest(struct clk_divider *divider, | ||
208 | unsigned long parent_rate, unsigned long rate) | ||
209 | { | ||
210 | int up, down, div; | ||
211 | |||
212 | up = down = div = DIV_ROUND_CLOSEST(parent_rate, rate); | ||
213 | |||
214 | if (divider->flags & CLK_DIVIDER_POWER_OF_TWO) { | ||
215 | up = __roundup_pow_of_two(div); | ||
216 | down = __rounddown_pow_of_two(div); | ||
217 | } else if (divider->table) { | ||
218 | up = _round_up_table(divider->table, div); | ||
219 | down = _round_down_table(divider->table, div); | ||
220 | } | ||
221 | |||
222 | return (up - div) <= (div - down) ? up : down; | ||
223 | } | ||
224 | |||
225 | static int _div_round(struct clk_divider *divider, unsigned long parent_rate, | ||
226 | unsigned long rate) | ||
227 | { | ||
228 | if (divider->flags & CLK_DIVIDER_ROUND_CLOSEST) | ||
229 | return _div_round_closest(divider, parent_rate, rate); | ||
230 | |||
231 | return _div_round_up(divider, parent_rate, rate); | ||
232 | } | ||
233 | |||
234 | static bool _is_best_div(struct clk_divider *divider, | ||
235 | int rate, int now, int best) | ||
236 | { | ||
237 | if (divider->flags & CLK_DIVIDER_ROUND_CLOSEST) | ||
238 | return abs(rate - now) < abs(rate - best); | ||
239 | |||
240 | return now <= rate && now > best; | ||
241 | } | ||
242 | |||
178 | static int clk_divider_bestdiv(struct clk_hw *hw, unsigned long rate, | 243 | static int clk_divider_bestdiv(struct clk_hw *hw, unsigned long rate, |
179 | unsigned long *best_parent_rate) | 244 | unsigned long *best_parent_rate) |
180 | { | 245 | { |
@@ -190,7 +255,7 @@ static int clk_divider_bestdiv(struct clk_hw *hw, unsigned long rate, | |||
190 | 255 | ||
191 | if (!(__clk_get_flags(hw->clk) & CLK_SET_RATE_PARENT)) { | 256 | if (!(__clk_get_flags(hw->clk) & CLK_SET_RATE_PARENT)) { |
192 | parent_rate = *best_parent_rate; | 257 | parent_rate = *best_parent_rate; |
193 | bestdiv = _div_round_up(divider, parent_rate, rate); | 258 | bestdiv = _div_round(divider, parent_rate, rate); |
194 | bestdiv = bestdiv == 0 ? 1 : bestdiv; | 259 | bestdiv = bestdiv == 0 ? 1 : bestdiv; |
195 | bestdiv = bestdiv > maxdiv ? maxdiv : bestdiv; | 260 | bestdiv = bestdiv > maxdiv ? maxdiv : bestdiv; |
196 | return bestdiv; | 261 | return bestdiv; |
@@ -217,7 +282,7 @@ static int clk_divider_bestdiv(struct clk_hw *hw, unsigned long rate, | |||
217 | parent_rate = __clk_round_rate(__clk_get_parent(hw->clk), | 282 | parent_rate = __clk_round_rate(__clk_get_parent(hw->clk), |
218 | MULT_ROUND_UP(rate, i)); | 283 | MULT_ROUND_UP(rate, i)); |
219 | now = DIV_ROUND_UP(parent_rate, i); | 284 | now = DIV_ROUND_UP(parent_rate, i); |
220 | if (now <= rate && now > best) { | 285 | if (_is_best_div(divider, rate, now, best)) { |
221 | bestdiv = i; | 286 | bestdiv = i; |
222 | best = now; | 287 | best = now; |
223 | *best_parent_rate = parent_rate; | 288 | *best_parent_rate = parent_rate; |