|
@@ -16,6 +16,7 @@
|
|
#include <linux/err.h>
|
|
#include <linux/err.h>
|
|
#include <linux/list.h>
|
|
#include <linux/list.h>
|
|
#include <linux/slab.h>
|
|
#include <linux/slab.h>
|
|
|
|
+#include <linux/of.h>
|
|
|
|
|
|
static DEFINE_SPINLOCK(enable_lock);
|
|
static DEFINE_SPINLOCK(enable_lock);
|
|
static DEFINE_MUTEX(prepare_lock);
|
|
static DEFINE_MUTEX(prepare_lock);
|
|
@@ -1550,3 +1551,142 @@ int clk_notifier_unregister(struct clk *clk, struct notifier_block *nb)
|
|
return ret;
|
|
return ret;
|
|
}
|
|
}
|
|
EXPORT_SYMBOL_GPL(clk_notifier_unregister);
|
|
EXPORT_SYMBOL_GPL(clk_notifier_unregister);
|
|
|
|
+
|
|
|
|
+#ifdef CONFIG_OF
|
|
|
|
+/**
|
|
|
|
+ * struct of_clk_provider - Clock provider registration structure
|
|
|
|
+ * @link: Entry in global list of clock providers
|
|
|
|
+ * @node: Pointer to device tree node of clock provider
|
|
|
|
+ * @get: Get clock callback. Returns NULL or a struct clk for the
|
|
|
|
+ * given clock specifier
|
|
|
|
+ * @data: context pointer to be passed into @get callback
|
|
|
|
+ */
|
|
|
|
+struct of_clk_provider {
|
|
|
|
+ struct list_head link;
|
|
|
|
+
|
|
|
|
+ struct device_node *node;
|
|
|
|
+ struct clk *(*get)(struct of_phandle_args *clkspec, void *data);
|
|
|
|
+ void *data;
|
|
|
|
+};
|
|
|
|
+
|
|
|
|
+static LIST_HEAD(of_clk_providers);
|
|
|
|
+static DEFINE_MUTEX(of_clk_lock);
|
|
|
|
+
|
|
|
|
+struct clk *of_clk_src_simple_get(struct of_phandle_args *clkspec,
|
|
|
|
+ void *data)
|
|
|
|
+{
|
|
|
|
+ return data;
|
|
|
|
+}
|
|
|
|
+EXPORT_SYMBOL_GPL(of_clk_src_simple_get);
|
|
|
|
+
|
|
|
|
+/**
|
|
|
|
+ * of_clk_add_provider() - Register a clock provider for a node
|
|
|
|
+ * @np: Device node pointer associated with clock provider
|
|
|
|
+ * @clk_src_get: callback for decoding clock
|
|
|
|
+ * @data: context pointer for @clk_src_get callback.
|
|
|
|
+ */
|
|
|
|
+int of_clk_add_provider(struct device_node *np,
|
|
|
|
+ struct clk *(*clk_src_get)(struct of_phandle_args *clkspec,
|
|
|
|
+ void *data),
|
|
|
|
+ void *data)
|
|
|
|
+{
|
|
|
|
+ struct of_clk_provider *cp;
|
|
|
|
+
|
|
|
|
+ cp = kzalloc(sizeof(struct of_clk_provider), GFP_KERNEL);
|
|
|
|
+ if (!cp)
|
|
|
|
+ return -ENOMEM;
|
|
|
|
+
|
|
|
|
+ cp->node = of_node_get(np);
|
|
|
|
+ cp->data = data;
|
|
|
|
+ cp->get = clk_src_get;
|
|
|
|
+
|
|
|
|
+ mutex_lock(&of_clk_lock);
|
|
|
|
+ list_add(&cp->link, &of_clk_providers);
|
|
|
|
+ mutex_unlock(&of_clk_lock);
|
|
|
|
+ pr_debug("Added clock from %s\n", np->full_name);
|
|
|
|
+
|
|
|
|
+ return 0;
|
|
|
|
+}
|
|
|
|
+EXPORT_SYMBOL_GPL(of_clk_add_provider);
|
|
|
|
+
|
|
|
|
+/**
|
|
|
|
+ * of_clk_del_provider() - Remove a previously registered clock provider
|
|
|
|
+ * @np: Device node pointer associated with clock provider
|
|
|
|
+ */
|
|
|
|
+void of_clk_del_provider(struct device_node *np)
|
|
|
|
+{
|
|
|
|
+ struct of_clk_provider *cp;
|
|
|
|
+
|
|
|
|
+ mutex_lock(&of_clk_lock);
|
|
|
|
+ list_for_each_entry(cp, &of_clk_providers, link) {
|
|
|
|
+ if (cp->node == np) {
|
|
|
|
+ list_del(&cp->link);
|
|
|
|
+ of_node_put(cp->node);
|
|
|
|
+ kfree(cp);
|
|
|
|
+ break;
|
|
|
|
+ }
|
|
|
|
+ }
|
|
|
|
+ mutex_unlock(&of_clk_lock);
|
|
|
|
+}
|
|
|
|
+EXPORT_SYMBOL_GPL(of_clk_del_provider);
|
|
|
|
+
|
|
|
|
+struct clk *of_clk_get_from_provider(struct of_phandle_args *clkspec)
|
|
|
|
+{
|
|
|
|
+ struct of_clk_provider *provider;
|
|
|
|
+ struct clk *clk = ERR_PTR(-ENOENT);
|
|
|
|
+
|
|
|
|
+ /* Check if we have such a provider in our array */
|
|
|
|
+ mutex_lock(&of_clk_lock);
|
|
|
|
+ list_for_each_entry(provider, &of_clk_providers, link) {
|
|
|
|
+ if (provider->node == clkspec->np)
|
|
|
|
+ clk = provider->get(clkspec, provider->data);
|
|
|
|
+ if (!IS_ERR(clk))
|
|
|
|
+ break;
|
|
|
|
+ }
|
|
|
|
+ mutex_unlock(&of_clk_lock);
|
|
|
|
+
|
|
|
|
+ return clk;
|
|
|
|
+}
|
|
|
|
+
|
|
|
|
+const char *of_clk_get_parent_name(struct device_node *np, int index)
|
|
|
|
+{
|
|
|
|
+ struct of_phandle_args clkspec;
|
|
|
|
+ const char *clk_name;
|
|
|
|
+ int rc;
|
|
|
|
+
|
|
|
|
+ if (index < 0)
|
|
|
|
+ return NULL;
|
|
|
|
+
|
|
|
|
+ rc = of_parse_phandle_with_args(np, "clocks", "#clock-cells", index,
|
|
|
|
+ &clkspec);
|
|
|
|
+ if (rc)
|
|
|
|
+ return NULL;
|
|
|
|
+
|
|
|
|
+ if (of_property_read_string_index(clkspec.np, "clock-output-names",
|
|
|
|
+ clkspec.args_count ? clkspec.args[0] : 0,
|
|
|
|
+ &clk_name) < 0)
|
|
|
|
+ clk_name = clkspec.np->name;
|
|
|
|
+
|
|
|
|
+ of_node_put(clkspec.np);
|
|
|
|
+ return clk_name;
|
|
|
|
+}
|
|
|
|
+EXPORT_SYMBOL_GPL(of_clk_get_parent_name);
|
|
|
|
+
|
|
|
|
+/**
|
|
|
|
+ * 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
|
|
|
|
+ */
|
|
|
|
+void __init of_clk_init(const struct of_device_id *matches)
|
|
|
|
+{
|
|
|
|
+ struct device_node *np;
|
|
|
|
+
|
|
|
|
+ for_each_matching_node(np, matches) {
|
|
|
|
+ const struct of_device_id *match = of_match_node(matches, np);
|
|
|
|
+ of_clk_init_cb_t clk_init_cb = match->data;
|
|
|
|
+ clk_init_cb(np);
|
|
|
|
+ }
|
|
|
|
+}
|
|
|
|
+#endif
|