[PATCH 1/2] clk: Add support for power of two type dividers

Rajendra Nayak rnayak at ti.com
Thu May 17 06:22:13 EDT 2012


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 at ti.com>
---
 drivers/clk/clk-divider.c |   66 ++++++++++++++++++++++++++++++++-------------
 1 files changed, 47 insertions(+), 19 deletions(-)

diff --git a/drivers/clk/clk-divider.c b/drivers/clk/clk-divider.c
index 8ea11b4..e548c43 100644
--- a/drivers/clk/clk-divider.c
+++ b/drivers/clk/clk-divider.c
@@ -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)
-- 
1.7.1




More information about the linux-arm-kernel mailing list