All of lore.kernel.org
 help / color / mirror / Atom feed
* [RFC PATCH 0/2] OMAP: PM: add devices wakeup latency constraints APIs
@ 2011-02-10 19:23 jean.pihet
  2011-02-10 19:23 ` [PATCH 1/2] OMAP PM: create a PM layer plugin for the devices wakeup latency constraints jean.pihet
  2011-02-10 19:23 ` [PATCH 2/2] OMAP: PM: implement devices wakeup latency constraints APIs jean.pihet
  0 siblings, 2 replies; 19+ messages in thread
From: jean.pihet @ 2011-02-10 19:23 UTC (permalink / raw)
  To: khilman, paul, Vibhore Vardhan, Santosh Shilimkar, Vishwanath BS, rnayak
  Cc: linux-omap, Jean Pihet

From: Jean Pihet <j-pihet@ti.com>

- Implement OMAP PM layer omap_pm_set_max_dev_wakeup_lat API.
- Created arch/arm/plat-omap/omap-pm-constraints.c file from 
arch/arm/plat-omap/omap-pm-noop.c and the associated Kconfig option
OMAP_PM_CONSTRAINTS.

This code is an early submission in RFC state and is for review only.
Testing is on-going.

ToDo:
- implement the API for the SDMA device (omap_pm_set_max_sdma_lat),
- change the power domains latency figures to some more realistic values,
- add the hook to the existing kernel PM QoS interfaces (cpu_dma_latency),
   cf. Documentation/power/pm_qos_interface.txt

Jean Pihet (2):
  OMAP PM: create a PM layer plugin for the devices wakeup latency
    constraints
  OMAP: PM: implement devices wakeup latency constraints APIs

 arch/arm/mach-omap2/omap_hwmod.c              |   62 +++++-
 arch/arm/mach-omap2/powerdomain.c             |  176 +++++++++++++-
 arch/arm/mach-omap2/powerdomain.h             |   31 +++
 arch/arm/mach-omap2/powerdomains3xxx_data.c   |   60 +++++
 arch/arm/plat-omap/Kconfig                    |    7 +
 arch/arm/plat-omap/Makefile                   |    1 +
 arch/arm/plat-omap/include/plat/omap_device.h |    2 +
 arch/arm/plat-omap/include/plat/omap_hwmod.h  |    2 +
 arch/arm/plat-omap/omap-pm-constraints.c      |  344 +++++++++++++++++++++++++
 arch/arm/plat-omap/omap_device.c              |   28 ++
 10 files changed, 710 insertions(+), 3 deletions(-)
 create mode 100644 arch/arm/plat-omap/omap-pm-constraints.c

-- 
1.7.2.3


^ permalink raw reply	[flat|nested] 19+ messages in thread

* [PATCH 1/2] OMAP PM: create a PM layer plugin for the devices wakeup latency constraints
  2011-02-10 19:23 [RFC PATCH 0/2] OMAP: PM: add devices wakeup latency constraints APIs jean.pihet
@ 2011-02-10 19:23 ` jean.pihet
  2011-02-10 19:23 ` [PATCH 2/2] OMAP: PM: implement devices wakeup latency constraints APIs jean.pihet
  1 sibling, 0 replies; 19+ messages in thread
From: jean.pihet @ 2011-02-10 19:23 UTC (permalink / raw)
  To: khilman, paul, Vibhore Vardhan, Santosh Shilimkar, Vishwanath BS, rnayak
  Cc: linux-omap, Jean Pihet

From: Jean Pihet <j-pihet@ti.com>

Created arch/arm/plat-omap/omap-pm-constraints.c file from
arch/arm/plat-omap/omap-pm-noop.c and the associated Kconfig option
OMAP_PM_CONSTRAINTS.

Signed-off-by: Jean Pihet <j-pihet@ti.com>
---
 arch/arm/plat-omap/Kconfig               |    7 +
 arch/arm/plat-omap/Makefile              |    1 +
 arch/arm/plat-omap/omap-pm-constraints.c |  363 ++++++++++++++++++++++++++++++
 3 files changed, 371 insertions(+), 0 deletions(-)
 create mode 100644 arch/arm/plat-omap/omap-pm-constraints.c

diff --git a/arch/arm/plat-omap/Kconfig b/arch/arm/plat-omap/Kconfig
index b6333ae..b8f51e3 100644
--- a/arch/arm/plat-omap/Kconfig
+++ b/arch/arm/plat-omap/Kconfig
@@ -215,6 +215,13 @@ config OMAP_PM_NONE
 config OMAP_PM_NOOP
 	bool "No-op/debug PM layer"
 
+config OMAP_PM_CONSTRAINTS
+	depends on PM
+	bool "OMAP PM layer implementation, devices wakeup latency constraints"
+	help
+	  Select this option to enable the PM layer plugin for
+	  the devices wakeup latency constraints support
+
 endchoice
 
 endmenu
diff --git a/arch/arm/plat-omap/Makefile b/arch/arm/plat-omap/Makefile
index a4a1285..a293367 100644
--- a/arch/arm/plat-omap/Makefile
+++ b/arch/arm/plat-omap/Makefile
@@ -32,3 +32,4 @@ obj-y += $(i2c-omap-m) $(i2c-omap-y)
 obj-$(CONFIG_OMAP_MBOX_FWK) += mailbox.o
 
 obj-$(CONFIG_OMAP_PM_NOOP) += omap-pm-noop.o
+obj-$(CONFIG_OMAP_PM_CONSTRAINTS) += omap-pm-constraints.o
diff --git a/arch/arm/plat-omap/omap-pm-constraints.c b/arch/arm/plat-omap/omap-pm-constraints.c
new file mode 100644
index 0000000..c8b4e4c
--- /dev/null
+++ b/arch/arm/plat-omap/omap-pm-constraints.c
@@ -0,0 +1,363 @@
+/*
+ * omap-pm.c - OMAP power management interface
+ *
+ * This code implements the OMAP power management interface to
+ * drivers, CPUIdle, CPUFreq, and DSP Bridge.
+ *
+ * Copyright (C) 2008-2009 Texas Instruments, Inc.
+ * Copyright (C) 2008-2009 Nokia Corporation
+ * Paul Walmsley
+ *
+ * Interface developed by (in alphabetical order):
+ * Karthik Dasu, Tony Lindgren, Jean Pihet, Rajendra Nayak, Sakari Poussa,
+ * Veeramanikandan Raju, Anand Sawant, Igor Stoppa, Paul Walmsley,
+ * Richard Woodruff
+ */
+
+#undef DEBUG
+
+#include <linux/init.h>
+#include <linux/cpufreq.h>
+#include <linux/device.h>
+#include <linux/platform_device.h>
+
+/* Interface documentation is in mach/omap-pm.h */
+#include <plat/omap-pm.h>
+#include <plat/omap_device.h>
+
+static bool off_mode_enabled;
+static u32 dummy_context_loss_counter;
+
+/*
+ * Device-driver-originated constraints (via board-*.c files)
+ */
+
+int omap_pm_set_max_mpu_wakeup_lat(struct device *dev, long t)
+{
+	if (!dev || t < -1) {
+		WARN(1, "OMAP PM: %s: invalid parameter(s)", __func__);
+		return -EINVAL;
+	};
+
+	if (t == -1)
+		pr_debug("OMAP PM: remove max MPU wakeup latency constraint: "
+			 "dev %s\n", dev_name(dev));
+	else
+		pr_debug("OMAP PM: add max MPU wakeup latency constraint: "
+			 "dev %s, t = %ld usec\n", dev_name(dev), t);
+
+	/*
+	 * For current Linux, this needs to map the MPU to a
+	 * powerdomain, then go through the list of current max lat
+	 * constraints on the MPU and find the smallest.  If
+	 * the latency constraint has changed, the code should
+	 * recompute the state to enter for the next powerdomain
+	 * state.
+	 *
+	 * TI CDP code can call constraint_set here.
+	 */
+
+	return 0;
+}
+
+int omap_pm_set_min_bus_tput(struct device *dev, u8 agent_id, unsigned long r)
+{
+	if (!dev || (agent_id != OCP_INITIATOR_AGENT &&
+	    agent_id != OCP_TARGET_AGENT)) {
+		WARN(1, "OMAP PM: %s: invalid parameter(s)", __func__);
+		return -EINVAL;
+	};
+
+	if (r == 0)
+		pr_debug("OMAP PM: remove min bus tput constraint: "
+			 "dev %s for agent_id %d\n", dev_name(dev), agent_id);
+	else
+		pr_debug("OMAP PM: add min bus tput constraint: "
+			 "dev %s for agent_id %d: rate %ld KiB\n",
+			 dev_name(dev), agent_id, r);
+
+	/*
+	 * This code should model the interconnect and compute the
+	 * required clock frequency, convert that to a VDD2 OPP ID, then
+	 * set the VDD2 OPP appropriately.
+	 *
+	 * TI CDP code can call constraint_set here on the VDD2 OPP.
+	 */
+
+	return 0;
+}
+
+int omap_pm_set_max_dev_wakeup_lat(struct device *req_dev, struct device *dev,
+				   long t)
+{
+	if (!req_dev || !dev || t < -1) {
+		WARN(1, "OMAP PM: %s: invalid parameter(s)", __func__);
+		return -EINVAL;
+	};
+
+	if (t == -1)
+		pr_debug("OMAP PM: remove max device latency constraint: "
+			 "dev %s\n", dev_name(dev));
+	else
+		pr_debug("OMAP PM: add max device latency constraint: "
+			 "dev %s, t = %ld usec\n", dev_name(dev), t);
+
+	/*
+	 * For current Linux, this needs to map the device to a
+	 * powerdomain, then go through the list of current max lat
+	 * constraints on that powerdomain and find the smallest.  If
+	 * the latency constraint has changed, the code should
+	 * recompute the state to enter for the next powerdomain
+	 * state.  Conceivably, this code should also determine
+	 * whether to actually disable the device clocks or not,
+	 * depending on how long it takes to re-enable the clocks.
+	 *
+	 * TI CDP code can call constraint_set here.
+	 */
+
+	return 0;
+}
+
+int omap_pm_set_max_sdma_lat(struct device *dev, long t)
+{
+	if (!dev || t < -1) {
+		WARN(1, "OMAP PM: %s: invalid parameter(s)", __func__);
+		return -EINVAL;
+	};
+
+	if (t == -1)
+		pr_debug("OMAP PM: remove max DMA latency constraint: "
+			 "dev %s\n", dev_name(dev));
+	else
+		pr_debug("OMAP PM: add max DMA latency constraint: "
+			 "dev %s, t = %ld usec\n", dev_name(dev), t);
+
+	/*
+	 * For current Linux PM QOS params, this code should scan the
+	 * list of maximum CPU and DMA latencies and select the
+	 * smallest, then set cpu_dma_latency pm_qos_param
+	 * accordingly.
+	 *
+	 * For future Linux PM QOS params, with separate CPU and DMA
+	 * latency params, this code should just set the dma_latency param.
+	 *
+	 * TI CDP code can call constraint_set here.
+	 */
+
+	return 0;
+}
+
+int omap_pm_set_min_clk_rate(struct device *dev, struct clk *c, long r)
+{
+	if (!dev || !c || r < 0) {
+		WARN(1, "OMAP PM: %s: invalid parameter(s)", __func__);
+		return -EINVAL;
+	}
+
+	if (r == 0)
+		pr_debug("OMAP PM: remove min clk rate constraint: "
+			 "dev %s\n", dev_name(dev));
+	else
+		pr_debug("OMAP PM: add min clk rate constraint: "
+			 "dev %s, rate = %ld Hz\n", dev_name(dev), r);
+
+	/*
+	 * Code in a real implementation should keep track of these
+	 * constraints on the clock, and determine the highest minimum
+	 * clock rate.  It should iterate over each OPP and determine
+	 * whether the OPP will result in a clock rate that would
+	 * satisfy this constraint (and any other PM constraint in effect
+	 * at that time).  Once it finds the lowest-voltage OPP that
+	 * meets those conditions, it should switch to it, or return
+	 * an error if the code is not capable of doing so.
+	 */
+
+	return 0;
+}
+
+/*
+ * DSP Bridge-specific constraints
+ */
+
+const struct omap_opp *omap_pm_dsp_get_opp_table(void)
+{
+	pr_debug("OMAP PM: DSP request for OPP table\n");
+
+	/*
+	 * Return DSP frequency table here:  The final item in the
+	 * array should have .rate = .opp_id = 0.
+	 */
+
+	return NULL;
+}
+
+void omap_pm_dsp_set_min_opp(u8 opp_id)
+{
+	if (opp_id == 0) {
+		WARN_ON(1);
+		return;
+	}
+
+	pr_debug("OMAP PM: DSP requests minimum VDD1 OPP to be %d\n", opp_id);
+
+	/*
+	 *
+	 * For l-o dev tree, our VDD1 clk is keyed on OPP ID, so we
+	 * can just test to see which is higher, the CPU's desired OPP
+	 * ID or the DSP's desired OPP ID, and use whichever is
+	 * highest.
+	 *
+	 * In CDP12.14+, the VDD1 OPP custom clock that controls the DSP
+	 * rate is keyed on MPU speed, not the OPP ID.  So we need to
+	 * map the OPP ID to the MPU speed for use with clk_set_rate()
+	 * if it is higher than the current OPP clock rate.
+	 *
+	 */
+}
+
+
+u8 omap_pm_dsp_get_opp(void)
+{
+	pr_debug("OMAP PM: DSP requests current DSP OPP ID\n");
+
+	/*
+	 * For l-o dev tree, call clk_get_rate() on VDD1 OPP clock
+	 *
+	 * CDP12.14+:
+	 * Call clk_get_rate() on the OPP custom clock, map that to an
+	 * OPP ID using the tables defined in board-*.c/chip-*.c files.
+	 */
+
+	return 0;
+}
+
+/*
+ * CPUFreq-originated constraint
+ *
+ * In the future, this should be handled by custom OPP clocktype
+ * functions.
+ */
+
+struct cpufreq_frequency_table **omap_pm_cpu_get_freq_table(void)
+{
+	pr_debug("OMAP PM: CPUFreq request for frequency table\n");
+
+	/*
+	 * Return CPUFreq frequency table here: loop over
+	 * all VDD1 clkrates, pull out the mpu_ck frequencies, build
+	 * table
+	 */
+
+	return NULL;
+}
+
+void omap_pm_cpu_set_freq(unsigned long f)
+{
+	if (f == 0) {
+		WARN_ON(1);
+		return;
+	}
+
+	pr_debug("OMAP PM: CPUFreq requests CPU frequency to be set to %lu\n",
+		 f);
+
+	/*
+	 * For l-o dev tree, determine whether MPU freq or DSP OPP id
+	 * freq is higher.  Find the OPP ID corresponding to the
+	 * higher frequency.  Call clk_round_rate() and clk_set_rate()
+	 * on the OPP custom clock.
+	 *
+	 * CDP should just be able to set the VDD1 OPP clock rate here.
+	 */
+}
+
+unsigned long omap_pm_cpu_get_freq(void)
+{
+	pr_debug("OMAP PM: CPUFreq requests current CPU frequency\n");
+
+	/*
+	 * Call clk_get_rate() on the mpu_ck.
+	 */
+
+	return 0;
+}
+
+/**
+ * omap_pm_enable_off_mode - notify OMAP PM that off-mode is enabled
+ *
+ * Intended for use only by OMAP PM core code to notify this layer
+ * that off mode has been enabled.
+ */
+void omap_pm_enable_off_mode(void)
+{
+	off_mode_enabled = true;
+}
+
+/**
+ * omap_pm_disable_off_mode - notify OMAP PM that off-mode is disabled
+ *
+ * Intended for use only by OMAP PM core code to notify this layer
+ * that off mode has been disabled.
+ */
+void omap_pm_disable_off_mode(void)
+{
+	off_mode_enabled = false;
+}
+
+/*
+ * Device context loss tracking
+ */
+
+#ifdef CONFIG_ARCH_OMAP2PLUS
+
+u32 omap_pm_get_dev_context_loss_count(struct device *dev)
+{
+	struct platform_device *pdev = to_platform_device(dev);
+	u32 count;
+
+	if (WARN_ON(!dev))
+		return 0;
+
+	if (dev->parent == &omap_device_parent) {
+		count = omap_device_get_context_loss_count(pdev);
+	} else {
+		WARN_ONCE(off_mode_enabled, "omap_pm: using dummy context "
+			  "loss counter; device %s should be converted to "
+			  "omap_device", dev_name(dev));
+		if (off_mode_enabled)
+			dummy_context_loss_counter++;
+		count = dummy_context_loss_counter;
+	}
+
+	pr_debug("OMAP PM: context loss count for dev %s = %d\n",
+		 dev_name(dev), count);
+
+	return count;
+}
+
+#else
+
+u32 omap_pm_get_dev_context_loss_count(struct device *dev)
+{
+	return dummy_context_loss_counter;
+}
+
+#endif
+
+/* Should be called before clk framework init */
+int __init omap_pm_if_early_init(void)
+{
+	return 0;
+}
+
+/* Must be called after clock framework is initialized */
+int __init omap_pm_if_init(void)
+{
+	return 0;
+}
+
+void omap_pm_if_exit(void)
+{
+	/* Deallocate CPUFreq frequency table here */
+}
+
-- 
1.7.2.3


^ permalink raw reply related	[flat|nested] 19+ messages in thread

* [PATCH 2/2] OMAP: PM: implement devices wakeup latency constraints APIs
  2011-02-10 19:23 [RFC PATCH 0/2] OMAP: PM: add devices wakeup latency constraints APIs jean.pihet
  2011-02-10 19:23 ` [PATCH 1/2] OMAP PM: create a PM layer plugin for the devices wakeup latency constraints jean.pihet
@ 2011-02-10 19:23 ` jean.pihet
  2011-02-11 10:23   ` Gulati, Shweta
                     ` (2 more replies)
  1 sibling, 3 replies; 19+ messages in thread
From: jean.pihet @ 2011-02-10 19:23 UTC (permalink / raw)
  To: khilman, paul, Santosh Shilimkar, Vishwanath BS, rnayak
  Cc: linux-omap, Jean Pihet, Vibhore Vardhan

From: Jean Pihet <j-pihet@ti.com>

Implement OMAP PM layer omap_pm_set_max_dev_wakeup_lat API by
creating similar APIs at the omap_device and omap_hwmod levels. The
omap_hwmod level call is the layer with access to the powerdomain
core, so it is the place where the powerdomain is queried to set and
release the constraints.

NOTE: only works for devices which have been converted to use
      omap_device/omap_hwmod.

Longer term, we could possibly remove this API from the OMAP PM layer,
and instead directly use the omap_device level API.

Based on Vibhore's original patch , adapted to omap_device and
omap_hwmod frameworks.

Signed-off-by: Jean Pihet <j-pihet@ti.com>
Cc: Vibhore Vardhan <vvardhan@ti.com>
---
 arch/arm/mach-omap2/omap_hwmod.c              |   62 +++++++++-
 arch/arm/mach-omap2/powerdomain.c             |  176 ++++++++++++++++++++++++-
 arch/arm/mach-omap2/powerdomain.h             |   31 +++++
 arch/arm/mach-omap2/powerdomains3xxx_data.c   |   60 +++++++++
 arch/arm/plat-omap/include/plat/omap_device.h |    2 +
 arch/arm/plat-omap/include/plat/omap_hwmod.h  |    2 +
 arch/arm/plat-omap/omap-pm-constraints.c      |  101 ++++++---------
 arch/arm/plat-omap/omap_device.c              |   28 ++++
 8 files changed, 399 insertions(+), 63 deletions(-)

diff --git a/arch/arm/mach-omap2/omap_hwmod.c b/arch/arm/mach-omap2/omap_hwmod.c
index e282e35..0dc096f 100644
--- a/arch/arm/mach-omap2/omap_hwmod.c
+++ b/arch/arm/mach-omap2/omap_hwmod.c
@@ -142,6 +142,7 @@
 #include "powerdomain.h"
 #include <plat/clock.h>
 #include <plat/omap_hwmod.h>
+#include <plat/omap_device.h>
 #include <plat/prcm.h>
 
 #include "cm2xxx_3xxx.h"
@@ -2198,10 +2199,69 @@ ohsps_unlock:
 }
 
 /**
+ * omap_hwmod_set_max_dev_wakeup_lat - set a device wake-up constraint
+ * @oh: struct omap_hwmod *
+ * @req_oh: struct omap_hwmod *
+ * @t: wakeup latency constraint (us). -1 removes the existing constraint
+ *
+ * Query the powerdomain of @oh to set/release the wake-up constraint
+ *
+ * Returns -EINVAL if @oh or @req_oh have no power domain, or the return
+ * code from the pwrdm function (pwrdm_wakeuplat_set/release_constraint)
+ * of the powerdomain assocated with @oh.
+ */
+int omap_hwmod_set_max_dev_wakeup_lat(struct omap_hwmod *req_oh,
+				      struct omap_hwmod *oh, long t)
+{
+	struct device *req_dev;
+	struct platform_device *pdev;
+	struct powerdomain *pwrdm;
+	int ret = 0;
+
+	pwrdm = omap_hwmod_get_pwrdm(oh);
+
+	/* Catch devices with undefined powerdomains */
+	if (!pwrdm) {
+		pr_err("omap_hwmod: Error: could not find parent "
+			"powerdomain for %s\n", oh->name);
+		return -EINVAL;
+	}
+
+	pdev = &(req_oh->od->pdev);
+	if (pdev == NULL) {
+		pr_err("omap_hwmod: Error: pdev not found for oh %s\n",
+		       oh->name);
+		return -EINVAL;
+	}
+
+	req_dev = &(pdev->dev);
+	if (req_dev == NULL) {
+		pr_err("omap_hwmod: Error: device not found for oh %s\n",
+		       oh->name);
+		return -EINVAL;
+	}
+
+	/* Call set/release_constraint for the given pwrdm */
+	if (t == -1) {
+		pr_debug("omap_hwmod: remove max device latency constraint: "
+			 "oh %s, pwrdm %s, req by oh %s\n",
+			 oh->name, pwrdm->name, req_oh->name);
+		ret = pwrdm_wakeuplat_release_constraint(pwrdm, req_dev);
+	} else {
+		pr_debug("omap_hwmod: add max device latency constraint: "
+			 "oh %s, t = %ld usec, pwrdm %s, req by oh %s\n",
+			 oh->name, t, pwrdm->name, req_oh->name);
+		ret = pwrdm_wakeuplat_set_constraint(pwrdm, req_dev, t);
+	}
+
+	return 0;
+}
+
+/**
  * omap_hwmod_get_context_loss_count - get lost context count
  * @oh: struct omap_hwmod *
  *
- * Query the powerdomain of of @oh to get the context loss
+ * Query the powerdomain of @oh to get the context loss
  * count for this device.
  *
  * Returns the context loss count of the powerdomain assocated with @oh
diff --git a/arch/arm/mach-omap2/powerdomain.c b/arch/arm/mach-omap2/powerdomain.c
index eaed0df..3ed3bea 100644
--- a/arch/arm/mach-omap2/powerdomain.c
+++ b/arch/arm/mach-omap2/powerdomain.c
@@ -19,16 +19,19 @@
 #include <linux/list.h>
 #include <linux/errno.h>
 #include <linux/string.h>
+#include <linux/slab.h>
+
+#include <plat/cpu.h>
+#include <plat/prcm.h>
+
 #include "cm2xxx_3xxx.h"
 #include "prcm44xx.h"
 #include "cm44xx.h"
 #include "prm2xxx_3xxx.h"
 #include "prm44xx.h"
 
-#include <plat/cpu.h>
 #include "powerdomain.h"
 #include "clockdomain.h"
-#include <plat/prcm.h>
 
 #include "pm.h"
 
@@ -103,6 +106,13 @@ static int _pwrdm_register(struct powerdomain *pwrdm)
 	pwrdm->state = pwrdm_read_pwrst(pwrdm);
 	pwrdm->state_counter[pwrdm->state] = 1;
 
+	/* Initialize priority ordered list for wakeup latency constraint */
+	spin_lock_init(&pwrdm->wakeuplat_lock);
+	plist_head_init(&pwrdm->wakeuplat_dev_list, &pwrdm->wakeuplat_lock);
+
+	/* res_mutex protects res_list add and del ops */
+	mutex_init(&pwrdm->wakeuplat_mutex);
+
 	pr_debug("powerdomain: registered %s\n", pwrdm->name);
 
 	return 0;
@@ -176,6 +186,62 @@ static int _pwrdm_post_transition_cb(struct powerdomain *pwrdm, void *unused)
 	return 0;
 }
 
+/**
+ * pwrdm_wakeuplat_update_pwrst - Update power domain power state if needed
+ * @pwrdm: struct powerdomain * to which requesting device belongs to
+ *
+ * Finds minimum latency value from all entries in the list and
+ * the power domain power state needing the constraint. Programs
+ * new state if it is different from current power state.
+ */
+static void pwrdm_wakeuplat_update_pwrst(struct powerdomain *pwrdm)
+{
+	struct plist_node *node;
+	int new_state;
+	unsigned long min_latency = -1;
+
+	if (!plist_head_empty(&pwrdm->wakeuplat_dev_list)) {
+		node = plist_last(&pwrdm->wakeuplat_dev_list);
+		min_latency = node->prio;
+	}
+
+	/* Find power state with wakeup latency < minimum constraint. */
+	for (new_state = 0x0; new_state < PWRDM_MAX_PWRSTS; new_state++) {
+		if (min_latency == -1 ||
+		    pwrdm->wakeup_lat[new_state] <= min_latency)
+			break;
+	}
+
+	switch (new_state) {
+	case PWRDM_FUNC_PWRST_OFF:
+		new_state = PWRDM_POWER_OFF;
+		break;
+	case PWRDM_FUNC_PWRST_OSWR:
+		pwrdm_set_logic_retst(pwrdm, PWRDM_POWER_OFF);
+	case PWRDM_FUNC_PWRST_CSWR:
+		new_state = PWRDM_POWER_RET;
+		break;
+	case PWRDM_FUNC_PWRST_ON:
+		new_state = PWRDM_POWER_ON;
+		break;
+	default:
+		pr_warn("powerdomain: requested latency constraint not "
+			"supported %s set to ON state\n", pwrdm->name);
+		new_state = PWRDM_POWER_ON;
+		break;
+	}
+
+	if (pwrdm_read_pwrst(pwrdm) != new_state) {
+		if (cpu_is_omap34xx())
+			omap_set_pwrdm_state(pwrdm, new_state);
+	}
+
+	pr_debug("powerdomain: %s pwrst: curr= %d, prev= %d next= %d "
+		 "wkuplat_min= %lu, set_state= %d\n", pwrdm->name,
+		 pwrdm_read_pwrst(pwrdm), pwrdm_read_prev_pwrst(pwrdm),
+		 pwrdm_read_next_pwrst(pwrdm), min_latency, new_state);
+}
+
 /* Public functions */
 
 /**
@@ -911,6 +977,112 @@ int pwrdm_post_transition(void)
 }
 
 /**
+ * pwrdm_wakeuplat_set_constraint - Set powerdomain wakeup latency constraint
+ * @pwrdm: struct powerdomain * to which requesting device belongs to
+ * @dev: struct device * of requesting device
+ * @t: wakeup latency constraint in microseconds
+ *
+ * Adds new entry to powerdomain's wakeup latency constraint list.
+ * If the requesting device already exists in the list, old value is
+ * overwritten. Checks whether current power state is still adequate.
+ * Returns -EINVAL if the powerdomain or device pointer is NULL,
+ * or -ENOMEM if kmalloc fails, or returns 0 upon success.
+ */
+int pwrdm_wakeuplat_set_constraint(struct powerdomain *pwrdm,
+				   struct device *req_dev, unsigned long t)
+{
+	struct  wakeuplat_dev_list *user;
+	int found = 0, ret = 0;
+
+	if (!pwrdm || !req_dev) {
+		WARN(1, "powerdomain: %s: invalid parameter(s)", __func__);
+		return -EINVAL;
+	}
+
+	mutex_lock(&pwrdm->wakeuplat_mutex);
+
+	plist_for_each_entry(user, &pwrdm->wakeuplat_dev_list, node) {
+		if (user->dev == req_dev) {
+			found = 1;
+			break;
+		}
+	}
+
+	/* Add new entry to the list or update existing request */
+	if (found && user->constraint_us == t) {
+		goto exit_set;
+	} else if (!found) {
+		user = kzalloc(sizeof(struct wakeuplat_dev_list), GFP_KERNEL);
+		if (!user) {
+			pr_err("powerdomain: FATAL ERROR: kzalloc failed\n");
+			ret = -ENOMEM;
+			goto exit_set;
+		}
+		user->dev = req_dev;
+	} else {
+		plist_del(&user->node, &pwrdm->wakeuplat_dev_list);
+	}
+
+	plist_node_init(&user->node, t);
+	plist_add(&user->node, &pwrdm->wakeuplat_dev_list);
+	user->node.prio = user->constraint_us = t;
+
+	pwrdm_wakeuplat_update_pwrst(pwrdm);
+
+exit_set:
+	mutex_unlock(&pwrdm->wakeuplat_mutex);
+
+	return ret;
+}
+
+/**
+ * pwrdm_wakeuplat_release_constraint - Release powerdomain wkuplat constraint
+ * @pwrdm: struct powerdomain * to which requesting device belongs to
+ * @req_dev: struct device * of requesting device
+ *
+ * Removes device's entry from powerdomain's wakeup latency constraint list.
+ * Checks whether current power state is still adequate.
+ * Returns -EINVAL if the powerdomain or device pointer is NULL or
+ * no such entry exists in the list, or returns 0 upon success.
+ */
+int pwrdm_wakeuplat_release_constraint(struct powerdomain *pwrdm,
+				       struct device *req_dev)
+{
+	struct wakeuplat_dev_list *user;
+	int found = 0, ret = 0;
+
+	if (!pwrdm || !req_dev) {
+		WARN(1, "powerdomain: %s: invalid parameter(s)", __func__);
+		return -EINVAL;
+	}
+
+	mutex_lock(&pwrdm->wakeuplat_mutex);
+
+	plist_for_each_entry(user, &pwrdm->wakeuplat_dev_list, node) {
+		if (user->dev == req_dev) {
+			found = 1;
+			break;
+		}
+	}
+
+	if (!found) {
+		pr_err("OMAP PM: Error: no prior constraint to release\n");
+		ret = -EINVAL;
+		goto exit_rls;
+	}
+
+	plist_del(&user->node, &pwrdm->wakeuplat_dev_list);
+	kfree(user);
+
+	pwrdm_wakeuplat_update_pwrst(pwrdm);
+
+exit_rls:
+	mutex_unlock(&pwrdm->wakeuplat_mutex);
+
+	return ret;
+}
+
+/**
  * pwrdm_get_context_loss_count - get powerdomain's context loss count
  * @pwrdm: struct powerdomain * to wait for
  *
diff --git a/arch/arm/mach-omap2/powerdomain.h b/arch/arm/mach-omap2/powerdomain.h
index c66431e..d4189d8 100644
--- a/arch/arm/mach-omap2/powerdomain.h
+++ b/arch/arm/mach-omap2/powerdomain.h
@@ -19,6 +19,9 @@
 
 #include <linux/types.h>
 #include <linux/list.h>
+#include <linux/plist.h>
+#include <linux/mutex.h>
+#include <linux/spinlock.h>
 
 #include <linux/atomic.h>
 
@@ -46,6 +49,15 @@
 
 #define PWRSTS_OFF_RET_ON	(PWRSTS_OFF_RET | (1 << PWRDM_POWER_ON))
 
+/* Powerdomain functional power states */
+#define PWRDM_FUNC_PWRST_OFF	0x0
+#define PWRDM_FUNC_PWRST_OSWR	0x1
+#define PWRDM_FUNC_PWRST_CSWR	0x2
+#define PWRDM_FUNC_PWRST_ON	0x3
+
+#define PWRDM_MAX_FUNC_PWRSTS	4
+
+#define UNSUP_STATE		-1
 
 /* Powerdomain flags */
 #define PWRDM_HAS_HDWR_SAR	(1 << 0) /* hardware save-and-restore support */
@@ -96,6 +108,10 @@ struct powerdomain;
  * @state_counter:
  * @timer:
  * @state_timer:
+ * @wakeup_lat: Wakeup latencies for possible powerdomain power states
+ * @wakeuplat_lock: spinlock for plist
+ * @wakeuplat_dev_list: plist_head linking all devices placing constraint
+ * @wakeuplat_mutex: mutex to protect per powerdomain list ops
  *
  * @prcm_partition possible values are defined in mach-omap2/prcm44xx.h.
  */
@@ -121,6 +137,16 @@ struct powerdomain {
 	s64 timer;
 	s64 state_timer[PWRDM_MAX_PWRSTS];
 #endif
+	const u32 wakeup_lat[PWRDM_MAX_FUNC_PWRSTS];
+	spinlock_t wakeuplat_lock;
+	struct plist_head wakeuplat_dev_list;
+	struct mutex wakeuplat_mutex;
+};
+
+struct wakeuplat_dev_list {
+	struct device *dev;
+	unsigned long constraint_us;
+	struct plist_node node;
 };
 
 /**
@@ -211,6 +237,11 @@ int pwrdm_clkdm_state_switch(struct clockdomain *clkdm);
 int pwrdm_pre_transition(void);
 int pwrdm_post_transition(void);
 int pwrdm_set_lowpwrstchange(struct powerdomain *pwrdm);
+
+int pwrdm_wakeuplat_set_constraint(struct powerdomain *pwrdm,
+				   struct device *dev, unsigned long t);
+int pwrdm_wakeuplat_release_constraint(struct powerdomain *pwrdm,
+				       struct device *dev);
 u32 pwrdm_get_context_loss_count(struct powerdomain *pwrdm);
 
 extern void omap2xxx_powerdomains_init(void);
diff --git a/arch/arm/mach-omap2/powerdomains3xxx_data.c b/arch/arm/mach-omap2/powerdomains3xxx_data.c
index e1bec56..4f7e44d 100644
--- a/arch/arm/mach-omap2/powerdomains3xxx_data.c
+++ b/arch/arm/mach-omap2/powerdomains3xxx_data.c
@@ -52,6 +52,12 @@ static struct powerdomain iva2_pwrdm = {
 		[2] = PWRSTS_OFF_ON,
 		[3] = PWRDM_POWER_ON,
 	},
+	.wakeup_lat = {
+		[PWRDM_FUNC_PWRST_OFF] = 1100,
+		[PWRDM_FUNC_PWRST_OSWR] = UNSUP_STATE,
+		[PWRDM_FUNC_PWRST_CSWR] = 350,
+		[PWRDM_FUNC_PWRST_ON] = 0,
+	},
 };
 
 static struct powerdomain mpu_3xxx_pwrdm = {
@@ -68,6 +74,12 @@ static struct powerdomain mpu_3xxx_pwrdm = {
 	.pwrsts_mem_on	  = {
 		[0] = PWRSTS_OFF_ON,
 	},
+	.wakeup_lat = {
+		[PWRDM_FUNC_PWRST_OFF] = 95,
+		[PWRDM_FUNC_PWRST_OSWR] = UNSUP_STATE,
+		[PWRDM_FUNC_PWRST_CSWR] = 45,
+		[PWRDM_FUNC_PWRST_ON] = 0,
+	},
 };
 
 /*
@@ -98,6 +110,12 @@ static struct powerdomain core_3xxx_pre_es3_1_pwrdm = {
 		[0] = PWRSTS_OFF_RET_ON, /* MEM1ONSTATE */
 		[1] = PWRSTS_OFF_RET_ON, /* MEM2ONSTATE */
 	},
+	.wakeup_lat = {
+		[PWRDM_FUNC_PWRST_OFF] = 100,
+		[PWRDM_FUNC_PWRST_OSWR] = UNSUP_STATE,
+		[PWRDM_FUNC_PWRST_CSWR] = 60,
+		[PWRDM_FUNC_PWRST_ON] = 0,
+	},
 };
 
 static struct powerdomain core_3xxx_es3_1_pwrdm = {
@@ -121,6 +139,12 @@ static struct powerdomain core_3xxx_es3_1_pwrdm = {
 		[0] = PWRSTS_OFF_RET_ON, /* MEM1ONSTATE */
 		[1] = PWRSTS_OFF_RET_ON, /* MEM2ONSTATE */
 	},
+	.wakeup_lat = {
+		[PWRDM_FUNC_PWRST_OFF] = 100,
+		[PWRDM_FUNC_PWRST_OSWR] = UNSUP_STATE,
+		[PWRDM_FUNC_PWRST_CSWR] = 60,
+		[PWRDM_FUNC_PWRST_ON] = 0,
+	},
 };
 
 static struct powerdomain dss_pwrdm = {
@@ -136,6 +160,12 @@ static struct powerdomain dss_pwrdm = {
 	.pwrsts_mem_on	  = {
 		[0] = PWRDM_POWER_ON,  /* MEMONSTATE */
 	},
+	.wakeup_lat = {
+		[PWRDM_FUNC_PWRST_OFF] = 70,
+		[PWRDM_FUNC_PWRST_OSWR] = UNSUP_STATE,
+		[PWRDM_FUNC_PWRST_CSWR] = 20,
+		[PWRDM_FUNC_PWRST_ON] = 0,
+	},
 };
 
 /*
@@ -157,6 +187,12 @@ static struct powerdomain sgx_pwrdm = {
 	.pwrsts_mem_on	  = {
 		[0] = PWRDM_POWER_ON,  /* MEMONSTATE */
 	},
+	.wakeup_lat = {
+		[PWRDM_FUNC_PWRST_OFF] = 1000,
+		[PWRDM_FUNC_PWRST_OSWR] = UNSUP_STATE,
+		[PWRDM_FUNC_PWRST_CSWR] = UNSUP_STATE,
+		[PWRDM_FUNC_PWRST_ON] = 0,
+	},
 };
 
 static struct powerdomain cam_pwrdm = {
@@ -172,6 +208,12 @@ static struct powerdomain cam_pwrdm = {
 	.pwrsts_mem_on	  = {
 		[0] = PWRDM_POWER_ON,  /* MEMONSTATE */
 	},
+	.wakeup_lat = {
+		[PWRDM_FUNC_PWRST_OFF] = 850,
+		[PWRDM_FUNC_PWRST_OSWR] = UNSUP_STATE,
+		[PWRDM_FUNC_PWRST_CSWR] = 35,
+		[PWRDM_FUNC_PWRST_ON] = 0,
+	},
 };
 
 static struct powerdomain per_pwrdm = {
@@ -187,6 +229,12 @@ static struct powerdomain per_pwrdm = {
 	.pwrsts_mem_on	  = {
 		[0] = PWRDM_POWER_ON,  /* MEMONSTATE */
 	},
+	.wakeup_lat = {
+		[PWRDM_FUNC_PWRST_OFF] = 200,
+		[PWRDM_FUNC_PWRST_OSWR] = UNSUP_STATE,
+		[PWRDM_FUNC_PWRST_CSWR] = 110,
+		[PWRDM_FUNC_PWRST_ON] = 0,
+	},
 };
 
 static struct powerdomain emu_pwrdm = {
@@ -201,6 +249,12 @@ static struct powerdomain neon_pwrdm = {
 	.omap_chip	  = OMAP_CHIP_INIT(CHIP_IS_OMAP3430),
 	.pwrsts		  = PWRSTS_OFF_RET_ON,
 	.pwrsts_logic_ret = PWRDM_POWER_RET,
+	.wakeup_lat = {
+		[PWRDM_FUNC_PWRST_OFF] = 200,
+		[PWRDM_FUNC_PWRST_OSWR] = UNSUP_STATE,
+		[PWRDM_FUNC_PWRST_CSWR] = 35,
+		[PWRDM_FUNC_PWRST_ON] = 0,
+	},
 };
 
 static struct powerdomain usbhost_pwrdm = {
@@ -223,6 +277,12 @@ static struct powerdomain usbhost_pwrdm = {
 	.pwrsts_mem_on	  = {
 		[0] = PWRDM_POWER_ON,  /* MEMONSTATE */
 	},
+	.wakeup_lat = {
+		[PWRDM_FUNC_PWRST_OFF] = 800,
+		[PWRDM_FUNC_PWRST_OSWR] = UNSUP_STATE,
+		[PWRDM_FUNC_PWRST_CSWR] = 150,
+		[PWRDM_FUNC_PWRST_ON] = 0,
+	},
 };
 
 static struct powerdomain dpll1_pwrdm = {
diff --git a/arch/arm/plat-omap/include/plat/omap_device.h b/arch/arm/plat-omap/include/plat/omap_device.h
index e4c349f..5da6b47 100644
--- a/arch/arm/plat-omap/include/plat/omap_device.h
+++ b/arch/arm/plat-omap/include/plat/omap_device.h
@@ -107,6 +107,8 @@ void __iomem *omap_device_get_rt_va(struct omap_device *od);
 int omap_device_align_pm_lat(struct platform_device *pdev,
 			     u32 new_wakeup_lat_limit);
 struct powerdomain *omap_device_get_pwrdm(struct omap_device *od);
+int omap_device_set_max_dev_wakeup_lat(struct platform_device *req_pdev,
+				       struct platform_device *pdev, long t);
 u32 omap_device_get_context_loss_count(struct platform_device *pdev);
 
 /* Other */
diff --git a/arch/arm/plat-omap/include/plat/omap_hwmod.h b/arch/arm/plat-omap/include/plat/omap_hwmod.h
index 1eee85a..0fbb974 100644
--- a/arch/arm/plat-omap/include/plat/omap_hwmod.h
+++ b/arch/arm/plat-omap/include/plat/omap_hwmod.h
@@ -587,6 +587,8 @@ int omap_hwmod_for_each_by_class(const char *classname,
 				 void *user);
 
 int omap_hwmod_set_postsetup_state(struct omap_hwmod *oh, u8 state);
+int omap_hwmod_set_max_dev_wakeup_lat(struct omap_hwmod *req_oh,
+				      struct omap_hwmod *oh, long t);
 u32 omap_hwmod_get_context_loss_count(struct omap_hwmod *oh);
 
 /*
diff --git a/arch/arm/plat-omap/omap-pm-constraints.c b/arch/arm/plat-omap/omap-pm-constraints.c
index c8b4e4c..7ae855f 100644
--- a/arch/arm/plat-omap/omap-pm-constraints.c
+++ b/arch/arm/plat-omap/omap-pm-constraints.c
@@ -24,6 +24,7 @@
 /* Interface documentation is in mach/omap-pm.h */
 #include <plat/omap-pm.h>
 #include <plat/omap_device.h>
+#include <plat/common.h>
 
 static bool off_mode_enabled;
 static u32 dummy_context_loss_counter;
@@ -32,34 +33,6 @@ static u32 dummy_context_loss_counter;
  * Device-driver-originated constraints (via board-*.c files)
  */
 
-int omap_pm_set_max_mpu_wakeup_lat(struct device *dev, long t)
-{
-	if (!dev || t < -1) {
-		WARN(1, "OMAP PM: %s: invalid parameter(s)", __func__);
-		return -EINVAL;
-	};
-
-	if (t == -1)
-		pr_debug("OMAP PM: remove max MPU wakeup latency constraint: "
-			 "dev %s\n", dev_name(dev));
-	else
-		pr_debug("OMAP PM: add max MPU wakeup latency constraint: "
-			 "dev %s, t = %ld usec\n", dev_name(dev), t);
-
-	/*
-	 * For current Linux, this needs to map the MPU to a
-	 * powerdomain, then go through the list of current max lat
-	 * constraints on the MPU and find the smallest.  If
-	 * the latency constraint has changed, the code should
-	 * recompute the state to enter for the next powerdomain
-	 * state.
-	 *
-	 * TI CDP code can call constraint_set here.
-	 */
-
-	return 0;
-}
-
 int omap_pm_set_min_bus_tput(struct device *dev, u8 agent_id, unsigned long r)
 {
 	if (!dev || (agent_id != OCP_INITIATOR_AGENT &&
@@ -87,62 +60,70 @@ int omap_pm_set_min_bus_tput(struct device *dev, u8 agent_id, unsigned long r)
 	return 0;
 }
 
+/*
+ * omap_pm_set_max_dev_wakeup_lat - set/release devices wake-up latency
+ * constraints
+ */
 int omap_pm_set_max_dev_wakeup_lat(struct device *req_dev, struct device *dev,
 				   long t)
 {
+	struct platform_device *pdev, *req_pdev;
+	int ret = 0;
+
 	if (!req_dev || !dev || t < -1) {
 		WARN(1, "OMAP PM: %s: invalid parameter(s)", __func__);
 		return -EINVAL;
+	}
+
+	/* Look for the platform devices */
+	pdev = container_of(dev, struct platform_device, dev);
+	req_pdev = container_of(req_dev, struct platform_device, dev);
+
+	/* Try to catch non platform devices. */
+	if ((pdev->name == NULL) || (req_pdev->name == NULL)) {
+		pr_err("OMAP-PM set_wakeup_lat: Error: platform devices "
+		       "not valid\n");
+		return -EINVAL;
+	} else {
+		/* Call the omap_device API */
+		ret = omap_device_set_max_dev_wakeup_lat(req_pdev, pdev, t);
+	}
+
+	return ret;
+}
+
+int omap_pm_set_max_mpu_wakeup_lat(struct device *req_dev, long t)
+{
+	if (!req_dev || t < -1) {
+		WARN(1, "OMAP PM: %s: invalid parameter(s)", __func__);
+		return -EINVAL;
 	};
 
 	if (t == -1)
-		pr_debug("OMAP PM: remove max device latency constraint: "
-			 "dev %s\n", dev_name(dev));
+		pr_debug("OMAP PM: remove max MPU wakeup latency constraint: "
+			 "dev %s\n", dev_name(req_dev));
 	else
-		pr_debug("OMAP PM: add max device latency constraint: "
-			 "dev %s, t = %ld usec\n", dev_name(dev), t);
+		pr_debug("OMAP PM: add max MPU wakeup latency constraint: "
+			 "dev %s, t = %ld usec\n", dev_name(req_dev), t);
 
-	/*
-	 * For current Linux, this needs to map the device to a
-	 * powerdomain, then go through the list of current max lat
-	 * constraints on that powerdomain and find the smallest.  If
-	 * the latency constraint has changed, the code should
-	 * recompute the state to enter for the next powerdomain
-	 * state.  Conceivably, this code should also determine
-	 * whether to actually disable the device clocks or not,
-	 * depending on how long it takes to re-enable the clocks.
-	 *
-	 * TI CDP code can call constraint_set here.
-	 */
+	omap_pm_set_max_dev_wakeup_lat(req_dev, omap2_get_mpuss_device(), t);
 
 	return 0;
 }
 
-int omap_pm_set_max_sdma_lat(struct device *dev, long t)
+int omap_pm_set_max_sdma_lat(struct device *req_dev, long t)
 {
-	if (!dev || t < -1) {
+	if (!req_dev || t < -1) {
 		WARN(1, "OMAP PM: %s: invalid parameter(s)", __func__);
 		return -EINVAL;
 	};
 
 	if (t == -1)
 		pr_debug("OMAP PM: remove max DMA latency constraint: "
-			 "dev %s\n", dev_name(dev));
+			 "dev %s\n", dev_name(req_dev));
 	else
 		pr_debug("OMAP PM: add max DMA latency constraint: "
-			 "dev %s, t = %ld usec\n", dev_name(dev), t);
-
-	/*
-	 * For current Linux PM QOS params, this code should scan the
-	 * list of maximum CPU and DMA latencies and select the
-	 * smallest, then set cpu_dma_latency pm_qos_param
-	 * accordingly.
-	 *
-	 * For future Linux PM QOS params, with separate CPU and DMA
-	 * latency params, this code should just set the dma_latency param.
-	 *
-	 * TI CDP code can call constraint_set here.
-	 */
+			 "dev %s, t = %ld usec\n", dev_name(req_dev), t);
 
 	return 0;
 }
diff --git a/arch/arm/plat-omap/omap_device.c b/arch/arm/plat-omap/omap_device.c
index 57adb27..1fe93d3 100644
--- a/arch/arm/plat-omap/omap_device.c
+++ b/arch/arm/plat-omap/omap_device.c
@@ -280,6 +280,34 @@ static void _add_optional_clock_alias(struct omap_device *od,
 /* Public functions for use by core code */
 
 /**
+ * omap_device_set_max_dev_wakeup_lat - set/release a device constraint
+ * @od: struct omap_device *
+ * @req_od: struct omap_device *
+ *
+ * Using the primary hwmod, set/release a device constraint for the pdev
+ * device, requested by the req_pdev device.
+ *
+ * If any hwmods exist for the omap_device assoiated with @pdev and @req_pdev,
+ * set/release the constraint for the corresponding hwmods, otherwise return
+ * -EINVAL.
+ */
+int omap_device_set_max_dev_wakeup_lat(struct platform_device *req_pdev,
+				       struct platform_device *pdev, long t)
+{
+	struct omap_device *od, *req_od;
+	u32 ret = -EINVAL;
+
+	od = _find_by_pdev(pdev);
+	req_od = _find_by_pdev(req_pdev);
+
+	if ((od->hwmods_cnt) && (req_od->hwmods_cnt))
+		ret = omap_hwmod_set_max_dev_wakeup_lat(req_od->hwmods[0],
+							od->hwmods[0], t);
+
+	return ret;
+}
+
+/**
  * omap_device_get_context_loss_count - get lost context count
  * @od: struct omap_device *
  *
-- 
1.7.2.3


^ permalink raw reply related	[flat|nested] 19+ messages in thread

* Re: [PATCH 2/2] OMAP: PM: implement devices wakeup latency constraints APIs
  2011-02-10 19:23 ` [PATCH 2/2] OMAP: PM: implement devices wakeup latency constraints APIs jean.pihet
@ 2011-02-11 10:23   ` Gulati, Shweta
  2011-02-11 16:31     ` Jean Pihet
  2011-02-15  9:35   ` Vishwanath Sripathy
  2011-02-18 11:04   ` Rajendra Nayak
  2 siblings, 1 reply; 19+ messages in thread
From: Gulati, Shweta @ 2011-02-11 10:23 UTC (permalink / raw)
  To: jean.pihet
  Cc: khilman, paul, Vibhore Vardhan, Santosh Shilimkar, Vishwanath BS,
	rnayak, linux-omap, Jean Pihet

Jean,

On Fri, Feb 11, 2011 at 12:53 AM,  <jean.pihet@newoldbits.com> wrote:
> From: Jean Pihet <j-pihet@ti.com>
>
> Implement OMAP PM layer omap_pm_set_max_dev_wakeup_lat API by
> creating similar APIs at the omap_device and omap_hwmod levels. The
> omap_hwmod level call is the layer with access to the powerdomain
> core, so it is the place where the powerdomain is queried to set and
> release the constraints.
>
> NOTE: only works for devices which have been converted to use
>      omap_device/omap_hwmod.
>
> Longer term, we could possibly remove this API from the OMAP PM layer,
> and instead directly use the omap_device level API.
>
> Based on Vibhore's original patch , adapted to omap_device and
> omap_hwmod frameworks.
>
> Signed-off-by: Jean Pihet <j-pihet@ti.com>
> Cc: Vibhore Vardhan <vvardhan@ti.com>
> ---
>  arch/arm/mach-omap2/omap_hwmod.c              |   62 +++++++++-
>  arch/arm/mach-omap2/powerdomain.c             |  176 ++++++++++++++++++++++++-
>  arch/arm/mach-omap2/powerdomain.h             |   31 +++++
>  arch/arm/mach-omap2/powerdomains3xxx_data.c   |   60 +++++++++
>  arch/arm/plat-omap/include/plat/omap_device.h |    2 +
>  arch/arm/plat-omap/include/plat/omap_hwmod.h  |    2 +
>  arch/arm/plat-omap/omap-pm-constraints.c      |  101 ++++++---------
>  arch/arm/plat-omap/omap_device.c              |   28 ++++
>  8 files changed, 399 insertions(+), 63 deletions(-)
>
> diff --git a/arch/arm/mach-omap2/omap_hwmod.c b/arch/arm/mach-omap2/omap_hwmod.c
> index e282e35..0dc096f 100644
> --- a/arch/arm/mach-omap2/omap_hwmod.c
> +++ b/arch/arm/mach-omap2/omap_hwmod.c
> @@ -142,6 +142,7 @@
>  #include "powerdomain.h"
>  #include <plat/clock.h>
>  #include <plat/omap_hwmod.h>
> +#include <plat/omap_device.h>
>  #include <plat/prcm.h>
>
>  #include "cm2xxx_3xxx.h"
> @@ -2198,10 +2199,69 @@ ohsps_unlock:
>  }
>
>  /**
> + * omap_hwmod_set_max_dev_wakeup_lat - set a device wake-up constraint
> + * @oh: struct omap_hwmod *
> + * @req_oh: struct omap_hwmod *
> + * @t: wakeup latency constraint (us). -1 removes the existing constraint
> + *
> + * Query the powerdomain of @oh to set/release the wake-up constraint
> + *
> + * Returns -EINVAL if @oh or @req_oh have no power domain, or the return
> + * code from the pwrdm function (pwrdm_wakeuplat_set/release_constraint)
> + * of the powerdomain assocated with @oh.
> + */
> +int omap_hwmod_set_max_dev_wakeup_lat(struct omap_hwmod *req_oh,
> +                                     struct omap_hwmod *oh, long t)
> +{
> +       struct device *req_dev;
> +       struct platform_device *pdev;
> +       struct powerdomain *pwrdm;
> +       int ret = 0;
> +
> +       pwrdm = omap_hwmod_get_pwrdm(oh);
> +
> +       /* Catch devices with undefined powerdomains */
> +       if (!pwrdm) {
You can use IS_ERR macro.
> +               pr_err("omap_hwmod: Error: could not find parent "
> +                       "powerdomain for %s\n", oh->name);
> +               return -EINVAL;
> +       }
> +
> +       pdev = &(req_oh->od->pdev);
> +       if (pdev == NULL) {
same
> +               pr_err("omap_hwmod: Error: pdev not found for oh %s\n",
> +                      oh->name);
> +               return -EINVAL;
> +       }
> +
> +       req_dev = &(pdev->dev);
> +       if (req_dev == NULL) {
> +               pr_err("omap_hwmod: Error: device not found for oh %s\n",
> +                      oh->name);
> +               return -EINVAL;
> +       }
> +
> +       /* Call set/release_constraint for the given pwrdm */
> +       if (t == -1) {
> +               pr_debug("omap_hwmod: remove max device latency constraint: "
> +                        "oh %s, pwrdm %s, req by oh %s\n",
> +                        oh->name, pwrdm->name, req_oh->name);
> +               ret = pwrdm_wakeuplat_release_constraint(pwrdm, req_dev);
> +       } else {
> +               pr_debug("omap_hwmod: add max device latency constraint: "
> +                        "oh %s, t = %ld usec, pwrdm %s, req by oh %s\n",
> +                        oh->name, t, pwrdm->name, req_oh->name);
> +               ret = pwrdm_wakeuplat_set_constraint(pwrdm, req_dev, t);
> +       }
> +
> +       return 0;
What if 'pwrdm_wakeuplat_release_constraint' and '
pwrdm_wakeuplat_set_constraint' APIs fails in some error hitting
cases, then this API
should ideally return 'ret'.
> +}
> +
> +/**
>  * omap_hwmod_get_context_loss_count - get lost context count
>  * @oh: struct omap_hwmod *
>  *
> - * Query the powerdomain of of @oh to get the context loss
> + * Query the powerdomain of @oh to get the context loss
>  * count for this device.
>  *
>  * Returns the context loss count of the powerdomain assocated with @oh
> diff --git a/arch/arm/mach-omap2/powerdomain.c b/arch/arm/mach-omap2/powerdomain.c
> index eaed0df..3ed3bea 100644
> --- a/arch/arm/mach-omap2/powerdomain.c
> +++ b/arch/arm/mach-omap2/powerdomain.c
> @@ -19,16 +19,19 @@
>  #include <linux/list.h>
>  #include <linux/errno.h>
>  #include <linux/string.h>
> +#include <linux/slab.h>
> +
> +#include <plat/cpu.h>
> +#include <plat/prcm.h>
> +
>  #include "cm2xxx_3xxx.h"
>  #include "prcm44xx.h"
>  #include "cm44xx.h"
>  #include "prm2xxx_3xxx.h"
>  #include "prm44xx.h"
>
> -#include <plat/cpu.h>
>  #include "powerdomain.h"
>  #include "clockdomain.h"
> -#include <plat/prcm.h>
>
>  #include "pm.h"
>
> @@ -103,6 +106,13 @@ static int _pwrdm_register(struct powerdomain *pwrdm)
>        pwrdm->state = pwrdm_read_pwrst(pwrdm);
>        pwrdm->state_counter[pwrdm->state] = 1;
>
> +       /* Initialize priority ordered list for wakeup latency constraint */
> +       spin_lock_init(&pwrdm->wakeuplat_lock);
> +       plist_head_init(&pwrdm->wakeuplat_dev_list, &pwrdm->wakeuplat_lock);
> +
> +       /* res_mutex protects res_list add and del ops */
> +       mutex_init(&pwrdm->wakeuplat_mutex);
> +
>        pr_debug("powerdomain: registered %s\n", pwrdm->name);
>
>        return 0;
> @@ -176,6 +186,62 @@ static int _pwrdm_post_transition_cb(struct powerdomain *pwrdm, void *unused)
>        return 0;
>  }
>
> +/**
> + * pwrdm_wakeuplat_update_pwrst - Update power domain power state if needed
> + * @pwrdm: struct powerdomain * to which requesting device belongs to
> + *
> + * Finds minimum latency value from all entries in the list and
> + * the power domain power state needing the constraint. Programs
> + * new state if it is different from current power state.
> + */
It should return failure or success depending whether it could set
correct PD power state.
> +static void pwrdm_wakeuplat_update_pwrst(struct powerdomain *pwrdm)
> +{
> +       struct plist_node *node;
> +       int new_state;
> +       unsigned long min_latency = -1;
> +
> +       if (!plist_head_empty(&pwrdm->wakeuplat_dev_list)) {
> +               node = plist_last(&pwrdm->wakeuplat_dev_list);
> +               min_latency = node->prio;
> +       }
> +
> +       /* Find power state with wakeup latency < minimum constraint. */
> +       for (new_state = 0x0; new_state < PWRDM_MAX_PWRSTS; new_state++) {
> +               if (min_latency == -1 ||
> +                   pwrdm->wakeup_lat[new_state] <= min_latency)
> +                       break;
> +       }
> +
> +       switch (new_state) {
> +       case PWRDM_FUNC_PWRST_OFF:
> +               new_state = PWRDM_POWER_OFF;
> +               break;
> +       case PWRDM_FUNC_PWRST_OSWR:
> +               pwrdm_set_logic_retst(pwrdm, PWRDM_POWER_OFF);
> +       case PWRDM_FUNC_PWRST_CSWR:
> +               new_state = PWRDM_POWER_RET;
> +               break;
> +       case PWRDM_FUNC_PWRST_ON:
> +               new_state = PWRDM_POWER_ON;
> +               break;
> +       default:
> +               pr_warn("powerdomain: requested latency constraint not "
> +                       "supported %s set to ON state\n", pwrdm->name);
> +               new_state = PWRDM_POWER_ON;
> +               break;
> +       }
> +
> +       if (pwrdm_read_pwrst(pwrdm) != new_state) {
> +               if (cpu_is_omap34xx())
> +                       omap_set_pwrdm_state(pwrdm, new_state);
> +       }
> +
What if API 'omap_set_pwrdm_state fails to set new pwr_state?
> +       pr_debug("powerdomain: %s pwrst: curr= %d, prev= %d next= %d "
> +                "wkuplat_min= %lu, set_state= %d\n", pwrdm->name,
> +                pwrdm_read_pwrst(pwrdm), pwrdm_read_prev_pwrst(pwrdm),
> +                pwrdm_read_next_pwrst(pwrdm), min_latency, new_state);
> +}
> +
>  /* Public functions */
>
>  /**
> @@ -911,6 +977,112 @@ int pwrdm_post_transition(void)
>  }
>
>  /**
> + * pwrdm_wakeuplat_set_constraint - Set powerdomain wakeup latency constraint
> + * @pwrdm: struct powerdomain * to which requesting device belongs to
> + * @dev: struct device * of requesting device
> + * @t: wakeup latency constraint in microseconds
> + *
> + * Adds new entry to powerdomain's wakeup latency constraint list.
> + * If the requesting device already exists in the list, old value is
> + * overwritten. Checks whether current power state is still adequate.
> + * Returns -EINVAL if the powerdomain or device pointer is NULL,
> + * or -ENOMEM if kmalloc fails, or returns 0 upon success.
> + */
> +int pwrdm_wakeuplat_set_constraint(struct powerdomain *pwrdm,
> +                                  struct device *req_dev, unsigned long t)
> +{
> +       struct  wakeuplat_dev_list *user;
> +       int found = 0, ret = 0;
> +
> +       if (!pwrdm || !req_dev) {
> +               WARN(1, "powerdomain: %s: invalid parameter(s)", __func__);
> +               return -EINVAL;
> +       }
> +
> +       mutex_lock(&pwrdm->wakeuplat_mutex);
> +
> +       plist_for_each_entry(user, &pwrdm->wakeuplat_dev_list, node) {
> +               if (user->dev == req_dev) {
> +                       found = 1;
> +                       break;
> +               }
> +       }
> +
> +       /* Add new entry to the list or update existing request */
> +       if (found && user->constraint_us == t) {
> +               goto exit_set;
> +       } else if (!found) {
> +               user = kzalloc(sizeof(struct wakeuplat_dev_list), GFP_KERNEL);
> +               if (!user) {
> +                       pr_err("powerdomain: FATAL ERROR: kzalloc failed\n");
> +                       ret = -ENOMEM;
> +                       goto exit_set;
> +               }
> +               user->dev = req_dev;
> +       } else {
> +               plist_del(&user->node, &pwrdm->wakeuplat_dev_list);
> +       }
> +
> +       plist_node_init(&user->node, t);
> +       plist_add(&user->node, &pwrdm->wakeuplat_dev_list);
> +       user->node.prio = user->constraint_us = t;
> +
> +       pwrdm_wakeuplat_update_pwrst(pwrdm);
> +
> +exit_set:
> +       mutex_unlock(&pwrdm->wakeuplat_mutex);
> +
> +       return ret;

'ret' should be indicative of success or failure to set pwrdm
constraint, in this code  it
will return 0 only even if  'pwrdm_wakeuplat_update_pwrst' fails.
> +}
> +
> +/**
> + * pwrdm_wakeuplat_release_constraint - Release powerdomain wkuplat constraint
> + * @pwrdm: struct powerdomain * to which requesting device belongs to
> + * @req_dev: struct device * of requesting device
> + *
> + * Removes device's entry from powerdomain's wakeup latency constraint list.
> + * Checks whether current power state is still adequate.
> + * Returns -EINVAL if the powerdomain or device pointer is NULL or
> + * no such entry exists in the list, or returns 0 upon success.
> + */
> +int pwrdm_wakeuplat_release_constraint(struct powerdomain *pwrdm,
> +                                      struct device *req_dev)
> +{
> +       struct wakeuplat_dev_list *user;
> +       int found = 0, ret = 0;
> +
> +       if (!pwrdm || !req_dev) {
> +               WARN(1, "powerdomain: %s: invalid parameter(s)", __func__);
> +               return -EINVAL;
> +       }
> +
> +       mutex_lock(&pwrdm->wakeuplat_mutex);
> +
> +       plist_for_each_entry(user, &pwrdm->wakeuplat_dev_list, node) {
> +               if (user->dev == req_dev) {
> +                       found = 1;
> +                       break;
> +               }
> +       }
> +
> +       if (!found) {
> +               pr_err("OMAP PM: Error: no prior constraint to release\n");
> +               ret = -EINVAL;
> +               goto exit_rls;
> +       }
> +
> +       plist_del(&user->node, &pwrdm->wakeuplat_dev_list);
> +       kfree(user);
> +
> +       pwrdm_wakeuplat_update_pwrst(pwrdm);
> +
> +exit_rls:
> +       mutex_unlock(&pwrdm->wakeuplat_mutex);
> +
> +       return ret;
Same as above.
> +}
> +
> +/**
>  * pwrdm_get_context_loss_count - get powerdomain's context loss count
>  * @pwrdm: struct powerdomain * to wait for
>  *
> diff --git a/arch/arm/mach-omap2/powerdomain.h b/arch/arm/mach-omap2/powerdomain.h
> index c66431e..d4189d8 100644
> --- a/arch/arm/mach-omap2/powerdomain.h
> +++ b/arch/arm/mach-omap2/powerdomain.h
> @@ -19,6 +19,9 @@
>
>  #include <linux/types.h>
>  #include <linux/list.h>
> +#include <linux/plist.h>
> +#include <linux/mutex.h>
> +#include <linux/spinlock.h>
>
>  #include <linux/atomic.h>
>
> @@ -46,6 +49,15 @@
>
>  #define PWRSTS_OFF_RET_ON      (PWRSTS_OFF_RET | (1 << PWRDM_POWER_ON))
>
> +/* Powerdomain functional power states */
> +#define PWRDM_FUNC_PWRST_OFF   0x0
> +#define PWRDM_FUNC_PWRST_OSWR  0x1
> +#define PWRDM_FUNC_PWRST_CSWR  0x2
> +#define PWRDM_FUNC_PWRST_ON    0x3
> +
> +#define PWRDM_MAX_FUNC_PWRSTS  4
> +
> +#define UNSUP_STATE            -1
>
>  /* Powerdomain flags */
>  #define PWRDM_HAS_HDWR_SAR     (1 << 0) /* hardware save-and-restore support */
> @@ -96,6 +108,10 @@ struct powerdomain;
>  * @state_counter:
>  * @timer:
>  * @state_timer:
> + * @wakeup_lat: Wakeup latencies for possible powerdomain power states
> + * @wakeuplat_lock: spinlock for plist
> + * @wakeuplat_dev_list: plist_head linking all devices placing constraint
> + * @wakeuplat_mutex: mutex to protect per powerdomain list ops
>  *
>  * @prcm_partition possible values are defined in mach-omap2/prcm44xx.h.
>  */
> @@ -121,6 +137,16 @@ struct powerdomain {
>        s64 timer;
>        s64 state_timer[PWRDM_MAX_PWRSTS];
>  #endif
> +       const u32 wakeup_lat[PWRDM_MAX_FUNC_PWRSTS];
> +       spinlock_t wakeuplat_lock;
> +       struct plist_head wakeuplat_dev_list;
> +       struct mutex wakeuplat_mutex;
> +};
> +
> +struct wakeuplat_dev_list {
> +       struct device *dev;
> +       unsigned long constraint_us;
> +       struct plist_node node;
>  };
>
>  /**
> @@ -211,6 +237,11 @@ int pwrdm_clkdm_state_switch(struct clockdomain *clkdm);
>  int pwrdm_pre_transition(void);
>  int pwrdm_post_transition(void);
>  int pwrdm_set_lowpwrstchange(struct powerdomain *pwrdm);
> +
> +int pwrdm_wakeuplat_set_constraint(struct powerdomain *pwrdm,
> +                                  struct device *dev, unsigned long t);
> +int pwrdm_wakeuplat_release_constraint(struct powerdomain *pwrdm,
> +                                      struct device *dev);
>  u32 pwrdm_get_context_loss_count(struct powerdomain *pwrdm);
>
>  extern void omap2xxx_powerdomains_init(void);
> diff --git a/arch/arm/mach-omap2/powerdomains3xxx_data.c b/arch/arm/mach-omap2/powerdomains3xxx_data.c
> index e1bec56..4f7e44d 100644
> --- a/arch/arm/mach-omap2/powerdomains3xxx_data.c
> +++ b/arch/arm/mach-omap2/powerdomains3xxx_data.c
> @@ -52,6 +52,12 @@ static struct powerdomain iva2_pwrdm = {
>                [2] = PWRSTS_OFF_ON,
>                [3] = PWRDM_POWER_ON,
>        },
> +       .wakeup_lat = {
> +               [PWRDM_FUNC_PWRST_OFF] = 1100,
> +               [PWRDM_FUNC_PWRST_OSWR] = UNSUP_STATE,
> +               [PWRDM_FUNC_PWRST_CSWR] = 350,
> +               [PWRDM_FUNC_PWRST_ON] = 0,
> +       },
>  };
>
>  static struct powerdomain mpu_3xxx_pwrdm = {
> @@ -68,6 +74,12 @@ static struct powerdomain mpu_3xxx_pwrdm = {
>        .pwrsts_mem_on    = {
>                [0] = PWRSTS_OFF_ON,
>        },
> +       .wakeup_lat = {
> +               [PWRDM_FUNC_PWRST_OFF] = 95,
> +               [PWRDM_FUNC_PWRST_OSWR] = UNSUP_STATE,
> +               [PWRDM_FUNC_PWRST_CSWR] = 45,
> +               [PWRDM_FUNC_PWRST_ON] = 0,
> +       },
>  };
>
>  /*
> @@ -98,6 +110,12 @@ static struct powerdomain core_3xxx_pre_es3_1_pwrdm = {
>                [0] = PWRSTS_OFF_RET_ON, /* MEM1ONSTATE */
>                [1] = PWRSTS_OFF_RET_ON, /* MEM2ONSTATE */
>        },
> +       .wakeup_lat = {
> +               [PWRDM_FUNC_PWRST_OFF] = 100,
> +               [PWRDM_FUNC_PWRST_OSWR] = UNSUP_STATE,
> +               [PWRDM_FUNC_PWRST_CSWR] = 60,
> +               [PWRDM_FUNC_PWRST_ON] = 0,
> +       },
>  };
>
>  static struct powerdomain core_3xxx_es3_1_pwrdm = {
> @@ -121,6 +139,12 @@ static struct powerdomain core_3xxx_es3_1_pwrdm = {
>                [0] = PWRSTS_OFF_RET_ON, /* MEM1ONSTATE */
>                [1] = PWRSTS_OFF_RET_ON, /* MEM2ONSTATE */
>        },
> +       .wakeup_lat = {
> +               [PWRDM_FUNC_PWRST_OFF] = 100,
> +               [PWRDM_FUNC_PWRST_OSWR] = UNSUP_STATE,
> +               [PWRDM_FUNC_PWRST_CSWR] = 60,
> +               [PWRDM_FUNC_PWRST_ON] = 0,
> +       },
>  };
>
>  static struct powerdomain dss_pwrdm = {
> @@ -136,6 +160,12 @@ static struct powerdomain dss_pwrdm = {
>        .pwrsts_mem_on    = {
>                [0] = PWRDM_POWER_ON,  /* MEMONSTATE */
>        },
> +       .wakeup_lat = {
> +               [PWRDM_FUNC_PWRST_OFF] = 70,
> +               [PWRDM_FUNC_PWRST_OSWR] = UNSUP_STATE,
> +               [PWRDM_FUNC_PWRST_CSWR] = 20,
> +               [PWRDM_FUNC_PWRST_ON] = 0,
> +       },
>  };
>
>  /*
> @@ -157,6 +187,12 @@ static struct powerdomain sgx_pwrdm = {
>        .pwrsts_mem_on    = {
>                [0] = PWRDM_POWER_ON,  /* MEMONSTATE */
>        },
> +       .wakeup_lat = {
> +               [PWRDM_FUNC_PWRST_OFF] = 1000,
> +               [PWRDM_FUNC_PWRST_OSWR] = UNSUP_STATE,
> +               [PWRDM_FUNC_PWRST_CSWR] = UNSUP_STATE,
> +               [PWRDM_FUNC_PWRST_ON] = 0,
> +       },
>  };
>
>  static struct powerdomain cam_pwrdm = {
> @@ -172,6 +208,12 @@ static struct powerdomain cam_pwrdm = {
>        .pwrsts_mem_on    = {
>                [0] = PWRDM_POWER_ON,  /* MEMONSTATE */
>        },
> +       .wakeup_lat = {
> +               [PWRDM_FUNC_PWRST_OFF] = 850,
> +               [PWRDM_FUNC_PWRST_OSWR] = UNSUP_STATE,
> +               [PWRDM_FUNC_PWRST_CSWR] = 35,
> +               [PWRDM_FUNC_PWRST_ON] = 0,
> +       },
>  };
>
>  static struct powerdomain per_pwrdm = {
> @@ -187,6 +229,12 @@ static struct powerdomain per_pwrdm = {
>        .pwrsts_mem_on    = {
>                [0] = PWRDM_POWER_ON,  /* MEMONSTATE */
>        },
> +       .wakeup_lat = {
> +               [PWRDM_FUNC_PWRST_OFF] = 200,
> +               [PWRDM_FUNC_PWRST_OSWR] = UNSUP_STATE,
> +               [PWRDM_FUNC_PWRST_CSWR] = 110,
> +               [PWRDM_FUNC_PWRST_ON] = 0,
> +       },
>  };
>
>  static struct powerdomain emu_pwrdm = {
> @@ -201,6 +249,12 @@ static struct powerdomain neon_pwrdm = {
>        .omap_chip        = OMAP_CHIP_INIT(CHIP_IS_OMAP3430),
>        .pwrsts           = PWRSTS_OFF_RET_ON,
>        .pwrsts_logic_ret = PWRDM_POWER_RET,
> +       .wakeup_lat = {
> +               [PWRDM_FUNC_PWRST_OFF] = 200,
> +               [PWRDM_FUNC_PWRST_OSWR] = UNSUP_STATE,
> +               [PWRDM_FUNC_PWRST_CSWR] = 35,
> +               [PWRDM_FUNC_PWRST_ON] = 0,
> +       },
>  };
>
>  static struct powerdomain usbhost_pwrdm = {
> @@ -223,6 +277,12 @@ static struct powerdomain usbhost_pwrdm = {
>        .pwrsts_mem_on    = {
>                [0] = PWRDM_POWER_ON,  /* MEMONSTATE */
>        },
> +       .wakeup_lat = {
> +               [PWRDM_FUNC_PWRST_OFF] = 800,
> +               [PWRDM_FUNC_PWRST_OSWR] = UNSUP_STATE,
> +               [PWRDM_FUNC_PWRST_CSWR] = 150,
> +               [PWRDM_FUNC_PWRST_ON] = 0,
> +       },
>  };
>
>  static struct powerdomain dpll1_pwrdm = {
> diff --git a/arch/arm/plat-omap/include/plat/omap_device.h b/arch/arm/plat-omap/include/plat/omap_device.h
> index e4c349f..5da6b47 100644
> --- a/arch/arm/plat-omap/include/plat/omap_device.h
> +++ b/arch/arm/plat-omap/include/plat/omap_device.h
> @@ -107,6 +107,8 @@ void __iomem *omap_device_get_rt_va(struct omap_device *od);
>  int omap_device_align_pm_lat(struct platform_device *pdev,
>                             u32 new_wakeup_lat_limit);
>  struct powerdomain *omap_device_get_pwrdm(struct omap_device *od);
> +int omap_device_set_max_dev_wakeup_lat(struct platform_device *req_pdev,
> +                                      struct platform_device *pdev, long t);
>  u32 omap_device_get_context_loss_count(struct platform_device *pdev);
>
>  /* Other */
> diff --git a/arch/arm/plat-omap/include/plat/omap_hwmod.h b/arch/arm/plat-omap/include/plat/omap_hwmod.h
> index 1eee85a..0fbb974 100644
> --- a/arch/arm/plat-omap/include/plat/omap_hwmod.h
> +++ b/arch/arm/plat-omap/include/plat/omap_hwmod.h
> @@ -587,6 +587,8 @@ int omap_hwmod_for_each_by_class(const char *classname,
>                                 void *user);
>
>  int omap_hwmod_set_postsetup_state(struct omap_hwmod *oh, u8 state);
> +int omap_hwmod_set_max_dev_wakeup_lat(struct omap_hwmod *req_oh,
> +                                     struct omap_hwmod *oh, long t);
>  u32 omap_hwmod_get_context_loss_count(struct omap_hwmod *oh);
>
>  /*
> diff --git a/arch/arm/plat-omap/omap-pm-constraints.c b/arch/arm/plat-omap/omap-pm-constraints.c
> index c8b4e4c..7ae855f 100644
> --- a/arch/arm/plat-omap/omap-pm-constraints.c
> +++ b/arch/arm/plat-omap/omap-pm-constraints.c
> @@ -24,6 +24,7 @@
>  /* Interface documentation is in mach/omap-pm.h */
>  #include <plat/omap-pm.h>
>  #include <plat/omap_device.h>
> +#include <plat/common.h>
>
>  static bool off_mode_enabled;
>  static u32 dummy_context_loss_counter;
> @@ -32,34 +33,6 @@ static u32 dummy_context_loss_counter;
>  * Device-driver-originated constraints (via board-*.c files)
>  */
>
> -int omap_pm_set_max_mpu_wakeup_lat(struct device *dev, long t)
> -{
> -       if (!dev || t < -1) {
> -               WARN(1, "OMAP PM: %s: invalid parameter(s)", __func__);
> -               return -EINVAL;
> -       };
> -
> -       if (t == -1)
> -               pr_debug("OMAP PM: remove max MPU wakeup latency constraint: "
> -                        "dev %s\n", dev_name(dev));
> -       else
> -               pr_debug("OMAP PM: add max MPU wakeup latency constraint: "
> -                        "dev %s, t = %ld usec\n", dev_name(dev), t);
> -
> -       /*
> -        * For current Linux, this needs to map the MPU to a
> -        * powerdomain, then go through the list of current max lat
> -        * constraints on the MPU and find the smallest.  If
> -        * the latency constraint has changed, the code should
> -        * recompute the state to enter for the next powerdomain
> -        * state.
> -        *
> -        * TI CDP code can call constraint_set here.
> -        */
> -
> -       return 0;
> -}
> -
>  int omap_pm_set_min_bus_tput(struct device *dev, u8 agent_id, unsigned long r)
>  {
>        if (!dev || (agent_id != OCP_INITIATOR_AGENT &&
> @@ -87,62 +60,70 @@ int omap_pm_set_min_bus_tput(struct device *dev, u8 agent_id, unsigned long r)
>        return 0;
>  }
>
> +/*
> + * omap_pm_set_max_dev_wakeup_lat - set/release devices wake-up latency
> + * constraints
> + */
>  int omap_pm_set_max_dev_wakeup_lat(struct device *req_dev, struct device *dev,
>                                   long t)
>  {
> +       struct platform_device *pdev, *req_pdev;
> +       int ret = 0;
> +
>        if (!req_dev || !dev || t < -1) {
>                WARN(1, "OMAP PM: %s: invalid parameter(s)", __func__);
>                return -EINVAL;
> +       }
> +
> +       /* Look for the platform devices */
> +       pdev = container_of(dev, struct platform_device, dev);
> +       req_pdev = container_of(req_dev, struct platform_device, dev);
> +
> +       /* Try to catch non platform devices. */
> +       if ((pdev->name == NULL) || (req_pdev->name == NULL)) {
> +               pr_err("OMAP-PM set_wakeup_lat: Error: platform devices "
> +                      "not valid\n");
> +               return -EINVAL;
> +       } else {
> +               /* Call the omap_device API */
> +               ret = omap_device_set_max_dev_wakeup_lat(req_pdev, pdev, t);
> +       }
> +
> +       return ret;
> +}
> +
> +int omap_pm_set_max_mpu_wakeup_lat(struct device *req_dev, long t)
> +{
> +       if (!req_dev || t < -1) {
> +               WARN(1, "OMAP PM: %s: invalid parameter(s)", __func__);
> +               return -EINVAL;
>        };
>
>        if (t == -1)
> -               pr_debug("OMAP PM: remove max device latency constraint: "
> -                        "dev %s\n", dev_name(dev));
> +               pr_debug("OMAP PM: remove max MPU wakeup latency constraint: "
> +                        "dev %s\n", dev_name(req_dev));
>        else
> -               pr_debug("OMAP PM: add max device latency constraint: "
> -                        "dev %s, t = %ld usec\n", dev_name(dev), t);
> +               pr_debug("OMAP PM: add max MPU wakeup latency constraint: "
> +                        "dev %s, t = %ld usec\n", dev_name(req_dev), t);
>
> -       /*
> -        * For current Linux, this needs to map the device to a
> -        * powerdomain, then go through the list of current max lat
> -        * constraints on that powerdomain and find the smallest.  If
> -        * the latency constraint has changed, the code should
> -        * recompute the state to enter for the next powerdomain
> -        * state.  Conceivably, this code should also determine
> -        * whether to actually disable the device clocks or not,
> -        * depending on how long it takes to re-enable the clocks.
> -        *
> -        * TI CDP code can call constraint_set here.
> -        */
> +       omap_pm_set_max_dev_wakeup_lat(req_dev, omap2_get_mpuss_device(), t);
>
>        return 0;
>  }
>
> -int omap_pm_set_max_sdma_lat(struct device *dev, long t)
> +int omap_pm_set_max_sdma_lat(struct device *req_dev, long t)
>  {
> -       if (!dev || t < -1) {
> +       if (!req_dev || t < -1) {
>                WARN(1, "OMAP PM: %s: invalid parameter(s)", __func__);
>                return -EINVAL;
>        };
>
>        if (t == -1)
>                pr_debug("OMAP PM: remove max DMA latency constraint: "
> -                        "dev %s\n", dev_name(dev));
> +                        "dev %s\n", dev_name(req_dev));
>        else
>                pr_debug("OMAP PM: add max DMA latency constraint: "
> -                        "dev %s, t = %ld usec\n", dev_name(dev), t);
> -
> -       /*
> -        * For current Linux PM QOS params, this code should scan the
> -        * list of maximum CPU and DMA latencies and select the
> -        * smallest, then set cpu_dma_latency pm_qos_param
> -        * accordingly.
> -        *
> -        * For future Linux PM QOS params, with separate CPU and DMA
> -        * latency params, this code should just set the dma_latency param.
> -        *
> -        * TI CDP code can call constraint_set here.
> -        */
> +                        "dev %s, t = %ld usec\n", dev_name(req_dev), t);
>
>        return 0;
>  }
> diff --git a/arch/arm/plat-omap/omap_device.c b/arch/arm/plat-omap/omap_device.c
> index 57adb27..1fe93d3 100644
> --- a/arch/arm/plat-omap/omap_device.c
> +++ b/arch/arm/plat-omap/omap_device.c
> @@ -280,6 +280,34 @@ static void _add_optional_clock_alias(struct omap_device *od,
>  /* Public functions for use by core code */
>
>  /**
> + * omap_device_set_max_dev_wakeup_lat - set/release a device constraint
> + * @od: struct omap_device *
> + * @req_od: struct omap_device *
> + *
> + * Using the primary hwmod, set/release a device constraint for the pdev
> + * device, requested by the req_pdev device.
> + *
> + * If any hwmods exist for the omap_device assoiated with @pdev and @req_pdev,
> + * set/release the constraint for the corresponding hwmods, otherwise return
> + * -EINVAL.
> + */
> +int omap_device_set_max_dev_wakeup_lat(struct platform_device *req_pdev,
> +                                      struct platform_device *pdev, long t)
> +{
> +       struct omap_device *od, *req_od;
> +       u32 ret = -EINVAL;
> +
> +       od = _find_by_pdev(pdev);
> +       req_od = _find_by_pdev(req_pdev);
> +
> +       if ((od->hwmods_cnt) && (req_od->hwmods_cnt))
> +               ret = omap_hwmod_set_max_dev_wakeup_lat(req_od->hwmods[0],
> +                                                       od->hwmods[0], t);
> +
> +       return ret;
> +}
> +
> +/**
>  * omap_device_get_context_loss_count - get lost context count
>  * @od: struct omap_device *
>  *
> --
> 1.7.2.3
>
> --
> To unsubscribe from this list: send the line "unsubscribe linux-omap" in
> the body of a message to majordomo@vger.kernel.org
> More majordomo info at  http://vger.kernel.org/majordomo-info.html
>



-- 
Thanks,
Regards,
Shweta
--
To unsubscribe from this list: send the line "unsubscribe linux-omap" in
the body of a message to majordomo@vger.kernel.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html

^ permalink raw reply	[flat|nested] 19+ messages in thread

* Re: [PATCH 2/2] OMAP: PM: implement devices wakeup latency constraints APIs
  2011-02-11 10:23   ` Gulati, Shweta
@ 2011-02-11 16:31     ` Jean Pihet
  0 siblings, 0 replies; 19+ messages in thread
From: Jean Pihet @ 2011-02-11 16:31 UTC (permalink / raw)
  To: Gulati, Shweta
  Cc: khilman, paul, Vibhore Vardhan, Santosh Shilimkar, Vishwanath BS,
	rnayak, linux-omap, Jean Pihet

Hi Shweta,

Indeed the error checking is not good in this version.
I have inlined some comments.

On Fri, Feb 11, 2011 at 11:23 AM, Gulati, Shweta <shweta.gulati@ti.com> wrote:
> Jean,
>
> On Fri, Feb 11, 2011 at 12:53 AM,  <jean.pihet@newoldbits.com> wrote:
>> From: Jean Pihet <j-pihet@ti.com>
>>
>> Implement OMAP PM layer omap_pm_set_max_dev_wakeup_lat API by
>> creating similar APIs at the omap_device and omap_hwmod levels. The
>> omap_hwmod level call is the layer with access to the powerdomain
>> core, so it is the place where the powerdomain is queried to set and
>> release the constraints.
>>
>> NOTE: only works for devices which have been converted to use
>>      omap_device/omap_hwmod.
>>
>> Longer term, we could possibly remove this API from the OMAP PM layer,
>> and instead directly use the omap_device level API.
>>
>> Based on Vibhore's original patch , adapted to omap_device and
>> omap_hwmod frameworks.
>>
>> Signed-off-by: Jean Pihet <j-pihet@ti.com>
>> Cc: Vibhore Vardhan <vvardhan@ti.com>
>> ---
>>  arch/arm/mach-omap2/omap_hwmod.c              |   62 +++++++++-
>>  arch/arm/mach-omap2/powerdomain.c             |  176 ++++++++++++++++++++++++-
>>  arch/arm/mach-omap2/powerdomain.h             |   31 +++++
>>  arch/arm/mach-omap2/powerdomains3xxx_data.c   |   60 +++++++++
>>  arch/arm/plat-omap/include/plat/omap_device.h |    2 +
>>  arch/arm/plat-omap/include/plat/omap_hwmod.h  |    2 +
>>  arch/arm/plat-omap/omap-pm-constraints.c      |  101 ++++++---------
>>  arch/arm/plat-omap/omap_device.c              |   28 ++++
>>  8 files changed, 399 insertions(+), 63 deletions(-)
>>
>> diff --git a/arch/arm/mach-omap2/omap_hwmod.c b/arch/arm/mach-omap2/omap_hwmod.c
>> index e282e35..0dc096f 100644
>> --- a/arch/arm/mach-omap2/omap_hwmod.c
>> +++ b/arch/arm/mach-omap2/omap_hwmod.c
>> @@ -142,6 +142,7 @@
>>  #include "powerdomain.h"
>>  #include <plat/clock.h>
>>  #include <plat/omap_hwmod.h>
>> +#include <plat/omap_device.h>
>>  #include <plat/prcm.h>
>>
>>  #include "cm2xxx_3xxx.h"
>> @@ -2198,10 +2199,69 @@ ohsps_unlock:
>>  }
>>
>>  /**
>> + * omap_hwmod_set_max_dev_wakeup_lat - set a device wake-up constraint
>> + * @oh: struct omap_hwmod *
>> + * @req_oh: struct omap_hwmod *
>> + * @t: wakeup latency constraint (us). -1 removes the existing constraint
>> + *
>> + * Query the powerdomain of @oh to set/release the wake-up constraint
>> + *
>> + * Returns -EINVAL if @oh or @req_oh have no power domain, or the return
>> + * code from the pwrdm function (pwrdm_wakeuplat_set/release_constraint)
>> + * of the powerdomain assocated with @oh.
>> + */
>> +int omap_hwmod_set_max_dev_wakeup_lat(struct omap_hwmod *req_oh,
>> +                                     struct omap_hwmod *oh, long t)
>> +{
>> +       struct device *req_dev;
>> +       struct platform_device *pdev;
>> +       struct powerdomain *pwrdm;
>> +       int ret = 0;
>> +
>> +       pwrdm = omap_hwmod_get_pwrdm(oh);
>> +
>> +       /* Catch devices with undefined powerdomains */
>> +       if (!pwrdm) {
> You can use IS_ERR macro.
'if (!PTR_ERR(pwrdm)) {' will do it, because omap_hwmod_get_pwrdm
returns a *pwrdm. Fixed!

>> +               pr_err("omap_hwmod: Error: could not find parent "
>> +                       "powerdomain for %s\n", oh->name);
>> +               return -EINVAL;
>> +       }
>> +
>> +       pdev = &(req_oh->od->pdev);
>> +       if (pdev == NULL) {
> same
if (!PTR_ERR(pdev)) {

>> +               pr_err("omap_hwmod: Error: pdev not found for oh %s\n",
>> +                      oh->name);
>> +               return -EINVAL;
>> +       }
>> +
>> +       req_dev = &(pdev->dev);
>> +       if (req_dev == NULL) {
if (!PTR_ERR(req_dev)) {

>> +               pr_err("omap_hwmod: Error: device not found for oh %s\n",
>> +                      oh->name);
>> +               return -EINVAL;
>> +       }
>> +
>> +       /* Call set/release_constraint for the given pwrdm */
>> +       if (t == -1) {
>> +               pr_debug("omap_hwmod: remove max device latency constraint: "
>> +                        "oh %s, pwrdm %s, req by oh %s\n",
>> +                        oh->name, pwrdm->name, req_oh->name);
>> +               ret = pwrdm_wakeuplat_release_constraint(pwrdm, req_dev);
>> +       } else {
>> +               pr_debug("omap_hwmod: add max device latency constraint: "
>> +                        "oh %s, t = %ld usec, pwrdm %s, req by oh %s\n",
>> +                        oh->name, t, pwrdm->name, req_oh->name);
>> +               ret = pwrdm_wakeuplat_set_constraint(pwrdm, req_dev, t);
>> +       }
>> +
>> +       return 0;
> What if 'pwrdm_wakeuplat_release_constraint' and '
> pwrdm_wakeuplat_set_constraint' APIs fails in some error hitting
> cases, then this API
> should ideally return 'ret'.
Correct! Fixed

>> +}
>> +
>> +/**
>>  * omap_hwmod_get_context_loss_count - get lost context count
>>  * @oh: struct omap_hwmod *
>>  *
>> - * Query the powerdomain of of @oh to get the context loss
>> + * Query the powerdomain of @oh to get the context loss
>>  * count for this device.
>>  *
>>  * Returns the context loss count of the powerdomain assocated with @oh
>> diff --git a/arch/arm/mach-omap2/powerdomain.c b/arch/arm/mach-omap2/powerdomain.c
>> index eaed0df..3ed3bea 100644
>> --- a/arch/arm/mach-omap2/powerdomain.c
>> +++ b/arch/arm/mach-omap2/powerdomain.c
>> @@ -19,16 +19,19 @@
>>  #include <linux/list.h>
>>  #include <linux/errno.h>
>>  #include <linux/string.h>
>> +#include <linux/slab.h>
>> +
>> +#include <plat/cpu.h>
>> +#include <plat/prcm.h>
>> +
>>  #include "cm2xxx_3xxx.h"
>>  #include "prcm44xx.h"
>>  #include "cm44xx.h"
>>  #include "prm2xxx_3xxx.h"
>>  #include "prm44xx.h"
>>
>> -#include <plat/cpu.h>
>>  #include "powerdomain.h"
>>  #include "clockdomain.h"
>> -#include <plat/prcm.h>
>>
>>  #include "pm.h"
>>
>> @@ -103,6 +106,13 @@ static int _pwrdm_register(struct powerdomain *pwrdm)
>>        pwrdm->state = pwrdm_read_pwrst(pwrdm);
>>        pwrdm->state_counter[pwrdm->state] = 1;
>>
>> +       /* Initialize priority ordered list for wakeup latency constraint */
>> +       spin_lock_init(&pwrdm->wakeuplat_lock);
>> +       plist_head_init(&pwrdm->wakeuplat_dev_list, &pwrdm->wakeuplat_lock);
>> +
>> +       /* res_mutex protects res_list add and del ops */
>> +       mutex_init(&pwrdm->wakeuplat_mutex);
>> +
>>        pr_debug("powerdomain: registered %s\n", pwrdm->name);
>>
>>        return 0;
>> @@ -176,6 +186,62 @@ static int _pwrdm_post_transition_cb(struct powerdomain *pwrdm, void *unused)
>>        return 0;
>>  }
>>
>> +/**
>> + * pwrdm_wakeuplat_update_pwrst - Update power domain power state if needed
>> + * @pwrdm: struct powerdomain * to which requesting device belongs to
>> + *
>> + * Finds minimum latency value from all entries in the list and
>> + * the power domain power state needing the constraint. Programs
>> + * new state if it is different from current power state.
>> + */
> It should return failure or success depending whether it could set
> correct PD power state.
Fixed in the function comment

>> +static void pwrdm_wakeuplat_update_pwrst(struct powerdomain *pwrdm)
>> +{
>> +       struct plist_node *node;
>> +       int new_state;
>> +       unsigned long min_latency = -1;
>> +
>> +       if (!plist_head_empty(&pwrdm->wakeuplat_dev_list)) {
>> +               node = plist_last(&pwrdm->wakeuplat_dev_list);
>> +               min_latency = node->prio;
>> +       }
>> +
>> +       /* Find power state with wakeup latency < minimum constraint. */
>> +       for (new_state = 0x0; new_state < PWRDM_MAX_PWRSTS; new_state++) {
>> +               if (min_latency == -1 ||
>> +                   pwrdm->wakeup_lat[new_state] <= min_latency)
>> +                       break;
>> +       }
>> +
>> +       switch (new_state) {
>> +       case PWRDM_FUNC_PWRST_OFF:
>> +               new_state = PWRDM_POWER_OFF;
>> +               break;
>> +       case PWRDM_FUNC_PWRST_OSWR:
>> +               pwrdm_set_logic_retst(pwrdm, PWRDM_POWER_OFF);
>> +       case PWRDM_FUNC_PWRST_CSWR:
>> +               new_state = PWRDM_POWER_RET;
>> +               break;
>> +       case PWRDM_FUNC_PWRST_ON:
>> +               new_state = PWRDM_POWER_ON;
>> +               break;
>> +       default:
>> +               pr_warn("powerdomain: requested latency constraint not "
>> +                       "supported %s set to ON state\n", pwrdm->name);
>> +               new_state = PWRDM_POWER_ON;
>> +               break;
>> +       }
>> +
>> +       if (pwrdm_read_pwrst(pwrdm) != new_state) {
>> +               if (cpu_is_omap34xx())
>> +                       omap_set_pwrdm_state(pwrdm, new_state);
>> +       }
>> +
> What if API 'omap_set_pwrdm_state fails to set new pwr_state?
Fixed! The function now returns an int which is the return value of
omap_set_pwrdm_state.

>> +       pr_debug("powerdomain: %s pwrst: curr= %d, prev= %d next= %d "
>> +                "wkuplat_min= %lu, set_state= %d\n", pwrdm->name,
>> +                pwrdm_read_pwrst(pwrdm), pwrdm_read_prev_pwrst(pwrdm),
>> +                pwrdm_read_next_pwrst(pwrdm), min_latency, new_state);
>> +}
>> +
>>  /* Public functions */
>>
>>  /**
>> @@ -911,6 +977,112 @@ int pwrdm_post_transition(void)
>>  }
>>
>>  /**
>> + * pwrdm_wakeuplat_set_constraint - Set powerdomain wakeup latency constraint
>> + * @pwrdm: struct powerdomain * to which requesting device belongs to
>> + * @dev: struct device * of requesting device
>> + * @t: wakeup latency constraint in microseconds
>> + *
>> + * Adds new entry to powerdomain's wakeup latency constraint list.
>> + * If the requesting device already exists in the list, old value is
>> + * overwritten. Checks whether current power state is still adequate.
>> + * Returns -EINVAL if the powerdomain or device pointer is NULL,
>> + * or -ENOMEM if kmalloc fails, or returns 0 upon success.
>> + */
>> +int pwrdm_wakeuplat_set_constraint(struct powerdomain *pwrdm,
>> +                                  struct device *req_dev, unsigned long t)
>> +{
>> +       struct  wakeuplat_dev_list *user;
>> +       int found = 0, ret = 0;
>> +
>> +       if (!pwrdm || !req_dev) {
>> +               WARN(1, "powerdomain: %s: invalid parameter(s)", __func__);
>> +               return -EINVAL;
>> +       }
>> +
>> +       mutex_lock(&pwrdm->wakeuplat_mutex);
>> +
>> +       plist_for_each_entry(user, &pwrdm->wakeuplat_dev_list, node) {
>> +               if (user->dev == req_dev) {
>> +                       found = 1;
>> +                       break;
>> +               }
>> +       }
>> +
>> +       /* Add new entry to the list or update existing request */
>> +       if (found && user->constraint_us == t) {
>> +               goto exit_set;
>> +       } else if (!found) {
>> +               user = kzalloc(sizeof(struct wakeuplat_dev_list), GFP_KERNEL);
>> +               if (!user) {
>> +                       pr_err("powerdomain: FATAL ERROR: kzalloc failed\n");
>> +                       ret = -ENOMEM;
>> +                       goto exit_set;
>> +               }
>> +               user->dev = req_dev;
>> +       } else {
>> +               plist_del(&user->node, &pwrdm->wakeuplat_dev_list);
>> +       }
>> +
>> +       plist_node_init(&user->node, t);
>> +       plist_add(&user->node, &pwrdm->wakeuplat_dev_list);
>> +       user->node.prio = user->constraint_us = t;
>> +
>> +       pwrdm_wakeuplat_update_pwrst(pwrdm);
>> +
>> +exit_set:
>> +       mutex_unlock(&pwrdm->wakeuplat_mutex);
>> +
>> +       return ret;
>
> 'ret' should be indicative of success or failure to set pwrdm
> constraint, in this code  it
> will return 0 only even if  'pwrdm_wakeuplat_update_pwrst' fails.
Fixed with 'ret = pwrdm_wakeuplat_update_pwrst(pwrdm);'

>> +}
>> +
>> +/**
>> + * pwrdm_wakeuplat_release_constraint - Release powerdomain wkuplat constraint
>> + * @pwrdm: struct powerdomain * to which requesting device belongs to
>> + * @req_dev: struct device * of requesting device
>> + *
>> + * Removes device's entry from powerdomain's wakeup latency constraint list.
>> + * Checks whether current power state is still adequate.
>> + * Returns -EINVAL if the powerdomain or device pointer is NULL or
>> + * no such entry exists in the list, or returns 0 upon success.
>> + */
>> +int pwrdm_wakeuplat_release_constraint(struct powerdomain *pwrdm,
>> +                                      struct device *req_dev)
>> +{
>> +       struct wakeuplat_dev_list *user;
>> +       int found = 0, ret = 0;
>> +
>> +       if (!pwrdm || !req_dev) {
>> +               WARN(1, "powerdomain: %s: invalid parameter(s)", __func__);
>> +               return -EINVAL;
>> +       }
>> +
>> +       mutex_lock(&pwrdm->wakeuplat_mutex);
>> +
>> +       plist_for_each_entry(user, &pwrdm->wakeuplat_dev_list, node) {
>> +               if (user->dev == req_dev) {
>> +                       found = 1;
>> +                       break;
>> +               }
>> +       }
>> +
>> +       if (!found) {
>> +               pr_err("OMAP PM: Error: no prior constraint to release\n");
>> +               ret = -EINVAL;
>> +               goto exit_rls;
>> +       }
>> +
>> +       plist_del(&user->node, &pwrdm->wakeuplat_dev_list);
>> +       kfree(user);
>> +
>> +       pwrdm_wakeuplat_update_pwrst(pwrdm);
>> +
>> +exit_rls:
>> +       mutex_unlock(&pwrdm->wakeuplat_mutex);
>> +
>> +       return ret;
> Same as above.
Fixed as well.

>> +}
>> +
>> +/**
>>  * pwrdm_get_context_loss_count - get powerdomain's context loss count
>>  * @pwrdm: struct powerdomain * to wait for
>>  *
>> diff --git a/arch/arm/mach-omap2/powerdomain.h b/arch/arm/mach-omap2/powerdomain.h
>> index c66431e..d4189d8 100644
>> --- a/arch/arm/mach-omap2/powerdomain.h
>> +++ b/arch/arm/mach-omap2/powerdomain.h
>> @@ -19,6 +19,9 @@
>>
>>  #include <linux/types.h>
>>  #include <linux/list.h>
>> +#include <linux/plist.h>
>> +#include <linux/mutex.h>
>> +#include <linux/spinlock.h>
>>
>>  #include <linux/atomic.h>
>>
>> @@ -46,6 +49,15 @@
>>
>>  #define PWRSTS_OFF_RET_ON      (PWRSTS_OFF_RET | (1 << PWRDM_POWER_ON))
>>
>> +/* Powerdomain functional power states */
>> +#define PWRDM_FUNC_PWRST_OFF   0x0
>> +#define PWRDM_FUNC_PWRST_OSWR  0x1
>> +#define PWRDM_FUNC_PWRST_CSWR  0x2
>> +#define PWRDM_FUNC_PWRST_ON    0x3
>> +
>> +#define PWRDM_MAX_FUNC_PWRSTS  4
>> +
>> +#define UNSUP_STATE            -1
>>
>>  /* Powerdomain flags */
>>  #define PWRDM_HAS_HDWR_SAR     (1 << 0) /* hardware save-and-restore support */
>> @@ -96,6 +108,10 @@ struct powerdomain;
>>  * @state_counter:
>>  * @timer:
>>  * @state_timer:
>> + * @wakeup_lat: Wakeup latencies for possible powerdomain power states
>> + * @wakeuplat_lock: spinlock for plist
>> + * @wakeuplat_dev_list: plist_head linking all devices placing constraint
>> + * @wakeuplat_mutex: mutex to protect per powerdomain list ops
>>  *
>>  * @prcm_partition possible values are defined in mach-omap2/prcm44xx.h.
>>  */
>> @@ -121,6 +137,16 @@ struct powerdomain {
>>        s64 timer;
>>        s64 state_timer[PWRDM_MAX_PWRSTS];
>>  #endif
>> +       const u32 wakeup_lat[PWRDM_MAX_FUNC_PWRSTS];
>> +       spinlock_t wakeuplat_lock;
>> +       struct plist_head wakeuplat_dev_list;
>> +       struct mutex wakeuplat_mutex;
>> +};
>> +
>> +struct wakeuplat_dev_list {
>> +       struct device *dev;
>> +       unsigned long constraint_us;
>> +       struct plist_node node;
>>  };
>>
>>  /**
>> @@ -211,6 +237,11 @@ int pwrdm_clkdm_state_switch(struct clockdomain *clkdm);
>>  int pwrdm_pre_transition(void);
>>  int pwrdm_post_transition(void);
>>  int pwrdm_set_lowpwrstchange(struct powerdomain *pwrdm);
>> +
>> +int pwrdm_wakeuplat_set_constraint(struct powerdomain *pwrdm,
>> +                                  struct device *dev, unsigned long t);
>> +int pwrdm_wakeuplat_release_constraint(struct powerdomain *pwrdm,
>> +                                      struct device *dev);
>>  u32 pwrdm_get_context_loss_count(struct powerdomain *pwrdm);
>>
>>  extern void omap2xxx_powerdomains_init(void);
>> diff --git a/arch/arm/mach-omap2/powerdomains3xxx_data.c b/arch/arm/mach-omap2/powerdomains3xxx_data.c
>> index e1bec56..4f7e44d 100644
>> --- a/arch/arm/mach-omap2/powerdomains3xxx_data.c
>> +++ b/arch/arm/mach-omap2/powerdomains3xxx_data.c
>> @@ -52,6 +52,12 @@ static struct powerdomain iva2_pwrdm = {
>>                [2] = PWRSTS_OFF_ON,
>>                [3] = PWRDM_POWER_ON,
>>        },
>> +       .wakeup_lat = {
>> +               [PWRDM_FUNC_PWRST_OFF] = 1100,
>> +               [PWRDM_FUNC_PWRST_OSWR] = UNSUP_STATE,
>> +               [PWRDM_FUNC_PWRST_CSWR] = 350,
>> +               [PWRDM_FUNC_PWRST_ON] = 0,
>> +       },
>>  };
>>
>>  static struct powerdomain mpu_3xxx_pwrdm = {
>> @@ -68,6 +74,12 @@ static struct powerdomain mpu_3xxx_pwrdm = {
>>        .pwrsts_mem_on    = {
>>                [0] = PWRSTS_OFF_ON,
>>        },
>> +       .wakeup_lat = {
>> +               [PWRDM_FUNC_PWRST_OFF] = 95,
>> +               [PWRDM_FUNC_PWRST_OSWR] = UNSUP_STATE,
>> +               [PWRDM_FUNC_PWRST_CSWR] = 45,
>> +               [PWRDM_FUNC_PWRST_ON] = 0,
>> +       },
>>  };
>>
>>  /*
>> @@ -98,6 +110,12 @@ static struct powerdomain core_3xxx_pre_es3_1_pwrdm = {
>>                [0] = PWRSTS_OFF_RET_ON, /* MEM1ONSTATE */
>>                [1] = PWRSTS_OFF_RET_ON, /* MEM2ONSTATE */
>>        },
>> +       .wakeup_lat = {
>> +               [PWRDM_FUNC_PWRST_OFF] = 100,
>> +               [PWRDM_FUNC_PWRST_OSWR] = UNSUP_STATE,
>> +               [PWRDM_FUNC_PWRST_CSWR] = 60,
>> +               [PWRDM_FUNC_PWRST_ON] = 0,
>> +       },
>>  };
>>
>>  static struct powerdomain core_3xxx_es3_1_pwrdm = {
>> @@ -121,6 +139,12 @@ static struct powerdomain core_3xxx_es3_1_pwrdm = {
>>                [0] = PWRSTS_OFF_RET_ON, /* MEM1ONSTATE */
>>                [1] = PWRSTS_OFF_RET_ON, /* MEM2ONSTATE */
>>        },
>> +       .wakeup_lat = {
>> +               [PWRDM_FUNC_PWRST_OFF] = 100,
>> +               [PWRDM_FUNC_PWRST_OSWR] = UNSUP_STATE,
>> +               [PWRDM_FUNC_PWRST_CSWR] = 60,
>> +               [PWRDM_FUNC_PWRST_ON] = 0,
>> +       },
>>  };
>>
>>  static struct powerdomain dss_pwrdm = {
>> @@ -136,6 +160,12 @@ static struct powerdomain dss_pwrdm = {
>>        .pwrsts_mem_on    = {
>>                [0] = PWRDM_POWER_ON,  /* MEMONSTATE */
>>        },
>> +       .wakeup_lat = {
>> +               [PWRDM_FUNC_PWRST_OFF] = 70,
>> +               [PWRDM_FUNC_PWRST_OSWR] = UNSUP_STATE,
>> +               [PWRDM_FUNC_PWRST_CSWR] = 20,
>> +               [PWRDM_FUNC_PWRST_ON] = 0,
>> +       },
>>  };
>>
>>  /*
>> @@ -157,6 +187,12 @@ static struct powerdomain sgx_pwrdm = {
>>        .pwrsts_mem_on    = {
>>                [0] = PWRDM_POWER_ON,  /* MEMONSTATE */
>>        },
>> +       .wakeup_lat = {
>> +               [PWRDM_FUNC_PWRST_OFF] = 1000,
>> +               [PWRDM_FUNC_PWRST_OSWR] = UNSUP_STATE,
>> +               [PWRDM_FUNC_PWRST_CSWR] = UNSUP_STATE,
>> +               [PWRDM_FUNC_PWRST_ON] = 0,
>> +       },
>>  };
>>
>>  static struct powerdomain cam_pwrdm = {
>> @@ -172,6 +208,12 @@ static struct powerdomain cam_pwrdm = {
>>        .pwrsts_mem_on    = {
>>                [0] = PWRDM_POWER_ON,  /* MEMONSTATE */
>>        },
>> +       .wakeup_lat = {
>> +               [PWRDM_FUNC_PWRST_OFF] = 850,
>> +               [PWRDM_FUNC_PWRST_OSWR] = UNSUP_STATE,
>> +               [PWRDM_FUNC_PWRST_CSWR] = 35,
>> +               [PWRDM_FUNC_PWRST_ON] = 0,
>> +       },
>>  };
>>
>>  static struct powerdomain per_pwrdm = {
>> @@ -187,6 +229,12 @@ static struct powerdomain per_pwrdm = {
>>        .pwrsts_mem_on    = {
>>                [0] = PWRDM_POWER_ON,  /* MEMONSTATE */
>>        },
>> +       .wakeup_lat = {
>> +               [PWRDM_FUNC_PWRST_OFF] = 200,
>> +               [PWRDM_FUNC_PWRST_OSWR] = UNSUP_STATE,
>> +               [PWRDM_FUNC_PWRST_CSWR] = 110,
>> +               [PWRDM_FUNC_PWRST_ON] = 0,
>> +       },
>>  };
>>
>>  static struct powerdomain emu_pwrdm = {
>> @@ -201,6 +249,12 @@ static struct powerdomain neon_pwrdm = {
>>        .omap_chip        = OMAP_CHIP_INIT(CHIP_IS_OMAP3430),
>>        .pwrsts           = PWRSTS_OFF_RET_ON,
>>        .pwrsts_logic_ret = PWRDM_POWER_RET,
>> +       .wakeup_lat = {
>> +               [PWRDM_FUNC_PWRST_OFF] = 200,
>> +               [PWRDM_FUNC_PWRST_OSWR] = UNSUP_STATE,
>> +               [PWRDM_FUNC_PWRST_CSWR] = 35,
>> +               [PWRDM_FUNC_PWRST_ON] = 0,
>> +       },
>>  };
>>
>>  static struct powerdomain usbhost_pwrdm = {
>> @@ -223,6 +277,12 @@ static struct powerdomain usbhost_pwrdm = {
>>        .pwrsts_mem_on    = {
>>                [0] = PWRDM_POWER_ON,  /* MEMONSTATE */
>>        },
>> +       .wakeup_lat = {
>> +               [PWRDM_FUNC_PWRST_OFF] = 800,
>> +               [PWRDM_FUNC_PWRST_OSWR] = UNSUP_STATE,
>> +               [PWRDM_FUNC_PWRST_CSWR] = 150,
>> +               [PWRDM_FUNC_PWRST_ON] = 0,
>> +       },
>>  };
>>
>>  static struct powerdomain dpll1_pwrdm = {
>> diff --git a/arch/arm/plat-omap/include/plat/omap_device.h b/arch/arm/plat-omap/include/plat/omap_device.h
>> index e4c349f..5da6b47 100644
>> --- a/arch/arm/plat-omap/include/plat/omap_device.h
>> +++ b/arch/arm/plat-omap/include/plat/omap_device.h
>> @@ -107,6 +107,8 @@ void __iomem *omap_device_get_rt_va(struct omap_device *od);
>>  int omap_device_align_pm_lat(struct platform_device *pdev,
>>                             u32 new_wakeup_lat_limit);
>>  struct powerdomain *omap_device_get_pwrdm(struct omap_device *od);
>> +int omap_device_set_max_dev_wakeup_lat(struct platform_device *req_pdev,
>> +                                      struct platform_device *pdev, long t);
>>  u32 omap_device_get_context_loss_count(struct platform_device *pdev);
>>
>>  /* Other */
>> diff --git a/arch/arm/plat-omap/include/plat/omap_hwmod.h b/arch/arm/plat-omap/include/plat/omap_hwmod.h
>> index 1eee85a..0fbb974 100644
>> --- a/arch/arm/plat-omap/include/plat/omap_hwmod.h
>> +++ b/arch/arm/plat-omap/include/plat/omap_hwmod.h
>> @@ -587,6 +587,8 @@ int omap_hwmod_for_each_by_class(const char *classname,
>>                                 void *user);
>>
>>  int omap_hwmod_set_postsetup_state(struct omap_hwmod *oh, u8 state);
>> +int omap_hwmod_set_max_dev_wakeup_lat(struct omap_hwmod *req_oh,
>> +                                     struct omap_hwmod *oh, long t);
>>  u32 omap_hwmod_get_context_loss_count(struct omap_hwmod *oh);
>>
>>  /*
>> diff --git a/arch/arm/plat-omap/omap-pm-constraints.c b/arch/arm/plat-omap/omap-pm-constraints.c
>> index c8b4e4c..7ae855f 100644
>> --- a/arch/arm/plat-omap/omap-pm-constraints.c
>> +++ b/arch/arm/plat-omap/omap-pm-constraints.c
>> @@ -24,6 +24,7 @@
>>  /* Interface documentation is in mach/omap-pm.h */
>>  #include <plat/omap-pm.h>
>>  #include <plat/omap_device.h>
>> +#include <plat/common.h>
>>
>>  static bool off_mode_enabled;
>>  static u32 dummy_context_loss_counter;
>> @@ -32,34 +33,6 @@ static u32 dummy_context_loss_counter;
>>  * Device-driver-originated constraints (via board-*.c files)
>>  */
>>
>> -int omap_pm_set_max_mpu_wakeup_lat(struct device *dev, long t)
>> -{
>> -       if (!dev || t < -1) {
>> -               WARN(1, "OMAP PM: %s: invalid parameter(s)", __func__);
>> -               return -EINVAL;
>> -       };
>> -
>> -       if (t == -1)
>> -               pr_debug("OMAP PM: remove max MPU wakeup latency constraint: "
>> -                        "dev %s\n", dev_name(dev));
>> -       else
>> -               pr_debug("OMAP PM: add max MPU wakeup latency constraint: "
>> -                        "dev %s, t = %ld usec\n", dev_name(dev), t);
>> -
>> -       /*
>> -        * For current Linux, this needs to map the MPU to a
>> -        * powerdomain, then go through the list of current max lat
>> -        * constraints on the MPU and find the smallest.  If
>> -        * the latency constraint has changed, the code should
>> -        * recompute the state to enter for the next powerdomain
>> -        * state.
>> -        *
>> -        * TI CDP code can call constraint_set here.
>> -        */
>> -
>> -       return 0;
>> -}
>> -
>>  int omap_pm_set_min_bus_tput(struct device *dev, u8 agent_id, unsigned long r)
>>  {
>>        if (!dev || (agent_id != OCP_INITIATOR_AGENT &&
>> @@ -87,62 +60,70 @@ int omap_pm_set_min_bus_tput(struct device *dev, u8 agent_id, unsigned long r)
>>        return 0;
>>  }
>>
>> +/*
>> + * omap_pm_set_max_dev_wakeup_lat - set/release devices wake-up latency
>> + * constraints
>> + */
>>  int omap_pm_set_max_dev_wakeup_lat(struct device *req_dev, struct device *dev,
>>                                   long t)
>>  {
>> +       struct platform_device *pdev, *req_pdev;
>> +       int ret = 0;
>> +
>>        if (!req_dev || !dev || t < -1) {
>>                WARN(1, "OMAP PM: %s: invalid parameter(s)", __func__);
>>                return -EINVAL;
>> +       }
>> +
>> +       /* Look for the platform devices */
>> +       pdev = container_of(dev, struct platform_device, dev);
>> +       req_pdev = container_of(req_dev, struct platform_device, dev);
>> +
>> +       /* Try to catch non platform devices. */
>> +       if ((pdev->name == NULL) || (req_pdev->name == NULL)) {
>> +               pr_err("OMAP-PM set_wakeup_lat: Error: platform devices "
>> +                      "not valid\n");
>> +               return -EINVAL;
>> +       } else {
>> +               /* Call the omap_device API */
>> +               ret = omap_device_set_max_dev_wakeup_lat(req_pdev, pdev, t);
>> +       }
>> +
>> +       return ret;
>> +}
>> +
>> +int omap_pm_set_max_mpu_wakeup_lat(struct device *req_dev, long t)
>> +{
>> +       if (!req_dev || t < -1) {
>> +               WARN(1, "OMAP PM: %s: invalid parameter(s)", __func__);
>> +               return -EINVAL;
>>        };
>>
>>        if (t == -1)
>> -               pr_debug("OMAP PM: remove max device latency constraint: "
>> -                        "dev %s\n", dev_name(dev));
>> +               pr_debug("OMAP PM: remove max MPU wakeup latency constraint: "
>> +                        "dev %s\n", dev_name(req_dev));
>>        else
>> -               pr_debug("OMAP PM: add max device latency constraint: "
>> -                        "dev %s, t = %ld usec\n", dev_name(dev), t);
>> +               pr_debug("OMAP PM: add max MPU wakeup latency constraint: "
>> +                        "dev %s, t = %ld usec\n", dev_name(req_dev), t);
>>
>> -       /*
>> -        * For current Linux, this needs to map the device to a
>> -        * powerdomain, then go through the list of current max lat
>> -        * constraints on that powerdomain and find the smallest.  If
>> -        * the latency constraint has changed, the code should
>> -        * recompute the state to enter for the next powerdomain
>> -        * state.  Conceivably, this code should also determine
>> -        * whether to actually disable the device clocks or not,
>> -        * depending on how long it takes to re-enable the clocks.
>> -        *
>> -        * TI CDP code can call constraint_set here.
>> -        */
>> +       omap_pm_set_max_dev_wakeup_lat(req_dev, omap2_get_mpuss_device(), t);
>>
>>        return 0;
Changed to:
        return omap_pm_set_max_dev_wakeup_lat(req_dev,
                                              omap2_get_mpuss_device(), t);

>>  }
>>
>> -int omap_pm_set_max_sdma_lat(struct device *dev, long t)
>> +int omap_pm_set_max_sdma_lat(struct device *req_dev, long t)
>>  {
>> -       if (!dev || t < -1) {
>> +       if (!req_dev || t < -1) {
>>                WARN(1, "OMAP PM: %s: invalid parameter(s)", __func__);
>>                return -EINVAL;
>>        };
>>
>>        if (t == -1)
>>                pr_debug("OMAP PM: remove max DMA latency constraint: "
>> -                        "dev %s\n", dev_name(dev));
>> +                        "dev %s\n", dev_name(req_dev));
>>        else
>>                pr_debug("OMAP PM: add max DMA latency constraint: "
>> -                        "dev %s, t = %ld usec\n", dev_name(dev), t);
>> -
>> -       /*
>> -        * For current Linux PM QOS params, this code should scan the
>> -        * list of maximum CPU and DMA latencies and select the
>> -        * smallest, then set cpu_dma_latency pm_qos_param
>> -        * accordingly.
>> -        *
>> -        * For future Linux PM QOS params, with separate CPU and DMA
>> -        * latency params, this code should just set the dma_latency param.
>> -        *
>> -        * TI CDP code can call constraint_set here.
>> -        */
>> +                        "dev %s, t = %ld usec\n", dev_name(req_dev), t);
>>
>>        return 0;
>>  }
>> diff --git a/arch/arm/plat-omap/omap_device.c b/arch/arm/plat-omap/omap_device.c
>> index 57adb27..1fe93d3 100644
>> --- a/arch/arm/plat-omap/omap_device.c
>> +++ b/arch/arm/plat-omap/omap_device.c
>> @@ -280,6 +280,34 @@ static void _add_optional_clock_alias(struct omap_device *od,
>>  /* Public functions for use by core code */
>>
>>  /**
>> + * omap_device_set_max_dev_wakeup_lat - set/release a device constraint
>> + * @od: struct omap_device *
>> + * @req_od: struct omap_device *
>> + *
>> + * Using the primary hwmod, set/release a device constraint for the pdev
>> + * device, requested by the req_pdev device.
>> + *
>> + * If any hwmods exist for the omap_device assoiated with @pdev and @req_pdev,
>> + * set/release the constraint for the corresponding hwmods, otherwise return
>> + * -EINVAL.
>> + */
>> +int omap_device_set_max_dev_wakeup_lat(struct platform_device *req_pdev,
>> +                                      struct platform_device *pdev, long t)
>> +{
>> +       struct omap_device *od, *req_od;
>> +       u32 ret = -EINVAL;
>> +
>> +       od = _find_by_pdev(pdev);
>> +       req_od = _find_by_pdev(req_pdev);
>> +
>> +       if ((od->hwmods_cnt) && (req_od->hwmods_cnt))
>> +               ret = omap_hwmod_set_max_dev_wakeup_lat(req_od->hwmods[0],
>> +                                                       od->hwmods[0], t);
>> +
>> +       return ret;
>> +}
>> +
>> +/**
>>  * omap_device_get_context_loss_count - get lost context count
>>  * @od: struct omap_device *
>>  *
>> --
>> 1.7.2.3
>>
>> --
>> To unsubscribe from this list: send the line "unsubscribe linux-omap" in
>> the body of a message to majordomo@vger.kernel.org
>> More majordomo info at  http://vger.kernel.org/majordomo-info.html
>>
>
>
>
> --
> Thanks,
> Regards,
> Shweta
>

Will submit the new version soon.
Thanks for reviewing!

Regards,
Jean
--
To unsubscribe from this list: send the line "unsubscribe linux-omap" in
the body of a message to majordomo@vger.kernel.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html

^ permalink raw reply	[flat|nested] 19+ messages in thread

* RE: [PATCH 2/2] OMAP: PM: implement devices wakeup latency constraints APIs
  2011-02-10 19:23 ` [PATCH 2/2] OMAP: PM: implement devices wakeup latency constraints APIs jean.pihet
  2011-02-11 10:23   ` Gulati, Shweta
@ 2011-02-15  9:35   ` Vishwanath Sripathy
  2011-02-15 13:52     ` Jean Pihet
  2011-02-18 11:04   ` Rajendra Nayak
  2 siblings, 1 reply; 19+ messages in thread
From: Vishwanath Sripathy @ 2011-02-15  9:35 UTC (permalink / raw)
  To: jean.pihet, Kevin Hilman, paul, Vibhore Vardhan,
	Santosh Shilimkar, Rajendra Nayak
  Cc: linux-omap, Jean Pihet-XID

> -----Original Message-----
> From: jean.pihet@newoldbits.com [mailto:jean.pihet@newoldbits.com]
> Sent: Friday, February 11, 2011 12:53 AM
> To: khilman@ti.com; paul@pwsan.com; Vibhore Vardhan; Santosh
> Shilimkar; Vishwanath BS; rnayak@ti.com
> Cc: linux-omap@vger.kernel.org; Jean Pihet; Vibhore Vardhan
> Subject: [PATCH 2/2] OMAP: PM: implement devices wakeup latency
> constraints APIs
>
> From: Jean Pihet <j-pihet@ti.com>
>
> Implement OMAP PM layer omap_pm_set_max_dev_wakeup_lat API by
> creating similar APIs at the omap_device and omap_hwmod levels. The
> omap_hwmod level call is the layer with access to the powerdomain
> core, so it is the place where the powerdomain is queried to set and
> release the constraints.
>
> NOTE: only works for devices which have been converted to use
>       omap_device/omap_hwmod.
>
> Longer term, we could possibly remove this API from the OMAP PM layer,
> and instead directly use the omap_device level API.
>
> Based on Vibhore's original patch , adapted to omap_device and
> omap_hwmod frameworks.
>
> Signed-off-by: Jean Pihet <j-pihet@ti.com>
> Cc: Vibhore Vardhan <vvardhan@ti.com>
> ---
>  arch/arm/mach-omap2/omap_hwmod.c              |   62 +++++++++-
>  arch/arm/mach-omap2/powerdomain.c             |  176
> ++++++++++++++++++++++++-
>  arch/arm/mach-omap2/powerdomain.h             |   31 +++++
>  arch/arm/mach-omap2/powerdomains3xxx_data.c   |   60
> +++++++++
>  arch/arm/plat-omap/include/plat/omap_device.h |    2 +
>  arch/arm/plat-omap/include/plat/omap_hwmod.h  |    2 +
>  arch/arm/plat-omap/omap-pm-constraints.c      |  101 ++++++---------
>  arch/arm/plat-omap/omap_device.c              |   28 ++++
>  8 files changed, 399 insertions(+), 63 deletions(-)
>
> diff --git a/arch/arm/mach-omap2/omap_hwmod.c b/arch/arm/mach-
> omap2/omap_hwmod.c
> index e282e35..0dc096f 100644
> --- a/arch/arm/mach-omap2/omap_hwmod.c
> +++ b/arch/arm/mach-omap2/omap_hwmod.c
> @@ -142,6 +142,7 @@
>  #include "powerdomain.h"
>  #include <plat/clock.h>
>  #include <plat/omap_hwmod.h>
> +#include <plat/omap_device.h>
>  #include <plat/prcm.h>
>
>  #include "cm2xxx_3xxx.h"
> @@ -2198,10 +2199,69 @@ ohsps_unlock:
>  }
>
>  /**
> + * omap_hwmod_set_max_dev_wakeup_lat - set a device wake-up
> constraint
> + * @oh: struct omap_hwmod *
> + * @req_oh: struct omap_hwmod *
Need to explain what this parameters mean.
> + * @t: wakeup latency constraint (us). -1 removes the existing
> constraint
> + *
> + * Query the powerdomain of @oh to set/release the wake-up
> constraint
> + *
> + * Returns -EINVAL if @oh or @req_oh have no power domain, or the
> return
> + * code from the pwrdm function
> (pwrdm_wakeuplat_set/release_constraint)
> + * of the powerdomain assocated with @oh.
> + */
> +int omap_hwmod_set_max_dev_wakeup_lat(struct omap_hwmod
> *req_oh,
> +				      struct omap_hwmod *oh, long t)
> +{
> +	struct device *req_dev;
> +	struct platform_device *pdev;
> +	struct powerdomain *pwrdm;
> +	int ret = 0;
> +
> +	pwrdm = omap_hwmod_get_pwrdm(oh);
> +
> +	/* Catch devices with undefined powerdomains */
> +	if (!pwrdm) {
> +		pr_err("omap_hwmod: Error: could not find parent "
> +			"powerdomain for %s\n", oh->name);
> +		return -EINVAL;
> +	}
> +
> +	pdev = &(req_oh->od->pdev);
> +	if (pdev == NULL) {
> +		pr_err("omap_hwmod: Error: pdev not found for oh %s\n",
> +		       oh->name);
> +		return -EINVAL;
> +	}
> +
> +	req_dev = &(pdev->dev);
> +	if (req_dev == NULL) {
> +		pr_err("omap_hwmod: Error: device not found for oh
> %s\n",
> +		       oh->name);
> +		return -EINVAL;
> +	}
If I understand correctly, req_dev is used for keeping track of different
requests. If so, why can't you directly pass req_dev as an input param
instead of deriving it from pdev.
> +
> +	/* Call set/release_constraint for the given pwrdm */
> +	if (t == -1) {
> +		pr_debug("omap_hwmod: remove max device latency
> constraint: "
> +			 "oh %s, pwrdm %s, req by oh %s\n",
> +			 oh->name, pwrdm->name, req_oh->name);
> +		ret = pwrdm_wakeuplat_release_constraint(pwrdm,
> req_dev);
> +	} else {
> +		pr_debug("omap_hwmod: add max device latency
> constraint: "
> +			 "oh %s, t = %ld usec, pwrdm %s, req by oh
> %s\n",
> +			 oh->name, t, pwrdm->name, req_oh->name);
> +		ret = pwrdm_wakeuplat_set_constraint(pwrdm, req_dev,
> t);
> +	}
> +
> +	return 0;
> +}
> +
> +/**
>   * omap_hwmod_get_context_loss_count - get lost context count
>   * @oh: struct omap_hwmod *
>   *
> - * Query the powerdomain of of @oh to get the context loss
> + * Query the powerdomain of @oh to get the context loss
>   * count for this device.
>   *
>   * Returns the context loss count of the powerdomain assocated with
> @oh
> diff --git a/arch/arm/mach-omap2/powerdomain.c b/arch/arm/mach-
> omap2/powerdomain.c
> index eaed0df..3ed3bea 100644
> --- a/arch/arm/mach-omap2/powerdomain.c
> +++ b/arch/arm/mach-omap2/powerdomain.c
> @@ -19,16 +19,19 @@
>  #include <linux/list.h>
>  #include <linux/errno.h>
>  #include <linux/string.h>
> +#include <linux/slab.h>
> +
> +#include <plat/cpu.h>
> +#include <plat/prcm.h>
> +
>  #include "cm2xxx_3xxx.h"
>  #include "prcm44xx.h"
>  #include "cm44xx.h"
>  #include "prm2xxx_3xxx.h"
>  #include "prm44xx.h"
>
> -#include <plat/cpu.h>
>  #include "powerdomain.h"
>  #include "clockdomain.h"
> -#include <plat/prcm.h>
>
>  #include "pm.h"
>
> @@ -103,6 +106,13 @@ static int _pwrdm_register(struct powerdomain
> *pwrdm)
>  	pwrdm->state = pwrdm_read_pwrst(pwrdm);
>  	pwrdm->state_counter[pwrdm->state] = 1;
>
> +	/* Initialize priority ordered list for wakeup latency constraint
*/
> +	spin_lock_init(&pwrdm->wakeuplat_lock);
> +	plist_head_init(&pwrdm->wakeuplat_dev_list, &pwrdm-
> >wakeuplat_lock);
> +
> +	/* res_mutex protects res_list add and del ops */
> +	mutex_init(&pwrdm->wakeuplat_mutex);
> +
>  	pr_debug("powerdomain: registered %s\n", pwrdm->name);
>
>  	return 0;
> @@ -176,6 +186,62 @@ static int _pwrdm_post_transition_cb(struct
> powerdomain *pwrdm, void *unused)
>  	return 0;
>  }
>
> +/**
> + * pwrdm_wakeuplat_update_pwrst - Update power domain power
> state if needed
> + * @pwrdm: struct powerdomain * to which requesting device belongs
> to
> + *
> + * Finds minimum latency value from all entries in the list and
> + * the power domain power state needing the constraint. Programs
> + * new state if it is different from current power state.
> + */
> +static void pwrdm_wakeuplat_update_pwrst(struct powerdomain
> *pwrdm)
> +{
> +	struct plist_node *node;
> +	int new_state;
> +	unsigned long min_latency = -1;
> +
> +	if (!plist_head_empty(&pwrdm->wakeuplat_dev_list)) {
> +		node = plist_last(&pwrdm->wakeuplat_dev_list);
Wouldn't plist_last return the node with highest latency? I think you are
looking for lowest latency.
> +		min_latency = node->prio;
> +	}
> +
> +	/* Find power state with wakeup latency < minimum constraint.
> */
> +	for (new_state = 0x0; new_state < PWRDM_MAX_PWRSTS;
> new_state++) {
> +		if (min_latency == -1 ||
> +		    pwrdm->wakeup_lat[new_state] <= min_latency)
> +			break;
> +	}
> +
> +	switch (new_state) {
> +	case PWRDM_FUNC_PWRST_OFF:
> +		new_state = PWRDM_POWER_OFF;
> +		break;
> +	case PWRDM_FUNC_PWRST_OSWR:
> +		pwrdm_set_logic_retst(pwrdm, PWRDM_POWER_OFF);
> +	case PWRDM_FUNC_PWRST_CSWR:
> +		new_state = PWRDM_POWER_RET;
> +		break;
> +	case PWRDM_FUNC_PWRST_ON:
> +		new_state = PWRDM_POWER_ON;
> +		break;
> +	default:
> +		pr_warn("powerdomain: requested latency constraint not "
> +			"supported %s set to ON state\n", pwrdm->name);
> +		new_state = PWRDM_POWER_ON;
> +		break;
> +	}
> +
> +	if (pwrdm_read_pwrst(pwrdm) != new_state) {
> +		if (cpu_is_omap34xx())
Why this cpu check here?
> +			omap_set_pwrdm_state(pwrdm, new_state);
> +	}
> +
> +	pr_debug("powerdomain: %s pwrst: curr= %d, prev= %d next=
> %d "
> +		 "wkuplat_min= %lu, set_state= %d\n", pwrdm->name,
> +		 pwrdm_read_pwrst(pwrdm),
> pwrdm_read_prev_pwrst(pwrdm),
> +		 pwrdm_read_next_pwrst(pwrdm), min_latency,
> new_state);
> +}
> +
>  /* Public functions */
>
>  /**
> @@ -911,6 +977,112 @@ int pwrdm_post_transition(void)
>  }
>
>  /**
> + * pwrdm_wakeuplat_set_constraint - Set powerdomain wakeup
> latency constraint
> + * @pwrdm: struct powerdomain * to which requesting device belongs
> to
> + * @dev: struct device * of requesting device
> + * @t: wakeup latency constraint in microseconds
> + *
> + * Adds new entry to powerdomain's wakeup latency constraint list.
> + * If the requesting device already exists in the list, old value is
> + * overwritten. Checks whether current power state is still adequate.
> + * Returns -EINVAL if the powerdomain or device pointer is NULL,
> + * or -ENOMEM if kmalloc fails, or returns 0 upon success.
> + */
> +int pwrdm_wakeuplat_set_constraint(struct powerdomain *pwrdm,
> +				   struct device *req_dev, unsigned long
t)
> +{
> +	struct  wakeuplat_dev_list *user;
> +	int found = 0, ret = 0;
> +
> +	if (!pwrdm || !req_dev) {
> +		WARN(1, "powerdomain: %s: invalid parameter(s)",
> __func__);
> +		return -EINVAL;
> +	}
> +
> +	mutex_lock(&pwrdm->wakeuplat_mutex);
> +
> +	plist_for_each_entry(user, &pwrdm->wakeuplat_dev_list, node) {
> +		if (user->dev == req_dev) {
> +			found = 1;
> +			break;
> +		}
> +	}
> +
> +	/* Add new entry to the list or update existing request */
> +	if (found && user->constraint_us == t) {
> +		goto exit_set;
> +	} else if (!found) {
> +		user = kzalloc(sizeof(struct wakeuplat_dev_list),
> GFP_KERNEL);
> +		if (!user) {
> +			pr_err("powerdomain: FATAL ERROR: kzalloc
> failed\n");
> +			ret = -ENOMEM;
> +			goto exit_set;
> +		}
> +		user->dev = req_dev;
> +	} else {
> +		plist_del(&user->node, &pwrdm->wakeuplat_dev_list);
> +	}
> +
> +	plist_node_init(&user->node, t);
> +	plist_add(&user->node, &pwrdm->wakeuplat_dev_list);
> +	user->node.prio = user->constraint_us = t;
> +
> +	pwrdm_wakeuplat_update_pwrst(pwrdm);
> +
> +exit_set:
> +	mutex_unlock(&pwrdm->wakeuplat_mutex);
> +
> +	return ret;
> +}
> +
> +/**
> + * pwrdm_wakeuplat_release_constraint - Release powerdomain
> wkuplat constraint
> + * @pwrdm: struct powerdomain * to which requesting device belongs
> to
> + * @req_dev: struct device * of requesting device
> + *
> + * Removes device's entry from powerdomain's wakeup latency
> constraint list.
> + * Checks whether current power state is still adequate.
> + * Returns -EINVAL if the powerdomain or device pointer is NULL or
> + * no such entry exists in the list, or returns 0 upon success.
> + */
> +int pwrdm_wakeuplat_release_constraint(struct powerdomain *pwrdm,
> +				       struct device *req_dev)
> +{
> +	struct wakeuplat_dev_list *user;
> +	int found = 0, ret = 0;
> +
> +	if (!pwrdm || !req_dev) {
> +		WARN(1, "powerdomain: %s: invalid parameter(s)",
> __func__);
> +		return -EINVAL;
> +	}
> +
> +	mutex_lock(&pwrdm->wakeuplat_mutex);
> +
> +	plist_for_each_entry(user, &pwrdm->wakeuplat_dev_list, node) {
> +		if (user->dev == req_dev) {
> +			found = 1;
> +			break;
> +		}
> +	}
> +
> +	if (!found) {
> +		pr_err("OMAP PM: Error: no prior constraint to
release\n");
> +		ret = -EINVAL;
> +		goto exit_rls;
> +	}
> +
> +	plist_del(&user->node, &pwrdm->wakeuplat_dev_list);
> +	kfree(user);
> +
> +	pwrdm_wakeuplat_update_pwrst(pwrdm);
> +
> +exit_rls:
> +	mutex_unlock(&pwrdm->wakeuplat_mutex);
> +
> +	return ret;
> +}
> +
> +/**
>   * pwrdm_get_context_loss_count - get powerdomain's context loss
> count
>   * @pwrdm: struct powerdomain * to wait for
>   *
> diff --git a/arch/arm/mach-omap2/powerdomain.h b/arch/arm/mach-
> omap2/powerdomain.h
> index c66431e..d4189d8 100644
> --- a/arch/arm/mach-omap2/powerdomain.h
> +++ b/arch/arm/mach-omap2/powerdomain.h
> @@ -19,6 +19,9 @@
>
>  #include <linux/types.h>
>  #include <linux/list.h>
> +#include <linux/plist.h>
> +#include <linux/mutex.h>
> +#include <linux/spinlock.h>
>
>  #include <linux/atomic.h>
>
> @@ -46,6 +49,15 @@
>
>  #define PWRSTS_OFF_RET_ON	(PWRSTS_OFF_RET | (1 <<
> PWRDM_POWER_ON))
>
> +/* Powerdomain functional power states */
> +#define PWRDM_FUNC_PWRST_OFF	0x0
> +#define PWRDM_FUNC_PWRST_OSWR	0x1
> +#define PWRDM_FUNC_PWRST_CSWR	0x2
> +#define PWRDM_FUNC_PWRST_ON	0x3
> +
> +#define PWRDM_MAX_FUNC_PWRSTS	4
> +
> +#define UNSUP_STATE		-1
>
>  /* Powerdomain flags */
>  #define PWRDM_HAS_HDWR_SAR	(1 << 0) /* hardware save-and-
> restore support */
> @@ -96,6 +108,10 @@ struct powerdomain;
>   * @state_counter:
>   * @timer:
>   * @state_timer:
> + * @wakeup_lat: Wakeup latencies for possible powerdomain power
> states
> + * @wakeuplat_lock: spinlock for plist
> + * @wakeuplat_dev_list: plist_head linking all devices placing
> constraint
> + * @wakeuplat_mutex: mutex to protect per powerdomain list ops
>   *
>   * @prcm_partition possible values are defined in mach-
> omap2/prcm44xx.h.
>   */
> @@ -121,6 +137,16 @@ struct powerdomain {
>  	s64 timer;
>  	s64 state_timer[PWRDM_MAX_PWRSTS];
>  #endif
> +	const u32 wakeup_lat[PWRDM_MAX_FUNC_PWRSTS];
> +	spinlock_t wakeuplat_lock;
> +	struct plist_head wakeuplat_dev_list;
> +	struct mutex wakeuplat_mutex;
> +};
> +
> +struct wakeuplat_dev_list {
> +	struct device *dev;
> +	unsigned long constraint_us;
> +	struct plist_node node;
>  };
>
>  /**
> @@ -211,6 +237,11 @@ int pwrdm_clkdm_state_switch(struct
> clockdomain *clkdm);
>  int pwrdm_pre_transition(void);
>  int pwrdm_post_transition(void);
>  int pwrdm_set_lowpwrstchange(struct powerdomain *pwrdm);
> +
> +int pwrdm_wakeuplat_set_constraint(struct powerdomain *pwrdm,
> +				   struct device *dev, unsigned long t);
> +int pwrdm_wakeuplat_release_constraint(struct powerdomain *pwrdm,
> +				       struct device *dev);
>  u32 pwrdm_get_context_loss_count(struct powerdomain *pwrdm);
>
>  extern void omap2xxx_powerdomains_init(void);
> diff --git a/arch/arm/mach-omap2/powerdomains3xxx_data.c
> b/arch/arm/mach-omap2/powerdomains3xxx_data.c
> index e1bec56..4f7e44d 100644
> --- a/arch/arm/mach-omap2/powerdomains3xxx_data.c
> +++ b/arch/arm/mach-omap2/powerdomains3xxx_data.c
> @@ -52,6 +52,12 @@ static struct powerdomain iva2_pwrdm = {
>  		[2] = PWRSTS_OFF_ON,
>  		[3] = PWRDM_POWER_ON,
>  	},
> +	.wakeup_lat = {
> +		[PWRDM_FUNC_PWRST_OFF] = 1100,
> +		[PWRDM_FUNC_PWRST_OSWR] = UNSUP_STATE,
> +		[PWRDM_FUNC_PWRST_CSWR] = 350,
> +		[PWRDM_FUNC_PWRST_ON] = 0,
> +	},
>  };
>
>  static struct powerdomain mpu_3xxx_pwrdm = {
> @@ -68,6 +74,12 @@ static struct powerdomain mpu_3xxx_pwrdm = {
>  	.pwrsts_mem_on	  = {
>  		[0] = PWRSTS_OFF_ON,
>  	},
> +	.wakeup_lat = {
> +		[PWRDM_FUNC_PWRST_OFF] = 95,
> +		[PWRDM_FUNC_PWRST_OSWR] = UNSUP_STATE,
> +		[PWRDM_FUNC_PWRST_CSWR] = 45,
> +		[PWRDM_FUNC_PWRST_ON] = 0,
> +	},
>  };
>
>  /*
> @@ -98,6 +110,12 @@ static struct powerdomain
> core_3xxx_pre_es3_1_pwrdm = {
>  		[0] = PWRSTS_OFF_RET_ON, /* MEM1ONSTATE */
>  		[1] = PWRSTS_OFF_RET_ON, /* MEM2ONSTATE */
>  	},
> +	.wakeup_lat = {
> +		[PWRDM_FUNC_PWRST_OFF] = 100,
> +		[PWRDM_FUNC_PWRST_OSWR] = UNSUP_STATE,
> +		[PWRDM_FUNC_PWRST_CSWR] = 60,
> +		[PWRDM_FUNC_PWRST_ON] = 0,
> +	},
>  };
>
>  static struct powerdomain core_3xxx_es3_1_pwrdm = {
> @@ -121,6 +139,12 @@ static struct powerdomain
> core_3xxx_es3_1_pwrdm = {
>  		[0] = PWRSTS_OFF_RET_ON, /* MEM1ONSTATE */
>  		[1] = PWRSTS_OFF_RET_ON, /* MEM2ONSTATE */
>  	},
> +	.wakeup_lat = {
> +		[PWRDM_FUNC_PWRST_OFF] = 100,
> +		[PWRDM_FUNC_PWRST_OSWR] = UNSUP_STATE,
> +		[PWRDM_FUNC_PWRST_CSWR] = 60,
> +		[PWRDM_FUNC_PWRST_ON] = 0,
> +	},
>  };
>
>  static struct powerdomain dss_pwrdm = {
> @@ -136,6 +160,12 @@ static struct powerdomain dss_pwrdm = {
>  	.pwrsts_mem_on	  = {
>  		[0] = PWRDM_POWER_ON,  /* MEMONSTATE */
>  	},
> +	.wakeup_lat = {
> +		[PWRDM_FUNC_PWRST_OFF] = 70,
> +		[PWRDM_FUNC_PWRST_OSWR] = UNSUP_STATE,
> +		[PWRDM_FUNC_PWRST_CSWR] = 20,
> +		[PWRDM_FUNC_PWRST_ON] = 0,
> +	},
>  };
>
>  /*
> @@ -157,6 +187,12 @@ static struct powerdomain sgx_pwrdm = {
>  	.pwrsts_mem_on	  = {
>  		[0] = PWRDM_POWER_ON,  /* MEMONSTATE */
>  	},
> +	.wakeup_lat = {
> +		[PWRDM_FUNC_PWRST_OFF] = 1000,
> +		[PWRDM_FUNC_PWRST_OSWR] = UNSUP_STATE,
> +		[PWRDM_FUNC_PWRST_CSWR] = UNSUP_STATE,
> +		[PWRDM_FUNC_PWRST_ON] = 0,
> +	},
>  };
>
>  static struct powerdomain cam_pwrdm = {
> @@ -172,6 +208,12 @@ static struct powerdomain cam_pwrdm = {
>  	.pwrsts_mem_on	  = {
>  		[0] = PWRDM_POWER_ON,  /* MEMONSTATE */
>  	},
> +	.wakeup_lat = {
> +		[PWRDM_FUNC_PWRST_OFF] = 850,
> +		[PWRDM_FUNC_PWRST_OSWR] = UNSUP_STATE,
> +		[PWRDM_FUNC_PWRST_CSWR] = 35,
> +		[PWRDM_FUNC_PWRST_ON] = 0,
> +	},
>  };
>
>  static struct powerdomain per_pwrdm = {
> @@ -187,6 +229,12 @@ static struct powerdomain per_pwrdm = {
>  	.pwrsts_mem_on	  = {
>  		[0] = PWRDM_POWER_ON,  /* MEMONSTATE */
>  	},
> +	.wakeup_lat = {
> +		[PWRDM_FUNC_PWRST_OFF] = 200,
> +		[PWRDM_FUNC_PWRST_OSWR] = UNSUP_STATE,
> +		[PWRDM_FUNC_PWRST_CSWR] = 110,
> +		[PWRDM_FUNC_PWRST_ON] = 0,
> +	},
>  };
>
>  static struct powerdomain emu_pwrdm = {
> @@ -201,6 +249,12 @@ static struct powerdomain neon_pwrdm = {
>  	.omap_chip	  = OMAP_CHIP_INIT(CHIP_IS_OMAP3430),
>  	.pwrsts		  = PWRSTS_OFF_RET_ON,
>  	.pwrsts_logic_ret = PWRDM_POWER_RET,
> +	.wakeup_lat = {
> +		[PWRDM_FUNC_PWRST_OFF] = 200,
> +		[PWRDM_FUNC_PWRST_OSWR] = UNSUP_STATE,
> +		[PWRDM_FUNC_PWRST_CSWR] = 35,
> +		[PWRDM_FUNC_PWRST_ON] = 0,
> +	},
>  };
>
>  static struct powerdomain usbhost_pwrdm = {
> @@ -223,6 +277,12 @@ static struct powerdomain usbhost_pwrdm = {
>  	.pwrsts_mem_on	  = {
>  		[0] = PWRDM_POWER_ON,  /* MEMONSTATE */
>  	},
> +	.wakeup_lat = {
> +		[PWRDM_FUNC_PWRST_OFF] = 800,
> +		[PWRDM_FUNC_PWRST_OSWR] = UNSUP_STATE,
> +		[PWRDM_FUNC_PWRST_CSWR] = 150,
> +		[PWRDM_FUNC_PWRST_ON] = 0,
> +	},
>  };
>
>  static struct powerdomain dpll1_pwrdm = {
> diff --git a/arch/arm/plat-omap/include/plat/omap_device.h
> b/arch/arm/plat-omap/include/plat/omap_device.h
> index e4c349f..5da6b47 100644
> --- a/arch/arm/plat-omap/include/plat/omap_device.h
> +++ b/arch/arm/plat-omap/include/plat/omap_device.h
> @@ -107,6 +107,8 @@ void __iomem *omap_device_get_rt_va(struct
> omap_device *od);
>  int omap_device_align_pm_lat(struct platform_device *pdev,
>  			     u32 new_wakeup_lat_limit);
>  struct powerdomain *omap_device_get_pwrdm(struct omap_device
> *od);
> +int omap_device_set_max_dev_wakeup_lat(struct platform_device
> *req_pdev,
> +				       struct platform_device *pdev, long
t);
>  u32 omap_device_get_context_loss_count(struct platform_device
> *pdev);
>
>  /* Other */
> diff --git a/arch/arm/plat-omap/include/plat/omap_hwmod.h
> b/arch/arm/plat-omap/include/plat/omap_hwmod.h
> index 1eee85a..0fbb974 100644
> --- a/arch/arm/plat-omap/include/plat/omap_hwmod.h
> +++ b/arch/arm/plat-omap/include/plat/omap_hwmod.h
> @@ -587,6 +587,8 @@ int omap_hwmod_for_each_by_class(const char
> *classname,
>  				 void *user);
>
>  int omap_hwmod_set_postsetup_state(struct omap_hwmod *oh, u8
> state);
> +int omap_hwmod_set_max_dev_wakeup_lat(struct omap_hwmod
> *req_oh,
> +				      struct omap_hwmod *oh, long t);
>  u32 omap_hwmod_get_context_loss_count(struct omap_hwmod *oh);
>
>  /*
> diff --git a/arch/arm/plat-omap/omap-pm-constraints.c b/arch/arm/plat-
> omap/omap-pm-constraints.c
> index c8b4e4c..7ae855f 100644
> --- a/arch/arm/plat-omap/omap-pm-constraints.c
> +++ b/arch/arm/plat-omap/omap-pm-constraints.c
> @@ -24,6 +24,7 @@
>  /* Interface documentation is in mach/omap-pm.h */
>  #include <plat/omap-pm.h>
>  #include <plat/omap_device.h>
> +#include <plat/common.h>
>
>  static bool off_mode_enabled;
>  static u32 dummy_context_loss_counter;
> @@ -32,34 +33,6 @@ static u32 dummy_context_loss_counter;
>   * Device-driver-originated constraints (via board-*.c files)
>   */
>
> -int omap_pm_set_max_mpu_wakeup_lat(struct device *dev, long t)
> -{
> -	if (!dev || t < -1) {
> -		WARN(1, "OMAP PM: %s: invalid parameter(s)",
> __func__);
> -		return -EINVAL;
> -	};
> -
> -	if (t == -1)
> -		pr_debug("OMAP PM: remove max MPU wakeup latency
> constraint: "
> -			 "dev %s\n", dev_name(dev));
> -	else
> -		pr_debug("OMAP PM: add max MPU wakeup latency
> constraint: "
> -			 "dev %s, t = %ld usec\n", dev_name(dev), t);
> -
> -	/*
> -	 * For current Linux, this needs to map the MPU to a
> -	 * powerdomain, then go through the list of current max lat
> -	 * constraints on the MPU and find the smallest.  If
> -	 * the latency constraint has changed, the code should
> -	 * recompute the state to enter for the next powerdomain
> -	 * state.
> -	 *
> -	 * TI CDP code can call constraint_set here.
> -	 */
> -
> -	return 0;
> -}
> -
>  int omap_pm_set_min_bus_tput(struct device *dev, u8 agent_id,
> unsigned long r)
>  {
>  	if (!dev || (agent_id != OCP_INITIATOR_AGENT &&
> @@ -87,62 +60,70 @@ int omap_pm_set_min_bus_tput(struct device
> *dev, u8 agent_id, unsigned long r)
>  	return 0;
>  }
>
> +/*
> + * omap_pm_set_max_dev_wakeup_lat - set/release devices wake-up
> latency
> + * constraints
> + */
>  int omap_pm_set_max_dev_wakeup_lat(struct device *req_dev, struct
> device *dev,
>  				   long t)
>  {
> +	struct platform_device *pdev, *req_pdev;
> +	int ret = 0;
> +
>  	if (!req_dev || !dev || t < -1) {
>  		WARN(1, "OMAP PM: %s: invalid parameter(s)",
> __func__);
>  		return -EINVAL;
> +	}
> +
> +	/* Look for the platform devices */
> +	pdev = container_of(dev, struct platform_device, dev);

You could perhaps use macro to_platform_device instead of using
container_of.

> +	req_pdev = container_of(req_dev, struct platform_device, dev);
> +
> +	/* Try to catch non platform devices. */
> +	if ((pdev->name == NULL) || (req_pdev->name == NULL)) {
> +		pr_err("OMAP-PM set_wakeup_lat: Error: platform devices
> "
> +		       "not valid\n");
> +		return -EINVAL;
> +	} else {
> +		/* Call the omap_device API */
> +		ret = omap_device_set_max_dev_wakeup_lat(req_pdev,
> pdev, t);
> +	}
> +
> +	return ret;
> +}
> +
> +int omap_pm_set_max_mpu_wakeup_lat(struct device *req_dev, long
> t)
> +{
> +	if (!req_dev || t < -1) {
> +		WARN(1, "OMAP PM: %s: invalid parameter(s)",
> __func__);
> +		return -EINVAL;
>  	};
>
>  	if (t == -1)
> -		pr_debug("OMAP PM: remove max device latency
> constraint: "
> -			 "dev %s\n", dev_name(dev));
> +		pr_debug("OMAP PM: remove max MPU wakeup latency
> constraint: "
> +			 "dev %s\n", dev_name(req_dev));
>  	else
> -		pr_debug("OMAP PM: add max device latency constraint: "
> -			 "dev %s, t = %ld usec\n", dev_name(dev), t);
> +		pr_debug("OMAP PM: add max MPU wakeup latency
> constraint: "
> +			 "dev %s, t = %ld usec\n", dev_name(req_dev), t);
>
> -	/*
> -	 * For current Linux, this needs to map the device to a
> -	 * powerdomain, then go through the list of current max lat
> -	 * constraints on that powerdomain and find the smallest.  If
> -	 * the latency constraint has changed, the code should
> -	 * recompute the state to enter for the next powerdomain
> -	 * state.  Conceivably, this code should also determine
> -	 * whether to actually disable the device clocks or not,
> -	 * depending on how long it takes to re-enable the clocks.
> -	 *
> -	 * TI CDP code can call constraint_set here.
> -	 */
> +	omap_pm_set_max_dev_wakeup_lat(req_dev,
> omap2_get_mpuss_device(), t);
>
>  	return 0;
>  }
>
> -int omap_pm_set_max_sdma_lat(struct device *dev, long t)
> +int omap_pm_set_max_sdma_lat(struct device *req_dev, long t)
>  {
> -	if (!dev || t < -1) {
> +	if (!req_dev || t < -1) {
>  		WARN(1, "OMAP PM: %s: invalid parameter(s)",
> __func__);
>  		return -EINVAL;
>  	};
>
>  	if (t == -1)
>  		pr_debug("OMAP PM: remove max DMA latency constraint:
> "
> -			 "dev %s\n", dev_name(dev));
> +			 "dev %s\n", dev_name(req_dev));
>  	else
>  		pr_debug("OMAP PM: add max DMA latency constraint: "
> -			 "dev %s, t = %ld usec\n", dev_name(dev), t);
> -
> -	/*
> -	 * For current Linux PM QOS params, this code should scan the
> -	 * list of maximum CPU and DMA latencies and select the
> -	 * smallest, then set cpu_dma_latency pm_qos_param
> -	 * accordingly.
> -	 *
> -	 * For future Linux PM QOS params, with separate CPU and DMA
> -	 * latency params, this code should just set the dma_latency
> param.
> -	 *
> -	 * TI CDP code can call constraint_set here.
> -	 */
> +			 "dev %s, t = %ld usec\n", dev_name(req_dev), t);
>
>  	return 0;
>  }
> diff --git a/arch/arm/plat-omap/omap_device.c b/arch/arm/plat-
> omap/omap_device.c
> index 57adb27..1fe93d3 100644
> --- a/arch/arm/plat-omap/omap_device.c
> +++ b/arch/arm/plat-omap/omap_device.c
> @@ -280,6 +280,34 @@ static void _add_optional_clock_alias(struct
> omap_device *od,
>  /* Public functions for use by core code */
>
>  /**
> + * omap_device_set_max_dev_wakeup_lat - set/release a device
> constraint
> + * @od: struct omap_device *
> + * @req_od: struct omap_device *
> + *
> + * Using the primary hwmod, set/release a device constraint for the
> pdev
> + * device, requested by the req_pdev device.
> + *
> + * If any hwmods exist for the omap_device assoiated with @pdev and
> @req_pdev,
> + * set/release the constraint for the corresponding hwmods, otherwise
> return
> + * -EINVAL.
> + */
> +int omap_device_set_max_dev_wakeup_lat(struct platform_device
> *req_pdev,
> +				       struct platform_device *pdev, long
t)
> +{
> +	struct omap_device *od, *req_od;
> +	u32 ret = -EINVAL;
> +
> +	od = _find_by_pdev(pdev);
> +	req_od = _find_by_pdev(req_pdev);
> +
> +	if ((od->hwmods_cnt) && (req_od->hwmods_cnt))
> +		ret = omap_hwmod_set_max_dev_wakeup_lat(req_od-
> >hwmods[0],
> +							od->hwmods[0], t);
> +
> +	return ret;
> +}
> +
> +/**
>   * omap_device_get_context_loss_count - get lost context count
>   * @od: struct omap_device *
>   *
Vishwa
> --
> 1.7.2.3

^ permalink raw reply	[flat|nested] 19+ messages in thread

* Re: [PATCH 2/2] OMAP: PM: implement devices wakeup latency constraints APIs
  2011-02-15  9:35   ` Vishwanath Sripathy
@ 2011-02-15 13:52     ` Jean Pihet
  2011-02-16 10:49       ` Vishwanath Sripathy
  0 siblings, 1 reply; 19+ messages in thread
From: Jean Pihet @ 2011-02-15 13:52 UTC (permalink / raw)
  To: Vishwanath Sripathy
  Cc: Kevin Hilman, paul, Vibhore Vardhan, Santosh Shilimkar,
	Rajendra Nayak, linux-omap, Jean Pihet-XID

Hi Vishwa,

On Tue, Feb 15, 2011 at 10:35 AM, Vishwanath Sripathy
<vishwanath.bs@ti.com> wrote:
>> -----Original Message-----
>> From: jean.pihet@newoldbits.com [mailto:jean.pihet@newoldbits.com]
>> Sent: Friday, February 11, 2011 12:53 AM
>> To: khilman@ti.com; paul@pwsan.com; Vibhore Vardhan; Santosh
>> Shilimkar; Vishwanath BS; rnayak@ti.com
>> Cc: linux-omap@vger.kernel.org; Jean Pihet; Vibhore Vardhan
>> Subject: [PATCH 2/2] OMAP: PM: implement devices wakeup latency
>> constraints APIs
>>
>> From: Jean Pihet <j-pihet@ti.com>
>>
>> Implement OMAP PM layer omap_pm_set_max_dev_wakeup_lat API by
>> creating similar APIs at the omap_device and omap_hwmod levels. The
>> omap_hwmod level call is the layer with access to the powerdomain
>> core, so it is the place where the powerdomain is queried to set and
>> release the constraints.
>>
>> NOTE: only works for devices which have been converted to use
>>       omap_device/omap_hwmod.
>>
>> Longer term, we could possibly remove this API from the OMAP PM layer,
>> and instead directly use the omap_device level API.
>>
>> Based on Vibhore's original patch , adapted to omap_device and
>> omap_hwmod frameworks.
>>
>> Signed-off-by: Jean Pihet <j-pihet@ti.com>
>> Cc: Vibhore Vardhan <vvardhan@ti.com>
>> ---
>>  arch/arm/mach-omap2/omap_hwmod.c              |   62 +++++++++-
>>  arch/arm/mach-omap2/powerdomain.c             |  176
>> ++++++++++++++++++++++++-
>>  arch/arm/mach-omap2/powerdomain.h             |   31 +++++
>>  arch/arm/mach-omap2/powerdomains3xxx_data.c   |   60
>> +++++++++
>>  arch/arm/plat-omap/include/plat/omap_device.h |    2 +
>>  arch/arm/plat-omap/include/plat/omap_hwmod.h  |    2 +
>>  arch/arm/plat-omap/omap-pm-constraints.c      |  101 ++++++---------
>>  arch/arm/plat-omap/omap_device.c              |   28 ++++
>>  8 files changed, 399 insertions(+), 63 deletions(-)
>>
>> diff --git a/arch/arm/mach-omap2/omap_hwmod.c b/arch/arm/mach-
>> omap2/omap_hwmod.c
>> index e282e35..0dc096f 100644
>> --- a/arch/arm/mach-omap2/omap_hwmod.c
>> +++ b/arch/arm/mach-omap2/omap_hwmod.c
>> @@ -142,6 +142,7 @@
>>  #include "powerdomain.h"
>>  #include <plat/clock.h>
>>  #include <plat/omap_hwmod.h>
>> +#include <plat/omap_device.h>
>>  #include <plat/prcm.h>
>>
>>  #include "cm2xxx_3xxx.h"
>> @@ -2198,10 +2199,69 @@ ohsps_unlock:
>>  }
>>
>>  /**
>> + * omap_hwmod_set_max_dev_wakeup_lat - set a device wake-up
>> constraint
>> + * @oh: struct omap_hwmod *
>> + * @req_oh: struct omap_hwmod *
> Need to explain what this parameters mean.
Ok, will add a description here. Basically oh corresponds to the
device (and so the power domain) to set a constraint on and req_oh is
the constraint requester. oh is used to determine which power domain
to set a constraint on, req_oh is used to record the requester for
later update or removal of a constraint.

>> + * @t: wakeup latency constraint (us). -1 removes the existing
>> constraint
>> + *
>> + * Query the powerdomain of @oh to set/release the wake-up
>> constraint
>> + *
>> + * Returns -EINVAL if @oh or @req_oh have no power domain, or the
>> return
>> + * code from the pwrdm function
>> (pwrdm_wakeuplat_set/release_constraint)
>> + * of the powerdomain assocated with @oh.
>> + */
>> +int omap_hwmod_set_max_dev_wakeup_lat(struct omap_hwmod
>> *req_oh,
>> +                                   struct omap_hwmod *oh, long t)
>> +{
>> +     struct device *req_dev;
>> +     struct platform_device *pdev;
>> +     struct powerdomain *pwrdm;
>> +     int ret = 0;
>> +
>> +     pwrdm = omap_hwmod_get_pwrdm(oh);
>> +
>> +     /* Catch devices with undefined powerdomains */
>> +     if (!pwrdm) {
>> +             pr_err("omap_hwmod: Error: could not find parent "
>> +                     "powerdomain for %s\n", oh->name);
>> +             return -EINVAL;
>> +     }
>> +
>> +     pdev = &(req_oh->od->pdev);
>> +     if (pdev == NULL) {
>> +             pr_err("omap_hwmod: Error: pdev not found for oh %s\n",
>> +                    oh->name);
>> +             return -EINVAL;
>> +     }
>> +
>> +     req_dev = &(pdev->dev);
>> +     if (req_dev == NULL) {
>> +             pr_err("omap_hwmod: Error: device not found for oh
>> %s\n",
>> +                    oh->name);
>> +             return -EINVAL;
>> +     }
> If I understand correctly, req_dev is used for keeping track of different
> requests. If so, why can't you directly pass req_dev as an input param
> instead of deriving it from pdev.
The layering in the API is as follows: caller -> omap-pm ->
omap_device -> omap_hwmod -> powerdomain. The  parameters types are
passed accordingly.

Note: I will rename pdev to req_pdev to make it clear that the
parameter relates to the requester.

>> +
>> +     /* Call set/release_constraint for the given pwrdm */
>> +     if (t == -1) {
>> +             pr_debug("omap_hwmod: remove max device latency
>> constraint: "
>> +                      "oh %s, pwrdm %s, req by oh %s\n",
>> +                      oh->name, pwrdm->name, req_oh->name);
>> +             ret = pwrdm_wakeuplat_release_constraint(pwrdm,
>> req_dev);
>> +     } else {
>> +             pr_debug("omap_hwmod: add max device latency
>> constraint: "
>> +                      "oh %s, t = %ld usec, pwrdm %s, req by oh
>> %s\n",
>> +                      oh->name, t, pwrdm->name, req_oh->name);
>> +             ret = pwrdm_wakeuplat_set_constraint(pwrdm, req_dev,
>> t);
>> +     }
>> +
>> +     return 0;
>> +}
>> +
>> +/**
>>   * omap_hwmod_get_context_loss_count - get lost context count
>>   * @oh: struct omap_hwmod *
>>   *
>> - * Query the powerdomain of of @oh to get the context loss
>> + * Query the powerdomain of @oh to get the context loss
>>   * count for this device.
>>   *
>>   * Returns the context loss count of the powerdomain assocated with
>> @oh
>> diff --git a/arch/arm/mach-omap2/powerdomain.c b/arch/arm/mach-
>> omap2/powerdomain.c
>> index eaed0df..3ed3bea 100644
>> --- a/arch/arm/mach-omap2/powerdomain.c
>> +++ b/arch/arm/mach-omap2/powerdomain.c
>> @@ -19,16 +19,19 @@
>>  #include <linux/list.h>
>>  #include <linux/errno.h>
>>  #include <linux/string.h>
>> +#include <linux/slab.h>
>> +
>> +#include <plat/cpu.h>
>> +#include <plat/prcm.h>
>> +
>>  #include "cm2xxx_3xxx.h"
>>  #include "prcm44xx.h"
>>  #include "cm44xx.h"
>>  #include "prm2xxx_3xxx.h"
>>  #include "prm44xx.h"
>>
>> -#include <plat/cpu.h>
>>  #include "powerdomain.h"
>>  #include "clockdomain.h"
>> -#include <plat/prcm.h>
>>
>>  #include "pm.h"
>>
>> @@ -103,6 +106,13 @@ static int _pwrdm_register(struct powerdomain
>> *pwrdm)
>>       pwrdm->state = pwrdm_read_pwrst(pwrdm);
>>       pwrdm->state_counter[pwrdm->state] = 1;
>>
>> +     /* Initialize priority ordered list for wakeup latency constraint
> */
>> +     spin_lock_init(&pwrdm->wakeuplat_lock);
>> +     plist_head_init(&pwrdm->wakeuplat_dev_list, &pwrdm-
>> >wakeuplat_lock);
>> +
>> +     /* res_mutex protects res_list add and del ops */
>> +     mutex_init(&pwrdm->wakeuplat_mutex);
>> +
>>       pr_debug("powerdomain: registered %s\n", pwrdm->name);
>>
>>       return 0;
>> @@ -176,6 +186,62 @@ static int _pwrdm_post_transition_cb(struct
>> powerdomain *pwrdm, void *unused)
>>       return 0;
>>  }
>>
>> +/**
>> + * pwrdm_wakeuplat_update_pwrst - Update power domain power
>> state if needed
>> + * @pwrdm: struct powerdomain * to which requesting device belongs
>> to
>> + *
>> + * Finds minimum latency value from all entries in the list and
>> + * the power domain power state needing the constraint. Programs
>> + * new state if it is different from current power state.
>> + */
>> +static void pwrdm_wakeuplat_update_pwrst(struct powerdomain
>> *pwrdm)
>> +{
>> +     struct plist_node *node;
>> +     int new_state;
>> +     unsigned long min_latency = -1;
>> +
>> +     if (!plist_head_empty(&pwrdm->wakeuplat_dev_list)) {
>> +             node = plist_last(&pwrdm->wakeuplat_dev_list);
> Wouldn't plist_last return the node with highest latency? I think you are
> looking for lowest latency.
Yes indeed. We need the strongest constraint, so the lowest allowed
wake-up latency value.

>> +             min_latency = node->prio;
>> +     }
>> +
>> +     /* Find power state with wakeup latency < minimum constraint.
>> */
>> +     for (new_state = 0x0; new_state < PWRDM_MAX_PWRSTS;
>> new_state++) {
>> +             if (min_latency == -1 ||
>> +                 pwrdm->wakeup_lat[new_state] <= min_latency)
>> +                     break;
>> +     }
>> +
>> +     switch (new_state) {
>> +     case PWRDM_FUNC_PWRST_OFF:
>> +             new_state = PWRDM_POWER_OFF;
>> +             break;
>> +     case PWRDM_FUNC_PWRST_OSWR:
>> +             pwrdm_set_logic_retst(pwrdm, PWRDM_POWER_OFF);
>> +     case PWRDM_FUNC_PWRST_CSWR:
>> +             new_state = PWRDM_POWER_RET;
>> +             break;
>> +     case PWRDM_FUNC_PWRST_ON:
>> +             new_state = PWRDM_POWER_ON;
>> +             break;
>> +     default:
>> +             pr_warn("powerdomain: requested latency constraint not "
>> +                     "supported %s set to ON state\n", pwrdm->name);
>> +             new_state = PWRDM_POWER_ON;
>> +             break;
>> +     }
>> +
>> +     if (pwrdm_read_pwrst(pwrdm) != new_state) {
>> +             if (cpu_is_omap34xx())
> Why this cpu check here?
Only 34xx has support for the latency constraints. The other platforms
have the wakeup latency values set as '0', which means that OFF mode
will always be chosen as preferred state. Is it allowed to set the
pwrdm_state in this case?

>> +                     omap_set_pwrdm_state(pwrdm, new_state);
>> +     }
>> +
>> +     pr_debug("powerdomain: %s pwrst: curr= %d, prev= %d next=
>> %d "
>> +              "wkuplat_min= %lu, set_state= %d\n", pwrdm->name,
>> +              pwrdm_read_pwrst(pwrdm),
>> pwrdm_read_prev_pwrst(pwrdm),
>> +              pwrdm_read_next_pwrst(pwrdm), min_latency,
>> new_state);
>> +}
>> +
>>  /* Public functions */
>>
>>  /**
>> @@ -911,6 +977,112 @@ int pwrdm_post_transition(void)
>>  }
>>
>>  /**
>> + * pwrdm_wakeuplat_set_constraint - Set powerdomain wakeup
>> latency constraint
>> + * @pwrdm: struct powerdomain * to which requesting device belongs
>> to
>> + * @dev: struct device * of requesting device
>> + * @t: wakeup latency constraint in microseconds
>> + *
>> + * Adds new entry to powerdomain's wakeup latency constraint list.
>> + * If the requesting device already exists in the list, old value is
>> + * overwritten. Checks whether current power state is still adequate.
>> + * Returns -EINVAL if the powerdomain or device pointer is NULL,
>> + * or -ENOMEM if kmalloc fails, or returns 0 upon success.
>> + */
>> +int pwrdm_wakeuplat_set_constraint(struct powerdomain *pwrdm,
>> +                                struct device *req_dev, unsigned long
> t)
>> +{
>> +     struct  wakeuplat_dev_list *user;
>> +     int found = 0, ret = 0;
>> +
>> +     if (!pwrdm || !req_dev) {
>> +             WARN(1, "powerdomain: %s: invalid parameter(s)",
>> __func__);
>> +             return -EINVAL;
>> +     }
>> +
>> +     mutex_lock(&pwrdm->wakeuplat_mutex);
>> +
>> +     plist_for_each_entry(user, &pwrdm->wakeuplat_dev_list, node) {
>> +             if (user->dev == req_dev) {
>> +                     found = 1;
>> +                     break;
>> +             }
>> +     }
>> +
>> +     /* Add new entry to the list or update existing request */
>> +     if (found && user->constraint_us == t) {
>> +             goto exit_set;
>> +     } else if (!found) {
>> +             user = kzalloc(sizeof(struct wakeuplat_dev_list),
>> GFP_KERNEL);
>> +             if (!user) {
>> +                     pr_err("powerdomain: FATAL ERROR: kzalloc
>> failed\n");
>> +                     ret = -ENOMEM;
>> +                     goto exit_set;
>> +             }
>> +             user->dev = req_dev;
>> +     } else {
>> +             plist_del(&user->node, &pwrdm->wakeuplat_dev_list);
>> +     }
>> +
>> +     plist_node_init(&user->node, t);
>> +     plist_add(&user->node, &pwrdm->wakeuplat_dev_list);
>> +     user->node.prio = user->constraint_us = t;
>> +
>> +     pwrdm_wakeuplat_update_pwrst(pwrdm);
>> +
>> +exit_set:
>> +     mutex_unlock(&pwrdm->wakeuplat_mutex);
>> +
>> +     return ret;
>> +}
>> +
>> +/**
>> + * pwrdm_wakeuplat_release_constraint - Release powerdomain
>> wkuplat constraint
>> + * @pwrdm: struct powerdomain * to which requesting device belongs
>> to
>> + * @req_dev: struct device * of requesting device
>> + *
>> + * Removes device's entry from powerdomain's wakeup latency
>> constraint list.
>> + * Checks whether current power state is still adequate.
>> + * Returns -EINVAL if the powerdomain or device pointer is NULL or
>> + * no such entry exists in the list, or returns 0 upon success.
>> + */
>> +int pwrdm_wakeuplat_release_constraint(struct powerdomain *pwrdm,
>> +                                    struct device *req_dev)
>> +{
>> +     struct wakeuplat_dev_list *user;
>> +     int found = 0, ret = 0;
>> +
>> +     if (!pwrdm || !req_dev) {
>> +             WARN(1, "powerdomain: %s: invalid parameter(s)",
>> __func__);
>> +             return -EINVAL;
>> +     }
>> +
>> +     mutex_lock(&pwrdm->wakeuplat_mutex);
>> +
>> +     plist_for_each_entry(user, &pwrdm->wakeuplat_dev_list, node) {
>> +             if (user->dev == req_dev) {
>> +                     found = 1;
>> +                     break;
>> +             }
>> +     }
>> +
>> +     if (!found) {
>> +             pr_err("OMAP PM: Error: no prior constraint to
> release\n");
>> +             ret = -EINVAL;
>> +             goto exit_rls;
>> +     }
>> +
>> +     plist_del(&user->node, &pwrdm->wakeuplat_dev_list);
>> +     kfree(user);
>> +
>> +     pwrdm_wakeuplat_update_pwrst(pwrdm);
>> +
>> +exit_rls:
>> +     mutex_unlock(&pwrdm->wakeuplat_mutex);
>> +
>> +     return ret;
>> +}
>> +
>> +/**
>>   * pwrdm_get_context_loss_count - get powerdomain's context loss
>> count
>>   * @pwrdm: struct powerdomain * to wait for
>>   *
>> diff --git a/arch/arm/mach-omap2/powerdomain.h b/arch/arm/mach-
>> omap2/powerdomain.h
>> index c66431e..d4189d8 100644
>> --- a/arch/arm/mach-omap2/powerdomain.h
>> +++ b/arch/arm/mach-omap2/powerdomain.h
>> @@ -19,6 +19,9 @@
>>
>>  #include <linux/types.h>
>>  #include <linux/list.h>
>> +#include <linux/plist.h>
>> +#include <linux/mutex.h>
>> +#include <linux/spinlock.h>
>>
>>  #include <linux/atomic.h>
>>
>> @@ -46,6 +49,15 @@
>>
>>  #define PWRSTS_OFF_RET_ON    (PWRSTS_OFF_RET | (1 <<
>> PWRDM_POWER_ON))
>>
>> +/* Powerdomain functional power states */
>> +#define PWRDM_FUNC_PWRST_OFF 0x0
>> +#define PWRDM_FUNC_PWRST_OSWR        0x1
>> +#define PWRDM_FUNC_PWRST_CSWR        0x2
>> +#define PWRDM_FUNC_PWRST_ON  0x3
>> +
>> +#define PWRDM_MAX_FUNC_PWRSTS        4
>> +
>> +#define UNSUP_STATE          -1
>>
>>  /* Powerdomain flags */
>>  #define PWRDM_HAS_HDWR_SAR   (1 << 0) /* hardware save-and-
>> restore support */
>> @@ -96,6 +108,10 @@ struct powerdomain;
>>   * @state_counter:
>>   * @timer:
>>   * @state_timer:
>> + * @wakeup_lat: Wakeup latencies for possible powerdomain power
>> states
Will add a comment about the ordering (latency values must be sorted
in decremental order).

>> + * @wakeuplat_lock: spinlock for plist
>> + * @wakeuplat_dev_list: plist_head linking all devices placing
>> constraint
>> + * @wakeuplat_mutex: mutex to protect per powerdomain list ops
>>   *
>>   * @prcm_partition possible values are defined in mach-
>> omap2/prcm44xx.h.
>>   */
>> @@ -121,6 +137,16 @@ struct powerdomain {
>>       s64 timer;
>>       s64 state_timer[PWRDM_MAX_PWRSTS];
>>  #endif
>> +     const u32 wakeup_lat[PWRDM_MAX_FUNC_PWRSTS];
>> +     spinlock_t wakeuplat_lock;
>> +     struct plist_head wakeuplat_dev_list;
>> +     struct mutex wakeuplat_mutex;
>> +};
>> +
>> +struct wakeuplat_dev_list {
>> +     struct device *dev;
>> +     unsigned long constraint_us;
>> +     struct plist_node node;
>>  };
>>
>>  /**
>> @@ -211,6 +237,11 @@ int pwrdm_clkdm_state_switch(struct
>> clockdomain *clkdm);
>>  int pwrdm_pre_transition(void);
>>  int pwrdm_post_transition(void);
>>  int pwrdm_set_lowpwrstchange(struct powerdomain *pwrdm);
>> +
>> +int pwrdm_wakeuplat_set_constraint(struct powerdomain *pwrdm,
>> +                                struct device *dev, unsigned long t);
>> +int pwrdm_wakeuplat_release_constraint(struct powerdomain *pwrdm,
>> +                                    struct device *dev);
>>  u32 pwrdm_get_context_loss_count(struct powerdomain *pwrdm);
>>
>>  extern void omap2xxx_powerdomains_init(void);
>> diff --git a/arch/arm/mach-omap2/powerdomains3xxx_data.c
>> b/arch/arm/mach-omap2/powerdomains3xxx_data.c
>> index e1bec56..4f7e44d 100644
>> --- a/arch/arm/mach-omap2/powerdomains3xxx_data.c
>> +++ b/arch/arm/mach-omap2/powerdomains3xxx_data.c
>> @@ -52,6 +52,12 @@ static struct powerdomain iva2_pwrdm = {
>>               [2] = PWRSTS_OFF_ON,
>>               [3] = PWRDM_POWER_ON,
>>       },
>> +     .wakeup_lat = {
>> +             [PWRDM_FUNC_PWRST_OFF] = 1100,
>> +             [PWRDM_FUNC_PWRST_OSWR] = UNSUP_STATE,
>> +             [PWRDM_FUNC_PWRST_CSWR] = 350,
>> +             [PWRDM_FUNC_PWRST_ON] = 0,
>> +     },
>>  };
>>
>>  static struct powerdomain mpu_3xxx_pwrdm = {
>> @@ -68,6 +74,12 @@ static struct powerdomain mpu_3xxx_pwrdm = {
>>       .pwrsts_mem_on    = {
>>               [0] = PWRSTS_OFF_ON,
>>       },
>> +     .wakeup_lat = {
>> +             [PWRDM_FUNC_PWRST_OFF] = 95,
>> +             [PWRDM_FUNC_PWRST_OSWR] = UNSUP_STATE,
>> +             [PWRDM_FUNC_PWRST_CSWR] = 45,
>> +             [PWRDM_FUNC_PWRST_ON] = 0,
>> +     },
>>  };
>>
>>  /*
>> @@ -98,6 +110,12 @@ static struct powerdomain
>> core_3xxx_pre_es3_1_pwrdm = {
>>               [0] = PWRSTS_OFF_RET_ON, /* MEM1ONSTATE */
>>               [1] = PWRSTS_OFF_RET_ON, /* MEM2ONSTATE */
>>       },
>> +     .wakeup_lat = {
>> +             [PWRDM_FUNC_PWRST_OFF] = 100,
>> +             [PWRDM_FUNC_PWRST_OSWR] = UNSUP_STATE,
>> +             [PWRDM_FUNC_PWRST_CSWR] = 60,
>> +             [PWRDM_FUNC_PWRST_ON] = 0,
>> +     },
>>  };
>>
>>  static struct powerdomain core_3xxx_es3_1_pwrdm = {
>> @@ -121,6 +139,12 @@ static struct powerdomain
>> core_3xxx_es3_1_pwrdm = {
>>               [0] = PWRSTS_OFF_RET_ON, /* MEM1ONSTATE */
>>               [1] = PWRSTS_OFF_RET_ON, /* MEM2ONSTATE */
>>       },
>> +     .wakeup_lat = {
>> +             [PWRDM_FUNC_PWRST_OFF] = 100,
>> +             [PWRDM_FUNC_PWRST_OSWR] = UNSUP_STATE,
>> +             [PWRDM_FUNC_PWRST_CSWR] = 60,
>> +             [PWRDM_FUNC_PWRST_ON] = 0,
>> +     },
>>  };
>>
>>  static struct powerdomain dss_pwrdm = {
>> @@ -136,6 +160,12 @@ static struct powerdomain dss_pwrdm = {
>>       .pwrsts_mem_on    = {
>>               [0] = PWRDM_POWER_ON,  /* MEMONSTATE */
>>       },
>> +     .wakeup_lat = {
>> +             [PWRDM_FUNC_PWRST_OFF] = 70,
>> +             [PWRDM_FUNC_PWRST_OSWR] = UNSUP_STATE,
>> +             [PWRDM_FUNC_PWRST_CSWR] = 20,
>> +             [PWRDM_FUNC_PWRST_ON] = 0,
>> +     },
>>  };
>>
>>  /*
>> @@ -157,6 +187,12 @@ static struct powerdomain sgx_pwrdm = {
>>       .pwrsts_mem_on    = {
>>               [0] = PWRDM_POWER_ON,  /* MEMONSTATE */
>>       },
>> +     .wakeup_lat = {
>> +             [PWRDM_FUNC_PWRST_OFF] = 1000,
>> +             [PWRDM_FUNC_PWRST_OSWR] = UNSUP_STATE,
>> +             [PWRDM_FUNC_PWRST_CSWR] = UNSUP_STATE,
>> +             [PWRDM_FUNC_PWRST_ON] = 0,
>> +     },
>>  };
>>
>>  static struct powerdomain cam_pwrdm = {
>> @@ -172,6 +208,12 @@ static struct powerdomain cam_pwrdm = {
>>       .pwrsts_mem_on    = {
>>               [0] = PWRDM_POWER_ON,  /* MEMONSTATE */
>>       },
>> +     .wakeup_lat = {
>> +             [PWRDM_FUNC_PWRST_OFF] = 850,
>> +             [PWRDM_FUNC_PWRST_OSWR] = UNSUP_STATE,
>> +             [PWRDM_FUNC_PWRST_CSWR] = 35,
>> +             [PWRDM_FUNC_PWRST_ON] = 0,
>> +     },
>>  };
>>
>>  static struct powerdomain per_pwrdm = {
>> @@ -187,6 +229,12 @@ static struct powerdomain per_pwrdm = {
>>       .pwrsts_mem_on    = {
>>               [0] = PWRDM_POWER_ON,  /* MEMONSTATE */
>>       },
>> +     .wakeup_lat = {
>> +             [PWRDM_FUNC_PWRST_OFF] = 200,
>> +             [PWRDM_FUNC_PWRST_OSWR] = UNSUP_STATE,
>> +             [PWRDM_FUNC_PWRST_CSWR] = 110,
>> +             [PWRDM_FUNC_PWRST_ON] = 0,
>> +     },
>>  };
>>
>>  static struct powerdomain emu_pwrdm = {
>> @@ -201,6 +249,12 @@ static struct powerdomain neon_pwrdm = {
>>       .omap_chip        = OMAP_CHIP_INIT(CHIP_IS_OMAP3430),
>>       .pwrsts           = PWRSTS_OFF_RET_ON,
>>       .pwrsts_logic_ret = PWRDM_POWER_RET,
>> +     .wakeup_lat = {
>> +             [PWRDM_FUNC_PWRST_OFF] = 200,
>> +             [PWRDM_FUNC_PWRST_OSWR] = UNSUP_STATE,
>> +             [PWRDM_FUNC_PWRST_CSWR] = 35,
>> +             [PWRDM_FUNC_PWRST_ON] = 0,
>> +     },
>>  };
>>
>>  static struct powerdomain usbhost_pwrdm = {
>> @@ -223,6 +277,12 @@ static struct powerdomain usbhost_pwrdm = {
>>       .pwrsts_mem_on    = {
>>               [0] = PWRDM_POWER_ON,  /* MEMONSTATE */
>>       },
>> +     .wakeup_lat = {
>> +             [PWRDM_FUNC_PWRST_OFF] = 800,
>> +             [PWRDM_FUNC_PWRST_OSWR] = UNSUP_STATE,
>> +             [PWRDM_FUNC_PWRST_CSWR] = 150,
>> +             [PWRDM_FUNC_PWRST_ON] = 0,
>> +     },
>>  };
>>
>>  static struct powerdomain dpll1_pwrdm = {
>> diff --git a/arch/arm/plat-omap/include/plat/omap_device.h
>> b/arch/arm/plat-omap/include/plat/omap_device.h
>> index e4c349f..5da6b47 100644
>> --- a/arch/arm/plat-omap/include/plat/omap_device.h
>> +++ b/arch/arm/plat-omap/include/plat/omap_device.h
>> @@ -107,6 +107,8 @@ void __iomem *omap_device_get_rt_va(struct
>> omap_device *od);
>>  int omap_device_align_pm_lat(struct platform_device *pdev,
>>                            u32 new_wakeup_lat_limit);
>>  struct powerdomain *omap_device_get_pwrdm(struct omap_device
>> *od);
>> +int omap_device_set_max_dev_wakeup_lat(struct platform_device
>> *req_pdev,
>> +                                    struct platform_device *pdev, long
> t);
>>  u32 omap_device_get_context_loss_count(struct platform_device
>> *pdev);
>>
>>  /* Other */
>> diff --git a/arch/arm/plat-omap/include/plat/omap_hwmod.h
>> b/arch/arm/plat-omap/include/plat/omap_hwmod.h
>> index 1eee85a..0fbb974 100644
>> --- a/arch/arm/plat-omap/include/plat/omap_hwmod.h
>> +++ b/arch/arm/plat-omap/include/plat/omap_hwmod.h
>> @@ -587,6 +587,8 @@ int omap_hwmod_for_each_by_class(const char
>> *classname,
>>                                void *user);
>>
>>  int omap_hwmod_set_postsetup_state(struct omap_hwmod *oh, u8
>> state);
>> +int omap_hwmod_set_max_dev_wakeup_lat(struct omap_hwmod
>> *req_oh,
>> +                                   struct omap_hwmod *oh, long t);
>>  u32 omap_hwmod_get_context_loss_count(struct omap_hwmod *oh);
>>
>>  /*
>> diff --git a/arch/arm/plat-omap/omap-pm-constraints.c b/arch/arm/plat-
>> omap/omap-pm-constraints.c
>> index c8b4e4c..7ae855f 100644
>> --- a/arch/arm/plat-omap/omap-pm-constraints.c
>> +++ b/arch/arm/plat-omap/omap-pm-constraints.c
>> @@ -24,6 +24,7 @@
>>  /* Interface documentation is in mach/omap-pm.h */
>>  #include <plat/omap-pm.h>
>>  #include <plat/omap_device.h>
>> +#include <plat/common.h>
>>
>>  static bool off_mode_enabled;
>>  static u32 dummy_context_loss_counter;
>> @@ -32,34 +33,6 @@ static u32 dummy_context_loss_counter;
>>   * Device-driver-originated constraints (via board-*.c files)
>>   */
>>
>> -int omap_pm_set_max_mpu_wakeup_lat(struct device *dev, long t)
>> -{
>> -     if (!dev || t < -1) {
>> -             WARN(1, "OMAP PM: %s: invalid parameter(s)",
>> __func__);
>> -             return -EINVAL;
>> -     };
>> -
>> -     if (t == -1)
>> -             pr_debug("OMAP PM: remove max MPU wakeup latency
>> constraint: "
>> -                      "dev %s\n", dev_name(dev));
>> -     else
>> -             pr_debug("OMAP PM: add max MPU wakeup latency
>> constraint: "
>> -                      "dev %s, t = %ld usec\n", dev_name(dev), t);
>> -
>> -     /*
>> -      * For current Linux, this needs to map the MPU to a
>> -      * powerdomain, then go through the list of current max lat
>> -      * constraints on the MPU and find the smallest.  If
>> -      * the latency constraint has changed, the code should
>> -      * recompute the state to enter for the next powerdomain
>> -      * state.
>> -      *
>> -      * TI CDP code can call constraint_set here.
>> -      */
>> -
>> -     return 0;
>> -}
>> -
>>  int omap_pm_set_min_bus_tput(struct device *dev, u8 agent_id,
>> unsigned long r)
>>  {
>>       if (!dev || (agent_id != OCP_INITIATOR_AGENT &&
>> @@ -87,62 +60,70 @@ int omap_pm_set_min_bus_tput(struct device
>> *dev, u8 agent_id, unsigned long r)
>>       return 0;
>>  }
>>
>> +/*
>> + * omap_pm_set_max_dev_wakeup_lat - set/release devices wake-up
>> latency
>> + * constraints
>> + */
>>  int omap_pm_set_max_dev_wakeup_lat(struct device *req_dev, struct
>> device *dev,
>>                                  long t)
>>  {
>> +     struct platform_device *pdev, *req_pdev;
>> +     int ret = 0;
>> +
>>       if (!req_dev || !dev || t < -1) {
>>               WARN(1, "OMAP PM: %s: invalid parameter(s)",
>> __func__);
>>               return -EINVAL;
>> +     }
>> +
>> +     /* Look for the platform devices */
>> +     pdev = container_of(dev, struct platform_device, dev);
>
> You could perhaps use macro to_platform_device instead of using
> container_of.
Yes that is better.

>
>> +     req_pdev = container_of(req_dev, struct platform_device, dev);
Same here.

>> +
>> +     /* Try to catch non platform devices. */
>> +     if ((pdev->name == NULL) || (req_pdev->name == NULL)) {
>> +             pr_err("OMAP-PM set_wakeup_lat: Error: platform devices
>> "
>> +                    "not valid\n");
>> +             return -EINVAL;
>> +     } else {
>> +             /* Call the omap_device API */
>> +             ret = omap_device_set_max_dev_wakeup_lat(req_pdev,
>> pdev, t);
>> +     }
>> +
>> +     return ret;
>> +}
>> +
>> +int omap_pm_set_max_mpu_wakeup_lat(struct device *req_dev, long
>> t)
>> +{
>> +     if (!req_dev || t < -1) {
>> +             WARN(1, "OMAP PM: %s: invalid parameter(s)",
>> __func__);
>> +             return -EINVAL;
>>       };
>>
>>       if (t == -1)
>> -             pr_debug("OMAP PM: remove max device latency
>> constraint: "
>> -                      "dev %s\n", dev_name(dev));
>> +             pr_debug("OMAP PM: remove max MPU wakeup latency
>> constraint: "
>> +                      "dev %s\n", dev_name(req_dev));
>>       else
>> -             pr_debug("OMAP PM: add max device latency constraint: "
>> -                      "dev %s, t = %ld usec\n", dev_name(dev), t);
>> +             pr_debug("OMAP PM: add max MPU wakeup latency
>> constraint: "
>> +                      "dev %s, t = %ld usec\n", dev_name(req_dev), t);
>>
>> -     /*
>> -      * For current Linux, this needs to map the device to a
>> -      * powerdomain, then go through the list of current max lat
>> -      * constraints on that powerdomain and find the smallest.  If
>> -      * the latency constraint has changed, the code should
>> -      * recompute the state to enter for the next powerdomain
>> -      * state.  Conceivably, this code should also determine
>> -      * whether to actually disable the device clocks or not,
>> -      * depending on how long it takes to re-enable the clocks.
>> -      *
>> -      * TI CDP code can call constraint_set here.
>> -      */
>> +     omap_pm_set_max_dev_wakeup_lat(req_dev,
>> omap2_get_mpuss_device(), t);
>>
>>       return 0;
>>  }
>>
>> -int omap_pm_set_max_sdma_lat(struct device *dev, long t)
>> +int omap_pm_set_max_sdma_lat(struct device *req_dev, long t)
>>  {
>> -     if (!dev || t < -1) {
>> +     if (!req_dev || t < -1) {
>>               WARN(1, "OMAP PM: %s: invalid parameter(s)",
>> __func__);
>>               return -EINVAL;
>>       };
>>
>>       if (t == -1)
>>               pr_debug("OMAP PM: remove max DMA latency constraint:
>> "
>> -                      "dev %s\n", dev_name(dev));
>> +                      "dev %s\n", dev_name(req_dev));
>>       else
>>               pr_debug("OMAP PM: add max DMA latency constraint: "
>> -                      "dev %s, t = %ld usec\n", dev_name(dev), t);
>> -
>> -     /*
>> -      * For current Linux PM QOS params, this code should scan the
>> -      * list of maximum CPU and DMA latencies and select the
>> -      * smallest, then set cpu_dma_latency pm_qos_param
>> -      * accordingly.
>> -      *
>> -      * For future Linux PM QOS params, with separate CPU and DMA
>> -      * latency params, this code should just set the dma_latency
>> param.
>> -      *
>> -      * TI CDP code can call constraint_set here.
>> -      */
>> +                      "dev %s, t = %ld usec\n", dev_name(req_dev), t);
>>
>>       return 0;
>>  }
>> diff --git a/arch/arm/plat-omap/omap_device.c b/arch/arm/plat-
>> omap/omap_device.c
>> index 57adb27..1fe93d3 100644
>> --- a/arch/arm/plat-omap/omap_device.c
>> +++ b/arch/arm/plat-omap/omap_device.c
>> @@ -280,6 +280,34 @@ static void _add_optional_clock_alias(struct
>> omap_device *od,
>>  /* Public functions for use by core code */
>>
>>  /**
>> + * omap_device_set_max_dev_wakeup_lat - set/release a device
>> constraint
>> + * @od: struct omap_device *
>> + * @req_od: struct omap_device *
>> + *
>> + * Using the primary hwmod, set/release a device constraint for the
>> pdev
>> + * device, requested by the req_pdev device.
>> + *
>> + * If any hwmods exist for the omap_device assoiated with @pdev and
>> @req_pdev,
>> + * set/release the constraint for the corresponding hwmods, otherwise
>> return
>> + * -EINVAL.
>> + */
>> +int omap_device_set_max_dev_wakeup_lat(struct platform_device
>> *req_pdev,
>> +                                    struct platform_device *pdev, long
> t)
>> +{
>> +     struct omap_device *od, *req_od;
>> +     u32 ret = -EINVAL;
>> +
>> +     od = _find_by_pdev(pdev);
>> +     req_od = _find_by_pdev(req_pdev);
>> +
>> +     if ((od->hwmods_cnt) && (req_od->hwmods_cnt))
>> +             ret = omap_hwmod_set_max_dev_wakeup_lat(req_od-
>> >hwmods[0],
>> +                                                     od->hwmods[0], t);
>> +
>> +     return ret;
>> +}
>> +
>> +/**
>>   * omap_device_get_context_loss_count - get lost context count
>>   * @od: struct omap_device *
>>   *
> Vishwa
>> --
>> 1.7.2.3
>

Thanks for the review, I will resubmit the patch asap.

Regards,
Jean
--
To unsubscribe from this list: send the line "unsubscribe linux-omap" in
the body of a message to majordomo@vger.kernel.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html

^ permalink raw reply	[flat|nested] 19+ messages in thread

* RE: [PATCH 2/2] OMAP: PM: implement devices wakeup latency constraints APIs
  2011-02-15 13:52     ` Jean Pihet
@ 2011-02-16 10:49       ` Vishwanath Sripathy
  2011-02-16 12:58         ` Pihet-XID, Jean
  0 siblings, 1 reply; 19+ messages in thread
From: Vishwanath Sripathy @ 2011-02-16 10:49 UTC (permalink / raw)
  To: Jean Pihet
  Cc: Kevin Hilman, paul, Vibhore Vardhan, Santosh Shilimkar,
	Rajendra Nayak, linux-omap, Jean Pihet-XID

> -----Original Message-----
> From: Jean Pihet [mailto:jean.pihet@newoldbits.com]
> Sent: Tuesday, February 15, 2011 7:22 PM
> To: Vishwanath Sripathy
> Cc: Kevin Hilman; paul@pwsan.com; Vibhore Vardhan; Santosh
> Shilimkar; Rajendra Nayak; linux-omap@vger.kernel.org; Jean Pihet-XID
> Subject: Re: [PATCH 2/2] OMAP: PM: implement devices wakeup latency
> constraints APIs
>
> Hi Vishwa,
>
> On Tue, Feb 15, 2011 at 10:35 AM, Vishwanath Sripathy
> <vishwanath.bs@ti.com> wrote:
> >> -----Original Message-----
> >> From: jean.pihet@newoldbits.com
> [mailto:jean.pihet@newoldbits.com]
> >> Sent: Friday, February 11, 2011 12:53 AM
> >> To: khilman@ti.com; paul@pwsan.com; Vibhore Vardhan; Santosh
> >> Shilimkar; Vishwanath BS; rnayak@ti.com
> >> Cc: linux-omap@vger.kernel.org; Jean Pihet; Vibhore Vardhan
> >> Subject: [PATCH 2/2] OMAP: PM: implement devices wakeup latency
> >> constraints APIs
> >>
> >> From: Jean Pihet <j-pihet@ti.com>
> >>
> >> Implement OMAP PM layer omap_pm_set_max_dev_wakeup_lat API
> by
> >> creating similar APIs at the omap_device and omap_hwmod levels.
> The
> >> omap_hwmod level call is the layer with access to the powerdomain
> >> core, so it is the place where the powerdomain is queried to set and
> >> release the constraints.
> >>
> >> NOTE: only works for devices which have been converted to use
> >>       omap_device/omap_hwmod.
> >>
> >> Longer term, we could possibly remove this API from the OMAP PM
> layer,
> >> and instead directly use the omap_device level API.
> >>
> >> Based on Vibhore's original patch , adapted to omap_device and
> >> omap_hwmod frameworks.
> >>
> >> Signed-off-by: Jean Pihet <j-pihet@ti.com>
> >> Cc: Vibhore Vardhan <vvardhan@ti.com>
> >> ---
> >>  arch/arm/mach-omap2/omap_hwmod.c              |   62
> +++++++++-
> >>  arch/arm/mach-omap2/powerdomain.c             |  176
> >> ++++++++++++++++++++++++-
> >>  arch/arm/mach-omap2/powerdomain.h             |   31 +++++
> >>  arch/arm/mach-omap2/powerdomains3xxx_data.c   |   60
> >> +++++++++
> >>  arch/arm/plat-omap/include/plat/omap_device.h |    2 +
> >>  arch/arm/plat-omap/include/plat/omap_hwmod.h  |    2 +
> >>  arch/arm/plat-omap/omap-pm-constraints.c      |  101 ++++++----
> -----
> >>  arch/arm/plat-omap/omap_device.c              |   28 ++++
> >>  8 files changed, 399 insertions(+), 63 deletions(-)
> >>
> >> diff --git a/arch/arm/mach-omap2/omap_hwmod.c
> b/arch/arm/mach-
> >> omap2/omap_hwmod.c
> >> index e282e35..0dc096f 100644
> >> --- a/arch/arm/mach-omap2/omap_hwmod.c
> >> +++ b/arch/arm/mach-omap2/omap_hwmod.c
> >> @@ -142,6 +142,7 @@
> >>  #include "powerdomain.h"
> >>  #include <plat/clock.h>
> >>  #include <plat/omap_hwmod.h>
> >> +#include <plat/omap_device.h>
> >>  #include <plat/prcm.h>
> >>
> >>  #include "cm2xxx_3xxx.h"
> >> @@ -2198,10 +2199,69 @@ ohsps_unlock:
> >>  }
> >>
> >>  /**
> >> + * omap_hwmod_set_max_dev_wakeup_lat - set a device wake-up
> >> constraint
> >> + * @oh: struct omap_hwmod *
> >> + * @req_oh: struct omap_hwmod *
> > Need to explain what this parameters mean.
> Ok, will add a description here. Basically oh corresponds to the
> device (and so the power domain) to set a constraint on and req_oh is
> the constraint requester. oh is used to determine which power domain
> to set a constraint on, req_oh is used to record the requester for
> later update or removal of a constraint.
>
> >> + * @t: wakeup latency constraint (us). -1 removes the existing
> >> constraint
> >> + *
> >> + * Query the powerdomain of @oh to set/release the wake-up
> >> constraint
> >> + *
> >> + * Returns -EINVAL if @oh or @req_oh have no power domain, or
> the
> >> return
> >> + * code from the pwrdm function
> >> (pwrdm_wakeuplat_set/release_constraint)
> >> + * of the powerdomain assocated with @oh.
> >> + */
> >> +int omap_hwmod_set_max_dev_wakeup_lat(struct omap_hwmod
> >> *req_oh,
> >> +                                   struct omap_hwmod *oh, long t)
> >> +{
> >> +     struct device *req_dev;
> >> +     struct platform_device *pdev;
> >> +     struct powerdomain *pwrdm;
> >> +     int ret = 0;
> >> +
> >> +     pwrdm = omap_hwmod_get_pwrdm(oh);
> >> +
> >> +     /* Catch devices with undefined powerdomains */
> >> +     if (!pwrdm) {
> >> +             pr_err("omap_hwmod: Error: could not find parent "
> >> +                     "powerdomain for %s\n", oh->name);
> >> +             return -EINVAL;
> >> +     }
> >> +
> >> +     pdev = &(req_oh->od->pdev);
> >> +     if (pdev == NULL) {
> >> +             pr_err("omap_hwmod: Error: pdev not found for oh
> %s\n",
> >> +                    oh->name);
> >> +             return -EINVAL;
> >> +     }
> >> +
> >> +     req_dev = &(pdev->dev);
> >> +     if (req_dev == NULL) {
> >> +             pr_err("omap_hwmod: Error: device not found for oh
> >> %s\n",
> >> +                    oh->name);
> >> +             return -EINVAL;
> >> +     }
> > If I understand correctly, req_dev is used for keeping track of
different
> > requests. If so, why can't you directly pass req_dev as an input param
> > instead of deriving it from pdev.
> The layering in the API is as follows: caller -> omap-pm ->
> omap_device -> omap_hwmod -> powerdomain. The  parameters types
> are
> passed accordingly.
>
> Note: I will rename pdev to req_pdev to make it clear that the
> parameter relates to the requester.
>
> >> +
> >> +     /* Call set/release_constraint for the given pwrdm */
> >> +     if (t == -1) {
> >> +             pr_debug("omap_hwmod: remove max device latency
> >> constraint: "
> >> +                      "oh %s, pwrdm %s, req by oh %s\n",
> >> +                      oh->name, pwrdm->name, req_oh->name);
> >> +             ret = pwrdm_wakeuplat_release_constraint(pwrdm,
> >> req_dev);
> >> +     } else {
> >> +             pr_debug("omap_hwmod: add max device latency
> >> constraint: "
> >> +                      "oh %s, t = %ld usec, pwrdm %s, req by oh
> >> %s\n",
> >> +                      oh->name, t, pwrdm->name, req_oh->name);
> >> +             ret = pwrdm_wakeuplat_set_constraint(pwrdm, req_dev,
> >> t);
> >> +     }
> >> +
> >> +     return 0;
> >> +}
> >> +
> >> +/**
> >>   * omap_hwmod_get_context_loss_count - get lost context count
> >>   * @oh: struct omap_hwmod *
> >>   *
> >> - * Query the powerdomain of of @oh to get the context loss
> >> + * Query the powerdomain of @oh to get the context loss
> >>   * count for this device.
> >>   *
> >>   * Returns the context loss count of the powerdomain assocated with
> >> @oh
> >> diff --git a/arch/arm/mach-omap2/powerdomain.c b/arch/arm/mach-
> >> omap2/powerdomain.c
> >> index eaed0df..3ed3bea 100644
> >> --- a/arch/arm/mach-omap2/powerdomain.c
> >> +++ b/arch/arm/mach-omap2/powerdomain.c
> >> @@ -19,16 +19,19 @@
> >>  #include <linux/list.h>
> >>  #include <linux/errno.h>
> >>  #include <linux/string.h>
> >> +#include <linux/slab.h>
> >> +
> >> +#include <plat/cpu.h>
> >> +#include <plat/prcm.h>
> >> +
> >>  #include "cm2xxx_3xxx.h"
> >>  #include "prcm44xx.h"
> >>  #include "cm44xx.h"
> >>  #include "prm2xxx_3xxx.h"
> >>  #include "prm44xx.h"
> >>
> >> -#include <plat/cpu.h>
> >>  #include "powerdomain.h"
> >>  #include "clockdomain.h"
> >> -#include <plat/prcm.h>
> >>
> >>  #include "pm.h"
> >>
> >> @@ -103,6 +106,13 @@ static int _pwrdm_register(struct
> powerdomain
> >> *pwrdm)
> >>       pwrdm->state = pwrdm_read_pwrst(pwrdm);
> >>       pwrdm->state_counter[pwrdm->state] = 1;
> >>
> >> +     /* Initialize priority ordered list for wakeup latency
constraint
> > */
> >> +     spin_lock_init(&pwrdm->wakeuplat_lock);
> >> +     plist_head_init(&pwrdm->wakeuplat_dev_list, &pwrdm-
> >> >wakeuplat_lock);
> >> +
> >> +     /* res_mutex protects res_list add and del ops */
> >> +     mutex_init(&pwrdm->wakeuplat_mutex);
> >> +
> >>       pr_debug("powerdomain: registered %s\n", pwrdm->name);
> >>
> >>       return 0;
> >> @@ -176,6 +186,62 @@ static int _pwrdm_post_transition_cb(struct
> >> powerdomain *pwrdm, void *unused)
> >>       return 0;
> >>  }
> >>
> >> +/**
> >> + * pwrdm_wakeuplat_update_pwrst - Update power domain power
> >> state if needed
> >> + * @pwrdm: struct powerdomain * to which requesting device
> belongs
> >> to
> >> + *
> >> + * Finds minimum latency value from all entries in the list and
> >> + * the power domain power state needing the constraint. Programs
> >> + * new state if it is different from current power state.
> >> + */
> >> +static void pwrdm_wakeuplat_update_pwrst(struct powerdomain
> >> *pwrdm)
> >> +{
> >> +     struct plist_node *node;
> >> +     int new_state;
> >> +     unsigned long min_latency = -1;
> >> +
> >> +     if (!plist_head_empty(&pwrdm->wakeuplat_dev_list)) {
> >> +             node = plist_last(&pwrdm->wakeuplat_dev_list);
> > Wouldn't plist_last return the node with highest latency? I think you
> are
> > looking for lowest latency.
> Yes indeed. We need the strongest constraint, so the lowest allowed
> wake-up latency value.
Then shouldn't you use plist_first?
I suppose plist_last will return the highest latency (weakest constraint).
>
> >> +             min_latency = node->prio;
> >> +     }
> >> +
> >> +     /* Find power state with wakeup latency < minimum constraint.
> >> */
> >> +     for (new_state = 0x0; new_state < PWRDM_MAX_PWRSTS;
> >> new_state++) {
> >> +             if (min_latency == -1 ||
> >> +                 pwrdm->wakeup_lat[new_state] <= min_latency)
> >> +                     break;
> >> +     }
> >> +
> >> +     switch (new_state) {
> >> +     case PWRDM_FUNC_PWRST_OFF:
> >> +             new_state = PWRDM_POWER_OFF;
> >> +             break;
> >> +     case PWRDM_FUNC_PWRST_OSWR:
> >> +             pwrdm_set_logic_retst(pwrdm, PWRDM_POWER_OFF);
> >> +     case PWRDM_FUNC_PWRST_CSWR:
> >> +             new_state = PWRDM_POWER_RET;
> >> +             break;
> >> +     case PWRDM_FUNC_PWRST_ON:
> >> +             new_state = PWRDM_POWER_ON;
> >> +             break;
> >> +     default:
> >> +             pr_warn("powerdomain: requested latency constraint not
"
> >> +                     "supported %s set to ON state\n", pwrdm->name);
> >> +             new_state = PWRDM_POWER_ON;
> >> +             break;
> >> +     }
> >> +
> >> +     if (pwrdm_read_pwrst(pwrdm) != new_state) {
> >> +             if (cpu_is_omap34xx())
> > Why this cpu check here?
> Only 34xx has support for the latency constraints. The other platforms
> have the wakeup latency values set as '0', which means that OFF mode
> will always be chosen as preferred state. Is it allowed to set the
> pwrdm_state in this case?
Then this will not work for OMAP4 right? OMAP4 also should support device
latency constraints.

Vishwa
>
> >> +                     omap_set_pwrdm_state(pwrdm, new_state);
> >> +     }
> >> +
> >> +     pr_debug("powerdomain: %s pwrst: curr= %d, prev= %d
> next=
> >> %d "
> >> +              "wkuplat_min= %lu, set_state= %d\n", pwrdm->name,
> >> +              pwrdm_read_pwrst(pwrdm),
> >> pwrdm_read_prev_pwrst(pwrdm),
> >> +              pwrdm_read_next_pwrst(pwrdm), min_latency,
> >> new_state);
> >> +}
> >> +
> >>  /* Public functions */
> >>
> >>  /**
> >> @@ -911,6 +977,112 @@ int pwrdm_post_transition(void)
> >>  }
> >>
> >>  /**
> >> + * pwrdm_wakeuplat_set_constraint - Set powerdomain wakeup
> >> latency constraint
> >> + * @pwrdm: struct powerdomain * to which requesting device
> belongs
> >> to
> >> + * @dev: struct device * of requesting device
> >> + * @t: wakeup latency constraint in microseconds
> >> + *
> >> + * Adds new entry to powerdomain's wakeup latency constraint list.
> >> + * If the requesting device already exists in the list, old value is
> >> + * overwritten. Checks whether current power state is still
> adequate.
> >> + * Returns -EINVAL if the powerdomain or device pointer is NULL,
> >> + * or -ENOMEM if kmalloc fails, or returns 0 upon success.
> >> + */
> >> +int pwrdm_wakeuplat_set_constraint(struct powerdomain *pwrdm,
> >> +                                struct device *req_dev, unsigned
long
> > t)
> >> +{
> >> +     struct  wakeuplat_dev_list *user;
> >> +     int found = 0, ret = 0;
> >> +
> >> +     if (!pwrdm || !req_dev) {
> >> +             WARN(1, "powerdomain: %s: invalid parameter(s)",
> >> __func__);
> >> +             return -EINVAL;
> >> +     }
> >> +
> >> +     mutex_lock(&pwrdm->wakeuplat_mutex);
> >> +
> >> +     plist_for_each_entry(user, &pwrdm->wakeuplat_dev_list, node)
> {
> >> +             if (user->dev == req_dev) {
> >> +                     found = 1;
> >> +                     break;
> >> +             }
> >> +     }
> >> +
> >> +     /* Add new entry to the list or update existing request */
> >> +     if (found && user->constraint_us == t) {
> >> +             goto exit_set;
> >> +     } else if (!found) {
> >> +             user = kzalloc(sizeof(struct wakeuplat_dev_list),
> >> GFP_KERNEL);
> >> +             if (!user) {
> >> +                     pr_err("powerdomain: FATAL ERROR: kzalloc
> >> failed\n");
> >> +                     ret = -ENOMEM;
> >> +                     goto exit_set;
> >> +             }
> >> +             user->dev = req_dev;
> >> +     } else {
> >> +             plist_del(&user->node, &pwrdm->wakeuplat_dev_list);
> >> +     }
> >> +
> >> +     plist_node_init(&user->node, t);
> >> +     plist_add(&user->node, &pwrdm->wakeuplat_dev_list);
> >> +     user->node.prio = user->constraint_us = t;
> >> +
> >> +     pwrdm_wakeuplat_update_pwrst(pwrdm);
> >> +
> >> +exit_set:
> >> +     mutex_unlock(&pwrdm->wakeuplat_mutex);
> >> +
> >> +     return ret;
> >> +}
> >> +
> >> +/**
> >> + * pwrdm_wakeuplat_release_constraint - Release powerdomain
> >> wkuplat constraint
> >> + * @pwrdm: struct powerdomain * to which requesting device
> belongs
> >> to
> >> + * @req_dev: struct device * of requesting device
> >> + *
> >> + * Removes device's entry from powerdomain's wakeup latency
> >> constraint list.
> >> + * Checks whether current power state is still adequate.
> >> + * Returns -EINVAL if the powerdomain or device pointer is NULL or
> >> + * no such entry exists in the list, or returns 0 upon success.
> >> + */
> >> +int pwrdm_wakeuplat_release_constraint(struct powerdomain
> *pwrdm,
> >> +                                    struct device *req_dev)
> >> +{
> >> +     struct wakeuplat_dev_list *user;
> >> +     int found = 0, ret = 0;
> >> +
> >> +     if (!pwrdm || !req_dev) {
> >> +             WARN(1, "powerdomain: %s: invalid parameter(s)",
> >> __func__);
> >> +             return -EINVAL;
> >> +     }
> >> +
> >> +     mutex_lock(&pwrdm->wakeuplat_mutex);
> >> +
> >> +     plist_for_each_entry(user, &pwrdm->wakeuplat_dev_list, node)
> {
> >> +             if (user->dev == req_dev) {
> >> +                     found = 1;
> >> +                     break;
> >> +             }
> >> +     }
> >> +
> >> +     if (!found) {
> >> +             pr_err("OMAP PM: Error: no prior constraint to
> > release\n");
> >> +             ret = -EINVAL;
> >> +             goto exit_rls;
> >> +     }
> >> +
> >> +     plist_del(&user->node, &pwrdm->wakeuplat_dev_list);
> >> +     kfree(user);
> >> +
> >> +     pwrdm_wakeuplat_update_pwrst(pwrdm);
> >> +
> >> +exit_rls:
> >> +     mutex_unlock(&pwrdm->wakeuplat_mutex);
> >> +
> >> +     return ret;
> >> +}
> >> +
> >> +/**
> >>   * pwrdm_get_context_loss_count - get powerdomain's context loss
> >> count
> >>   * @pwrdm: struct powerdomain * to wait for
> >>   *
> >> diff --git a/arch/arm/mach-omap2/powerdomain.h b/arch/arm/mach-
> >> omap2/powerdomain.h
> >> index c66431e..d4189d8 100644
> >> --- a/arch/arm/mach-omap2/powerdomain.h
> >> +++ b/arch/arm/mach-omap2/powerdomain.h
> >> @@ -19,6 +19,9 @@
> >>
> >>  #include <linux/types.h>
> >>  #include <linux/list.h>
> >> +#include <linux/plist.h>
> >> +#include <linux/mutex.h>
> >> +#include <linux/spinlock.h>
> >>
> >>  #include <linux/atomic.h>
> >>
> >> @@ -46,6 +49,15 @@
> >>
> >>  #define PWRSTS_OFF_RET_ON    (PWRSTS_OFF_RET | (1 <<
> >> PWRDM_POWER_ON))
> >>
> >> +/* Powerdomain functional power states */
> >> +#define PWRDM_FUNC_PWRST_OFF 0x0
> >> +#define PWRDM_FUNC_PWRST_OSWR        0x1
> >> +#define PWRDM_FUNC_PWRST_CSWR        0x2
> >> +#define PWRDM_FUNC_PWRST_ON  0x3
> >> +
> >> +#define PWRDM_MAX_FUNC_PWRSTS        4
> >> +
> >> +#define UNSUP_STATE          -1
> >>
> >>  /* Powerdomain flags */
> >>  #define PWRDM_HAS_HDWR_SAR   (1 << 0) /* hardware save-
> and-
> >> restore support */
> >> @@ -96,6 +108,10 @@ struct powerdomain;
> >>   * @state_counter:
> >>   * @timer:
> >>   * @state_timer:
> >> + * @wakeup_lat: Wakeup latencies for possible powerdomain power
> >> states
> Will add a comment about the ordering (latency values must be sorted
> in decremental order).
>
> >> + * @wakeuplat_lock: spinlock for plist
> >> + * @wakeuplat_dev_list: plist_head linking all devices placing
> >> constraint
> >> + * @wakeuplat_mutex: mutex to protect per powerdomain list ops
> >>   *
> >>   * @prcm_partition possible values are defined in mach-
> >> omap2/prcm44xx.h.
> >>   */
> >> @@ -121,6 +137,16 @@ struct powerdomain {
> >>       s64 timer;
> >>       s64 state_timer[PWRDM_MAX_PWRSTS];
> >>  #endif
> >> +     const u32 wakeup_lat[PWRDM_MAX_FUNC_PWRSTS];
> >> +     spinlock_t wakeuplat_lock;
> >> +     struct plist_head wakeuplat_dev_list;
> >> +     struct mutex wakeuplat_mutex;
> >> +};
> >> +
> >> +struct wakeuplat_dev_list {
> >> +     struct device *dev;
> >> +     unsigned long constraint_us;
> >> +     struct plist_node node;
> >>  };
> >>
> >>  /**
> >> @@ -211,6 +237,11 @@ int pwrdm_clkdm_state_switch(struct
> >> clockdomain *clkdm);
> >>  int pwrdm_pre_transition(void);
> >>  int pwrdm_post_transition(void);
> >>  int pwrdm_set_lowpwrstchange(struct powerdomain *pwrdm);
> >> +
> >> +int pwrdm_wakeuplat_set_constraint(struct powerdomain *pwrdm,
> >> +                                struct device *dev, unsigned long
t);
> >> +int pwrdm_wakeuplat_release_constraint(struct powerdomain
> *pwrdm,
> >> +                                    struct device *dev);
> >>  u32 pwrdm_get_context_loss_count(struct powerdomain *pwrdm);
> >>
> >>  extern void omap2xxx_powerdomains_init(void);
> >> diff --git a/arch/arm/mach-omap2/powerdomains3xxx_data.c
> >> b/arch/arm/mach-omap2/powerdomains3xxx_data.c
> >> index e1bec56..4f7e44d 100644
> >> --- a/arch/arm/mach-omap2/powerdomains3xxx_data.c
> >> +++ b/arch/arm/mach-omap2/powerdomains3xxx_data.c
> >> @@ -52,6 +52,12 @@ static struct powerdomain iva2_pwrdm = {
> >>               [2] = PWRSTS_OFF_ON,
> >>               [3] = PWRDM_POWER_ON,
> >>       },
> >> +     .wakeup_lat = {
> >> +             [PWRDM_FUNC_PWRST_OFF] = 1100,
> >> +             [PWRDM_FUNC_PWRST_OSWR] = UNSUP_STATE,
> >> +             [PWRDM_FUNC_PWRST_CSWR] = 350,
> >> +             [PWRDM_FUNC_PWRST_ON] = 0,
> >> +     },
> >>  };
> >>
> >>  static struct powerdomain mpu_3xxx_pwrdm = {
> >> @@ -68,6 +74,12 @@ static struct powerdomain mpu_3xxx_pwrdm
> = {
> >>       .pwrsts_mem_on    = {
> >>               [0] = PWRSTS_OFF_ON,
> >>       },
> >> +     .wakeup_lat = {
> >> +             [PWRDM_FUNC_PWRST_OFF] = 95,
> >> +             [PWRDM_FUNC_PWRST_OSWR] = UNSUP_STATE,
> >> +             [PWRDM_FUNC_PWRST_CSWR] = 45,
> >> +             [PWRDM_FUNC_PWRST_ON] = 0,
> >> +     },
> >>  };
> >>
> >>  /*
> >> @@ -98,6 +110,12 @@ static struct powerdomain
> >> core_3xxx_pre_es3_1_pwrdm = {
> >>               [0] = PWRSTS_OFF_RET_ON, /* MEM1ONSTATE */
> >>               [1] = PWRSTS_OFF_RET_ON, /* MEM2ONSTATE */
> >>       },
> >> +     .wakeup_lat = {
> >> +             [PWRDM_FUNC_PWRST_OFF] = 100,
> >> +             [PWRDM_FUNC_PWRST_OSWR] = UNSUP_STATE,
> >> +             [PWRDM_FUNC_PWRST_CSWR] = 60,
> >> +             [PWRDM_FUNC_PWRST_ON] = 0,
> >> +     },
> >>  };
> >>
> >>  static struct powerdomain core_3xxx_es3_1_pwrdm = {
> >> @@ -121,6 +139,12 @@ static struct powerdomain
> >> core_3xxx_es3_1_pwrdm = {
> >>               [0] = PWRSTS_OFF_RET_ON, /* MEM1ONSTATE */
> >>               [1] = PWRSTS_OFF_RET_ON, /* MEM2ONSTATE */
> >>       },
> >> +     .wakeup_lat = {
> >> +             [PWRDM_FUNC_PWRST_OFF] = 100,
> >> +             [PWRDM_FUNC_PWRST_OSWR] = UNSUP_STATE,
> >> +             [PWRDM_FUNC_PWRST_CSWR] = 60,
> >> +             [PWRDM_FUNC_PWRST_ON] = 0,
> >> +     },
> >>  };
> >>
> >>  static struct powerdomain dss_pwrdm = {
> >> @@ -136,6 +160,12 @@ static struct powerdomain dss_pwrdm = {
> >>       .pwrsts_mem_on    = {
> >>               [0] = PWRDM_POWER_ON,  /* MEMONSTATE */
> >>       },
> >> +     .wakeup_lat = {
> >> +             [PWRDM_FUNC_PWRST_OFF] = 70,
> >> +             [PWRDM_FUNC_PWRST_OSWR] = UNSUP_STATE,
> >> +             [PWRDM_FUNC_PWRST_CSWR] = 20,
> >> +             [PWRDM_FUNC_PWRST_ON] = 0,
> >> +     },
> >>  };
> >>
> >>  /*
> >> @@ -157,6 +187,12 @@ static struct powerdomain sgx_pwrdm = {
> >>       .pwrsts_mem_on    = {
> >>               [0] = PWRDM_POWER_ON,  /* MEMONSTATE */
> >>       },
> >> +     .wakeup_lat = {
> >> +             [PWRDM_FUNC_PWRST_OFF] = 1000,
> >> +             [PWRDM_FUNC_PWRST_OSWR] = UNSUP_STATE,
> >> +             [PWRDM_FUNC_PWRST_CSWR] = UNSUP_STATE,
> >> +             [PWRDM_FUNC_PWRST_ON] = 0,
> >> +     },
> >>  };
> >>
> >>  static struct powerdomain cam_pwrdm = {
> >> @@ -172,6 +208,12 @@ static struct powerdomain cam_pwrdm = {
> >>       .pwrsts_mem_on    = {
> >>               [0] = PWRDM_POWER_ON,  /* MEMONSTATE */
> >>       },
> >> +     .wakeup_lat = {
> >> +             [PWRDM_FUNC_PWRST_OFF] = 850,
> >> +             [PWRDM_FUNC_PWRST_OSWR] = UNSUP_STATE,
> >> +             [PWRDM_FUNC_PWRST_CSWR] = 35,
> >> +             [PWRDM_FUNC_PWRST_ON] = 0,
> >> +     },
> >>  };
> >>
> >>  static struct powerdomain per_pwrdm = {
> >> @@ -187,6 +229,12 @@ static struct powerdomain per_pwrdm = {
> >>       .pwrsts_mem_on    = {
> >>               [0] = PWRDM_POWER_ON,  /* MEMONSTATE */
> >>       },
> >> +     .wakeup_lat = {
> >> +             [PWRDM_FUNC_PWRST_OFF] = 200,
> >> +             [PWRDM_FUNC_PWRST_OSWR] = UNSUP_STATE,
> >> +             [PWRDM_FUNC_PWRST_CSWR] = 110,
> >> +             [PWRDM_FUNC_PWRST_ON] = 0,
> >> +     },
> >>  };
> >>
> >>  static struct powerdomain emu_pwrdm = {
> >> @@ -201,6 +249,12 @@ static struct powerdomain neon_pwrdm = {
> >>       .omap_chip        = OMAP_CHIP_INIT(CHIP_IS_OMAP3430),
> >>       .pwrsts           = PWRSTS_OFF_RET_ON,
> >>       .pwrsts_logic_ret = PWRDM_POWER_RET,
> >> +     .wakeup_lat = {
> >> +             [PWRDM_FUNC_PWRST_OFF] = 200,
> >> +             [PWRDM_FUNC_PWRST_OSWR] = UNSUP_STATE,
> >> +             [PWRDM_FUNC_PWRST_CSWR] = 35,
> >> +             [PWRDM_FUNC_PWRST_ON] = 0,
> >> +     },
> >>  };
> >>
> >>  static struct powerdomain usbhost_pwrdm = {
> >> @@ -223,6 +277,12 @@ static struct powerdomain usbhost_pwrdm
> = {
> >>       .pwrsts_mem_on    = {
> >>               [0] = PWRDM_POWER_ON,  /* MEMONSTATE */
> >>       },
> >> +     .wakeup_lat = {
> >> +             [PWRDM_FUNC_PWRST_OFF] = 800,
> >> +             [PWRDM_FUNC_PWRST_OSWR] = UNSUP_STATE,
> >> +             [PWRDM_FUNC_PWRST_CSWR] = 150,
> >> +             [PWRDM_FUNC_PWRST_ON] = 0,
> >> +     },
> >>  };
> >>
> >>  static struct powerdomain dpll1_pwrdm = {
> >> diff --git a/arch/arm/plat-omap/include/plat/omap_device.h
> >> b/arch/arm/plat-omap/include/plat/omap_device.h
> >> index e4c349f..5da6b47 100644
> >> --- a/arch/arm/plat-omap/include/plat/omap_device.h
> >> +++ b/arch/arm/plat-omap/include/plat/omap_device.h
> >> @@ -107,6 +107,8 @@ void __iomem
> *omap_device_get_rt_va(struct
> >> omap_device *od);
> >>  int omap_device_align_pm_lat(struct platform_device *pdev,
> >>                            u32 new_wakeup_lat_limit);
> >>  struct powerdomain *omap_device_get_pwrdm(struct omap_device
> >> *od);
> >> +int omap_device_set_max_dev_wakeup_lat(struct platform_device
> >> *req_pdev,
> >> +                                    struct platform_device *pdev,
long
> > t);
> >>  u32 omap_device_get_context_loss_count(struct platform_device
> >> *pdev);
> >>
> >>  /* Other */
> >> diff --git a/arch/arm/plat-omap/include/plat/omap_hwmod.h
> >> b/arch/arm/plat-omap/include/plat/omap_hwmod.h
> >> index 1eee85a..0fbb974 100644
> >> --- a/arch/arm/plat-omap/include/plat/omap_hwmod.h
> >> +++ b/arch/arm/plat-omap/include/plat/omap_hwmod.h
> >> @@ -587,6 +587,8 @@ int omap_hwmod_for_each_by_class(const
> char
> >> *classname,
> >>                                void *user);
> >>
> >>  int omap_hwmod_set_postsetup_state(struct omap_hwmod *oh, u8
> >> state);
> >> +int omap_hwmod_set_max_dev_wakeup_lat(struct omap_hwmod
> >> *req_oh,
> >> +                                   struct omap_hwmod *oh, long t);
> >>  u32 omap_hwmod_get_context_loss_count(struct omap_hwmod
> *oh);
> >>
> >>  /*
> >> diff --git a/arch/arm/plat-omap/omap-pm-constraints.c
> b/arch/arm/plat-
> >> omap/omap-pm-constraints.c
> >> index c8b4e4c..7ae855f 100644
> >> --- a/arch/arm/plat-omap/omap-pm-constraints.c
> >> +++ b/arch/arm/plat-omap/omap-pm-constraints.c
> >> @@ -24,6 +24,7 @@
> >>  /* Interface documentation is in mach/omap-pm.h */
> >>  #include <plat/omap-pm.h>
> >>  #include <plat/omap_device.h>
> >> +#include <plat/common.h>
> >>
> >>  static bool off_mode_enabled;
> >>  static u32 dummy_context_loss_counter;
> >> @@ -32,34 +33,6 @@ static u32 dummy_context_loss_counter;
> >>   * Device-driver-originated constraints (via board-*.c files)
> >>   */
> >>
> >> -int omap_pm_set_max_mpu_wakeup_lat(struct device *dev, long t)
> >> -{
> >> -     if (!dev || t < -1) {
> >> -             WARN(1, "OMAP PM: %s: invalid parameter(s)",
> >> __func__);
> >> -             return -EINVAL;
> >> -     };
> >> -
> >> -     if (t == -1)
> >> -             pr_debug("OMAP PM: remove max MPU wakeup latency
> >> constraint: "
> >> -                      "dev %s\n", dev_name(dev));
> >> -     else
> >> -             pr_debug("OMAP PM: add max MPU wakeup latency
> >> constraint: "
> >> -                      "dev %s, t = %ld usec\n", dev_name(dev), t);
> >> -
> >> -     /*
> >> -      * For current Linux, this needs to map the MPU to a
> >> -      * powerdomain, then go through the list of current max lat
> >> -      * constraints on the MPU and find the smallest.  If
> >> -      * the latency constraint has changed, the code should
> >> -      * recompute the state to enter for the next powerdomain
> >> -      * state.
> >> -      *
> >> -      * TI CDP code can call constraint_set here.
> >> -      */
> >> -
> >> -     return 0;
> >> -}
> >> -
> >>  int omap_pm_set_min_bus_tput(struct device *dev, u8 agent_id,
> >> unsigned long r)
> >>  {
> >>       if (!dev || (agent_id != OCP_INITIATOR_AGENT &&
> >> @@ -87,62 +60,70 @@ int omap_pm_set_min_bus_tput(struct
> device
> >> *dev, u8 agent_id, unsigned long r)
> >>       return 0;
> >>  }
> >>
> >> +/*
> >> + * omap_pm_set_max_dev_wakeup_lat - set/release devices wake-
> up
> >> latency
> >> + * constraints
> >> + */
> >>  int omap_pm_set_max_dev_wakeup_lat(struct device *req_dev,
> struct
> >> device *dev,
> >>                                  long t)
> >>  {
> >> +     struct platform_device *pdev, *req_pdev;
> >> +     int ret = 0;
> >> +
> >>       if (!req_dev || !dev || t < -1) {
> >>               WARN(1, "OMAP PM: %s: invalid parameter(s)",
> >> __func__);
> >>               return -EINVAL;
> >> +     }
> >> +
> >> +     /* Look for the platform devices */
> >> +     pdev = container_of(dev, struct platform_device, dev);
> >
> > You could perhaps use macro to_platform_device instead of using
> > container_of.
> Yes that is better.
>
> >
> >> +     req_pdev = container_of(req_dev, struct platform_device, dev);
> Same here.
>
> >> +
> >> +     /* Try to catch non platform devices. */
> >> +     if ((pdev->name == NULL) || (req_pdev->name == NULL)) {
> >> +             pr_err("OMAP-PM set_wakeup_lat: Error: platform devices
> >> "
> >> +                    "not valid\n");
> >> +             return -EINVAL;
> >> +     } else {
> >> +             /* Call the omap_device API */
> >> +             ret = omap_device_set_max_dev_wakeup_lat(req_pdev,
> >> pdev, t);
> >> +     }
> >> +
> >> +     return ret;
> >> +}
> >> +
> >> +int omap_pm_set_max_mpu_wakeup_lat(struct device *req_dev,
> long
> >> t)
> >> +{
> >> +     if (!req_dev || t < -1) {
> >> +             WARN(1, "OMAP PM: %s: invalid parameter(s)",
> >> __func__);
> >> +             return -EINVAL;
> >>       };
> >>
> >>       if (t == -1)
> >> -             pr_debug("OMAP PM: remove max device latency
> >> constraint: "
> >> -                      "dev %s\n", dev_name(dev));
> >> +             pr_debug("OMAP PM: remove max MPU wakeup latency
> >> constraint: "
> >> +                      "dev %s\n", dev_name(req_dev));
> >>       else
> >> -             pr_debug("OMAP PM: add max device latency constraint: "
> >> -                      "dev %s, t = %ld usec\n", dev_name(dev), t);
> >> +             pr_debug("OMAP PM: add max MPU wakeup latency
> >> constraint: "
> >> +                      "dev %s, t = %ld usec\n", dev_name(req_dev),
t);
> >>
> >> -     /*
> >> -      * For current Linux, this needs to map the device to a
> >> -      * powerdomain, then go through the list of current max lat
> >> -      * constraints on that powerdomain and find the smallest.  If
> >> -      * the latency constraint has changed, the code should
> >> -      * recompute the state to enter for the next powerdomain
> >> -      * state.  Conceivably, this code should also determine
> >> -      * whether to actually disable the device clocks or not,
> >> -      * depending on how long it takes to re-enable the clocks.
> >> -      *
> >> -      * TI CDP code can call constraint_set here.
> >> -      */
> >> +     omap_pm_set_max_dev_wakeup_lat(req_dev,
> >> omap2_get_mpuss_device(), t);
> >>
> >>       return 0;
> >>  }
> >>
> >> -int omap_pm_set_max_sdma_lat(struct device *dev, long t)
> >> +int omap_pm_set_max_sdma_lat(struct device *req_dev, long t)
> >>  {
> >> -     if (!dev || t < -1) {
> >> +     if (!req_dev || t < -1) {
> >>               WARN(1, "OMAP PM: %s: invalid parameter(s)",
> >> __func__);
> >>               return -EINVAL;
> >>       };
> >>
> >>       if (t == -1)
> >>               pr_debug("OMAP PM: remove max DMA latency constraint:
> >> "
> >> -                      "dev %s\n", dev_name(dev));
> >> +                      "dev %s\n", dev_name(req_dev));
> >>       else
> >>               pr_debug("OMAP PM: add max DMA latency constraint: "
> >> -                      "dev %s, t = %ld usec\n", dev_name(dev), t);
> >> -
> >> -     /*
> >> -      * For current Linux PM QOS params, this code should scan the
> >> -      * list of maximum CPU and DMA latencies and select the
> >> -      * smallest, then set cpu_dma_latency pm_qos_param
> >> -      * accordingly.
> >> -      *
> >> -      * For future Linux PM QOS params, with separate CPU and DMA
> >> -      * latency params, this code should just set the dma_latency
> >> param.
> >> -      *
> >> -      * TI CDP code can call constraint_set here.
> >> -      */
> >> +                      "dev %s, t = %ld usec\n", dev_name(req_dev),
t);
> >>
> >>       return 0;
> >>  }
> >> diff --git a/arch/arm/plat-omap/omap_device.c b/arch/arm/plat-
> >> omap/omap_device.c
> >> index 57adb27..1fe93d3 100644
> >> --- a/arch/arm/plat-omap/omap_device.c
> >> +++ b/arch/arm/plat-omap/omap_device.c
> >> @@ -280,6 +280,34 @@ static void _add_optional_clock_alias(struct
> >> omap_device *od,
> >>  /* Public functions for use by core code */
> >>
> >>  /**
> >> + * omap_device_set_max_dev_wakeup_lat - set/release a device
> >> constraint
> >> + * @od: struct omap_device *
> >> + * @req_od: struct omap_device *
> >> + *
> >> + * Using the primary hwmod, set/release a device constraint for the
> >> pdev
> >> + * device, requested by the req_pdev device.
> >> + *
> >> + * If any hwmods exist for the omap_device assoiated with @pdev
> and
> >> @req_pdev,
> >> + * set/release the constraint for the corresponding hwmods,
> otherwise
> >> return
> >> + * -EINVAL.
> >> + */
> >> +int omap_device_set_max_dev_wakeup_lat(struct platform_device
> >> *req_pdev,
> >> +                                    struct platform_device *pdev,
long
> > t)
> >> +{
> >> +     struct omap_device *od, *req_od;
> >> +     u32 ret = -EINVAL;
> >> +
> >> +     od = _find_by_pdev(pdev);
> >> +     req_od = _find_by_pdev(req_pdev);
> >> +
> >> +     if ((od->hwmods_cnt) && (req_od->hwmods_cnt))
> >> +             ret = omap_hwmod_set_max_dev_wakeup_lat(req_od-
> >> >hwmods[0],
> >> +                                                     od->hwmods[0],
t);
> >> +
> >> +     return ret;
> >> +}
> >> +
> >> +/**
> >>   * omap_device_get_context_loss_count - get lost context count
> >>   * @od: struct omap_device *
> >>   *
> > Vishwa
> >> --
> >> 1.7.2.3
> >
>
> Thanks for the review, I will resubmit the patch asap.
>
> Regards,
> Jean
--
To unsubscribe from this list: send the line "unsubscribe linux-omap" in
the body of a message to majordomo@vger.kernel.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html

^ permalink raw reply	[flat|nested] 19+ messages in thread

* Re: [PATCH 2/2] OMAP: PM: implement devices wakeup latency constraints APIs
  2011-02-16 10:49       ` Vishwanath Sripathy
@ 2011-02-16 12:58         ` Pihet-XID, Jean
  0 siblings, 0 replies; 19+ messages in thread
From: Pihet-XID, Jean @ 2011-02-16 12:58 UTC (permalink / raw)
  To: Vishwanath Sripathy
  Cc: Jean Pihet, Kevin Hilman, paul, Vibhore Vardhan,
	Santosh Shilimkar, Rajendra Nayak, linux-omap

Hi Vishwa,

On Wed, Feb 16, 2011 at 11:49 AM, Vishwanath Sripathy
<vishwanath.bs@ti.com> wrote:
>> -----Original Message-----
>> From: Jean Pihet [mailto:jean.pihet@newoldbits.com]
>> Sent: Tuesday, February 15, 2011 7:22 PM
>> To: Vishwanath Sripathy
>> Cc: Kevin Hilman; paul@pwsan.com; Vibhore Vardhan; Santosh
>> Shilimkar; Rajendra Nayak; linux-omap@vger.kernel.org; Jean Pihet-XID
>> Subject: Re: [PATCH 2/2] OMAP: PM: implement devices wakeup latency
>> constraints APIs
>>
>> Hi Vishwa,
>>
>> On Tue, Feb 15, 2011 at 10:35 AM, Vishwanath Sripathy
>> <vishwanath.bs@ti.com> wrote:
>> >> -----Original Message-----
>> >> From: jean.pihet@newoldbits.com
>> [mailto:jean.pihet@newoldbits.com]
>> >> Sent: Friday, February 11, 2011 12:53 AM
>> >> To: khilman@ti.com; paul@pwsan.com; Vibhore Vardhan; Santosh
>> >> Shilimkar; Vishwanath BS; rnayak@ti.com
>> >> Cc: linux-omap@vger.kernel.org; Jean Pihet; Vibhore Vardhan
>> >> Subject: [PATCH 2/2] OMAP: PM: implement devices wakeup latency
>> >> constraints APIs
>> >>
>> >> From: Jean Pihet <j-pihet@ti.com>
>> >>
>> >> Implement OMAP PM layer omap_pm_set_max_dev_wakeup_lat API
>> by
>> >> creating similar APIs at the omap_device and omap_hwmod levels.
>> The
>> >> omap_hwmod level call is the layer with access to the powerdomain
>> >> core, so it is the place where the powerdomain is queried to set and
>> >> release the constraints.
>> >>
>> >> NOTE: only works for devices which have been converted to use
>> >>       omap_device/omap_hwmod.
>> >>
>> >> Longer term, we could possibly remove this API from the OMAP PM
>> layer,
>> >> and instead directly use the omap_device level API.
>> >>
>> >> Based on Vibhore's original patch , adapted to omap_device and
>> >> omap_hwmod frameworks.
>> >>
>> >> Signed-off-by: Jean Pihet <j-pihet@ti.com>
>> >> Cc: Vibhore Vardhan <vvardhan@ti.com>
>> >> ---
>> >>  arch/arm/mach-omap2/omap_hwmod.c              |   62
>> +++++++++-
>> >>  arch/arm/mach-omap2/powerdomain.c             |  176
>> >> ++++++++++++++++++++++++-
>> >>  arch/arm/mach-omap2/powerdomain.h             |   31 +++++
>> >>  arch/arm/mach-omap2/powerdomains3xxx_data.c   |   60
>> >> +++++++++
>> >>  arch/arm/plat-omap/include/plat/omap_device.h |    2 +
>> >>  arch/arm/plat-omap/include/plat/omap_hwmod.h  |    2 +
>> >>  arch/arm/plat-omap/omap-pm-constraints.c      |  101 ++++++----
>> -----
>> >>  arch/arm/plat-omap/omap_device.c              |   28 ++++
>> >>  8 files changed, 399 insertions(+), 63 deletions(-)
>> >>
>> >> diff --git a/arch/arm/mach-omap2/omap_hwmod.c
>> b/arch/arm/mach-
>> >> omap2/omap_hwmod.c
>> >> index e282e35..0dc096f 100644
>> >> --- a/arch/arm/mach-omap2/omap_hwmod.c
>> >> +++ b/arch/arm/mach-omap2/omap_hwmod.c
>> >> @@ -142,6 +142,7 @@
>> >>  #include "powerdomain.h"
>> >>  #include <plat/clock.h>
>> >>  #include <plat/omap_hwmod.h>
>> >> +#include <plat/omap_device.h>
>> >>  #include <plat/prcm.h>
>> >>
>> >>  #include "cm2xxx_3xxx.h"
>> >> @@ -2198,10 +2199,69 @@ ohsps_unlock:
>> >>  }
>> >>
>> >>  /**
>> >> + * omap_hwmod_set_max_dev_wakeup_lat - set a device wake-up
>> >> constraint
>> >> + * @oh: struct omap_hwmod *
>> >> + * @req_oh: struct omap_hwmod *
>> > Need to explain what this parameters mean.
>> Ok, will add a description here. Basically oh corresponds to the
>> device (and so the power domain) to set a constraint on and req_oh is
>> the constraint requester. oh is used to determine which power domain
>> to set a constraint on, req_oh is used to record the requester for
>> later update or removal of a constraint.
>>
>> >> + * @t: wakeup latency constraint (us). -1 removes the existing
>> >> constraint
>> >> + *
>> >> + * Query the powerdomain of @oh to set/release the wake-up
>> >> constraint
>> >> + *
>> >> + * Returns -EINVAL if @oh or @req_oh have no power domain, or
>> the
>> >> return
>> >> + * code from the pwrdm function
>> >> (pwrdm_wakeuplat_set/release_constraint)
>> >> + * of the powerdomain assocated with @oh.
>> >> + */
>> >> +int omap_hwmod_set_max_dev_wakeup_lat(struct omap_hwmod
>> >> *req_oh,
>> >> +                                   struct omap_hwmod *oh, long t)
>> >> +{
>> >> +     struct device *req_dev;
>> >> +     struct platform_device *pdev;
>> >> +     struct powerdomain *pwrdm;
>> >> +     int ret = 0;
>> >> +
>> >> +     pwrdm = omap_hwmod_get_pwrdm(oh);
>> >> +
>> >> +     /* Catch devices with undefined powerdomains */
>> >> +     if (!pwrdm) {
>> >> +             pr_err("omap_hwmod: Error: could not find parent "
>> >> +                     "powerdomain for %s\n", oh->name);
>> >> +             return -EINVAL;
>> >> +     }
>> >> +
>> >> +     pdev = &(req_oh->od->pdev);
>> >> +     if (pdev == NULL) {
>> >> +             pr_err("omap_hwmod: Error: pdev not found for oh
>> %s\n",
>> >> +                    oh->name);
>> >> +             return -EINVAL;
>> >> +     }
>> >> +
>> >> +     req_dev = &(pdev->dev);
>> >> +     if (req_dev == NULL) {
>> >> +             pr_err("omap_hwmod: Error: device not found for oh
>> >> %s\n",
>> >> +                    oh->name);
>> >> +             return -EINVAL;
>> >> +     }
>> > If I understand correctly, req_dev is used for keeping track of
> different
>> > requests. If so, why can't you directly pass req_dev as an input param
>> > instead of deriving it from pdev.
>> The layering in the API is as follows: caller -> omap-pm ->
>> omap_device -> omap_hwmod -> powerdomain. The  parameters types
>> are
>> passed accordingly.
>>
>> Note: I will rename pdev to req_pdev to make it clear that the
>> parameter relates to the requester.
>>
>> >> +
>> >> +     /* Call set/release_constraint for the given pwrdm */
>> >> +     if (t == -1) {
>> >> +             pr_debug("omap_hwmod: remove max device latency
>> >> constraint: "
>> >> +                      "oh %s, pwrdm %s, req by oh %s\n",
>> >> +                      oh->name, pwrdm->name, req_oh->name);
>> >> +             ret = pwrdm_wakeuplat_release_constraint(pwrdm,
>> >> req_dev);
>> >> +     } else {
>> >> +             pr_debug("omap_hwmod: add max device latency
>> >> constraint: "
>> >> +                      "oh %s, t = %ld usec, pwrdm %s, req by oh
>> >> %s\n",
>> >> +                      oh->name, t, pwrdm->name, req_oh->name);
>> >> +             ret = pwrdm_wakeuplat_set_constraint(pwrdm, req_dev,
>> >> t);
>> >> +     }
>> >> +
>> >> +     return 0;
>> >> +}
>> >> +
>> >> +/**
>> >>   * omap_hwmod_get_context_loss_count - get lost context count
>> >>   * @oh: struct omap_hwmod *
>> >>   *
>> >> - * Query the powerdomain of of @oh to get the context loss
>> >> + * Query the powerdomain of @oh to get the context loss
>> >>   * count for this device.
>> >>   *
>> >>   * Returns the context loss count of the powerdomain assocated with
>> >> @oh
>> >> diff --git a/arch/arm/mach-omap2/powerdomain.c b/arch/arm/mach-
>> >> omap2/powerdomain.c
>> >> index eaed0df..3ed3bea 100644
>> >> --- a/arch/arm/mach-omap2/powerdomain.c
>> >> +++ b/arch/arm/mach-omap2/powerdomain.c
>> >> @@ -19,16 +19,19 @@
>> >>  #include <linux/list.h>
>> >>  #include <linux/errno.h>
>> >>  #include <linux/string.h>
>> >> +#include <linux/slab.h>
>> >> +
>> >> +#include <plat/cpu.h>
>> >> +#include <plat/prcm.h>
>> >> +
>> >>  #include "cm2xxx_3xxx.h"
>> >>  #include "prcm44xx.h"
>> >>  #include "cm44xx.h"
>> >>  #include "prm2xxx_3xxx.h"
>> >>  #include "prm44xx.h"
>> >>
>> >> -#include <plat/cpu.h>
>> >>  #include "powerdomain.h"
>> >>  #include "clockdomain.h"
>> >> -#include <plat/prcm.h>
>> >>
>> >>  #include "pm.h"
>> >>
>> >> @@ -103,6 +106,13 @@ static int _pwrdm_register(struct
>> powerdomain
>> >> *pwrdm)
>> >>       pwrdm->state = pwrdm_read_pwrst(pwrdm);
>> >>       pwrdm->state_counter[pwrdm->state] = 1;
>> >>
>> >> +     /* Initialize priority ordered list for wakeup latency
> constraint
>> > */
>> >> +     spin_lock_init(&pwrdm->wakeuplat_lock);
>> >> +     plist_head_init(&pwrdm->wakeuplat_dev_list, &pwrdm-
>> >> >wakeuplat_lock);
>> >> +
>> >> +     /* res_mutex protects res_list add and del ops */
>> >> +     mutex_init(&pwrdm->wakeuplat_mutex);
>> >> +
>> >>       pr_debug("powerdomain: registered %s\n", pwrdm->name);
>> >>
>> >>       return 0;
>> >> @@ -176,6 +186,62 @@ static int _pwrdm_post_transition_cb(struct
>> >> powerdomain *pwrdm, void *unused)
>> >>       return 0;
>> >>  }
>> >>
>> >> +/**
>> >> + * pwrdm_wakeuplat_update_pwrst - Update power domain power
>> >> state if needed
>> >> + * @pwrdm: struct powerdomain * to which requesting device
>> belongs
>> >> to
>> >> + *
>> >> + * Finds minimum latency value from all entries in the list and
>> >> + * the power domain power state needing the constraint. Programs
>> >> + * new state if it is different from current power state.
>> >> + */
>> >> +static void pwrdm_wakeuplat_update_pwrst(struct powerdomain
>> >> *pwrdm)
>> >> +{
>> >> +     struct plist_node *node;
>> >> +     int new_state;
>> >> +     unsigned long min_latency = -1;
>> >> +
>> >> +     if (!plist_head_empty(&pwrdm->wakeuplat_dev_list)) {
>> >> +             node = plist_last(&pwrdm->wakeuplat_dev_list);
>> > Wouldn't plist_last return the node with highest latency? I think you
>> are
>> > looking for lowest latency.
>> Yes indeed. We need the strongest constraint, so the lowest allowed
>> wake-up latency value.
> Then shouldn't you use plist_first?
> I suppose plist_last will return the highest latency (weakest constraint).
That is correct. plist_first must be used instead.

>>
>> >> +             min_latency = node->prio;
>> >> +     }
>> >> +
>> >> +     /* Find power state with wakeup latency < minimum constraint.
>> >> */
>> >> +     for (new_state = 0x0; new_state < PWRDM_MAX_PWRSTS;
>> >> new_state++) {
>> >> +             if (min_latency == -1 ||
>> >> +                 pwrdm->wakeup_lat[new_state] <= min_latency)
>> >> +                     break;
>> >> +     }
>> >> +
>> >> +     switch (new_state) {
>> >> +     case PWRDM_FUNC_PWRST_OFF:
>> >> +             new_state = PWRDM_POWER_OFF;
>> >> +             break;
>> >> +     case PWRDM_FUNC_PWRST_OSWR:
>> >> +             pwrdm_set_logic_retst(pwrdm, PWRDM_POWER_OFF);
>> >> +     case PWRDM_FUNC_PWRST_CSWR:
>> >> +             new_state = PWRDM_POWER_RET;
>> >> +             break;
>> >> +     case PWRDM_FUNC_PWRST_ON:
>> >> +             new_state = PWRDM_POWER_ON;
>> >> +             break;
>> >> +     default:
>> >> +             pr_warn("powerdomain: requested latency constraint not
> "
>> >> +                     "supported %s set to ON state\n", pwrdm->name);
>> >> +             new_state = PWRDM_POWER_ON;
>> >> +             break;
>> >> +     }
>> >> +
>> >> +     if (pwrdm_read_pwrst(pwrdm) != new_state) {
>> >> +             if (cpu_is_omap34xx())
>> > Why this cpu check here?
>> Only 34xx has support for the latency constraints. The other platforms
>> have the wakeup latency values set as '0', which means that OFF mode
>> will always be chosen as preferred state. Is it allowed to set the
>> pwrdm_state in this case?
> Then this will not work for OMAP4 right? OMAP4 also should support device
> latency constraints.
Yes OMAP4 support is coming later. The changes for OMAP4 will be minor
once OMAP3 support is working.

Thanks,
Jean

> Vishwa
>>
>> >> +                     omap_set_pwrdm_state(pwrdm, new_state);
>> >> +     }
>> >> +
>> >> +     pr_debug("powerdomain: %s pwrst: curr= %d, prev= %d
>> next=
>> >> %d "
>> >> +              "wkuplat_min= %lu, set_state= %d\n", pwrdm->name,
>> >> +              pwrdm_read_pwrst(pwrdm),
>> >> pwrdm_read_prev_pwrst(pwrdm),
>> >> +              pwrdm_read_next_pwrst(pwrdm), min_latency,
>> >> new_state);
>> >> +}
>> >> +
>> >>  /* Public functions */
>> >>
>> >>  /**
>> >> @@ -911,6 +977,112 @@ int pwrdm_post_transition(void)
>> >>  }
>> >>
>> >>  /**
>> >> + * pwrdm_wakeuplat_set_constraint - Set powerdomain wakeup
>> >> latency constraint
>> >> + * @pwrdm: struct powerdomain * to which requesting device
>> belongs
>> >> to
>> >> + * @dev: struct device * of requesting device
>> >> + * @t: wakeup latency constraint in microseconds
>> >> + *
>> >> + * Adds new entry to powerdomain's wakeup latency constraint list.
>> >> + * If the requesting device already exists in the list, old value is
>> >> + * overwritten. Checks whether current power state is still
>> adequate.
>> >> + * Returns -EINVAL if the powerdomain or device pointer is NULL,
>> >> + * or -ENOMEM if kmalloc fails, or returns 0 upon success.
>> >> + */
>> >> +int pwrdm_wakeuplat_set_constraint(struct powerdomain *pwrdm,
>> >> +                                struct device *req_dev, unsigned
> long
>> > t)
>> >> +{
>> >> +     struct  wakeuplat_dev_list *user;
>> >> +     int found = 0, ret = 0;
>> >> +
>> >> +     if (!pwrdm || !req_dev) {
>> >> +             WARN(1, "powerdomain: %s: invalid parameter(s)",
>> >> __func__);
>> >> +             return -EINVAL;
>> >> +     }
>> >> +
>> >> +     mutex_lock(&pwrdm->wakeuplat_mutex);
>> >> +
>> >> +     plist_for_each_entry(user, &pwrdm->wakeuplat_dev_list, node)
>> {
>> >> +             if (user->dev == req_dev) {
>> >> +                     found = 1;
>> >> +                     break;
>> >> +             }
>> >> +     }
>> >> +
>> >> +     /* Add new entry to the list or update existing request */
>> >> +     if (found && user->constraint_us == t) {
>> >> +             goto exit_set;
>> >> +     } else if (!found) {
>> >> +             user = kzalloc(sizeof(struct wakeuplat_dev_list),
>> >> GFP_KERNEL);
>> >> +             if (!user) {
>> >> +                     pr_err("powerdomain: FATAL ERROR: kzalloc
>> >> failed\n");
>> >> +                     ret = -ENOMEM;
>> >> +                     goto exit_set;
>> >> +             }
>> >> +             user->dev = req_dev;
>> >> +     } else {
>> >> +             plist_del(&user->node, &pwrdm->wakeuplat_dev_list);
>> >> +     }
>> >> +
>> >> +     plist_node_init(&user->node, t);
>> >> +     plist_add(&user->node, &pwrdm->wakeuplat_dev_list);
>> >> +     user->node.prio = user->constraint_us = t;
>> >> +
>> >> +     pwrdm_wakeuplat_update_pwrst(pwrdm);
>> >> +
>> >> +exit_set:
>> >> +     mutex_unlock(&pwrdm->wakeuplat_mutex);
>> >> +
>> >> +     return ret;
>> >> +}
>> >> +
>> >> +/**
>> >> + * pwrdm_wakeuplat_release_constraint - Release powerdomain
>> >> wkuplat constraint
>> >> + * @pwrdm: struct powerdomain * to which requesting device
>> belongs
>> >> to
>> >> + * @req_dev: struct device * of requesting device
>> >> + *
>> >> + * Removes device's entry from powerdomain's wakeup latency
>> >> constraint list.
>> >> + * Checks whether current power state is still adequate.
>> >> + * Returns -EINVAL if the powerdomain or device pointer is NULL or
>> >> + * no such entry exists in the list, or returns 0 upon success.
>> >> + */
>> >> +int pwrdm_wakeuplat_release_constraint(struct powerdomain
>> *pwrdm,
>> >> +                                    struct device *req_dev)
>> >> +{
>> >> +     struct wakeuplat_dev_list *user;
>> >> +     int found = 0, ret = 0;
>> >> +
>> >> +     if (!pwrdm || !req_dev) {
>> >> +             WARN(1, "powerdomain: %s: invalid parameter(s)",
>> >> __func__);
>> >> +             return -EINVAL;
>> >> +     }
>> >> +
>> >> +     mutex_lock(&pwrdm->wakeuplat_mutex);
>> >> +
>> >> +     plist_for_each_entry(user, &pwrdm->wakeuplat_dev_list, node)
>> {
>> >> +             if (user->dev == req_dev) {
>> >> +                     found = 1;
>> >> +                     break;
>> >> +             }
>> >> +     }
>> >> +
>> >> +     if (!found) {
>> >> +             pr_err("OMAP PM: Error: no prior constraint to
>> > release\n");
>> >> +             ret = -EINVAL;
>> >> +             goto exit_rls;
>> >> +     }
>> >> +
>> >> +     plist_del(&user->node, &pwrdm->wakeuplat_dev_list);
>> >> +     kfree(user);
>> >> +
>> >> +     pwrdm_wakeuplat_update_pwrst(pwrdm);
>> >> +
>> >> +exit_rls:
>> >> +     mutex_unlock(&pwrdm->wakeuplat_mutex);
>> >> +
>> >> +     return ret;
>> >> +}
>> >> +
>> >> +/**
>> >>   * pwrdm_get_context_loss_count - get powerdomain's context loss
>> >> count
>> >>   * @pwrdm: struct powerdomain * to wait for
>> >>   *
>> >> diff --git a/arch/arm/mach-omap2/powerdomain.h b/arch/arm/mach-
>> >> omap2/powerdomain.h
>> >> index c66431e..d4189d8 100644
>> >> --- a/arch/arm/mach-omap2/powerdomain.h
>> >> +++ b/arch/arm/mach-omap2/powerdomain.h
>> >> @@ -19,6 +19,9 @@
>> >>
>> >>  #include <linux/types.h>
>> >>  #include <linux/list.h>
>> >> +#include <linux/plist.h>
>> >> +#include <linux/mutex.h>
>> >> +#include <linux/spinlock.h>
>> >>
>> >>  #include <linux/atomic.h>
>> >>
>> >> @@ -46,6 +49,15 @@
>> >>
>> >>  #define PWRSTS_OFF_RET_ON    (PWRSTS_OFF_RET | (1 <<
>> >> PWRDM_POWER_ON))
>> >>
>> >> +/* Powerdomain functional power states */
>> >> +#define PWRDM_FUNC_PWRST_OFF 0x0
>> >> +#define PWRDM_FUNC_PWRST_OSWR        0x1
>> >> +#define PWRDM_FUNC_PWRST_CSWR        0x2
>> >> +#define PWRDM_FUNC_PWRST_ON  0x3
>> >> +
>> >> +#define PWRDM_MAX_FUNC_PWRSTS        4
>> >> +
>> >> +#define UNSUP_STATE          -1
>> >>
>> >>  /* Powerdomain flags */
>> >>  #define PWRDM_HAS_HDWR_SAR   (1 << 0) /* hardware save-
>> and-
>> >> restore support */
>> >> @@ -96,6 +108,10 @@ struct powerdomain;
>> >>   * @state_counter:
>> >>   * @timer:
>> >>   * @state_timer:
>> >> + * @wakeup_lat: Wakeup latencies for possible powerdomain power
>> >> states
>> Will add a comment about the ordering (latency values must be sorted
>> in decremental order).
>>
>> >> + * @wakeuplat_lock: spinlock for plist
>> >> + * @wakeuplat_dev_list: plist_head linking all devices placing
>> >> constraint
>> >> + * @wakeuplat_mutex: mutex to protect per powerdomain list ops
>> >>   *
>> >>   * @prcm_partition possible values are defined in mach-
>> >> omap2/prcm44xx.h.
>> >>   */
>> >> @@ -121,6 +137,16 @@ struct powerdomain {
>> >>       s64 timer;
>> >>       s64 state_timer[PWRDM_MAX_PWRSTS];
>> >>  #endif
>> >> +     const u32 wakeup_lat[PWRDM_MAX_FUNC_PWRSTS];
>> >> +     spinlock_t wakeuplat_lock;
>> >> +     struct plist_head wakeuplat_dev_list;
>> >> +     struct mutex wakeuplat_mutex;
>> >> +};
>> >> +
>> >> +struct wakeuplat_dev_list {
>> >> +     struct device *dev;
>> >> +     unsigned long constraint_us;
>> >> +     struct plist_node node;
>> >>  };
>> >>
>> >>  /**
>> >> @@ -211,6 +237,11 @@ int pwrdm_clkdm_state_switch(struct
>> >> clockdomain *clkdm);
>> >>  int pwrdm_pre_transition(void);
>> >>  int pwrdm_post_transition(void);
>> >>  int pwrdm_set_lowpwrstchange(struct powerdomain *pwrdm);
>> >> +
>> >> +int pwrdm_wakeuplat_set_constraint(struct powerdomain *pwrdm,
>> >> +                                struct device *dev, unsigned long
> t);
>> >> +int pwrdm_wakeuplat_release_constraint(struct powerdomain
>> *pwrdm,
>> >> +                                    struct device *dev);
>> >>  u32 pwrdm_get_context_loss_count(struct powerdomain *pwrdm);
>> >>
>> >>  extern void omap2xxx_powerdomains_init(void);
>> >> diff --git a/arch/arm/mach-omap2/powerdomains3xxx_data.c
>> >> b/arch/arm/mach-omap2/powerdomains3xxx_data.c
>> >> index e1bec56..4f7e44d 100644
>> >> --- a/arch/arm/mach-omap2/powerdomains3xxx_data.c
>> >> +++ b/arch/arm/mach-omap2/powerdomains3xxx_data.c
>> >> @@ -52,6 +52,12 @@ static struct powerdomain iva2_pwrdm = {
>> >>               [2] = PWRSTS_OFF_ON,
>> >>               [3] = PWRDM_POWER_ON,
>> >>       },
>> >> +     .wakeup_lat = {
>> >> +             [PWRDM_FUNC_PWRST_OFF] = 1100,
>> >> +             [PWRDM_FUNC_PWRST_OSWR] = UNSUP_STATE,
>> >> +             [PWRDM_FUNC_PWRST_CSWR] = 350,
>> >> +             [PWRDM_FUNC_PWRST_ON] = 0,
>> >> +     },
>> >>  };
>> >>
>> >>  static struct powerdomain mpu_3xxx_pwrdm = {
>> >> @@ -68,6 +74,12 @@ static struct powerdomain mpu_3xxx_pwrdm
>> = {
>> >>       .pwrsts_mem_on    = {
>> >>               [0] = PWRSTS_OFF_ON,
>> >>       },
>> >> +     .wakeup_lat = {
>> >> +             [PWRDM_FUNC_PWRST_OFF] = 95,
>> >> +             [PWRDM_FUNC_PWRST_OSWR] = UNSUP_STATE,
>> >> +             [PWRDM_FUNC_PWRST_CSWR] = 45,
>> >> +             [PWRDM_FUNC_PWRST_ON] = 0,
>> >> +     },
>> >>  };
>> >>
>> >>  /*
>> >> @@ -98,6 +110,12 @@ static struct powerdomain
>> >> core_3xxx_pre_es3_1_pwrdm = {
>> >>               [0] = PWRSTS_OFF_RET_ON, /* MEM1ONSTATE */
>> >>               [1] = PWRSTS_OFF_RET_ON, /* MEM2ONSTATE */
>> >>       },
>> >> +     .wakeup_lat = {
>> >> +             [PWRDM_FUNC_PWRST_OFF] = 100,
>> >> +             [PWRDM_FUNC_PWRST_OSWR] = UNSUP_STATE,
>> >> +             [PWRDM_FUNC_PWRST_CSWR] = 60,
>> >> +             [PWRDM_FUNC_PWRST_ON] = 0,
>> >> +     },
>> >>  };
>> >>
>> >>  static struct powerdomain core_3xxx_es3_1_pwrdm = {
>> >> @@ -121,6 +139,12 @@ static struct powerdomain
>> >> core_3xxx_es3_1_pwrdm = {
>> >>               [0] = PWRSTS_OFF_RET_ON, /* MEM1ONSTATE */
>> >>               [1] = PWRSTS_OFF_RET_ON, /* MEM2ONSTATE */
>> >>       },
>> >> +     .wakeup_lat = {
>> >> +             [PWRDM_FUNC_PWRST_OFF] = 100,
>> >> +             [PWRDM_FUNC_PWRST_OSWR] = UNSUP_STATE,
>> >> +             [PWRDM_FUNC_PWRST_CSWR] = 60,
>> >> +             [PWRDM_FUNC_PWRST_ON] = 0,
>> >> +     },
>> >>  };
>> >>
>> >>  static struct powerdomain dss_pwrdm = {
>> >> @@ -136,6 +160,12 @@ static struct powerdomain dss_pwrdm = {
>> >>       .pwrsts_mem_on    = {
>> >>               [0] = PWRDM_POWER_ON,  /* MEMONSTATE */
>> >>       },
>> >> +     .wakeup_lat = {
>> >> +             [PWRDM_FUNC_PWRST_OFF] = 70,
>> >> +             [PWRDM_FUNC_PWRST_OSWR] = UNSUP_STATE,
>> >> +             [PWRDM_FUNC_PWRST_CSWR] = 20,
>> >> +             [PWRDM_FUNC_PWRST_ON] = 0,
>> >> +     },
>> >>  };
>> >>
>> >>  /*
>> >> @@ -157,6 +187,12 @@ static struct powerdomain sgx_pwrdm = {
>> >>       .pwrsts_mem_on    = {
>> >>               [0] = PWRDM_POWER_ON,  /* MEMONSTATE */
>> >>       },
>> >> +     .wakeup_lat = {
>> >> +             [PWRDM_FUNC_PWRST_OFF] = 1000,
>> >> +             [PWRDM_FUNC_PWRST_OSWR] = UNSUP_STATE,
>> >> +             [PWRDM_FUNC_PWRST_CSWR] = UNSUP_STATE,
>> >> +             [PWRDM_FUNC_PWRST_ON] = 0,
>> >> +     },
>> >>  };
>> >>
>> >>  static struct powerdomain cam_pwrdm = {
>> >> @@ -172,6 +208,12 @@ static struct powerdomain cam_pwrdm = {
>> >>       .pwrsts_mem_on    = {
>> >>               [0] = PWRDM_POWER_ON,  /* MEMONSTATE */
>> >>       },
>> >> +     .wakeup_lat = {
>> >> +             [PWRDM_FUNC_PWRST_OFF] = 850,
>> >> +             [PWRDM_FUNC_PWRST_OSWR] = UNSUP_STATE,
>> >> +             [PWRDM_FUNC_PWRST_CSWR] = 35,
>> >> +             [PWRDM_FUNC_PWRST_ON] = 0,
>> >> +     },
>> >>  };
>> >>
>> >>  static struct powerdomain per_pwrdm = {
>> >> @@ -187,6 +229,12 @@ static struct powerdomain per_pwrdm = {
>> >>       .pwrsts_mem_on    = {
>> >>               [0] = PWRDM_POWER_ON,  /* MEMONSTATE */
>> >>       },
>> >> +     .wakeup_lat = {
>> >> +             [PWRDM_FUNC_PWRST_OFF] = 200,
>> >> +             [PWRDM_FUNC_PWRST_OSWR] = UNSUP_STATE,
>> >> +             [PWRDM_FUNC_PWRST_CSWR] = 110,
>> >> +             [PWRDM_FUNC_PWRST_ON] = 0,
>> >> +     },
>> >>  };
>> >>
>> >>  static struct powerdomain emu_pwrdm = {
>> >> @@ -201,6 +249,12 @@ static struct powerdomain neon_pwrdm = {
>> >>       .omap_chip        = OMAP_CHIP_INIT(CHIP_IS_OMAP3430),
>> >>       .pwrsts           = PWRSTS_OFF_RET_ON,
>> >>       .pwrsts_logic_ret = PWRDM_POWER_RET,
>> >> +     .wakeup_lat = {
>> >> +             [PWRDM_FUNC_PWRST_OFF] = 200,
>> >> +             [PWRDM_FUNC_PWRST_OSWR] = UNSUP_STATE,
>> >> +             [PWRDM_FUNC_PWRST_CSWR] = 35,
>> >> +             [PWRDM_FUNC_PWRST_ON] = 0,
>> >> +     },
>> >>  };
>> >>
>> >>  static struct powerdomain usbhost_pwrdm = {
>> >> @@ -223,6 +277,12 @@ static struct powerdomain usbhost_pwrdm
>> = {
>> >>       .pwrsts_mem_on    = {
>> >>               [0] = PWRDM_POWER_ON,  /* MEMONSTATE */
>> >>       },
>> >> +     .wakeup_lat = {
>> >> +             [PWRDM_FUNC_PWRST_OFF] = 800,
>> >> +             [PWRDM_FUNC_PWRST_OSWR] = UNSUP_STATE,
>> >> +             [PWRDM_FUNC_PWRST_CSWR] = 150,
>> >> +             [PWRDM_FUNC_PWRST_ON] = 0,
>> >> +     },
>> >>  };
>> >>
>> >>  static struct powerdomain dpll1_pwrdm = {
>> >> diff --git a/arch/arm/plat-omap/include/plat/omap_device.h
>> >> b/arch/arm/plat-omap/include/plat/omap_device.h
>> >> index e4c349f..5da6b47 100644
>> >> --- a/arch/arm/plat-omap/include/plat/omap_device.h
>> >> +++ b/arch/arm/plat-omap/include/plat/omap_device.h
>> >> @@ -107,6 +107,8 @@ void __iomem
>> *omap_device_get_rt_va(struct
>> >> omap_device *od);
>> >>  int omap_device_align_pm_lat(struct platform_device *pdev,
>> >>                            u32 new_wakeup_lat_limit);
>> >>  struct powerdomain *omap_device_get_pwrdm(struct omap_device
>> >> *od);
>> >> +int omap_device_set_max_dev_wakeup_lat(struct platform_device
>> >> *req_pdev,
>> >> +                                    struct platform_device *pdev,
> long
>> > t);
>> >>  u32 omap_device_get_context_loss_count(struct platform_device
>> >> *pdev);
>> >>
>> >>  /* Other */
>> >> diff --git a/arch/arm/plat-omap/include/plat/omap_hwmod.h
>> >> b/arch/arm/plat-omap/include/plat/omap_hwmod.h
>> >> index 1eee85a..0fbb974 100644
>> >> --- a/arch/arm/plat-omap/include/plat/omap_hwmod.h
>> >> +++ b/arch/arm/plat-omap/include/plat/omap_hwmod.h
>> >> @@ -587,6 +587,8 @@ int omap_hwmod_for_each_by_class(const
>> char
>> >> *classname,
>> >>                                void *user);
>> >>
>> >>  int omap_hwmod_set_postsetup_state(struct omap_hwmod *oh, u8
>> >> state);
>> >> +int omap_hwmod_set_max_dev_wakeup_lat(struct omap_hwmod
>> >> *req_oh,
>> >> +                                   struct omap_hwmod *oh, long t);
>> >>  u32 omap_hwmod_get_context_loss_count(struct omap_hwmod
>> *oh);
>> >>
>> >>  /*
>> >> diff --git a/arch/arm/plat-omap/omap-pm-constraints.c
>> b/arch/arm/plat-
>> >> omap/omap-pm-constraints.c
>> >> index c8b4e4c..7ae855f 100644
>> >> --- a/arch/arm/plat-omap/omap-pm-constraints.c
>> >> +++ b/arch/arm/plat-omap/omap-pm-constraints.c
>> >> @@ -24,6 +24,7 @@
>> >>  /* Interface documentation is in mach/omap-pm.h */
>> >>  #include <plat/omap-pm.h>
>> >>  #include <plat/omap_device.h>
>> >> +#include <plat/common.h>
>> >>
>> >>  static bool off_mode_enabled;
>> >>  static u32 dummy_context_loss_counter;
>> >> @@ -32,34 +33,6 @@ static u32 dummy_context_loss_counter;
>> >>   * Device-driver-originated constraints (via board-*.c files)
>> >>   */
>> >>
>> >> -int omap_pm_set_max_mpu_wakeup_lat(struct device *dev, long t)
>> >> -{
>> >> -     if (!dev || t < -1) {
>> >> -             WARN(1, "OMAP PM: %s: invalid parameter(s)",
>> >> __func__);
>> >> -             return -EINVAL;
>> >> -     };
>> >> -
>> >> -     if (t == -1)
>> >> -             pr_debug("OMAP PM: remove max MPU wakeup latency
>> >> constraint: "
>> >> -                      "dev %s\n", dev_name(dev));
>> >> -     else
>> >> -             pr_debug("OMAP PM: add max MPU wakeup latency
>> >> constraint: "
>> >> -                      "dev %s, t = %ld usec\n", dev_name(dev), t);
>> >> -
>> >> -     /*
>> >> -      * For current Linux, this needs to map the MPU to a
>> >> -      * powerdomain, then go through the list of current max lat
>> >> -      * constraints on the MPU and find the smallest.  If
>> >> -      * the latency constraint has changed, the code should
>> >> -      * recompute the state to enter for the next powerdomain
>> >> -      * state.
>> >> -      *
>> >> -      * TI CDP code can call constraint_set here.
>> >> -      */
>> >> -
>> >> -     return 0;
>> >> -}
>> >> -
>> >>  int omap_pm_set_min_bus_tput(struct device *dev, u8 agent_id,
>> >> unsigned long r)
>> >>  {
>> >>       if (!dev || (agent_id != OCP_INITIATOR_AGENT &&
>> >> @@ -87,62 +60,70 @@ int omap_pm_set_min_bus_tput(struct
>> device
>> >> *dev, u8 agent_id, unsigned long r)
>> >>       return 0;
>> >>  }
>> >>
>> >> +/*
>> >> + * omap_pm_set_max_dev_wakeup_lat - set/release devices wake-
>> up
>> >> latency
>> >> + * constraints
>> >> + */
>> >>  int omap_pm_set_max_dev_wakeup_lat(struct device *req_dev,
>> struct
>> >> device *dev,
>> >>                                  long t)
>> >>  {
>> >> +     struct platform_device *pdev, *req_pdev;
>> >> +     int ret = 0;
>> >> +
>> >>       if (!req_dev || !dev || t < -1) {
>> >>               WARN(1, "OMAP PM: %s: invalid parameter(s)",
>> >> __func__);
>> >>               return -EINVAL;
>> >> +     }
>> >> +
>> >> +     /* Look for the platform devices */
>> >> +     pdev = container_of(dev, struct platform_device, dev);
>> >
>> > You could perhaps use macro to_platform_device instead of using
>> > container_of.
>> Yes that is better.
>>
>> >
>> >> +     req_pdev = container_of(req_dev, struct platform_device, dev);
>> Same here.
>>
>> >> +
>> >> +     /* Try to catch non platform devices. */
>> >> +     if ((pdev->name == NULL) || (req_pdev->name == NULL)) {
>> >> +             pr_err("OMAP-PM set_wakeup_lat: Error: platform devices
>> >> "
>> >> +                    "not valid\n");
>> >> +             return -EINVAL;
>> >> +     } else {
>> >> +             /* Call the omap_device API */
>> >> +             ret = omap_device_set_max_dev_wakeup_lat(req_pdev,
>> >> pdev, t);
>> >> +     }
>> >> +
>> >> +     return ret;
>> >> +}
>> >> +
>> >> +int omap_pm_set_max_mpu_wakeup_lat(struct device *req_dev,
>> long
>> >> t)
>> >> +{
>> >> +     if (!req_dev || t < -1) {
>> >> +             WARN(1, "OMAP PM: %s: invalid parameter(s)",
>> >> __func__);
>> >> +             return -EINVAL;
>> >>       };
>> >>
>> >>       if (t == -1)
>> >> -             pr_debug("OMAP PM: remove max device latency
>> >> constraint: "
>> >> -                      "dev %s\n", dev_name(dev));
>> >> +             pr_debug("OMAP PM: remove max MPU wakeup latency
>> >> constraint: "
>> >> +                      "dev %s\n", dev_name(req_dev));
>> >>       else
>> >> -             pr_debug("OMAP PM: add max device latency constraint: "
>> >> -                      "dev %s, t = %ld usec\n", dev_name(dev), t);
>> >> +             pr_debug("OMAP PM: add max MPU wakeup latency
>> >> constraint: "
>> >> +                      "dev %s, t = %ld usec\n", dev_name(req_dev),
> t);
>> >>
>> >> -     /*
>> >> -      * For current Linux, this needs to map the device to a
>> >> -      * powerdomain, then go through the list of current max lat
>> >> -      * constraints on that powerdomain and find the smallest.  If
>> >> -      * the latency constraint has changed, the code should
>> >> -      * recompute the state to enter for the next powerdomain
>> >> -      * state.  Conceivably, this code should also determine
>> >> -      * whether to actually disable the device clocks or not,
>> >> -      * depending on how long it takes to re-enable the clocks.
>> >> -      *
>> >> -      * TI CDP code can call constraint_set here.
>> >> -      */
>> >> +     omap_pm_set_max_dev_wakeup_lat(req_dev,
>> >> omap2_get_mpuss_device(), t);
>> >>
>> >>       return 0;
>> >>  }
>> >>
>> >> -int omap_pm_set_max_sdma_lat(struct device *dev, long t)
>> >> +int omap_pm_set_max_sdma_lat(struct device *req_dev, long t)
>> >>  {
>> >> -     if (!dev || t < -1) {
>> >> +     if (!req_dev || t < -1) {
>> >>               WARN(1, "OMAP PM: %s: invalid parameter(s)",
>> >> __func__);
>> >>               return -EINVAL;
>> >>       };
>> >>
>> >>       if (t == -1)
>> >>               pr_debug("OMAP PM: remove max DMA latency constraint:
>> >> "
>> >> -                      "dev %s\n", dev_name(dev));
>> >> +                      "dev %s\n", dev_name(req_dev));
>> >>       else
>> >>               pr_debug("OMAP PM: add max DMA latency constraint: "
>> >> -                      "dev %s, t = %ld usec\n", dev_name(dev), t);
>> >> -
>> >> -     /*
>> >> -      * For current Linux PM QOS params, this code should scan the
>> >> -      * list of maximum CPU and DMA latencies and select the
>> >> -      * smallest, then set cpu_dma_latency pm_qos_param
>> >> -      * accordingly.
>> >> -      *
>> >> -      * For future Linux PM QOS params, with separate CPU and DMA
>> >> -      * latency params, this code should just set the dma_latency
>> >> param.
>> >> -      *
>> >> -      * TI CDP code can call constraint_set here.
>> >> -      */
>> >> +                      "dev %s, t = %ld usec\n", dev_name(req_dev),
> t);
>> >>
>> >>       return 0;
>> >>  }
>> >> diff --git a/arch/arm/plat-omap/omap_device.c b/arch/arm/plat-
>> >> omap/omap_device.c
>> >> index 57adb27..1fe93d3 100644
>> >> --- a/arch/arm/plat-omap/omap_device.c
>> >> +++ b/arch/arm/plat-omap/omap_device.c
>> >> @@ -280,6 +280,34 @@ static void _add_optional_clock_alias(struct
>> >> omap_device *od,
>> >>  /* Public functions for use by core code */
>> >>
>> >>  /**
>> >> + * omap_device_set_max_dev_wakeup_lat - set/release a device
>> >> constraint
>> >> + * @od: struct omap_device *
>> >> + * @req_od: struct omap_device *
>> >> + *
>> >> + * Using the primary hwmod, set/release a device constraint for the
>> >> pdev
>> >> + * device, requested by the req_pdev device.
>> >> + *
>> >> + * If any hwmods exist for the omap_device assoiated with @pdev
>> and
>> >> @req_pdev,
>> >> + * set/release the constraint for the corresponding hwmods,
>> otherwise
>> >> return
>> >> + * -EINVAL.
>> >> + */
>> >> +int omap_device_set_max_dev_wakeup_lat(struct platform_device
>> >> *req_pdev,
>> >> +                                    struct platform_device *pdev,
> long
>> > t)
>> >> +{
>> >> +     struct omap_device *od, *req_od;
>> >> +     u32 ret = -EINVAL;
>> >> +
>> >> +     od = _find_by_pdev(pdev);
>> >> +     req_od = _find_by_pdev(req_pdev);
>> >> +
>> >> +     if ((od->hwmods_cnt) && (req_od->hwmods_cnt))
>> >> +             ret = omap_hwmod_set_max_dev_wakeup_lat(req_od-
>> >> >hwmods[0],
>> >> +                                                     od->hwmods[0],
> t);
>> >> +
>> >> +     return ret;
>> >> +}
>> >> +
>> >> +/**
>> >>   * omap_device_get_context_loss_count - get lost context count
>> >>   * @od: struct omap_device *
>> >>   *
>> > Vishwa
>> >> --
>> >> 1.7.2.3
>> >
>>
>> Thanks for the review, I will resubmit the patch asap.
>>
>> Regards,
>> Jean
>
--
To unsubscribe from this list: send the line "unsubscribe linux-omap" in
the body of a message to majordomo@vger.kernel.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html

^ permalink raw reply	[flat|nested] 19+ messages in thread

* RE: [PATCH 2/2] OMAP: PM: implement devices wakeup latency constraints APIs
  2011-02-10 19:23 ` [PATCH 2/2] OMAP: PM: implement devices wakeup latency constraints APIs jean.pihet
  2011-02-11 10:23   ` Gulati, Shweta
  2011-02-15  9:35   ` Vishwanath Sripathy
@ 2011-02-18 11:04   ` Rajendra Nayak
  2011-02-18 15:03     ` Jean Pihet
  2 siblings, 1 reply; 19+ messages in thread
From: Rajendra Nayak @ 2011-02-18 11:04 UTC (permalink / raw)
  To: jean.pihet, Kevin Hilman, paul, Vibhore Vardhan,
	Santosh Shilimkar, Vishwanath Sripathy
  Cc: linux-omap, Jean Pihet-XID

> -----Original Message-----
> From: jean.pihet@newoldbits.com [mailto:jean.pihet@newoldbits.com]
> Sent: Friday, February 11, 2011 12:53 AM
> To: khilman@ti.com; paul@pwsan.com; Vibhore Vardhan; Santosh Shilimkar;
Vishwanath BS; rnayak@ti.com
> Cc: linux-omap@vger.kernel.org; Jean Pihet; Vibhore Vardhan
> Subject: [PATCH 2/2] OMAP: PM: implement devices wakeup latency
constraints APIs
>
> From: Jean Pihet <j-pihet@ti.com>
>
> Implement OMAP PM layer omap_pm_set_max_dev_wakeup_lat API by
> creating similar APIs at the omap_device and omap_hwmod levels. The
> omap_hwmod level call is the layer with access to the powerdomain
> core, so it is the place where the powerdomain is queried to set and
> release the constraints.
>
> NOTE: only works for devices which have been converted to use
>       omap_device/omap_hwmod.
>
> Longer term, we could possibly remove this API from the OMAP PM layer,
> and instead directly use the omap_device level API.
>
> Based on Vibhore's original patch , adapted to omap_device and
> omap_hwmod frameworks.
>
> Signed-off-by: Jean Pihet <j-pihet@ti.com>
> Cc: Vibhore Vardhan <vvardhan@ti.com>
> ---
>  arch/arm/mach-omap2/omap_hwmod.c              |   62 +++++++++-
>  arch/arm/mach-omap2/powerdomain.c             |  176
++++++++++++++++++++++++-
>  arch/arm/mach-omap2/powerdomain.h             |   31 +++++
>  arch/arm/mach-omap2/powerdomains3xxx_data.c   |   60 +++++++++
>  arch/arm/plat-omap/include/plat/omap_device.h |    2 +
>  arch/arm/plat-omap/include/plat/omap_hwmod.h  |    2 +
>  arch/arm/plat-omap/omap-pm-constraints.c      |  101 ++++++---------
>  arch/arm/plat-omap/omap_device.c              |   28 ++++
>  8 files changed, 399 insertions(+), 63 deletions(-)
>
> diff --git a/arch/arm/mach-omap2/omap_hwmod.c
b/arch/arm/mach-omap2/omap_hwmod.c
> index e282e35..0dc096f 100644
> --- a/arch/arm/mach-omap2/omap_hwmod.c
> +++ b/arch/arm/mach-omap2/omap_hwmod.c
> @@ -142,6 +142,7 @@
>  #include "powerdomain.h"
>  #include <plat/clock.h>
>  #include <plat/omap_hwmod.h>
> +#include <plat/omap_device.h>
>  #include <plat/prcm.h>
>
>  #include "cm2xxx_3xxx.h"
> @@ -2198,10 +2199,69 @@ ohsps_unlock:
>  }
>
>  /**
> + * omap_hwmod_set_max_dev_wakeup_lat - set a device wake-up constraint
> + * @oh: struct omap_hwmod *
> + * @req_oh: struct omap_hwmod *

This does not seem to help understand what @oh and @req_oh are for.

> + * @t: wakeup latency constraint (us). -1 removes the existing
constraint
> + *
> + * Query the powerdomain of @oh to set/release the wake-up constraint
> + *
> + * Returns -EINVAL if @oh or @req_oh have no power domain, or the
return
> + * code from the pwrdm function
(pwrdm_wakeuplat_set/release_constraint)
> + * of the powerdomain assocated with @oh.
> + */
> +int omap_hwmod_set_max_dev_wakeup_lat(struct omap_hwmod *req_oh,
> +				      struct omap_hwmod *oh, long t)
> +{
> +	struct device *req_dev;
> +	struct platform_device *pdev;
> +	struct powerdomain *pwrdm;
> +	int ret = 0;
> +
> +	pwrdm = omap_hwmod_get_pwrdm(oh);
> +
> +	/* Catch devices with undefined powerdomains */
> +	if (!pwrdm) {
> +		pr_err("omap_hwmod: Error: could not find parent "
> +			"powerdomain for %s\n", oh->name);
> +		return -EINVAL;
> +	}
> +
> +	pdev = &(req_oh->od->pdev);
> +	if (pdev == NULL) {
> +		pr_err("omap_hwmod: Error: pdev not found for oh %s\n",
> +		       oh->name);
> +		return -EINVAL;
> +	}
> +
> +	req_dev = &(pdev->dev);
> +	if (req_dev == NULL) {
> +		pr_err("omap_hwmod: Error: device not found for oh %s\n",
> +		       oh->name);
> +		return -EINVAL;
> +	}
> +
> +	/* Call set/release_constraint for the given pwrdm */
> +	if (t == -1) {
> +		pr_debug("omap_hwmod: remove max device latency
constraint: "
> +			 "oh %s, pwrdm %s, req by oh %s\n",
> +			 oh->name, pwrdm->name, req_oh->name);
> +		ret = pwrdm_wakeuplat_release_constraint(pwrdm, req_dev);
> +	} else {
> +		pr_debug("omap_hwmod: add max device latency constraint: "
> +			 "oh %s, t = %ld usec, pwrdm %s, req by oh %s\n",
> +			 oh->name, t, pwrdm->name, req_oh->name);
> +		ret = pwrdm_wakeuplat_set_constraint(pwrdm, req_dev, t);
> +	}
> +
> +	return 0;
> +}
> +
> +/**
>   * omap_hwmod_get_context_loss_count - get lost context count
>   * @oh: struct omap_hwmod *
>   *
> - * Query the powerdomain of of @oh to get the context loss
> + * Query the powerdomain of @oh to get the context loss

Stray change.

>   * count for this device.
>   *
>   * Returns the context loss count of the powerdomain assocated with @oh
> diff --git a/arch/arm/mach-omap2/powerdomain.c
b/arch/arm/mach-omap2/powerdomain.c
> index eaed0df..3ed3bea 100644
> --- a/arch/arm/mach-omap2/powerdomain.c
> +++ b/arch/arm/mach-omap2/powerdomain.c
> @@ -19,16 +19,19 @@
>  #include <linux/list.h>
>  #include <linux/errno.h>
>  #include <linux/string.h>
> +#include <linux/slab.h>
> +
> +#include <plat/cpu.h>
> +#include <plat/prcm.h>
> +
>  #include "cm2xxx_3xxx.h"
>  #include "prcm44xx.h"
>  #include "cm44xx.h"
>  #include "prm2xxx_3xxx.h"
>  #include "prm44xx.h"
>
> -#include <plat/cpu.h>
>  #include "powerdomain.h"
>  #include "clockdomain.h"
> -#include <plat/prcm.h>
>
>  #include "pm.h"
>
> @@ -103,6 +106,13 @@ static int _pwrdm_register(struct powerdomain
*pwrdm)
>  	pwrdm->state = pwrdm_read_pwrst(pwrdm);
>  	pwrdm->state_counter[pwrdm->state] = 1;
>
> +	/* Initialize priority ordered list for wakeup latency constraint
*/
> +	spin_lock_init(&pwrdm->wakeuplat_lock);
> +	plist_head_init(&pwrdm->wakeuplat_dev_list,
&pwrdm->wakeuplat_lock);
> +
> +	/* res_mutex protects res_list add and del ops */
> +	mutex_init(&pwrdm->wakeuplat_mutex);
> +
>  	pr_debug("powerdomain: registered %s\n", pwrdm->name);
>
>  	return 0;
> @@ -176,6 +186,62 @@ static int _pwrdm_post_transition_cb(struct
powerdomain *pwrdm, void *unused)
>  	return 0;
>  }
>
> +/**
> + * pwrdm_wakeuplat_update_pwrst - Update power domain power state if
needed
> + * @pwrdm: struct powerdomain * to which requesting device belongs to
> + *
> + * Finds minimum latency value from all entries in the list and
> + * the power domain power state needing the constraint. Programs
> + * new state if it is different from current power state.
> + */
> +static void pwrdm_wakeuplat_update_pwrst(struct powerdomain *pwrdm)
> +{
> +	struct plist_node *node;
> +	int new_state;
> +	unsigned long min_latency = -1;
> +
> +	if (!plist_head_empty(&pwrdm->wakeuplat_dev_list)) {
> +		node = plist_last(&pwrdm->wakeuplat_dev_list);
> +		min_latency = node->prio;
> +	}
> +
> +	/* Find power state with wakeup latency < minimum constraint. */
> +	for (new_state = 0x0; new_state < PWRDM_MAX_PWRSTS; new_state++) {
> +		if (min_latency == -1 ||
> +		    pwrdm->wakeup_lat[new_state] <= min_latency)
> +			break;
> +	}
> +
> +	switch (new_state) {
> +	case PWRDM_FUNC_PWRST_OFF:
> +		new_state = PWRDM_POWER_OFF;
> +		break;
> +	case PWRDM_FUNC_PWRST_OSWR:
> +		pwrdm_set_logic_retst(pwrdm, PWRDM_POWER_OFF);
> +	case PWRDM_FUNC_PWRST_CSWR:
> +		new_state = PWRDM_POWER_RET;
> +		break;
> +	case PWRDM_FUNC_PWRST_ON:
> +		new_state = PWRDM_POWER_ON;
> +		break;
> +	default:
> +		pr_warn("powerdomain: requested latency constraint not "
> +			"supported %s set to ON state\n", pwrdm->name);
> +		new_state = PWRDM_POWER_ON;
> +		break;
> +	}
> +
> +	if (pwrdm_read_pwrst(pwrdm) != new_state) {
> +		if (cpu_is_omap34xx())
> +			omap_set_pwrdm_state(pwrdm, new_state);
> +	}
> +
> +	pr_debug("powerdomain: %s pwrst: curr= %d, prev= %d next= %d "
> +		 "wkuplat_min= %lu, set_state= %d\n", pwrdm->name,
> +		 pwrdm_read_pwrst(pwrdm), pwrdm_read_prev_pwrst(pwrdm),
> +		 pwrdm_read_next_pwrst(pwrdm), min_latency, new_state);
> +}
> +
>  /* Public functions */
>
>  /**
> @@ -911,6 +977,112 @@ int pwrdm_post_transition(void)
>  }
>
>  /**
> + * pwrdm_wakeuplat_set_constraint - Set powerdomain wakeup latency
constraint
> + * @pwrdm: struct powerdomain * to which requesting device belongs to
> + * @dev: struct device * of requesting device
> + * @t: wakeup latency constraint in microseconds
> + *
> + * Adds new entry to powerdomain's wakeup latency constraint list.
> + * If the requesting device already exists in the list, old value is
> + * overwritten. Checks whether current power state is still adequate.
> + * Returns -EINVAL if the powerdomain or device pointer is NULL,
> + * or -ENOMEM if kmalloc fails, or returns 0 upon success.
> + */
> +int pwrdm_wakeuplat_set_constraint(struct powerdomain *pwrdm,
> +				   struct device *req_dev, unsigned long
t)
> +{
> +	struct  wakeuplat_dev_list *user;
> +	int found = 0, ret = 0;
> +
> +	if (!pwrdm || !req_dev) {
> +		WARN(1, "powerdomain: %s: invalid parameter(s)",
__func__);
> +		return -EINVAL;
> +	}
> +
> +	mutex_lock(&pwrdm->wakeuplat_mutex);
> +
> +	plist_for_each_entry(user, &pwrdm->wakeuplat_dev_list, node) {
> +		if (user->dev == req_dev) {
> +			found = 1;
> +			break;
> +		}
> +	}
> +
> +	/* Add new entry to the list or update existing request */
> +	if (found && user->constraint_us == t) {
> +		goto exit_set;
> +	} else if (!found) {
> +		user = kzalloc(sizeof(struct wakeuplat_dev_list),
GFP_KERNEL);
> +		if (!user) {
> +			pr_err("powerdomain: FATAL ERROR: kzalloc
failed\n");
> +			ret = -ENOMEM;
> +			goto exit_set;
> +		}
> +		user->dev = req_dev;
> +	} else {
> +		plist_del(&user->node, &pwrdm->wakeuplat_dev_list);
> +	}
> +
> +	plist_node_init(&user->node, t);
> +	plist_add(&user->node, &pwrdm->wakeuplat_dev_list);
> +	user->node.prio = user->constraint_us = t;
> +
> +	pwrdm_wakeuplat_update_pwrst(pwrdm);
> +
> +exit_set:
> +	mutex_unlock(&pwrdm->wakeuplat_mutex);
> +
> +	return ret;
> +}
> +
> +/**
> + * pwrdm_wakeuplat_release_constraint - Release powerdomain wkuplat
constraint
> + * @pwrdm: struct powerdomain * to which requesting device belongs to
> + * @req_dev: struct device * of requesting device
> + *
> + * Removes device's entry from powerdomain's wakeup latency constraint
list.
> + * Checks whether current power state is still adequate.
> + * Returns -EINVAL if the powerdomain or device pointer is NULL or
> + * no such entry exists in the list, or returns 0 upon success.
> + */
> +int pwrdm_wakeuplat_release_constraint(struct powerdomain *pwrdm,
> +				       struct device *req_dev)
> +{
> +	struct wakeuplat_dev_list *user;
> +	int found = 0, ret = 0;
> +
> +	if (!pwrdm || !req_dev) {
> +		WARN(1, "powerdomain: %s: invalid parameter(s)",
__func__);
> +		return -EINVAL;
> +	}
> +
> +	mutex_lock(&pwrdm->wakeuplat_mutex);
> +
> +	plist_for_each_entry(user, &pwrdm->wakeuplat_dev_list, node) {
> +		if (user->dev == req_dev) {
> +			found = 1;
> +			break;
> +		}
> +	}
> +
> +	if (!found) {
> +		pr_err("OMAP PM: Error: no prior constraint to
release\n");
> +		ret = -EINVAL;
> +		goto exit_rls;
> +	}
> +
> +	plist_del(&user->node, &pwrdm->wakeuplat_dev_list);
> +	kfree(user);
> +
> +	pwrdm_wakeuplat_update_pwrst(pwrdm);
> +
> +exit_rls:
> +	mutex_unlock(&pwrdm->wakeuplat_mutex);
> +
> +	return ret;
> +}
> +
> +/**
>   * pwrdm_get_context_loss_count - get powerdomain's context loss count
>   * @pwrdm: struct powerdomain * to wait for
>   *
> diff --git a/arch/arm/mach-omap2/powerdomain.h
b/arch/arm/mach-omap2/powerdomain.h
> index c66431e..d4189d8 100644
> --- a/arch/arm/mach-omap2/powerdomain.h
> +++ b/arch/arm/mach-omap2/powerdomain.h
> @@ -19,6 +19,9 @@
>
>  #include <linux/types.h>
>  #include <linux/list.h>
> +#include <linux/plist.h>
> +#include <linux/mutex.h>
> +#include <linux/spinlock.h>
>
>  #include <linux/atomic.h>
>
> @@ -46,6 +49,15 @@
>
>  #define PWRSTS_OFF_RET_ON	(PWRSTS_OFF_RET | (1 << PWRDM_POWER_ON))
>
> +/* Powerdomain functional power states */
> +#define PWRDM_FUNC_PWRST_OFF	0x0
> +#define PWRDM_FUNC_PWRST_OSWR	0x1
> +#define PWRDM_FUNC_PWRST_CSWR	0x2
> +#define PWRDM_FUNC_PWRST_ON	0x3
> +
> +#define PWRDM_MAX_FUNC_PWRSTS	4
> +
> +#define UNSUP_STATE		-1
>
>  /* Powerdomain flags */
>  #define PWRDM_HAS_HDWR_SAR	(1 << 0) /* hardware save-and-restore
support */
> @@ -96,6 +108,10 @@ struct powerdomain;
>   * @state_counter:
>   * @timer:
>   * @state_timer:
> + * @wakeup_lat: Wakeup latencies for possible powerdomain power states
> + * @wakeuplat_lock: spinlock for plist
> + * @wakeuplat_dev_list: plist_head linking all devices placing
constraint
> + * @wakeuplat_mutex: mutex to protect per powerdomain list ops
>   *
>   * @prcm_partition possible values are defined in
mach-omap2/prcm44xx.h.
>   */
> @@ -121,6 +137,16 @@ struct powerdomain {
>  	s64 timer;
>  	s64 state_timer[PWRDM_MAX_PWRSTS];
>  #endif
> +	const u32 wakeup_lat[PWRDM_MAX_FUNC_PWRSTS];
> +	spinlock_t wakeuplat_lock;
> +	struct plist_head wakeuplat_dev_list;
> +	struct mutex wakeuplat_mutex;
> +};
> +
> +struct wakeuplat_dev_list {
> +	struct device *dev;
> +	unsigned long constraint_us;
> +	struct plist_node node;
>  };
>
>  /**
> @@ -211,6 +237,11 @@ int pwrdm_clkdm_state_switch(struct clockdomain
*clkdm);
>  int pwrdm_pre_transition(void);
>  int pwrdm_post_transition(void);
>  int pwrdm_set_lowpwrstchange(struct powerdomain *pwrdm);
> +
> +int pwrdm_wakeuplat_set_constraint(struct powerdomain *pwrdm,
> +				   struct device *dev, unsigned long t);
> +int pwrdm_wakeuplat_release_constraint(struct powerdomain *pwrdm,
> +				       struct device *dev);
>  u32 pwrdm_get_context_loss_count(struct powerdomain *pwrdm);
>
>  extern void omap2xxx_powerdomains_init(void);
> diff --git a/arch/arm/mach-omap2/powerdomains3xxx_data.c
b/arch/arm/mach-omap2/powerdomains3xxx_data.c
> index e1bec56..4f7e44d 100644
> --- a/arch/arm/mach-omap2/powerdomains3xxx_data.c
> +++ b/arch/arm/mach-omap2/powerdomains3xxx_data.c
> @@ -52,6 +52,12 @@ static struct powerdomain iva2_pwrdm = {
>  		[2] = PWRSTS_OFF_ON,
>  		[3] = PWRDM_POWER_ON,
>  	},
> +	.wakeup_lat = {
> +		[PWRDM_FUNC_PWRST_OFF] = 1100,
> +		[PWRDM_FUNC_PWRST_OSWR] = UNSUP_STATE,
> +		[PWRDM_FUNC_PWRST_CSWR] = 350,
> +		[PWRDM_FUNC_PWRST_ON] = 0,
> +	},
>  };
>
>  static struct powerdomain mpu_3xxx_pwrdm = {
> @@ -68,6 +74,12 @@ static struct powerdomain mpu_3xxx_pwrdm = {
>  	.pwrsts_mem_on	  = {
>  		[0] = PWRSTS_OFF_ON,
>  	},
> +	.wakeup_lat = {
> +		[PWRDM_FUNC_PWRST_OFF] = 95,
> +		[PWRDM_FUNC_PWRST_OSWR] = UNSUP_STATE,
> +		[PWRDM_FUNC_PWRST_CSWR] = 45,
> +		[PWRDM_FUNC_PWRST_ON] = 0,
> +	},
>  };
>
>  /*
> @@ -98,6 +110,12 @@ static struct powerdomain core_3xxx_pre_es3_1_pwrdm
= {
>  		[0] = PWRSTS_OFF_RET_ON, /* MEM1ONSTATE */
>  		[1] = PWRSTS_OFF_RET_ON, /* MEM2ONSTATE */
>  	},
> +	.wakeup_lat = {
> +		[PWRDM_FUNC_PWRST_OFF] = 100,
> +		[PWRDM_FUNC_PWRST_OSWR] = UNSUP_STATE,
> +		[PWRDM_FUNC_PWRST_CSWR] = 60,
> +		[PWRDM_FUNC_PWRST_ON] = 0,
> +	},
>  };
>
>  static struct powerdomain core_3xxx_es3_1_pwrdm = {
> @@ -121,6 +139,12 @@ static struct powerdomain core_3xxx_es3_1_pwrdm = {
>  		[0] = PWRSTS_OFF_RET_ON, /* MEM1ONSTATE */
>  		[1] = PWRSTS_OFF_RET_ON, /* MEM2ONSTATE */
>  	},
> +	.wakeup_lat = {
> +		[PWRDM_FUNC_PWRST_OFF] = 100,
> +		[PWRDM_FUNC_PWRST_OSWR] = UNSUP_STATE,
> +		[PWRDM_FUNC_PWRST_CSWR] = 60,
> +		[PWRDM_FUNC_PWRST_ON] = 0,
> +	},
>  };
>
>  static struct powerdomain dss_pwrdm = {
> @@ -136,6 +160,12 @@ static struct powerdomain dss_pwrdm = {
>  	.pwrsts_mem_on	  = {
>  		[0] = PWRDM_POWER_ON,  /* MEMONSTATE */
>  	},
> +	.wakeup_lat = {
> +		[PWRDM_FUNC_PWRST_OFF] = 70,
> +		[PWRDM_FUNC_PWRST_OSWR] = UNSUP_STATE,
> +		[PWRDM_FUNC_PWRST_CSWR] = 20,
> +		[PWRDM_FUNC_PWRST_ON] = 0,
> +	},
>  };
>
>  /*
> @@ -157,6 +187,12 @@ static struct powerdomain sgx_pwrdm = {
>  	.pwrsts_mem_on	  = {
>  		[0] = PWRDM_POWER_ON,  /* MEMONSTATE */
>  	},
> +	.wakeup_lat = {
> +		[PWRDM_FUNC_PWRST_OFF] = 1000,
> +		[PWRDM_FUNC_PWRST_OSWR] = UNSUP_STATE,
> +		[PWRDM_FUNC_PWRST_CSWR] = UNSUP_STATE,
> +		[PWRDM_FUNC_PWRST_ON] = 0,
> +	},
>  };
>
>  static struct powerdomain cam_pwrdm = {
> @@ -172,6 +208,12 @@ static struct powerdomain cam_pwrdm = {
>  	.pwrsts_mem_on	  = {
>  		[0] = PWRDM_POWER_ON,  /* MEMONSTATE */
>  	},
> +	.wakeup_lat = {
> +		[PWRDM_FUNC_PWRST_OFF] = 850,
> +		[PWRDM_FUNC_PWRST_OSWR] = UNSUP_STATE,
> +		[PWRDM_FUNC_PWRST_CSWR] = 35,
> +		[PWRDM_FUNC_PWRST_ON] = 0,
> +	},
>  };
>
>  static struct powerdomain per_pwrdm = {
> @@ -187,6 +229,12 @@ static struct powerdomain per_pwrdm = {
>  	.pwrsts_mem_on	  = {
>  		[0] = PWRDM_POWER_ON,  /* MEMONSTATE */
>  	},
> +	.wakeup_lat = {
> +		[PWRDM_FUNC_PWRST_OFF] = 200,
> +		[PWRDM_FUNC_PWRST_OSWR] = UNSUP_STATE,
> +		[PWRDM_FUNC_PWRST_CSWR] = 110,
> +		[PWRDM_FUNC_PWRST_ON] = 0,
> +	},
>  };
>
>  static struct powerdomain emu_pwrdm = {
> @@ -201,6 +249,12 @@ static struct powerdomain neon_pwrdm = {
>  	.omap_chip	  = OMAP_CHIP_INIT(CHIP_IS_OMAP3430),
>  	.pwrsts		  = PWRSTS_OFF_RET_ON,
>  	.pwrsts_logic_ret = PWRDM_POWER_RET,
> +	.wakeup_lat = {
> +		[PWRDM_FUNC_PWRST_OFF] = 200,
> +		[PWRDM_FUNC_PWRST_OSWR] = UNSUP_STATE,
> +		[PWRDM_FUNC_PWRST_CSWR] = 35,
> +		[PWRDM_FUNC_PWRST_ON] = 0,
> +	},
>  };
>
>  static struct powerdomain usbhost_pwrdm = {
> @@ -223,6 +277,12 @@ static struct powerdomain usbhost_pwrdm = {
>  	.pwrsts_mem_on	  = {
>  		[0] = PWRDM_POWER_ON,  /* MEMONSTATE */
>  	},
> +	.wakeup_lat = {
> +		[PWRDM_FUNC_PWRST_OFF] = 800,
> +		[PWRDM_FUNC_PWRST_OSWR] = UNSUP_STATE,
> +		[PWRDM_FUNC_PWRST_CSWR] = 150,
> +		[PWRDM_FUNC_PWRST_ON] = 0,
> +	},
>  };
>
>  static struct powerdomain dpll1_pwrdm = {
> diff --git a/arch/arm/plat-omap/include/plat/omap_device.h
b/arch/arm/plat-omap/include/plat/omap_device.h
> index e4c349f..5da6b47 100644
> --- a/arch/arm/plat-omap/include/plat/omap_device.h
> +++ b/arch/arm/plat-omap/include/plat/omap_device.h
> @@ -107,6 +107,8 @@ void __iomem *omap_device_get_rt_va(struct
omap_device *od);
>  int omap_device_align_pm_lat(struct platform_device *pdev,
>  			     u32 new_wakeup_lat_limit);
>  struct powerdomain *omap_device_get_pwrdm(struct omap_device *od);
> +int omap_device_set_max_dev_wakeup_lat(struct platform_device
*req_pdev,
> +				       struct platform_device *pdev, long
t);
>  u32 omap_device_get_context_loss_count(struct platform_device *pdev);
>
>  /* Other */
> diff --git a/arch/arm/plat-omap/include/plat/omap_hwmod.h
b/arch/arm/plat-omap/include/plat/omap_hwmod.h
> index 1eee85a..0fbb974 100644
> --- a/arch/arm/plat-omap/include/plat/omap_hwmod.h
> +++ b/arch/arm/plat-omap/include/plat/omap_hwmod.h
> @@ -587,6 +587,8 @@ int omap_hwmod_for_each_by_class(const char
*classname,
>  				 void *user);
>
>  int omap_hwmod_set_postsetup_state(struct omap_hwmod *oh, u8 state);
> +int omap_hwmod_set_max_dev_wakeup_lat(struct omap_hwmod *req_oh,
> +				      struct omap_hwmod *oh, long t);
>  u32 omap_hwmod_get_context_loss_count(struct omap_hwmod *oh);
>
>  /*
> diff --git a/arch/arm/plat-omap/omap-pm-constraints.c
b/arch/arm/plat-omap/omap-pm-constraints.c
> index c8b4e4c..7ae855f 100644
> --- a/arch/arm/plat-omap/omap-pm-constraints.c
> +++ b/arch/arm/plat-omap/omap-pm-constraints.c
> @@ -24,6 +24,7 @@
>  /* Interface documentation is in mach/omap-pm.h */
>  #include <plat/omap-pm.h>
>  #include <plat/omap_device.h>
> +#include <plat/common.h>
>
>  static bool off_mode_enabled;
>  static u32 dummy_context_loss_counter;
> @@ -32,34 +33,6 @@ static u32 dummy_context_loss_counter;
>   * Device-driver-originated constraints (via board-*.c files)
>   */
>
> -int omap_pm_set_max_mpu_wakeup_lat(struct device *dev, long t)
> -{
> -	if (!dev || t < -1) {
> -		WARN(1, "OMAP PM: %s: invalid parameter(s)", __func__);
> -		return -EINVAL;
> -	};
> -
> -	if (t == -1)
> -		pr_debug("OMAP PM: remove max MPU wakeup latency
constraint: "
> -			 "dev %s\n", dev_name(dev));
> -	else
> -		pr_debug("OMAP PM: add max MPU wakeup latency constraint:
"
> -			 "dev %s, t = %ld usec\n", dev_name(dev), t);
> -
> -	/*
> -	 * For current Linux, this needs to map the MPU to a
> -	 * powerdomain, then go through the list of current max lat
> -	 * constraints on the MPU and find the smallest.  If
> -	 * the latency constraint has changed, the code should
> -	 * recompute the state to enter for the next powerdomain
> -	 * state.
> -	 *
> -	 * TI CDP code can call constraint_set here.
> -	 */
> -
> -	return 0;
> -}
> -
>  int omap_pm_set_min_bus_tput(struct device *dev, u8 agent_id, unsigned
long r)
>  {
>  	if (!dev || (agent_id != OCP_INITIATOR_AGENT &&
> @@ -87,62 +60,70 @@ int omap_pm_set_min_bus_tput(struct device *dev, u8
agent_id, unsigned long r)
>  	return 0;
>  }
>
> +/*
> + * omap_pm_set_max_dev_wakeup_lat - set/release devices wake-up latency
> + * constraints
> + */
>  int omap_pm_set_max_dev_wakeup_lat(struct device *req_dev, struct
device *dev,
>  				   long t)

I seem to have a fundamental problem understanding the flow,
starting with what these 2 struct devices are used to identify?
Can you help explain with some example what these would be,
say incase an MMC driver wants to put a constraint?

Besides, the struct device passed in the top level api gets
converted into platform_device and then into omap_device
followed by a omap_hwmod and then its converted back into
platform_device using the omap_device pointer in the
omap_hwmod struct. Something just dosen't seem right.

Regards,
Rajendra

>  {
> +	struct platform_device *pdev, *req_pdev;
> +	int ret = 0;
> +
>  	if (!req_dev || !dev || t < -1) {
>  		WARN(1, "OMAP PM: %s: invalid parameter(s)", __func__);
>  		return -EINVAL;
> +	}
> +
> +	/* Look for the platform devices */
> +	pdev = container_of(dev, struct platform_device, dev);
> +	req_pdev = container_of(req_dev, struct platform_device, dev);
> +
> +	/* Try to catch non platform devices. */
> +	if ((pdev->name == NULL) || (req_pdev->name == NULL)) {
> +		pr_err("OMAP-PM set_wakeup_lat: Error: platform devices "
> +		       "not valid\n");
> +		return -EINVAL;
> +	} else {
> +		/* Call the omap_device API */
> +		ret = omap_device_set_max_dev_wakeup_lat(req_pdev, pdev,
t);
> +	}
> +
> +	return ret;
> +}
> +
> +int omap_pm_set_max_mpu_wakeup_lat(struct device *req_dev, long t)
> +{
> +	if (!req_dev || t < -1) {
> +		WARN(1, "OMAP PM: %s: invalid parameter(s)", __func__);
> +		return -EINVAL;
>  	};
>
>  	if (t == -1)
> -		pr_debug("OMAP PM: remove max device latency constraint: "
> -			 "dev %s\n", dev_name(dev));
> +		pr_debug("OMAP PM: remove max MPU wakeup latency
constraint: "
> +			 "dev %s\n", dev_name(req_dev));
>  	else
> -		pr_debug("OMAP PM: add max device latency constraint: "
> -			 "dev %s, t = %ld usec\n", dev_name(dev), t);
> +		pr_debug("OMAP PM: add max MPU wakeup latency constraint:
"
> +			 "dev %s, t = %ld usec\n", dev_name(req_dev), t);
>
> -	/*
> -	 * For current Linux, this needs to map the device to a
> -	 * powerdomain, then go through the list of current max lat
> -	 * constraints on that powerdomain and find the smallest.  If
> -	 * the latency constraint has changed, the code should
> -	 * recompute the state to enter for the next powerdomain
> -	 * state.  Conceivably, this code should also determine
> -	 * whether to actually disable the device clocks or not,
> -	 * depending on how long it takes to re-enable the clocks.
> -	 *
> -	 * TI CDP code can call constraint_set here.
> -	 */
> +	omap_pm_set_max_dev_wakeup_lat(req_dev, omap2_get_mpuss_device(),
t);
>
>  	return 0;
>  }
>
> -int omap_pm_set_max_sdma_lat(struct device *dev, long t)
> +int omap_pm_set_max_sdma_lat(struct device *req_dev, long t)
>  {
> -	if (!dev || t < -1) {
> +	if (!req_dev || t < -1) {
>  		WARN(1, "OMAP PM: %s: invalid parameter(s)", __func__);
>  		return -EINVAL;
>  	};
>
>  	if (t == -1)
>  		pr_debug("OMAP PM: remove max DMA latency constraint: "
> -			 "dev %s\n", dev_name(dev));
> +			 "dev %s\n", dev_name(req_dev));
>  	else
>  		pr_debug("OMAP PM: add max DMA latency constraint: "
> -			 "dev %s, t = %ld usec\n", dev_name(dev), t);
> -
> -	/*
> -	 * For current Linux PM QOS params, this code should scan the
> -	 * list of maximum CPU and DMA latencies and select the
> -	 * smallest, then set cpu_dma_latency pm_qos_param
> -	 * accordingly.
> -	 *
> -	 * For future Linux PM QOS params, with separate CPU and DMA
> -	 * latency params, this code should just set the dma_latency
param.
> -	 *
> -	 * TI CDP code can call constraint_set here.
> -	 */
> +			 "dev %s, t = %ld usec\n", dev_name(req_dev), t);
>
>  	return 0;
>  }
> diff --git a/arch/arm/plat-omap/omap_device.c
b/arch/arm/plat-omap/omap_device.c
> index 57adb27..1fe93d3 100644
> --- a/arch/arm/plat-omap/omap_device.c
> +++ b/arch/arm/plat-omap/omap_device.c
> @@ -280,6 +280,34 @@ static void _add_optional_clock_alias(struct
omap_device *od,
>  /* Public functions for use by core code */
>
>  /**
> + * omap_device_set_max_dev_wakeup_lat - set/release a device constraint
> + * @od: struct omap_device *
> + * @req_od: struct omap_device *
> + *
> + * Using the primary hwmod, set/release a device constraint for the
pdev
> + * device, requested by the req_pdev device.
> + *
> + * If any hwmods exist for the omap_device assoiated with @pdev and
@req_pdev,
> + * set/release the constraint for the corresponding hwmods, otherwise
return
> + * -EINVAL.
> + */
> +int omap_device_set_max_dev_wakeup_lat(struct platform_device
*req_pdev,
> +				       struct platform_device *pdev, long
t)
> +{
> +	struct omap_device *od, *req_od;
> +	u32 ret = -EINVAL;
> +
> +	od = _find_by_pdev(pdev);
> +	req_od = _find_by_pdev(req_pdev);
> +
> +	if ((od->hwmods_cnt) && (req_od->hwmods_cnt))
> +		ret = omap_hwmod_set_max_dev_wakeup_lat(req_od->hwmods[0],
> +							od->hwmods[0], t);
> +
> +	return ret;
> +}
> +
> +/**
>   * omap_device_get_context_loss_count - get lost context count
>   * @od: struct omap_device *
>   *
> --
> 1.7.2.3

^ permalink raw reply	[flat|nested] 19+ messages in thread

* Re: [PATCH 2/2] OMAP: PM: implement devices wakeup latency constraints APIs
  2011-02-18 11:04   ` Rajendra Nayak
@ 2011-02-18 15:03     ` Jean Pihet
  0 siblings, 0 replies; 19+ messages in thread
From: Jean Pihet @ 2011-02-18 15:03 UTC (permalink / raw)
  To: Rajendra Nayak
  Cc: Kevin Hilman, paul, Vibhore Vardhan, Santosh Shilimkar,
	Vishwanath Sripathy, linux-omap, Jean Pihet-XID

Hi Rajendra,

The patch below is not the latest one. I sent the latest patch set
attached to the offlist e-mail I sent yesterday. As soon as the
architecture discussions are sorted out I will re-submit.

Some more comments inline.

On Fri, Feb 18, 2011 at 12:04 PM, Rajendra Nayak <rnayak@ti.com> wrote:
>> -----Original Message-----
>> From: jean.pihet@newoldbits.com [mailto:jean.pihet@newoldbits.com]
>> Sent: Friday, February 11, 2011 12:53 AM
>> To: khilman@ti.com; paul@pwsan.com; Vibhore Vardhan; Santosh Shilimkar;
> Vishwanath BS; rnayak@ti.com
>> Cc: linux-omap@vger.kernel.org; Jean Pihet; Vibhore Vardhan
>> Subject: [PATCH 2/2] OMAP: PM: implement devices wakeup latency
> constraints APIs
>>
>> From: Jean Pihet <j-pihet@ti.com>
>>
>> Implement OMAP PM layer omap_pm_set_max_dev_wakeup_lat API by
>> creating similar APIs at the omap_device and omap_hwmod levels. The
>> omap_hwmod level call is the layer with access to the powerdomain
>> core, so it is the place where the powerdomain is queried to set and
>> release the constraints.
>>
>> NOTE: only works for devices which have been converted to use
>>       omap_device/omap_hwmod.
>>
>> Longer term, we could possibly remove this API from the OMAP PM layer,
>> and instead directly use the omap_device level API.
>>
>> Based on Vibhore's original patch , adapted to omap_device and
>> omap_hwmod frameworks.
>>
>> Signed-off-by: Jean Pihet <j-pihet@ti.com>
>> Cc: Vibhore Vardhan <vvardhan@ti.com>
>> ---
>>  arch/arm/mach-omap2/omap_hwmod.c              |   62 +++++++++-
>>  arch/arm/mach-omap2/powerdomain.c             |  176
> ++++++++++++++++++++++++-
>>  arch/arm/mach-omap2/powerdomain.h             |   31 +++++
>>  arch/arm/mach-omap2/powerdomains3xxx_data.c   |   60 +++++++++
>>  arch/arm/plat-omap/include/plat/omap_device.h |    2 +
>>  arch/arm/plat-omap/include/plat/omap_hwmod.h  |    2 +
>>  arch/arm/plat-omap/omap-pm-constraints.c      |  101 ++++++---------
>>  arch/arm/plat-omap/omap_device.c              |   28 ++++
>>  8 files changed, 399 insertions(+), 63 deletions(-)
>>
>> diff --git a/arch/arm/mach-omap2/omap_hwmod.c
> b/arch/arm/mach-omap2/omap_hwmod.c
>> index e282e35..0dc096f 100644
>> --- a/arch/arm/mach-omap2/omap_hwmod.c
>> +++ b/arch/arm/mach-omap2/omap_hwmod.c
>> @@ -142,6 +142,7 @@
>>  #include "powerdomain.h"
>>  #include <plat/clock.h>
>>  #include <plat/omap_hwmod.h>
>> +#include <plat/omap_device.h>
>>  #include <plat/prcm.h>
>>
>>  #include "cm2xxx_3xxx.h"
>> @@ -2198,10 +2199,69 @@ ohsps_unlock:
>>  }
>>
>>  /**
>> + * omap_hwmod_set_max_dev_wakeup_lat - set a device wake-up constraint
>> + * @oh: struct omap_hwmod *
>> + * @req_oh: struct omap_hwmod *
>
> This does not seem to help understand what @oh and @req_oh are for.
Corrected in the latest version

>
>> + * @t: wakeup latency constraint (us). -1 removes the existing
> constraint
>> + *
>> + * Query the powerdomain of @oh to set/release the wake-up constraint
>> + *
>> + * Returns -EINVAL if @oh or @req_oh have no power domain, or the
> return
>> + * code from the pwrdm function
> (pwrdm_wakeuplat_set/release_constraint)
>> + * of the powerdomain assocated with @oh.
>> + */
>> +int omap_hwmod_set_max_dev_wakeup_lat(struct omap_hwmod *req_oh,
>> +                                   struct omap_hwmod *oh, long t)
>> +{
>> +     struct device *req_dev;
>> +     struct platform_device *pdev;
>> +     struct powerdomain *pwrdm;
>> +     int ret = 0;
>> +
>> +     pwrdm = omap_hwmod_get_pwrdm(oh);
>> +
>> +     /* Catch devices with undefined powerdomains */
>> +     if (!pwrdm) {
>> +             pr_err("omap_hwmod: Error: could not find parent "
>> +                     "powerdomain for %s\n", oh->name);
>> +             return -EINVAL;
>> +     }
>> +
>> +     pdev = &(req_oh->od->pdev);
>> +     if (pdev == NULL) {
>> +             pr_err("omap_hwmod: Error: pdev not found for oh %s\n",
>> +                    oh->name);
>> +             return -EINVAL;
>> +     }
>> +
>> +     req_dev = &(pdev->dev);
>> +     if (req_dev == NULL) {
>> +             pr_err("omap_hwmod: Error: device not found for oh %s\n",
>> +                    oh->name);
>> +             return -EINVAL;
>> +     }
>> +
>> +     /* Call set/release_constraint for the given pwrdm */
>> +     if (t == -1) {
>> +             pr_debug("omap_hwmod: remove max device latency
> constraint: "
>> +                      "oh %s, pwrdm %s, req by oh %s\n",
>> +                      oh->name, pwrdm->name, req_oh->name);
>> +             ret = pwrdm_wakeuplat_release_constraint(pwrdm, req_dev);
>> +     } else {
>> +             pr_debug("omap_hwmod: add max device latency constraint: "
>> +                      "oh %s, t = %ld usec, pwrdm %s, req by oh %s\n",
>> +                      oh->name, t, pwrdm->name, req_oh->name);
>> +             ret = pwrdm_wakeuplat_set_constraint(pwrdm, req_dev, t);
>> +     }
>> +
>> +     return 0;
>> +}
>> +
>> +/**
>>   * omap_hwmod_get_context_loss_count - get lost context count
>>   * @oh: struct omap_hwmod *
>>   *
>> - * Query the powerdomain of of @oh to get the context loss
>> + * Query the powerdomain of @oh to get the context loss
>
> Stray change.
>
>>   * count for this device.
>>   *
>>   * Returns the context loss count of the powerdomain assocated with @oh
>> diff --git a/arch/arm/mach-omap2/powerdomain.c
> b/arch/arm/mach-omap2/powerdomain.c
>> index eaed0df..3ed3bea 100644
>> --- a/arch/arm/mach-omap2/powerdomain.c
>> +++ b/arch/arm/mach-omap2/powerdomain.c
>> @@ -19,16 +19,19 @@
>>  #include <linux/list.h>
>>  #include <linux/errno.h>
>>  #include <linux/string.h>
>> +#include <linux/slab.h>
>> +
>> +#include <plat/cpu.h>
>> +#include <plat/prcm.h>
>> +
>>  #include "cm2xxx_3xxx.h"
>>  #include "prcm44xx.h"
>>  #include "cm44xx.h"
>>  #include "prm2xxx_3xxx.h"
>>  #include "prm44xx.h"
>>
>> -#include <plat/cpu.h>
>>  #include "powerdomain.h"
>>  #include "clockdomain.h"
>> -#include <plat/prcm.h>
>>
>>  #include "pm.h"
>>
>> @@ -103,6 +106,13 @@ static int _pwrdm_register(struct powerdomain
> *pwrdm)
>>       pwrdm->state = pwrdm_read_pwrst(pwrdm);
>>       pwrdm->state_counter[pwrdm->state] = 1;
>>
>> +     /* Initialize priority ordered list for wakeup latency constraint
> */
>> +     spin_lock_init(&pwrdm->wakeuplat_lock);
>> +     plist_head_init(&pwrdm->wakeuplat_dev_list,
> &pwrdm->wakeuplat_lock);
>> +
>> +     /* res_mutex protects res_list add and del ops */
>> +     mutex_init(&pwrdm->wakeuplat_mutex);
>> +
>>       pr_debug("powerdomain: registered %s\n", pwrdm->name);
>>
>>       return 0;
>> @@ -176,6 +186,62 @@ static int _pwrdm_post_transition_cb(struct
> powerdomain *pwrdm, void *unused)
>>       return 0;
>>  }
>>
>> +/**
>> + * pwrdm_wakeuplat_update_pwrst - Update power domain power state if
> needed
>> + * @pwrdm: struct powerdomain * to which requesting device belongs to
>> + *
>> + * Finds minimum latency value from all entries in the list and
>> + * the power domain power state needing the constraint. Programs
>> + * new state if it is different from current power state.
>> + */
>> +static void pwrdm_wakeuplat_update_pwrst(struct powerdomain *pwrdm)
>> +{
>> +     struct plist_node *node;
>> +     int new_state;
>> +     unsigned long min_latency = -1;
>> +
>> +     if (!plist_head_empty(&pwrdm->wakeuplat_dev_list)) {
>> +             node = plist_last(&pwrdm->wakeuplat_dev_list);
>> +             min_latency = node->prio;
>> +     }
>> +
>> +     /* Find power state with wakeup latency < minimum constraint. */
>> +     for (new_state = 0x0; new_state < PWRDM_MAX_PWRSTS; new_state++) {
>> +             if (min_latency == -1 ||
>> +                 pwrdm->wakeup_lat[new_state] <= min_latency)
>> +                     break;
>> +     }
>> +
>> +     switch (new_state) {
>> +     case PWRDM_FUNC_PWRST_OFF:
>> +             new_state = PWRDM_POWER_OFF;
>> +             break;
>> +     case PWRDM_FUNC_PWRST_OSWR:
>> +             pwrdm_set_logic_retst(pwrdm, PWRDM_POWER_OFF);
>> +     case PWRDM_FUNC_PWRST_CSWR:
>> +             new_state = PWRDM_POWER_RET;
>> +             break;
>> +     case PWRDM_FUNC_PWRST_ON:
>> +             new_state = PWRDM_POWER_ON;
>> +             break;
>> +     default:
>> +             pr_warn("powerdomain: requested latency constraint not "
>> +                     "supported %s set to ON state\n", pwrdm->name);
>> +             new_state = PWRDM_POWER_ON;
>> +             break;
>> +     }
>> +
>> +     if (pwrdm_read_pwrst(pwrdm) != new_state) {
>> +             if (cpu_is_omap34xx())
>> +                     omap_set_pwrdm_state(pwrdm, new_state);
>> +     }
>> +
>> +     pr_debug("powerdomain: %s pwrst: curr= %d, prev= %d next= %d "
>> +              "wkuplat_min= %lu, set_state= %d\n", pwrdm->name,
>> +              pwrdm_read_pwrst(pwrdm), pwrdm_read_prev_pwrst(pwrdm),
>> +              pwrdm_read_next_pwrst(pwrdm), min_latency, new_state);
>> +}
>> +
>>  /* Public functions */
>>
>>  /**
>> @@ -911,6 +977,112 @@ int pwrdm_post_transition(void)
>>  }
>>
>>  /**
>> + * pwrdm_wakeuplat_set_constraint - Set powerdomain wakeup latency
> constraint
>> + * @pwrdm: struct powerdomain * to which requesting device belongs to
>> + * @dev: struct device * of requesting device
>> + * @t: wakeup latency constraint in microseconds
>> + *
>> + * Adds new entry to powerdomain's wakeup latency constraint list.
>> + * If the requesting device already exists in the list, old value is
>> + * overwritten. Checks whether current power state is still adequate.
>> + * Returns -EINVAL if the powerdomain or device pointer is NULL,
>> + * or -ENOMEM if kmalloc fails, or returns 0 upon success.
>> + */
>> +int pwrdm_wakeuplat_set_constraint(struct powerdomain *pwrdm,
>> +                                struct device *req_dev, unsigned long
> t)
>> +{
>> +     struct  wakeuplat_dev_list *user;
>> +     int found = 0, ret = 0;
>> +
>> +     if (!pwrdm || !req_dev) {
>> +             WARN(1, "powerdomain: %s: invalid parameter(s)",
> __func__);
>> +             return -EINVAL;
>> +     }
>> +
>> +     mutex_lock(&pwrdm->wakeuplat_mutex);
>> +
>> +     plist_for_each_entry(user, &pwrdm->wakeuplat_dev_list, node) {
>> +             if (user->dev == req_dev) {
>> +                     found = 1;
>> +                     break;
>> +             }
>> +     }
>> +
>> +     /* Add new entry to the list or update existing request */
>> +     if (found && user->constraint_us == t) {
>> +             goto exit_set;
>> +     } else if (!found) {
>> +             user = kzalloc(sizeof(struct wakeuplat_dev_list),
> GFP_KERNEL);
>> +             if (!user) {
>> +                     pr_err("powerdomain: FATAL ERROR: kzalloc
> failed\n");
>> +                     ret = -ENOMEM;
>> +                     goto exit_set;
>> +             }
>> +             user->dev = req_dev;
>> +     } else {
>> +             plist_del(&user->node, &pwrdm->wakeuplat_dev_list);
>> +     }
>> +
>> +     plist_node_init(&user->node, t);
>> +     plist_add(&user->node, &pwrdm->wakeuplat_dev_list);
>> +     user->node.prio = user->constraint_us = t;
>> +
>> +     pwrdm_wakeuplat_update_pwrst(pwrdm);
>> +
>> +exit_set:
>> +     mutex_unlock(&pwrdm->wakeuplat_mutex);
>> +
>> +     return ret;
>> +}
>> +
>> +/**
>> + * pwrdm_wakeuplat_release_constraint - Release powerdomain wkuplat
> constraint
>> + * @pwrdm: struct powerdomain * to which requesting device belongs to
>> + * @req_dev: struct device * of requesting device
>> + *
>> + * Removes device's entry from powerdomain's wakeup latency constraint
> list.
>> + * Checks whether current power state is still adequate.
>> + * Returns -EINVAL if the powerdomain or device pointer is NULL or
>> + * no such entry exists in the list, or returns 0 upon success.
>> + */
>> +int pwrdm_wakeuplat_release_constraint(struct powerdomain *pwrdm,
>> +                                    struct device *req_dev)
>> +{
>> +     struct wakeuplat_dev_list *user;
>> +     int found = 0, ret = 0;
>> +
>> +     if (!pwrdm || !req_dev) {
>> +             WARN(1, "powerdomain: %s: invalid parameter(s)",
> __func__);
>> +             return -EINVAL;
>> +     }
>> +
>> +     mutex_lock(&pwrdm->wakeuplat_mutex);
>> +
>> +     plist_for_each_entry(user, &pwrdm->wakeuplat_dev_list, node) {
>> +             if (user->dev == req_dev) {
>> +                     found = 1;
>> +                     break;
>> +             }
>> +     }
>> +
>> +     if (!found) {
>> +             pr_err("OMAP PM: Error: no prior constraint to
> release\n");
>> +             ret = -EINVAL;
>> +             goto exit_rls;
>> +     }
>> +
>> +     plist_del(&user->node, &pwrdm->wakeuplat_dev_list);
>> +     kfree(user);
>> +
>> +     pwrdm_wakeuplat_update_pwrst(pwrdm);
>> +
>> +exit_rls:
>> +     mutex_unlock(&pwrdm->wakeuplat_mutex);
>> +
>> +     return ret;
>> +}
>> +
>> +/**
>>   * pwrdm_get_context_loss_count - get powerdomain's context loss count
>>   * @pwrdm: struct powerdomain * to wait for
>>   *
>> diff --git a/arch/arm/mach-omap2/powerdomain.h
> b/arch/arm/mach-omap2/powerdomain.h
>> index c66431e..d4189d8 100644
>> --- a/arch/arm/mach-omap2/powerdomain.h
>> +++ b/arch/arm/mach-omap2/powerdomain.h
>> @@ -19,6 +19,9 @@
>>
>>  #include <linux/types.h>
>>  #include <linux/list.h>
>> +#include <linux/plist.h>
>> +#include <linux/mutex.h>
>> +#include <linux/spinlock.h>
>>
>>  #include <linux/atomic.h>
>>
>> @@ -46,6 +49,15 @@
>>
>>  #define PWRSTS_OFF_RET_ON    (PWRSTS_OFF_RET | (1 << PWRDM_POWER_ON))
>>
>> +/* Powerdomain functional power states */
>> +#define PWRDM_FUNC_PWRST_OFF 0x0
>> +#define PWRDM_FUNC_PWRST_OSWR        0x1
>> +#define PWRDM_FUNC_PWRST_CSWR        0x2
>> +#define PWRDM_FUNC_PWRST_ON  0x3
>> +
>> +#define PWRDM_MAX_FUNC_PWRSTS        4
>> +
>> +#define UNSUP_STATE          -1
>>
>>  /* Powerdomain flags */
>>  #define PWRDM_HAS_HDWR_SAR   (1 << 0) /* hardware save-and-restore
> support */
>> @@ -96,6 +108,10 @@ struct powerdomain;
>>   * @state_counter:
>>   * @timer:
>>   * @state_timer:
>> + * @wakeup_lat: Wakeup latencies for possible powerdomain power states
>> + * @wakeuplat_lock: spinlock for plist
>> + * @wakeuplat_dev_list: plist_head linking all devices placing
> constraint
>> + * @wakeuplat_mutex: mutex to protect per powerdomain list ops
>>   *
>>   * @prcm_partition possible values are defined in
> mach-omap2/prcm44xx.h.
>>   */
>> @@ -121,6 +137,16 @@ struct powerdomain {
>>       s64 timer;
>>       s64 state_timer[PWRDM_MAX_PWRSTS];
>>  #endif
>> +     const u32 wakeup_lat[PWRDM_MAX_FUNC_PWRSTS];
>> +     spinlock_t wakeuplat_lock;
>> +     struct plist_head wakeuplat_dev_list;
>> +     struct mutex wakeuplat_mutex;
>> +};
>> +
>> +struct wakeuplat_dev_list {
>> +     struct device *dev;
>> +     unsigned long constraint_us;
>> +     struct plist_node node;
>>  };
>>
>>  /**
>> @@ -211,6 +237,11 @@ int pwrdm_clkdm_state_switch(struct clockdomain
> *clkdm);
>>  int pwrdm_pre_transition(void);
>>  int pwrdm_post_transition(void);
>>  int pwrdm_set_lowpwrstchange(struct powerdomain *pwrdm);
>> +
>> +int pwrdm_wakeuplat_set_constraint(struct powerdomain *pwrdm,
>> +                                struct device *dev, unsigned long t);
>> +int pwrdm_wakeuplat_release_constraint(struct powerdomain *pwrdm,
>> +                                    struct device *dev);
>>  u32 pwrdm_get_context_loss_count(struct powerdomain *pwrdm);
>>
>>  extern void omap2xxx_powerdomains_init(void);
>> diff --git a/arch/arm/mach-omap2/powerdomains3xxx_data.c
> b/arch/arm/mach-omap2/powerdomains3xxx_data.c
>> index e1bec56..4f7e44d 100644
>> --- a/arch/arm/mach-omap2/powerdomains3xxx_data.c
>> +++ b/arch/arm/mach-omap2/powerdomains3xxx_data.c
>> @@ -52,6 +52,12 @@ static struct powerdomain iva2_pwrdm = {
>>               [2] = PWRSTS_OFF_ON,
>>               [3] = PWRDM_POWER_ON,
>>       },
>> +     .wakeup_lat = {
>> +             [PWRDM_FUNC_PWRST_OFF] = 1100,
>> +             [PWRDM_FUNC_PWRST_OSWR] = UNSUP_STATE,
>> +             [PWRDM_FUNC_PWRST_CSWR] = 350,
>> +             [PWRDM_FUNC_PWRST_ON] = 0,
>> +     },
>>  };
>>
>>  static struct powerdomain mpu_3xxx_pwrdm = {
>> @@ -68,6 +74,12 @@ static struct powerdomain mpu_3xxx_pwrdm = {
>>       .pwrsts_mem_on    = {
>>               [0] = PWRSTS_OFF_ON,
>>       },
>> +     .wakeup_lat = {
>> +             [PWRDM_FUNC_PWRST_OFF] = 95,
>> +             [PWRDM_FUNC_PWRST_OSWR] = UNSUP_STATE,
>> +             [PWRDM_FUNC_PWRST_CSWR] = 45,
>> +             [PWRDM_FUNC_PWRST_ON] = 0,
>> +     },
>>  };
>>
>>  /*
>> @@ -98,6 +110,12 @@ static struct powerdomain core_3xxx_pre_es3_1_pwrdm
> = {
>>               [0] = PWRSTS_OFF_RET_ON, /* MEM1ONSTATE */
>>               [1] = PWRSTS_OFF_RET_ON, /* MEM2ONSTATE */
>>       },
>> +     .wakeup_lat = {
>> +             [PWRDM_FUNC_PWRST_OFF] = 100,
>> +             [PWRDM_FUNC_PWRST_OSWR] = UNSUP_STATE,
>> +             [PWRDM_FUNC_PWRST_CSWR] = 60,
>> +             [PWRDM_FUNC_PWRST_ON] = 0,
>> +     },
>>  };
>>
>>  static struct powerdomain core_3xxx_es3_1_pwrdm = {
>> @@ -121,6 +139,12 @@ static struct powerdomain core_3xxx_es3_1_pwrdm = {
>>               [0] = PWRSTS_OFF_RET_ON, /* MEM1ONSTATE */
>>               [1] = PWRSTS_OFF_RET_ON, /* MEM2ONSTATE */
>>       },
>> +     .wakeup_lat = {
>> +             [PWRDM_FUNC_PWRST_OFF] = 100,
>> +             [PWRDM_FUNC_PWRST_OSWR] = UNSUP_STATE,
>> +             [PWRDM_FUNC_PWRST_CSWR] = 60,
>> +             [PWRDM_FUNC_PWRST_ON] = 0,
>> +     },
>>  };
>>
>>  static struct powerdomain dss_pwrdm = {
>> @@ -136,6 +160,12 @@ static struct powerdomain dss_pwrdm = {
>>       .pwrsts_mem_on    = {
>>               [0] = PWRDM_POWER_ON,  /* MEMONSTATE */
>>       },
>> +     .wakeup_lat = {
>> +             [PWRDM_FUNC_PWRST_OFF] = 70,
>> +             [PWRDM_FUNC_PWRST_OSWR] = UNSUP_STATE,
>> +             [PWRDM_FUNC_PWRST_CSWR] = 20,
>> +             [PWRDM_FUNC_PWRST_ON] = 0,
>> +     },
>>  };
>>
>>  /*
>> @@ -157,6 +187,12 @@ static struct powerdomain sgx_pwrdm = {
>>       .pwrsts_mem_on    = {
>>               [0] = PWRDM_POWER_ON,  /* MEMONSTATE */
>>       },
>> +     .wakeup_lat = {
>> +             [PWRDM_FUNC_PWRST_OFF] = 1000,
>> +             [PWRDM_FUNC_PWRST_OSWR] = UNSUP_STATE,
>> +             [PWRDM_FUNC_PWRST_CSWR] = UNSUP_STATE,
>> +             [PWRDM_FUNC_PWRST_ON] = 0,
>> +     },
>>  };
>>
>>  static struct powerdomain cam_pwrdm = {
>> @@ -172,6 +208,12 @@ static struct powerdomain cam_pwrdm = {
>>       .pwrsts_mem_on    = {
>>               [0] = PWRDM_POWER_ON,  /* MEMONSTATE */
>>       },
>> +     .wakeup_lat = {
>> +             [PWRDM_FUNC_PWRST_OFF] = 850,
>> +             [PWRDM_FUNC_PWRST_OSWR] = UNSUP_STATE,
>> +             [PWRDM_FUNC_PWRST_CSWR] = 35,
>> +             [PWRDM_FUNC_PWRST_ON] = 0,
>> +     },
>>  };
>>
>>  static struct powerdomain per_pwrdm = {
>> @@ -187,6 +229,12 @@ static struct powerdomain per_pwrdm = {
>>       .pwrsts_mem_on    = {
>>               [0] = PWRDM_POWER_ON,  /* MEMONSTATE */
>>       },
>> +     .wakeup_lat = {
>> +             [PWRDM_FUNC_PWRST_OFF] = 200,
>> +             [PWRDM_FUNC_PWRST_OSWR] = UNSUP_STATE,
>> +             [PWRDM_FUNC_PWRST_CSWR] = 110,
>> +             [PWRDM_FUNC_PWRST_ON] = 0,
>> +     },
>>  };
>>
>>  static struct powerdomain emu_pwrdm = {
>> @@ -201,6 +249,12 @@ static struct powerdomain neon_pwrdm = {
>>       .omap_chip        = OMAP_CHIP_INIT(CHIP_IS_OMAP3430),
>>       .pwrsts           = PWRSTS_OFF_RET_ON,
>>       .pwrsts_logic_ret = PWRDM_POWER_RET,
>> +     .wakeup_lat = {
>> +             [PWRDM_FUNC_PWRST_OFF] = 200,
>> +             [PWRDM_FUNC_PWRST_OSWR] = UNSUP_STATE,
>> +             [PWRDM_FUNC_PWRST_CSWR] = 35,
>> +             [PWRDM_FUNC_PWRST_ON] = 0,
>> +     },
>>  };
>>
>>  static struct powerdomain usbhost_pwrdm = {
>> @@ -223,6 +277,12 @@ static struct powerdomain usbhost_pwrdm = {
>>       .pwrsts_mem_on    = {
>>               [0] = PWRDM_POWER_ON,  /* MEMONSTATE */
>>       },
>> +     .wakeup_lat = {
>> +             [PWRDM_FUNC_PWRST_OFF] = 800,
>> +             [PWRDM_FUNC_PWRST_OSWR] = UNSUP_STATE,
>> +             [PWRDM_FUNC_PWRST_CSWR] = 150,
>> +             [PWRDM_FUNC_PWRST_ON] = 0,
>> +     },
>>  };
>>
>>  static struct powerdomain dpll1_pwrdm = {
>> diff --git a/arch/arm/plat-omap/include/plat/omap_device.h
> b/arch/arm/plat-omap/include/plat/omap_device.h
>> index e4c349f..5da6b47 100644
>> --- a/arch/arm/plat-omap/include/plat/omap_device.h
>> +++ b/arch/arm/plat-omap/include/plat/omap_device.h
>> @@ -107,6 +107,8 @@ void __iomem *omap_device_get_rt_va(struct
> omap_device *od);
>>  int omap_device_align_pm_lat(struct platform_device *pdev,
>>                            u32 new_wakeup_lat_limit);
>>  struct powerdomain *omap_device_get_pwrdm(struct omap_device *od);
>> +int omap_device_set_max_dev_wakeup_lat(struct platform_device
> *req_pdev,
>> +                                    struct platform_device *pdev, long
> t);
>>  u32 omap_device_get_context_loss_count(struct platform_device *pdev);
>>
>>  /* Other */
>> diff --git a/arch/arm/plat-omap/include/plat/omap_hwmod.h
> b/arch/arm/plat-omap/include/plat/omap_hwmod.h
>> index 1eee85a..0fbb974 100644
>> --- a/arch/arm/plat-omap/include/plat/omap_hwmod.h
>> +++ b/arch/arm/plat-omap/include/plat/omap_hwmod.h
>> @@ -587,6 +587,8 @@ int omap_hwmod_for_each_by_class(const char
> *classname,
>>                                void *user);
>>
>>  int omap_hwmod_set_postsetup_state(struct omap_hwmod *oh, u8 state);
>> +int omap_hwmod_set_max_dev_wakeup_lat(struct omap_hwmod *req_oh,
>> +                                   struct omap_hwmod *oh, long t);
>>  u32 omap_hwmod_get_context_loss_count(struct omap_hwmod *oh);
>>
>>  /*
>> diff --git a/arch/arm/plat-omap/omap-pm-constraints.c
> b/arch/arm/plat-omap/omap-pm-constraints.c
>> index c8b4e4c..7ae855f 100644
>> --- a/arch/arm/plat-omap/omap-pm-constraints.c
>> +++ b/arch/arm/plat-omap/omap-pm-constraints.c
>> @@ -24,6 +24,7 @@
>>  /* Interface documentation is in mach/omap-pm.h */
>>  #include <plat/omap-pm.h>
>>  #include <plat/omap_device.h>
>> +#include <plat/common.h>
>>
>>  static bool off_mode_enabled;
>>  static u32 dummy_context_loss_counter;
>> @@ -32,34 +33,6 @@ static u32 dummy_context_loss_counter;
>>   * Device-driver-originated constraints (via board-*.c files)
>>   */
>>
>> -int omap_pm_set_max_mpu_wakeup_lat(struct device *dev, long t)
>> -{
>> -     if (!dev || t < -1) {
>> -             WARN(1, "OMAP PM: %s: invalid parameter(s)", __func__);
>> -             return -EINVAL;
>> -     };
>> -
>> -     if (t == -1)
>> -             pr_debug("OMAP PM: remove max MPU wakeup latency
> constraint: "
>> -                      "dev %s\n", dev_name(dev));
>> -     else
>> -             pr_debug("OMAP PM: add max MPU wakeup latency constraint:
> "
>> -                      "dev %s, t = %ld usec\n", dev_name(dev), t);
>> -
>> -     /*
>> -      * For current Linux, this needs to map the MPU to a
>> -      * powerdomain, then go through the list of current max lat
>> -      * constraints on the MPU and find the smallest.  If
>> -      * the latency constraint has changed, the code should
>> -      * recompute the state to enter for the next powerdomain
>> -      * state.
>> -      *
>> -      * TI CDP code can call constraint_set here.
>> -      */
>> -
>> -     return 0;
>> -}
>> -
>>  int omap_pm_set_min_bus_tput(struct device *dev, u8 agent_id, unsigned
> long r)
>>  {
>>       if (!dev || (agent_id != OCP_INITIATOR_AGENT &&
>> @@ -87,62 +60,70 @@ int omap_pm_set_min_bus_tput(struct device *dev, u8
> agent_id, unsigned long r)
>>       return 0;
>>  }
>>
>> +/*
>> + * omap_pm_set_max_dev_wakeup_lat - set/release devices wake-up latency
>> + * constraints
>> + */
>>  int omap_pm_set_max_dev_wakeup_lat(struct device *req_dev, struct
> device *dev,
>>                                  long t)
>
> I seem to have a fundamental problem understanding the flow,
> starting with what these 2 struct devices are used to identify?
> Can you help explain with some example what these would be,
> say incase an MMC driver wants to put a constraint?
>
> Besides, the struct device passed in the top level api gets
> converted into platform_device and then into omap_device
> followed by a omap_hwmod and then its converted back into
> platform_device using the omap_device pointer in the
> omap_hwmod struct. Something just dosen't seem right.
There is no conversion involved but only packaging into the respective
structs types.
IIUC this is the way the architecture sees the layering.

Any comment for the arch gurus?

Thanks,
Jean

>
> Regards,
> Rajendra
>
>>  {
>> +     struct platform_device *pdev, *req_pdev;
>> +     int ret = 0;
>> +
>>       if (!req_dev || !dev || t < -1) {
>>               WARN(1, "OMAP PM: %s: invalid parameter(s)", __func__);
>>               return -EINVAL;
>> +     }
>> +
>> +     /* Look for the platform devices */
>> +     pdev = container_of(dev, struct platform_device, dev);
>> +     req_pdev = container_of(req_dev, struct platform_device, dev);
>> +
>> +     /* Try to catch non platform devices. */
>> +     if ((pdev->name == NULL) || (req_pdev->name == NULL)) {
>> +             pr_err("OMAP-PM set_wakeup_lat: Error: platform devices "
>> +                    "not valid\n");
>> +             return -EINVAL;
>> +     } else {
>> +             /* Call the omap_device API */
>> +             ret = omap_device_set_max_dev_wakeup_lat(req_pdev, pdev,
> t);
>> +     }
>> +
>> +     return ret;
>> +}
>> +
>> +int omap_pm_set_max_mpu_wakeup_lat(struct device *req_dev, long t)
>> +{
>> +     if (!req_dev || t < -1) {
>> +             WARN(1, "OMAP PM: %s: invalid parameter(s)", __func__);
>> +             return -EINVAL;
>>       };
>>
>>       if (t == -1)
>> -             pr_debug("OMAP PM: remove max device latency constraint: "
>> -                      "dev %s\n", dev_name(dev));
>> +             pr_debug("OMAP PM: remove max MPU wakeup latency
> constraint: "
>> +                      "dev %s\n", dev_name(req_dev));
>>       else
>> -             pr_debug("OMAP PM: add max device latency constraint: "
>> -                      "dev %s, t = %ld usec\n", dev_name(dev), t);
>> +             pr_debug("OMAP PM: add max MPU wakeup latency constraint:
> "
>> +                      "dev %s, t = %ld usec\n", dev_name(req_dev), t);
>>
>> -     /*
>> -      * For current Linux, this needs to map the device to a
>> -      * powerdomain, then go through the list of current max lat
>> -      * constraints on that powerdomain and find the smallest.  If
>> -      * the latency constraint has changed, the code should
>> -      * recompute the state to enter for the next powerdomain
>> -      * state.  Conceivably, this code should also determine
>> -      * whether to actually disable the device clocks or not,
>> -      * depending on how long it takes to re-enable the clocks.
>> -      *
>> -      * TI CDP code can call constraint_set here.
>> -      */
>> +     omap_pm_set_max_dev_wakeup_lat(req_dev, omap2_get_mpuss_device(),
> t);
>>
>>       return 0;
>>  }
>>
>> -int omap_pm_set_max_sdma_lat(struct device *dev, long t)
>> +int omap_pm_set_max_sdma_lat(struct device *req_dev, long t)
>>  {
>> -     if (!dev || t < -1) {
>> +     if (!req_dev || t < -1) {
>>               WARN(1, "OMAP PM: %s: invalid parameter(s)", __func__);
>>               return -EINVAL;
>>       };
>>
>>       if (t == -1)
>>               pr_debug("OMAP PM: remove max DMA latency constraint: "
>> -                      "dev %s\n", dev_name(dev));
>> +                      "dev %s\n", dev_name(req_dev));
>>       else
>>               pr_debug("OMAP PM: add max DMA latency constraint: "
>> -                      "dev %s, t = %ld usec\n", dev_name(dev), t);
>> -
>> -     /*
>> -      * For current Linux PM QOS params, this code should scan the
>> -      * list of maximum CPU and DMA latencies and select the
>> -      * smallest, then set cpu_dma_latency pm_qos_param
>> -      * accordingly.
>> -      *
>> -      * For future Linux PM QOS params, with separate CPU and DMA
>> -      * latency params, this code should just set the dma_latency
> param.
>> -      *
>> -      * TI CDP code can call constraint_set here.
>> -      */
>> +                      "dev %s, t = %ld usec\n", dev_name(req_dev), t);
>>
>>       return 0;
>>  }
>> diff --git a/arch/arm/plat-omap/omap_device.c
> b/arch/arm/plat-omap/omap_device.c
>> index 57adb27..1fe93d3 100644
>> --- a/arch/arm/plat-omap/omap_device.c
>> +++ b/arch/arm/plat-omap/omap_device.c
>> @@ -280,6 +280,34 @@ static void _add_optional_clock_alias(struct
> omap_device *od,
>>  /* Public functions for use by core code */
>>
>>  /**
>> + * omap_device_set_max_dev_wakeup_lat - set/release a device constraint
>> + * @od: struct omap_device *
>> + * @req_od: struct omap_device *
>> + *
>> + * Using the primary hwmod, set/release a device constraint for the
> pdev
>> + * device, requested by the req_pdev device.
>> + *
>> + * If any hwmods exist for the omap_device assoiated with @pdev and
> @req_pdev,
>> + * set/release the constraint for the corresponding hwmods, otherwise
> return
>> + * -EINVAL.
>> + */
>> +int omap_device_set_max_dev_wakeup_lat(struct platform_device
> *req_pdev,
>> +                                    struct platform_device *pdev, long
> t)
>> +{
>> +     struct omap_device *od, *req_od;
>> +     u32 ret = -EINVAL;
>> +
>> +     od = _find_by_pdev(pdev);
>> +     req_od = _find_by_pdev(req_pdev);
>> +
>> +     if ((od->hwmods_cnt) && (req_od->hwmods_cnt))
>> +             ret = omap_hwmod_set_max_dev_wakeup_lat(req_od->hwmods[0],
>> +                                                     od->hwmods[0], t);
>> +
>> +     return ret;
>> +}
>> +
>> +/**
>>   * omap_device_get_context_loss_count - get lost context count
>>   * @od: struct omap_device *
>>   *
>> --
>> 1.7.2.3
>
--
To unsubscribe from this list: send the line "unsubscribe linux-omap" in
the body of a message to majordomo@vger.kernel.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html

^ permalink raw reply	[flat|nested] 19+ messages in thread

* Re: [PATCH 2/2] OMAP: PM: implement devices wakeup latency constraints APIs
  2011-03-08 15:54       ` Jean Pihet
@ 2011-03-08 17:33         ` Kevin Hilman
  -1 siblings, 0 replies; 19+ messages in thread
From: Kevin Hilman @ 2011-03-08 17:33 UTC (permalink / raw)
  To: Jean Pihet; +Cc: linux-omap, linux-arm-kernel, Jean Pihet, Vibhore Vardhan

Jean Pihet <jean.pihet@newoldbits.com> writes:

> On Tue, Mar 8, 2011 at 3:15 AM, Kevin Hilman <khilman@ti.com> wrote:
>> Jean Pihet <jean.pihet@newoldbits.com> writes:
>>
>>> Implement OMAP PM layer omap_pm_set_max_dev_wakeup_lat API by
>>> creating similar APIs at the omap_device and omap_hwmod levels. The
>>> omap_hwmod level call is the layer with access to the powerdomain
>>> core, so it is the place where the powerdomain is queried to set and
>>> release the constraints.
>>>
>>> NOTE: only works for devices which have been converted to use
>>>       omap_device/omap_hwmod.
>>>
>>> Longer term, we could possibly remove this API from the OMAP PM layer,
>>> and instead directly use the device level API.
>>>
>>> The power domains get the next power state programmed directly in
>>> the registers via pwrdm_wakeuplat_update_pwrst.
>>>
>>> Note about PM QOS: the MPU and CORE power domains get the next power
>>> state via cpuidle, which get the strongest wake-up latency constraint
>>> by querying PM QOS. The usage of PM QOS is temporary, until a generic
>>> solution is in place.
>>>
>>> Based on Vibhore's original patch, adapted to omap_device, omap_hwmod
>>> and PM QOS frameworks.
>>
>> I haven't got to a detailed review of this code yet, but did do some
>> experiements and have some general comments on the code/design to get
>> started.
>>
>> Also, I had a problem doing a real dumb test until I figured out the
>> problem with the init sequence.  I tried setting a constraint in the
>> device init code for UART (mach-omap2/serial.c:omap_serial_init_port()),
>> and then I realized that that runs before
>> mach-omap2/pm34xx.c:pwrdms_setup() which also calls
>> omap_set_pwrdm_state() for each powerdomain to initialize.
>
> Do we need to change the behavior at init?
>

I think so.  

At a minimum, it should be documented somewhere that the constraints API
cannot be used before pwrdms_setup().  But since that happens much later
in the boot process than device init code, that might be too strict.

>> Also, for debug purposes, it might be useful to have a per-device sysfs
>> interface to setting this wakeup latency constraint.  Something like
>>
>>   /sys/devices/platform/omap/.../power/wakeup_latency
> I do not see this as a debug interface but rather an API to the user space.
> If that is the case there are some issues to deal with, cf. below.

By debug, I was thinking under '#ifdef CONFIG_PM_DEBUG'.  Even if you
did this as a separate patch that we did not try to get upstream, it
would be extremely valuable for testing this framework.

>> I'm not sure exactly what to set the requesting device to though.
> And also, how to track the requesters? PM QOS is using a /dev node
> that must be kept open as long as the constraints remains valid.

A dummy device could be created for all sysfs requesters since you would
want subsequent writes to override  previous ones.

>> As far as implementation goes, you've so far implemented only wakeup
>> latencies, but not througput.  When you implement throughput you will
>> have to duplicate large parts of this code and data structures for
>> throughput, and if ever add some other constraint (frequency, voltage)
>> it would need to be duplicated again.
>>
>> Maybe now is the time to consider an interface to add a generic
>> per-device constraint, with a type (latency, throughput, etc.), or
>> "class" as it's called in PM QoS.  For now the type/class does not need
>> to be exposed externally, but will make implementing new constraint
>> types much easer.
> Ok that makes sense.
> In the current patch the constraints plist is stored inside the
> powerdomain structure. This does not apply to throughput, frequency
> and voltage constraints.
> Should the list constraint be managed in the OMAP PM layer instead of
> in the power domain code? If so where to store the constraints?

I was thinking at the omap_device layer, where there would be a
constraint plist managed for each class of constraint.

>> Some other comments below...
>>
>>> Signed-off-by: Jean Pihet <j-pihet@ti.com>
>>> Cc: Vibhore Vardhan <vvardhan@ti.com>
>>> ---
>>> Based on khilman's pm-core branch
>>>
>>>  arch/arm/mach-omap2/omap_hwmod.c              |   62 ++++++++-
>>>  arch/arm/mach-omap2/powerdomain.c             |  197 +++++++++++++++++++++++++
>>>  arch/arm/mach-omap2/powerdomain.h             |   39 +++++-
>>>  arch/arm/mach-omap2/powerdomains3xxx_data.c   |   60 ++++++++
>>>  arch/arm/plat-omap/include/plat/omap_device.h |    2 +
>>>  arch/arm/plat-omap/include/plat/omap_hwmod.h  |    2 +
>>>  arch/arm/plat-omap/omap-pm-constraints.c      |  121 +++++++--------
>>>  arch/arm/plat-omap/omap_device.c              |   28 ++++
>>>  8 files changed, 446 insertions(+), 65 deletions(-)
>>>
>>> diff --git a/arch/arm/mach-omap2/omap_hwmod.c b/arch/arm/mach-omap2/omap_hwmod.c
>>> index 028efda..bad8248 100644
>>> --- a/arch/arm/mach-omap2/omap_hwmod.c
>>> +++ b/arch/arm/mach-omap2/omap_hwmod.c
>>> @@ -142,6 +142,7 @@
>>>  #include "powerdomain.h"
>>>  #include <plat/clock.h>
>>>  #include <plat/omap_hwmod.h>
>>> +#include <plat/omap_device.h>
>>>  #include <plat/prcm.h>
>>>
>>>  #include "cm2xxx_3xxx.h"
>>> @@ -2267,10 +2268,69 @@ ohsps_unlock:
>>>  }
>>>
>>>  /**
>>> + * omap_hwmod_set_max_dev_wakeup_lat - set a device wake-up constraint
>>> + * @oh: the device of @oh to set a constraint on.
>>> + * @req_oh: the device of @req_oh is the requester of the constraint.
>>> + * @t: wakeup latency constraint (us). -1 removes the existing constraint.
>>> + *
>>> + * Query the powerdomain of @oh to set/release the wake-up constraint.
>>> + * @oh is used to determine which power domain to set a constraint on.
>>> + * @req_oh is used to record the requester for later update or removal
>>> + * of a constraint.
>>> + *
>>> + * Returns -EINVAL if @oh or @req_oh have no power domain, or the return
>>> + * code from the pwrdm function (pwrdm_wakeuplat_set/release_constraint)
>>> + * of the powerdomain assocated with @oh.
>>> + */
>>> +int omap_hwmod_set_max_dev_wakeup_lat(struct omap_hwmod *req_oh,
>>> +                                   struct omap_hwmod *oh, long t)
>>> +{
>>> +     struct device *req_dev;
>>> +     struct platform_device *req_pdev;
>>> +     struct powerdomain *pwrdm;
>>> +
>>> +     pwrdm = omap_hwmod_get_pwrdm(oh);
>>> +
>>> +     /* Catch devices with undefined powerdomains */
>>> +     if (!PTR_ERR(pwrdm)) {
>>> +             pr_err("omap_hwmod: Error: could not find parent "
>>> +                     "powerdomain for %s\n", oh->name);
>>> +             return -EINVAL;
>>> +     }
>>> +
>>> +     req_pdev = &(req_oh->od->pdev);
>>> +     if (!PTR_ERR(req_pdev)) {
>>> +             pr_err("omap_hwmod: Error: pdev not found for oh %s\n",
>>> +                    oh->name);
>>> +             return -EINVAL;
>>> +     }
>>> +
>>> +     req_dev = &(req_pdev->dev);
>>> +     if (!PTR_ERR(req_dev)) {
>>> +             pr_err("omap_hwmod: Error: device not found for oh %s\n",
>>> +                    oh->name);
>>> +             return -EINVAL;
>>> +     }
>>> +
>>> +     /* Call set/release_constraint for the given pwrdm */
>>> +     if (t == -1) {
>>> +             pr_debug("omap_hwmod: remove max device latency constraint: "
>>> +                      "oh %s, pwrdm %s, req by oh %s\n",
>>> +                      oh->name, pwrdm->name, req_oh->name);
>>> +     } else {
>>> +             pr_debug("omap_hwmod: add max device latency constraint: "
>>> +                      "oh %s, t = %ld usec, pwrdm %s, req by oh %s\n",
>>> +                      oh->name, t, pwrdm->name, req_oh->name);
>>> +     }
>>> +
>>> +     return pwrdm_wakeuplat_set_constraint(pwrdm, req_dev, t);
>>> +}
>>> +
>>> +/**
>>>   * omap_hwmod_get_context_loss_count - get lost context count
>>>   * @oh: struct omap_hwmod *
>>>   *
>>> - * Query the powerdomain of of @oh to get the context loss
>>> + * Query the powerdomain of @oh to get the context loss
>>>   * count for this device.
>>>   *
>>>   * Returns the context loss count of the powerdomain assocated with @oh
>>> diff --git a/arch/arm/mach-omap2/powerdomain.c b/arch/arm/mach-omap2/powerdomain.c
>>> index eaed0df..6fb4741 100644
>>> --- a/arch/arm/mach-omap2/powerdomain.c
>>> +++ b/arch/arm/mach-omap2/powerdomain.c
>>> @@ -19,6 +19,8 @@
>>>  #include <linux/list.h>
>>>  #include <linux/errno.h>
>>>  #include <linux/string.h>
>>> +#include <linux/slab.h>
>>> +
>>>  #include "cm2xxx_3xxx.h"
>>>  #include "prcm44xx.h"
>>>  #include "cm44xx.h"
>>> @@ -103,6 +105,13 @@ static int _pwrdm_register(struct powerdomain *pwrdm)
>>>       pwrdm->state = pwrdm_read_pwrst(pwrdm);
>>>       pwrdm->state_counter[pwrdm->state] = 1;
>>>
>>> +     /* Initialize priority ordered list for wakeup latency constraint */
>>> +     spin_lock_init(&pwrdm->wakeuplat_lock);
>>> +     plist_head_init(&pwrdm->wakeuplat_dev_list, &pwrdm->wakeuplat_lock);
>>> +
>>> +     /* res_mutex protects res_list add and del ops */
>>> +     mutex_init(&pwrdm->wakeuplat_mutex);
>>> +
>>>       pr_debug("powerdomain: registered %s\n", pwrdm->name);
>>>
>>>       return 0;
>>> @@ -176,6 +185,74 @@ static int _pwrdm_post_transition_cb(struct powerdomain *pwrdm, void *unused)
>>>       return 0;
>>>  }
>>>
>>> +/**
>>> + * pwrdm_wakeuplat_update_pwrst - Update power domain power state if needed
>>> + * @pwrdm: struct powerdomain * to which requesting device belongs to.
>>> + *
>>> + * Finds the minimum allowed wake-up latency value from all entries
>>> + * in the list and the power domain power state needing the constraint.
>>> + * Programs a new target state if it is different from current power state.
>>> + *
>>> + * Only OMAP3xxx is supported for now
>>> + *
>>> + * Returns 0 upon success.
>>> + */
>>> +static int pwrdm_wakeuplat_update_pwrst(struct powerdomain *pwrdm)
>>> +{
>>> +     struct plist_node *node;
>>> +     int ret = 0, new_state;
>>> +     long min_latency = -1;
>>> +
>>> +     /* Find the strongest constraint from the plist */
>>> +     if (!plist_head_empty(&pwrdm->wakeuplat_dev_list)) {
>>> +             node = plist_first(&pwrdm->wakeuplat_dev_list);
>>> +             min_latency = node->prio;
>>> +     }
>>> +
>>> +     /* Find power state with wakeup latency < minimum constraint. */
>>> +     for (new_state = 0x0; new_state < PWRDM_MAX_PWRSTS; new_state++) {
>>> +             if (min_latency == -1 ||
>>> +                 pwrdm->wakeup_lat[new_state] <= min_latency)
>>> +                     break;
>>> +     }
>>> +
>>> +     switch (new_state) {
>>> +     case PWRDM_FUNC_PWRST_OFF:
>>> +             new_state = PWRDM_POWER_OFF;
>>> +             break;
>>> +     case PWRDM_FUNC_PWRST_OSWR:
>>> +             if (cpu_is_omap34xx())
>>> +                     pwrdm_set_logic_retst(pwrdm, PWRDM_POWER_OFF);
>>
>> cpu_is_* checks here aren't right.
>>
>> You should use SoC specific function pointers as are done for many of the
>> other powerdomain calls after Rajendra's splitup series.
> The cpu_is_* tests are not related to the SoC specific function
> pointers but rather to the comment in the function description 'Only
> OMAP3xxx is supported for now'.
> In fact the function that are conditionally called
> (pwrdm_set_logic_retst and omap_set_pwrdm_state) are correctly using
> the SoC specific pointers.
>
> Is this test needed? If so is it the correct way to do it?

Right, I think you can just drop the cpu_is_* checks here since as you
pointed out, the called functions already handle SoC specifics.

>>
>>> +             new_state = PWRDM_POWER_RET;
>>> +             break;
>>> +     case PWRDM_FUNC_PWRST_CSWR:
>>> +             if (cpu_is_omap34xx())
>>> +                     pwrdm_set_logic_retst(pwrdm, PWRDM_POWER_RET);
>>> +             new_state = PWRDM_POWER_RET;
>>> +             break;
>>> +     case PWRDM_FUNC_PWRST_ON:
>>> +             new_state = PWRDM_POWER_ON;
>>> +             break;
>>> +     default:
>>> +             pr_warn("powerdomain: requested latency constraint not "
>>> +                     "supported %s set to ON state\n", pwrdm->name);
>>> +             new_state = PWRDM_POWER_ON;
>>> +             break;
>>> +     }
>>> +
>>> +     if (pwrdm_read_pwrst(pwrdm) != new_state) {
>>> +             if (cpu_is_omap34xx())
>>> +                     ret = omap_set_pwrdm_state(pwrdm, new_state);
>>> +     }
>>> +
>>> +     pr_debug("powerdomain: %s pwrst: curr=%d, prev=%d next=%d "
>>> +              "min_latency=%ld, set_state=%d\n", pwrdm->name,
>>> +              pwrdm_read_pwrst(pwrdm), pwrdm_read_prev_pwrst(pwrdm),
>>> +              pwrdm_read_next_pwrst(pwrdm), min_latency, new_state);
>>> +
>>> +     return ret;
>>> +}
>>> +
>>
>> [...]
>>
>>> diff --git a/arch/arm/mach-omap2/powerdomains3xxx_data.c b/arch/arm/mach-omap2/powerdomains3xxx_data.c
>>> index e1bec56..4f7e44d 100644
>>> --- a/arch/arm/mach-omap2/powerdomains3xxx_data.c
>>> +++ b/arch/arm/mach-omap2/powerdomains3xxx_data.c
>>> @@ -52,6 +52,12 @@ static struct powerdomain iva2_pwrdm = {
>>>               [2] = PWRSTS_OFF_ON,
>>>               [3] = PWRDM_POWER_ON,
>>>       },
>>> +     .wakeup_lat = {
>>> +             [PWRDM_FUNC_PWRST_OFF] = 1100,
>>> +             [PWRDM_FUNC_PWRST_OSWR] = UNSUP_STATE,
>>> +             [PWRDM_FUNC_PWRST_CSWR] = 350,
>>> +             [PWRDM_FUNC_PWRST_ON] = 0,
>>> +     },
>>>  };
>>>
>>>  static struct powerdomain mpu_3xxx_pwrdm = {
>>> @@ -68,6 +74,12 @@ static struct powerdomain mpu_3xxx_pwrdm = {
>>>       .pwrsts_mem_on    = {
>>>               [0] = PWRSTS_OFF_ON,
>>>       },
>>> +     .wakeup_lat = {
>>> +             [PWRDM_FUNC_PWRST_OFF] = 95,
>>> +             [PWRDM_FUNC_PWRST_OSWR] = UNSUP_STATE,
>>> +             [PWRDM_FUNC_PWRST_CSWR] = 45,
>>> +             [PWRDM_FUNC_PWRST_ON] = 0,
>>> +     },
>>>  };
>>>
>>>  /*
>>> @@ -98,6 +110,12 @@ static struct powerdomain core_3xxx_pre_es3_1_pwrdm = {
>>>               [0] = PWRSTS_OFF_RET_ON, /* MEM1ONSTATE */
>>>               [1] = PWRSTS_OFF_RET_ON, /* MEM2ONSTATE */
>>>       },
>>> +     .wakeup_lat = {
>>> +             [PWRDM_FUNC_PWRST_OFF] = 100,
>>> +             [PWRDM_FUNC_PWRST_OSWR] = UNSUP_STATE,
>>> +             [PWRDM_FUNC_PWRST_CSWR] = 60,
>>> +             [PWRDM_FUNC_PWRST_ON] = 0,
>>> +     },
>>>  };
>>>
>>>  static struct powerdomain core_3xxx_es3_1_pwrdm = {
>>> @@ -121,6 +139,12 @@ static struct powerdomain core_3xxx_es3_1_pwrdm = {
>>>               [0] = PWRSTS_OFF_RET_ON, /* MEM1ONSTATE */
>>>               [1] = PWRSTS_OFF_RET_ON, /* MEM2ONSTATE */
>>>       },
>>> +     .wakeup_lat = {
>>> +             [PWRDM_FUNC_PWRST_OFF] = 100,
>>> +             [PWRDM_FUNC_PWRST_OSWR] = UNSUP_STATE,
>>> +             [PWRDM_FUNC_PWRST_CSWR] = 60,
>>> +             [PWRDM_FUNC_PWRST_ON] = 0,
>>> +     },
>>>  };
>>>
>>>  static struct powerdomain dss_pwrdm = {
>>> @@ -136,6 +160,12 @@ static struct powerdomain dss_pwrdm = {
>>>       .pwrsts_mem_on    = {
>>>               [0] = PWRDM_POWER_ON,  /* MEMONSTATE */
>>>       },
>>> +     .wakeup_lat = {
>>> +             [PWRDM_FUNC_PWRST_OFF] = 70,
>>> +             [PWRDM_FUNC_PWRST_OSWR] = UNSUP_STATE,
>>> +             [PWRDM_FUNC_PWRST_CSWR] = 20,
>>> +             [PWRDM_FUNC_PWRST_ON] = 0,
>>> +     },
>>>  };
>>>
>>>  /*
>>> @@ -157,6 +187,12 @@ static struct powerdomain sgx_pwrdm = {
>>>       .pwrsts_mem_on    = {
>>>               [0] = PWRDM_POWER_ON,  /* MEMONSTATE */
>>>       },
>>> +     .wakeup_lat = {
>>> +             [PWRDM_FUNC_PWRST_OFF] = 1000,
>>> +             [PWRDM_FUNC_PWRST_OSWR] = UNSUP_STATE,
>>> +             [PWRDM_FUNC_PWRST_CSWR] = UNSUP_STATE,
>>> +             [PWRDM_FUNC_PWRST_ON] = 0,
>>> +     },
>>>  };
>>>
>>>  static struct powerdomain cam_pwrdm = {
>>> @@ -172,6 +208,12 @@ static struct powerdomain cam_pwrdm = {
>>>       .pwrsts_mem_on    = {
>>>               [0] = PWRDM_POWER_ON,  /* MEMONSTATE */
>>>       },
>>> +     .wakeup_lat = {
>>> +             [PWRDM_FUNC_PWRST_OFF] = 850,
>>> +             [PWRDM_FUNC_PWRST_OSWR] = UNSUP_STATE,
>>> +             [PWRDM_FUNC_PWRST_CSWR] = 35,
>>> +             [PWRDM_FUNC_PWRST_ON] = 0,
>>> +     },
>>>  };
>>>
>>>  static struct powerdomain per_pwrdm = {
>>> @@ -187,6 +229,12 @@ static struct powerdomain per_pwrdm = {
>>>       .pwrsts_mem_on    = {
>>>               [0] = PWRDM_POWER_ON,  /* MEMONSTATE */
>>>       },
>>> +     .wakeup_lat = {
>>> +             [PWRDM_FUNC_PWRST_OFF] = 200,
>>> +             [PWRDM_FUNC_PWRST_OSWR] = UNSUP_STATE,
>>> +             [PWRDM_FUNC_PWRST_CSWR] = 110,
>>> +             [PWRDM_FUNC_PWRST_ON] = 0,
>>> +     },
>>>  };
>>>
>>>  static struct powerdomain emu_pwrdm = {
>>> @@ -201,6 +249,12 @@ static struct powerdomain neon_pwrdm = {
>>>       .omap_chip        = OMAP_CHIP_INIT(CHIP_IS_OMAP3430),
>>>       .pwrsts           = PWRSTS_OFF_RET_ON,
>>>       .pwrsts_logic_ret = PWRDM_POWER_RET,
>>> +     .wakeup_lat = {
>>> +             [PWRDM_FUNC_PWRST_OFF] = 200,
>>> +             [PWRDM_FUNC_PWRST_OSWR] = UNSUP_STATE,
>>> +             [PWRDM_FUNC_PWRST_CSWR] = 35,
>>> +             [PWRDM_FUNC_PWRST_ON] = 0,
>>> +     },
>>>  };
>>>
>>>  static struct powerdomain usbhost_pwrdm = {
>>> @@ -223,6 +277,12 @@ static struct powerdomain usbhost_pwrdm = {
>>>       .pwrsts_mem_on    = {
>>>               [0] = PWRDM_POWER_ON,  /* MEMONSTATE */
>>>       },
>>> +     .wakeup_lat = {
>>> +             [PWRDM_FUNC_PWRST_OFF] = 800,
>>> +             [PWRDM_FUNC_PWRST_OSWR] = UNSUP_STATE,
>>> +             [PWRDM_FUNC_PWRST_CSWR] = 150,
>>> +             [PWRDM_FUNC_PWRST_ON] = 0,
>>> +     },
>>>  };
>>
>> A summary about where the latency numbers for each powerdomain come from
>> would be useful.
> Ok
>
>>
>> [...]
>>
>>> @@ -87,64 +60,86 @@ int omap_pm_set_min_bus_tput(struct device *dev, u8 agent_id, unsigned long r)
>>>       return 0;
>>>  }
>>>
>>> +/*
>>> + * omap_pm_set_max_dev_wakeup_lat - set/release devices wake-up latency
>>> + * constraints
>>> + */
>>>  int omap_pm_set_max_dev_wakeup_lat(struct device *req_dev, struct device *dev,
>>>                                  long t)
>>>  {
>>> +     struct platform_device *pdev, *req_pdev;
>>> +     int ret = 0;
>>> +
>>>       if (!req_dev || !dev || t < -1) {
>>>               WARN(1, "OMAP PM: %s: invalid parameter(s)", __func__);
>>>               return -EINVAL;
>>> +     }
>>> +
>>> +     /* Look for the platform devices */
>>> +     pdev = to_platform_device(dev);
>>> +     req_pdev = to_platform_device(req_dev);
>>> +
>>> +     /* Try to catch non platform devices. */
>>> +     if ((pdev->name == NULL) || (req_pdev->name == NULL)) {
>>> +             pr_err("OMAP-PM set_wakeup_lat: Error: platform devices "
>>> +                    "not valid\n");
>>> +             return -EINVAL;
>>> +     } else {
>>> +             /* Call the omap_device API */
>>> +             ret = omap_device_set_max_dev_wakeup_lat(req_pdev, pdev, t);
>>> +     }
>>
>> I don't think a NULL name check is the right sanity check here.  WHat
>> you really need to know is whether the target device is an omap_device.
>> The requesting device can be anything (I think.)
>>
>> Here's a simpler check:
>>
>>        if (pdev->dev->parent == &omap_device_parent)
>>                ret = omap_device_set_max_dev_wakeup_lat(req_pdev, pdev, t);
>>        else
>>                ...
> Ok I will use this test. Note: the dev parameter is already checked
> for NULL above.
>
> Also I think the requester parameter needs to keep the device* type
> across all layers. In any case only this type is used by the low level
> code (in powerdomain.c). This allows for any device (i.e. that does
> not necessarily have an associated omap_device) to request
> constraints.
> What do you think?

I agree that the only the target device needs to be an omap_device, the
requester can be any device.

Kevin
--
To unsubscribe from this list: send the line "unsubscribe linux-omap" in
the body of a message to majordomo@vger.kernel.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html

^ permalink raw reply	[flat|nested] 19+ messages in thread

* [PATCH 2/2] OMAP: PM: implement devices wakeup latency constraints APIs
@ 2011-03-08 17:33         ` Kevin Hilman
  0 siblings, 0 replies; 19+ messages in thread
From: Kevin Hilman @ 2011-03-08 17:33 UTC (permalink / raw)
  To: linux-arm-kernel

Jean Pihet <jean.pihet@newoldbits.com> writes:

> On Tue, Mar 8, 2011 at 3:15 AM, Kevin Hilman <khilman@ti.com> wrote:
>> Jean Pihet <jean.pihet@newoldbits.com> writes:
>>
>>> Implement OMAP PM layer omap_pm_set_max_dev_wakeup_lat API by
>>> creating similar APIs at the omap_device and omap_hwmod levels. The
>>> omap_hwmod level call is the layer with access to the powerdomain
>>> core, so it is the place where the powerdomain is queried to set and
>>> release the constraints.
>>>
>>> NOTE: only works for devices which have been converted to use
>>> ? ? ? omap_device/omap_hwmod.
>>>
>>> Longer term, we could possibly remove this API from the OMAP PM layer,
>>> and instead directly use the device level API.
>>>
>>> The power domains get the next power state programmed directly in
>>> the registers via pwrdm_wakeuplat_update_pwrst.
>>>
>>> Note about PM QOS: the MPU and CORE power domains get the next power
>>> state via cpuidle, which get the strongest wake-up latency constraint
>>> by querying PM QOS. The usage of PM QOS is temporary, until a generic
>>> solution is in place.
>>>
>>> Based on Vibhore's original patch, adapted to omap_device, omap_hwmod
>>> and PM QOS frameworks.
>>
>> I haven't got to a detailed review of this code yet, but did do some
>> experiements and have some general comments on the code/design to get
>> started.
>>
>> Also, I had a problem doing a real dumb test until I figured out the
>> problem with the init sequence. ?I tried setting a constraint in the
>> device init code for UART (mach-omap2/serial.c:omap_serial_init_port()),
>> and then I realized that that runs before
>> mach-omap2/pm34xx.c:pwrdms_setup() which also calls
>> omap_set_pwrdm_state() for each powerdomain to initialize.
>
> Do we need to change the behavior at init?
>

I think so.  

At a minimum, it should be documented somewhere that the constraints API
cannot be used before pwrdms_setup().  But since that happens much later
in the boot process than device init code, that might be too strict.

>> Also, for debug purposes, it might be useful to have a per-device sysfs
>> interface to setting this wakeup latency constraint. ?Something like
>>
>> ? /sys/devices/platform/omap/.../power/wakeup_latency
> I do not see this as a debug interface but rather an API to the user space.
> If that is the case there are some issues to deal with, cf. below.

By debug, I was thinking under '#ifdef CONFIG_PM_DEBUG'.  Even if you
did this as a separate patch that we did not try to get upstream, it
would be extremely valuable for testing this framework.

>> I'm not sure exactly what to set the requesting device to though.
> And also, how to track the requesters? PM QOS is using a /dev node
> that must be kept open as long as the constraints remains valid.

A dummy device could be created for all sysfs requesters since you would
want subsequent writes to override  previous ones.

>> As far as implementation goes, you've so far implemented only wakeup
>> latencies, but not througput. ?When you implement throughput you will
>> have to duplicate large parts of this code and data structures for
>> throughput, and if ever add some other constraint (frequency, voltage)
>> it would need to be duplicated again.
>>
>> Maybe now is the time to consider an interface to add a generic
>> per-device constraint, with a type (latency, throughput, etc.), or
>> "class" as it's called in PM QoS. ?For now the type/class does not need
>> to be exposed externally, but will make implementing new constraint
>> types much easer.
> Ok that makes sense.
> In the current patch the constraints plist is stored inside the
> powerdomain structure. This does not apply to throughput, frequency
> and voltage constraints.
> Should the list constraint be managed in the OMAP PM layer instead of
> in the power domain code? If so where to store the constraints?

I was thinking at the omap_device layer, where there would be a
constraint plist managed for each class of constraint.

>> Some other comments below...
>>
>>> Signed-off-by: Jean Pihet <j-pihet@ti.com>
>>> Cc: Vibhore Vardhan <vvardhan@ti.com>
>>> ---
>>> Based on khilman's pm-core branch
>>>
>>> ?arch/arm/mach-omap2/omap_hwmod.c ? ? ? ? ? ? ?| ? 62 ++++++++-
>>> ?arch/arm/mach-omap2/powerdomain.c ? ? ? ? ? ? | ?197 +++++++++++++++++++++++++
>>> ?arch/arm/mach-omap2/powerdomain.h ? ? ? ? ? ? | ? 39 +++++-
>>> ?arch/arm/mach-omap2/powerdomains3xxx_data.c ? | ? 60 ++++++++
>>> ?arch/arm/plat-omap/include/plat/omap_device.h | ? ?2 +
>>> ?arch/arm/plat-omap/include/plat/omap_hwmod.h ?| ? ?2 +
>>> ?arch/arm/plat-omap/omap-pm-constraints.c ? ? ?| ?121 +++++++--------
>>> ?arch/arm/plat-omap/omap_device.c ? ? ? ? ? ? ?| ? 28 ++++
>>> ?8 files changed, 446 insertions(+), 65 deletions(-)
>>>
>>> diff --git a/arch/arm/mach-omap2/omap_hwmod.c b/arch/arm/mach-omap2/omap_hwmod.c
>>> index 028efda..bad8248 100644
>>> --- a/arch/arm/mach-omap2/omap_hwmod.c
>>> +++ b/arch/arm/mach-omap2/omap_hwmod.c
>>> @@ -142,6 +142,7 @@
>>> ?#include "powerdomain.h"
>>> ?#include <plat/clock.h>
>>> ?#include <plat/omap_hwmod.h>
>>> +#include <plat/omap_device.h>
>>> ?#include <plat/prcm.h>
>>>
>>> ?#include "cm2xxx_3xxx.h"
>>> @@ -2267,10 +2268,69 @@ ohsps_unlock:
>>> ?}
>>>
>>> ?/**
>>> + * omap_hwmod_set_max_dev_wakeup_lat - set a device wake-up constraint
>>> + * @oh: the device of @oh to set a constraint on.
>>> + * @req_oh: the device of @req_oh is the requester of the constraint.
>>> + * @t: wakeup latency constraint (us). -1 removes the existing constraint.
>>> + *
>>> + * Query the powerdomain of @oh to set/release the wake-up constraint.
>>> + * @oh is used to determine which power domain to set a constraint on.
>>> + * @req_oh is used to record the requester for later update or removal
>>> + * of a constraint.
>>> + *
>>> + * Returns -EINVAL if @oh or @req_oh have no power domain, or the return
>>> + * code from the pwrdm function (pwrdm_wakeuplat_set/release_constraint)
>>> + * of the powerdomain assocated with @oh.
>>> + */
>>> +int omap_hwmod_set_max_dev_wakeup_lat(struct omap_hwmod *req_oh,
>>> + ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? struct omap_hwmod *oh, long t)
>>> +{
>>> + ? ? struct device *req_dev;
>>> + ? ? struct platform_device *req_pdev;
>>> + ? ? struct powerdomain *pwrdm;
>>> +
>>> + ? ? pwrdm = omap_hwmod_get_pwrdm(oh);
>>> +
>>> + ? ? /* Catch devices with undefined powerdomains */
>>> + ? ? if (!PTR_ERR(pwrdm)) {
>>> + ? ? ? ? ? ? pr_err("omap_hwmod: Error: could not find parent "
>>> + ? ? ? ? ? ? ? ? ? ? "powerdomain for %s\n", oh->name);
>>> + ? ? ? ? ? ? return -EINVAL;
>>> + ? ? }
>>> +
>>> + ? ? req_pdev = &(req_oh->od->pdev);
>>> + ? ? if (!PTR_ERR(req_pdev)) {
>>> + ? ? ? ? ? ? pr_err("omap_hwmod: Error: pdev not found for oh %s\n",
>>> + ? ? ? ? ? ? ? ? ? ?oh->name);
>>> + ? ? ? ? ? ? return -EINVAL;
>>> + ? ? }
>>> +
>>> + ? ? req_dev = &(req_pdev->dev);
>>> + ? ? if (!PTR_ERR(req_dev)) {
>>> + ? ? ? ? ? ? pr_err("omap_hwmod: Error: device not found for oh %s\n",
>>> + ? ? ? ? ? ? ? ? ? ?oh->name);
>>> + ? ? ? ? ? ? return -EINVAL;
>>> + ? ? }
>>> +
>>> + ? ? /* Call set/release_constraint for the given pwrdm */
>>> + ? ? if (t == -1) {
>>> + ? ? ? ? ? ? pr_debug("omap_hwmod: remove max device latency constraint: "
>>> + ? ? ? ? ? ? ? ? ? ? ?"oh %s, pwrdm %s, req by oh %s\n",
>>> + ? ? ? ? ? ? ? ? ? ? ?oh->name, pwrdm->name, req_oh->name);
>>> + ? ? } else {
>>> + ? ? ? ? ? ? pr_debug("omap_hwmod: add max device latency constraint: "
>>> + ? ? ? ? ? ? ? ? ? ? ?"oh %s, t = %ld usec, pwrdm %s, req by oh %s\n",
>>> + ? ? ? ? ? ? ? ? ? ? ?oh->name, t, pwrdm->name, req_oh->name);
>>> + ? ? }
>>> +
>>> + ? ? return pwrdm_wakeuplat_set_constraint(pwrdm, req_dev, t);
>>> +}
>>> +
>>> +/**
>>> ? * omap_hwmod_get_context_loss_count - get lost context count
>>> ? * @oh: struct omap_hwmod *
>>> ? *
>>> - * Query the powerdomain of of @oh to get the context loss
>>> + * Query the powerdomain of @oh to get the context loss
>>> ? * count for this device.
>>> ? *
>>> ? * Returns the context loss count of the powerdomain assocated with @oh
>>> diff --git a/arch/arm/mach-omap2/powerdomain.c b/arch/arm/mach-omap2/powerdomain.c
>>> index eaed0df..6fb4741 100644
>>> --- a/arch/arm/mach-omap2/powerdomain.c
>>> +++ b/arch/arm/mach-omap2/powerdomain.c
>>> @@ -19,6 +19,8 @@
>>> ?#include <linux/list.h>
>>> ?#include <linux/errno.h>
>>> ?#include <linux/string.h>
>>> +#include <linux/slab.h>
>>> +
>>> ?#include "cm2xxx_3xxx.h"
>>> ?#include "prcm44xx.h"
>>> ?#include "cm44xx.h"
>>> @@ -103,6 +105,13 @@ static int _pwrdm_register(struct powerdomain *pwrdm)
>>> ? ? ? pwrdm->state = pwrdm_read_pwrst(pwrdm);
>>> ? ? ? pwrdm->state_counter[pwrdm->state] = 1;
>>>
>>> + ? ? /* Initialize priority ordered list for wakeup latency constraint */
>>> + ? ? spin_lock_init(&pwrdm->wakeuplat_lock);
>>> + ? ? plist_head_init(&pwrdm->wakeuplat_dev_list, &pwrdm->wakeuplat_lock);
>>> +
>>> + ? ? /* res_mutex protects res_list add and del ops */
>>> + ? ? mutex_init(&pwrdm->wakeuplat_mutex);
>>> +
>>> ? ? ? pr_debug("powerdomain: registered %s\n", pwrdm->name);
>>>
>>> ? ? ? return 0;
>>> @@ -176,6 +185,74 @@ static int _pwrdm_post_transition_cb(struct powerdomain *pwrdm, void *unused)
>>> ? ? ? return 0;
>>> ?}
>>>
>>> +/**
>>> + * pwrdm_wakeuplat_update_pwrst - Update power domain power state if needed
>>> + * @pwrdm: struct powerdomain * to which requesting device belongs to.
>>> + *
>>> + * Finds the minimum allowed wake-up latency value from all entries
>>> + * in the list and the power domain power state needing the constraint.
>>> + * Programs a new target state if it is different from current power state.
>>> + *
>>> + * Only OMAP3xxx is supported for now
>>> + *
>>> + * Returns 0 upon success.
>>> + */
>>> +static int pwrdm_wakeuplat_update_pwrst(struct powerdomain *pwrdm)
>>> +{
>>> + ? ? struct plist_node *node;
>>> + ? ? int ret = 0, new_state;
>>> + ? ? long min_latency = -1;
>>> +
>>> + ? ? /* Find the strongest constraint from the plist */
>>> + ? ? if (!plist_head_empty(&pwrdm->wakeuplat_dev_list)) {
>>> + ? ? ? ? ? ? node = plist_first(&pwrdm->wakeuplat_dev_list);
>>> + ? ? ? ? ? ? min_latency = node->prio;
>>> + ? ? }
>>> +
>>> + ? ? /* Find power state with wakeup latency < minimum constraint. */
>>> + ? ? for (new_state = 0x0; new_state < PWRDM_MAX_PWRSTS; new_state++) {
>>> + ? ? ? ? ? ? if (min_latency == -1 ||
>>> + ? ? ? ? ? ? ? ? pwrdm->wakeup_lat[new_state] <= min_latency)
>>> + ? ? ? ? ? ? ? ? ? ? break;
>>> + ? ? }
>>> +
>>> + ? ? switch (new_state) {
>>> + ? ? case PWRDM_FUNC_PWRST_OFF:
>>> + ? ? ? ? ? ? new_state = PWRDM_POWER_OFF;
>>> + ? ? ? ? ? ? break;
>>> + ? ? case PWRDM_FUNC_PWRST_OSWR:
>>> + ? ? ? ? ? ? if (cpu_is_omap34xx())
>>> + ? ? ? ? ? ? ? ? ? ? pwrdm_set_logic_retst(pwrdm, PWRDM_POWER_OFF);
>>
>> cpu_is_* checks here aren't right.
>>
>> You should use SoC specific function pointers as are done for many of the
>> other powerdomain calls after Rajendra's splitup series.
> The cpu_is_* tests are not related to the SoC specific function
> pointers but rather to the comment in the function description 'Only
> OMAP3xxx is supported for now'.
> In fact the function that are conditionally called
> (pwrdm_set_logic_retst and omap_set_pwrdm_state) are correctly using
> the SoC specific pointers.
>
> Is this test needed? If so is it the correct way to do it?

Right, I think you can just drop the cpu_is_* checks here since as you
pointed out, the called functions already handle SoC specifics.

>>
>>> + ? ? ? ? ? ? new_state = PWRDM_POWER_RET;
>>> + ? ? ? ? ? ? break;
>>> + ? ? case PWRDM_FUNC_PWRST_CSWR:
>>> + ? ? ? ? ? ? if (cpu_is_omap34xx())
>>> + ? ? ? ? ? ? ? ? ? ? pwrdm_set_logic_retst(pwrdm, PWRDM_POWER_RET);
>>> + ? ? ? ? ? ? new_state = PWRDM_POWER_RET;
>>> + ? ? ? ? ? ? break;
>>> + ? ? case PWRDM_FUNC_PWRST_ON:
>>> + ? ? ? ? ? ? new_state = PWRDM_POWER_ON;
>>> + ? ? ? ? ? ? break;
>>> + ? ? default:
>>> + ? ? ? ? ? ? pr_warn("powerdomain: requested latency constraint not "
>>> + ? ? ? ? ? ? ? ? ? ? "supported %s set to ON state\n", pwrdm->name);
>>> + ? ? ? ? ? ? new_state = PWRDM_POWER_ON;
>>> + ? ? ? ? ? ? break;
>>> + ? ? }
>>> +
>>> + ? ? if (pwrdm_read_pwrst(pwrdm) != new_state) {
>>> + ? ? ? ? ? ? if (cpu_is_omap34xx())
>>> + ? ? ? ? ? ? ? ? ? ? ret = omap_set_pwrdm_state(pwrdm, new_state);
>>> + ? ? }
>>> +
>>> + ? ? pr_debug("powerdomain: %s pwrst: curr=%d, prev=%d next=%d "
>>> + ? ? ? ? ? ? ?"min_latency=%ld, set_state=%d\n", pwrdm->name,
>>> + ? ? ? ? ? ? ?pwrdm_read_pwrst(pwrdm), pwrdm_read_prev_pwrst(pwrdm),
>>> + ? ? ? ? ? ? ?pwrdm_read_next_pwrst(pwrdm), min_latency, new_state);
>>> +
>>> + ? ? return ret;
>>> +}
>>> +
>>
>> [...]
>>
>>> diff --git a/arch/arm/mach-omap2/powerdomains3xxx_data.c b/arch/arm/mach-omap2/powerdomains3xxx_data.c
>>> index e1bec56..4f7e44d 100644
>>> --- a/arch/arm/mach-omap2/powerdomains3xxx_data.c
>>> +++ b/arch/arm/mach-omap2/powerdomains3xxx_data.c
>>> @@ -52,6 +52,12 @@ static struct powerdomain iva2_pwrdm = {
>>> ? ? ? ? ? ? ? [2] = PWRSTS_OFF_ON,
>>> ? ? ? ? ? ? ? [3] = PWRDM_POWER_ON,
>>> ? ? ? },
>>> + ? ? .wakeup_lat = {
>>> + ? ? ? ? ? ? [PWRDM_FUNC_PWRST_OFF] = 1100,
>>> + ? ? ? ? ? ? [PWRDM_FUNC_PWRST_OSWR] = UNSUP_STATE,
>>> + ? ? ? ? ? ? [PWRDM_FUNC_PWRST_CSWR] = 350,
>>> + ? ? ? ? ? ? [PWRDM_FUNC_PWRST_ON] = 0,
>>> + ? ? },
>>> ?};
>>>
>>> ?static struct powerdomain mpu_3xxx_pwrdm = {
>>> @@ -68,6 +74,12 @@ static struct powerdomain mpu_3xxx_pwrdm = {
>>> ? ? ? .pwrsts_mem_on ? ?= {
>>> ? ? ? ? ? ? ? [0] = PWRSTS_OFF_ON,
>>> ? ? ? },
>>> + ? ? .wakeup_lat = {
>>> + ? ? ? ? ? ? [PWRDM_FUNC_PWRST_OFF] = 95,
>>> + ? ? ? ? ? ? [PWRDM_FUNC_PWRST_OSWR] = UNSUP_STATE,
>>> + ? ? ? ? ? ? [PWRDM_FUNC_PWRST_CSWR] = 45,
>>> + ? ? ? ? ? ? [PWRDM_FUNC_PWRST_ON] = 0,
>>> + ? ? },
>>> ?};
>>>
>>> ?/*
>>> @@ -98,6 +110,12 @@ static struct powerdomain core_3xxx_pre_es3_1_pwrdm = {
>>> ? ? ? ? ? ? ? [0] = PWRSTS_OFF_RET_ON, /* MEM1ONSTATE */
>>> ? ? ? ? ? ? ? [1] = PWRSTS_OFF_RET_ON, /* MEM2ONSTATE */
>>> ? ? ? },
>>> + ? ? .wakeup_lat = {
>>> + ? ? ? ? ? ? [PWRDM_FUNC_PWRST_OFF] = 100,
>>> + ? ? ? ? ? ? [PWRDM_FUNC_PWRST_OSWR] = UNSUP_STATE,
>>> + ? ? ? ? ? ? [PWRDM_FUNC_PWRST_CSWR] = 60,
>>> + ? ? ? ? ? ? [PWRDM_FUNC_PWRST_ON] = 0,
>>> + ? ? },
>>> ?};
>>>
>>> ?static struct powerdomain core_3xxx_es3_1_pwrdm = {
>>> @@ -121,6 +139,12 @@ static struct powerdomain core_3xxx_es3_1_pwrdm = {
>>> ? ? ? ? ? ? ? [0] = PWRSTS_OFF_RET_ON, /* MEM1ONSTATE */
>>> ? ? ? ? ? ? ? [1] = PWRSTS_OFF_RET_ON, /* MEM2ONSTATE */
>>> ? ? ? },
>>> + ? ? .wakeup_lat = {
>>> + ? ? ? ? ? ? [PWRDM_FUNC_PWRST_OFF] = 100,
>>> + ? ? ? ? ? ? [PWRDM_FUNC_PWRST_OSWR] = UNSUP_STATE,
>>> + ? ? ? ? ? ? [PWRDM_FUNC_PWRST_CSWR] = 60,
>>> + ? ? ? ? ? ? [PWRDM_FUNC_PWRST_ON] = 0,
>>> + ? ? },
>>> ?};
>>>
>>> ?static struct powerdomain dss_pwrdm = {
>>> @@ -136,6 +160,12 @@ static struct powerdomain dss_pwrdm = {
>>> ? ? ? .pwrsts_mem_on ? ?= {
>>> ? ? ? ? ? ? ? [0] = PWRDM_POWER_ON, ?/* MEMONSTATE */
>>> ? ? ? },
>>> + ? ? .wakeup_lat = {
>>> + ? ? ? ? ? ? [PWRDM_FUNC_PWRST_OFF] = 70,
>>> + ? ? ? ? ? ? [PWRDM_FUNC_PWRST_OSWR] = UNSUP_STATE,
>>> + ? ? ? ? ? ? [PWRDM_FUNC_PWRST_CSWR] = 20,
>>> + ? ? ? ? ? ? [PWRDM_FUNC_PWRST_ON] = 0,
>>> + ? ? },
>>> ?};
>>>
>>> ?/*
>>> @@ -157,6 +187,12 @@ static struct powerdomain sgx_pwrdm = {
>>> ? ? ? .pwrsts_mem_on ? ?= {
>>> ? ? ? ? ? ? ? [0] = PWRDM_POWER_ON, ?/* MEMONSTATE */
>>> ? ? ? },
>>> + ? ? .wakeup_lat = {
>>> + ? ? ? ? ? ? [PWRDM_FUNC_PWRST_OFF] = 1000,
>>> + ? ? ? ? ? ? [PWRDM_FUNC_PWRST_OSWR] = UNSUP_STATE,
>>> + ? ? ? ? ? ? [PWRDM_FUNC_PWRST_CSWR] = UNSUP_STATE,
>>> + ? ? ? ? ? ? [PWRDM_FUNC_PWRST_ON] = 0,
>>> + ? ? },
>>> ?};
>>>
>>> ?static struct powerdomain cam_pwrdm = {
>>> @@ -172,6 +208,12 @@ static struct powerdomain cam_pwrdm = {
>>> ? ? ? .pwrsts_mem_on ? ?= {
>>> ? ? ? ? ? ? ? [0] = PWRDM_POWER_ON, ?/* MEMONSTATE */
>>> ? ? ? },
>>> + ? ? .wakeup_lat = {
>>> + ? ? ? ? ? ? [PWRDM_FUNC_PWRST_OFF] = 850,
>>> + ? ? ? ? ? ? [PWRDM_FUNC_PWRST_OSWR] = UNSUP_STATE,
>>> + ? ? ? ? ? ? [PWRDM_FUNC_PWRST_CSWR] = 35,
>>> + ? ? ? ? ? ? [PWRDM_FUNC_PWRST_ON] = 0,
>>> + ? ? },
>>> ?};
>>>
>>> ?static struct powerdomain per_pwrdm = {
>>> @@ -187,6 +229,12 @@ static struct powerdomain per_pwrdm = {
>>> ? ? ? .pwrsts_mem_on ? ?= {
>>> ? ? ? ? ? ? ? [0] = PWRDM_POWER_ON, ?/* MEMONSTATE */
>>> ? ? ? },
>>> + ? ? .wakeup_lat = {
>>> + ? ? ? ? ? ? [PWRDM_FUNC_PWRST_OFF] = 200,
>>> + ? ? ? ? ? ? [PWRDM_FUNC_PWRST_OSWR] = UNSUP_STATE,
>>> + ? ? ? ? ? ? [PWRDM_FUNC_PWRST_CSWR] = 110,
>>> + ? ? ? ? ? ? [PWRDM_FUNC_PWRST_ON] = 0,
>>> + ? ? },
>>> ?};
>>>
>>> ?static struct powerdomain emu_pwrdm = {
>>> @@ -201,6 +249,12 @@ static struct powerdomain neon_pwrdm = {
>>> ? ? ? .omap_chip ? ? ? ?= OMAP_CHIP_INIT(CHIP_IS_OMAP3430),
>>> ? ? ? .pwrsts ? ? ? ? ? = PWRSTS_OFF_RET_ON,
>>> ? ? ? .pwrsts_logic_ret = PWRDM_POWER_RET,
>>> + ? ? .wakeup_lat = {
>>> + ? ? ? ? ? ? [PWRDM_FUNC_PWRST_OFF] = 200,
>>> + ? ? ? ? ? ? [PWRDM_FUNC_PWRST_OSWR] = UNSUP_STATE,
>>> + ? ? ? ? ? ? [PWRDM_FUNC_PWRST_CSWR] = 35,
>>> + ? ? ? ? ? ? [PWRDM_FUNC_PWRST_ON] = 0,
>>> + ? ? },
>>> ?};
>>>
>>> ?static struct powerdomain usbhost_pwrdm = {
>>> @@ -223,6 +277,12 @@ static struct powerdomain usbhost_pwrdm = {
>>> ? ? ? .pwrsts_mem_on ? ?= {
>>> ? ? ? ? ? ? ? [0] = PWRDM_POWER_ON, ?/* MEMONSTATE */
>>> ? ? ? },
>>> + ? ? .wakeup_lat = {
>>> + ? ? ? ? ? ? [PWRDM_FUNC_PWRST_OFF] = 800,
>>> + ? ? ? ? ? ? [PWRDM_FUNC_PWRST_OSWR] = UNSUP_STATE,
>>> + ? ? ? ? ? ? [PWRDM_FUNC_PWRST_CSWR] = 150,
>>> + ? ? ? ? ? ? [PWRDM_FUNC_PWRST_ON] = 0,
>>> + ? ? },
>>> ?};
>>
>> A summary about where the latency numbers for each powerdomain come from
>> would be useful.
> Ok
>
>>
>> [...]
>>
>>> @@ -87,64 +60,86 @@ int omap_pm_set_min_bus_tput(struct device *dev, u8 agent_id, unsigned long r)
>>> ? ? ? return 0;
>>> ?}
>>>
>>> +/*
>>> + * omap_pm_set_max_dev_wakeup_lat - set/release devices wake-up latency
>>> + * constraints
>>> + */
>>> ?int omap_pm_set_max_dev_wakeup_lat(struct device *req_dev, struct device *dev,
>>> ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ?long t)
>>> ?{
>>> + ? ? struct platform_device *pdev, *req_pdev;
>>> + ? ? int ret = 0;
>>> +
>>> ? ? ? if (!req_dev || !dev || t < -1) {
>>> ? ? ? ? ? ? ? WARN(1, "OMAP PM: %s: invalid parameter(s)", __func__);
>>> ? ? ? ? ? ? ? return -EINVAL;
>>> + ? ? }
>>> +
>>> + ? ? /* Look for the platform devices */
>>> + ? ? pdev = to_platform_device(dev);
>>> + ? ? req_pdev = to_platform_device(req_dev);
>>> +
>>> + ? ? /* Try to catch non platform devices. */
>>> + ? ? if ((pdev->name == NULL) || (req_pdev->name == NULL)) {
>>> + ? ? ? ? ? ? pr_err("OMAP-PM set_wakeup_lat: Error: platform devices "
>>> + ? ? ? ? ? ? ? ? ? ?"not valid\n");
>>> + ? ? ? ? ? ? return -EINVAL;
>>> + ? ? } else {
>>> + ? ? ? ? ? ? /* Call the omap_device API */
>>> + ? ? ? ? ? ? ret = omap_device_set_max_dev_wakeup_lat(req_pdev, pdev, t);
>>> + ? ? }
>>
>> I don't think a NULL name check is the right sanity check here. ?WHat
>> you really need to know is whether the target device is an omap_device.
>> The requesting device can be anything (I think.)
>>
>> Here's a simpler check:
>>
>> ? ? ? ?if (pdev->dev->parent == &omap_device_parent)
>> ? ? ? ? ? ? ? ?ret = omap_device_set_max_dev_wakeup_lat(req_pdev, pdev, t);
>> ? ? ? ?else
>> ? ? ? ? ? ? ? ?...
> Ok I will use this test. Note: the dev parameter is already checked
> for NULL above.
>
> Also I think the requester parameter needs to keep the device* type
> across all layers. In any case only this type is used by the low level
> code (in powerdomain.c). This allows for any device (i.e. that does
> not necessarily have an associated omap_device) to request
> constraints.
> What do you think?

I agree that the only the target device needs to be an omap_device, the
requester can be any device.

Kevin

^ permalink raw reply	[flat|nested] 19+ messages in thread

* Re: [PATCH 2/2] OMAP: PM: implement devices wakeup latency constraints APIs
  2011-03-08  2:15     ` Kevin Hilman
@ 2011-03-08 15:54       ` Jean Pihet
  -1 siblings, 0 replies; 19+ messages in thread
From: Jean Pihet @ 2011-03-08 15:54 UTC (permalink / raw)
  To: Kevin Hilman; +Cc: linux-omap, linux-arm-kernel, Jean Pihet, Vibhore Vardhan

On Tue, Mar 8, 2011 at 3:15 AM, Kevin Hilman <khilman@ti.com> wrote:
> Jean Pihet <jean.pihet@newoldbits.com> writes:
>
>> Implement OMAP PM layer omap_pm_set_max_dev_wakeup_lat API by
>> creating similar APIs at the omap_device and omap_hwmod levels. The
>> omap_hwmod level call is the layer with access to the powerdomain
>> core, so it is the place where the powerdomain is queried to set and
>> release the constraints.
>>
>> NOTE: only works for devices which have been converted to use
>>       omap_device/omap_hwmod.
>>
>> Longer term, we could possibly remove this API from the OMAP PM layer,
>> and instead directly use the device level API.
>>
>> The power domains get the next power state programmed directly in
>> the registers via pwrdm_wakeuplat_update_pwrst.
>>
>> Note about PM QOS: the MPU and CORE power domains get the next power
>> state via cpuidle, which get the strongest wake-up latency constraint
>> by querying PM QOS. The usage of PM QOS is temporary, until a generic
>> solution is in place.
>>
>> Based on Vibhore's original patch, adapted to omap_device, omap_hwmod
>> and PM QOS frameworks.
>
> I haven't got to a detailed review of this code yet, but did do some
> experiements and have some general comments on the code/design to get
> started.
>
> Also, I had a problem doing a real dumb test until I figured out the
> problem with the init sequence.  I tried setting a constraint in the
> device init code for UART (mach-omap2/serial.c:omap_serial_init_port()),
> and then I realized that that runs before
> mach-omap2/pm34xx.c:pwrdms_setup() which also calls
> omap_set_pwrdm_state() for each powerdomain to initialize.
Do we need to change the behavior at init?

> Also, for debug purposes, it might be useful to have a per-device sysfs
> interface to setting this wakeup latency constraint.  Something like
>
>   /sys/devices/platform/omap/.../power/wakeup_latency
I do not see this as a debug interface but rather an API to the user space.
If that is the case there are some issues to deal with, cf. below.

> I'm not sure exactly what to set the requesting device to though.
And also, how to track the requesters? PM QOS is using a /dev node
that must be kept open as long as the constraints remains valid.

> As far as implementation goes, you've so far implemented only wakeup
> latencies, but not througput.  When you implement throughput you will
> have to duplicate large parts of this code and data structures for
> throughput, and if ever add some other constraint (frequency, voltage)
> it would need to be duplicated again.
>
> Maybe now is the time to consider an interface to add a generic
> per-device constraint, with a type (latency, throughput, etc.), or
> "class" as it's called in PM QoS.  For now the type/class does not need
> to be exposed externally, but will make implementing new constraint
> types much easer.
Ok that makes sense.
In the current patch the constraints plist is stored inside the
powerdomain structure. This does not apply to throughput, frequency
and voltage constraints.
Should the list constraint be managed in the OMAP PM layer instead of
in the power domain code? If so where to store the constraints?

> Some other comments below...
>
>> Signed-off-by: Jean Pihet <j-pihet@ti.com>
>> Cc: Vibhore Vardhan <vvardhan@ti.com>
>> ---
>> Based on khilman's pm-core branch
>>
>>  arch/arm/mach-omap2/omap_hwmod.c              |   62 ++++++++-
>>  arch/arm/mach-omap2/powerdomain.c             |  197 +++++++++++++++++++++++++
>>  arch/arm/mach-omap2/powerdomain.h             |   39 +++++-
>>  arch/arm/mach-omap2/powerdomains3xxx_data.c   |   60 ++++++++
>>  arch/arm/plat-omap/include/plat/omap_device.h |    2 +
>>  arch/arm/plat-omap/include/plat/omap_hwmod.h  |    2 +
>>  arch/arm/plat-omap/omap-pm-constraints.c      |  121 +++++++--------
>>  arch/arm/plat-omap/omap_device.c              |   28 ++++
>>  8 files changed, 446 insertions(+), 65 deletions(-)
>>
>> diff --git a/arch/arm/mach-omap2/omap_hwmod.c b/arch/arm/mach-omap2/omap_hwmod.c
>> index 028efda..bad8248 100644
>> --- a/arch/arm/mach-omap2/omap_hwmod.c
>> +++ b/arch/arm/mach-omap2/omap_hwmod.c
>> @@ -142,6 +142,7 @@
>>  #include "powerdomain.h"
>>  #include <plat/clock.h>
>>  #include <plat/omap_hwmod.h>
>> +#include <plat/omap_device.h>
>>  #include <plat/prcm.h>
>>
>>  #include "cm2xxx_3xxx.h"
>> @@ -2267,10 +2268,69 @@ ohsps_unlock:
>>  }
>>
>>  /**
>> + * omap_hwmod_set_max_dev_wakeup_lat - set a device wake-up constraint
>> + * @oh: the device of @oh to set a constraint on.
>> + * @req_oh: the device of @req_oh is the requester of the constraint.
>> + * @t: wakeup latency constraint (us). -1 removes the existing constraint.
>> + *
>> + * Query the powerdomain of @oh to set/release the wake-up constraint.
>> + * @oh is used to determine which power domain to set a constraint on.
>> + * @req_oh is used to record the requester for later update or removal
>> + * of a constraint.
>> + *
>> + * Returns -EINVAL if @oh or @req_oh have no power domain, or the return
>> + * code from the pwrdm function (pwrdm_wakeuplat_set/release_constraint)
>> + * of the powerdomain assocated with @oh.
>> + */
>> +int omap_hwmod_set_max_dev_wakeup_lat(struct omap_hwmod *req_oh,
>> +                                   struct omap_hwmod *oh, long t)
>> +{
>> +     struct device *req_dev;
>> +     struct platform_device *req_pdev;
>> +     struct powerdomain *pwrdm;
>> +
>> +     pwrdm = omap_hwmod_get_pwrdm(oh);
>> +
>> +     /* Catch devices with undefined powerdomains */
>> +     if (!PTR_ERR(pwrdm)) {
>> +             pr_err("omap_hwmod: Error: could not find parent "
>> +                     "powerdomain for %s\n", oh->name);
>> +             return -EINVAL;
>> +     }
>> +
>> +     req_pdev = &(req_oh->od->pdev);
>> +     if (!PTR_ERR(req_pdev)) {
>> +             pr_err("omap_hwmod: Error: pdev not found for oh %s\n",
>> +                    oh->name);
>> +             return -EINVAL;
>> +     }
>> +
>> +     req_dev = &(req_pdev->dev);
>> +     if (!PTR_ERR(req_dev)) {
>> +             pr_err("omap_hwmod: Error: device not found for oh %s\n",
>> +                    oh->name);
>> +             return -EINVAL;
>> +     }
>> +
>> +     /* Call set/release_constraint for the given pwrdm */
>> +     if (t == -1) {
>> +             pr_debug("omap_hwmod: remove max device latency constraint: "
>> +                      "oh %s, pwrdm %s, req by oh %s\n",
>> +                      oh->name, pwrdm->name, req_oh->name);
>> +     } else {
>> +             pr_debug("omap_hwmod: add max device latency constraint: "
>> +                      "oh %s, t = %ld usec, pwrdm %s, req by oh %s\n",
>> +                      oh->name, t, pwrdm->name, req_oh->name);
>> +     }
>> +
>> +     return pwrdm_wakeuplat_set_constraint(pwrdm, req_dev, t);
>> +}
>> +
>> +/**
>>   * omap_hwmod_get_context_loss_count - get lost context count
>>   * @oh: struct omap_hwmod *
>>   *
>> - * Query the powerdomain of of @oh to get the context loss
>> + * Query the powerdomain of @oh to get the context loss
>>   * count for this device.
>>   *
>>   * Returns the context loss count of the powerdomain assocated with @oh
>> diff --git a/arch/arm/mach-omap2/powerdomain.c b/arch/arm/mach-omap2/powerdomain.c
>> index eaed0df..6fb4741 100644
>> --- a/arch/arm/mach-omap2/powerdomain.c
>> +++ b/arch/arm/mach-omap2/powerdomain.c
>> @@ -19,6 +19,8 @@
>>  #include <linux/list.h>
>>  #include <linux/errno.h>
>>  #include <linux/string.h>
>> +#include <linux/slab.h>
>> +
>>  #include "cm2xxx_3xxx.h"
>>  #include "prcm44xx.h"
>>  #include "cm44xx.h"
>> @@ -103,6 +105,13 @@ static int _pwrdm_register(struct powerdomain *pwrdm)
>>       pwrdm->state = pwrdm_read_pwrst(pwrdm);
>>       pwrdm->state_counter[pwrdm->state] = 1;
>>
>> +     /* Initialize priority ordered list for wakeup latency constraint */
>> +     spin_lock_init(&pwrdm->wakeuplat_lock);
>> +     plist_head_init(&pwrdm->wakeuplat_dev_list, &pwrdm->wakeuplat_lock);
>> +
>> +     /* res_mutex protects res_list add and del ops */
>> +     mutex_init(&pwrdm->wakeuplat_mutex);
>> +
>>       pr_debug("powerdomain: registered %s\n", pwrdm->name);
>>
>>       return 0;
>> @@ -176,6 +185,74 @@ static int _pwrdm_post_transition_cb(struct powerdomain *pwrdm, void *unused)
>>       return 0;
>>  }
>>
>> +/**
>> + * pwrdm_wakeuplat_update_pwrst - Update power domain power state if needed
>> + * @pwrdm: struct powerdomain * to which requesting device belongs to.
>> + *
>> + * Finds the minimum allowed wake-up latency value from all entries
>> + * in the list and the power domain power state needing the constraint.
>> + * Programs a new target state if it is different from current power state.
>> + *
>> + * Only OMAP3xxx is supported for now
>> + *
>> + * Returns 0 upon success.
>> + */
>> +static int pwrdm_wakeuplat_update_pwrst(struct powerdomain *pwrdm)
>> +{
>> +     struct plist_node *node;
>> +     int ret = 0, new_state;
>> +     long min_latency = -1;
>> +
>> +     /* Find the strongest constraint from the plist */
>> +     if (!plist_head_empty(&pwrdm->wakeuplat_dev_list)) {
>> +             node = plist_first(&pwrdm->wakeuplat_dev_list);
>> +             min_latency = node->prio;
>> +     }
>> +
>> +     /* Find power state with wakeup latency < minimum constraint. */
>> +     for (new_state = 0x0; new_state < PWRDM_MAX_PWRSTS; new_state++) {
>> +             if (min_latency == -1 ||
>> +                 pwrdm->wakeup_lat[new_state] <= min_latency)
>> +                     break;
>> +     }
>> +
>> +     switch (new_state) {
>> +     case PWRDM_FUNC_PWRST_OFF:
>> +             new_state = PWRDM_POWER_OFF;
>> +             break;
>> +     case PWRDM_FUNC_PWRST_OSWR:
>> +             if (cpu_is_omap34xx())
>> +                     pwrdm_set_logic_retst(pwrdm, PWRDM_POWER_OFF);
>
> cpu_is_* checks here aren't right.
>
> You should use SoC specific function pointers as are done for many of the
> other powerdomain calls after Rajendra's splitup series.
The cpu_is_* tests are not related to the SoC specific function
pointers but rather to the comment in the function description 'Only
OMAP3xxx is supported for now'.
In fact the function that are conditionally called
(pwrdm_set_logic_retst and omap_set_pwrdm_state) are correctly using
the SoC specific pointers.

Is this test needed? If so is it the correct way to do it?

>
>> +             new_state = PWRDM_POWER_RET;
>> +             break;
>> +     case PWRDM_FUNC_PWRST_CSWR:
>> +             if (cpu_is_omap34xx())
>> +                     pwrdm_set_logic_retst(pwrdm, PWRDM_POWER_RET);
>> +             new_state = PWRDM_POWER_RET;
>> +             break;
>> +     case PWRDM_FUNC_PWRST_ON:
>> +             new_state = PWRDM_POWER_ON;
>> +             break;
>> +     default:
>> +             pr_warn("powerdomain: requested latency constraint not "
>> +                     "supported %s set to ON state\n", pwrdm->name);
>> +             new_state = PWRDM_POWER_ON;
>> +             break;
>> +     }
>> +
>> +     if (pwrdm_read_pwrst(pwrdm) != new_state) {
>> +             if (cpu_is_omap34xx())
>> +                     ret = omap_set_pwrdm_state(pwrdm, new_state);
>> +     }
>> +
>> +     pr_debug("powerdomain: %s pwrst: curr=%d, prev=%d next=%d "
>> +              "min_latency=%ld, set_state=%d\n", pwrdm->name,
>> +              pwrdm_read_pwrst(pwrdm), pwrdm_read_prev_pwrst(pwrdm),
>> +              pwrdm_read_next_pwrst(pwrdm), min_latency, new_state);
>> +
>> +     return ret;
>> +}
>> +
>
> [...]
>
>> diff --git a/arch/arm/mach-omap2/powerdomains3xxx_data.c b/arch/arm/mach-omap2/powerdomains3xxx_data.c
>> index e1bec56..4f7e44d 100644
>> --- a/arch/arm/mach-omap2/powerdomains3xxx_data.c
>> +++ b/arch/arm/mach-omap2/powerdomains3xxx_data.c
>> @@ -52,6 +52,12 @@ static struct powerdomain iva2_pwrdm = {
>>               [2] = PWRSTS_OFF_ON,
>>               [3] = PWRDM_POWER_ON,
>>       },
>> +     .wakeup_lat = {
>> +             [PWRDM_FUNC_PWRST_OFF] = 1100,
>> +             [PWRDM_FUNC_PWRST_OSWR] = UNSUP_STATE,
>> +             [PWRDM_FUNC_PWRST_CSWR] = 350,
>> +             [PWRDM_FUNC_PWRST_ON] = 0,
>> +     },
>>  };
>>
>>  static struct powerdomain mpu_3xxx_pwrdm = {
>> @@ -68,6 +74,12 @@ static struct powerdomain mpu_3xxx_pwrdm = {
>>       .pwrsts_mem_on    = {
>>               [0] = PWRSTS_OFF_ON,
>>       },
>> +     .wakeup_lat = {
>> +             [PWRDM_FUNC_PWRST_OFF] = 95,
>> +             [PWRDM_FUNC_PWRST_OSWR] = UNSUP_STATE,
>> +             [PWRDM_FUNC_PWRST_CSWR] = 45,
>> +             [PWRDM_FUNC_PWRST_ON] = 0,
>> +     },
>>  };
>>
>>  /*
>> @@ -98,6 +110,12 @@ static struct powerdomain core_3xxx_pre_es3_1_pwrdm = {
>>               [0] = PWRSTS_OFF_RET_ON, /* MEM1ONSTATE */
>>               [1] = PWRSTS_OFF_RET_ON, /* MEM2ONSTATE */
>>       },
>> +     .wakeup_lat = {
>> +             [PWRDM_FUNC_PWRST_OFF] = 100,
>> +             [PWRDM_FUNC_PWRST_OSWR] = UNSUP_STATE,
>> +             [PWRDM_FUNC_PWRST_CSWR] = 60,
>> +             [PWRDM_FUNC_PWRST_ON] = 0,
>> +     },
>>  };
>>
>>  static struct powerdomain core_3xxx_es3_1_pwrdm = {
>> @@ -121,6 +139,12 @@ static struct powerdomain core_3xxx_es3_1_pwrdm = {
>>               [0] = PWRSTS_OFF_RET_ON, /* MEM1ONSTATE */
>>               [1] = PWRSTS_OFF_RET_ON, /* MEM2ONSTATE */
>>       },
>> +     .wakeup_lat = {
>> +             [PWRDM_FUNC_PWRST_OFF] = 100,
>> +             [PWRDM_FUNC_PWRST_OSWR] = UNSUP_STATE,
>> +             [PWRDM_FUNC_PWRST_CSWR] = 60,
>> +             [PWRDM_FUNC_PWRST_ON] = 0,
>> +     },
>>  };
>>
>>  static struct powerdomain dss_pwrdm = {
>> @@ -136,6 +160,12 @@ static struct powerdomain dss_pwrdm = {
>>       .pwrsts_mem_on    = {
>>               [0] = PWRDM_POWER_ON,  /* MEMONSTATE */
>>       },
>> +     .wakeup_lat = {
>> +             [PWRDM_FUNC_PWRST_OFF] = 70,
>> +             [PWRDM_FUNC_PWRST_OSWR] = UNSUP_STATE,
>> +             [PWRDM_FUNC_PWRST_CSWR] = 20,
>> +             [PWRDM_FUNC_PWRST_ON] = 0,
>> +     },
>>  };
>>
>>  /*
>> @@ -157,6 +187,12 @@ static struct powerdomain sgx_pwrdm = {
>>       .pwrsts_mem_on    = {
>>               [0] = PWRDM_POWER_ON,  /* MEMONSTATE */
>>       },
>> +     .wakeup_lat = {
>> +             [PWRDM_FUNC_PWRST_OFF] = 1000,
>> +             [PWRDM_FUNC_PWRST_OSWR] = UNSUP_STATE,
>> +             [PWRDM_FUNC_PWRST_CSWR] = UNSUP_STATE,
>> +             [PWRDM_FUNC_PWRST_ON] = 0,
>> +     },
>>  };
>>
>>  static struct powerdomain cam_pwrdm = {
>> @@ -172,6 +208,12 @@ static struct powerdomain cam_pwrdm = {
>>       .pwrsts_mem_on    = {
>>               [0] = PWRDM_POWER_ON,  /* MEMONSTATE */
>>       },
>> +     .wakeup_lat = {
>> +             [PWRDM_FUNC_PWRST_OFF] = 850,
>> +             [PWRDM_FUNC_PWRST_OSWR] = UNSUP_STATE,
>> +             [PWRDM_FUNC_PWRST_CSWR] = 35,
>> +             [PWRDM_FUNC_PWRST_ON] = 0,
>> +     },
>>  };
>>
>>  static struct powerdomain per_pwrdm = {
>> @@ -187,6 +229,12 @@ static struct powerdomain per_pwrdm = {
>>       .pwrsts_mem_on    = {
>>               [0] = PWRDM_POWER_ON,  /* MEMONSTATE */
>>       },
>> +     .wakeup_lat = {
>> +             [PWRDM_FUNC_PWRST_OFF] = 200,
>> +             [PWRDM_FUNC_PWRST_OSWR] = UNSUP_STATE,
>> +             [PWRDM_FUNC_PWRST_CSWR] = 110,
>> +             [PWRDM_FUNC_PWRST_ON] = 0,
>> +     },
>>  };
>>
>>  static struct powerdomain emu_pwrdm = {
>> @@ -201,6 +249,12 @@ static struct powerdomain neon_pwrdm = {
>>       .omap_chip        = OMAP_CHIP_INIT(CHIP_IS_OMAP3430),
>>       .pwrsts           = PWRSTS_OFF_RET_ON,
>>       .pwrsts_logic_ret = PWRDM_POWER_RET,
>> +     .wakeup_lat = {
>> +             [PWRDM_FUNC_PWRST_OFF] = 200,
>> +             [PWRDM_FUNC_PWRST_OSWR] = UNSUP_STATE,
>> +             [PWRDM_FUNC_PWRST_CSWR] = 35,
>> +             [PWRDM_FUNC_PWRST_ON] = 0,
>> +     },
>>  };
>>
>>  static struct powerdomain usbhost_pwrdm = {
>> @@ -223,6 +277,12 @@ static struct powerdomain usbhost_pwrdm = {
>>       .pwrsts_mem_on    = {
>>               [0] = PWRDM_POWER_ON,  /* MEMONSTATE */
>>       },
>> +     .wakeup_lat = {
>> +             [PWRDM_FUNC_PWRST_OFF] = 800,
>> +             [PWRDM_FUNC_PWRST_OSWR] = UNSUP_STATE,
>> +             [PWRDM_FUNC_PWRST_CSWR] = 150,
>> +             [PWRDM_FUNC_PWRST_ON] = 0,
>> +     },
>>  };
>
> A summary about where the latency numbers for each powerdomain come from
> would be useful.
Ok

>
> [...]
>
>> @@ -87,64 +60,86 @@ int omap_pm_set_min_bus_tput(struct device *dev, u8 agent_id, unsigned long r)
>>       return 0;
>>  }
>>
>> +/*
>> + * omap_pm_set_max_dev_wakeup_lat - set/release devices wake-up latency
>> + * constraints
>> + */
>>  int omap_pm_set_max_dev_wakeup_lat(struct device *req_dev, struct device *dev,
>>                                  long t)
>>  {
>> +     struct platform_device *pdev, *req_pdev;
>> +     int ret = 0;
>> +
>>       if (!req_dev || !dev || t < -1) {
>>               WARN(1, "OMAP PM: %s: invalid parameter(s)", __func__);
>>               return -EINVAL;
>> +     }
>> +
>> +     /* Look for the platform devices */
>> +     pdev = to_platform_device(dev);
>> +     req_pdev = to_platform_device(req_dev);
>> +
>> +     /* Try to catch non platform devices. */
>> +     if ((pdev->name == NULL) || (req_pdev->name == NULL)) {
>> +             pr_err("OMAP-PM set_wakeup_lat: Error: platform devices "
>> +                    "not valid\n");
>> +             return -EINVAL;
>> +     } else {
>> +             /* Call the omap_device API */
>> +             ret = omap_device_set_max_dev_wakeup_lat(req_pdev, pdev, t);
>> +     }
>
> I don't think a NULL name check is the right sanity check here.  WHat
> you really need to know is whether the target device is an omap_device.
> The requesting device can be anything (I think.)
>
> Here's a simpler check:
>
>        if (pdev->dev->parent == &omap_device_parent)
>                ret = omap_device_set_max_dev_wakeup_lat(req_pdev, pdev, t);
>        else
>                ...
Ok I will use this test. Note: the dev parameter is already checked
for NULL above.

Also I think the requester parameter needs to keep the device* type
across all layers. In any case only this type is used by the low level
code (in powerdomain.c). This allows for any device (i.e. that does
not necessarily have an associated omap_device) to request
constraints.
What do you think?

>
>
>> +     return ret;
>> +}
>
> Kevin
>

Thanks for reviewing the code.

Jean
--
To unsubscribe from this list: send the line "unsubscribe linux-omap" in
the body of a message to majordomo@vger.kernel.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html

^ permalink raw reply	[flat|nested] 19+ messages in thread

* [PATCH 2/2] OMAP: PM: implement devices wakeup latency constraints APIs
@ 2011-03-08 15:54       ` Jean Pihet
  0 siblings, 0 replies; 19+ messages in thread
From: Jean Pihet @ 2011-03-08 15:54 UTC (permalink / raw)
  To: linux-arm-kernel

On Tue, Mar 8, 2011 at 3:15 AM, Kevin Hilman <khilman@ti.com> wrote:
> Jean Pihet <jean.pihet@newoldbits.com> writes:
>
>> Implement OMAP PM layer omap_pm_set_max_dev_wakeup_lat API by
>> creating similar APIs at the omap_device and omap_hwmod levels. The
>> omap_hwmod level call is the layer with access to the powerdomain
>> core, so it is the place where the powerdomain is queried to set and
>> release the constraints.
>>
>> NOTE: only works for devices which have been converted to use
>> ? ? ? omap_device/omap_hwmod.
>>
>> Longer term, we could possibly remove this API from the OMAP PM layer,
>> and instead directly use the device level API.
>>
>> The power domains get the next power state programmed directly in
>> the registers via pwrdm_wakeuplat_update_pwrst.
>>
>> Note about PM QOS: the MPU and CORE power domains get the next power
>> state via cpuidle, which get the strongest wake-up latency constraint
>> by querying PM QOS. The usage of PM QOS is temporary, until a generic
>> solution is in place.
>>
>> Based on Vibhore's original patch, adapted to omap_device, omap_hwmod
>> and PM QOS frameworks.
>
> I haven't got to a detailed review of this code yet, but did do some
> experiements and have some general comments on the code/design to get
> started.
>
> Also, I had a problem doing a real dumb test until I figured out the
> problem with the init sequence. ?I tried setting a constraint in the
> device init code for UART (mach-omap2/serial.c:omap_serial_init_port()),
> and then I realized that that runs before
> mach-omap2/pm34xx.c:pwrdms_setup() which also calls
> omap_set_pwrdm_state() for each powerdomain to initialize.
Do we need to change the behavior at init?

> Also, for debug purposes, it might be useful to have a per-device sysfs
> interface to setting this wakeup latency constraint. ?Something like
>
> ? /sys/devices/platform/omap/.../power/wakeup_latency
I do not see this as a debug interface but rather an API to the user space.
If that is the case there are some issues to deal with, cf. below.

> I'm not sure exactly what to set the requesting device to though.
And also, how to track the requesters? PM QOS is using a /dev node
that must be kept open as long as the constraints remains valid.

> As far as implementation goes, you've so far implemented only wakeup
> latencies, but not througput. ?When you implement throughput you will
> have to duplicate large parts of this code and data structures for
> throughput, and if ever add some other constraint (frequency, voltage)
> it would need to be duplicated again.
>
> Maybe now is the time to consider an interface to add a generic
> per-device constraint, with a type (latency, throughput, etc.), or
> "class" as it's called in PM QoS. ?For now the type/class does not need
> to be exposed externally, but will make implementing new constraint
> types much easer.
Ok that makes sense.
In the current patch the constraints plist is stored inside the
powerdomain structure. This does not apply to throughput, frequency
and voltage constraints.
Should the list constraint be managed in the OMAP PM layer instead of
in the power domain code? If so where to store the constraints?

> Some other comments below...
>
>> Signed-off-by: Jean Pihet <j-pihet@ti.com>
>> Cc: Vibhore Vardhan <vvardhan@ti.com>
>> ---
>> Based on khilman's pm-core branch
>>
>> ?arch/arm/mach-omap2/omap_hwmod.c ? ? ? ? ? ? ?| ? 62 ++++++++-
>> ?arch/arm/mach-omap2/powerdomain.c ? ? ? ? ? ? | ?197 +++++++++++++++++++++++++
>> ?arch/arm/mach-omap2/powerdomain.h ? ? ? ? ? ? | ? 39 +++++-
>> ?arch/arm/mach-omap2/powerdomains3xxx_data.c ? | ? 60 ++++++++
>> ?arch/arm/plat-omap/include/plat/omap_device.h | ? ?2 +
>> ?arch/arm/plat-omap/include/plat/omap_hwmod.h ?| ? ?2 +
>> ?arch/arm/plat-omap/omap-pm-constraints.c ? ? ?| ?121 +++++++--------
>> ?arch/arm/plat-omap/omap_device.c ? ? ? ? ? ? ?| ? 28 ++++
>> ?8 files changed, 446 insertions(+), 65 deletions(-)
>>
>> diff --git a/arch/arm/mach-omap2/omap_hwmod.c b/arch/arm/mach-omap2/omap_hwmod.c
>> index 028efda..bad8248 100644
>> --- a/arch/arm/mach-omap2/omap_hwmod.c
>> +++ b/arch/arm/mach-omap2/omap_hwmod.c
>> @@ -142,6 +142,7 @@
>> ?#include "powerdomain.h"
>> ?#include <plat/clock.h>
>> ?#include <plat/omap_hwmod.h>
>> +#include <plat/omap_device.h>
>> ?#include <plat/prcm.h>
>>
>> ?#include "cm2xxx_3xxx.h"
>> @@ -2267,10 +2268,69 @@ ohsps_unlock:
>> ?}
>>
>> ?/**
>> + * omap_hwmod_set_max_dev_wakeup_lat - set a device wake-up constraint
>> + * @oh: the device of @oh to set a constraint on.
>> + * @req_oh: the device of @req_oh is the requester of the constraint.
>> + * @t: wakeup latency constraint (us). -1 removes the existing constraint.
>> + *
>> + * Query the powerdomain of @oh to set/release the wake-up constraint.
>> + * @oh is used to determine which power domain to set a constraint on.
>> + * @req_oh is used to record the requester for later update or removal
>> + * of a constraint.
>> + *
>> + * Returns -EINVAL if @oh or @req_oh have no power domain, or the return
>> + * code from the pwrdm function (pwrdm_wakeuplat_set/release_constraint)
>> + * of the powerdomain assocated with @oh.
>> + */
>> +int omap_hwmod_set_max_dev_wakeup_lat(struct omap_hwmod *req_oh,
>> + ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? struct omap_hwmod *oh, long t)
>> +{
>> + ? ? struct device *req_dev;
>> + ? ? struct platform_device *req_pdev;
>> + ? ? struct powerdomain *pwrdm;
>> +
>> + ? ? pwrdm = omap_hwmod_get_pwrdm(oh);
>> +
>> + ? ? /* Catch devices with undefined powerdomains */
>> + ? ? if (!PTR_ERR(pwrdm)) {
>> + ? ? ? ? ? ? pr_err("omap_hwmod: Error: could not find parent "
>> + ? ? ? ? ? ? ? ? ? ? "powerdomain for %s\n", oh->name);
>> + ? ? ? ? ? ? return -EINVAL;
>> + ? ? }
>> +
>> + ? ? req_pdev = &(req_oh->od->pdev);
>> + ? ? if (!PTR_ERR(req_pdev)) {
>> + ? ? ? ? ? ? pr_err("omap_hwmod: Error: pdev not found for oh %s\n",
>> + ? ? ? ? ? ? ? ? ? ?oh->name);
>> + ? ? ? ? ? ? return -EINVAL;
>> + ? ? }
>> +
>> + ? ? req_dev = &(req_pdev->dev);
>> + ? ? if (!PTR_ERR(req_dev)) {
>> + ? ? ? ? ? ? pr_err("omap_hwmod: Error: device not found for oh %s\n",
>> + ? ? ? ? ? ? ? ? ? ?oh->name);
>> + ? ? ? ? ? ? return -EINVAL;
>> + ? ? }
>> +
>> + ? ? /* Call set/release_constraint for the given pwrdm */
>> + ? ? if (t == -1) {
>> + ? ? ? ? ? ? pr_debug("omap_hwmod: remove max device latency constraint: "
>> + ? ? ? ? ? ? ? ? ? ? ?"oh %s, pwrdm %s, req by oh %s\n",
>> + ? ? ? ? ? ? ? ? ? ? ?oh->name, pwrdm->name, req_oh->name);
>> + ? ? } else {
>> + ? ? ? ? ? ? pr_debug("omap_hwmod: add max device latency constraint: "
>> + ? ? ? ? ? ? ? ? ? ? ?"oh %s, t = %ld usec, pwrdm %s, req by oh %s\n",
>> + ? ? ? ? ? ? ? ? ? ? ?oh->name, t, pwrdm->name, req_oh->name);
>> + ? ? }
>> +
>> + ? ? return pwrdm_wakeuplat_set_constraint(pwrdm, req_dev, t);
>> +}
>> +
>> +/**
>> ? * omap_hwmod_get_context_loss_count - get lost context count
>> ? * @oh: struct omap_hwmod *
>> ? *
>> - * Query the powerdomain of of @oh to get the context loss
>> + * Query the powerdomain of @oh to get the context loss
>> ? * count for this device.
>> ? *
>> ? * Returns the context loss count of the powerdomain assocated with @oh
>> diff --git a/arch/arm/mach-omap2/powerdomain.c b/arch/arm/mach-omap2/powerdomain.c
>> index eaed0df..6fb4741 100644
>> --- a/arch/arm/mach-omap2/powerdomain.c
>> +++ b/arch/arm/mach-omap2/powerdomain.c
>> @@ -19,6 +19,8 @@
>> ?#include <linux/list.h>
>> ?#include <linux/errno.h>
>> ?#include <linux/string.h>
>> +#include <linux/slab.h>
>> +
>> ?#include "cm2xxx_3xxx.h"
>> ?#include "prcm44xx.h"
>> ?#include "cm44xx.h"
>> @@ -103,6 +105,13 @@ static int _pwrdm_register(struct powerdomain *pwrdm)
>> ? ? ? pwrdm->state = pwrdm_read_pwrst(pwrdm);
>> ? ? ? pwrdm->state_counter[pwrdm->state] = 1;
>>
>> + ? ? /* Initialize priority ordered list for wakeup latency constraint */
>> + ? ? spin_lock_init(&pwrdm->wakeuplat_lock);
>> + ? ? plist_head_init(&pwrdm->wakeuplat_dev_list, &pwrdm->wakeuplat_lock);
>> +
>> + ? ? /* res_mutex protects res_list add and del ops */
>> + ? ? mutex_init(&pwrdm->wakeuplat_mutex);
>> +
>> ? ? ? pr_debug("powerdomain: registered %s\n", pwrdm->name);
>>
>> ? ? ? return 0;
>> @@ -176,6 +185,74 @@ static int _pwrdm_post_transition_cb(struct powerdomain *pwrdm, void *unused)
>> ? ? ? return 0;
>> ?}
>>
>> +/**
>> + * pwrdm_wakeuplat_update_pwrst - Update power domain power state if needed
>> + * @pwrdm: struct powerdomain * to which requesting device belongs to.
>> + *
>> + * Finds the minimum allowed wake-up latency value from all entries
>> + * in the list and the power domain power state needing the constraint.
>> + * Programs a new target state if it is different from current power state.
>> + *
>> + * Only OMAP3xxx is supported for now
>> + *
>> + * Returns 0 upon success.
>> + */
>> +static int pwrdm_wakeuplat_update_pwrst(struct powerdomain *pwrdm)
>> +{
>> + ? ? struct plist_node *node;
>> + ? ? int ret = 0, new_state;
>> + ? ? long min_latency = -1;
>> +
>> + ? ? /* Find the strongest constraint from the plist */
>> + ? ? if (!plist_head_empty(&pwrdm->wakeuplat_dev_list)) {
>> + ? ? ? ? ? ? node = plist_first(&pwrdm->wakeuplat_dev_list);
>> + ? ? ? ? ? ? min_latency = node->prio;
>> + ? ? }
>> +
>> + ? ? /* Find power state with wakeup latency < minimum constraint. */
>> + ? ? for (new_state = 0x0; new_state < PWRDM_MAX_PWRSTS; new_state++) {
>> + ? ? ? ? ? ? if (min_latency == -1 ||
>> + ? ? ? ? ? ? ? ? pwrdm->wakeup_lat[new_state] <= min_latency)
>> + ? ? ? ? ? ? ? ? ? ? break;
>> + ? ? }
>> +
>> + ? ? switch (new_state) {
>> + ? ? case PWRDM_FUNC_PWRST_OFF:
>> + ? ? ? ? ? ? new_state = PWRDM_POWER_OFF;
>> + ? ? ? ? ? ? break;
>> + ? ? case PWRDM_FUNC_PWRST_OSWR:
>> + ? ? ? ? ? ? if (cpu_is_omap34xx())
>> + ? ? ? ? ? ? ? ? ? ? pwrdm_set_logic_retst(pwrdm, PWRDM_POWER_OFF);
>
> cpu_is_* checks here aren't right.
>
> You should use SoC specific function pointers as are done for many of the
> other powerdomain calls after Rajendra's splitup series.
The cpu_is_* tests are not related to the SoC specific function
pointers but rather to the comment in the function description 'Only
OMAP3xxx is supported for now'.
In fact the function that are conditionally called
(pwrdm_set_logic_retst and omap_set_pwrdm_state) are correctly using
the SoC specific pointers.

Is this test needed? If so is it the correct way to do it?

>
>> + ? ? ? ? ? ? new_state = PWRDM_POWER_RET;
>> + ? ? ? ? ? ? break;
>> + ? ? case PWRDM_FUNC_PWRST_CSWR:
>> + ? ? ? ? ? ? if (cpu_is_omap34xx())
>> + ? ? ? ? ? ? ? ? ? ? pwrdm_set_logic_retst(pwrdm, PWRDM_POWER_RET);
>> + ? ? ? ? ? ? new_state = PWRDM_POWER_RET;
>> + ? ? ? ? ? ? break;
>> + ? ? case PWRDM_FUNC_PWRST_ON:
>> + ? ? ? ? ? ? new_state = PWRDM_POWER_ON;
>> + ? ? ? ? ? ? break;
>> + ? ? default:
>> + ? ? ? ? ? ? pr_warn("powerdomain: requested latency constraint not "
>> + ? ? ? ? ? ? ? ? ? ? "supported %s set to ON state\n", pwrdm->name);
>> + ? ? ? ? ? ? new_state = PWRDM_POWER_ON;
>> + ? ? ? ? ? ? break;
>> + ? ? }
>> +
>> + ? ? if (pwrdm_read_pwrst(pwrdm) != new_state) {
>> + ? ? ? ? ? ? if (cpu_is_omap34xx())
>> + ? ? ? ? ? ? ? ? ? ? ret = omap_set_pwrdm_state(pwrdm, new_state);
>> + ? ? }
>> +
>> + ? ? pr_debug("powerdomain: %s pwrst: curr=%d, prev=%d next=%d "
>> + ? ? ? ? ? ? ?"min_latency=%ld, set_state=%d\n", pwrdm->name,
>> + ? ? ? ? ? ? ?pwrdm_read_pwrst(pwrdm), pwrdm_read_prev_pwrst(pwrdm),
>> + ? ? ? ? ? ? ?pwrdm_read_next_pwrst(pwrdm), min_latency, new_state);
>> +
>> + ? ? return ret;
>> +}
>> +
>
> [...]
>
>> diff --git a/arch/arm/mach-omap2/powerdomains3xxx_data.c b/arch/arm/mach-omap2/powerdomains3xxx_data.c
>> index e1bec56..4f7e44d 100644
>> --- a/arch/arm/mach-omap2/powerdomains3xxx_data.c
>> +++ b/arch/arm/mach-omap2/powerdomains3xxx_data.c
>> @@ -52,6 +52,12 @@ static struct powerdomain iva2_pwrdm = {
>> ? ? ? ? ? ? ? [2] = PWRSTS_OFF_ON,
>> ? ? ? ? ? ? ? [3] = PWRDM_POWER_ON,
>> ? ? ? },
>> + ? ? .wakeup_lat = {
>> + ? ? ? ? ? ? [PWRDM_FUNC_PWRST_OFF] = 1100,
>> + ? ? ? ? ? ? [PWRDM_FUNC_PWRST_OSWR] = UNSUP_STATE,
>> + ? ? ? ? ? ? [PWRDM_FUNC_PWRST_CSWR] = 350,
>> + ? ? ? ? ? ? [PWRDM_FUNC_PWRST_ON] = 0,
>> + ? ? },
>> ?};
>>
>> ?static struct powerdomain mpu_3xxx_pwrdm = {
>> @@ -68,6 +74,12 @@ static struct powerdomain mpu_3xxx_pwrdm = {
>> ? ? ? .pwrsts_mem_on ? ?= {
>> ? ? ? ? ? ? ? [0] = PWRSTS_OFF_ON,
>> ? ? ? },
>> + ? ? .wakeup_lat = {
>> + ? ? ? ? ? ? [PWRDM_FUNC_PWRST_OFF] = 95,
>> + ? ? ? ? ? ? [PWRDM_FUNC_PWRST_OSWR] = UNSUP_STATE,
>> + ? ? ? ? ? ? [PWRDM_FUNC_PWRST_CSWR] = 45,
>> + ? ? ? ? ? ? [PWRDM_FUNC_PWRST_ON] = 0,
>> + ? ? },
>> ?};
>>
>> ?/*
>> @@ -98,6 +110,12 @@ static struct powerdomain core_3xxx_pre_es3_1_pwrdm = {
>> ? ? ? ? ? ? ? [0] = PWRSTS_OFF_RET_ON, /* MEM1ONSTATE */
>> ? ? ? ? ? ? ? [1] = PWRSTS_OFF_RET_ON, /* MEM2ONSTATE */
>> ? ? ? },
>> + ? ? .wakeup_lat = {
>> + ? ? ? ? ? ? [PWRDM_FUNC_PWRST_OFF] = 100,
>> + ? ? ? ? ? ? [PWRDM_FUNC_PWRST_OSWR] = UNSUP_STATE,
>> + ? ? ? ? ? ? [PWRDM_FUNC_PWRST_CSWR] = 60,
>> + ? ? ? ? ? ? [PWRDM_FUNC_PWRST_ON] = 0,
>> + ? ? },
>> ?};
>>
>> ?static struct powerdomain core_3xxx_es3_1_pwrdm = {
>> @@ -121,6 +139,12 @@ static struct powerdomain core_3xxx_es3_1_pwrdm = {
>> ? ? ? ? ? ? ? [0] = PWRSTS_OFF_RET_ON, /* MEM1ONSTATE */
>> ? ? ? ? ? ? ? [1] = PWRSTS_OFF_RET_ON, /* MEM2ONSTATE */
>> ? ? ? },
>> + ? ? .wakeup_lat = {
>> + ? ? ? ? ? ? [PWRDM_FUNC_PWRST_OFF] = 100,
>> + ? ? ? ? ? ? [PWRDM_FUNC_PWRST_OSWR] = UNSUP_STATE,
>> + ? ? ? ? ? ? [PWRDM_FUNC_PWRST_CSWR] = 60,
>> + ? ? ? ? ? ? [PWRDM_FUNC_PWRST_ON] = 0,
>> + ? ? },
>> ?};
>>
>> ?static struct powerdomain dss_pwrdm = {
>> @@ -136,6 +160,12 @@ static struct powerdomain dss_pwrdm = {
>> ? ? ? .pwrsts_mem_on ? ?= {
>> ? ? ? ? ? ? ? [0] = PWRDM_POWER_ON, ?/* MEMONSTATE */
>> ? ? ? },
>> + ? ? .wakeup_lat = {
>> + ? ? ? ? ? ? [PWRDM_FUNC_PWRST_OFF] = 70,
>> + ? ? ? ? ? ? [PWRDM_FUNC_PWRST_OSWR] = UNSUP_STATE,
>> + ? ? ? ? ? ? [PWRDM_FUNC_PWRST_CSWR] = 20,
>> + ? ? ? ? ? ? [PWRDM_FUNC_PWRST_ON] = 0,
>> + ? ? },
>> ?};
>>
>> ?/*
>> @@ -157,6 +187,12 @@ static struct powerdomain sgx_pwrdm = {
>> ? ? ? .pwrsts_mem_on ? ?= {
>> ? ? ? ? ? ? ? [0] = PWRDM_POWER_ON, ?/* MEMONSTATE */
>> ? ? ? },
>> + ? ? .wakeup_lat = {
>> + ? ? ? ? ? ? [PWRDM_FUNC_PWRST_OFF] = 1000,
>> + ? ? ? ? ? ? [PWRDM_FUNC_PWRST_OSWR] = UNSUP_STATE,
>> + ? ? ? ? ? ? [PWRDM_FUNC_PWRST_CSWR] = UNSUP_STATE,
>> + ? ? ? ? ? ? [PWRDM_FUNC_PWRST_ON] = 0,
>> + ? ? },
>> ?};
>>
>> ?static struct powerdomain cam_pwrdm = {
>> @@ -172,6 +208,12 @@ static struct powerdomain cam_pwrdm = {
>> ? ? ? .pwrsts_mem_on ? ?= {
>> ? ? ? ? ? ? ? [0] = PWRDM_POWER_ON, ?/* MEMONSTATE */
>> ? ? ? },
>> + ? ? .wakeup_lat = {
>> + ? ? ? ? ? ? [PWRDM_FUNC_PWRST_OFF] = 850,
>> + ? ? ? ? ? ? [PWRDM_FUNC_PWRST_OSWR] = UNSUP_STATE,
>> + ? ? ? ? ? ? [PWRDM_FUNC_PWRST_CSWR] = 35,
>> + ? ? ? ? ? ? [PWRDM_FUNC_PWRST_ON] = 0,
>> + ? ? },
>> ?};
>>
>> ?static struct powerdomain per_pwrdm = {
>> @@ -187,6 +229,12 @@ static struct powerdomain per_pwrdm = {
>> ? ? ? .pwrsts_mem_on ? ?= {
>> ? ? ? ? ? ? ? [0] = PWRDM_POWER_ON, ?/* MEMONSTATE */
>> ? ? ? },
>> + ? ? .wakeup_lat = {
>> + ? ? ? ? ? ? [PWRDM_FUNC_PWRST_OFF] = 200,
>> + ? ? ? ? ? ? [PWRDM_FUNC_PWRST_OSWR] = UNSUP_STATE,
>> + ? ? ? ? ? ? [PWRDM_FUNC_PWRST_CSWR] = 110,
>> + ? ? ? ? ? ? [PWRDM_FUNC_PWRST_ON] = 0,
>> + ? ? },
>> ?};
>>
>> ?static struct powerdomain emu_pwrdm = {
>> @@ -201,6 +249,12 @@ static struct powerdomain neon_pwrdm = {
>> ? ? ? .omap_chip ? ? ? ?= OMAP_CHIP_INIT(CHIP_IS_OMAP3430),
>> ? ? ? .pwrsts ? ? ? ? ? = PWRSTS_OFF_RET_ON,
>> ? ? ? .pwrsts_logic_ret = PWRDM_POWER_RET,
>> + ? ? .wakeup_lat = {
>> + ? ? ? ? ? ? [PWRDM_FUNC_PWRST_OFF] = 200,
>> + ? ? ? ? ? ? [PWRDM_FUNC_PWRST_OSWR] = UNSUP_STATE,
>> + ? ? ? ? ? ? [PWRDM_FUNC_PWRST_CSWR] = 35,
>> + ? ? ? ? ? ? [PWRDM_FUNC_PWRST_ON] = 0,
>> + ? ? },
>> ?};
>>
>> ?static struct powerdomain usbhost_pwrdm = {
>> @@ -223,6 +277,12 @@ static struct powerdomain usbhost_pwrdm = {
>> ? ? ? .pwrsts_mem_on ? ?= {
>> ? ? ? ? ? ? ? [0] = PWRDM_POWER_ON, ?/* MEMONSTATE */
>> ? ? ? },
>> + ? ? .wakeup_lat = {
>> + ? ? ? ? ? ? [PWRDM_FUNC_PWRST_OFF] = 800,
>> + ? ? ? ? ? ? [PWRDM_FUNC_PWRST_OSWR] = UNSUP_STATE,
>> + ? ? ? ? ? ? [PWRDM_FUNC_PWRST_CSWR] = 150,
>> + ? ? ? ? ? ? [PWRDM_FUNC_PWRST_ON] = 0,
>> + ? ? },
>> ?};
>
> A summary about where the latency numbers for each powerdomain come from
> would be useful.
Ok

>
> [...]
>
>> @@ -87,64 +60,86 @@ int omap_pm_set_min_bus_tput(struct device *dev, u8 agent_id, unsigned long r)
>> ? ? ? return 0;
>> ?}
>>
>> +/*
>> + * omap_pm_set_max_dev_wakeup_lat - set/release devices wake-up latency
>> + * constraints
>> + */
>> ?int omap_pm_set_max_dev_wakeup_lat(struct device *req_dev, struct device *dev,
>> ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ?long t)
>> ?{
>> + ? ? struct platform_device *pdev, *req_pdev;
>> + ? ? int ret = 0;
>> +
>> ? ? ? if (!req_dev || !dev || t < -1) {
>> ? ? ? ? ? ? ? WARN(1, "OMAP PM: %s: invalid parameter(s)", __func__);
>> ? ? ? ? ? ? ? return -EINVAL;
>> + ? ? }
>> +
>> + ? ? /* Look for the platform devices */
>> + ? ? pdev = to_platform_device(dev);
>> + ? ? req_pdev = to_platform_device(req_dev);
>> +
>> + ? ? /* Try to catch non platform devices. */
>> + ? ? if ((pdev->name == NULL) || (req_pdev->name == NULL)) {
>> + ? ? ? ? ? ? pr_err("OMAP-PM set_wakeup_lat: Error: platform devices "
>> + ? ? ? ? ? ? ? ? ? ?"not valid\n");
>> + ? ? ? ? ? ? return -EINVAL;
>> + ? ? } else {
>> + ? ? ? ? ? ? /* Call the omap_device API */
>> + ? ? ? ? ? ? ret = omap_device_set_max_dev_wakeup_lat(req_pdev, pdev, t);
>> + ? ? }
>
> I don't think a NULL name check is the right sanity check here. ?WHat
> you really need to know is whether the target device is an omap_device.
> The requesting device can be anything (I think.)
>
> Here's a simpler check:
>
> ? ? ? ?if (pdev->dev->parent == &omap_device_parent)
> ? ? ? ? ? ? ? ?ret = omap_device_set_max_dev_wakeup_lat(req_pdev, pdev, t);
> ? ? ? ?else
> ? ? ? ? ? ? ? ?...
Ok I will use this test. Note: the dev parameter is already checked
for NULL above.

Also I think the requester parameter needs to keep the device* type
across all layers. In any case only this type is used by the low level
code (in powerdomain.c). This allows for any device (i.e. that does
not necessarily have an associated omap_device) to request
constraints.
What do you think?

>
>
>> + ? ? return ret;
>> +}
>
> Kevin
>

Thanks for reviewing the code.

Jean

^ permalink raw reply	[flat|nested] 19+ messages in thread

* Re: [PATCH 2/2] OMAP: PM: implement devices wakeup latency constraints APIs
  2011-03-04 14:52   ` Jean Pihet
@ 2011-03-08  2:15     ` Kevin Hilman
  -1 siblings, 0 replies; 19+ messages in thread
From: Kevin Hilman @ 2011-03-08  2:15 UTC (permalink / raw)
  To: Jean Pihet; +Cc: linux-omap, linux-arm-kernel, Jean Pihet, Vibhore Vardhan

Jean Pihet <jean.pihet@newoldbits.com> writes:

> Implement OMAP PM layer omap_pm_set_max_dev_wakeup_lat API by
> creating similar APIs at the omap_device and omap_hwmod levels. The
> omap_hwmod level call is the layer with access to the powerdomain
> core, so it is the place where the powerdomain is queried to set and
> release the constraints.
>
> NOTE: only works for devices which have been converted to use
>       omap_device/omap_hwmod.
>
> Longer term, we could possibly remove this API from the OMAP PM layer,
> and instead directly use the device level API.
>
> The power domains get the next power state programmed directly in
> the registers via pwrdm_wakeuplat_update_pwrst.
>
> Note about PM QOS: the MPU and CORE power domains get the next power
> state via cpuidle, which get the strongest wake-up latency constraint
> by querying PM QOS. The usage of PM QOS is temporary, until a generic
> solution is in place.
>
> Based on Vibhore's original patch, adapted to omap_device, omap_hwmod
> and PM QOS frameworks.

I haven't got to a detailed review of this code yet, but did do some
experiements and have some general comments on the code/design to get
started.

Also, I had a problem doing a real dumb test until I figured out the
problem with the init sequence.  I tried setting a constraint in the
device init code for UART (mach-omap2/serial.c:omap_serial_init_port()),
and then I realized that that runs before
mach-omap2/pm34xx.c:pwrdms_setup() which also calls
omap_set_pwrdm_state() for each powerdomain to initialize.

Also, for debug purposes, it might be useful to have a per-device sysfs
interface to setting this wakeup latency constraint.  Something like

   /sys/devices/platform/omap/.../power/wakeup_latency

I'm not sure exactly what to set the requesting device to though.  

As far as implementation goes, you've so far implemented only wakeup
latencies, but not througput.  When you implement throughput you will
have to duplicate large parts of this code and data structures for
throughput, and if ever add some other constraint (frequency, voltage)
it would need to be duplicated again.

Maybe now is the time to consider an interface to add a generic
per-device constraint, with a type (latency, throughput, etc.), or
"class" as it's called in PM QoS.  For now the type/class does not need
to be exposed externally, but will make implementing new constraint
types much easer.

Some other comments below...

> Signed-off-by: Jean Pihet <j-pihet@ti.com>
> Cc: Vibhore Vardhan <vvardhan@ti.com>
> ---
> Based on khilman's pm-core branch
>
>  arch/arm/mach-omap2/omap_hwmod.c              |   62 ++++++++-
>  arch/arm/mach-omap2/powerdomain.c             |  197 +++++++++++++++++++++++++
>  arch/arm/mach-omap2/powerdomain.h             |   39 +++++-
>  arch/arm/mach-omap2/powerdomains3xxx_data.c   |   60 ++++++++
>  arch/arm/plat-omap/include/plat/omap_device.h |    2 +
>  arch/arm/plat-omap/include/plat/omap_hwmod.h  |    2 +
>  arch/arm/plat-omap/omap-pm-constraints.c      |  121 +++++++--------
>  arch/arm/plat-omap/omap_device.c              |   28 ++++
>  8 files changed, 446 insertions(+), 65 deletions(-)
>
> diff --git a/arch/arm/mach-omap2/omap_hwmod.c b/arch/arm/mach-omap2/omap_hwmod.c
> index 028efda..bad8248 100644
> --- a/arch/arm/mach-omap2/omap_hwmod.c
> +++ b/arch/arm/mach-omap2/omap_hwmod.c
> @@ -142,6 +142,7 @@
>  #include "powerdomain.h"
>  #include <plat/clock.h>
>  #include <plat/omap_hwmod.h>
> +#include <plat/omap_device.h>
>  #include <plat/prcm.h>
>  
>  #include "cm2xxx_3xxx.h"
> @@ -2267,10 +2268,69 @@ ohsps_unlock:
>  }
>  
>  /**
> + * omap_hwmod_set_max_dev_wakeup_lat - set a device wake-up constraint
> + * @oh: the device of @oh to set a constraint on.
> + * @req_oh: the device of @req_oh is the requester of the constraint.
> + * @t: wakeup latency constraint (us). -1 removes the existing constraint.
> + *
> + * Query the powerdomain of @oh to set/release the wake-up constraint.
> + * @oh is used to determine which power domain to set a constraint on.
> + * @req_oh is used to record the requester for later update or removal
> + * of a constraint.
> + *
> + * Returns -EINVAL if @oh or @req_oh have no power domain, or the return
> + * code from the pwrdm function (pwrdm_wakeuplat_set/release_constraint)
> + * of the powerdomain assocated with @oh.
> + */
> +int omap_hwmod_set_max_dev_wakeup_lat(struct omap_hwmod *req_oh,
> +				      struct omap_hwmod *oh, long t)
> +{
> +	struct device *req_dev;
> +	struct platform_device *req_pdev;
> +	struct powerdomain *pwrdm;
> +
> +	pwrdm = omap_hwmod_get_pwrdm(oh);
> +
> +	/* Catch devices with undefined powerdomains */
> +	if (!PTR_ERR(pwrdm)) {
> +		pr_err("omap_hwmod: Error: could not find parent "
> +			"powerdomain for %s\n", oh->name);
> +		return -EINVAL;
> +	}
> +
> +	req_pdev = &(req_oh->od->pdev);
> +	if (!PTR_ERR(req_pdev)) {
> +		pr_err("omap_hwmod: Error: pdev not found for oh %s\n",
> +		       oh->name);
> +		return -EINVAL;
> +	}
> +
> +	req_dev = &(req_pdev->dev);
> +	if (!PTR_ERR(req_dev)) {
> +		pr_err("omap_hwmod: Error: device not found for oh %s\n",
> +		       oh->name);
> +		return -EINVAL;
> +	}
> +
> +	/* Call set/release_constraint for the given pwrdm */
> +	if (t == -1) {
> +		pr_debug("omap_hwmod: remove max device latency constraint: "
> +			 "oh %s, pwrdm %s, req by oh %s\n",
> +			 oh->name, pwrdm->name, req_oh->name);
> +	} else {
> +		pr_debug("omap_hwmod: add max device latency constraint: "
> +			 "oh %s, t = %ld usec, pwrdm %s, req by oh %s\n",
> +			 oh->name, t, pwrdm->name, req_oh->name);
> +	}
> +
> +	return pwrdm_wakeuplat_set_constraint(pwrdm, req_dev, t);
> +}
> +
> +/**
>   * omap_hwmod_get_context_loss_count - get lost context count
>   * @oh: struct omap_hwmod *
>   *
> - * Query the powerdomain of of @oh to get the context loss
> + * Query the powerdomain of @oh to get the context loss
>   * count for this device.
>   *
>   * Returns the context loss count of the powerdomain assocated with @oh
> diff --git a/arch/arm/mach-omap2/powerdomain.c b/arch/arm/mach-omap2/powerdomain.c
> index eaed0df..6fb4741 100644
> --- a/arch/arm/mach-omap2/powerdomain.c
> +++ b/arch/arm/mach-omap2/powerdomain.c
> @@ -19,6 +19,8 @@
>  #include <linux/list.h>
>  #include <linux/errno.h>
>  #include <linux/string.h>
> +#include <linux/slab.h>
> +
>  #include "cm2xxx_3xxx.h"
>  #include "prcm44xx.h"
>  #include "cm44xx.h"
> @@ -103,6 +105,13 @@ static int _pwrdm_register(struct powerdomain *pwrdm)
>  	pwrdm->state = pwrdm_read_pwrst(pwrdm);
>  	pwrdm->state_counter[pwrdm->state] = 1;
>  
> +	/* Initialize priority ordered list for wakeup latency constraint */
> +	spin_lock_init(&pwrdm->wakeuplat_lock);
> +	plist_head_init(&pwrdm->wakeuplat_dev_list, &pwrdm->wakeuplat_lock);
> +
> +	/* res_mutex protects res_list add and del ops */
> +	mutex_init(&pwrdm->wakeuplat_mutex);
> +
>  	pr_debug("powerdomain: registered %s\n", pwrdm->name);
>  
>  	return 0;
> @@ -176,6 +185,74 @@ static int _pwrdm_post_transition_cb(struct powerdomain *pwrdm, void *unused)
>  	return 0;
>  }
>  
> +/**
> + * pwrdm_wakeuplat_update_pwrst - Update power domain power state if needed
> + * @pwrdm: struct powerdomain * to which requesting device belongs to.
> + *
> + * Finds the minimum allowed wake-up latency value from all entries
> + * in the list and the power domain power state needing the constraint.
> + * Programs a new target state if it is different from current power state.
> + *
> + * Only OMAP3xxx is supported for now
> + *
> + * Returns 0 upon success.
> + */
> +static int pwrdm_wakeuplat_update_pwrst(struct powerdomain *pwrdm)
> +{
> +	struct plist_node *node;
> +	int ret = 0, new_state;
> +	long min_latency = -1;
> +
> +	/* Find the strongest constraint from the plist */
> +	if (!plist_head_empty(&pwrdm->wakeuplat_dev_list)) {
> +		node = plist_first(&pwrdm->wakeuplat_dev_list);
> +		min_latency = node->prio;
> +	}
> +
> +	/* Find power state with wakeup latency < minimum constraint. */
> +	for (new_state = 0x0; new_state < PWRDM_MAX_PWRSTS; new_state++) {
> +		if (min_latency == -1 ||
> +		    pwrdm->wakeup_lat[new_state] <= min_latency)
> +			break;
> +	}
> +
> +	switch (new_state) {
> +	case PWRDM_FUNC_PWRST_OFF:
> +		new_state = PWRDM_POWER_OFF;
> +		break;
> +	case PWRDM_FUNC_PWRST_OSWR:
> +		if (cpu_is_omap34xx())
> +			pwrdm_set_logic_retst(pwrdm, PWRDM_POWER_OFF);

cpu_is_* checks here aren't right.

You should use SoC specific function pointers as are done for many of the
other powerdomain calls after Rajendra's splitup series.

> +		new_state = PWRDM_POWER_RET;
> +		break;
> +	case PWRDM_FUNC_PWRST_CSWR:
> +		if (cpu_is_omap34xx())
> +			pwrdm_set_logic_retst(pwrdm, PWRDM_POWER_RET);
> +		new_state = PWRDM_POWER_RET;
> +		break;
> +	case PWRDM_FUNC_PWRST_ON:
> +		new_state = PWRDM_POWER_ON;
> +		break;
> +	default:
> +		pr_warn("powerdomain: requested latency constraint not "
> +			"supported %s set to ON state\n", pwrdm->name);
> +		new_state = PWRDM_POWER_ON;
> +		break;
> +	}
> +
> +	if (pwrdm_read_pwrst(pwrdm) != new_state) {
> +		if (cpu_is_omap34xx())
> +			ret = omap_set_pwrdm_state(pwrdm, new_state);
> +	}
> +
> +	pr_debug("powerdomain: %s pwrst: curr=%d, prev=%d next=%d "
> +		 "min_latency=%ld, set_state=%d\n", pwrdm->name,
> +		 pwrdm_read_pwrst(pwrdm), pwrdm_read_prev_pwrst(pwrdm),
> +		 pwrdm_read_next_pwrst(pwrdm), min_latency, new_state);
> +
> +	return ret;
> +}
> +

[...]

> diff --git a/arch/arm/mach-omap2/powerdomains3xxx_data.c b/arch/arm/mach-omap2/powerdomains3xxx_data.c
> index e1bec56..4f7e44d 100644
> --- a/arch/arm/mach-omap2/powerdomains3xxx_data.c
> +++ b/arch/arm/mach-omap2/powerdomains3xxx_data.c
> @@ -52,6 +52,12 @@ static struct powerdomain iva2_pwrdm = {
>  		[2] = PWRSTS_OFF_ON,
>  		[3] = PWRDM_POWER_ON,
>  	},
> +	.wakeup_lat = {
> +		[PWRDM_FUNC_PWRST_OFF] = 1100,
> +		[PWRDM_FUNC_PWRST_OSWR] = UNSUP_STATE,
> +		[PWRDM_FUNC_PWRST_CSWR] = 350,
> +		[PWRDM_FUNC_PWRST_ON] = 0,
> +	},
>  };
>  
>  static struct powerdomain mpu_3xxx_pwrdm = {
> @@ -68,6 +74,12 @@ static struct powerdomain mpu_3xxx_pwrdm = {
>  	.pwrsts_mem_on	  = {
>  		[0] = PWRSTS_OFF_ON,
>  	},
> +	.wakeup_lat = {
> +		[PWRDM_FUNC_PWRST_OFF] = 95,
> +		[PWRDM_FUNC_PWRST_OSWR] = UNSUP_STATE,
> +		[PWRDM_FUNC_PWRST_CSWR] = 45,
> +		[PWRDM_FUNC_PWRST_ON] = 0,
> +	},
>  };
>  
>  /*
> @@ -98,6 +110,12 @@ static struct powerdomain core_3xxx_pre_es3_1_pwrdm = {
>  		[0] = PWRSTS_OFF_RET_ON, /* MEM1ONSTATE */
>  		[1] = PWRSTS_OFF_RET_ON, /* MEM2ONSTATE */
>  	},
> +	.wakeup_lat = {
> +		[PWRDM_FUNC_PWRST_OFF] = 100,
> +		[PWRDM_FUNC_PWRST_OSWR] = UNSUP_STATE,
> +		[PWRDM_FUNC_PWRST_CSWR] = 60,
> +		[PWRDM_FUNC_PWRST_ON] = 0,
> +	},
>  };
>  
>  static struct powerdomain core_3xxx_es3_1_pwrdm = {
> @@ -121,6 +139,12 @@ static struct powerdomain core_3xxx_es3_1_pwrdm = {
>  		[0] = PWRSTS_OFF_RET_ON, /* MEM1ONSTATE */
>  		[1] = PWRSTS_OFF_RET_ON, /* MEM2ONSTATE */
>  	},
> +	.wakeup_lat = {
> +		[PWRDM_FUNC_PWRST_OFF] = 100,
> +		[PWRDM_FUNC_PWRST_OSWR] = UNSUP_STATE,
> +		[PWRDM_FUNC_PWRST_CSWR] = 60,
> +		[PWRDM_FUNC_PWRST_ON] = 0,
> +	},
>  };
>  
>  static struct powerdomain dss_pwrdm = {
> @@ -136,6 +160,12 @@ static struct powerdomain dss_pwrdm = {
>  	.pwrsts_mem_on	  = {
>  		[0] = PWRDM_POWER_ON,  /* MEMONSTATE */
>  	},
> +	.wakeup_lat = {
> +		[PWRDM_FUNC_PWRST_OFF] = 70,
> +		[PWRDM_FUNC_PWRST_OSWR] = UNSUP_STATE,
> +		[PWRDM_FUNC_PWRST_CSWR] = 20,
> +		[PWRDM_FUNC_PWRST_ON] = 0,
> +	},
>  };
>  
>  /*
> @@ -157,6 +187,12 @@ static struct powerdomain sgx_pwrdm = {
>  	.pwrsts_mem_on	  = {
>  		[0] = PWRDM_POWER_ON,  /* MEMONSTATE */
>  	},
> +	.wakeup_lat = {
> +		[PWRDM_FUNC_PWRST_OFF] = 1000,
> +		[PWRDM_FUNC_PWRST_OSWR] = UNSUP_STATE,
> +		[PWRDM_FUNC_PWRST_CSWR] = UNSUP_STATE,
> +		[PWRDM_FUNC_PWRST_ON] = 0,
> +	},
>  };
>  
>  static struct powerdomain cam_pwrdm = {
> @@ -172,6 +208,12 @@ static struct powerdomain cam_pwrdm = {
>  	.pwrsts_mem_on	  = {
>  		[0] = PWRDM_POWER_ON,  /* MEMONSTATE */
>  	},
> +	.wakeup_lat = {
> +		[PWRDM_FUNC_PWRST_OFF] = 850,
> +		[PWRDM_FUNC_PWRST_OSWR] = UNSUP_STATE,
> +		[PWRDM_FUNC_PWRST_CSWR] = 35,
> +		[PWRDM_FUNC_PWRST_ON] = 0,
> +	},
>  };
>  
>  static struct powerdomain per_pwrdm = {
> @@ -187,6 +229,12 @@ static struct powerdomain per_pwrdm = {
>  	.pwrsts_mem_on	  = {
>  		[0] = PWRDM_POWER_ON,  /* MEMONSTATE */
>  	},
> +	.wakeup_lat = {
> +		[PWRDM_FUNC_PWRST_OFF] = 200,
> +		[PWRDM_FUNC_PWRST_OSWR] = UNSUP_STATE,
> +		[PWRDM_FUNC_PWRST_CSWR] = 110,
> +		[PWRDM_FUNC_PWRST_ON] = 0,
> +	},
>  };
>  
>  static struct powerdomain emu_pwrdm = {
> @@ -201,6 +249,12 @@ static struct powerdomain neon_pwrdm = {
>  	.omap_chip	  = OMAP_CHIP_INIT(CHIP_IS_OMAP3430),
>  	.pwrsts		  = PWRSTS_OFF_RET_ON,
>  	.pwrsts_logic_ret = PWRDM_POWER_RET,
> +	.wakeup_lat = {
> +		[PWRDM_FUNC_PWRST_OFF] = 200,
> +		[PWRDM_FUNC_PWRST_OSWR] = UNSUP_STATE,
> +		[PWRDM_FUNC_PWRST_CSWR] = 35,
> +		[PWRDM_FUNC_PWRST_ON] = 0,
> +	},
>  };
>  
>  static struct powerdomain usbhost_pwrdm = {
> @@ -223,6 +277,12 @@ static struct powerdomain usbhost_pwrdm = {
>  	.pwrsts_mem_on	  = {
>  		[0] = PWRDM_POWER_ON,  /* MEMONSTATE */
>  	},
> +	.wakeup_lat = {
> +		[PWRDM_FUNC_PWRST_OFF] = 800,
> +		[PWRDM_FUNC_PWRST_OSWR] = UNSUP_STATE,
> +		[PWRDM_FUNC_PWRST_CSWR] = 150,
> +		[PWRDM_FUNC_PWRST_ON] = 0,
> +	},
>  };

A summary about where the latency numbers for each powerdomain come from
would be useful.

[...]

> @@ -87,64 +60,86 @@ int omap_pm_set_min_bus_tput(struct device *dev, u8 agent_id, unsigned long r)
>  	return 0;
>  }
>  
> +/*
> + * omap_pm_set_max_dev_wakeup_lat - set/release devices wake-up latency
> + * constraints
> + */
>  int omap_pm_set_max_dev_wakeup_lat(struct device *req_dev, struct device *dev,
>  				   long t)
>  {
> +	struct platform_device *pdev, *req_pdev;
> +	int ret = 0;
> +
>  	if (!req_dev || !dev || t < -1) {
>  		WARN(1, "OMAP PM: %s: invalid parameter(s)", __func__);
>  		return -EINVAL;
> +	}
> +
> +	/* Look for the platform devices */
> +	pdev = to_platform_device(dev);
> +	req_pdev = to_platform_device(req_dev);
> +
> +	/* Try to catch non platform devices. */
> +	if ((pdev->name == NULL) || (req_pdev->name == NULL)) {
> +		pr_err("OMAP-PM set_wakeup_lat: Error: platform devices "
> +		       "not valid\n");
> +		return -EINVAL;
> +	} else {
> +		/* Call the omap_device API */
> +		ret = omap_device_set_max_dev_wakeup_lat(req_pdev, pdev, t);
> +	}

I don't think a NULL name check is the right sanity check here.  WHat
you really need to know is whether the target device is an omap_device.
The requesting device can be anything (I think.)

Here's a simpler check:

	if (pdev->dev->parent == &omap_device_parent)
		ret = omap_device_set_max_dev_wakeup_lat(req_pdev, pdev, t);
	else
		...

                        
> +	return ret;
> +}

Kevin

^ permalink raw reply	[flat|nested] 19+ messages in thread

* [PATCH 2/2] OMAP: PM: implement devices wakeup latency constraints APIs
@ 2011-03-08  2:15     ` Kevin Hilman
  0 siblings, 0 replies; 19+ messages in thread
From: Kevin Hilman @ 2011-03-08  2:15 UTC (permalink / raw)
  To: linux-arm-kernel

Jean Pihet <jean.pihet@newoldbits.com> writes:

> Implement OMAP PM layer omap_pm_set_max_dev_wakeup_lat API by
> creating similar APIs at the omap_device and omap_hwmod levels. The
> omap_hwmod level call is the layer with access to the powerdomain
> core, so it is the place where the powerdomain is queried to set and
> release the constraints.
>
> NOTE: only works for devices which have been converted to use
>       omap_device/omap_hwmod.
>
> Longer term, we could possibly remove this API from the OMAP PM layer,
> and instead directly use the device level API.
>
> The power domains get the next power state programmed directly in
> the registers via pwrdm_wakeuplat_update_pwrst.
>
> Note about PM QOS: the MPU and CORE power domains get the next power
> state via cpuidle, which get the strongest wake-up latency constraint
> by querying PM QOS. The usage of PM QOS is temporary, until a generic
> solution is in place.
>
> Based on Vibhore's original patch, adapted to omap_device, omap_hwmod
> and PM QOS frameworks.

I haven't got to a detailed review of this code yet, but did do some
experiements and have some general comments on the code/design to get
started.

Also, I had a problem doing a real dumb test until I figured out the
problem with the init sequence.  I tried setting a constraint in the
device init code for UART (mach-omap2/serial.c:omap_serial_init_port()),
and then I realized that that runs before
mach-omap2/pm34xx.c:pwrdms_setup() which also calls
omap_set_pwrdm_state() for each powerdomain to initialize.

Also, for debug purposes, it might be useful to have a per-device sysfs
interface to setting this wakeup latency constraint.  Something like

   /sys/devices/platform/omap/.../power/wakeup_latency

I'm not sure exactly what to set the requesting device to though.  

As far as implementation goes, you've so far implemented only wakeup
latencies, but not througput.  When you implement throughput you will
have to duplicate large parts of this code and data structures for
throughput, and if ever add some other constraint (frequency, voltage)
it would need to be duplicated again.

Maybe now is the time to consider an interface to add a generic
per-device constraint, with a type (latency, throughput, etc.), or
"class" as it's called in PM QoS.  For now the type/class does not need
to be exposed externally, but will make implementing new constraint
types much easer.

Some other comments below...

> Signed-off-by: Jean Pihet <j-pihet@ti.com>
> Cc: Vibhore Vardhan <vvardhan@ti.com>
> ---
> Based on khilman's pm-core branch
>
>  arch/arm/mach-omap2/omap_hwmod.c              |   62 ++++++++-
>  arch/arm/mach-omap2/powerdomain.c             |  197 +++++++++++++++++++++++++
>  arch/arm/mach-omap2/powerdomain.h             |   39 +++++-
>  arch/arm/mach-omap2/powerdomains3xxx_data.c   |   60 ++++++++
>  arch/arm/plat-omap/include/plat/omap_device.h |    2 +
>  arch/arm/plat-omap/include/plat/omap_hwmod.h  |    2 +
>  arch/arm/plat-omap/omap-pm-constraints.c      |  121 +++++++--------
>  arch/arm/plat-omap/omap_device.c              |   28 ++++
>  8 files changed, 446 insertions(+), 65 deletions(-)
>
> diff --git a/arch/arm/mach-omap2/omap_hwmod.c b/arch/arm/mach-omap2/omap_hwmod.c
> index 028efda..bad8248 100644
> --- a/arch/arm/mach-omap2/omap_hwmod.c
> +++ b/arch/arm/mach-omap2/omap_hwmod.c
> @@ -142,6 +142,7 @@
>  #include "powerdomain.h"
>  #include <plat/clock.h>
>  #include <plat/omap_hwmod.h>
> +#include <plat/omap_device.h>
>  #include <plat/prcm.h>
>  
>  #include "cm2xxx_3xxx.h"
> @@ -2267,10 +2268,69 @@ ohsps_unlock:
>  }
>  
>  /**
> + * omap_hwmod_set_max_dev_wakeup_lat - set a device wake-up constraint
> + * @oh: the device of @oh to set a constraint on.
> + * @req_oh: the device of @req_oh is the requester of the constraint.
> + * @t: wakeup latency constraint (us). -1 removes the existing constraint.
> + *
> + * Query the powerdomain of @oh to set/release the wake-up constraint.
> + * @oh is used to determine which power domain to set a constraint on.
> + * @req_oh is used to record the requester for later update or removal
> + * of a constraint.
> + *
> + * Returns -EINVAL if @oh or @req_oh have no power domain, or the return
> + * code from the pwrdm function (pwrdm_wakeuplat_set/release_constraint)
> + * of the powerdomain assocated with @oh.
> + */
> +int omap_hwmod_set_max_dev_wakeup_lat(struct omap_hwmod *req_oh,
> +				      struct omap_hwmod *oh, long t)
> +{
> +	struct device *req_dev;
> +	struct platform_device *req_pdev;
> +	struct powerdomain *pwrdm;
> +
> +	pwrdm = omap_hwmod_get_pwrdm(oh);
> +
> +	/* Catch devices with undefined powerdomains */
> +	if (!PTR_ERR(pwrdm)) {
> +		pr_err("omap_hwmod: Error: could not find parent "
> +			"powerdomain for %s\n", oh->name);
> +		return -EINVAL;
> +	}
> +
> +	req_pdev = &(req_oh->od->pdev);
> +	if (!PTR_ERR(req_pdev)) {
> +		pr_err("omap_hwmod: Error: pdev not found for oh %s\n",
> +		       oh->name);
> +		return -EINVAL;
> +	}
> +
> +	req_dev = &(req_pdev->dev);
> +	if (!PTR_ERR(req_dev)) {
> +		pr_err("omap_hwmod: Error: device not found for oh %s\n",
> +		       oh->name);
> +		return -EINVAL;
> +	}
> +
> +	/* Call set/release_constraint for the given pwrdm */
> +	if (t == -1) {
> +		pr_debug("omap_hwmod: remove max device latency constraint: "
> +			 "oh %s, pwrdm %s, req by oh %s\n",
> +			 oh->name, pwrdm->name, req_oh->name);
> +	} else {
> +		pr_debug("omap_hwmod: add max device latency constraint: "
> +			 "oh %s, t = %ld usec, pwrdm %s, req by oh %s\n",
> +			 oh->name, t, pwrdm->name, req_oh->name);
> +	}
> +
> +	return pwrdm_wakeuplat_set_constraint(pwrdm, req_dev, t);
> +}
> +
> +/**
>   * omap_hwmod_get_context_loss_count - get lost context count
>   * @oh: struct omap_hwmod *
>   *
> - * Query the powerdomain of of @oh to get the context loss
> + * Query the powerdomain of @oh to get the context loss
>   * count for this device.
>   *
>   * Returns the context loss count of the powerdomain assocated with @oh
> diff --git a/arch/arm/mach-omap2/powerdomain.c b/arch/arm/mach-omap2/powerdomain.c
> index eaed0df..6fb4741 100644
> --- a/arch/arm/mach-omap2/powerdomain.c
> +++ b/arch/arm/mach-omap2/powerdomain.c
> @@ -19,6 +19,8 @@
>  #include <linux/list.h>
>  #include <linux/errno.h>
>  #include <linux/string.h>
> +#include <linux/slab.h>
> +
>  #include "cm2xxx_3xxx.h"
>  #include "prcm44xx.h"
>  #include "cm44xx.h"
> @@ -103,6 +105,13 @@ static int _pwrdm_register(struct powerdomain *pwrdm)
>  	pwrdm->state = pwrdm_read_pwrst(pwrdm);
>  	pwrdm->state_counter[pwrdm->state] = 1;
>  
> +	/* Initialize priority ordered list for wakeup latency constraint */
> +	spin_lock_init(&pwrdm->wakeuplat_lock);
> +	plist_head_init(&pwrdm->wakeuplat_dev_list, &pwrdm->wakeuplat_lock);
> +
> +	/* res_mutex protects res_list add and del ops */
> +	mutex_init(&pwrdm->wakeuplat_mutex);
> +
>  	pr_debug("powerdomain: registered %s\n", pwrdm->name);
>  
>  	return 0;
> @@ -176,6 +185,74 @@ static int _pwrdm_post_transition_cb(struct powerdomain *pwrdm, void *unused)
>  	return 0;
>  }
>  
> +/**
> + * pwrdm_wakeuplat_update_pwrst - Update power domain power state if needed
> + * @pwrdm: struct powerdomain * to which requesting device belongs to.
> + *
> + * Finds the minimum allowed wake-up latency value from all entries
> + * in the list and the power domain power state needing the constraint.
> + * Programs a new target state if it is different from current power state.
> + *
> + * Only OMAP3xxx is supported for now
> + *
> + * Returns 0 upon success.
> + */
> +static int pwrdm_wakeuplat_update_pwrst(struct powerdomain *pwrdm)
> +{
> +	struct plist_node *node;
> +	int ret = 0, new_state;
> +	long min_latency = -1;
> +
> +	/* Find the strongest constraint from the plist */
> +	if (!plist_head_empty(&pwrdm->wakeuplat_dev_list)) {
> +		node = plist_first(&pwrdm->wakeuplat_dev_list);
> +		min_latency = node->prio;
> +	}
> +
> +	/* Find power state with wakeup latency < minimum constraint. */
> +	for (new_state = 0x0; new_state < PWRDM_MAX_PWRSTS; new_state++) {
> +		if (min_latency == -1 ||
> +		    pwrdm->wakeup_lat[new_state] <= min_latency)
> +			break;
> +	}
> +
> +	switch (new_state) {
> +	case PWRDM_FUNC_PWRST_OFF:
> +		new_state = PWRDM_POWER_OFF;
> +		break;
> +	case PWRDM_FUNC_PWRST_OSWR:
> +		if (cpu_is_omap34xx())
> +			pwrdm_set_logic_retst(pwrdm, PWRDM_POWER_OFF);

cpu_is_* checks here aren't right.

You should use SoC specific function pointers as are done for many of the
other powerdomain calls after Rajendra's splitup series.

> +		new_state = PWRDM_POWER_RET;
> +		break;
> +	case PWRDM_FUNC_PWRST_CSWR:
> +		if (cpu_is_omap34xx())
> +			pwrdm_set_logic_retst(pwrdm, PWRDM_POWER_RET);
> +		new_state = PWRDM_POWER_RET;
> +		break;
> +	case PWRDM_FUNC_PWRST_ON:
> +		new_state = PWRDM_POWER_ON;
> +		break;
> +	default:
> +		pr_warn("powerdomain: requested latency constraint not "
> +			"supported %s set to ON state\n", pwrdm->name);
> +		new_state = PWRDM_POWER_ON;
> +		break;
> +	}
> +
> +	if (pwrdm_read_pwrst(pwrdm) != new_state) {
> +		if (cpu_is_omap34xx())
> +			ret = omap_set_pwrdm_state(pwrdm, new_state);
> +	}
> +
> +	pr_debug("powerdomain: %s pwrst: curr=%d, prev=%d next=%d "
> +		 "min_latency=%ld, set_state=%d\n", pwrdm->name,
> +		 pwrdm_read_pwrst(pwrdm), pwrdm_read_prev_pwrst(pwrdm),
> +		 pwrdm_read_next_pwrst(pwrdm), min_latency, new_state);
> +
> +	return ret;
> +}
> +

[...]

> diff --git a/arch/arm/mach-omap2/powerdomains3xxx_data.c b/arch/arm/mach-omap2/powerdomains3xxx_data.c
> index e1bec56..4f7e44d 100644
> --- a/arch/arm/mach-omap2/powerdomains3xxx_data.c
> +++ b/arch/arm/mach-omap2/powerdomains3xxx_data.c
> @@ -52,6 +52,12 @@ static struct powerdomain iva2_pwrdm = {
>  		[2] = PWRSTS_OFF_ON,
>  		[3] = PWRDM_POWER_ON,
>  	},
> +	.wakeup_lat = {
> +		[PWRDM_FUNC_PWRST_OFF] = 1100,
> +		[PWRDM_FUNC_PWRST_OSWR] = UNSUP_STATE,
> +		[PWRDM_FUNC_PWRST_CSWR] = 350,
> +		[PWRDM_FUNC_PWRST_ON] = 0,
> +	},
>  };
>  
>  static struct powerdomain mpu_3xxx_pwrdm = {
> @@ -68,6 +74,12 @@ static struct powerdomain mpu_3xxx_pwrdm = {
>  	.pwrsts_mem_on	  = {
>  		[0] = PWRSTS_OFF_ON,
>  	},
> +	.wakeup_lat = {
> +		[PWRDM_FUNC_PWRST_OFF] = 95,
> +		[PWRDM_FUNC_PWRST_OSWR] = UNSUP_STATE,
> +		[PWRDM_FUNC_PWRST_CSWR] = 45,
> +		[PWRDM_FUNC_PWRST_ON] = 0,
> +	},
>  };
>  
>  /*
> @@ -98,6 +110,12 @@ static struct powerdomain core_3xxx_pre_es3_1_pwrdm = {
>  		[0] = PWRSTS_OFF_RET_ON, /* MEM1ONSTATE */
>  		[1] = PWRSTS_OFF_RET_ON, /* MEM2ONSTATE */
>  	},
> +	.wakeup_lat = {
> +		[PWRDM_FUNC_PWRST_OFF] = 100,
> +		[PWRDM_FUNC_PWRST_OSWR] = UNSUP_STATE,
> +		[PWRDM_FUNC_PWRST_CSWR] = 60,
> +		[PWRDM_FUNC_PWRST_ON] = 0,
> +	},
>  };
>  
>  static struct powerdomain core_3xxx_es3_1_pwrdm = {
> @@ -121,6 +139,12 @@ static struct powerdomain core_3xxx_es3_1_pwrdm = {
>  		[0] = PWRSTS_OFF_RET_ON, /* MEM1ONSTATE */
>  		[1] = PWRSTS_OFF_RET_ON, /* MEM2ONSTATE */
>  	},
> +	.wakeup_lat = {
> +		[PWRDM_FUNC_PWRST_OFF] = 100,
> +		[PWRDM_FUNC_PWRST_OSWR] = UNSUP_STATE,
> +		[PWRDM_FUNC_PWRST_CSWR] = 60,
> +		[PWRDM_FUNC_PWRST_ON] = 0,
> +	},
>  };
>  
>  static struct powerdomain dss_pwrdm = {
> @@ -136,6 +160,12 @@ static struct powerdomain dss_pwrdm = {
>  	.pwrsts_mem_on	  = {
>  		[0] = PWRDM_POWER_ON,  /* MEMONSTATE */
>  	},
> +	.wakeup_lat = {
> +		[PWRDM_FUNC_PWRST_OFF] = 70,
> +		[PWRDM_FUNC_PWRST_OSWR] = UNSUP_STATE,
> +		[PWRDM_FUNC_PWRST_CSWR] = 20,
> +		[PWRDM_FUNC_PWRST_ON] = 0,
> +	},
>  };
>  
>  /*
> @@ -157,6 +187,12 @@ static struct powerdomain sgx_pwrdm = {
>  	.pwrsts_mem_on	  = {
>  		[0] = PWRDM_POWER_ON,  /* MEMONSTATE */
>  	},
> +	.wakeup_lat = {
> +		[PWRDM_FUNC_PWRST_OFF] = 1000,
> +		[PWRDM_FUNC_PWRST_OSWR] = UNSUP_STATE,
> +		[PWRDM_FUNC_PWRST_CSWR] = UNSUP_STATE,
> +		[PWRDM_FUNC_PWRST_ON] = 0,
> +	},
>  };
>  
>  static struct powerdomain cam_pwrdm = {
> @@ -172,6 +208,12 @@ static struct powerdomain cam_pwrdm = {
>  	.pwrsts_mem_on	  = {
>  		[0] = PWRDM_POWER_ON,  /* MEMONSTATE */
>  	},
> +	.wakeup_lat = {
> +		[PWRDM_FUNC_PWRST_OFF] = 850,
> +		[PWRDM_FUNC_PWRST_OSWR] = UNSUP_STATE,
> +		[PWRDM_FUNC_PWRST_CSWR] = 35,
> +		[PWRDM_FUNC_PWRST_ON] = 0,
> +	},
>  };
>  
>  static struct powerdomain per_pwrdm = {
> @@ -187,6 +229,12 @@ static struct powerdomain per_pwrdm = {
>  	.pwrsts_mem_on	  = {
>  		[0] = PWRDM_POWER_ON,  /* MEMONSTATE */
>  	},
> +	.wakeup_lat = {
> +		[PWRDM_FUNC_PWRST_OFF] = 200,
> +		[PWRDM_FUNC_PWRST_OSWR] = UNSUP_STATE,
> +		[PWRDM_FUNC_PWRST_CSWR] = 110,
> +		[PWRDM_FUNC_PWRST_ON] = 0,
> +	},
>  };
>  
>  static struct powerdomain emu_pwrdm = {
> @@ -201,6 +249,12 @@ static struct powerdomain neon_pwrdm = {
>  	.omap_chip	  = OMAP_CHIP_INIT(CHIP_IS_OMAP3430),
>  	.pwrsts		  = PWRSTS_OFF_RET_ON,
>  	.pwrsts_logic_ret = PWRDM_POWER_RET,
> +	.wakeup_lat = {
> +		[PWRDM_FUNC_PWRST_OFF] = 200,
> +		[PWRDM_FUNC_PWRST_OSWR] = UNSUP_STATE,
> +		[PWRDM_FUNC_PWRST_CSWR] = 35,
> +		[PWRDM_FUNC_PWRST_ON] = 0,
> +	},
>  };
>  
>  static struct powerdomain usbhost_pwrdm = {
> @@ -223,6 +277,12 @@ static struct powerdomain usbhost_pwrdm = {
>  	.pwrsts_mem_on	  = {
>  		[0] = PWRDM_POWER_ON,  /* MEMONSTATE */
>  	},
> +	.wakeup_lat = {
> +		[PWRDM_FUNC_PWRST_OFF] = 800,
> +		[PWRDM_FUNC_PWRST_OSWR] = UNSUP_STATE,
> +		[PWRDM_FUNC_PWRST_CSWR] = 150,
> +		[PWRDM_FUNC_PWRST_ON] = 0,
> +	},
>  };

A summary about where the latency numbers for each powerdomain come from
would be useful.

[...]

> @@ -87,64 +60,86 @@ int omap_pm_set_min_bus_tput(struct device *dev, u8 agent_id, unsigned long r)
>  	return 0;
>  }
>  
> +/*
> + * omap_pm_set_max_dev_wakeup_lat - set/release devices wake-up latency
> + * constraints
> + */
>  int omap_pm_set_max_dev_wakeup_lat(struct device *req_dev, struct device *dev,
>  				   long t)
>  {
> +	struct platform_device *pdev, *req_pdev;
> +	int ret = 0;
> +
>  	if (!req_dev || !dev || t < -1) {
>  		WARN(1, "OMAP PM: %s: invalid parameter(s)", __func__);
>  		return -EINVAL;
> +	}
> +
> +	/* Look for the platform devices */
> +	pdev = to_platform_device(dev);
> +	req_pdev = to_platform_device(req_dev);
> +
> +	/* Try to catch non platform devices. */
> +	if ((pdev->name == NULL) || (req_pdev->name == NULL)) {
> +		pr_err("OMAP-PM set_wakeup_lat: Error: platform devices "
> +		       "not valid\n");
> +		return -EINVAL;
> +	} else {
> +		/* Call the omap_device API */
> +		ret = omap_device_set_max_dev_wakeup_lat(req_pdev, pdev, t);
> +	}

I don't think a NULL name check is the right sanity check here.  WHat
you really need to know is whether the target device is an omap_device.
The requesting device can be anything (I think.)

Here's a simpler check:

	if (pdev->dev->parent == &omap_device_parent)
		ret = omap_device_set_max_dev_wakeup_lat(req_pdev, pdev, t);
	else
		...

                        
> +	return ret;
> +}

Kevin

^ permalink raw reply	[flat|nested] 19+ messages in thread

* [PATCH 2/2] OMAP: PM: implement devices wakeup latency constraints APIs
  2011-03-04 14:52 [PATCH 0/2] " Jean Pihet
@ 2011-03-04 14:52   ` Jean Pihet
  0 siblings, 0 replies; 19+ messages in thread
From: Jean Pihet @ 2011-03-04 14:52 UTC (permalink / raw)
  To: linux-omap, linux-arm-kernel, Kevin Hilman; +Cc: Jean Pihet, Vibhore Vardhan

Implement OMAP PM layer omap_pm_set_max_dev_wakeup_lat API by
creating similar APIs at the omap_device and omap_hwmod levels. The
omap_hwmod level call is the layer with access to the powerdomain
core, so it is the place where the powerdomain is queried to set and
release the constraints.

NOTE: only works for devices which have been converted to use
      omap_device/omap_hwmod.

Longer term, we could possibly remove this API from the OMAP PM layer,
and instead directly use the device level API.

The power domains get the next power state programmed directly in
the registers via pwrdm_wakeuplat_update_pwrst.

Note about PM QOS: the MPU and CORE power domains get the next power
state via cpuidle, which get the strongest wake-up latency constraint
by querying PM QOS. The usage of PM QOS is temporary, until a generic
solution is in place.

Based on Vibhore's original patch, adapted to omap_device, omap_hwmod
and PM QOS frameworks.

Signed-off-by: Jean Pihet <j-pihet@ti.com>
Cc: Vibhore Vardhan <vvardhan@ti.com>
---
Based on khilman's pm-core branch

 arch/arm/mach-omap2/omap_hwmod.c              |   62 ++++++++-
 arch/arm/mach-omap2/powerdomain.c             |  197 +++++++++++++++++++++++++
 arch/arm/mach-omap2/powerdomain.h             |   39 +++++-
 arch/arm/mach-omap2/powerdomains3xxx_data.c   |   60 ++++++++
 arch/arm/plat-omap/include/plat/omap_device.h |    2 +
 arch/arm/plat-omap/include/plat/omap_hwmod.h  |    2 +
 arch/arm/plat-omap/omap-pm-constraints.c      |  121 +++++++--------
 arch/arm/plat-omap/omap_device.c              |   28 ++++
 8 files changed, 446 insertions(+), 65 deletions(-)

diff --git a/arch/arm/mach-omap2/omap_hwmod.c b/arch/arm/mach-omap2/omap_hwmod.c
index 028efda..bad8248 100644
--- a/arch/arm/mach-omap2/omap_hwmod.c
+++ b/arch/arm/mach-omap2/omap_hwmod.c
@@ -142,6 +142,7 @@
 #include "powerdomain.h"
 #include <plat/clock.h>
 #include <plat/omap_hwmod.h>
+#include <plat/omap_device.h>
 #include <plat/prcm.h>
 
 #include "cm2xxx_3xxx.h"
@@ -2267,10 +2268,69 @@ ohsps_unlock:
 }
 
 /**
+ * omap_hwmod_set_max_dev_wakeup_lat - set a device wake-up constraint
+ * @oh: the device of @oh to set a constraint on.
+ * @req_oh: the device of @req_oh is the requester of the constraint.
+ * @t: wakeup latency constraint (us). -1 removes the existing constraint.
+ *
+ * Query the powerdomain of @oh to set/release the wake-up constraint.
+ * @oh is used to determine which power domain to set a constraint on.
+ * @req_oh is used to record the requester for later update or removal
+ * of a constraint.
+ *
+ * Returns -EINVAL if @oh or @req_oh have no power domain, or the return
+ * code from the pwrdm function (pwrdm_wakeuplat_set/release_constraint)
+ * of the powerdomain assocated with @oh.
+ */
+int omap_hwmod_set_max_dev_wakeup_lat(struct omap_hwmod *req_oh,
+				      struct omap_hwmod *oh, long t)
+{
+	struct device *req_dev;
+	struct platform_device *req_pdev;
+	struct powerdomain *pwrdm;
+
+	pwrdm = omap_hwmod_get_pwrdm(oh);
+
+	/* Catch devices with undefined powerdomains */
+	if (!PTR_ERR(pwrdm)) {
+		pr_err("omap_hwmod: Error: could not find parent "
+			"powerdomain for %s\n", oh->name);
+		return -EINVAL;
+	}
+
+	req_pdev = &(req_oh->od->pdev);
+	if (!PTR_ERR(req_pdev)) {
+		pr_err("omap_hwmod: Error: pdev not found for oh %s\n",
+		       oh->name);
+		return -EINVAL;
+	}
+
+	req_dev = &(req_pdev->dev);
+	if (!PTR_ERR(req_dev)) {
+		pr_err("omap_hwmod: Error: device not found for oh %s\n",
+		       oh->name);
+		return -EINVAL;
+	}
+
+	/* Call set/release_constraint for the given pwrdm */
+	if (t == -1) {
+		pr_debug("omap_hwmod: remove max device latency constraint: "
+			 "oh %s, pwrdm %s, req by oh %s\n",
+			 oh->name, pwrdm->name, req_oh->name);
+	} else {
+		pr_debug("omap_hwmod: add max device latency constraint: "
+			 "oh %s, t = %ld usec, pwrdm %s, req by oh %s\n",
+			 oh->name, t, pwrdm->name, req_oh->name);
+	}
+
+	return pwrdm_wakeuplat_set_constraint(pwrdm, req_dev, t);
+}
+
+/**
  * omap_hwmod_get_context_loss_count - get lost context count
  * @oh: struct omap_hwmod *
  *
- * Query the powerdomain of of @oh to get the context loss
+ * Query the powerdomain of @oh to get the context loss
  * count for this device.
  *
  * Returns the context loss count of the powerdomain assocated with @oh
diff --git a/arch/arm/mach-omap2/powerdomain.c b/arch/arm/mach-omap2/powerdomain.c
index eaed0df..6fb4741 100644
--- a/arch/arm/mach-omap2/powerdomain.c
+++ b/arch/arm/mach-omap2/powerdomain.c
@@ -19,6 +19,8 @@
 #include <linux/list.h>
 #include <linux/errno.h>
 #include <linux/string.h>
+#include <linux/slab.h>
+
 #include "cm2xxx_3xxx.h"
 #include "prcm44xx.h"
 #include "cm44xx.h"
@@ -103,6 +105,13 @@ static int _pwrdm_register(struct powerdomain *pwrdm)
 	pwrdm->state = pwrdm_read_pwrst(pwrdm);
 	pwrdm->state_counter[pwrdm->state] = 1;
 
+	/* Initialize priority ordered list for wakeup latency constraint */
+	spin_lock_init(&pwrdm->wakeuplat_lock);
+	plist_head_init(&pwrdm->wakeuplat_dev_list, &pwrdm->wakeuplat_lock);
+
+	/* res_mutex protects res_list add and del ops */
+	mutex_init(&pwrdm->wakeuplat_mutex);
+
 	pr_debug("powerdomain: registered %s\n", pwrdm->name);
 
 	return 0;
@@ -176,6 +185,74 @@ static int _pwrdm_post_transition_cb(struct powerdomain *pwrdm, void *unused)
 	return 0;
 }
 
+/**
+ * pwrdm_wakeuplat_update_pwrst - Update power domain power state if needed
+ * @pwrdm: struct powerdomain * to which requesting device belongs to.
+ *
+ * Finds the minimum allowed wake-up latency value from all entries
+ * in the list and the power domain power state needing the constraint.
+ * Programs a new target state if it is different from current power state.
+ *
+ * Only OMAP3xxx is supported for now
+ *
+ * Returns 0 upon success.
+ */
+static int pwrdm_wakeuplat_update_pwrst(struct powerdomain *pwrdm)
+{
+	struct plist_node *node;
+	int ret = 0, new_state;
+	long min_latency = -1;
+
+	/* Find the strongest constraint from the plist */
+	if (!plist_head_empty(&pwrdm->wakeuplat_dev_list)) {
+		node = plist_first(&pwrdm->wakeuplat_dev_list);
+		min_latency = node->prio;
+	}
+
+	/* Find power state with wakeup latency < minimum constraint. */
+	for (new_state = 0x0; new_state < PWRDM_MAX_PWRSTS; new_state++) {
+		if (min_latency == -1 ||
+		    pwrdm->wakeup_lat[new_state] <= min_latency)
+			break;
+	}
+
+	switch (new_state) {
+	case PWRDM_FUNC_PWRST_OFF:
+		new_state = PWRDM_POWER_OFF;
+		break;
+	case PWRDM_FUNC_PWRST_OSWR:
+		if (cpu_is_omap34xx())
+			pwrdm_set_logic_retst(pwrdm, PWRDM_POWER_OFF);
+		new_state = PWRDM_POWER_RET;
+		break;
+	case PWRDM_FUNC_PWRST_CSWR:
+		if (cpu_is_omap34xx())
+			pwrdm_set_logic_retst(pwrdm, PWRDM_POWER_RET);
+		new_state = PWRDM_POWER_RET;
+		break;
+	case PWRDM_FUNC_PWRST_ON:
+		new_state = PWRDM_POWER_ON;
+		break;
+	default:
+		pr_warn("powerdomain: requested latency constraint not "
+			"supported %s set to ON state\n", pwrdm->name);
+		new_state = PWRDM_POWER_ON;
+		break;
+	}
+
+	if (pwrdm_read_pwrst(pwrdm) != new_state) {
+		if (cpu_is_omap34xx())
+			ret = omap_set_pwrdm_state(pwrdm, new_state);
+	}
+
+	pr_debug("powerdomain: %s pwrst: curr=%d, prev=%d next=%d "
+		 "min_latency=%ld, set_state=%d\n", pwrdm->name,
+		 pwrdm_read_pwrst(pwrdm), pwrdm_read_prev_pwrst(pwrdm),
+		 pwrdm_read_next_pwrst(pwrdm), min_latency, new_state);
+
+	return ret;
+}
+
 /* Public functions */
 
 /**
@@ -910,6 +987,126 @@ int pwrdm_post_transition(void)
 	return 0;
 }
 
+/*
+ * Test if the current powerdomain is MPU or CORE
+ *
+ * This function is only used by pwrdm_wakeuplat_set_constraint
+ * which applies the MPU and CORE power domains constraints using PM QOS.
+ *
+ * This will disappear when all power domains will be controlled directly
+ * from the wake-up latency constraints framework
+ */
+static inline int is_pwrdm_mpu_or_core(struct powerdomain *pwrdm)
+{
+	if ((strncmp(pwrdm->name, "mpu_pwrdm", 9) == 0) ||
+	    (strncmp(pwrdm->name, "core_pwrdm", 10) == 0))
+		return 1;
+	else
+		return 0;
+}
+
+/*
+ * pwrdm_wakeuplat_set_constraint - Set/update/remove powerdomain wakeup
+ * latency constraint
+ * @pwrdm: struct powerdomain * to which requesting device belongs to
+ * @dev: struct device * of requesting device
+ * @t: wakeup latency constraint in microseconds. The value of -1 removes
+ * the constraint
+ *
+ * Constraint set/update: Adds new entry to powerdomain's wakeup latency
+ * constraint list.
+ * If the requesting device already exists in the list, old value is
+ * overwritten. Checks whether current power state is still adequate.
+ *
+ * Constraint removal: Removes device's entry from powerdomain's wakeup
+ * latency constraint list. Checks whether current power state is still
+ * adequate.
+ *
+ * Note about PM QOS: the MPU and CORE power domains get the next power
+ * state via cpuidle, which get the strongest wake-up latency constraint
+ * by querying PM QOS. The usage of PM QOS is temporary, until a generic
+ * solution is in place.
+ * The power domains other then MPU and CORE get the next power state
+ * programmed directly in the registers via pwrdm_wakeuplat_update_pwrst.
+ *
+ * Returns -EINVAL if the powerdomain or device pointer is NULL or
+ * no such entry exists in the list, or -ENOMEM if kmalloc fails, or
+ * returns 0 upon success.
+ */
+int pwrdm_wakeuplat_set_constraint(struct powerdomain *pwrdm,
+				   struct device *req_dev, long t)
+{
+	struct  wakeuplat_dev_list *user;
+	int found = 0, ret = 0;
+
+	if (!pwrdm || !req_dev) {
+		WARN(1, "powerdomain: %s: invalid parameter(s)", __func__);
+		return -EINVAL;
+	}
+
+	mutex_lock(&pwrdm->wakeuplat_mutex);
+
+	plist_for_each_entry(user, &pwrdm->wakeuplat_dev_list, node) {
+		if (user->dev == req_dev) {
+			found = 1;
+			break;
+		}
+	}
+
+	if (t >= 0) {
+		/* Add new entry to the list or update existing request */
+		if (found && user->constraint_us == t) {
+			goto exit_set;
+		} else if (!found) {
+			user = kzalloc(sizeof(struct wakeuplat_dev_list),
+				       GFP_KERNEL);
+			if (!user) {
+				pr_err("powerdomain: FATAL ERROR: kzalloc "
+				       "failed\n");
+				ret = -ENOMEM;
+				goto exit_set;
+			}
+			user->dev = req_dev;
+		} else {
+			plist_del(&user->node, &pwrdm->wakeuplat_dev_list);
+			if (is_pwrdm_mpu_or_core(pwrdm))
+				pm_qos_remove_request(&(user->pm_qos_request));
+		}
+
+		plist_node_init(&user->node, t);
+		plist_add(&user->node, &pwrdm->wakeuplat_dev_list);
+		user->node.prio = user->constraint_us = t;
+		if (is_pwrdm_mpu_or_core(pwrdm))
+			pm_qos_add_request(&(user->pm_qos_request),
+					   PM_QOS_CPU_DMA_LATENCY, t);
+	} else {
+		/* Remove the constraint from the list */
+		if (!found) {
+			pr_err("OMAP PM: Error: no prior constraint to "
+			       "release\n");
+			ret = -EINVAL;
+			goto exit_set;
+		}
+
+		if (is_pwrdm_mpu_or_core(pwrdm))
+			pm_qos_remove_request(&(user->pm_qos_request));
+		plist_del(&user->node, &pwrdm->wakeuplat_dev_list);
+		kfree(user);
+	}
+
+	/*
+	 * Constraints on MPU and CORE are passed to PM QOS while other power
+	 * domains get the next power state programmed directly
+	 */
+	if (!(is_pwrdm_mpu_or_core(pwrdm)))
+		ret = pwrdm_wakeuplat_update_pwrst(pwrdm);
+
+exit_set:
+	mutex_unlock(&pwrdm->wakeuplat_mutex);
+
+	return ret;
+}
+
 /**
  * pwrdm_get_context_loss_count - get powerdomain's context loss count
  * @pwrdm: struct powerdomain * to wait for
diff --git a/arch/arm/mach-omap2/powerdomain.h b/arch/arm/mach-omap2/powerdomain.h
index 0b7a357..98eea82 100644
--- a/arch/arm/mach-omap2/powerdomain.h
+++ b/arch/arm/mach-omap2/powerdomain.h
@@ -19,7 +19,10 @@
 
 #include <linux/types.h>
 #include <linux/list.h>
-
+#include <linux/plist.h>
+#include <linux/mutex.h>
+#include <linux/spinlock.h>
+#include <linux/pm_qos_params.h>
 #include <linux/atomic.h>
 
 #include <plat/cpu.h>
@@ -46,6 +49,15 @@
 
 #define PWRSTS_OFF_RET_ON	(PWRSTS_OFF_RET | (1 << PWRDM_POWER_ON))
 
+/* Powerdomain functional power states */
+#define PWRDM_FUNC_PWRST_OFF	0x0
+#define PWRDM_FUNC_PWRST_OSWR	0x1
+#define PWRDM_FUNC_PWRST_CSWR	0x2
+#define PWRDM_FUNC_PWRST_ON	0x3
+
+#define PWRDM_MAX_FUNC_PWRSTS	4
+
+#define UNSUP_STATE		-1
 
 /* Powerdomain flags */
 #define PWRDM_HAS_HDWR_SAR	(1 << 0) /* hardware save-and-restore support */
@@ -96,6 +108,12 @@ struct powerdomain;
  * @state_counter:
  * @timer:
  * @state_timer:
+ * @wakeup_lat: wakeup latencies for possible powerdomain power states
+ * Note about the wakeup latencies ordering: the values must be sorted
+ *  in decremental order
+ * @wakeuplat_lock: spinlock for plist
+ * @wakeuplat_dev_list: plist_head linking all devices placing constraint
+ * @wakeuplat_mutex: mutex to protect per powerdomain list ops
  *
  * @prcm_partition possible values are defined in mach-omap2/prcm44xx.h.
  */
@@ -121,6 +139,22 @@ struct powerdomain {
 	s64 timer;
 	s64 state_timer[PWRDM_MAX_PWRSTS];
 #endif
+	const u32 wakeup_lat[PWRDM_MAX_FUNC_PWRSTS];
+	spinlock_t wakeuplat_lock;
+	struct plist_head wakeuplat_dev_list;
+	struct mutex wakeuplat_mutex;
+};
+
+/*
+ * Linked list for the power domains wakeup latencies constraints
+ * The PM QOS handle is only used to control the MPU and CORE power
+ * domains states; to be removed when a generic solution is in place.
+*/
+struct wakeuplat_dev_list {
+	struct device			*dev;
+	unsigned long			constraint_us;
+	struct plist_node		node;
+	struct pm_qos_request_list	pm_qos_request;
 };
 
 /**
@@ -210,6 +244,9 @@ int pwrdm_clkdm_state_switch(struct clockdomain *clkdm);
 int pwrdm_pre_transition(void);
 int pwrdm_post_transition(void);
 int pwrdm_set_lowpwrstchange(struct powerdomain *pwrdm);
+
+int pwrdm_wakeuplat_set_constraint(struct powerdomain *pwrdm,
+				   struct device *req_dev, long t);
 u32 pwrdm_get_context_loss_count(struct powerdomain *pwrdm);
 
 extern void omap2xxx_powerdomains_init(void);
diff --git a/arch/arm/mach-omap2/powerdomains3xxx_data.c b/arch/arm/mach-omap2/powerdomains3xxx_data.c
index e1bec56..4f7e44d 100644
--- a/arch/arm/mach-omap2/powerdomains3xxx_data.c
+++ b/arch/arm/mach-omap2/powerdomains3xxx_data.c
@@ -52,6 +52,12 @@ static struct powerdomain iva2_pwrdm = {
 		[2] = PWRSTS_OFF_ON,
 		[3] = PWRDM_POWER_ON,
 	},
+	.wakeup_lat = {
+		[PWRDM_FUNC_PWRST_OFF] = 1100,
+		[PWRDM_FUNC_PWRST_OSWR] = UNSUP_STATE,
+		[PWRDM_FUNC_PWRST_CSWR] = 350,
+		[PWRDM_FUNC_PWRST_ON] = 0,
+	},
 };
 
 static struct powerdomain mpu_3xxx_pwrdm = {
@@ -68,6 +74,12 @@ static struct powerdomain mpu_3xxx_pwrdm = {
 	.pwrsts_mem_on	  = {
 		[0] = PWRSTS_OFF_ON,
 	},
+	.wakeup_lat = {
+		[PWRDM_FUNC_PWRST_OFF] = 95,
+		[PWRDM_FUNC_PWRST_OSWR] = UNSUP_STATE,
+		[PWRDM_FUNC_PWRST_CSWR] = 45,
+		[PWRDM_FUNC_PWRST_ON] = 0,
+	},
 };
 
 /*
@@ -98,6 +110,12 @@ static struct powerdomain core_3xxx_pre_es3_1_pwrdm = {
 		[0] = PWRSTS_OFF_RET_ON, /* MEM1ONSTATE */
 		[1] = PWRSTS_OFF_RET_ON, /* MEM2ONSTATE */
 	},
+	.wakeup_lat = {
+		[PWRDM_FUNC_PWRST_OFF] = 100,
+		[PWRDM_FUNC_PWRST_OSWR] = UNSUP_STATE,
+		[PWRDM_FUNC_PWRST_CSWR] = 60,
+		[PWRDM_FUNC_PWRST_ON] = 0,
+	},
 };
 
 static struct powerdomain core_3xxx_es3_1_pwrdm = {
@@ -121,6 +139,12 @@ static struct powerdomain core_3xxx_es3_1_pwrdm = {
 		[0] = PWRSTS_OFF_RET_ON, /* MEM1ONSTATE */
 		[1] = PWRSTS_OFF_RET_ON, /* MEM2ONSTATE */
 	},
+	.wakeup_lat = {
+		[PWRDM_FUNC_PWRST_OFF] = 100,
+		[PWRDM_FUNC_PWRST_OSWR] = UNSUP_STATE,
+		[PWRDM_FUNC_PWRST_CSWR] = 60,
+		[PWRDM_FUNC_PWRST_ON] = 0,
+	},
 };
 
 static struct powerdomain dss_pwrdm = {
@@ -136,6 +160,12 @@ static struct powerdomain dss_pwrdm = {
 	.pwrsts_mem_on	  = {
 		[0] = PWRDM_POWER_ON,  /* MEMONSTATE */
 	},
+	.wakeup_lat = {
+		[PWRDM_FUNC_PWRST_OFF] = 70,
+		[PWRDM_FUNC_PWRST_OSWR] = UNSUP_STATE,
+		[PWRDM_FUNC_PWRST_CSWR] = 20,
+		[PWRDM_FUNC_PWRST_ON] = 0,
+	},
 };
 
 /*
@@ -157,6 +187,12 @@ static struct powerdomain sgx_pwrdm = {
 	.pwrsts_mem_on	  = {
 		[0] = PWRDM_POWER_ON,  /* MEMONSTATE */
 	},
+	.wakeup_lat = {
+		[PWRDM_FUNC_PWRST_OFF] = 1000,
+		[PWRDM_FUNC_PWRST_OSWR] = UNSUP_STATE,
+		[PWRDM_FUNC_PWRST_CSWR] = UNSUP_STATE,
+		[PWRDM_FUNC_PWRST_ON] = 0,
+	},
 };
 
 static struct powerdomain cam_pwrdm = {
@@ -172,6 +208,12 @@ static struct powerdomain cam_pwrdm = {
 	.pwrsts_mem_on	  = {
 		[0] = PWRDM_POWER_ON,  /* MEMONSTATE */
 	},
+	.wakeup_lat = {
+		[PWRDM_FUNC_PWRST_OFF] = 850,
+		[PWRDM_FUNC_PWRST_OSWR] = UNSUP_STATE,
+		[PWRDM_FUNC_PWRST_CSWR] = 35,
+		[PWRDM_FUNC_PWRST_ON] = 0,
+	},
 };
 
 static struct powerdomain per_pwrdm = {
@@ -187,6 +229,12 @@ static struct powerdomain per_pwrdm = {
 	.pwrsts_mem_on	  = {
 		[0] = PWRDM_POWER_ON,  /* MEMONSTATE */
 	},
+	.wakeup_lat = {
+		[PWRDM_FUNC_PWRST_OFF] = 200,
+		[PWRDM_FUNC_PWRST_OSWR] = UNSUP_STATE,
+		[PWRDM_FUNC_PWRST_CSWR] = 110,
+		[PWRDM_FUNC_PWRST_ON] = 0,
+	},
 };
 
 static struct powerdomain emu_pwrdm = {
@@ -201,6 +249,12 @@ static struct powerdomain neon_pwrdm = {
 	.omap_chip	  = OMAP_CHIP_INIT(CHIP_IS_OMAP3430),
 	.pwrsts		  = PWRSTS_OFF_RET_ON,
 	.pwrsts_logic_ret = PWRDM_POWER_RET,
+	.wakeup_lat = {
+		[PWRDM_FUNC_PWRST_OFF] = 200,
+		[PWRDM_FUNC_PWRST_OSWR] = UNSUP_STATE,
+		[PWRDM_FUNC_PWRST_CSWR] = 35,
+		[PWRDM_FUNC_PWRST_ON] = 0,
+	},
 };
 
 static struct powerdomain usbhost_pwrdm = {
@@ -223,6 +277,12 @@ static struct powerdomain usbhost_pwrdm = {
 	.pwrsts_mem_on	  = {
 		[0] = PWRDM_POWER_ON,  /* MEMONSTATE */
 	},
+	.wakeup_lat = {
+		[PWRDM_FUNC_PWRST_OFF] = 800,
+		[PWRDM_FUNC_PWRST_OSWR] = UNSUP_STATE,
+		[PWRDM_FUNC_PWRST_CSWR] = 150,
+		[PWRDM_FUNC_PWRST_ON] = 0,
+	},
 };
 
 static struct powerdomain dpll1_pwrdm = {
diff --git a/arch/arm/plat-omap/include/plat/omap_device.h b/arch/arm/plat-omap/include/plat/omap_device.h
index e4c349f..5da6b47 100644
--- a/arch/arm/plat-omap/include/plat/omap_device.h
+++ b/arch/arm/plat-omap/include/plat/omap_device.h
@@ -107,6 +107,8 @@ void __iomem *omap_device_get_rt_va(struct omap_device *od);
 int omap_device_align_pm_lat(struct platform_device *pdev,
 			     u32 new_wakeup_lat_limit);
 struct powerdomain *omap_device_get_pwrdm(struct omap_device *od);
+int omap_device_set_max_dev_wakeup_lat(struct platform_device *req_pdev,
+				       struct platform_device *pdev, long t);
 u32 omap_device_get_context_loss_count(struct platform_device *pdev);
 
 /* Other */
diff --git a/arch/arm/plat-omap/include/plat/omap_hwmod.h b/arch/arm/plat-omap/include/plat/omap_hwmod.h
index 4f5b23e..7fcf168 100644
--- a/arch/arm/plat-omap/include/plat/omap_hwmod.h
+++ b/arch/arm/plat-omap/include/plat/omap_hwmod.h
@@ -595,6 +595,8 @@ int omap_hwmod_for_each_by_class(const char *classname,
 				 void *user);
 
 int omap_hwmod_set_postsetup_state(struct omap_hwmod *oh, u8 state);
+int omap_hwmod_set_max_dev_wakeup_lat(struct omap_hwmod *req_oh,
+				      struct omap_hwmod *oh, long t);
 u32 omap_hwmod_get_context_loss_count(struct omap_hwmod *oh);
 
 /*
diff --git a/arch/arm/plat-omap/omap-pm-constraints.c b/arch/arm/plat-omap/omap-pm-constraints.c
index c8b4e4c..df24e5e 100644
--- a/arch/arm/plat-omap/omap-pm-constraints.c
+++ b/arch/arm/plat-omap/omap-pm-constraints.c
@@ -24,6 +24,7 @@
 /* Interface documentation is in mach/omap-pm.h */
 #include <plat/omap-pm.h>
 #include <plat/omap_device.h>
+#include <plat/common.h>
 
 static bool off_mode_enabled;
 static u32 dummy_context_loss_counter;
@@ -32,34 +33,6 @@ static u32 dummy_context_loss_counter;
  * Device-driver-originated constraints (via board-*.c files)
  */
 
-int omap_pm_set_max_mpu_wakeup_lat(struct device *dev, long t)
-{
-	if (!dev || t < -1) {
-		WARN(1, "OMAP PM: %s: invalid parameter(s)", __func__);
-		return -EINVAL;
-	};
-
-	if (t == -1)
-		pr_debug("OMAP PM: remove max MPU wakeup latency constraint: "
-			 "dev %s\n", dev_name(dev));
-	else
-		pr_debug("OMAP PM: add max MPU wakeup latency constraint: "
-			 "dev %s, t = %ld usec\n", dev_name(dev), t);
-
-	/*
-	 * For current Linux, this needs to map the MPU to a
-	 * powerdomain, then go through the list of current max lat
-	 * constraints on the MPU and find the smallest.  If
-	 * the latency constraint has changed, the code should
-	 * recompute the state to enter for the next powerdomain
-	 * state.
-	 *
-	 * TI CDP code can call constraint_set here.
-	 */
-
-	return 0;
-}
-
 int omap_pm_set_min_bus_tput(struct device *dev, u8 agent_id, unsigned long r)
 {
 	if (!dev || (agent_id != OCP_INITIATOR_AGENT &&
@@ -87,64 +60,86 @@ int omap_pm_set_min_bus_tput(struct device *dev, u8 agent_id, unsigned long r)
 	return 0;
 }
 
+/*
+ * omap_pm_set_max_dev_wakeup_lat - set/release devices wake-up latency
+ * constraints
+ */
 int omap_pm_set_max_dev_wakeup_lat(struct device *req_dev, struct device *dev,
 				   long t)
 {
+	struct platform_device *pdev, *req_pdev;
+	int ret = 0;
+
 	if (!req_dev || !dev || t < -1) {
 		WARN(1, "OMAP PM: %s: invalid parameter(s)", __func__);
 		return -EINVAL;
+	}
+
+	/* Look for the platform devices */
+	pdev = to_platform_device(dev);
+	req_pdev = to_platform_device(req_dev);
+
+	/* Try to catch non platform devices. */
+	if ((pdev->name == NULL) || (req_pdev->name == NULL)) {
+		pr_err("OMAP-PM set_wakeup_lat: Error: platform devices "
+		       "not valid\n");
+		return -EINVAL;
+	} else {
+		/* Call the omap_device API */
+		ret = omap_device_set_max_dev_wakeup_lat(req_pdev, pdev, t);
+	}
+
+	return ret;
+}
+
+/*
+ * omap_pm_set_max_mpu_wakeup_lat - set/release MPU wake-up latency
+ * constraints
+ *
+ * Maps to omap_pm_set_max_dev_wakeup_lat with the MPU device as constraints
+ * target.
+ */
+int omap_pm_set_max_mpu_wakeup_lat(struct device *req_dev, long t)
+{
+	if (!req_dev || t < -1) {
+		WARN(1, "OMAP PM: %s: invalid parameter(s)", __func__);
+		return -EINVAL;
 	};
 
 	if (t == -1)
-		pr_debug("OMAP PM: remove max device latency constraint: "
-			 "dev %s\n", dev_name(dev));
+		pr_debug("OMAP PM: remove max MPU wakeup latency constraint: "
+			 "dev %s\n", dev_name(req_dev));
 	else
-		pr_debug("OMAP PM: add max device latency constraint: "
-			 "dev %s, t = %ld usec\n", dev_name(dev), t);
-
-	/*
-	 * For current Linux, this needs to map the device to a
-	 * powerdomain, then go through the list of current max lat
-	 * constraints on that powerdomain and find the smallest.  If
-	 * the latency constraint has changed, the code should
-	 * recompute the state to enter for the next powerdomain
-	 * state.  Conceivably, this code should also determine
-	 * whether to actually disable the device clocks or not,
-	 * depending on how long it takes to re-enable the clocks.
-	 *
-	 * TI CDP code can call constraint_set here.
-	 */
+		pr_debug("OMAP PM: add max MPU wakeup latency constraint: "
+			 "dev %s, t = %ld usec\n", dev_name(req_dev), t);
 
-	return 0;
+	return omap_pm_set_max_dev_wakeup_lat(req_dev,
+					      omap2_get_mpuss_device(), t);
 }
 
-int omap_pm_set_max_sdma_lat(struct device *dev, long t)
+/*
+ * omap_pm_set_max_sdma_lat - set/release SDMA start latency
+ * constraints
+ *
+ * Currenlt maps to omap_pm_set_max_dev_wakeup_lat with the L3 device as
+ * constraints target.
+ */
+int omap_pm_set_max_sdma_lat(struct device *req_dev, long t)
 {
-	if (!dev || t < -1) {
+	if (!req_dev || t < -1) {
 		WARN(1, "OMAP PM: %s: invalid parameter(s)", __func__);
 		return -EINVAL;
 	};
 
 	if (t == -1)
 		pr_debug("OMAP PM: remove max DMA latency constraint: "
-			 "dev %s\n", dev_name(dev));
+			 "dev %s\n", dev_name(req_dev));
 	else
 		pr_debug("OMAP PM: add max DMA latency constraint: "
-			 "dev %s, t = %ld usec\n", dev_name(dev), t);
+			 "dev %s, t = %ld usec\n", dev_name(req_dev), t);
 
-	/*
-	 * For current Linux PM QOS params, this code should scan the
-	 * list of maximum CPU and DMA latencies and select the
-	 * smallest, then set cpu_dma_latency pm_qos_param
-	 * accordingly.
-	 *
-	 * For future Linux PM QOS params, with separate CPU and DMA
-	 * latency params, this code should just set the dma_latency param.
-	 *
-	 * TI CDP code can call constraint_set here.
-	 */
-
-	return 0;
+	return omap_pm_set_max_dev_wakeup_lat(req_dev,
+					      omap2_get_l3_device(), t);
 }
 
 int omap_pm_set_min_clk_rate(struct device *dev, struct clk *c, long r)
diff --git a/arch/arm/plat-omap/omap_device.c b/arch/arm/plat-omap/omap_device.c
index 9bbda9a..43dba02 100644
--- a/arch/arm/plat-omap/omap_device.c
+++ b/arch/arm/plat-omap/omap_device.c
@@ -296,6 +296,34 @@ static void _add_optional_clock_clkdev(struct omap_device *od,
 /* Public functions for use by core code */
 
 /**
+ * omap_device_set_max_dev_wakeup_lat - set/release a device constraint
+ * @od: struct omap_device *
+ * @req_od: struct omap_device *
+ *
+ * Using the primary hwmod, set/release a device constraint for the pdev
+ * device, requested by the req_pdev device.
+ *
+ * If any hwmods exist for the omap_device assoiated with @pdev and @req_pdev,
+ * set/release the constraint for the corresponding hwmods, otherwise return
+ * -EINVAL.
+ */
+int omap_device_set_max_dev_wakeup_lat(struct platform_device *req_pdev,
+				       struct platform_device *pdev, long t)
+{
+	struct omap_device *od, *req_od;
+	u32 ret = -EINVAL;
+
+	od = _find_by_pdev(pdev);
+	req_od = _find_by_pdev(req_pdev);
+
+	if ((od->hwmods_cnt) && (req_od->hwmods_cnt))
+		ret = omap_hwmod_set_max_dev_wakeup_lat(req_od->hwmods[0],
+							od->hwmods[0], t);
+
+	return ret;
+}
+
+/**
  * omap_device_get_context_loss_count - get lost context count
  * @od: struct omap_device *
  *
-- 
1.7.2.3


^ permalink raw reply related	[flat|nested] 19+ messages in thread

* [PATCH 2/2] OMAP: PM: implement devices wakeup latency constraints APIs
@ 2011-03-04 14:52   ` Jean Pihet
  0 siblings, 0 replies; 19+ messages in thread
From: Jean Pihet @ 2011-03-04 14:52 UTC (permalink / raw)
  To: linux-arm-kernel

Implement OMAP PM layer omap_pm_set_max_dev_wakeup_lat API by
creating similar APIs at the omap_device and omap_hwmod levels. The
omap_hwmod level call is the layer with access to the powerdomain
core, so it is the place where the powerdomain is queried to set and
release the constraints.

NOTE: only works for devices which have been converted to use
      omap_device/omap_hwmod.

Longer term, we could possibly remove this API from the OMAP PM layer,
and instead directly use the device level API.

The power domains get the next power state programmed directly in
the registers via pwrdm_wakeuplat_update_pwrst.

Note about PM QOS: the MPU and CORE power domains get the next power
state via cpuidle, which get the strongest wake-up latency constraint
by querying PM QOS. The usage of PM QOS is temporary, until a generic
solution is in place.

Based on Vibhore's original patch, adapted to omap_device, omap_hwmod
and PM QOS frameworks.

Signed-off-by: Jean Pihet <j-pihet@ti.com>
Cc: Vibhore Vardhan <vvardhan@ti.com>
---
Based on khilman's pm-core branch

 arch/arm/mach-omap2/omap_hwmod.c              |   62 ++++++++-
 arch/arm/mach-omap2/powerdomain.c             |  197 +++++++++++++++++++++++++
 arch/arm/mach-omap2/powerdomain.h             |   39 +++++-
 arch/arm/mach-omap2/powerdomains3xxx_data.c   |   60 ++++++++
 arch/arm/plat-omap/include/plat/omap_device.h |    2 +
 arch/arm/plat-omap/include/plat/omap_hwmod.h  |    2 +
 arch/arm/plat-omap/omap-pm-constraints.c      |  121 +++++++--------
 arch/arm/plat-omap/omap_device.c              |   28 ++++
 8 files changed, 446 insertions(+), 65 deletions(-)

diff --git a/arch/arm/mach-omap2/omap_hwmod.c b/arch/arm/mach-omap2/omap_hwmod.c
index 028efda..bad8248 100644
--- a/arch/arm/mach-omap2/omap_hwmod.c
+++ b/arch/arm/mach-omap2/omap_hwmod.c
@@ -142,6 +142,7 @@
 #include "powerdomain.h"
 #include <plat/clock.h>
 #include <plat/omap_hwmod.h>
+#include <plat/omap_device.h>
 #include <plat/prcm.h>
 
 #include "cm2xxx_3xxx.h"
@@ -2267,10 +2268,69 @@ ohsps_unlock:
 }
 
 /**
+ * omap_hwmod_set_max_dev_wakeup_lat - set a device wake-up constraint
+ * @oh: the device of @oh to set a constraint on.
+ * @req_oh: the device of @req_oh is the requester of the constraint.
+ * @t: wakeup latency constraint (us). -1 removes the existing constraint.
+ *
+ * Query the powerdomain of @oh to set/release the wake-up constraint.
+ * @oh is used to determine which power domain to set a constraint on.
+ * @req_oh is used to record the requester for later update or removal
+ * of a constraint.
+ *
+ * Returns -EINVAL if @oh or @req_oh have no power domain, or the return
+ * code from the pwrdm function (pwrdm_wakeuplat_set/release_constraint)
+ * of the powerdomain assocated with @oh.
+ */
+int omap_hwmod_set_max_dev_wakeup_lat(struct omap_hwmod *req_oh,
+				      struct omap_hwmod *oh, long t)
+{
+	struct device *req_dev;
+	struct platform_device *req_pdev;
+	struct powerdomain *pwrdm;
+
+	pwrdm = omap_hwmod_get_pwrdm(oh);
+
+	/* Catch devices with undefined powerdomains */
+	if (!PTR_ERR(pwrdm)) {
+		pr_err("omap_hwmod: Error: could not find parent "
+			"powerdomain for %s\n", oh->name);
+		return -EINVAL;
+	}
+
+	req_pdev = &(req_oh->od->pdev);
+	if (!PTR_ERR(req_pdev)) {
+		pr_err("omap_hwmod: Error: pdev not found for oh %s\n",
+		       oh->name);
+		return -EINVAL;
+	}
+
+	req_dev = &(req_pdev->dev);
+	if (!PTR_ERR(req_dev)) {
+		pr_err("omap_hwmod: Error: device not found for oh %s\n",
+		       oh->name);
+		return -EINVAL;
+	}
+
+	/* Call set/release_constraint for the given pwrdm */
+	if (t == -1) {
+		pr_debug("omap_hwmod: remove max device latency constraint: "
+			 "oh %s, pwrdm %s, req by oh %s\n",
+			 oh->name, pwrdm->name, req_oh->name);
+	} else {
+		pr_debug("omap_hwmod: add max device latency constraint: "
+			 "oh %s, t = %ld usec, pwrdm %s, req by oh %s\n",
+			 oh->name, t, pwrdm->name, req_oh->name);
+	}
+
+	return pwrdm_wakeuplat_set_constraint(pwrdm, req_dev, t);
+}
+
+/**
  * omap_hwmod_get_context_loss_count - get lost context count
  * @oh: struct omap_hwmod *
  *
- * Query the powerdomain of of @oh to get the context loss
+ * Query the powerdomain of @oh to get the context loss
  * count for this device.
  *
  * Returns the context loss count of the powerdomain assocated with @oh
diff --git a/arch/arm/mach-omap2/powerdomain.c b/arch/arm/mach-omap2/powerdomain.c
index eaed0df..6fb4741 100644
--- a/arch/arm/mach-omap2/powerdomain.c
+++ b/arch/arm/mach-omap2/powerdomain.c
@@ -19,6 +19,8 @@
 #include <linux/list.h>
 #include <linux/errno.h>
 #include <linux/string.h>
+#include <linux/slab.h>
+
 #include "cm2xxx_3xxx.h"
 #include "prcm44xx.h"
 #include "cm44xx.h"
@@ -103,6 +105,13 @@ static int _pwrdm_register(struct powerdomain *pwrdm)
 	pwrdm->state = pwrdm_read_pwrst(pwrdm);
 	pwrdm->state_counter[pwrdm->state] = 1;
 
+	/* Initialize priority ordered list for wakeup latency constraint */
+	spin_lock_init(&pwrdm->wakeuplat_lock);
+	plist_head_init(&pwrdm->wakeuplat_dev_list, &pwrdm->wakeuplat_lock);
+
+	/* res_mutex protects res_list add and del ops */
+	mutex_init(&pwrdm->wakeuplat_mutex);
+
 	pr_debug("powerdomain: registered %s\n", pwrdm->name);
 
 	return 0;
@@ -176,6 +185,74 @@ static int _pwrdm_post_transition_cb(struct powerdomain *pwrdm, void *unused)
 	return 0;
 }
 
+/**
+ * pwrdm_wakeuplat_update_pwrst - Update power domain power state if needed
+ * @pwrdm: struct powerdomain * to which requesting device belongs to.
+ *
+ * Finds the minimum allowed wake-up latency value from all entries
+ * in the list and the power domain power state needing the constraint.
+ * Programs a new target state if it is different from current power state.
+ *
+ * Only OMAP3xxx is supported for now
+ *
+ * Returns 0 upon success.
+ */
+static int pwrdm_wakeuplat_update_pwrst(struct powerdomain *pwrdm)
+{
+	struct plist_node *node;
+	int ret = 0, new_state;
+	long min_latency = -1;
+
+	/* Find the strongest constraint from the plist */
+	if (!plist_head_empty(&pwrdm->wakeuplat_dev_list)) {
+		node = plist_first(&pwrdm->wakeuplat_dev_list);
+		min_latency = node->prio;
+	}
+
+	/* Find power state with wakeup latency < minimum constraint. */
+	for (new_state = 0x0; new_state < PWRDM_MAX_PWRSTS; new_state++) {
+		if (min_latency == -1 ||
+		    pwrdm->wakeup_lat[new_state] <= min_latency)
+			break;
+	}
+
+	switch (new_state) {
+	case PWRDM_FUNC_PWRST_OFF:
+		new_state = PWRDM_POWER_OFF;
+		break;
+	case PWRDM_FUNC_PWRST_OSWR:
+		if (cpu_is_omap34xx())
+			pwrdm_set_logic_retst(pwrdm, PWRDM_POWER_OFF);
+		new_state = PWRDM_POWER_RET;
+		break;
+	case PWRDM_FUNC_PWRST_CSWR:
+		if (cpu_is_omap34xx())
+			pwrdm_set_logic_retst(pwrdm, PWRDM_POWER_RET);
+		new_state = PWRDM_POWER_RET;
+		break;
+	case PWRDM_FUNC_PWRST_ON:
+		new_state = PWRDM_POWER_ON;
+		break;
+	default:
+		pr_warn("powerdomain: requested latency constraint not "
+			"supported %s set to ON state\n", pwrdm->name);
+		new_state = PWRDM_POWER_ON;
+		break;
+	}
+
+	if (pwrdm_read_pwrst(pwrdm) != new_state) {
+		if (cpu_is_omap34xx())
+			ret = omap_set_pwrdm_state(pwrdm, new_state);
+	}
+
+	pr_debug("powerdomain: %s pwrst: curr=%d, prev=%d next=%d "
+		 "min_latency=%ld, set_state=%d\n", pwrdm->name,
+		 pwrdm_read_pwrst(pwrdm), pwrdm_read_prev_pwrst(pwrdm),
+		 pwrdm_read_next_pwrst(pwrdm), min_latency, new_state);
+
+	return ret;
+}
+
 /* Public functions */
 
 /**
@@ -910,6 +987,126 @@ int pwrdm_post_transition(void)
 	return 0;
 }
 
+/*
+ * Test if the current powerdomain is MPU or CORE
+ *
+ * This function is only used by pwrdm_wakeuplat_set_constraint
+ * which applies the MPU and CORE power domains constraints using PM QOS.
+ *
+ * This will disappear when all power domains will be controlled directly
+ * from the wake-up latency constraints framework
+ */
+static inline int is_pwrdm_mpu_or_core(struct powerdomain *pwrdm)
+{
+	if ((strncmp(pwrdm->name, "mpu_pwrdm", 9) == 0) ||
+	    (strncmp(pwrdm->name, "core_pwrdm", 10) == 0))
+		return 1;
+	else
+		return 0;
+}
+
+/*
+ * pwrdm_wakeuplat_set_constraint - Set/update/remove powerdomain wakeup
+ * latency constraint
+ * @pwrdm: struct powerdomain * to which requesting device belongs to
+ * @dev: struct device * of requesting device
+ * @t: wakeup latency constraint in microseconds. The value of -1 removes
+ * the constraint
+ *
+ * Constraint set/update: Adds new entry to powerdomain's wakeup latency
+ * constraint list.
+ * If the requesting device already exists in the list, old value is
+ * overwritten. Checks whether current power state is still adequate.
+ *
+ * Constraint removal: Removes device's entry from powerdomain's wakeup
+ * latency constraint list. Checks whether current power state is still
+ * adequate.
+ *
+ * Note about PM QOS: the MPU and CORE power domains get the next power
+ * state via cpuidle, which get the strongest wake-up latency constraint
+ * by querying PM QOS. The usage of PM QOS is temporary, until a generic
+ * solution is in place.
+ * The power domains other then MPU and CORE get the next power state
+ * programmed directly in the registers via pwrdm_wakeuplat_update_pwrst.
+ *
+ * Returns -EINVAL if the powerdomain or device pointer is NULL or
+ * no such entry exists in the list, or -ENOMEM if kmalloc fails, or
+ * returns 0 upon success.
+ */
+int pwrdm_wakeuplat_set_constraint(struct powerdomain *pwrdm,
+				   struct device *req_dev, long t)
+{
+	struct  wakeuplat_dev_list *user;
+	int found = 0, ret = 0;
+
+	if (!pwrdm || !req_dev) {
+		WARN(1, "powerdomain: %s: invalid parameter(s)", __func__);
+		return -EINVAL;
+	}
+
+	mutex_lock(&pwrdm->wakeuplat_mutex);
+
+	plist_for_each_entry(user, &pwrdm->wakeuplat_dev_list, node) {
+		if (user->dev == req_dev) {
+			found = 1;
+			break;
+		}
+	}
+
+	if (t >= 0) {
+		/* Add new entry to the list or update existing request */
+		if (found && user->constraint_us == t) {
+			goto exit_set;
+		} else if (!found) {
+			user = kzalloc(sizeof(struct wakeuplat_dev_list),
+				       GFP_KERNEL);
+			if (!user) {
+				pr_err("powerdomain: FATAL ERROR: kzalloc "
+				       "failed\n");
+				ret = -ENOMEM;
+				goto exit_set;
+			}
+			user->dev = req_dev;
+		} else {
+			plist_del(&user->node, &pwrdm->wakeuplat_dev_list);
+			if (is_pwrdm_mpu_or_core(pwrdm))
+				pm_qos_remove_request(&(user->pm_qos_request));
+		}
+
+		plist_node_init(&user->node, t);
+		plist_add(&user->node, &pwrdm->wakeuplat_dev_list);
+		user->node.prio = user->constraint_us = t;
+		if (is_pwrdm_mpu_or_core(pwrdm))
+			pm_qos_add_request(&(user->pm_qos_request),
+					   PM_QOS_CPU_DMA_LATENCY, t);
+	} else {
+		/* Remove the constraint from the list */
+		if (!found) {
+			pr_err("OMAP PM: Error: no prior constraint to "
+			       "release\n");
+			ret = -EINVAL;
+			goto exit_set;
+		}
+
+		if (is_pwrdm_mpu_or_core(pwrdm))
+			pm_qos_remove_request(&(user->pm_qos_request));
+		plist_del(&user->node, &pwrdm->wakeuplat_dev_list);
+		kfree(user);
+	}
+
+	/*
+	 * Constraints on MPU and CORE are passed to PM QOS while other power
+	 * domains get the next power state programmed directly
+	 */
+	if (!(is_pwrdm_mpu_or_core(pwrdm)))
+		ret = pwrdm_wakeuplat_update_pwrst(pwrdm);
+
+exit_set:
+	mutex_unlock(&pwrdm->wakeuplat_mutex);
+
+	return ret;
+}
+
 /**
  * pwrdm_get_context_loss_count - get powerdomain's context loss count
  * @pwrdm: struct powerdomain * to wait for
diff --git a/arch/arm/mach-omap2/powerdomain.h b/arch/arm/mach-omap2/powerdomain.h
index 0b7a357..98eea82 100644
--- a/arch/arm/mach-omap2/powerdomain.h
+++ b/arch/arm/mach-omap2/powerdomain.h
@@ -19,7 +19,10 @@
 
 #include <linux/types.h>
 #include <linux/list.h>
-
+#include <linux/plist.h>
+#include <linux/mutex.h>
+#include <linux/spinlock.h>
+#include <linux/pm_qos_params.h>
 #include <linux/atomic.h>
 
 #include <plat/cpu.h>
@@ -46,6 +49,15 @@
 
 #define PWRSTS_OFF_RET_ON	(PWRSTS_OFF_RET | (1 << PWRDM_POWER_ON))
 
+/* Powerdomain functional power states */
+#define PWRDM_FUNC_PWRST_OFF	0x0
+#define PWRDM_FUNC_PWRST_OSWR	0x1
+#define PWRDM_FUNC_PWRST_CSWR	0x2
+#define PWRDM_FUNC_PWRST_ON	0x3
+
+#define PWRDM_MAX_FUNC_PWRSTS	4
+
+#define UNSUP_STATE		-1
 
 /* Powerdomain flags */
 #define PWRDM_HAS_HDWR_SAR	(1 << 0) /* hardware save-and-restore support */
@@ -96,6 +108,12 @@ struct powerdomain;
  * @state_counter:
  * @timer:
  * @state_timer:
+ * @wakeup_lat: wakeup latencies for possible powerdomain power states
+ * Note about the wakeup latencies ordering: the values must be sorted
+ *  in decremental order
+ * @wakeuplat_lock: spinlock for plist
+ * @wakeuplat_dev_list: plist_head linking all devices placing constraint
+ * @wakeuplat_mutex: mutex to protect per powerdomain list ops
  *
  * @prcm_partition possible values are defined in mach-omap2/prcm44xx.h.
  */
@@ -121,6 +139,22 @@ struct powerdomain {
 	s64 timer;
 	s64 state_timer[PWRDM_MAX_PWRSTS];
 #endif
+	const u32 wakeup_lat[PWRDM_MAX_FUNC_PWRSTS];
+	spinlock_t wakeuplat_lock;
+	struct plist_head wakeuplat_dev_list;
+	struct mutex wakeuplat_mutex;
+};
+
+/*
+ * Linked list for the power domains wakeup latencies constraints
+ * The PM QOS handle is only used to control the MPU and CORE power
+ * domains states; to be removed when a generic solution is in place.
+*/
+struct wakeuplat_dev_list {
+	struct device			*dev;
+	unsigned long			constraint_us;
+	struct plist_node		node;
+	struct pm_qos_request_list	pm_qos_request;
 };
 
 /**
@@ -210,6 +244,9 @@ int pwrdm_clkdm_state_switch(struct clockdomain *clkdm);
 int pwrdm_pre_transition(void);
 int pwrdm_post_transition(void);
 int pwrdm_set_lowpwrstchange(struct powerdomain *pwrdm);
+
+int pwrdm_wakeuplat_set_constraint(struct powerdomain *pwrdm,
+				   struct device *req_dev, long t);
 u32 pwrdm_get_context_loss_count(struct powerdomain *pwrdm);
 
 extern void omap2xxx_powerdomains_init(void);
diff --git a/arch/arm/mach-omap2/powerdomains3xxx_data.c b/arch/arm/mach-omap2/powerdomains3xxx_data.c
index e1bec56..4f7e44d 100644
--- a/arch/arm/mach-omap2/powerdomains3xxx_data.c
+++ b/arch/arm/mach-omap2/powerdomains3xxx_data.c
@@ -52,6 +52,12 @@ static struct powerdomain iva2_pwrdm = {
 		[2] = PWRSTS_OFF_ON,
 		[3] = PWRDM_POWER_ON,
 	},
+	.wakeup_lat = {
+		[PWRDM_FUNC_PWRST_OFF] = 1100,
+		[PWRDM_FUNC_PWRST_OSWR] = UNSUP_STATE,
+		[PWRDM_FUNC_PWRST_CSWR] = 350,
+		[PWRDM_FUNC_PWRST_ON] = 0,
+	},
 };
 
 static struct powerdomain mpu_3xxx_pwrdm = {
@@ -68,6 +74,12 @@ static struct powerdomain mpu_3xxx_pwrdm = {
 	.pwrsts_mem_on	  = {
 		[0] = PWRSTS_OFF_ON,
 	},
+	.wakeup_lat = {
+		[PWRDM_FUNC_PWRST_OFF] = 95,
+		[PWRDM_FUNC_PWRST_OSWR] = UNSUP_STATE,
+		[PWRDM_FUNC_PWRST_CSWR] = 45,
+		[PWRDM_FUNC_PWRST_ON] = 0,
+	},
 };
 
 /*
@@ -98,6 +110,12 @@ static struct powerdomain core_3xxx_pre_es3_1_pwrdm = {
 		[0] = PWRSTS_OFF_RET_ON, /* MEM1ONSTATE */
 		[1] = PWRSTS_OFF_RET_ON, /* MEM2ONSTATE */
 	},
+	.wakeup_lat = {
+		[PWRDM_FUNC_PWRST_OFF] = 100,
+		[PWRDM_FUNC_PWRST_OSWR] = UNSUP_STATE,
+		[PWRDM_FUNC_PWRST_CSWR] = 60,
+		[PWRDM_FUNC_PWRST_ON] = 0,
+	},
 };
 
 static struct powerdomain core_3xxx_es3_1_pwrdm = {
@@ -121,6 +139,12 @@ static struct powerdomain core_3xxx_es3_1_pwrdm = {
 		[0] = PWRSTS_OFF_RET_ON, /* MEM1ONSTATE */
 		[1] = PWRSTS_OFF_RET_ON, /* MEM2ONSTATE */
 	},
+	.wakeup_lat = {
+		[PWRDM_FUNC_PWRST_OFF] = 100,
+		[PWRDM_FUNC_PWRST_OSWR] = UNSUP_STATE,
+		[PWRDM_FUNC_PWRST_CSWR] = 60,
+		[PWRDM_FUNC_PWRST_ON] = 0,
+	},
 };
 
 static struct powerdomain dss_pwrdm = {
@@ -136,6 +160,12 @@ static struct powerdomain dss_pwrdm = {
 	.pwrsts_mem_on	  = {
 		[0] = PWRDM_POWER_ON,  /* MEMONSTATE */
 	},
+	.wakeup_lat = {
+		[PWRDM_FUNC_PWRST_OFF] = 70,
+		[PWRDM_FUNC_PWRST_OSWR] = UNSUP_STATE,
+		[PWRDM_FUNC_PWRST_CSWR] = 20,
+		[PWRDM_FUNC_PWRST_ON] = 0,
+	},
 };
 
 /*
@@ -157,6 +187,12 @@ static struct powerdomain sgx_pwrdm = {
 	.pwrsts_mem_on	  = {
 		[0] = PWRDM_POWER_ON,  /* MEMONSTATE */
 	},
+	.wakeup_lat = {
+		[PWRDM_FUNC_PWRST_OFF] = 1000,
+		[PWRDM_FUNC_PWRST_OSWR] = UNSUP_STATE,
+		[PWRDM_FUNC_PWRST_CSWR] = UNSUP_STATE,
+		[PWRDM_FUNC_PWRST_ON] = 0,
+	},
 };
 
 static struct powerdomain cam_pwrdm = {
@@ -172,6 +208,12 @@ static struct powerdomain cam_pwrdm = {
 	.pwrsts_mem_on	  = {
 		[0] = PWRDM_POWER_ON,  /* MEMONSTATE */
 	},
+	.wakeup_lat = {
+		[PWRDM_FUNC_PWRST_OFF] = 850,
+		[PWRDM_FUNC_PWRST_OSWR] = UNSUP_STATE,
+		[PWRDM_FUNC_PWRST_CSWR] = 35,
+		[PWRDM_FUNC_PWRST_ON] = 0,
+	},
 };
 
 static struct powerdomain per_pwrdm = {
@@ -187,6 +229,12 @@ static struct powerdomain per_pwrdm = {
 	.pwrsts_mem_on	  = {
 		[0] = PWRDM_POWER_ON,  /* MEMONSTATE */
 	},
+	.wakeup_lat = {
+		[PWRDM_FUNC_PWRST_OFF] = 200,
+		[PWRDM_FUNC_PWRST_OSWR] = UNSUP_STATE,
+		[PWRDM_FUNC_PWRST_CSWR] = 110,
+		[PWRDM_FUNC_PWRST_ON] = 0,
+	},
 };
 
 static struct powerdomain emu_pwrdm = {
@@ -201,6 +249,12 @@ static struct powerdomain neon_pwrdm = {
 	.omap_chip	  = OMAP_CHIP_INIT(CHIP_IS_OMAP3430),
 	.pwrsts		  = PWRSTS_OFF_RET_ON,
 	.pwrsts_logic_ret = PWRDM_POWER_RET,
+	.wakeup_lat = {
+		[PWRDM_FUNC_PWRST_OFF] = 200,
+		[PWRDM_FUNC_PWRST_OSWR] = UNSUP_STATE,
+		[PWRDM_FUNC_PWRST_CSWR] = 35,
+		[PWRDM_FUNC_PWRST_ON] = 0,
+	},
 };
 
 static struct powerdomain usbhost_pwrdm = {
@@ -223,6 +277,12 @@ static struct powerdomain usbhost_pwrdm = {
 	.pwrsts_mem_on	  = {
 		[0] = PWRDM_POWER_ON,  /* MEMONSTATE */
 	},
+	.wakeup_lat = {
+		[PWRDM_FUNC_PWRST_OFF] = 800,
+		[PWRDM_FUNC_PWRST_OSWR] = UNSUP_STATE,
+		[PWRDM_FUNC_PWRST_CSWR] = 150,
+		[PWRDM_FUNC_PWRST_ON] = 0,
+	},
 };
 
 static struct powerdomain dpll1_pwrdm = {
diff --git a/arch/arm/plat-omap/include/plat/omap_device.h b/arch/arm/plat-omap/include/plat/omap_device.h
index e4c349f..5da6b47 100644
--- a/arch/arm/plat-omap/include/plat/omap_device.h
+++ b/arch/arm/plat-omap/include/plat/omap_device.h
@@ -107,6 +107,8 @@ void __iomem *omap_device_get_rt_va(struct omap_device *od);
 int omap_device_align_pm_lat(struct platform_device *pdev,
 			     u32 new_wakeup_lat_limit);
 struct powerdomain *omap_device_get_pwrdm(struct omap_device *od);
+int omap_device_set_max_dev_wakeup_lat(struct platform_device *req_pdev,
+				       struct platform_device *pdev, long t);
 u32 omap_device_get_context_loss_count(struct platform_device *pdev);
 
 /* Other */
diff --git a/arch/arm/plat-omap/include/plat/omap_hwmod.h b/arch/arm/plat-omap/include/plat/omap_hwmod.h
index 4f5b23e..7fcf168 100644
--- a/arch/arm/plat-omap/include/plat/omap_hwmod.h
+++ b/arch/arm/plat-omap/include/plat/omap_hwmod.h
@@ -595,6 +595,8 @@ int omap_hwmod_for_each_by_class(const char *classname,
 				 void *user);
 
 int omap_hwmod_set_postsetup_state(struct omap_hwmod *oh, u8 state);
+int omap_hwmod_set_max_dev_wakeup_lat(struct omap_hwmod *req_oh,
+				      struct omap_hwmod *oh, long t);
 u32 omap_hwmod_get_context_loss_count(struct omap_hwmod *oh);
 
 /*
diff --git a/arch/arm/plat-omap/omap-pm-constraints.c b/arch/arm/plat-omap/omap-pm-constraints.c
index c8b4e4c..df24e5e 100644
--- a/arch/arm/plat-omap/omap-pm-constraints.c
+++ b/arch/arm/plat-omap/omap-pm-constraints.c
@@ -24,6 +24,7 @@
 /* Interface documentation is in mach/omap-pm.h */
 #include <plat/omap-pm.h>
 #include <plat/omap_device.h>
+#include <plat/common.h>
 
 static bool off_mode_enabled;
 static u32 dummy_context_loss_counter;
@@ -32,34 +33,6 @@ static u32 dummy_context_loss_counter;
  * Device-driver-originated constraints (via board-*.c files)
  */
 
-int omap_pm_set_max_mpu_wakeup_lat(struct device *dev, long t)
-{
-	if (!dev || t < -1) {
-		WARN(1, "OMAP PM: %s: invalid parameter(s)", __func__);
-		return -EINVAL;
-	};
-
-	if (t == -1)
-		pr_debug("OMAP PM: remove max MPU wakeup latency constraint: "
-			 "dev %s\n", dev_name(dev));
-	else
-		pr_debug("OMAP PM: add max MPU wakeup latency constraint: "
-			 "dev %s, t = %ld usec\n", dev_name(dev), t);
-
-	/*
-	 * For current Linux, this needs to map the MPU to a
-	 * powerdomain, then go through the list of current max lat
-	 * constraints on the MPU and find the smallest.  If
-	 * the latency constraint has changed, the code should
-	 * recompute the state to enter for the next powerdomain
-	 * state.
-	 *
-	 * TI CDP code can call constraint_set here.
-	 */
-
-	return 0;
-}
-
 int omap_pm_set_min_bus_tput(struct device *dev, u8 agent_id, unsigned long r)
 {
 	if (!dev || (agent_id != OCP_INITIATOR_AGENT &&
@@ -87,64 +60,86 @@ int omap_pm_set_min_bus_tput(struct device *dev, u8 agent_id, unsigned long r)
 	return 0;
 }
 
+/*
+ * omap_pm_set_max_dev_wakeup_lat - set/release devices wake-up latency
+ * constraints
+ */
 int omap_pm_set_max_dev_wakeup_lat(struct device *req_dev, struct device *dev,
 				   long t)
 {
+	struct platform_device *pdev, *req_pdev;
+	int ret = 0;
+
 	if (!req_dev || !dev || t < -1) {
 		WARN(1, "OMAP PM: %s: invalid parameter(s)", __func__);
 		return -EINVAL;
+	}
+
+	/* Look for the platform devices */
+	pdev = to_platform_device(dev);
+	req_pdev = to_platform_device(req_dev);
+
+	/* Try to catch non platform devices. */
+	if ((pdev->name == NULL) || (req_pdev->name == NULL)) {
+		pr_err("OMAP-PM set_wakeup_lat: Error: platform devices "
+		       "not valid\n");
+		return -EINVAL;
+	} else {
+		/* Call the omap_device API */
+		ret = omap_device_set_max_dev_wakeup_lat(req_pdev, pdev, t);
+	}
+
+	return ret;
+}
+
+/*
+ * omap_pm_set_max_mpu_wakeup_lat - set/release MPU wake-up latency
+ * constraints
+ *
+ * Maps to omap_pm_set_max_dev_wakeup_lat with the MPU device as constraints
+ * target.
+ */
+int omap_pm_set_max_mpu_wakeup_lat(struct device *req_dev, long t)
+{
+	if (!req_dev || t < -1) {
+		WARN(1, "OMAP PM: %s: invalid parameter(s)", __func__);
+		return -EINVAL;
 	};
 
 	if (t == -1)
-		pr_debug("OMAP PM: remove max device latency constraint: "
-			 "dev %s\n", dev_name(dev));
+		pr_debug("OMAP PM: remove max MPU wakeup latency constraint: "
+			 "dev %s\n", dev_name(req_dev));
 	else
-		pr_debug("OMAP PM: add max device latency constraint: "
-			 "dev %s, t = %ld usec\n", dev_name(dev), t);
-
-	/*
-	 * For current Linux, this needs to map the device to a
-	 * powerdomain, then go through the list of current max lat
-	 * constraints on that powerdomain and find the smallest.  If
-	 * the latency constraint has changed, the code should
-	 * recompute the state to enter for the next powerdomain
-	 * state.  Conceivably, this code should also determine
-	 * whether to actually disable the device clocks or not,
-	 * depending on how long it takes to re-enable the clocks.
-	 *
-	 * TI CDP code can call constraint_set here.
-	 */
+		pr_debug("OMAP PM: add max MPU wakeup latency constraint: "
+			 "dev %s, t = %ld usec\n", dev_name(req_dev), t);
 
-	return 0;
+	return omap_pm_set_max_dev_wakeup_lat(req_dev,
+					      omap2_get_mpuss_device(), t);
 }
 
-int omap_pm_set_max_sdma_lat(struct device *dev, long t)
+/*
+ * omap_pm_set_max_sdma_lat - set/release SDMA start latency
+ * constraints
+ *
+ * Currenlt maps to omap_pm_set_max_dev_wakeup_lat with the L3 device as
+ * constraints target.
+ */
+int omap_pm_set_max_sdma_lat(struct device *req_dev, long t)
 {
-	if (!dev || t < -1) {
+	if (!req_dev || t < -1) {
 		WARN(1, "OMAP PM: %s: invalid parameter(s)", __func__);
 		return -EINVAL;
 	};
 
 	if (t == -1)
 		pr_debug("OMAP PM: remove max DMA latency constraint: "
-			 "dev %s\n", dev_name(dev));
+			 "dev %s\n", dev_name(req_dev));
 	else
 		pr_debug("OMAP PM: add max DMA latency constraint: "
-			 "dev %s, t = %ld usec\n", dev_name(dev), t);
+			 "dev %s, t = %ld usec\n", dev_name(req_dev), t);
 
-	/*
-	 * For current Linux PM QOS params, this code should scan the
-	 * list of maximum CPU and DMA latencies and select the
-	 * smallest, then set cpu_dma_latency pm_qos_param
-	 * accordingly.
-	 *
-	 * For future Linux PM QOS params, with separate CPU and DMA
-	 * latency params, this code should just set the dma_latency param.
-	 *
-	 * TI CDP code can call constraint_set here.
-	 */
-
-	return 0;
+	return omap_pm_set_max_dev_wakeup_lat(req_dev,
+					      omap2_get_l3_device(), t);
 }
 
 int omap_pm_set_min_clk_rate(struct device *dev, struct clk *c, long r)
diff --git a/arch/arm/plat-omap/omap_device.c b/arch/arm/plat-omap/omap_device.c
index 9bbda9a..43dba02 100644
--- a/arch/arm/plat-omap/omap_device.c
+++ b/arch/arm/plat-omap/omap_device.c
@@ -296,6 +296,34 @@ static void _add_optional_clock_clkdev(struct omap_device *od,
 /* Public functions for use by core code */
 
 /**
+ * omap_device_set_max_dev_wakeup_lat - set/release a device constraint
+ * @od: struct omap_device *
+ * @req_od: struct omap_device *
+ *
+ * Using the primary hwmod, set/release a device constraint for the pdev
+ * device, requested by the req_pdev device.
+ *
+ * If any hwmods exist for the omap_device assoiated with @pdev and @req_pdev,
+ * set/release the constraint for the corresponding hwmods, otherwise return
+ * -EINVAL.
+ */
+int omap_device_set_max_dev_wakeup_lat(struct platform_device *req_pdev,
+				       struct platform_device *pdev, long t)
+{
+	struct omap_device *od, *req_od;
+	u32 ret = -EINVAL;
+
+	od = _find_by_pdev(pdev);
+	req_od = _find_by_pdev(req_pdev);
+
+	if ((od->hwmods_cnt) && (req_od->hwmods_cnt))
+		ret = omap_hwmod_set_max_dev_wakeup_lat(req_od->hwmods[0],
+							od->hwmods[0], t);
+
+	return ret;
+}
+
+/**
  * omap_device_get_context_loss_count - get lost context count
  * @od: struct omap_device *
  *
-- 
1.7.2.3

^ permalink raw reply related	[flat|nested] 19+ messages in thread

end of thread, other threads:[~2011-03-08 17:33 UTC | newest]

Thread overview: 19+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2011-02-10 19:23 [RFC PATCH 0/2] OMAP: PM: add devices wakeup latency constraints APIs jean.pihet
2011-02-10 19:23 ` [PATCH 1/2] OMAP PM: create a PM layer plugin for the devices wakeup latency constraints jean.pihet
2011-02-10 19:23 ` [PATCH 2/2] OMAP: PM: implement devices wakeup latency constraints APIs jean.pihet
2011-02-11 10:23   ` Gulati, Shweta
2011-02-11 16:31     ` Jean Pihet
2011-02-15  9:35   ` Vishwanath Sripathy
2011-02-15 13:52     ` Jean Pihet
2011-02-16 10:49       ` Vishwanath Sripathy
2011-02-16 12:58         ` Pihet-XID, Jean
2011-02-18 11:04   ` Rajendra Nayak
2011-02-18 15:03     ` Jean Pihet
2011-03-04 14:52 [PATCH 0/2] " Jean Pihet
2011-03-04 14:52 ` [PATCH 2/2] " Jean Pihet
2011-03-04 14:52   ` Jean Pihet
2011-03-08  2:15   ` Kevin Hilman
2011-03-08  2:15     ` Kevin Hilman
2011-03-08 15:54     ` Jean Pihet
2011-03-08 15:54       ` Jean Pihet
2011-03-08 17:33       ` Kevin Hilman
2011-03-08 17:33         ` Kevin Hilman

This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.