diff --git a/drivers/clk/versatile/clk-icst.c b/drivers/clk/versatile/clk-icst.c index 87bd4667b126..e62f8cb2c9b5 100644 --- a/drivers/clk/versatile/clk-icst.c +++ b/drivers/clk/versatile/clk-icst.c @@ -3,7 +3,7 @@ * We wrap the custom interface from into the generic * clock framework. * - * Copyright (C) 2012 Linus Walleij + * Copyright (C) 2012-2015 Linus Walleij * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2 as @@ -206,3 +206,90 @@ struct clk *icst_clk_register(struct device *dev, return icst_clk_setup(dev, desc, name, parent_name, map); } EXPORT_SYMBOL_GPL(icst_clk_register); + +#ifdef CONFIG_OF +/* + * In a device tree, an memory-mapped ICST clock appear as a child + * of a syscon node. Assume this and probe it only as a child of a + * syscon. + */ + +static const struct icst_params icst525_params = { + .vco_max = ICST525_VCO_MAX_5V, + .vco_min = ICST525_VCO_MIN, + .vd_min = 8, + .vd_max = 263, + .rd_min = 3, + .rd_max = 65, + .s2div = icst525_s2div, + .idx2s = icst525_idx2s, +}; + +static const struct icst_params icst307_params = { + .vco_max = ICST307_VCO_MAX, + .vco_min = ICST307_VCO_MIN, + .vd_min = 4 + 8, + .vd_max = 511 + 8, + .rd_min = 1 + 2, + .rd_max = 127 + 2, + .s2div = icst307_s2div, + .idx2s = icst307_idx2s, +}; + +static void __init of_syscon_icst_setup(struct device_node *np) +{ + struct device_node *parent; + struct regmap *map; + struct clk_icst_desc icst_desc; + const char *name = np->name; + const char *parent_name; + struct clk *regclk; + + /* We do not release this reference, we are using it perpetually */ + parent = of_get_parent(np); + if (!parent) { + pr_err("no parent node for syscon ICST clock\n"); + return; + } + map = syscon_node_to_regmap(parent); + if (IS_ERR(map)) { + pr_err("no regmap for syscon ICST clock parent\n"); + return; + } + + if (of_property_read_u32(np, "vco-offset", &icst_desc.vco_offset)) { + pr_err("no VCO register offset for ICST clock\n"); + return; + } + if (of_property_read_u32(np, "lock-offset", &icst_desc.lock_offset)) { + pr_err("no lock register offset for ICST clock\n"); + return; + } + + if (of_device_is_compatible(np, "arm,syscon-icst525")) + icst_desc.params = &icst525_params; + else if (of_device_is_compatible(np, "arm,syscon-icst307")) + icst_desc.params = &icst307_params; + else { + pr_err("unknown ICST clock %s\n", name); + return; + } + + /* Parent clock name is not the same as node parent */ + parent_name = of_clk_get_parent_name(np, 0); + + regclk = icst_clk_setup(NULL, &icst_desc, name, parent_name, map); + if (IS_ERR(regclk)) { + pr_err("error setting up syscon ICST clock %s\n", name); + return; + } + of_clk_add_provider(np, of_clk_src_simple_get, regclk); + pr_debug("registered syscon ICST clock %s\n", name); +} + +CLK_OF_DECLARE(arm_syscon_icst525_clk, + "arm,syscon-icst525", of_syscon_icst_setup); +CLK_OF_DECLARE(arm_syscon_icst307_clk, + "arm,syscon-icst307", of_syscon_icst_setup); + +#endif