diff options
author | Sascha Hauer <s.hauer@pengutronix.de> | 2016-11-14 12:37:20 +0100 |
---|---|---|
committer | Sascha Hauer <s.hauer@pengutronix.de> | 2016-11-14 12:37:20 +0100 |
commit | 8eb1fb3519b6c1ae6ddab114a7cdc86bb24da296 (patch) | |
tree | a85ef526dca96e6c12df5cc33c935b854d77fb93 /drivers/clk | |
parent | f55e379fca456444ade761358e8cce8657ea9672 (diff) | |
parent | 764f84809fedd39fca75e2c8731ab181a421fd5b (diff) | |
download | barebox-8eb1fb3519b6c1ae6ddab114a7cdc86bb24da296.tar.gz barebox-8eb1fb3519b6c1ae6ddab114a7cdc86bb24da296.tar.xz |
Merge branch 'for-next/vybrid'
Diffstat (limited to 'drivers/clk')
-rw-r--r-- | drivers/clk/clk.c | 89 |
1 files changed, 82 insertions, 7 deletions
diff --git a/drivers/clk/clk.c b/drivers/clk/clk.c index 15e424db1b..19377b893c 100644 --- a/drivers/clk/clk.c +++ b/drivers/clk/clk.c @@ -406,7 +406,7 @@ 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); + struct clk *clk = ERR_PTR(-EPROBE_DEFER); /* Check if we have such a provider in our array */ list_for_each_entry(provider, &of_clk_providers, link) { @@ -439,6 +439,47 @@ char *of_clk_get_parent_name(struct device_node *np, unsigned int index) } EXPORT_SYMBOL_GPL(of_clk_get_parent_name); +struct clock_provider { + of_clk_init_cb_t clk_init_cb; + struct device_node *np; + struct list_head node; +}; + +/* + * This function looks for a parent clock. If there is one, then it + * checks that the provider for this parent clock was initialized, in + * this case the parent clock will be ready. + */ +static int parent_ready(struct device_node *np) +{ + int i = 0; + + while (true) { + struct clk *clk = of_clk_get(np, i); + + /* this parent is ready we can check the next one */ + if (!IS_ERR(clk)) { + clk_put(clk); + i++; + continue; + } + + /* at least one parent is not ready, we exit now */ + if (PTR_ERR(clk) == -EPROBE_DEFER) + return 0; + + /* + * Here we make assumption that the device tree is + * written correctly. So an error means that there is + * no more parent. As we didn't exit yet, then the + * previous parent are ready. If there is no clock + * parent, no need to wait for them, then we can + * consider their absence as being ready + */ + return 1; + } +} + /** * of_clk_init() - Scan and init clock providers from the DT * @root: parent of the first level to probe or NULL for the root of the tree @@ -451,8 +492,11 @@ EXPORT_SYMBOL_GPL(of_clk_get_parent_name); */ int of_clk_init(struct device_node *root, const struct of_device_id *matches) { + struct clock_provider *clk_provider, *next; + bool is_init_done; + bool force = false; + LIST_HEAD(clk_provider_list); const struct of_device_id *match; - int rc; if (!root) root = of_find_node_by_path("/"); @@ -461,12 +505,43 @@ int of_clk_init(struct device_node *root, const struct of_device_id *matches) if (!matches) matches = __clk_of_table_start; + /* First prepare the list of the clocks providers */ for_each_matching_node_and_match(root, matches, &match) { - of_clk_init_cb_t clk_init_cb = (of_clk_init_cb_t)match->data; - rc = clk_init_cb(root); - if (rc) - pr_err("%s: failed to init clock for %s: %d\n", - __func__, root->full_name, rc); + struct clock_provider *parent; + + if (!of_device_is_available(root)) + continue; + + parent = xzalloc(sizeof(*parent)); + + parent->clk_init_cb = match->data; + parent->np = root; + list_add_tail(&parent->node, &clk_provider_list); + } + + while (!list_empty(&clk_provider_list)) { + is_init_done = false; + list_for_each_entry_safe(clk_provider, next, + &clk_provider_list, node) { + + if (force || parent_ready(clk_provider->np)) { + + clk_provider->clk_init_cb(clk_provider->np); + + list_del(&clk_provider->node); + free(clk_provider); + is_init_done = true; + } + } + + /* + * We didn't manage to initialize any of the + * remaining providers during the last loop, so now we + * initialize all the remaining ones unconditionally + * in case the clock parent was not mandatory + */ + if (!is_init_done) + force = true; } return 0; |