diff --git a/drivers/clk/clk.c b/drivers/clk/clk.c index 895b3d204e22..e1a192240055 100644 --- a/drivers/clk/clk.c +++ b/drivers/clk/clk.c @@ -2539,24 +2539,99 @@ const char *of_clk_get_parent_name(struct device_node *np, 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; +}; + +static LIST_HEAD(clk_provider_list); + +/* + * 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 * @matches: array of compatible values and init functions for providers. * - * This function scans the device tree for matching clock providers and - * calls their initialization functions + * This function scans the device tree for matching clock providers + * and calls their initialization functions. It also do it by trying + * to follow the dependencies. */ void __init of_clk_init(const struct of_device_id *matches) { const struct of_device_id *match; struct device_node *np; + struct clock_provider *clk_provider, *next; + bool is_init_done; + bool force = false; if (!matches) matches = &__clk_of_table; + /* First prepare the list of the clocks providers */ for_each_matching_node_and_match(np, matches, &match) { - of_clk_init_cb_t clk_init_cb = match->data; - clk_init_cb(np); + struct clock_provider *parent = + kzalloc(sizeof(struct clock_provider), GFP_KERNEL); + + parent->clk_init_cb = match->data; + parent->np = np; + list_add(&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); + kfree(clk_provider); + is_init_done = true; + } + } + + /* + * We didn't managed 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; + } } #endif