clk: Add support for power of two type dividers

Quite often dividers and the value programmed in the
register have a relation of 'power of two', something like
value	div
0	1
1	2
2	4
3	8...

Add support for such dividers as part of clk-divider.

The clk-divider flag 'CLK_DIVIDER_POWER_OF_TWO' should be used
to define such clocks.

Signed-off-by: Rajendra Nayak <rnayak@ti.com>
Signed-off-by: Mike Turquette <mturquette@linaro.org>
This commit is contained in:
Rajendra Nayak 2012-05-17 15:52:13 +05:30 committed by Mike Turquette
parent bd0a521e88
commit 6d9252bd9a

View File

@ -30,18 +30,50 @@
#define to_clk_divider(_hw) container_of(_hw, struct clk_divider, hw)
#define div_mask(d) ((1 << (d->width)) - 1)
#define is_power_of_two(i) !(i & ~i)
static unsigned int _get_maxdiv(struct clk_divider *divider)
{
if (divider->flags & CLK_DIVIDER_ONE_BASED)
return div_mask(divider);
if (divider->flags & CLK_DIVIDER_POWER_OF_TWO)
return 1 << div_mask(divider);
return div_mask(divider) + 1;
}
static unsigned int _get_div(struct clk_divider *divider, unsigned int val)
{
if (divider->flags & CLK_DIVIDER_ONE_BASED)
return val;
if (divider->flags & CLK_DIVIDER_POWER_OF_TWO)
return 1 << val;
return val + 1;
}
static unsigned int _get_val(struct clk_divider *divider, u8 div)
{
if (divider->flags & CLK_DIVIDER_ONE_BASED)
return div;
if (divider->flags & CLK_DIVIDER_POWER_OF_TWO)
return __ffs(div);
return div - 1;
}
static unsigned long clk_divider_recalc_rate(struct clk_hw *hw,
unsigned long parent_rate)
{
struct clk_divider *divider = to_clk_divider(hw);
unsigned int div;
unsigned int div, val;
div = readl(divider->reg) >> divider->shift;
div &= div_mask(divider);
val = readl(divider->reg) >> divider->shift;
val &= div_mask(divider);
if (!(divider->flags & CLK_DIVIDER_ONE_BASED))
div++;
div = _get_div(divider, val);
if (!div) {
WARN(1, "%s: Invalid divisor for clock %s\n", __func__,
__clk_get_name(hw->clk));
return parent_rate;
}
return parent_rate / div;
}
@ -62,10 +94,7 @@ static int clk_divider_bestdiv(struct clk_hw *hw, unsigned long rate,
if (!rate)
rate = 1;
maxdiv = (1 << divider->width);
if (divider->flags & CLK_DIVIDER_ONE_BASED)
maxdiv--;
maxdiv = _get_maxdiv(divider);
if (!(__clk_get_flags(hw->clk) & CLK_SET_RATE_PARENT)) {
parent_rate = *best_parent_rate;
@ -82,6 +111,9 @@ static int clk_divider_bestdiv(struct clk_hw *hw, unsigned long rate,
maxdiv = min(ULONG_MAX / rate, maxdiv);
for (i = 1; i <= maxdiv; i++) {
if ((divider->flags & CLK_DIVIDER_POWER_OF_TWO)
&& (!is_power_of_two(i)))
continue;
parent_rate = __clk_round_rate(__clk_get_parent(hw->clk),
MULT_ROUND_UP(rate, i));
now = parent_rate / i;
@ -93,9 +125,7 @@ static int clk_divider_bestdiv(struct clk_hw *hw, unsigned long rate,
}
if (!bestdiv) {
bestdiv = (1 << divider->width);
if (divider->flags & CLK_DIVIDER_ONE_BASED)
bestdiv--;
bestdiv = _get_maxdiv(divider);
*best_parent_rate = __clk_round_rate(__clk_get_parent(hw->clk), 1);
}
@ -115,24 +145,22 @@ static int clk_divider_set_rate(struct clk_hw *hw, unsigned long rate,
unsigned long parent_rate)
{
struct clk_divider *divider = to_clk_divider(hw);
unsigned int div;
unsigned int div, value;
unsigned long flags = 0;
u32 val;
div = parent_rate / rate;
value = _get_val(divider, div);
if (!(divider->flags & CLK_DIVIDER_ONE_BASED))
div--;
if (div > div_mask(divider))
div = div_mask(divider);
if (value > div_mask(divider))
value = div_mask(divider);
if (divider->lock)
spin_lock_irqsave(divider->lock, flags);
val = readl(divider->reg);
val &= ~(div_mask(divider) << divider->shift);
val |= div << divider->shift;
val |= value << divider->shift;
writel(val, divider->reg);
if (divider->lock)