Merge branches 'clk-cdce-regulator', 'clk-bcm', 'clk-evict-parent-cache' and 'clk...
[linux-2.6-microblaze.git] / drivers / clk / clk.c
index e7300e1..498cae5 100644 (file)
@@ -37,6 +37,12 @@ static HLIST_HEAD(clk_root_list);
 static HLIST_HEAD(clk_orphan_list);
 static LIST_HEAD(clk_notifier_list);
 
+static struct hlist_head *all_lists[] = {
+       &clk_root_list,
+       &clk_orphan_list,
+       NULL,
+};
+
 /***    private data structures    ***/
 
 struct clk_parent_map {
@@ -2841,12 +2847,6 @@ static int inited = 0;
 static DEFINE_MUTEX(clk_debug_lock);
 static HLIST_HEAD(clk_debug_list);
 
-static struct hlist_head *all_lists[] = {
-       &clk_root_list,
-       &clk_orphan_list,
-       NULL,
-};
-
 static struct hlist_head *orphan_list[] = {
        &clk_orphan_list,
        NULL,
@@ -3777,6 +3777,34 @@ static const struct clk_ops clk_nodrv_ops = {
        .set_parent     = clk_nodrv_set_parent,
 };
 
+static void clk_core_evict_parent_cache_subtree(struct clk_core *root,
+                                               struct clk_core *target)
+{
+       int i;
+       struct clk_core *child;
+
+       for (i = 0; i < root->num_parents; i++)
+               if (root->parents[i].core == target)
+                       root->parents[i].core = NULL;
+
+       hlist_for_each_entry(child, &root->children, child_node)
+               clk_core_evict_parent_cache_subtree(child, target);
+}
+
+/* Remove this clk from all parent caches */
+static void clk_core_evict_parent_cache(struct clk_core *core)
+{
+       struct hlist_head **lists;
+       struct clk_core *root;
+
+       lockdep_assert_held(&prepare_lock);
+
+       for (lists = all_lists; *lists; lists++)
+               hlist_for_each_entry(root, *lists, child_node)
+                       clk_core_evict_parent_cache_subtree(root, core);
+
+}
+
 /**
  * clk_unregister - unregister a currently registered clock
  * @clk: clock to unregister
@@ -3815,6 +3843,8 @@ void clk_unregister(struct clk *clk)
                        clk_core_set_parent_nolock(child, NULL);
        }
 
+       clk_core_evict_parent_cache(clk->core);
+
        hlist_del_init(&clk->core->child_node);
 
        if (clk->core->prepare_count)