]> asedeno.scripts.mit.edu Git - linux.git/blobdiff - drivers/clk/clk.c
Merge branch 'work.mount3' of git://git.kernel.org/pub/scm/linux/kernel/git/viro/vfs
[linux.git] / drivers / clk / clk.c
index 7783c25fb407b30ef70f59a336ffa70d64d81ef8..1c677d7f7f530739ef2dd91428697d308dee3349 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 {
@@ -324,6 +330,25 @@ static struct clk_core *clk_core_lookup(const char *name)
        return NULL;
 }
 
+#ifdef CONFIG_OF
+static int of_parse_clkspec(const struct device_node *np, int index,
+                           const char *name, struct of_phandle_args *out_args);
+static struct clk_hw *
+of_clk_get_hw_from_clkspec(struct of_phandle_args *clkspec);
+#else
+static inline int of_parse_clkspec(const struct device_node *np, int index,
+                                  const char *name,
+                                  struct of_phandle_args *out_args)
+{
+       return -ENOENT;
+}
+static inline struct clk_hw *
+of_clk_get_hw_from_clkspec(struct of_phandle_args *clkspec)
+{
+       return ERR_PTR(-ENOENT);
+}
+#endif
+
 /**
  * clk_core_get - Find the clk_core parent of a clk
  * @core: clk to find parent of
@@ -355,8 +380,9 @@ static struct clk_core *clk_core_lookup(const char *name)
  *      };
  *
  * Returns: -ENOENT when the provider can't be found or the clk doesn't
- * exist in the provider. -EINVAL when the name can't be found. NULL when the
- * provider knows about the clk but it isn't provided on this system.
+ * exist in the provider or the name can't be found in the DT node or
+ * in a clkdev lookup. NULL when the provider knows about the clk but it
+ * isn't provided on this system.
  * A valid clk_core pointer when the clk can be found in the provider.
  */
 static struct clk_core *clk_core_get(struct clk_core *core, u8 p_index)
@@ -367,17 +393,19 @@ static struct clk_core *clk_core_get(struct clk_core *core, u8 p_index)
        struct device *dev = core->dev;
        const char *dev_id = dev ? dev_name(dev) : NULL;
        struct device_node *np = core->of_node;
+       struct of_phandle_args clkspec;
 
-       if (np && (name || index >= 0))
-               hw = of_clk_get_hw(np, index, name);
-
-       /*
-        * If the DT search above couldn't find the provider or the provider
-        * didn't know about this clk, fallback to looking up via clkdev based
-        * clk_lookups
-        */
-       if (PTR_ERR(hw) == -ENOENT && name)
+       if (np && (name || index >= 0) &&
+           !of_parse_clkspec(np, index, name, &clkspec)) {
+               hw = of_clk_get_hw_from_clkspec(&clkspec);
+               of_node_put(clkspec.np);
+       } else if (name) {
+               /*
+                * If the DT search above couldn't find the provider fallback to
+                * looking up via clkdev based clk_lookups.
+                */
                hw = clk_find_hw(dev_id, name);
+       }
 
        if (IS_ERR(hw))
                return ERR_CAST(hw);
@@ -401,7 +429,7 @@ static void clk_core_fill_parent_index(struct clk_core *core, u8 index)
                        parent = ERR_PTR(-EPROBE_DEFER);
        } else {
                parent = clk_core_get(core, index);
-               if (IS_ERR(parent) && PTR_ERR(parent) == -ENOENT)
+               if (IS_ERR(parent) && PTR_ERR(parent) == -ENOENT && entry->name)
                        parent = clk_core_lookup(entry->name);
        }
 
@@ -1634,7 +1662,8 @@ static int clk_fetch_parent_index(struct clk_core *core,
                        break;
 
                /* Fallback to comparing globally unique names */
-               if (!strcmp(parent->name, core->parents[i].name))
+               if (core->parents[i].name &&
+                   !strcmp(parent->name, core->parents[i].name))
                        break;
        }
 
