Commit 6d9252bd authored by Rajendra Nayak's avatar Rajendra Nayak Committed by Mike Turquette

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: default avatarRajendra Nayak <rnayak@ti.com>
Signed-off-by: default avatarMike Turquette <mturquette@linaro.org>
parent bd0a521e
...@@ -30,18 +30,50 @@ ...@@ -30,18 +30,50 @@
#define to_clk_divider(_hw) container_of(_hw, struct clk_divider, hw) #define to_clk_divider(_hw) container_of(_hw, struct clk_divider, hw)
#define div_mask(d) ((1 << (d->width)) - 1) #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, static unsigned long clk_divider_recalc_rate(struct clk_hw *hw,
unsigned long parent_rate) unsigned long parent_rate)
{ {
struct clk_divider *divider = to_clk_divider(hw); struct clk_divider *divider = to_clk_divider(hw);
unsigned int div; unsigned int div, val;
div = readl(divider->reg) >> divider->shift; val = readl(divider->reg) >> divider->shift;
div &= div_mask(divider); val &= div_mask(divider);
if (!(divider->flags & CLK_DIVIDER_ONE_BASED)) div = _get_div(divider, val);
div++; if (!div) {
WARN(1, "%s: Invalid divisor for clock %s\n", __func__,
__clk_get_name(hw->clk));
return parent_rate;
}
return parent_rate / div; return parent_rate / div;
} }
...@@ -62,10 +94,7 @@ static int clk_divider_bestdiv(struct clk_hw *hw, unsigned long rate, ...@@ -62,10 +94,7 @@ static int clk_divider_bestdiv(struct clk_hw *hw, unsigned long rate,
if (!rate) if (!rate)
rate = 1; rate = 1;
maxdiv = (1 << divider->width); maxdiv = _get_maxdiv(divider);
if (divider->flags & CLK_DIVIDER_ONE_BASED)
maxdiv--;
if (!(__clk_get_flags(hw->clk) & CLK_SET_RATE_PARENT)) { if (!(__clk_get_flags(hw->clk) & CLK_SET_RATE_PARENT)) {
parent_rate = *best_parent_rate; parent_rate = *best_parent_rate;
...@@ -82,6 +111,9 @@ static int clk_divider_bestdiv(struct clk_hw *hw, unsigned long rate, ...@@ -82,6 +111,9 @@ static int clk_divider_bestdiv(struct clk_hw *hw, unsigned long rate,
maxdiv = min(ULONG_MAX / rate, maxdiv); maxdiv = min(ULONG_MAX / rate, maxdiv);
for (i = 1; i <= maxdiv; i++) { 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), parent_rate = __clk_round_rate(__clk_get_parent(hw->clk),
MULT_ROUND_UP(rate, i)); MULT_ROUND_UP(rate, i));
now = parent_rate / i; now = parent_rate / i;
...@@ -93,9 +125,7 @@ static int clk_divider_bestdiv(struct clk_hw *hw, unsigned long rate, ...@@ -93,9 +125,7 @@ static int clk_divider_bestdiv(struct clk_hw *hw, unsigned long rate,
} }
if (!bestdiv) { if (!bestdiv) {
bestdiv = (1 << divider->width); bestdiv = _get_maxdiv(divider);
if (divider->flags & CLK_DIVIDER_ONE_BASED)
bestdiv--;
*best_parent_rate = __clk_round_rate(__clk_get_parent(hw->clk), 1); *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, ...@@ -115,24 +145,22 @@ static int clk_divider_set_rate(struct clk_hw *hw, unsigned long rate,
unsigned long parent_rate) unsigned long parent_rate)
{ {
struct clk_divider *divider = to_clk_divider(hw); struct clk_divider *divider = to_clk_divider(hw);
unsigned int div; unsigned int div, value;
unsigned long flags = 0; unsigned long flags = 0;
u32 val; u32 val;
div = parent_rate / rate; div = parent_rate / rate;
value = _get_val(divider, div);
if (!(divider->flags & CLK_DIVIDER_ONE_BASED)) if (value > div_mask(divider))
div--; value = div_mask(divider);
if (div > div_mask(divider))
div = div_mask(divider);
if (divider->lock) if (divider->lock)
spin_lock_irqsave(divider->lock, flags); spin_lock_irqsave(divider->lock, flags);
val = readl(divider->reg); val = readl(divider->reg);
val &= ~(div_mask(divider) << divider->shift); val &= ~(div_mask(divider) << divider->shift);
val |= div << divider->shift; val |= value << divider->shift;
writel(val, divider->reg); writel(val, divider->reg);
if (divider->lock) if (divider->lock)
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment