mirror of
				https://github.com/torvalds/linux.git
				synced 2025-11-04 10:40:15 +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 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 a new issue