mirror of
				https://github.com/torvalds/linux.git
				synced 2025-11-04 02:30:34 +02: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
					
				
					 1 changed files with 47 additions and 19 deletions
				
			
		| 
						 | 
				
			
			@ -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)
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
		Loading…
	
		Reference in a new issue