From b0d851d9517e113481b3c32bf90006b3f2d7503e Mon Sep 17 00:00:00 2001 From: Lucas Stach Date: Sun, 30 Jun 2013 23:08:43 +0200 Subject: clk: add clock lookup from devicetree Taken from the Linuxkernel with some small adjustments for barebox. Signed-off-by: Lucas Stach Signed-off-by: Sascha Hauer --- drivers/clk/clk.c | 97 +++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 97 insertions(+) (limited to 'drivers/clk/clk.c') diff --git a/drivers/clk/clk.c b/drivers/clk/clk.c index 690a0c63da..7ec3d93787 100644 --- a/drivers/clk/clk.c +++ b/drivers/clk/clk.c @@ -16,6 +16,7 @@ */ #include #include +#include #include #include @@ -253,6 +254,102 @@ int clk_is_enabled_always(struct clk *clk) return 1; } +#if defined(CONFIG_OFTREE) && defined(CONFIG_COMMON_CLK_OF_PROVIDER) +/** + * struct of_clk_provider - Clock provider registration structure + * @link: Entry in global list of clock providers + * @node: Pointer to device tree node of clock provider + * @get: Get clock callback. Returns NULL or a struct clk for the + * given clock specifier + * @data: context pointer to be passed into @get callback + */ +struct of_clk_provider { + struct list_head link; + + struct device_node *node; + struct clk *(*get)(struct of_phandle_args *clkspec, void *data); + void *data; +}; + +static LIST_HEAD(of_clk_providers); + +struct clk *of_clk_src_onecell_get(struct of_phandle_args *clkspec, void *data) +{ + struct clk_onecell_data *clk_data = data; + unsigned int idx = clkspec->args[0]; + + if (idx >= clk_data->clk_num) { + pr_err("%s: invalid clock index %d\n", __func__, idx); + return ERR_PTR(-EINVAL); + } + + return clk_data->clks[idx]; +} +EXPORT_SYMBOL_GPL(of_clk_src_onecell_get); + +/** + * of_clk_add_provider() - Register a clock provider for a node + * @np: Device node pointer associated with clock provider + * @clk_src_get: callback for decoding clock + * @data: context pointer for @clk_src_get callback. + */ +int of_clk_add_provider(struct device_node *np, + struct clk *(*clk_src_get)(struct of_phandle_args *clkspec, + void *data), + void *data) +{ + struct of_clk_provider *cp; + + cp = kzalloc(sizeof(struct of_clk_provider), GFP_KERNEL); + if (!cp) + return -ENOMEM; + + cp->node = np; + cp->data = data; + cp->get = clk_src_get; + + list_add(&cp->link, &of_clk_providers); + pr_debug("Added clock from %s\n", np->full_name); + + return 0; +} +EXPORT_SYMBOL_GPL(of_clk_add_provider); + +/** + * of_clk_del_provider() - Remove a previously registered clock provider + * @np: Device node pointer associated with clock provider + */ +void of_clk_del_provider(struct device_node *np) +{ + struct of_clk_provider *cp; + + list_for_each_entry(cp, &of_clk_providers, link) { + if (cp->node == np) { + list_del(&cp->link); + kfree(cp); + break; + } + } +} +EXPORT_SYMBOL_GPL(of_clk_del_provider); + +struct clk *of_clk_get_from_provider(struct of_phandle_args *clkspec) +{ + struct of_clk_provider *provider; + struct clk *clk = ERR_PTR(-ENOENT); + + /* Check if we have such a provider in our array */ + list_for_each_entry(provider, &of_clk_providers, link) { + if (provider->node == clkspec->np) + clk = provider->get(clkspec, provider->data); + if (!IS_ERR(clk)) + break; + } + + return clk; +} +#endif + static void dump_one(struct clk *clk, int verbose, int indent) { struct clk *c; -- cgit v1.2.3