mirror of
https://github.com/torvalds/linux.git
synced 2024-11-11 06:31:49 +00:00
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:
parent
bd0a521e88
commit
6d9252bd9a
@ -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,
|
|||||||
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,
|
|||||||
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,
|
|||||||
}
|
}
|
||||||
|
|
||||||
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,
|
|||||||
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)
|
||||||
|
Loading…
Reference in New Issue
Block a user