|
@@ -21,6 +21,7 @@
|
|
|
#include <linux/of.h>
|
|
|
#include <linux/of_address.h>
|
|
|
#include <linux/clk/ti.h>
|
|
|
+#include "clock.h"
|
|
|
|
|
|
#undef pr_fmt
|
|
|
#define pr_fmt(fmt) "%s: " fmt, __func__
|
|
@@ -300,6 +301,134 @@ static struct clk *_register_divider(struct device *dev, const char *name,
|
|
|
return clk;
|
|
|
}
|
|
|
|
|
|
+static struct clk_div_table *
|
|
|
+_get_div_table_from_setup(struct ti_clk_divider *setup, u8 *width)
|
|
|
+{
|
|
|
+ int valid_div = 0;
|
|
|
+ struct clk_div_table *table;
|
|
|
+ int i;
|
|
|
+ int div;
|
|
|
+ u32 val;
|
|
|
+ u8 flags;
|
|
|
+
|
|
|
+ if (!setup->num_dividers) {
|
|
|
+ /* Clk divider table not provided, determine min/max divs */
|
|
|
+ flags = setup->flags;
|
|
|
+
|
|
|
+ if (flags & CLKF_INDEX_STARTS_AT_ONE)
|
|
|
+ val = 1;
|
|
|
+ else
|
|
|
+ val = 0;
|
|
|
+
|
|
|
+ div = 1;
|
|
|
+
|
|
|
+ while (div < setup->max_div) {
|
|
|
+ if (flags & CLKF_INDEX_POWER_OF_TWO)
|
|
|
+ div <<= 1;
|
|
|
+ else
|
|
|
+ div++;
|
|
|
+ val++;
|
|
|
+ }
|
|
|
+
|
|
|
+ *width = fls(val);
|
|
|
+
|
|
|
+ return NULL;
|
|
|
+ }
|
|
|
+
|
|
|
+ for (i = 0; i < setup->num_dividers; i++)
|
|
|
+ if (setup->dividers[i])
|
|
|
+ valid_div++;
|
|
|
+
|
|
|
+ table = kzalloc(sizeof(*table) * (valid_div + 1), GFP_KERNEL);
|
|
|
+ if (!table)
|
|
|
+ return ERR_PTR(-ENOMEM);
|
|
|
+
|
|
|
+ valid_div = 0;
|
|
|
+ *width = 0;
|
|
|
+
|
|
|
+ for (i = 0; i < setup->num_dividers; i++)
|
|
|
+ if (setup->dividers[i]) {
|
|
|
+ table[valid_div].div = setup->dividers[i];
|
|
|
+ table[valid_div].val = i;
|
|
|
+ valid_div++;
|
|
|
+ *width = i;
|
|
|
+ }
|
|
|
+
|
|
|
+ *width = fls(*width);
|
|
|
+
|
|
|
+ return table;
|
|
|
+}
|
|
|
+
|
|
|
+struct clk_hw *ti_clk_build_component_div(struct ti_clk_divider *setup)
|
|
|
+{
|
|
|
+ struct clk_divider *div;
|
|
|
+ struct clk_omap_reg *reg;
|
|
|
+
|
|
|
+ if (!setup)
|
|
|
+ return NULL;
|
|
|
+
|
|
|
+ div = kzalloc(sizeof(*div), GFP_KERNEL);
|
|
|
+ if (!div)
|
|
|
+ return ERR_PTR(-ENOMEM);
|
|
|
+
|
|
|
+ reg = (struct clk_omap_reg *)&div->reg;
|
|
|
+ reg->index = setup->module;
|
|
|
+ reg->offset = setup->reg;
|
|
|
+
|
|
|
+ if (setup->flags & CLKF_INDEX_STARTS_AT_ONE)
|
|
|
+ div->flags |= CLK_DIVIDER_ONE_BASED;
|
|
|
+
|
|
|
+ if (setup->flags & CLKF_INDEX_POWER_OF_TWO)
|
|
|
+ div->flags |= CLK_DIVIDER_POWER_OF_TWO;
|
|
|
+
|
|
|
+ div->table = _get_div_table_from_setup(setup, &div->width);
|
|
|
+
|
|
|
+ div->shift = setup->bit_shift;
|
|
|
+
|
|
|
+ return &div->hw;
|
|
|
+}
|
|
|
+
|
|
|
+struct clk *ti_clk_register_divider(struct ti_clk *setup)
|
|
|
+{
|
|
|
+ struct ti_clk_divider *div;
|
|
|
+ struct clk_omap_reg *reg_setup;
|
|
|
+ u32 reg;
|
|
|
+ u8 width;
|
|
|
+ u32 flags = 0;
|
|
|
+ u8 div_flags = 0;
|
|
|
+ struct clk_div_table *table;
|
|
|
+ struct clk *clk;
|
|
|
+
|
|
|
+ div = setup->data;
|
|
|
+
|
|
|
+ reg_setup = (struct clk_omap_reg *)®
|
|
|
+
|
|
|
+ reg_setup->index = div->module;
|
|
|
+ reg_setup->offset = div->reg;
|
|
|
+
|
|
|
+ if (div->flags & CLKF_INDEX_STARTS_AT_ONE)
|
|
|
+ div_flags |= CLK_DIVIDER_ONE_BASED;
|
|
|
+
|
|
|
+ if (div->flags & CLKF_INDEX_POWER_OF_TWO)
|
|
|
+ div_flags |= CLK_DIVIDER_POWER_OF_TWO;
|
|
|
+
|
|
|
+ if (div->flags & CLKF_SET_RATE_PARENT)
|
|
|
+ flags |= CLK_SET_RATE_PARENT;
|
|
|
+
|
|
|
+ table = _get_div_table_from_setup(div, &width);
|
|
|
+ if (IS_ERR(table))
|
|
|
+ return (struct clk *)table;
|
|
|
+
|
|
|
+ clk = _register_divider(NULL, setup->name, div->parent,
|
|
|
+ flags, (void __iomem *)reg, div->bit_shift,
|
|
|
+ width, div_flags, table, NULL);
|
|
|
+
|
|
|
+ if (IS_ERR(clk))
|
|
|
+ kfree(table);
|
|
|
+
|
|
|
+ return clk;
|
|
|
+}
|
|
|
+
|
|
|
static struct clk_div_table *
|
|
|
__init ti_clk_get_div_table(struct device_node *node)
|
|
|
{
|
|
@@ -455,7 +584,8 @@ static void __init of_ti_divider_clk_setup(struct device_node *node)
|
|
|
goto cleanup;
|
|
|
|
|
|
clk = _register_divider(NULL, node->name, parent_name, flags, reg,
|
|
|
- shift, width, clk_divider_flags, table, NULL);
|
|
|
+ shift, width, clk_divider_flags, table,
|
|
|
+ NULL);
|
|
|
|
|
|
if (!IS_ERR(clk)) {
|
|
|
of_clk_add_provider(node, of_clk_src_simple_get, clk);
|