diff options
author | LiXin <li.xin@linaro.org> | 2013-01-31 17:25:07 +0800 |
---|---|---|
committer | Guodong Xu <guodong.xu@linaro.org> | 2013-02-21 16:12:30 +0800 |
commit | 548aec90f8d38329d919bd439c3595e529c24d79 (patch) | |
tree | 3a9a3760952dedee2c80b3a4dd1aee465f93d0f6 /drivers | |
parent | 4cc6a78feb17038b49f50bb0eb6e709ec953a0ec (diff) |
ARM: hs: add clock driver to hi4511
Add clock driver based on common clock framework to hi4511.
Signed-off-by: LiXin <li.xin@linaro.org>
Diffstat (limited to 'drivers')
-rw-r--r-- | drivers/clk/clk-hs.c | 194 |
1 files changed, 190 insertions, 4 deletions
diff --git a/drivers/clk/clk-hs.c b/drivers/clk/clk-hs.c index 99f907f0d23..88ad41c59fa 100644 --- a/drivers/clk/clk-hs.c +++ b/drivers/clk/clk-hs.c @@ -23,23 +23,209 @@ #include <linux/kernel.h> #include <linux/clk-provider.h> +#include <linux/clk-private.h> #include <linux/clkdev.h> #include <linux/delay.h> #include <linux/io.h> #include <linux/of.h> #include <linux/of_address.h> +#include <linux/of_device.h> #include <linux/slab.h> +#include <linux/clk.h> + +static DEFINE_SPINLOCK(_lock); + + +void __iomem *pmctrl_base; +void __iomem *sctrl_base; + + +void __init of_gate_clk_setup(struct device_node *node) +{ + struct clk *clk; + const char *clk_name = node->name; + const char *clk_parnet; + int err; + u32 data[2]; + void __iomem *reg; + u8 enable_bit; + + err = of_property_read_u32_array(node, "hisilicon,hi3620-clkgate", &data[0], 2); + if (err) { + printk("[%s] cann't values form hisilicon,hi3620-clkgate property! \n", node->name); + return ; + } + + reg = sctrl_base + data[0]; + enable_bit = (u8)data[1]; + + clk_parnet = of_clk_get_parent_name(node, 0); + + clk = clk_register_gate(NULL, clk_name, clk_parnet, CLK_IS_BASIC, + reg, enable_bit, 1, &_lock); + if (!IS_ERR(clk)) + of_clk_add_provider(node, of_clk_src_simple_get, clk); +} + + +void __init of_fixed_factor_clk_setup(struct device_node *node) +{ + struct clk *clk; + const char *clk_name = node->name; + const char *clk_parnet; + int err; + unsigned long rate; + u32 vals[2]; + + clk_parnet = of_clk_get_parent_name(node, 0); + + err = of_property_read_u32_array(node, "hisilicon,fixed-factor", vals, 2); + if (err) { + printk("[%s] cann't values form hisilicon,fixed_factor property! \n", node->name); + return ; + } + + clk = clk_register_fixed_factor(NULL, clk_name, clk_parnet, CLK_IS_BASIC, vals[0], vals[1]); + if (!IS_ERR(clk)) + of_clk_add_provider(node, of_clk_src_simple_get, clk); +} + +void __init of_divider_clk_setup(struct device_node *node) +{ + struct clk *clk; + const char *clk_name = node->name; + const char *clk_parnet; + struct clk_div_table *table; + int err; + void __iomem *reg; + u8 shift,width; + unsigned int table_num; + int i; + u32 data[3]; + char *propname = "hisilicon,clkdiv-table"; + char *cellname = "#hisilicon,clkdiv-table-cells"; + struct of_phandle_args div_table; + + /*process the div_table*/ + for (i = 0; ; i++) { + err = of_parse_phandle_with_args(node, propname, cellname, i, &div_table); + if (err) + break; + } + + /*table ends with <0, 0>, so plus one to table_num*/ + table_num = i + 1; + + table = kzalloc(sizeof(struct clk_div_table) * table_num, GFP_KERNEL); + if (!table) + return ; + + for (i = 0; ; i++) { + err = of_parse_phandle_with_args(node, propname, cellname, i, &div_table); + if (err) + break; + + table[i].val = div_table.args[0]; + table[i].div = div_table.args[1]; + } + + err = of_property_read_u32_array(node, "hisilicon,hi3620-clkdiv", &data[0], 3); + if (err) { + printk("[%s] cann't values form hisilicon,hi3620-clkdiv property! \n", node->name); + kfree(table); + return ; + } + + reg = sctrl_base + data[0]; + shift = (u8)data[1]; + width = (u8)data[2]; + + clk_parnet = of_clk_get_parent_name(node, 0); + + clk = clk_register_divider_table(NULL, clk_name, clk_parnet, CLK_IS_BASIC, reg, + shift, width, 0, table, &_lock); + if (!IS_ERR(clk)) + of_clk_add_provider(node, of_clk_src_simple_get, clk); + else + kfree(table); +} + + +static void __init of_mux_clk_setup(struct device_node *node) +{ + int i, err; + void __iomem *clk_sel; + u8 num_parents; + const char *clk_name = node->name; + const char **parent_names; + struct clk *clk; + unsigned int shift,width; + u32 vals[2]; + + /* get the count of items in mux */ + for (i = 0; ; i++) { + /* parent's #clock-cells property is always 0 */ + if (!of_parse_phandle(node, "clocks", i)) + break; + } + + num_parents = i; + + if (!num_parents) + return; + + err = of_property_read_u32_array(node, "hisilicon,hi3620-clkmux", &vals[0], 2); + if (err) { + printk("cann't values form hisilicon,hi3620-clkmux property!\n"); + return; + } + clk_sel = sctrl_base + vals[0]; + + shift = ffs(vals[1]) - 1; + width = fls(vals[1]) - ffs(vals[1]) + 1; + + parent_names = kzalloc(sizeof(char *) * num_parents, GFP_KERNEL); + if (!parent_names) + return; + + for (i = 0; i < num_parents; i++) { + parent_names[i] = of_clk_get_parent_name(node, i); + } + clk = clk_register_mux(NULL, clk_name, parent_names, num_parents, + CLK_SET_RATE_PARENT, + clk_sel, shift, width, 0, &_lock); + if (IS_ERR(clk)) + goto err_mux; + + clk_register_clkdev(clk, clk_name, NULL); + of_clk_add_provider(node, of_clk_src_simple_get, clk); + + return; +err_mux: + kfree(parent_names); +} static const __initconst struct of_device_id hs_clk_match[] = { - { - .compatible = "fixed-clock", - .data = of_fixed_clk_setup, - }, + { .compatible = "fixed-clock", .data = of_fixed_clk_setup, }, + { .compatible = "hisilicon,pll", .data = of_fixed_clk_setup, }, + { .compatible = "hisilicon,muxclock", .data = of_mux_clk_setup, }, + { .compatible = "hisilicon,periclock", .data = of_gate_clk_setup, }, + { .compatible = "hisilicon,divclock", .data = of_divider_clk_setup, }, + { .compatible = "hisilicon,cfgaxi", .data = of_fixed_factor_clk_setup,}, + {} }; void __init hs_init_clocks(void) { struct device_node *node; + /*map pmctrl registers*/ + node = of_find_compatible_node(NULL, NULL, "hisilicon,pmctrl"); + pmctrl_base = of_iomap(node, 0); + WARN_ON(!pmctrl_base); + + node = of_find_compatible_node(NULL, NULL, "hisilicon,sctrl"); + sctrl_base = of_iomap(node, 0); + of_clk_init(hs_clk_match); } |