As clocks are registered their parents are resolved and the parent_map
is updated to cache the clk_core objects of each existing parent.
But in the event of a clock being unregistered this cache will carry
dangling pointers if not invalidated, so do this for all children of the
clock being unregistered.

Signed-off-by: Bjorn Andersson <bjorn.anders...@linaro.org>
---

This resolves the issue seen where the DSI PLL (and it's provided clocks) is
being registered and unregistered multiple times due to probe deferral.

Marking it RFC because I don't fully understand the life of the clock yet.

 drivers/clk/clk.c | 18 +++++++++++++++---
 1 file changed, 15 insertions(+), 3 deletions(-)

diff --git a/drivers/clk/clk.c b/drivers/clk/clk.c
index c0990703ce54..8cd1ad977c50 100644
--- a/drivers/clk/clk.c
+++ b/drivers/clk/clk.c
@@ -2423,11 +2423,14 @@ bool clk_has_parent(struct clk *clk, struct clk *parent)
 EXPORT_SYMBOL_GPL(clk_has_parent);
 
 static int clk_core_set_parent_nolock(struct clk_core *core,
-                                     struct clk_core *parent)
+                                     struct clk_core *parent,
+                                     bool invalidate_parent)
 {
+       struct clk_core *old_parent = core->parent;
        int ret = 0;
        int p_index = 0;
        unsigned long p_rate = 0;
+       int i;
 
        lockdep_assert_held(&prepare_lock);
 
@@ -2481,6 +2484,14 @@ static int clk_core_set_parent_nolock(struct clk_core 
*core,
                __clk_recalc_accuracies(core);
        }
 
+       /* invalidate the parent cache */
+       if (!parent && invalidate_parent) {
+               for (i = 0; i < core->num_parents; i++) {
+                       if (core->parents[i].core == old_parent)
+                               core->parents[i].core = NULL;
+               }
+       }
+
 runtime_put:
        clk_pm_runtime_put(core);
 
@@ -2517,7 +2528,8 @@ int clk_set_parent(struct clk *clk, struct clk *parent)
                clk_core_rate_unprotect(clk->core);
 
        ret = clk_core_set_parent_nolock(clk->core,
-                                        parent ? parent->core : NULL);
+                                        parent ? parent->core : NULL,
+                                        false);
 
        if (clk->exclusive_count)
                clk_core_rate_protect(clk->core);
@@ -3772,7 +3784,7 @@ void clk_unregister(struct clk *clk)
                /* Reparent all children to the orphan list. */
                hlist_for_each_entry_safe(child, t, &clk->core->children,
                                          child_node)
-                       clk_core_set_parent_nolock(child, NULL);
+                       clk_core_set_parent_nolock(child, NULL, true);
        }
 
        hlist_del_init(&clk->core->child_node);
-- 
2.18.0

Reply via email to