PM / OPP: Initialize OPP table from device tree
authorShawn Guo <shawn.guo@linaro.org>
Tue, 4 Sep 2012 23:09:12 +0000 (01:09 +0200)
committerRafael J. Wysocki <rjw@sisk.pl>
Sun, 9 Sep 2012 20:06:33 +0000 (22:06 +0200)
With a lot of devices booting from device tree nowadays, it requires
that OPP table can be initialized from device tree.  The patch adds
a helper function of_init_opp_table together with a binding doc for
that purpose.

Signed-off-by: Shawn Guo <shawn.guo@linaro.org>
Acked-by: Rob Herring <rob.herring@calxeda.com>
Signed-off-by: Rafael J. Wysocki <rjw@sisk.pl>
Documentation/devicetree/bindings/power/opp.txt [new file with mode: 0644]
drivers/base/power/opp.c
include/linux/opp.h

diff --git a/Documentation/devicetree/bindings/power/opp.txt b/Documentation/devicetree/bindings/power/opp.txt
new file mode 100644 (file)
index 0000000..74499e5
--- /dev/null
@@ -0,0 +1,25 @@
+* Generic OPP Interface
+
+SoCs have a standard set of tuples consisting of frequency and
+voltage pairs that the device will support per voltage domain. These
+are called Operating Performance Points or OPPs.
+
+Properties:
+- operating-points: An array of 2-tuples items, and each item consists
+  of frequency and voltage like <freq-kHz vol-uV>.
+       freq: clock frequency in kHz
+       vol: voltage in microvolt
+
+Examples:
+
+cpu@0 {
+       compatible = "arm,cortex-a9";
+       reg = <0>;
+       next-level-cache = <&L2>;
+       operating-points = <
+               /* kHz    uV */
+               792000  1100000
+               396000  950000
+               198000  850000
+       >;
+};
index ac993eafec82ecd707cfc6e2654b5a8f30703fc6..d9468642fc414c22f8578c82e57e2cac9ba224e5 100644 (file)
@@ -22,6 +22,7 @@
 #include <linux/rculist.h>
 #include <linux/rcupdate.h>
 #include <linux/opp.h>
+#include <linux/of.h>
 
 /*
  * Internal data structure organization with the OPP layer library is as
@@ -674,3 +675,49 @@ struct srcu_notifier_head *opp_get_notifier(struct device *dev)
 
        return &dev_opp->head;
 }
+
+#ifdef CONFIG_OF
+/**
+ * of_init_opp_table() - Initialize opp table from device tree
+ * @dev:       device pointer used to lookup device OPPs.
+ *
+ * Register the initial OPP table with the OPP library for given device.
+ */
+int of_init_opp_table(struct device *dev)
+{
+       const struct property *prop;
+       const __be32 *val;
+       int nr;
+
+       prop = of_find_property(dev->of_node, "operating-points", NULL);
+       if (!prop)
+               return -ENODEV;
+       if (!prop->value)
+               return -ENODATA;
+
+       /*
+        * Each OPP is a set of tuples consisting of frequency and
+        * voltage like <freq-kHz vol-uV>.
+        */
+       nr = prop->length / sizeof(u32);
+       if (nr % 2) {
+               dev_err(dev, "%s: Invalid OPP list\n", __func__);
+               return -EINVAL;
+       }
+
+       val = prop->value;
+       while (nr) {
+               unsigned long freq = be32_to_cpup(val++) * 1000;
+               unsigned long volt = be32_to_cpup(val++);
+
+               if (opp_add(dev, freq, volt)) {
+                       dev_warn(dev, "%s: Failed to add OPP %ld\n",
+                                __func__, freq);
+                       continue;
+               }
+               nr -= 2;
+       }
+
+       return 0;
+}
+#endif
index 2a4e5faee904fcfd85fd54eba39901978bdeecf1..214e0ebcb84d85a80b31ee98bf4647eb25fd538c 100644 (file)
@@ -48,6 +48,14 @@ int opp_disable(struct device *dev, unsigned long freq);
 
 struct srcu_notifier_head *opp_get_notifier(struct device *dev);
 
+#ifdef CONFIG_OF
+int of_init_opp_table(struct device *dev);
+#else
+static inline int of_init_opp_table(struct device *dev)
+{
+       return -EINVAL;
+}
+#endif /* CONFIG_OF */
 #else
 static inline unsigned long opp_get_voltage(struct opp *opp)
 {