From: Jeremy Kerr <jeremy.k...@canonical.com>

Based on work by Ben Herrenschmidt, this patch adds an of_clk_get
function to allow platforms to retrieve clock data from the device tree.

Platform register a provider through of_clk_add_provider, which will be
called when a device references the provider's OF node for a clock
reference.

Signed-off-by: Jeremy Kerr <jeremy.k...@canonical.com>
[grant.lik...@secretlab.ca: fix Kconfig conflict]
Signed-off-by: Grant Likely <grant.lik...@secretlab.ca>
---
 drivers/of/Kconfig     |    5 ++
 drivers/of/Makefile    |    1 
 drivers/of/clock.c     |  134 ++++++++++++++++++++++++++++++++++++++++++++++++
 include/linux/of_clk.h |   36 +++++++++++++
 4 files changed, 176 insertions(+), 0 deletions(-)
 create mode 100644 drivers/of/clock.c
 create mode 100644 include/linux/of_clk.h

diff --git a/drivers/of/Kconfig b/drivers/of/Kconfig
index 3c6e100..6650583 100644
--- a/drivers/of/Kconfig
+++ b/drivers/of/Kconfig
@@ -69,4 +69,9 @@ config OF_MDIO
        help
          OpenFirmware MDIO bus (Ethernet PHY) accessors
 
+config OF_CLOCK
+       def_bool y
+       depends on HAVE_CLK
+       help
+         OpenFirmware clock accessors
 endmenu # OF
diff --git a/drivers/of/Makefile b/drivers/of/Makefile
index 3ab21a0..c378c7b 100644
--- a/drivers/of/Makefile
+++ b/drivers/of/Makefile
@@ -5,6 +5,7 @@ obj-$(CONFIG_OF_ADDRESS)  += address.o
 obj-$(CONFIG_OF_IRQ)    += irq.o
 obj-$(CONFIG_OF_DEVICE) += device.o platform.o
 obj-$(CONFIG_OF_GPIO)   += gpio.o
+obj-$(CONFIG_OF_CLOCK) += clock.o
 obj-$(CONFIG_OF_I2C)   += of_i2c.o
 obj-$(CONFIG_OF_NET)   += of_net.o
 obj-$(CONFIG_OF_SPI)   += of_spi.o
diff --git a/drivers/of/clock.c b/drivers/of/clock.c
new file mode 100644
index 0000000..3e2b221
--- /dev/null
+++ b/drivers/of/clock.c
@@ -0,0 +1,134 @@
+/*
+ * Clock infrastructure for device tree platforms
+ */
+
+#include <linux/clk.h>
+#include <linux/err.h>
+#include <linux/errno.h>
+#include <linux/module.h>
+#include <linux/of.h>
+#include <linux/of_clk.h>
+#include <linux/list.h>
+#include <linux/mutex.h>
+#include <linux/slab.h>
+#include <linux/device.h>
+
+struct of_clk_provider {
+       struct list_head        link;
+       struct device_node      *node;
+
+       /* Return NULL if no such clock output */
+       struct clk              *(*get)(struct device_node *np,
+                                       const char *output_id, void *data);
+       void                    *data;
+};
+
+static LIST_HEAD(of_clk_providers);
+static DEFINE_MUTEX(of_clk_lock);
+
+int of_clk_add_provider(struct device_node *np,
+               struct clk *(*clk_src_get)(struct device_node *np,
+                       const char *output_id,
+                       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;
+}
+
+void of_clk_del_provider(struct device_node *np,
+               struct clk *(*clk_src_get)(struct device_node *np,
+                       const char *output_id,
+                       void *data),
+               void *data)
+{
+       struct of_clk_provider *cp, *tmp;
+
+       mutex_lock(&of_clk_lock);
+       list_for_each_entry_safe(cp, tmp, &of_clk_providers, link) {
+               if (cp->node == np && cp->get == clk_src_get &&
+                               cp->data == data) {
+                       list_del(&cp->link);
+                       of_node_put(cp->node);
+                       kfree(cp);
+                       break;
+               }
+       }
+       mutex_unlock(&of_clk_lock);
+}
+
+static struct clk *__of_clk_get_from_provider(struct device_node *np, const 
char *clk_output)
+{
+       struct of_clk_provider *provider;
+       struct clk *clk = NULL;
+
+       /* 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 == np)
+                       clk = provider->get(np, clk_output, provider->data);
+               if (clk)
+                       break;
+       }
+       mutex_unlock(&of_clk_lock);
+
+       return clk;
+}
+
+struct clk *of_clk_get(struct device *dev, const char *id)
+{
+       struct device_node *provnode;
+       u32 provhandle;
+       int sz;
+       struct clk *clk;
+       char prop_name[32]; /* 32 is max size of property name */
+       const void *prop;
+
+       dev_dbg(dev, "Looking up %s-clock from device tree\n", id);
+
+       snprintf(prop_name, 32, "%s-clock", id ? id : "bus");
+       prop = of_get_property(dev->of_node, prop_name, &sz);
+       if (!prop || sz < 4)
+               return NULL;
+
+       /* Extract the phandle from the start of the property value */
+       provhandle = be32_to_cpup(prop);
+       prop += 4;
+       sz -= 4;
+
+       /* Make sure the clock name is properly terminated and within the
+        * size of the property. */
+       if (strlen(prop) + 1 > sz)
+               return NULL;
+
+       /* Find the clock provider node; check if it is registered as a
+        * provider, and ask it for the relevant clk structure */
+       provnode = of_find_node_by_phandle(provhandle);
+       if (!provnode) {
+               pr_warn("%s: %s property in node %s references invalid phandle",
+                       __func__, prop_name, dev->of_node->full_name);
+               return NULL;
+       }
+       clk = __of_clk_get_from_provider(provnode, prop);
+       if (clk)
+               dev_dbg(dev, "Using clock from %s\n", provnode->full_name);
+
+       of_node_put(provnode);
+
+       return clk;
+}
+
diff --git a/include/linux/of_clk.h b/include/linux/of_clk.h
new file mode 100644
index 0000000..c2f6b5a
--- /dev/null
+++ b/include/linux/of_clk.h
@@ -0,0 +1,36 @@
+/*
+ * Clock infrastructure for device tree platforms
+ */
+#ifndef __OF_CLK_H
+#define __OF_CLK_H
+
+struct device;
+struct clk;
+
+#ifdef CONFIG_OF_CLOCK
+
+struct device_node;
+
+int of_clk_add_provider(struct device_node *np,
+               struct clk *(*clk_src_get)(struct device_node *np,
+                       const char *output_id,
+                       void *data),
+               void *data);
+
+void of_clk_del_provider(struct device_node *np,
+               struct clk *(*clk_src_get)(struct device_node *np,
+                       const char *output_id,
+                       void *data),
+               void *data);
+
+struct clk *of_clk_get(struct device *dev, const char *id);
+
+#else
+static inline struct clk *of_clk_get(struct device *dev, const char *id)
+{
+       return NULL;
+}
+#endif
+
+#endif /* __OF_CLK_H */
+

_______________________________________________
devicetree-discuss mailing list
devicetree-discuss@lists.ozlabs.org
https://lists.ozlabs.org/listinfo/devicetree-discuss

Reply via email to