Browse Source

clk: allow a clk divider with max divisor when zero

This commit allows certain Broadcom STB clock dividers to be used with
clk-divider.c.  It allows for a clock whose field value is the equal
to the divisor, execpt when the field value is zero, in which case the
divisor is 2^width.  For example, consider a divisor clock with a two
bit field:

value		divisor
0		4
1		1
2		2
3		3

Signed-off-by: Jim Quinlan <jim2101024@gmail.com>
Signed-off-by: Michael Turquette <mturquette@baylibre.com>
Jim Quinlan 10 years ago
parent
commit
afe76c8fd0
2 changed files with 15 additions and 5 deletions
  1. 11 5
      drivers/clk/clk-divider.c
  2. 4 0
      include/linux/clk-provider.h

+ 11 - 5
drivers/clk/clk-divider.c

@@ -78,12 +78,14 @@ static unsigned int _get_table_div(const struct clk_div_table *table,
 }
 }
 
 
 static unsigned int _get_div(const struct clk_div_table *table,
 static unsigned int _get_div(const struct clk_div_table *table,
-			     unsigned int val, unsigned long flags)
+			     unsigned int val, unsigned long flags, u8 width)
 {
 {
 	if (flags & CLK_DIVIDER_ONE_BASED)
 	if (flags & CLK_DIVIDER_ONE_BASED)
 		return val;
 		return val;
 	if (flags & CLK_DIVIDER_POWER_OF_TWO)
 	if (flags & CLK_DIVIDER_POWER_OF_TWO)
 		return 1 << val;
 		return 1 << val;
+	if (flags & CLK_DIVIDER_MAX_AT_ZERO)
+		return val ? val : div_mask(width) + 1;
 	if (table)
 	if (table)
 		return _get_table_div(table, val);
 		return _get_table_div(table, val);
 	return val + 1;
 	return val + 1;
@@ -101,12 +103,14 @@ static unsigned int _get_table_val(const struct clk_div_table *table,
 }
 }
 
 
 static unsigned int _get_val(const struct clk_div_table *table,
 static unsigned int _get_val(const struct clk_div_table *table,
-			     unsigned int div, unsigned long flags)
+			     unsigned int div, unsigned long flags, u8 width)
 {
 {
 	if (flags & CLK_DIVIDER_ONE_BASED)
 	if (flags & CLK_DIVIDER_ONE_BASED)
 		return div;
 		return div;
 	if (flags & CLK_DIVIDER_POWER_OF_TWO)
 	if (flags & CLK_DIVIDER_POWER_OF_TWO)
 		return __ffs(div);
 		return __ffs(div);
+	if (flags & CLK_DIVIDER_MAX_AT_ZERO)
+		return (div == div_mask(width) + 1) ? 0 : div;
 	if (table)
 	if (table)
 		return  _get_table_val(table, div);
 		return  _get_table_val(table, div);
 	return div - 1;
 	return div - 1;
@@ -117,9 +121,10 @@ unsigned long divider_recalc_rate(struct clk_hw *hw, unsigned long parent_rate,
 				  const struct clk_div_table *table,
 				  const struct clk_div_table *table,
 				  unsigned long flags)
 				  unsigned long flags)
 {
 {
+	struct clk_divider *divider = to_clk_divider(hw);
 	unsigned int div;
 	unsigned int div;
 
 
-	div = _get_div(table, val, flags);
+	div = _get_div(table, val, flags, divider->width);
 	if (!div) {
 	if (!div) {
 		WARN(!(flags & CLK_DIVIDER_ALLOW_ZERO),
 		WARN(!(flags & CLK_DIVIDER_ALLOW_ZERO),
 			"%s: Zero divisor and CLK_DIVIDER_ALLOW_ZERO not set\n",
 			"%s: Zero divisor and CLK_DIVIDER_ALLOW_ZERO not set\n",
@@ -351,7 +356,8 @@ static long clk_divider_round_rate(struct clk_hw *hw, unsigned long rate,
 	if (divider->flags & CLK_DIVIDER_READ_ONLY) {
 	if (divider->flags & CLK_DIVIDER_READ_ONLY) {
 		bestdiv = readl(divider->reg) >> divider->shift;
 		bestdiv = readl(divider->reg) >> divider->shift;
 		bestdiv &= div_mask(divider->width);
 		bestdiv &= div_mask(divider->width);
-		bestdiv = _get_div(divider->table, bestdiv, divider->flags);
+		bestdiv = _get_div(divider->table, bestdiv, divider->flags,
+			divider->width);
 		return DIV_ROUND_UP(*prate, bestdiv);
 		return DIV_ROUND_UP(*prate, bestdiv);
 	}
 	}
 
 
@@ -370,7 +376,7 @@ int divider_get_val(unsigned long rate, unsigned long parent_rate,
 	if (!_is_valid_div(table, div, flags))
 	if (!_is_valid_div(table, div, flags))
 		return -EINVAL;
 		return -EINVAL;
 
 
-	value = _get_val(table, div, flags);
+	value = _get_val(table, div, flags, width);
 
 
 	return min_t(unsigned int, value, div_mask(width));
 	return min_t(unsigned int, value, div_mask(width));
 }
 }

+ 4 - 0
include/linux/clk-provider.h

@@ -361,6 +361,9 @@ struct clk_div_table {
  *	to the closest integer instead of the up one.
  *	to the closest integer instead of the up one.
  * CLK_DIVIDER_READ_ONLY - The divider settings are preconfigured and should
  * CLK_DIVIDER_READ_ONLY - The divider settings are preconfigured and should
  *	not be changed by the clock framework.
  *	not be changed by the clock framework.
+ * CLK_DIVIDER_MAX_AT_ZERO - For dividers which are like CLK_DIVIDER_ONE_BASED
+ *	except when the value read from the register is zero, the divisor is
+ *	2^width of the field.
  */
  */
 struct clk_divider {
 struct clk_divider {
 	struct clk_hw	hw;
 	struct clk_hw	hw;
@@ -378,6 +381,7 @@ struct clk_divider {
 #define CLK_DIVIDER_HIWORD_MASK		BIT(3)
 #define CLK_DIVIDER_HIWORD_MASK		BIT(3)
 #define CLK_DIVIDER_ROUND_CLOSEST	BIT(4)
 #define CLK_DIVIDER_ROUND_CLOSEST	BIT(4)
 #define CLK_DIVIDER_READ_ONLY		BIT(5)
 #define CLK_DIVIDER_READ_ONLY		BIT(5)
+#define CLK_DIVIDER_MAX_AT_ZERO		BIT(6)
 
 
 extern const struct clk_ops clk_divider_ops;
 extern const struct clk_ops clk_divider_ops;