@@ -2439,7 +2468,7 @@ static int clk_core_set_parent_nolock(struct clk_core *core,
        if (core->parent == parent)
                return 0;
 
-       /* verify ops for for multi-parent clks */
+       /* verify ops for multi-parent clks */
        if (core->num_parents > 1 && !core->ops->set_parent)
                return -EPERM;
 
@@ -2489,6 +2518,12 @@ static int clk_core_set_parent_nolock(struct clk_core *core,
        return ret;
 }
 
+int clk_hw_set_parent(struct clk_hw *hw, struct clk_hw *parent)
+{
+       return clk_core_set_parent_nolock(hw->core, parent->core);
+}
+EXPORT_SYMBOL_GPL(clk_hw_set_parent);
+
 /**
  * clk_set_parent - switch the parent of a mux clk
  * @clk: the mux clk whose input we are switching
@@ -2835,12 +2870,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,
@@ -3510,9 +3539,9 @@ static int clk_cpy_name(const char **dst_p, const char *src, bool must_exist)
        return 0;
 }
 
-static int clk_core_populate_parent_map(struct clk_core *core)
+static int clk_core_populate_parent_map(struct clk_core *core,
+                                       const struct clk_init_data *init)
 {
-       const struct clk_init_data *init = core->hw->init;
        u8 num_parents = init->num_parents;
        const char * const *parent_names = init->parent_names;
        const struct clk_hw **parent_hws = init->parent_hws;
@@ -3592,6 +3621,14 @@ __clk_register(struct device *dev, struct device_node *np, struct clk_hw *hw)
 {
        int ret;
        struct clk_core *core;
+       const struct clk_init_data *init = hw->init;
+
+       /*
+        * The init data is not supposed to be used outside of registration path.
+        * Set it to NULL so that provider drivers can't use it either and so that
+        * we catch use of hw->init early on in the core.
+        */
+       hw->init = NULL;
 
        core = kzalloc(sizeof(*core), GFP_KERNEL);
        if (!core) {
@@ -3599,17 +3636,17 @@ __clk_register(struct device *dev, struct device_node *np, struct clk_hw *hw)
                goto fail_out;
        }
 
-       core->name = kstrdup_const(hw->init->name, GFP_KERNEL);
+       core->name = kstrdup_const(init->name, GFP_KERNEL);
        if (!core->name) {
                ret = -ENOMEM;
                goto fail_name;
        }
 
-       if (WARN_ON(!hw->init->ops)) {
+       if (WARN_ON(!init->ops)) {
                ret = -EINVAL;
                goto fail_ops;
        }
-       core->ops = hw->init->ops;
+       core->ops = init->ops;
 
        if (dev && pm_runtime_enabled(dev))
                core->rpm_enabled = true;
@@ -3618,13 +3655,13 @@ __clk_register(struct device *dev, struct device_node *np, struct clk_hw *hw)
        if (dev && dev->driver)
                core->owner = dev->driver->owner;
        core->hw = hw;
-       core->flags = hw->init->flags;
-       core->num_parents = hw->init->num_parents;
+       core->flags = init->flags;
+       core->num_parents = init->num_parents;
        core->min_rate = 0;
        core->max_rate = ULONG_MAX;
        hw->core = core;
 
-       ret = clk_core_populate_parent_map(core);
+       ret = clk_core_populate_parent_map(core, init);
        if (ret)
                goto fail_parents;
 
@@ -3763,6 +3800,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
@@ -3801,6 +3866,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)
@@ -4342,12 +4409,43 @@ void devm_of_clk_del_provider(struct device *dev)
 }
 EXPORT_SYMBOL(devm_of_clk_del_provider);
 
-/*
- * Beware the return values when np is valid, but no clock provider is found.
- * If name == NULL, the function returns -ENOENT.
- * If name != NULL, the function returns -EINVAL. This is because
- * of_parse_phandle_with_args() is called even if of_property_match_string()
- * returns an error.
+/**
+ * of_parse_clkspec() - Parse a DT clock specifier for a given device node
+ * @np: device node to parse clock specifier from
+ * @index: index of phandle to parse clock out of. If index < 0, @name is used
+ * @name: clock name to find and parse. If name is NULL, the index is used
+ * @out_args: Result of parsing the clock specifier
+ *
+ * Parses a device node's "clocks" and "clock-names" properties to find the
+ * phandle and cells for the index or name that is desired. The resulting clock
+ * specifier is placed into @out_args, or an errno is returned when there's a
+ * parsing error. The @index argument is ignored if @name is non-NULL.
+ *
+ * Example:
+ *
+ * phandle1: clock-controller@1 {
+ *     #clock-cells = <2>;
+ * }
+ *
+ * phandle2: clock-controller@2 {
+ *     #clock-cells = <1>;
+ * }
+ *
+ * clock-consumer@3 {
+ *     clocks = <&phandle1 1 2 &phandle2 3>;
+ *     clock-names = "name1", "name2";
+ * }
+ *
+ * To get a device_node for `clock-controller@2' node you may call this
+ * function a few different ways:
+ *
+ *   of_parse_clkspec(clock-consumer@3, -1, "name2", &args);
+ *   of_parse_clkspec(clock-consumer@3, 1, NULL, &args);
+ *   of_parse_clkspec(clock-consumer@3, 1, "name2", &args);
+ *
+ * Return: 0 upon successfully parsing the clock specifier. Otherwise, -ENOENT
+ * if @name is NULL or -EINVAL if @name is non-NULL and it can't be found in
+ * the "clock-names" property of @np.
  */
 static int of_parse_clkspec(const struct device_node *np, int index,
                            const char *name, struct of_phandle_args *out_args)