Commit afe76c8f authored by Jim Quinlan's avatar Jim Quinlan Committed by Stephen Boyd

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: default avatarJim Quinlan <jim2101024@gmail.com>
Signed-off-by: default avatarMichael Turquette <mturquette@baylibre.com>
parent 25d4d341
...@@ -78,12 +78,14 @@ static unsigned int _get_table_div(const struct clk_div_table *table, ...@@ -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, ...@@ -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, ...@@ -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, ...@@ -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, ...@@ -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));
} }
......
...@@ -361,6 +361,9 @@ struct clk_div_table { ...@@ -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 { ...@@ -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;
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment