]> git.karo-electronics.de Git - karo-tx-linux.git/blobdiff - drivers/clk/clk.c
clk: WARN_ON about to disable a critical clock
[karo-tx-linux.git] / drivers / clk / clk.c
index b4db67a446c81ad961d8ab3e027f1ff955483efb..dede0ce679e47e271537e99f716c92c91fb27bce 100644 (file)
@@ -350,13 +350,12 @@ static struct clk_core *clk_core_get_parent_by_index(struct clk_core *core,
 {
        if (!core || index >= core->num_parents)
                return NULL;
-       else if (!core->parents)
-               return clk_core_lookup(core->parent_names[index]);
-       else if (!core->parents[index])
-               return core->parents[index] =
-                       clk_core_lookup(core->parent_names[index]);
-       else
-               return core->parents[index];
+
+       if (!core->parents[index])
+               core->parents[index] =
+                               clk_core_lookup(core->parent_names[index]);
+
+       return core->parents[index];
 }
 
 struct clk_hw *
@@ -386,7 +385,7 @@ static unsigned long clk_core_get_rate_nolock(struct clk_core *core)
 
        ret = core->rate;
 
-       if (core->flags & CLK_IS_ROOT)
+       if (!core->num_parents)
                goto out;
 
        if (!core->parent)
@@ -575,6 +574,9 @@ static void clk_core_unprepare(struct clk_core *core)
        if (WARN_ON(core->prepare_count == 0))
                return;
 
+       if (WARN_ON(core->prepare_count == 1 && core->flags & CLK_IS_CRITICAL))
+               return;
+
        if (--core->prepare_count > 0)
                return;
 
@@ -680,6 +682,9 @@ static void clk_core_disable(struct clk_core *core)
        if (WARN_ON(core->enable_count == 0))
                return;
 
+       if (WARN_ON(core->enable_count == 1 && core->flags & CLK_IS_CRITICAL))
+               return;
+
        if (--core->enable_count > 0)
                return;
 
@@ -1067,30 +1072,12 @@ static int clk_fetch_parent_index(struct clk_core *core,
 {
        int i;
 
-       if (!core->parents) {
-               core->parents = kcalloc(core->num_parents,
-                                       sizeof(struct clk *), GFP_KERNEL);
-               if (!core->parents)
-                       return -ENOMEM;
-       }
-
-       /*
-        * find index of new parent clock using cached parent ptrs,
-        * or if not yet cached, use string name comparison and cache
-        * them now to avoid future calls to clk_core_lookup.
-        */
-       for (i = 0; i < core->num_parents; i++) {
-               if (core->parents[i] == parent)
-                       return i;
-
-               if (core->parents[i])
-                       continue;
+       if (!parent)
+               return -EINVAL;
 
-               if (!strcmp(core->parent_names[i], parent->name)) {
-                       core->parents[i] = clk_core_lookup(parent->name);
+       for (i = 0; i < core->num_parents; i++)
+               if (clk_core_get_parent_by_index(core, i) == parent)
                        return i;
-               }
-       }
 
        return -EINVAL;
 }
@@ -1677,56 +1664,14 @@ struct clk *clk_get_parent(struct clk *clk)
 }
 EXPORT_SYMBOL_GPL(clk_get_parent);
 
-/*
- * .get_parent is mandatory for clocks with multiple possible parents.  It is
- * optional for single-parent clocks.  Always call .get_parent if it is
- * available and WARN if it is missing for multi-parent clocks.
- *
- * For single-parent clocks without .get_parent, first check to see if the
- * .parents array exists, and if so use it to avoid an expensive tree
- * traversal.  If .parents does not exist then walk the tree.
- */
 static struct clk_core *__clk_init_parent(struct clk_core *core)
 {
-       struct clk_core *ret = NULL;
-       u8 index;
-
-       /* handle the trivial cases */
-
-       if (!core->num_parents)
-               goto out;
-
-       if (core->num_parents == 1) {
-               if (IS_ERR_OR_NULL(core->parent))
-                       core->parent = clk_core_lookup(core->parent_names[0]);
-               ret = core->parent;
-               goto out;
-       }
-
-       if (!core->ops->get_parent) {
-               WARN(!core->ops->get_parent,
-                       "%s: multi-parent clocks must implement .get_parent\n",
-                       __func__);
-               goto out;
-       }
+       u8 index = 0;
 
-       /*
-        * Do our best to cache parent clocks in core->parents.  This prevents
-        * unnecessary and expensive lookups.  We don't set core->parent here;
-        * that is done by the calling function.
-        */
-
-       index = core->ops->get_parent(core->hw);
-
-       if (!core->parents)
-               core->parents =
-                       kcalloc(core->num_parents, sizeof(struct clk *),
-                                       GFP_KERNEL);
-
-       ret = clk_core_get_parent_by_index(core, index);
+       if (core->num_parents > 1 && core->ops->get_parent)
+               index = core->ops->get_parent(core->hw);
 
-out:
-       return ret;
+       return clk_core_get_parent_by_index(core, index);
 }
 
 static void clk_core_reparent(struct clk_core *core,
@@ -1809,13 +1754,13 @@ static int clk_core_set_parent(struct clk_core *core, struct clk_core *parent)
        /* try finding the new parent index */
        if (parent) {
                p_index = clk_fetch_parent_index(core, parent);
-               p_rate = parent->rate;
                if (p_index < 0) {
                        pr_debug("%s: clk %s can not be parent of clk %s\n",
                                        __func__, parent->name, core->name);
                        ret = p_index;
                        goto out;
                }
+               p_rate = parent->rate;
        }
 
        /* propagate PRE_RATE_CHANGE notifications */
@@ -1902,6 +1847,10 @@ int clk_set_phase(struct clk *clk, int degrees)
 
        clk_prepare_lock();
 
+       /* bail early if nothing to do */
+       if (degrees == clk->core->phase)
+               goto out;
+
        trace_clk_set_phase(clk->core, degrees);
 
        if (clk->core->ops->set_phase)
@@ -1912,6 +1861,7 @@ int clk_set_phase(struct clk *clk, int degrees)
        if (!ret)
                clk->core->phase = degrees;
 
+out:
        clk_prepare_unlock();
 
        return ret;
@@ -2218,7 +2168,7 @@ unlock:
  *
  * Dynamically removes a clk and all its child nodes from the
  * debugfs clk directory if clk->dentry points to debugfs created by
- * clk_debug_register in __clk_init.
+ * clk_debug_register in __clk_core_init.
  */
 static void clk_debug_unregister(struct clk_core *core)
 {
@@ -2303,26 +2253,22 @@ static inline void clk_debug_unregister(struct clk_core *core)
 #endif
 
 /**
- * __clk_init - initialize the data structures in a struct clk
- * @dev:       device initializing this clk, placeholder for now
- * @clk:       clk being initialized
+ * __clk_core_init - initialize the data structures in a struct clk_core
+ * @core:      clk_core being initialized
  *
  * Initializes the lists in struct clk_core, queries the hardware for the
  * parent and rate and sets them both.
  */
-static int __clk_init(struct device *dev, struct clk *clk_user)
+static int __clk_core_init(struct clk_core *core)
 {
        int i, ret = 0;
        struct clk_core *orphan;
        struct hlist_node *tmp2;
-       struct clk_core *core;
        unsigned long rate;
 
-       if (!clk_user)
+       if (!core)
                return -EINVAL;
 
-       core = clk_user->core;
-
        clk_prepare_lock();
 
        /* check to see if a clock with this name is already registered */
@@ -2337,22 +2283,29 @@ static int __clk_init(struct device *dev, struct clk *clk_user)
        if (core->ops->set_rate &&
            !((core->ops->round_rate || core->ops->determine_rate) &&
              core->ops->recalc_rate)) {
-               pr_warning("%s: %s must implement .round_rate or .determine_rate in addition to .recalc_rate\n",
-                               __func__, core->name);
+               pr_err("%s: %s must implement .round_rate or .determine_rate in addition to .recalc_rate\n",
+                      __func__, core->name);
                ret = -EINVAL;
                goto out;
        }
 
        if (core->ops->set_parent && !core->ops->get_parent) {
-               pr_warning("%s: %s must implement .get_parent & .set_parent\n",
-                               __func__, core->name);
+               pr_err("%s: %s must implement .get_parent & .set_parent\n",
+                      __func__, core->name);
+               ret = -EINVAL;
+               goto out;
+       }
+
+       if (core->num_parents > 1 && !core->ops->get_parent) {
+               pr_err("%s: %s must implement .get_parent as it has multi parents\n",
+                      __func__, core->name);
                ret = -EINVAL;
                goto out;
        }
 
        if (core->ops->set_rate_and_parent &&
                        !(core->ops->set_parent && core->ops->set_rate)) {
-               pr_warn("%s: %s must implement .set_parent & .set_rate\n",
+               pr_err("%s: %s must implement .set_parent & .set_rate\n",
                                __func__, core->name);
                ret = -EINVAL;
                goto out;
@@ -2364,37 +2317,12 @@ static int __clk_init(struct device *dev, struct clk *clk_user)
                                "%s: invalid NULL in %s's .parent_names\n",
                                __func__, core->name);
 
-       /*
-        * Allocate an array of struct clk *'s to avoid unnecessary string
-        * look-ups of clk's possible parents.  This can fail for clocks passed
-        * in to clk_init during early boot; thus any access to core->parents[]
-        * must always check for a NULL pointer and try to populate it if
-        * necessary.
-        *
-        * If core->parents is not NULL we skip this entire block.  This allows
-        * for clock drivers to statically initialize core->parents.
-        */
-       if (core->num_parents > 1 && !core->parents) {
-               core->parents = kcalloc(core->num_parents, sizeof(struct clk *),
-                                       GFP_KERNEL);
-               /*
-                * clk_core_lookup returns NULL for parents that have not been
-                * clk_init'd; thus any access to clk->parents[] must check
-                * for a NULL pointer.  We can always perform lazy lookups for
-                * missing parents later on.
-                */
-               if (core->parents)
-                       for (i = 0; i < core->num_parents; i++)
-                               core->parents[i] =
-                                       clk_core_lookup(core->parent_names[i]);
-       }
-
        core->parent = __clk_init_parent(core);
 
        /*
-        * Populate core->parent if parent has already been __clk_init'd.  If
-        * parent has not yet been __clk_init'd then place clk in the orphan
-        * list.  If clk has set the CLK_IS_ROOT flag then place it in the root
+        * Populate core->parent if parent has already been clk_core_init'd. If
+        * parent has not yet been clk_core_init'd then place clk in the orphan
+        * list.  If clk doesn't have any parents then place it in the root
         * clk list.
         *
         * Every time a new clk is clk_init'd then we walk the list of orphan
@@ -2405,7 +2333,7 @@ static int __clk_init(struct device *dev, struct clk *clk_user)
                hlist_add_head(&core->child_node,
                                &core->parent->children);
                core->orphan = core->parent->orphan;
-       } else if (core->flags & CLK_IS_ROOT) {
+       } else if (!core->num_parents) {
                hlist_add_head(&core->child_node, &clk_root_list);
                core->orphan = false;
        } else {
@@ -2454,24 +2382,15 @@ static int __clk_init(struct device *dev, struct clk *clk_user)
        core->rate = core->req_rate = rate;
 
        /*
-        * walk the list of orphan clocks and reparent any that are children of
-        * this clock
+        * walk the list of orphan clocks and reparent any that newly finds a
+        * parent.
         */
        hlist_for_each_entry_safe(orphan, tmp2, &clk_orphan_list, child_node) {
-               if (orphan->num_parents && orphan->ops->get_parent) {
-                       i = orphan->ops->get_parent(orphan->hw);
-                       if (i >= 0 && i < orphan->num_parents &&
-                           !strcmp(core->name, orphan->parent_names[i]))
-                               clk_core_reparent(orphan, core);
-                       continue;
-               }
+               struct clk_core *parent = __clk_init_parent(orphan);
 
-               for (i = 0; i < orphan->num_parents; i++)
-                       if (!strcmp(core->name, orphan->parent_names[i])) {
-                               clk_core_reparent(orphan, core);
-                               break;
-                       }
-        }
+               if (parent)
+                       clk_core_reparent(orphan, parent);
+       }
 
        /*
         * optional platform-specific magic
@@ -2484,6 +2403,11 @@ static int __clk_init(struct device *dev, struct clk *clk_user)
        if (core->ops->init)
                core->ops->init(core->hw);
 
+       if (core->flags & CLK_IS_CRITICAL) {
+               clk_core_prepare(core);
+               clk_core_enable(core);
+       }
+
        kref_init(&core->ref);
 out:
        clk_prepare_unlock();
@@ -2585,21 +2509,31 @@ struct clk *clk_register(struct device *dev, struct clk_hw *hw)
                }
        }
 
+       /* avoid unnecessary string look-ups of clk_core's possible parents. */
+       core->parents = kcalloc(core->num_parents, sizeof(*core->parents),
+                               GFP_KERNEL);
+       if (!core->parents) {
+               ret = -ENOMEM;
+               goto fail_parents;
+       };
+
        INIT_HLIST_HEAD(&core->clks);
 
        hw->clk = __clk_create_clk(hw, NULL, NULL);
        if (IS_ERR(hw->clk)) {
                ret = PTR_ERR(hw->clk);
-               goto fail_parent_names_copy;
+               goto fail_parents;
        }
 
-       ret = __clk_init(dev, hw->clk);
+       ret = __clk_core_init(core);
        if (!ret)
                return hw->clk;
 
        __clk_free_clk(hw->clk);
        hw->clk = NULL;
 
+fail_parents:
+       kfree(core->parents);
 fail_parent_names_copy:
        while (--i >= 0)
                kfree_const(core->parent_names[i]);
@@ -2683,7 +2617,7 @@ void clk_unregister(struct clk *clk)
        if (clk->core->ops == &clk_nodrv_ops) {
                pr_err("%s: unregistered clock: %s\n", __func__,
                       clk->core->name);
-               return;
+               goto unlock;
        }
        /*
         * Assign empty clock ops for consumers that might still hold
@@ -2709,7 +2643,7 @@ void clk_unregister(struct clk *clk)
                pr_warn("%s: unregistering prepared clock: %s\n",
                                        __func__, clk->core->name);
        kref_put(&clk->core->ref, __clk_release);
-
+unlock:
        clk_prepare_unlock();
 }
 EXPORT_SYMBOL_GPL(clk_unregister);
@@ -3061,10 +2995,23 @@ struct clk *of_clk_get_from_provider(struct of_phandle_args *clkspec)
 {
        return __of_clk_get_from_provider(clkspec, NULL, __func__);
 }
+EXPORT_SYMBOL_GPL(of_clk_get_from_provider);
 
-int of_clk_get_parent_count(struct device_node *np)
+/**
+ * of_clk_get_parent_count() - Count the number of clocks a device node has
+ * @np: device node to count
+ *
+ * Returns: The number of clocks that are possible parents of this node
+ */
+unsigned int of_clk_get_parent_count(struct device_node *np)
 {
-       return of_count_phandle_with_args(np, "clocks", "#clock-cells");
+       int count;
+
+       count = of_count_phandle_with_args(np, "clocks", "#clock-cells");
+       if (count < 0)
+               return 0;
+
+       return count;
 }
 EXPORT_SYMBOL_GPL(of_clk_get_parent_count);
 
@@ -3214,6 +3161,9 @@ void __init of_clk_init(const struct of_device_id *matches)
        for_each_matching_node_and_match(np, matches, &match) {
                struct clock_provider *parent;
 
+               if (!of_device_is_available(np))
+                       continue;
+
                parent = kzalloc(sizeof(*parent), GFP_KERNEL);
                if (!parent) {
                        list_for_each_entry_safe(clk_provider, next,