Skip to content

Commit

Permalink
clk: divider: Add round to closest divider
Browse files Browse the repository at this point in the history
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>
  • Loading branch information
Maxime COQUELIN authored and Mike Turquette committed Apr 30, 2014
1 parent 874f224 commit 774b514
Show file tree
Hide file tree
Showing 2 changed files with 70 additions and 2 deletions.
69 changes: 67 additions & 2 deletions drivers/clk/clk-divider.c
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,17 @@ static unsigned int _get_table_maxdiv(const struct clk_div_table *table)
return maxdiv;
}

static unsigned int _get_table_mindiv(const struct clk_div_table *table)
{
unsigned int mindiv = UINT_MAX;
const struct clk_div_table *clkt;

for (clkt = table; clkt->div; clkt++)
if (clkt->div < mindiv)
mindiv = clkt->div;
return mindiv;
}

static unsigned int _get_maxdiv(struct clk_divider *divider)
{
if (divider->flags & CLK_DIVIDER_ONE_BASED)
Expand Down Expand Up @@ -162,6 +173,24 @@ static int _round_up_table(const struct clk_div_table *table, int div)
return up;
}

static int _round_down_table(const struct clk_div_table *table, int div)
{
const struct clk_div_table *clkt;
int down = _get_table_mindiv(table);

for (clkt = table; clkt->div; clkt++) {
if (clkt->div == div)
return clkt->div;
else if (clkt->div > div)
continue;

if ((div - clkt->div) < (div - down))
down = clkt->div;
}

return down;
}

static int _div_round_up(struct clk_divider *divider,
unsigned long parent_rate, unsigned long rate)
{
Expand All @@ -175,6 +204,42 @@ static int _div_round_up(struct clk_divider *divider,
return div;
}

static int _div_round_closest(struct clk_divider *divider,
unsigned long parent_rate, unsigned long rate)
{
int up, down, div;

up = down = div = DIV_ROUND_CLOSEST(parent_rate, rate);

if (divider->flags & CLK_DIVIDER_POWER_OF_TWO) {
up = __roundup_pow_of_two(div);
down = __rounddown_pow_of_two(div);
} else if (divider->table) {
up = _round_up_table(divider->table, div);
down = _round_down_table(divider->table, div);
}

return (up - div) <= (div - down) ? up : down;
}

static int _div_round(struct clk_divider *divider, unsigned long parent_rate,
unsigned long rate)
{
if (divider->flags & CLK_DIVIDER_ROUND_CLOSEST)
return _div_round_closest(divider, parent_rate, rate);

return _div_round_up(divider, parent_rate, rate);
}

static bool _is_best_div(struct clk_divider *divider,
int rate, int now, int best)
{
if (divider->flags & CLK_DIVIDER_ROUND_CLOSEST)
return abs(rate - now) < abs(rate - best);

return now <= rate && now > best;
}

static int clk_divider_bestdiv(struct clk_hw *hw, unsigned long rate,
unsigned long *best_parent_rate)
{
Expand All @@ -190,7 +255,7 @@ static int clk_divider_bestdiv(struct clk_hw *hw, unsigned long rate,

if (!(__clk_get_flags(hw->clk) & CLK_SET_RATE_PARENT)) {
parent_rate = *best_parent_rate;
bestdiv = _div_round_up(divider, parent_rate, rate);
bestdiv = _div_round(divider, parent_rate, rate);
bestdiv = bestdiv == 0 ? 1 : bestdiv;
bestdiv = bestdiv > maxdiv ? maxdiv : bestdiv;
return bestdiv;
Expand All @@ -217,7 +282,7 @@ static int clk_divider_bestdiv(struct clk_hw *hw, unsigned long rate,
parent_rate = __clk_round_rate(__clk_get_parent(hw->clk),
MULT_ROUND_UP(rate, i));
now = DIV_ROUND_UP(parent_rate, i);
if (now <= rate && now > best) {
if (_is_best_div(divider, rate, now, best)) {
bestdiv = i;
best = now;
*best_parent_rate = parent_rate;
Expand Down
3 changes: 3 additions & 0 deletions include/linux/clk-provider.h
Original file line number Diff line number Diff line change
Expand Up @@ -312,6 +312,8 @@ struct clk_div_table {
* of this register, and mask of divider bits are in higher 16-bit of this
* register. While setting the divider bits, higher 16-bit should also be
* updated to indicate changing divider bits.
* CLK_DIVIDER_ROUND_CLOSEST - Makes the best calculated divider to be rounded
* to the closest integer instead of the up one.
*/
struct clk_divider {
struct clk_hw hw;
Expand All @@ -327,6 +329,7 @@ struct clk_divider {
#define CLK_DIVIDER_POWER_OF_TWO BIT(1)
#define CLK_DIVIDER_ALLOW_ZERO BIT(2)
#define CLK_DIVIDER_HIWORD_MASK BIT(3)
#define CLK_DIVIDER_ROUND_CLOSEST BIT(4)

extern const struct clk_ops clk_divider_ops;
struct clk *clk_register_divider(struct device *dev, const char *name,
Expand Down

0 comments on commit 774b514

Please sign in to comment.