Commit b496dfbc authored by Shawn Guo's avatar Shawn Guo Committed by Rafael J. Wysocki
Browse files

PM / OPP: Initialize OPP table from device tree



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: default avatarShawn Guo <shawn.guo@linaro.org>
Acked-by: default avatarRob Herring <rob.herring@calxeda.com>
Signed-off-by: default avatarRafael J. Wysocki <rjw@sisk.pl>
parent ec971ea5
Loading
Loading
Loading
Loading
+25 −0
Original line number Diff line number Diff line
* 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
	>;
};
+47 −0
Original line number Diff line number Diff line
@@ -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
+8 −0
Original line number Diff line number Diff line
@@ -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)
{