aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--drivers/clk/clk-divider.c125
-rw-r--r--include/linux/clk-private.h20
-rw-r--r--include/linux/clk-provider.h12
3 files changed, 139 insertions, 18 deletions
diff --git a/drivers/clk/clk-divider.c b/drivers/clk/clk-divider.c
index e548c4328f3c..02a4da98176b 100644
--- a/drivers/clk/clk-divider.c
+++ b/drivers/clk/clk-divider.c
@@ -32,30 +32,69 @@
#define div_mask(d) ((1 << (d->width)) - 1)
#define is_power_of_two(i) !(i & ~i)
+static unsigned int _get_table_maxdiv(const struct clk_div_table *table)
+{
+ unsigned int maxdiv = 0;
+ const struct clk_div_table *clkt;
+
+ for (clkt = table; clkt->div; clkt++)
+ if (clkt->div > maxdiv)
+ maxdiv = clkt->div;
+ return maxdiv;
+}
+
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);
+ if (divider->table)
+ return _get_table_maxdiv(divider->table);
return div_mask(divider) + 1;
}
+static unsigned int _get_table_div(const struct clk_div_table *table,
+ unsigned int val)
+{
+ const struct clk_div_table *clkt;
+
+ for (clkt = table; clkt->div; clkt++)
+ if (clkt->val == val)
+ return clkt->div;
+ return 0;
+}
+
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;
+ if (divider->table)
+ return _get_table_div(divider->table, val);
return val + 1;
}
+static unsigned int _get_table_val(const struct clk_div_table *table,
+ unsigned int div)
+{
+ const struct clk_div_table *clkt;
+
+ for (clkt = table; clkt->div; clkt++)
+ if (clkt->div == div)
+ return clkt->val;
+ return 0;
+}
+
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);
+ if (divider->table)
+ return _get_table_val(divider->table, div);
return div - 1;
}
@@ -84,6 +123,26 @@ static unsigned long clk_divider_recalc_rate(struct clk_hw *hw,
*/
#define MULT_ROUND_UP(r, m) ((r) * (m) + (m) - 1)
+static bool _is_valid_table_div(const struct clk_div_table *table,
+ unsigned int div)
+{
+ const struct clk_div_table *clkt;
+
+ for (clkt = table; clkt->div; clkt++)
+ if (clkt->div == div)
+ return true;
+ return false;
+}
+
+static bool _is_valid_div(struct clk_divider *divider, unsigned int div)
+{
+ if (divider->flags & CLK_DIVIDER_POWER_OF_TWO)
+ return is_power_of_two(div);
+ if (divider->table)
+ return _is_valid_table_div(divider->table, div);
+ return true;
+}
+
static int clk_divider_bestdiv(struct clk_hw *hw, unsigned long rate,
unsigned long *best_parent_rate)
{
@@ -111,8 +170,7 @@ 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)))
+ if (!_is_valid_div(divider, i))
continue;
parent_rate = __clk_round_rate(__clk_get_parent(hw->clk),
MULT_ROUND_UP(rate, i));
@@ -176,22 +234,11 @@ const struct clk_ops clk_divider_ops = {
};
EXPORT_SYMBOL_GPL(clk_divider_ops);
-/**
- * clk_register_divider - register a divider clock with the clock framework
- * @dev: device registering this clock
- * @name: name of this clock
- * @parent_name: name of clock's parent
- * @flags: framework-specific flags
- * @reg: register address to adjust divider
- * @shift: number of bits to shift the bitfield
- * @width: width of the bitfield
- * @clk_divider_flags: divider-specific flags for this clock
- * @lock: shared register lock for this clock
- */
-struct clk *clk_register_divider(struct device *dev, const char *name,
+static struct clk *_register_divider(struct device *dev, const char *name,
const char *parent_name, unsigned long flags,
void __iomem *reg, u8 shift, u8 width,
- u8 clk_divider_flags, spinlock_t *lock)
+ u8 clk_divider_flags, const struct clk_div_table *table,
+ spinlock_t *lock)
{
struct clk_divider *div;
struct clk *clk;
@@ -217,6 +264,7 @@ struct clk *clk_register_divider(struct device *dev, const char *name,
div->flags = clk_divider_flags;
div->lock = lock;
div->hw.init = &init;
+ div->table = table;
/* register the clock */
clk = clk_register(dev, &div->hw);
@@ -226,3 +274,48 @@ struct clk *clk_register_divider(struct device *dev, const char *name,
return clk;
}
+
+/**
+ * clk_register_divider - register a divider clock with the clock framework
+ * @dev: device registering this clock
+ * @name: name of this clock
+ * @parent_name: name of clock's parent
+ * @flags: framework-specific flags
+ * @reg: register address to adjust divider
+ * @shift: number of bits to shift the bitfield
+ * @width: width of the bitfield
+ * @clk_divider_flags: divider-specific flags for this clock
+ * @lock: shared register lock for this clock
+ */
+struct clk *clk_register_divider(struct device *dev, const char *name,
+ const char *parent_name, unsigned long flags,
+ void __iomem *reg, u8 shift, u8 width,
+ u8 clk_divider_flags, spinlock_t *lock)
+{
+ return _register_divider(dev, name, parent_name, flags, reg, shift,
+ width, clk_divider_flags, NULL, lock);
+}
+
+/**
+ * clk_register_divider_table - register a table based divider clock with
+ * the clock framework
+ * @dev: device registering this clock
+ * @name: name of this clock
+ * @parent_name: name of clock's parent
+ * @flags: framework-specific flags
+ * @reg: register address to adjust divider
+ * @shift: number of bits to shift the bitfield
+ * @width: width of the bitfield
+ * @clk_divider_flags: divider-specific flags for this clock
+ * @table: array of divider/value pairs ending with a div set to 0
+ * @lock: shared register lock for this clock
+ */
+struct clk *clk_register_divider_table(struct device *dev, const char *name,
+ const char *parent_name, unsigned long flags,
+ void __iomem *reg, u8 shift, u8 width,
+ u8 clk_divider_flags, const struct clk_div_table *table,
+ spinlock_t *lock)
+{
+ return _register_divider(dev, name, parent_name, flags, reg, shift,
+ width, clk_divider_flags, table, lock);
+}
diff --git a/include/linux/clk-private.h b/include/linux/clk-private.h
index eb3f84bc5325..cc9972d1429c 100644
--- a/include/linux/clk-private.h
+++ b/include/linux/clk-private.h
@@ -103,9 +103,9 @@ struct clk {
DEFINE_CLK(_name, clk_gate_ops, _flags, \
_name##_parent_names, _name##_parents);
-#define DEFINE_CLK_DIVIDER(_name, _parent_name, _parent_ptr, \
+#define _DEFINE_CLK_DIVIDER(_name, _parent_name, _parent_ptr, \
_flags, _reg, _shift, _width, \
- _divider_flags, _lock) \
+ _divider_flags, _table, _lock) \
static struct clk _name; \
static const char *_name##_parent_names[] = { \
_parent_name, \
@@ -121,11 +121,27 @@ struct clk {
.shift = _shift, \
.width = _width, \
.flags = _divider_flags, \
+ .table = _table, \
.lock = _lock, \
}; \
DEFINE_CLK(_name, clk_divider_ops, _flags, \
_name##_parent_names, _name##_parents);
+#define DEFINE_CLK_DIVIDER(_name, _parent_name, _parent_ptr, \
+ _flags, _reg, _shift, _width, \
+ _divider_flags, _lock) \
+ _DEFINE_CLK_DIVIDER(_name, _parent_name, _parent_ptr, \
+ _flags, _reg, _shift, _width, \
+ _divider_flags, NULL, _lock)
+
+#define DEFINE_CLK_DIVIDER_TABLE(_name, _parent_name, \
+ _parent_ptr, _flags, _reg, \
+ _shift, _width, _divider_flags, \
+ _table, _lock) \
+ _DEFINE_CLK_DIVIDER(_name, _parent_name, _parent_ptr, \
+ _flags, _reg, _shift, _width, \
+ _divider_flags, _table, _lock) \
+
#define DEFINE_CLK_MUX(_name, _parent_names, _parents, _flags, \
_reg, _shift, _width, \
_mux_flags, _lock) \
diff --git a/include/linux/clk-provider.h b/include/linux/clk-provider.h
index 4a0b483986c3..79caee9f1489 100644
--- a/include/linux/clk-provider.h
+++ b/include/linux/clk-provider.h
@@ -203,6 +203,11 @@ struct clk *clk_register_gate(struct device *dev, const char *name,
void __iomem *reg, u8 bit_idx,
u8 clk_gate_flags, spinlock_t *lock);
+struct clk_div_table {
+ unsigned int val;
+ unsigned int div;
+};
+
/**
* struct clk_divider - adjustable divider clock
*
@@ -210,6 +215,7 @@ struct clk *clk_register_gate(struct device *dev, const char *name,
* @reg: register containing the divider
* @shift: shift to the divider bit field
* @width: width of the divider bit field
+ * @table: array of value/divider pairs, last entry should have div = 0
* @lock: register lock
*
* Clock with an adjustable divider affecting its output frequency. Implements
@@ -229,6 +235,7 @@ struct clk_divider {
u8 shift;
u8 width;
u8 flags;
+ const struct clk_div_table *table;
spinlock_t *lock;
};
@@ -240,6 +247,11 @@ struct clk *clk_register_divider(struct device *dev, const char *name,
const char *parent_name, unsigned long flags,
void __iomem *reg, u8 shift, u8 width,
u8 clk_divider_flags, spinlock_t *lock);
+struct clk *clk_register_divider_table(struct device *dev, const char *name,
+ const char *parent_name, unsigned long flags,
+ void __iomem *reg, u8 shift, u8 width,
+ u8 clk_divider_flags, const struct clk_div_table *table,
+ spinlock_t *lock);
/**
* struct clk_mux - multiplexer clock