All of lore.kernel.org
 help / color / mirror / Atom feed
* [PATCH v9 0/8] PM QoS: implement the OMAP low level constraints management code
@ 2012-09-18  8:52 ` Jean Pihet
  0 siblings, 0 replies; 28+ messages in thread
From: Jean Pihet @ 2012-09-18  8:52 UTC (permalink / raw)
  To: linux-omap, paul, linux-arm-kernel, khilman; +Cc: Jean Pihet

. Implement the devices wake-up latency constraints using the global
  device PM QoS notification handler which applies the constraints to the
  underlying layer,
. Implement the low level code which controls the power domains next
  functional power states, through the hwmod and pwrdm layers,
. Add cpuidle and power domains wake-up latency figures for OMAP3, cf. 
  comments in the code and [1] for the details on where the numbers
  are magically coming from,
. Implement the relation between the cpuidle and per-device PM QoS frameworks
  in the OMAP3 specific idle callbacks.
  The chosen C-state shall satisfy the following conditions:
   . it satisfies the enable_off_mode flag,
   . the next state for MPU and CORE power domains is not lower than the
     state programmed by the per-device PM QoS.
. convert I2C driver to PM QoS for latency constraints,
. remove the latency related functions from the API (omap_pm_set_*) and
  update the kernel Documentation accordingly.


ToDo:
1. Re-visit the OMAP power domains states initialization procedure. Currently
   the power states that have been changed from the constraints API which were
   applied before the initialization of the power domains are lost
2. Further clean-up the OMAP PM layer, use the generic frameworks instead (OPP,
   PM QoS for throughput constraints ...)


Based on mainline kernel 3.6.0-rc4 with the functional power states changes v6 [2]
applied.

Tested cpuidle and suspend on OMAP3 Beagleboard (ES2.x) with constraints
on MPU, CORE, PER in RETention and OFF modes.

[1] http://www.omappedia.org/wiki/Power_Management_Device_Latencies_Measurement
[2] http://marc.info/?l=linux-omap&m=134744375320874&w=2


History:
v9:
. ported to latest functional power states code,
. changed the locking from mutex to spinlock, as reported by Djamil Elaidi
  <d-elaidi@ti.com>,
. simplified the memory allocation code, using GFP_ATOMIC since the code can be
  used under spinlock

v8:
. reworked the code after internal review and testing with OMAP3&4 device
   OFF

v7:
. rebased on top of the functional power state changes

v6:
. minor change in the commits description after Kevin's review
. added Kevin's Reviewed-by

v5:
. rebased on latest linux-omap
. rework after Kevin's comments on the MLs

v4:
. split up the patches which remove the omap_pm_ code from the patch set.
  Those patches are to be submitted later, on top of this patch set.
. latency numbers: provide the measurements setup and conditions in the code
  comments, added the link to the details on wiki [1].
. improved kerneldoc
. split big functions into smaller ones, in order to improve the readability

v3: reworked the error return path and improved the kerneldoc

v2: reworked the OMAP specific cpuidle code to demote the initial C-state to
     a valid C-state which fulfills the per-device constraints

v1: initial version


Jean Pihet (8):
  ARM: OMAP2+: PM QoS: control the power domains next state from the
    constraints
  ARM: OMAP2+: hwmod: manage the wake-up latency constraints
  ARM: OMAP: omap_device: register to the per-device PM QoS framework
  ARM: OMAP3: cpuidle: next C-state decision depends on the PM QoS MPU
    and CORE constraints
  ARM: OMAP3: update cpuidle latency and threshold figures
  ARM: OMAP3: powerdomain data: add wake-up latency figures
  ARM: OMAP: convert I2C driver to PM QoS for latency constraints
  ARM: OMAP: PM: remove the latency related functions from the API

 Documentation/arm/OMAP/omap_pm               |   68 +++------
 arch/arm/mach-omap2/cpuidle34xx.c            |  103 +++++++++----
 arch/arm/mach-omap2/omap_hwmod.c             |   44 +++++-
 arch/arm/mach-omap2/powerdomain.c            |  212 ++++++++++++++++++++++++++
 arch/arm/mach-omap2/powerdomain.h            |   18 ++-
 arch/arm/mach-omap2/powerdomains3xxx_data.c  |   83 ++++++++++
 arch/arm/plat-omap/i2c.c                     |   21 ---
 arch/arm/plat-omap/include/plat/omap-pm.h    |   99 ------------
 arch/arm/plat-omap/include/plat/omap_hwmod.h |    5 +
 arch/arm/plat-omap/omap-pm-noop.c            |   88 -----------
 arch/arm/plat-omap/omap_device.c             |   81 ++++++++++-
 drivers/i2c/busses/i2c-omap.c                |   28 ++--
 include/linux/i2c-omap.h                     |    1 -
 13 files changed, 550 insertions(+), 301 deletions(-)

-- 
1.7.7.6


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

* [PATCH v9 0/8] PM QoS: implement the OMAP low level constraints management code
@ 2012-09-18  8:52 ` Jean Pihet
  0 siblings, 0 replies; 28+ messages in thread
From: Jean Pihet @ 2012-09-18  8:52 UTC (permalink / raw)
  To: linux-arm-kernel

. Implement the devices wake-up latency constraints using the global
  device PM QoS notification handler which applies the constraints to the
  underlying layer,
. Implement the low level code which controls the power domains next
  functional power states, through the hwmod and pwrdm layers,
. Add cpuidle and power domains wake-up latency figures for OMAP3, cf. 
  comments in the code and [1] for the details on where the numbers
  are magically coming from,
. Implement the relation between the cpuidle and per-device PM QoS frameworks
  in the OMAP3 specific idle callbacks.
  The chosen C-state shall satisfy the following conditions:
   . it satisfies the enable_off_mode flag,
   . the next state for MPU and CORE power domains is not lower than the
     state programmed by the per-device PM QoS.
. convert I2C driver to PM QoS for latency constraints,
. remove the latency related functions from the API (omap_pm_set_*) and
  update the kernel Documentation accordingly.


ToDo:
1. Re-visit the OMAP power domains states initialization procedure. Currently
   the power states that have been changed from the constraints API which were
   applied before the initialization of the power domains are lost
2. Further clean-up the OMAP PM layer, use the generic frameworks instead (OPP,
   PM QoS for throughput constraints ...)


Based on mainline kernel 3.6.0-rc4 with the functional power states changes v6 [2]
applied.

Tested cpuidle and suspend on OMAP3 Beagleboard (ES2.x) with constraints
on MPU, CORE, PER in RETention and OFF modes.

[1] http://www.omappedia.org/wiki/Power_Management_Device_Latencies_Measurement
[2] http://marc.info/?l=linux-omap&m=134744375320874&w=2


History:
v9:
. ported to latest functional power states code,
. changed the locking from mutex to spinlock, as reported by Djamil Elaidi
  <d-elaidi@ti.com>,
. simplified the memory allocation code, using GFP_ATOMIC since the code can be
  used under spinlock

v8:
. reworked the code after internal review and testing with OMAP3&4 device
   OFF

v7:
. rebased on top of the functional power state changes

v6:
. minor change in the commits description after Kevin's review
. added Kevin's Reviewed-by

v5:
. rebased on latest linux-omap
. rework after Kevin's comments on the MLs

v4:
. split up the patches which remove the omap_pm_ code from the patch set.
  Those patches are to be submitted later, on top of this patch set.
. latency numbers: provide the measurements setup and conditions in the code
  comments, added the link to the details on wiki [1].
. improved kerneldoc
. split big functions into smaller ones, in order to improve the readability

v3: reworked the error return path and improved the kerneldoc

v2: reworked the OMAP specific cpuidle code to demote the initial C-state to
     a valid C-state which fulfills the per-device constraints

v1: initial version


Jean Pihet (8):
  ARM: OMAP2+: PM QoS: control the power domains next state from the
    constraints
  ARM: OMAP2+: hwmod: manage the wake-up latency constraints
  ARM: OMAP: omap_device: register to the per-device PM QoS framework
  ARM: OMAP3: cpuidle: next C-state decision depends on the PM QoS MPU
    and CORE constraints
  ARM: OMAP3: update cpuidle latency and threshold figures
  ARM: OMAP3: powerdomain data: add wake-up latency figures
  ARM: OMAP: convert I2C driver to PM QoS for latency constraints
  ARM: OMAP: PM: remove the latency related functions from the API

 Documentation/arm/OMAP/omap_pm               |   68 +++------
 arch/arm/mach-omap2/cpuidle34xx.c            |  103 +++++++++----
 arch/arm/mach-omap2/omap_hwmod.c             |   44 +++++-
 arch/arm/mach-omap2/powerdomain.c            |  212 ++++++++++++++++++++++++++
 arch/arm/mach-omap2/powerdomain.h            |   18 ++-
 arch/arm/mach-omap2/powerdomains3xxx_data.c  |   83 ++++++++++
 arch/arm/plat-omap/i2c.c                     |   21 ---
 arch/arm/plat-omap/include/plat/omap-pm.h    |   99 ------------
 arch/arm/plat-omap/include/plat/omap_hwmod.h |    5 +
 arch/arm/plat-omap/omap-pm-noop.c            |   88 -----------
 arch/arm/plat-omap/omap_device.c             |   81 ++++++++++-
 drivers/i2c/busses/i2c-omap.c                |   28 ++--
 include/linux/i2c-omap.h                     |    1 -
 13 files changed, 550 insertions(+), 301 deletions(-)

-- 
1.7.7.6

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

* [PATCH 1/8] ARM: OMAP2+: PM QoS: control the power domains next state from the constraints
  2012-09-18  8:52 ` Jean Pihet
@ 2012-09-18  8:52   ` Jean Pihet
  -1 siblings, 0 replies; 28+ messages in thread
From: Jean Pihet @ 2012-09-18  8:52 UTC (permalink / raw)
  To: linux-omap, paul, linux-arm-kernel, khilman; +Cc: Jean Pihet, Djamil Elaidi

When a PM QoS device latency constraint is requested or removed the
constraint is stored in the constraints list of the corresponding power
domain, then the aggregated constraint value is applied by programming
the next functional power state using pwrdm_set_fpwrst.

The per-device PM QoS locking requires a spinlock to be used. The reasons
is that some drivers need to use the per-device PM QoS functionality from
interrupt context or spinlock protected context, as reported by Djamil.
An example of such a driver is the OMAP HSI (high-speed synchronous serial
interface) driver which needs to control the IP block idle state from
the FIFO empty state, from interrupt context.

Tested on OMAP3 Beagleboard and OMAP4 Pandaboard in RET/OFF using
wake-up latency constraints on MPU, CORE and PER.

Signed-off-by: Jean Pihet <j-pihet@ti.com>
Cc: Djamil Elaidi <d-elaidi@ti.com>
---
 arch/arm/mach-omap2/powerdomain.c |  212 +++++++++++++++++++++++++++++++++++++
 arch/arm/mach-omap2/powerdomain.h |   18 +++-
 2 files changed, 229 insertions(+), 1 deletions(-)

diff --git a/arch/arm/mach-omap2/powerdomain.c b/arch/arm/mach-omap2/powerdomain.c
index 37dfabf..3d81ac7 100644
--- a/arch/arm/mach-omap2/powerdomain.c
+++ b/arch/arm/mach-omap2/powerdomain.c
@@ -17,8 +17,10 @@
 #include <linux/kernel.h>
 #include <linux/types.h>
 #include <linux/list.h>
+#include <linux/slab.h>
 #include <linux/errno.h>
 #include <linux/string.h>
+#include <linux/pm_qos.h>
 #include <linux/ratelimit.h>
 #include <trace/events/power.h>
 
@@ -114,6 +116,12 @@ static int _pwrdm_register(struct powerdomain *pwrdm)
 	for (i = 0; i < pwrdm->banks; i++)
 		pwrdm->ret_mem_off_counter[i] = 0;
 
+	/* Initialize the per-device wake-up constraints framework data */
+	spin_lock_init(&pwrdm->wkup_lat_plist_lock);
+	plist_head_init(&pwrdm->wkup_lat_plist_head);
+	pwrdm->wkup_lat_next_state = PWRDM_FUNC_PWRST_OFF;
+
+	/* Initialize the pwrdm state */
 	pwrdm_wait_transition(pwrdm);
 	pwrdm->state = pwrdm_read_fpwrst(pwrdm);
 	pwrdm->state_counter[pwrdm->state] = 1;
@@ -418,6 +426,60 @@ static int _pwrdm_read_next_fpwrst(struct powerdomain *pwrdm)
 	return _pwrdm_pwrst_to_fpwrst(pwrdm, next_pwrst, next_logic);
 }
 
+/**
+ * _pwrdm_wakeuplat_update_pwrst - Update power domain power state if needed
+ * @pwrdm: struct powerdomain * to which requesting device belongs to.
+ * @min_latency: the allowed wake-up latency for the given power domain. A
+ *  value of PM_QOS_DEV_LAT_DEFAULT_VALUE means 'no constraint' on the pwrdm.
+ *
+ * Finds the power domain next power state that fulfills the constraint.
+ * Programs a new target state if it is different from current power state.
+ * The power domains get the next power state programmed directly in the
+ * registers.
+ *
+ * Must be called with the wkup_lat_plist_lock lock held.
+ *
+ * Returns 0 in case of success, -EINVAL in case of invalid parameters,
+ * or the return value from pwrdm_set_fpwrst.
+ */
+static int _pwrdm_wakeuplat_update_pwrst(struct powerdomain *pwrdm,
+					 long min_latency)
+{
+	int ret = 0, state, new_state = PWRDM_FUNC_PWRST_ON;
+
+	if (!pwrdm) {
+		WARN(1, "powerdomain: %s: invalid parameter(s)", __func__);
+		return -EINVAL;
+	}
+
+	/*
+	 * Find the next supported power state with
+	 *  wakeup latency <= min_latency.
+	 * Pick the lower state if no constraint on the pwrdm
+	 *  (min_latency == PM_QOS_DEV_LAT_DEFAULT_VALUE).
+	 * Skip the states marked as unsupported (UNSUP_STATE).
+	 * If no power state found, fall back to PWRDM_FUNC_PWRST_ON.
+	 */
+	for (state = 0x0; state < PWRDM_MAX_FUNC_PWRSTS; state++) {
+		if ((min_latency == PM_QOS_DEV_LAT_DEFAULT_VALUE) ||
+		    ((pwrdm->wakeup_lat[state] != UNSUP_STATE) &&
+		     (pwrdm->wakeup_lat[state] <= min_latency))) {
+			new_state = state;
+			break;
+		}
+	}
+
+	pwrdm->wkup_lat_next_state = new_state;
+	ret = pwrdm_set_fpwrst(pwrdm, new_state);
+
+	pr_debug("%s: func pwrst for %s: curr=%d, next=%d, min_latency=%ld, new_state=%d\n",
+		 __func__, pwrdm->name, pwrdm_read_fpwrst(pwrdm),
+		 pwrdm_read_next_fpwrst(pwrdm), min_latency, new_state);
+
+	return ret;
+}
+
+
 /* Public functions */
 
 /**
@@ -1484,6 +1546,156 @@ int pwrdm_post_transition(struct powerdomain *pwrdm)
 }
 
 /**
+ * pwrdm_wakeuplat_update_constraint - Set or update a powerdomain wakeup
+ *  latency constraint and apply it
+ * @pwrdm: struct powerdomain * which the constraint applies to
+ * @cookie: constraint identifier, used for tracking
+ * @min_latency: minimum wakeup latency constraint (in microseconds) for
+ *  the given pwrdm
+ *
+ * Tracks the constraints by @cookie.
+ * Constraint set/update: Adds a new entry to powerdomain's wake-up latency
+ * constraint list. If the constraint identifier already exists in the list,
+ * the old value is overwritten.
+ *
+ * Applies the aggregated constraint value for the given pwrdm by calling
+ * _pwrdm_wakeuplat_update_pwrst.
+ *
+ * Returns 0 upon success, -ENOMEM in case of memory shortage, -EINVAL in
+ * case of invalid latency value, or the return value from
+ * _pwrdm_wakeuplat_update_pwrst.
+ *
+ * The caller must check the validity of the parameters.
+ */
+int pwrdm_wakeuplat_update_constraint(struct powerdomain *pwrdm, void *cookie,
+				      long min_latency)
+{
+	struct pwrdm_wkup_constraints_entry *tmp_user, *user = NULL;
+	long value = PM_QOS_DEV_LAT_DEFAULT_VALUE;
+	unsigned long flags;
+	int ret = 0;
+
+	pr_debug("powerdomain: %s: pwrdm %s, cookie=0x%p, min_latency=%ld\n",
+		 __func__, pwrdm->name, cookie, min_latency);
+
+	if (min_latency <= PM_QOS_DEV_LAT_DEFAULT_VALUE) {
+		pr_warn("%s: min_latency >= PM_QOS_DEV_LAT_DEFAULT_VALUE\n",
+			__func__);
+		return -EINVAL;
+	}
+
+	spin_lock_irqsave(&pwrdm->wkup_lat_plist_lock, flags);
+
+	/* Check if there already is a constraint for cookie */
+	plist_for_each_entry(tmp_user, &pwrdm->wkup_lat_plist_head, node) {
+		if (tmp_user->cookie == cookie) {
+			user = tmp_user;
+			break;
+		}
+	}
+
+	/* If nothing to update, job done */
+	if (user && (user->node.prio == min_latency))
+		goto out;
+
+	if (!user) {
+		/* Allocate a new entry for insertion in the list */
+		user = kzalloc(sizeof(struct pwrdm_wkup_constraints_entry),
+			       GFP_ATOMIC);
+		if (!user) {
+			pr_err("%s: FATAL ERROR: kzalloc failed\n", __func__);
+			ret = -ENOMEM;
+			goto out;
+		}
+		user->cookie = cookie;
+	} else {
+		/* Update existing entry */
+		plist_del(&user->node, &pwrdm->wkup_lat_plist_head);
+	}
+
+	plist_node_init(&user->node, min_latency);
+	plist_add(&user->node, &pwrdm->wkup_lat_plist_head);
+
+	/* Find the aggregated constraint value from the list */
+	if (!plist_head_empty(&pwrdm->wkup_lat_plist_head))
+		value = plist_first(&pwrdm->wkup_lat_plist_head)->prio;
+
+	spin_unlock_irqrestore(&pwrdm->wkup_lat_plist_lock, flags);
+
+	/* Apply the constraint to the pwrdm */
+	pr_debug("powerdomain: %s: pwrdm %s, value=%ld\n",
+		 __func__, pwrdm->name, value);
+	return _pwrdm_wakeuplat_update_pwrst(pwrdm, value);
+
+out:
+	spin_unlock_irqrestore(&pwrdm->wkup_lat_plist_lock, flags);
+	return ret;
+}
+
+/**
+ * pwrdm_wakeuplat_remove_constraint - Release a powerdomain wakeup latency
+ *  constraint and apply it
+ * @pwrdm: struct powerdomain * which the constraint applies to
+ * @cookie: constraint identifier, used for tracking
+ *
+ * Tracks the constraints by @cookie.
+ * Constraint removal: Removes the identifier's entry from powerdomain's
+ * wakeup latency constraint list.
+ *
+ * Applies the aggregated constraint value for the given pwrdm by calling
+ * _pwrdm_wakeuplat_update_pwrst.
+ *
+ * Returns 0 upon success, -EINVAL in case the constraint to remove is not
+ * existing, or the return value from _pwrdm_wakeuplat_update_pwrst.
+ *
+ * The caller must check the validity of the parameters.
+ */
+int pwrdm_wakeuplat_remove_constraint(struct powerdomain *pwrdm, void *cookie)
+{
+	struct pwrdm_wkup_constraints_entry *tmp_user, *user = NULL;
+	long value = PM_QOS_DEV_LAT_DEFAULT_VALUE;
+	unsigned long flags;
+
+	pr_debug("powerdomain: %s: pwrdm %s, cookie=0x%p\n",
+		 __func__, pwrdm->name, cookie);
+
+	spin_lock_irqsave(&pwrdm->wkup_lat_plist_lock, flags);
+
+	/* Check if there is a constraint for cookie */
+	plist_for_each_entry(tmp_user, &pwrdm->wkup_lat_plist_head, node) {
+		if (tmp_user->cookie == cookie) {
+			user = tmp_user;
+			break;
+		}
+	}
+
+	/* If constraint not existing or list empty, return -EINVAL */
+	if (!user)
+		goto out;
+
+	/* Remove the constraint from the list */
+	plist_del(&user->node, &pwrdm->wkup_lat_plist_head);
+
+	/* Find the aggregated constraint value from the list */
+	if (!plist_head_empty(&pwrdm->wkup_lat_plist_head))
+		value = plist_first(&pwrdm->wkup_lat_plist_head)->prio;
+
+	spin_unlock_irqrestore(&pwrdm->wkup_lat_plist_lock, flags);
+
+	/* Release the constraint memory */
+	kfree(user);
+
+	/* Apply the constraint to the pwrdm */
+	pr_debug("powerdomain: %s: pwrdm %s, value=%ld\n",
+		 __func__, pwrdm->name, value);
+	return _pwrdm_wakeuplat_update_pwrst(pwrdm, value);
+
+out:
+	spin_unlock_irqrestore(&pwrdm->wkup_lat_plist_lock, flags);
+	return -EINVAL;
+}
+
+/**
  * 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 dcd2315..c114ec2 100644
--- a/arch/arm/mach-omap2/powerdomain.h
+++ b/arch/arm/mach-omap2/powerdomain.h
@@ -16,7 +16,7 @@
 
 #include <linux/types.h>
 #include <linux/list.h>
-
+#include <linux/plist.h>
 #include <linux/spinlock.h>
 #include <linux/atomic.h>
 
@@ -115,6 +115,8 @@ extern void omap44xx_powerdomains_init(void);
 
 #define PWRDM_MAX_PWRSTS	4
 
+#define UNSUP_STATE		-1
+
 /* Powerdomain allowable state bitfields */
 #define PWRSTS_ON		(1 << PWRDM_POWER_ON)
 #define PWRSTS_INACTIVE		(1 << PWRDM_POWER_INACTIVE)
@@ -222,6 +224,16 @@ struct powerdomain {
 	s64 timer;
 	s64 state_timer[PWRDM_MAX_FUNC_PWRSTS];
 #endif
+	const s32 wakeup_lat[PWRDM_MAX_FUNC_PWRSTS];
+	struct plist_head wkup_lat_plist_head;
+	spinlock_t wkup_lat_plist_lock;
+	int wkup_lat_next_state;
+};
+
+/* Linked list for the wake-up latency constraints */
+struct pwrdm_wkup_constraints_entry {
+	void			*cookie;
+	struct plist_node	node;
 };
 
 /**
@@ -284,6 +296,10 @@ int pwrdm_wait_transition(struct powerdomain *pwrdm);
 
 int pwrdm_set_lowpwrstchange(struct powerdomain *pwrdm);
 
+int pwrdm_wakeuplat_update_constraint(struct powerdomain *pwrdm, void *cookie,
+				      long min_latency);
+int pwrdm_wakeuplat_remove_constraint(struct powerdomain *pwrdm, void *cookie);
+
 extern struct pwrdm_ops omap2_pwrdm_operations;
 extern struct pwrdm_ops omap3_pwrdm_operations;
 extern struct pwrdm_ops am33xx_pwrdm_operations;
-- 
1.7.7.6


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

* [PATCH 1/8] ARM: OMAP2+: PM QoS: control the power domains next state from the constraints
@ 2012-09-18  8:52   ` Jean Pihet
  0 siblings, 0 replies; 28+ messages in thread
From: Jean Pihet @ 2012-09-18  8:52 UTC (permalink / raw)
  To: linux-arm-kernel

When a PM QoS device latency constraint is requested or removed the
constraint is stored in the constraints list of the corresponding power
domain, then the aggregated constraint value is applied by programming
the next functional power state using pwrdm_set_fpwrst.

The per-device PM QoS locking requires a spinlock to be used. The reasons
is that some drivers need to use the per-device PM QoS functionality from
interrupt context or spinlock protected context, as reported by Djamil.
An example of such a driver is the OMAP HSI (high-speed synchronous serial
interface) driver which needs to control the IP block idle state from
the FIFO empty state, from interrupt context.

Tested on OMAP3 Beagleboard and OMAP4 Pandaboard in RET/OFF using
wake-up latency constraints on MPU, CORE and PER.

Signed-off-by: Jean Pihet <j-pihet@ti.com>
Cc: Djamil Elaidi <d-elaidi@ti.com>
---
 arch/arm/mach-omap2/powerdomain.c |  212 +++++++++++++++++++++++++++++++++++++
 arch/arm/mach-omap2/powerdomain.h |   18 +++-
 2 files changed, 229 insertions(+), 1 deletions(-)

diff --git a/arch/arm/mach-omap2/powerdomain.c b/arch/arm/mach-omap2/powerdomain.c
index 37dfabf..3d81ac7 100644
--- a/arch/arm/mach-omap2/powerdomain.c
+++ b/arch/arm/mach-omap2/powerdomain.c
@@ -17,8 +17,10 @@
 #include <linux/kernel.h>
 #include <linux/types.h>
 #include <linux/list.h>
+#include <linux/slab.h>
 #include <linux/errno.h>
 #include <linux/string.h>
+#include <linux/pm_qos.h>
 #include <linux/ratelimit.h>
 #include <trace/events/power.h>
 
@@ -114,6 +116,12 @@ static int _pwrdm_register(struct powerdomain *pwrdm)
 	for (i = 0; i < pwrdm->banks; i++)
 		pwrdm->ret_mem_off_counter[i] = 0;
 
+	/* Initialize the per-device wake-up constraints framework data */
+	spin_lock_init(&pwrdm->wkup_lat_plist_lock);
+	plist_head_init(&pwrdm->wkup_lat_plist_head);
+	pwrdm->wkup_lat_next_state = PWRDM_FUNC_PWRST_OFF;
+
+	/* Initialize the pwrdm state */
 	pwrdm_wait_transition(pwrdm);
 	pwrdm->state = pwrdm_read_fpwrst(pwrdm);
 	pwrdm->state_counter[pwrdm->state] = 1;
@@ -418,6 +426,60 @@ static int _pwrdm_read_next_fpwrst(struct powerdomain *pwrdm)
 	return _pwrdm_pwrst_to_fpwrst(pwrdm, next_pwrst, next_logic);
 }
 
+/**
+ * _pwrdm_wakeuplat_update_pwrst - Update power domain power state if needed
+ * @pwrdm: struct powerdomain * to which requesting device belongs to.
+ * @min_latency: the allowed wake-up latency for the given power domain. A
+ *  value of PM_QOS_DEV_LAT_DEFAULT_VALUE means 'no constraint' on the pwrdm.
+ *
+ * Finds the power domain next power state that fulfills the constraint.
+ * Programs a new target state if it is different from current power state.
+ * The power domains get the next power state programmed directly in the
+ * registers.
+ *
+ * Must be called with the wkup_lat_plist_lock lock held.
+ *
+ * Returns 0 in case of success, -EINVAL in case of invalid parameters,
+ * or the return value from pwrdm_set_fpwrst.
+ */
+static int _pwrdm_wakeuplat_update_pwrst(struct powerdomain *pwrdm,
+					 long min_latency)
+{
+	int ret = 0, state, new_state = PWRDM_FUNC_PWRST_ON;
+
+	if (!pwrdm) {
+		WARN(1, "powerdomain: %s: invalid parameter(s)", __func__);
+		return -EINVAL;
+	}
+
+	/*
+	 * Find the next supported power state with
+	 *  wakeup latency <= min_latency.
+	 * Pick the lower state if no constraint on the pwrdm
+	 *  (min_latency == PM_QOS_DEV_LAT_DEFAULT_VALUE).
+	 * Skip the states marked as unsupported (UNSUP_STATE).
+	 * If no power state found, fall back to PWRDM_FUNC_PWRST_ON.
+	 */
+	for (state = 0x0; state < PWRDM_MAX_FUNC_PWRSTS; state++) {
+		if ((min_latency == PM_QOS_DEV_LAT_DEFAULT_VALUE) ||
+		    ((pwrdm->wakeup_lat[state] != UNSUP_STATE) &&
+		     (pwrdm->wakeup_lat[state] <= min_latency))) {
+			new_state = state;
+			break;
+		}
+	}
+
+	pwrdm->wkup_lat_next_state = new_state;
+	ret = pwrdm_set_fpwrst(pwrdm, new_state);
+
+	pr_debug("%s: func pwrst for %s: curr=%d, next=%d, min_latency=%ld, new_state=%d\n",
+		 __func__, pwrdm->name, pwrdm_read_fpwrst(pwrdm),
+		 pwrdm_read_next_fpwrst(pwrdm), min_latency, new_state);
+
+	return ret;
+}
+
+
 /* Public functions */
 
 /**
@@ -1484,6 +1546,156 @@ int pwrdm_post_transition(struct powerdomain *pwrdm)
 }
 
 /**
+ * pwrdm_wakeuplat_update_constraint - Set or update a powerdomain wakeup
+ *  latency constraint and apply it
+ * @pwrdm: struct powerdomain * which the constraint applies to
+ * @cookie: constraint identifier, used for tracking
+ * @min_latency: minimum wakeup latency constraint (in microseconds) for
+ *  the given pwrdm
+ *
+ * Tracks the constraints by @cookie.
+ * Constraint set/update: Adds a new entry to powerdomain's wake-up latency
+ * constraint list. If the constraint identifier already exists in the list,
+ * the old value is overwritten.
+ *
+ * Applies the aggregated constraint value for the given pwrdm by calling
+ * _pwrdm_wakeuplat_update_pwrst.
+ *
+ * Returns 0 upon success, -ENOMEM in case of memory shortage, -EINVAL in
+ * case of invalid latency value, or the return value from
+ * _pwrdm_wakeuplat_update_pwrst.
+ *
+ * The caller must check the validity of the parameters.
+ */
+int pwrdm_wakeuplat_update_constraint(struct powerdomain *pwrdm, void *cookie,
+				      long min_latency)
+{
+	struct pwrdm_wkup_constraints_entry *tmp_user, *user = NULL;
+	long value = PM_QOS_DEV_LAT_DEFAULT_VALUE;
+	unsigned long flags;
+	int ret = 0;
+
+	pr_debug("powerdomain: %s: pwrdm %s, cookie=0x%p, min_latency=%ld\n",
+		 __func__, pwrdm->name, cookie, min_latency);
+
+	if (min_latency <= PM_QOS_DEV_LAT_DEFAULT_VALUE) {
+		pr_warn("%s: min_latency >= PM_QOS_DEV_LAT_DEFAULT_VALUE\n",
+			__func__);
+		return -EINVAL;
+	}
+
+	spin_lock_irqsave(&pwrdm->wkup_lat_plist_lock, flags);
+
+	/* Check if there already is a constraint for cookie */
+	plist_for_each_entry(tmp_user, &pwrdm->wkup_lat_plist_head, node) {
+		if (tmp_user->cookie == cookie) {
+			user = tmp_user;
+			break;
+		}
+	}
+
+	/* If nothing to update, job done */
+	if (user && (user->node.prio == min_latency))
+		goto out;
+
+	if (!user) {
+		/* Allocate a new entry for insertion in the list */
+		user = kzalloc(sizeof(struct pwrdm_wkup_constraints_entry),
+			       GFP_ATOMIC);
+		if (!user) {
+			pr_err("%s: FATAL ERROR: kzalloc failed\n", __func__);
+			ret = -ENOMEM;
+			goto out;
+		}
+		user->cookie = cookie;
+	} else {
+		/* Update existing entry */
+		plist_del(&user->node, &pwrdm->wkup_lat_plist_head);
+	}
+
+	plist_node_init(&user->node, min_latency);
+	plist_add(&user->node, &pwrdm->wkup_lat_plist_head);
+
+	/* Find the aggregated constraint value from the list */
+	if (!plist_head_empty(&pwrdm->wkup_lat_plist_head))
+		value = plist_first(&pwrdm->wkup_lat_plist_head)->prio;
+
+	spin_unlock_irqrestore(&pwrdm->wkup_lat_plist_lock, flags);
+
+	/* Apply the constraint to the pwrdm */
+	pr_debug("powerdomain: %s: pwrdm %s, value=%ld\n",
+		 __func__, pwrdm->name, value);
+	return _pwrdm_wakeuplat_update_pwrst(pwrdm, value);
+
+out:
+	spin_unlock_irqrestore(&pwrdm->wkup_lat_plist_lock, flags);
+	return ret;
+}
+
+/**
+ * pwrdm_wakeuplat_remove_constraint - Release a powerdomain wakeup latency
+ *  constraint and apply it
+ * @pwrdm: struct powerdomain * which the constraint applies to
+ * @cookie: constraint identifier, used for tracking
+ *
+ * Tracks the constraints by @cookie.
+ * Constraint removal: Removes the identifier's entry from powerdomain's
+ * wakeup latency constraint list.
+ *
+ * Applies the aggregated constraint value for the given pwrdm by calling
+ * _pwrdm_wakeuplat_update_pwrst.
+ *
+ * Returns 0 upon success, -EINVAL in case the constraint to remove is not
+ * existing, or the return value from _pwrdm_wakeuplat_update_pwrst.
+ *
+ * The caller must check the validity of the parameters.
+ */
+int pwrdm_wakeuplat_remove_constraint(struct powerdomain *pwrdm, void *cookie)
+{
+	struct pwrdm_wkup_constraints_entry *tmp_user, *user = NULL;
+	long value = PM_QOS_DEV_LAT_DEFAULT_VALUE;
+	unsigned long flags;
+
+	pr_debug("powerdomain: %s: pwrdm %s, cookie=0x%p\n",
+		 __func__, pwrdm->name, cookie);
+
+	spin_lock_irqsave(&pwrdm->wkup_lat_plist_lock, flags);
+
+	/* Check if there is a constraint for cookie */
+	plist_for_each_entry(tmp_user, &pwrdm->wkup_lat_plist_head, node) {
+		if (tmp_user->cookie == cookie) {
+			user = tmp_user;
+			break;
+		}
+	}
+
+	/* If constraint not existing or list empty, return -EINVAL */
+	if (!user)
+		goto out;
+
+	/* Remove the constraint from the list */
+	plist_del(&user->node, &pwrdm->wkup_lat_plist_head);
+
+	/* Find the aggregated constraint value from the list */
+	if (!plist_head_empty(&pwrdm->wkup_lat_plist_head))
+		value = plist_first(&pwrdm->wkup_lat_plist_head)->prio;
+
+	spin_unlock_irqrestore(&pwrdm->wkup_lat_plist_lock, flags);
+
+	/* Release the constraint memory */
+	kfree(user);
+
+	/* Apply the constraint to the pwrdm */
+	pr_debug("powerdomain: %s: pwrdm %s, value=%ld\n",
+		 __func__, pwrdm->name, value);
+	return _pwrdm_wakeuplat_update_pwrst(pwrdm, value);
+
+out:
+	spin_unlock_irqrestore(&pwrdm->wkup_lat_plist_lock, flags);
+	return -EINVAL;
+}
+
+/**
  * 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 dcd2315..c114ec2 100644
--- a/arch/arm/mach-omap2/powerdomain.h
+++ b/arch/arm/mach-omap2/powerdomain.h
@@ -16,7 +16,7 @@
 
 #include <linux/types.h>
 #include <linux/list.h>
-
+#include <linux/plist.h>
 #include <linux/spinlock.h>
 #include <linux/atomic.h>
 
@@ -115,6 +115,8 @@ extern void omap44xx_powerdomains_init(void);
 
 #define PWRDM_MAX_PWRSTS	4
 
+#define UNSUP_STATE		-1
+
 /* Powerdomain allowable state bitfields */
 #define PWRSTS_ON		(1 << PWRDM_POWER_ON)
 #define PWRSTS_INACTIVE		(1 << PWRDM_POWER_INACTIVE)
@@ -222,6 +224,16 @@ struct powerdomain {
 	s64 timer;
 	s64 state_timer[PWRDM_MAX_FUNC_PWRSTS];
 #endif
+	const s32 wakeup_lat[PWRDM_MAX_FUNC_PWRSTS];
+	struct plist_head wkup_lat_plist_head;
+	spinlock_t wkup_lat_plist_lock;
+	int wkup_lat_next_state;
+};
+
+/* Linked list for the wake-up latency constraints */
+struct pwrdm_wkup_constraints_entry {
+	void			*cookie;
+	struct plist_node	node;
 };
 
 /**
@@ -284,6 +296,10 @@ int pwrdm_wait_transition(struct powerdomain *pwrdm);
 
 int pwrdm_set_lowpwrstchange(struct powerdomain *pwrdm);
 
+int pwrdm_wakeuplat_update_constraint(struct powerdomain *pwrdm, void *cookie,
+				      long min_latency);
+int pwrdm_wakeuplat_remove_constraint(struct powerdomain *pwrdm, void *cookie);
+
 extern struct pwrdm_ops omap2_pwrdm_operations;
 extern struct pwrdm_ops omap3_pwrdm_operations;
 extern struct pwrdm_ops am33xx_pwrdm_operations;
-- 
1.7.7.6

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

* [PATCH 2/8] ARM: OMAP2+: hwmod: manage the wake-up latency constraints
  2012-09-18  8:52 ` Jean Pihet
@ 2012-09-18  8:52   ` Jean Pihet
  -1 siblings, 0 replies; 28+ messages in thread
From: Jean Pihet @ 2012-09-18  8:52 UTC (permalink / raw)
  To: linux-omap, paul, linux-arm-kernel, khilman; +Cc: Jean Pihet

The OMAP PM code implements a handler for the per-device PM QoS framework.
The handler queries the omap_hwmod layer in order to manage the power domains
wake-up latency constraints. Hwmod retrieves the correct power domain
and if it exists it calls the corresponding power domain function.

Tested on OMAP3 Beagleboard and OMAP4 Pandaboard in RET/OFF using wake-up
latency constraints on MPU, CORE and PER.

Signed-off-by: Jean Pihet <j-pihet@ti.com>
---
 arch/arm/mach-omap2/omap_hwmod.c             |   44 +++++++++++++++++++++++++-
 arch/arm/plat-omap/include/plat/omap_hwmod.h |    5 +++
 2 files changed, 48 insertions(+), 1 deletions(-)

diff --git a/arch/arm/mach-omap2/omap_hwmod.c b/arch/arm/mach-omap2/omap_hwmod.c
index 6ca8e51..5990835 100644
--- a/arch/arm/mach-omap2/omap_hwmod.c
+++ b/arch/arm/mach-omap2/omap_hwmod.c
@@ -145,6 +145,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"
@@ -3554,10 +3555,51 @@ ohsps_unlock:
 }
 
 /**
+ * omap_hwmod_set_wakeuplat_constraint - Set or update a wake-up latency
+ * constraint
+ * @oh: struct omap_hwmod* to which the target device belongs to.
+ * @cookie: identifier of the constraints list for @oh.
+ * @min_latency: the minimum allowed wake-up latency for @oh.
+ *
+ * Returns the return value from pwrdm_wakeuplat_update_constraint(),
+ * or -EINVAL in case of invalid parameters.
+ */
+int omap_hwmod_set_wakeuplat_constraint(struct omap_hwmod *oh, void *cookie,
+					long min_latency)
+{
+	struct powerdomain *pwrdm = omap_hwmod_get_pwrdm(oh);
+
+	if (!pwrdm)
+		return -EINVAL;
+
+	return pwrdm_wakeuplat_update_constraint(pwrdm, cookie, min_latency);
+}
+
+/**
+ * omap_hwmod_remove_wakeuplat_constraint - Release a wake-up latency
+ * constraint
+ * @oh: struct omap_hwmod* to which the target device belongs to.
+ * @cookie: identifier of the constraints list for @oh.
+ *
+ * Removes a wakeup latency contraint.  Returns the return value from
+ * pwrdm_wakeuplat_remove_constraint(), or -EINVAL in case of invalid
+ * parameters.
+ */
+int omap_hwmod_remove_wakeuplat_constraint(struct omap_hwmod *oh, void *cookie)
+{
+	struct powerdomain *pwrdm = omap_hwmod_get_pwrdm(oh);
+
+	if (!pwrdm)
+		return -EINVAL;
+
+	return pwrdm_wakeuplat_remove_constraint(pwrdm, cookie);
+}
+
+/**
  * 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/plat-omap/include/plat/omap_hwmod.h b/arch/arm/plat-omap/include/plat/omap_hwmod.h
index 6132972..fa0b350 100644
--- a/arch/arm/plat-omap/include/plat/omap_hwmod.h
+++ b/arch/arm/plat-omap/include/plat/omap_hwmod.h
@@ -640,6 +640,11 @@ 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_wakeuplat_constraint(struct omap_hwmod *oh, void *cookie,
+					long min_latency);
+int omap_hwmod_remove_wakeuplat_constraint(struct omap_hwmod *oh, void *cookie);
+
 int omap_hwmod_get_context_loss_count(struct omap_hwmod *oh);
 
 int omap_hwmod_no_setup_reset(struct omap_hwmod *oh);
-- 
1.7.7.6


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

* [PATCH 2/8] ARM: OMAP2+: hwmod: manage the wake-up latency constraints
@ 2012-09-18  8:52   ` Jean Pihet
  0 siblings, 0 replies; 28+ messages in thread
From: Jean Pihet @ 2012-09-18  8:52 UTC (permalink / raw)
  To: linux-arm-kernel

The OMAP PM code implements a handler for the per-device PM QoS framework.
The handler queries the omap_hwmod layer in order to manage the power domains
wake-up latency constraints. Hwmod retrieves the correct power domain
and if it exists it calls the corresponding power domain function.

Tested on OMAP3 Beagleboard and OMAP4 Pandaboard in RET/OFF using wake-up
latency constraints on MPU, CORE and PER.

Signed-off-by: Jean Pihet <j-pihet@ti.com>
---
 arch/arm/mach-omap2/omap_hwmod.c             |   44 +++++++++++++++++++++++++-
 arch/arm/plat-omap/include/plat/omap_hwmod.h |    5 +++
 2 files changed, 48 insertions(+), 1 deletions(-)

diff --git a/arch/arm/mach-omap2/omap_hwmod.c b/arch/arm/mach-omap2/omap_hwmod.c
index 6ca8e51..5990835 100644
--- a/arch/arm/mach-omap2/omap_hwmod.c
+++ b/arch/arm/mach-omap2/omap_hwmod.c
@@ -145,6 +145,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"
@@ -3554,10 +3555,51 @@ ohsps_unlock:
 }
 
 /**
+ * omap_hwmod_set_wakeuplat_constraint - Set or update a wake-up latency
+ * constraint
+ * @oh: struct omap_hwmod* to which the target device belongs to.
+ * @cookie: identifier of the constraints list for @oh.
+ * @min_latency: the minimum allowed wake-up latency for @oh.
+ *
+ * Returns the return value from pwrdm_wakeuplat_update_constraint(),
+ * or -EINVAL in case of invalid parameters.
+ */
+int omap_hwmod_set_wakeuplat_constraint(struct omap_hwmod *oh, void *cookie,
+					long min_latency)
+{
+	struct powerdomain *pwrdm = omap_hwmod_get_pwrdm(oh);
+
+	if (!pwrdm)
+		return -EINVAL;
+
+	return pwrdm_wakeuplat_update_constraint(pwrdm, cookie, min_latency);
+}
+
+/**
+ * omap_hwmod_remove_wakeuplat_constraint - Release a wake-up latency
+ * constraint
+ * @oh: struct omap_hwmod* to which the target device belongs to.
+ * @cookie: identifier of the constraints list for @oh.
+ *
+ * Removes a wakeup latency contraint.  Returns the return value from
+ * pwrdm_wakeuplat_remove_constraint(), or -EINVAL in case of invalid
+ * parameters.
+ */
+int omap_hwmod_remove_wakeuplat_constraint(struct omap_hwmod *oh, void *cookie)
+{
+	struct powerdomain *pwrdm = omap_hwmod_get_pwrdm(oh);
+
+	if (!pwrdm)
+		return -EINVAL;
+
+	return pwrdm_wakeuplat_remove_constraint(pwrdm, cookie);
+}
+
+/**
  * 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/plat-omap/include/plat/omap_hwmod.h b/arch/arm/plat-omap/include/plat/omap_hwmod.h
index 6132972..fa0b350 100644
--- a/arch/arm/plat-omap/include/plat/omap_hwmod.h
+++ b/arch/arm/plat-omap/include/plat/omap_hwmod.h
@@ -640,6 +640,11 @@ 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_wakeuplat_constraint(struct omap_hwmod *oh, void *cookie,
+					long min_latency);
+int omap_hwmod_remove_wakeuplat_constraint(struct omap_hwmod *oh, void *cookie);
+
 int omap_hwmod_get_context_loss_count(struct omap_hwmod *oh);
 
 int omap_hwmod_no_setup_reset(struct omap_hwmod *oh);
-- 
1.7.7.6

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

* [PATCH 3/8] ARM: OMAP: omap_device: register to the per-device PM QoS framework
  2012-09-18  8:52 ` Jean Pihet
@ 2012-09-18  8:52   ` Jean Pihet
  -1 siblings, 0 replies; 28+ messages in thread
From: Jean Pihet @ 2012-09-18  8:52 UTC (permalink / raw)
  To: linux-omap, paul, linux-arm-kernel, khilman; +Cc: Jean Pihet

Implement the devices wake-up latency constraints using the global
device PM QoS notification handler which applies the constraints to the
underlying layer by calling the corresponding function at hwmod level.

Reworked after Paul's suggestions.

Tested on OMAP3 Beagleboard and OMAP4 Pandaboard in RET/OFF using wake-up
latency constraints on MPU, CORE and PER.

Signed-off-by: Jean Pihet <j-pihet@ti.com>
Cc: Paul Walmsley <paul@pwsan.com>
---
 arch/arm/plat-omap/omap_device.c |   81 +++++++++++++++++++++++++++++++++++++-
 1 files changed, 80 insertions(+), 1 deletions(-)

diff --git a/arch/arm/plat-omap/omap_device.c b/arch/arm/plat-omap/omap_device.c
index c490240..011825f 100644
--- a/arch/arm/plat-omap/omap_device.c
+++ b/arch/arm/plat-omap/omap_device.c
@@ -3,6 +3,7 @@
  * omap_device implementation
  *
  * Copyright (C) 2009-2010 Nokia Corporation
+ * Copyright (C) 2012 Texas Instruments, Inc.
  * Paul Walmsley, Kevin Hilman
  *
  * Developed in collaboration with (alphabetical order): Benoit
@@ -89,6 +90,7 @@
 #include <linux/pm_runtime.h>
 #include <linux/of.h>
 #include <linux/notifier.h>
+#include <linux/pm_qos.h>
 
 #include <plat/omap_device.h>
 #include <plat/omap_hwmod.h>
@@ -401,6 +403,72 @@ static int _omap_device_notifier_call(struct notifier_block *nb,
 	return NOTIFY_DONE;
 }
 
+/**
+ * _omap_device_pm_qos_handler - interface to the per-device PM QoS framework
+ * @nb: pointer to omap_device_pm_qos_nb (not used)
+ * @new_value: new maximum wakeup latency constraint for @req->dev (in us)
+ * @req: struct dev_pm_qos_request * passed by the Linux PM QoS code
+ *
+ * Called by the Linux core device PM QoS code to alter the maximum
+ * wakeup latency constraint on a device.  If the underlying device is
+ * an omap_device, then this code will pass the constraint on to the
+ * underlying hwmods.  Returns -EINVAL if this code can't handle the
+ * constraint for some reason, or passes along the return code from the
+ * hwmod wakeup latency constraint functions.
+ */
+static int _omap_device_pm_qos_handler(struct notifier_block *nb,
+				       unsigned long new_value,
+				       void *req)
+{
+	struct omap_device *od;
+	struct omap_hwmod *oh;
+	struct platform_device *pdev;
+	struct dev_pm_qos_request *dev_pm_qos_req = req;
+	int ret = NOTIFY_OK;
+	int r, i;
+
+	pr_debug("OMAP PM constraints: req@0x%p, new_value=%lu\n",
+		 req, new_value);
+
+	/* Look for the platform device for the constraint target device */
+	pdev = to_platform_device(dev_pm_qos_req->dev);
+
+	/* Try to catch non platform devices */
+	if (pdev->name == NULL) {
+		pr_err("%s: Error: platform device for device %s not valid\n",
+		       __func__, dev_name(dev_pm_qos_req->dev));
+		return NOTIFY_DONE;
+	}
+
+	/* Find the associated omap_device for dev */
+	od = to_omap_device(pdev);
+	if (od == NULL) {
+		pr_err("%s: Error: no omap_device for device %s\n",
+		       __func__, dev_name(dev_pm_qos_req->dev));
+		return NOTIFY_DONE;
+	}
+
+	pr_debug("OMAP PM constraints: req@0x%p, dev=0x%p, new_value=%lu\n",
+		 req, dev_pm_qos_req->dev, new_value);
+
+	for (i = 0; i < od->hwmods_cnt; i++) {
+		oh = od->hwmods[i];
+		if (new_value == PM_QOS_DEV_LAT_DEFAULT_VALUE)
+			r = omap_hwmod_remove_wakeuplat_constraint(
+							oh,
+							dev_pm_qos_req);
+		else
+			r = omap_hwmod_set_wakeuplat_constraint(
+							oh,
+							dev_pm_qos_req,
+							new_value);
+
+		if (!r)
+			ret = NOTIFY_BAD;
+	}
+
+	return ret;
+}
 
 /* Public functions for use by core code */
 
@@ -1115,13 +1183,24 @@ int omap_device_enable_clocks(struct omap_device *od)
 	return 0;
 }
 
+static struct notifier_block omap_device_pm_qos_nb = {
+	.notifier_call = _omap_device_pm_qos_handler,
+};
+
 static struct notifier_block platform_nb = {
 	.notifier_call = _omap_device_notifier_call,
 };
 
 static int __init omap_device_init(void)
 {
+	int ret;
+
 	bus_register_notifier(&platform_bus_type, &platform_nb);
-	return 0;
+
+	ret = dev_pm_qos_add_global_notifier(&omap_device_pm_qos_nb);
+	if (ret)
+		pr_err("omap_device: cannot add global notifier for dev PM QoS\n");
+
+	return ret;
 }
 core_initcall(omap_device_init);
-- 
1.7.7.6


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

* [PATCH 3/8] ARM: OMAP: omap_device: register to the per-device PM QoS framework
@ 2012-09-18  8:52   ` Jean Pihet
  0 siblings, 0 replies; 28+ messages in thread
From: Jean Pihet @ 2012-09-18  8:52 UTC (permalink / raw)
  To: linux-arm-kernel

Implement the devices wake-up latency constraints using the global
device PM QoS notification handler which applies the constraints to the
underlying layer by calling the corresponding function at hwmod level.

Reworked after Paul's suggestions.

Tested on OMAP3 Beagleboard and OMAP4 Pandaboard in RET/OFF using wake-up
latency constraints on MPU, CORE and PER.

Signed-off-by: Jean Pihet <j-pihet@ti.com>
Cc: Paul Walmsley <paul@pwsan.com>
---
 arch/arm/plat-omap/omap_device.c |   81 +++++++++++++++++++++++++++++++++++++-
 1 files changed, 80 insertions(+), 1 deletions(-)

diff --git a/arch/arm/plat-omap/omap_device.c b/arch/arm/plat-omap/omap_device.c
index c490240..011825f 100644
--- a/arch/arm/plat-omap/omap_device.c
+++ b/arch/arm/plat-omap/omap_device.c
@@ -3,6 +3,7 @@
  * omap_device implementation
  *
  * Copyright (C) 2009-2010 Nokia Corporation
+ * Copyright (C) 2012 Texas Instruments, Inc.
  * Paul Walmsley, Kevin Hilman
  *
  * Developed in collaboration with (alphabetical order): Benoit
@@ -89,6 +90,7 @@
 #include <linux/pm_runtime.h>
 #include <linux/of.h>
 #include <linux/notifier.h>
+#include <linux/pm_qos.h>
 
 #include <plat/omap_device.h>
 #include <plat/omap_hwmod.h>
@@ -401,6 +403,72 @@ static int _omap_device_notifier_call(struct notifier_block *nb,
 	return NOTIFY_DONE;
 }
 
+/**
+ * _omap_device_pm_qos_handler - interface to the per-device PM QoS framework
+ * @nb: pointer to omap_device_pm_qos_nb (not used)
+ * @new_value: new maximum wakeup latency constraint for @req->dev (in us)
+ * @req: struct dev_pm_qos_request * passed by the Linux PM QoS code
+ *
+ * Called by the Linux core device PM QoS code to alter the maximum
+ * wakeup latency constraint on a device.  If the underlying device is
+ * an omap_device, then this code will pass the constraint on to the
+ * underlying hwmods.  Returns -EINVAL if this code can't handle the
+ * constraint for some reason, or passes along the return code from the
+ * hwmod wakeup latency constraint functions.
+ */
+static int _omap_device_pm_qos_handler(struct notifier_block *nb,
+				       unsigned long new_value,
+				       void *req)
+{
+	struct omap_device *od;
+	struct omap_hwmod *oh;
+	struct platform_device *pdev;
+	struct dev_pm_qos_request *dev_pm_qos_req = req;
+	int ret = NOTIFY_OK;
+	int r, i;
+
+	pr_debug("OMAP PM constraints: req at 0x%p, new_value=%lu\n",
+		 req, new_value);
+
+	/* Look for the platform device for the constraint target device */
+	pdev = to_platform_device(dev_pm_qos_req->dev);
+
+	/* Try to catch non platform devices */
+	if (pdev->name == NULL) {
+		pr_err("%s: Error: platform device for device %s not valid\n",
+		       __func__, dev_name(dev_pm_qos_req->dev));
+		return NOTIFY_DONE;
+	}
+
+	/* Find the associated omap_device for dev */
+	od = to_omap_device(pdev);
+	if (od == NULL) {
+		pr_err("%s: Error: no omap_device for device %s\n",
+		       __func__, dev_name(dev_pm_qos_req->dev));
+		return NOTIFY_DONE;
+	}
+
+	pr_debug("OMAP PM constraints: req at 0x%p, dev=0x%p, new_value=%lu\n",
+		 req, dev_pm_qos_req->dev, new_value);
+
+	for (i = 0; i < od->hwmods_cnt; i++) {
+		oh = od->hwmods[i];
+		if (new_value == PM_QOS_DEV_LAT_DEFAULT_VALUE)
+			r = omap_hwmod_remove_wakeuplat_constraint(
+							oh,
+							dev_pm_qos_req);
+		else
+			r = omap_hwmod_set_wakeuplat_constraint(
+							oh,
+							dev_pm_qos_req,
+							new_value);
+
+		if (!r)
+			ret = NOTIFY_BAD;
+	}
+
+	return ret;
+}
 
 /* Public functions for use by core code */
 
@@ -1115,13 +1183,24 @@ int omap_device_enable_clocks(struct omap_device *od)
 	return 0;
 }
 
+static struct notifier_block omap_device_pm_qos_nb = {
+	.notifier_call = _omap_device_pm_qos_handler,
+};
+
 static struct notifier_block platform_nb = {
 	.notifier_call = _omap_device_notifier_call,
 };
 
 static int __init omap_device_init(void)
 {
+	int ret;
+
 	bus_register_notifier(&platform_bus_type, &platform_nb);
-	return 0;
+
+	ret = dev_pm_qos_add_global_notifier(&omap_device_pm_qos_nb);
+	if (ret)
+		pr_err("omap_device: cannot add global notifier for dev PM QoS\n");
+
+	return ret;
 }
 core_initcall(omap_device_init);
-- 
1.7.7.6

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

* [PATCH 4/8] ARM: OMAP3: cpuidle: next C-state decision depends on the PM QoS MPU and CORE constraints
  2012-09-18  8:52 ` Jean Pihet
@ 2012-09-18  8:52   ` Jean Pihet
  -1 siblings, 0 replies; 28+ messages in thread
From: Jean Pihet @ 2012-09-18  8:52 UTC (permalink / raw)
  To: linux-omap, paul, linux-arm-kernel, khilman; +Cc: Jean Pihet

The MPU latency figures for cpuidle include the MPU itself and also
the peripherals needed for the MPU to execute instructions (e.g.
main memory, caches, IRQ controller, MMU etc). On OMAP3 those
peripherals belong to the MPU and CORE power domains and so the
cpuidle C-states are a combination of MPU and CORE states.

This patch implements the relation between the cpuidle and per-
device PM QoS frameworks in the OMAP3 specific idle callbacks.

The chosen C-state shall satisfy the following conditions:
 . it satisfies the enable_off_mode flag,
 . the next state for MPU and CORE power domains is not lower than the
   next state calculated by the per-device PM QoS.

Tested on OMAP3 Beagleboard in RET/OFF using wake-up latency constraints
on MPU, CORE and PER.

Signed-off-by: Jean Pihet <j-pihet@ti.com>
---
 arch/arm/mach-omap2/cpuidle34xx.c |   49 +++++++++++++++++++++++++------------
 1 files changed, 33 insertions(+), 16 deletions(-)

diff --git a/arch/arm/mach-omap2/cpuidle34xx.c b/arch/arm/mach-omap2/cpuidle34xx.c
index 4ca37d2..ca6cb71 100644
--- a/arch/arm/mach-omap2/cpuidle34xx.c
+++ b/arch/arm/mach-omap2/cpuidle34xx.c
@@ -76,8 +76,8 @@ static struct omap3_idle_statedata omap3_idle_data[] = {
 static struct powerdomain *mpu_pd, *core_pd, *per_pd, *cam_pd;
 
 static int __omap3_enter_idle(struct cpuidle_device *dev,
-				struct cpuidle_driver *drv,
-				int index)
+			      struct cpuidle_driver *drv,
+			      int index)
 {
 	struct omap3_idle_statedata *cx = &omap3_idle_data[index];
 	u32 mpu_state = cx->mpu_state, core_state = cx->core_state;
@@ -134,10 +134,14 @@ return_sleep_time:
  *
  * Called from the CPUidle framework to program the device to the
  * specified target state selected by the governor.
+ *
+ * Note: this function does not check for any pending activity or dependency
+ * between power domains states, so the caller shall check the parameters
+ * correctness.
  */
 static inline int omap3_enter_idle(struct cpuidle_device *dev,
-				struct cpuidle_driver *drv,
-				int index)
+				   struct cpuidle_driver *drv,
+				   int index)
 {
 	return cpuidle_wrap_enter(dev, drv, index, __omap3_enter_idle);
 }
@@ -152,8 +156,10 @@ static inline int omap3_enter_idle(struct cpuidle_device *dev,
  * to the caller. Else, this function searches for a lower c-state which is
  * still valid (as defined in omap3_power_states[]) and returns its index.
  *
- * A state is valid if the 'valid' field is enabled and
- * if it satisfies the enable_off_mode condition.
+ * A state is valid if:
+ * . it satisfies the enable_off_mode flag,
+ * . the next state for MPU and CORE power domains is not lower than the
+ *   state programmed by the per-device PM QoS.
  */
 static int next_valid_state(struct cpuidle_device *dev,
 			    struct cpuidle_driver *drv, int index)
@@ -161,6 +167,8 @@ static int next_valid_state(struct cpuidle_device *dev,
 	struct omap3_idle_statedata *cx = &omap3_idle_data[index];
 	u32 mpu_deepest_state = PWRDM_FUNC_PWRST_CSWR;
 	u32 core_deepest_state = PWRDM_FUNC_PWRST_CSWR;
+	u32 mpu_pm_qos_next_state = mpu_pd->wkup_lat_next_state;
+	u32 core_pm_qos_next_state = core_pd->wkup_lat_next_state;
 	int idx;
 	int next_index = 0; /* C1 is the default value */
 
@@ -177,7 +185,9 @@ static int next_valid_state(struct cpuidle_device *dev,
 
 	/* Check if current state is valid */
 	if ((cx->mpu_state >= mpu_deepest_state) &&
-	    (cx->core_state >= core_deepest_state))
+	    (cx->core_state >= core_deepest_state) &&
+	    (cx->mpu_state >= mpu_pm_qos_next_state) &&
+	    (cx->core_state >= core_pm_qos_next_state))
 		return index;
 
 	/*
@@ -187,7 +197,9 @@ static int next_valid_state(struct cpuidle_device *dev,
 	for (idx = index - 1; idx >= 0; idx--) {
 		cx =  &omap3_idle_data[idx];
 		if ((cx->mpu_state >= mpu_deepest_state) &&
-		    (cx->core_state >= core_deepest_state)) {
+		    (cx->core_state >= core_deepest_state) &&
+		    (cx->mpu_state >= mpu_pm_qos_next_state) &&
+		    (cx->core_state >= core_pm_qos_next_state)) {
 			next_index = idx;
 			break;
 		}
@@ -223,14 +235,6 @@ static int omap3_enter_idle_bm(struct cpuidle_device *dev,
 	else
 		new_state_idx = next_valid_state(dev, drv, index);
 
-	/*
-	 * FIXME: we currently manage device-specific idle states
-	 *        for PER and CORE in combination with CPU-specific
-	 *        idle states.  This is wrong, and device-specific
-	 *        idle management needs to be separated out into
-	 *        its own code.
-	 */
-
 	/* Program PER state */
 	cx = &omap3_idle_data[new_state_idx];
 	core_next_state = cx->core_state;
@@ -264,6 +268,19 @@ static int omap3_enter_idle_bm(struct cpuidle_device *dev,
 
 DEFINE_PER_CPU(struct cpuidle_device, omap3_idle_dev);
 
+/*
+ * Note about the latency related fields of the cpuidle_driver struct:
+ *
+ * - exit_latency = sleep + wake-up latencies of the MPU,
+ *  which include the MPU itself and the peripherals needed
+ *  for the MPU to execute instructions (e.g. main memory,
+ *  caches, IRQ controller, MMU etc). Some of those peripherals
+ *  can belong to other power domains than the MPU subsystem and so
+ *  the corresponding latencies must be included in this figure
+ *
+ * - target_residency: required amount of time in the C state
+ *  to break even on energy cost
+ */
 struct cpuidle_driver omap3_idle_driver = {
 	.name = 	"omap3_idle",
 	.owner = 	THIS_MODULE,
-- 
1.7.7.6


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

* [PATCH 4/8] ARM: OMAP3: cpuidle: next C-state decision depends on the PM QoS MPU and CORE constraints
@ 2012-09-18  8:52   ` Jean Pihet
  0 siblings, 0 replies; 28+ messages in thread
From: Jean Pihet @ 2012-09-18  8:52 UTC (permalink / raw)
  To: linux-arm-kernel

The MPU latency figures for cpuidle include the MPU itself and also
the peripherals needed for the MPU to execute instructions (e.g.
main memory, caches, IRQ controller, MMU etc). On OMAP3 those
peripherals belong to the MPU and CORE power domains and so the
cpuidle C-states are a combination of MPU and CORE states.

This patch implements the relation between the cpuidle and per-
device PM QoS frameworks in the OMAP3 specific idle callbacks.

The chosen C-state shall satisfy the following conditions:
 . it satisfies the enable_off_mode flag,
 . the next state for MPU and CORE power domains is not lower than the
   next state calculated by the per-device PM QoS.

Tested on OMAP3 Beagleboard in RET/OFF using wake-up latency constraints
on MPU, CORE and PER.

Signed-off-by: Jean Pihet <j-pihet@ti.com>
---
 arch/arm/mach-omap2/cpuidle34xx.c |   49 +++++++++++++++++++++++++------------
 1 files changed, 33 insertions(+), 16 deletions(-)

diff --git a/arch/arm/mach-omap2/cpuidle34xx.c b/arch/arm/mach-omap2/cpuidle34xx.c
index 4ca37d2..ca6cb71 100644
--- a/arch/arm/mach-omap2/cpuidle34xx.c
+++ b/arch/arm/mach-omap2/cpuidle34xx.c
@@ -76,8 +76,8 @@ static struct omap3_idle_statedata omap3_idle_data[] = {
 static struct powerdomain *mpu_pd, *core_pd, *per_pd, *cam_pd;
 
 static int __omap3_enter_idle(struct cpuidle_device *dev,
-				struct cpuidle_driver *drv,
-				int index)
+			      struct cpuidle_driver *drv,
+			      int index)
 {
 	struct omap3_idle_statedata *cx = &omap3_idle_data[index];
 	u32 mpu_state = cx->mpu_state, core_state = cx->core_state;
@@ -134,10 +134,14 @@ return_sleep_time:
  *
  * Called from the CPUidle framework to program the device to the
  * specified target state selected by the governor.
+ *
+ * Note: this function does not check for any pending activity or dependency
+ * between power domains states, so the caller shall check the parameters
+ * correctness.
  */
 static inline int omap3_enter_idle(struct cpuidle_device *dev,
-				struct cpuidle_driver *drv,
-				int index)
+				   struct cpuidle_driver *drv,
+				   int index)
 {
 	return cpuidle_wrap_enter(dev, drv, index, __omap3_enter_idle);
 }
@@ -152,8 +156,10 @@ static inline int omap3_enter_idle(struct cpuidle_device *dev,
  * to the caller. Else, this function searches for a lower c-state which is
  * still valid (as defined in omap3_power_states[]) and returns its index.
  *
- * A state is valid if the 'valid' field is enabled and
- * if it satisfies the enable_off_mode condition.
+ * A state is valid if:
+ * . it satisfies the enable_off_mode flag,
+ * . the next state for MPU and CORE power domains is not lower than the
+ *   state programmed by the per-device PM QoS.
  */
 static int next_valid_state(struct cpuidle_device *dev,
 			    struct cpuidle_driver *drv, int index)
@@ -161,6 +167,8 @@ static int next_valid_state(struct cpuidle_device *dev,
 	struct omap3_idle_statedata *cx = &omap3_idle_data[index];
 	u32 mpu_deepest_state = PWRDM_FUNC_PWRST_CSWR;
 	u32 core_deepest_state = PWRDM_FUNC_PWRST_CSWR;
+	u32 mpu_pm_qos_next_state = mpu_pd->wkup_lat_next_state;
+	u32 core_pm_qos_next_state = core_pd->wkup_lat_next_state;
 	int idx;
 	int next_index = 0; /* C1 is the default value */
 
@@ -177,7 +185,9 @@ static int next_valid_state(struct cpuidle_device *dev,
 
 	/* Check if current state is valid */
 	if ((cx->mpu_state >= mpu_deepest_state) &&
-	    (cx->core_state >= core_deepest_state))
+	    (cx->core_state >= core_deepest_state) &&
+	    (cx->mpu_state >= mpu_pm_qos_next_state) &&
+	    (cx->core_state >= core_pm_qos_next_state))
 		return index;
 
 	/*
@@ -187,7 +197,9 @@ static int next_valid_state(struct cpuidle_device *dev,
 	for (idx = index - 1; idx >= 0; idx--) {
 		cx =  &omap3_idle_data[idx];
 		if ((cx->mpu_state >= mpu_deepest_state) &&
-		    (cx->core_state >= core_deepest_state)) {
+		    (cx->core_state >= core_deepest_state) &&
+		    (cx->mpu_state >= mpu_pm_qos_next_state) &&
+		    (cx->core_state >= core_pm_qos_next_state)) {
 			next_index = idx;
 			break;
 		}
@@ -223,14 +235,6 @@ static int omap3_enter_idle_bm(struct cpuidle_device *dev,
 	else
 		new_state_idx = next_valid_state(dev, drv, index);
 
-	/*
-	 * FIXME: we currently manage device-specific idle states
-	 *        for PER and CORE in combination with CPU-specific
-	 *        idle states.  This is wrong, and device-specific
-	 *        idle management needs to be separated out into
-	 *        its own code.
-	 */
-
 	/* Program PER state */
 	cx = &omap3_idle_data[new_state_idx];
 	core_next_state = cx->core_state;
@@ -264,6 +268,19 @@ static int omap3_enter_idle_bm(struct cpuidle_device *dev,
 
 DEFINE_PER_CPU(struct cpuidle_device, omap3_idle_dev);
 
+/*
+ * Note about the latency related fields of the cpuidle_driver struct:
+ *
+ * - exit_latency = sleep + wake-up latencies of the MPU,
+ *  which include the MPU itself and the peripherals needed
+ *  for the MPU to execute instructions (e.g. main memory,
+ *  caches, IRQ controller, MMU etc). Some of those peripherals
+ *  can belong to other power domains than the MPU subsystem and so
+ *  the corresponding latencies must be included in this figure
+ *
+ * - target_residency: required amount of time in the C state
+ *  to break even on energy cost
+ */
 struct cpuidle_driver omap3_idle_driver = {
 	.name = 	"omap3_idle",
 	.owner = 	THIS_MODULE,
-- 
1.7.7.6

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

* [PATCH 5/8] ARM: OMAP3: update cpuidle latency and threshold figures
  2012-09-18  8:52 ` Jean Pihet
@ 2012-09-18  8:52   ` Jean Pihet
  -1 siblings, 0 replies; 28+ messages in thread
From: Jean Pihet @ 2012-09-18  8:52 UTC (permalink / raw)
  To: linux-omap, paul, linux-arm-kernel, khilman; +Cc: Jean Pihet

Update the data from the measurements performed at HW and SW levels.

Cf. http://www.omappedia.org/wiki/Power_Management_Device_Latencies_Measurement
for a detailed explanation on where are the numbers coming from.

ToDo:
- Measure the wake-up latencies for all power domains for OMAP3
- Correct some numbers when sys_clkreq and sys_offmode are supported

Signed-off-by: Jean Pihet <j-pihet@ti.com>
Reviewed-by: Kevin Hilman <khilman@ti.com>
---
 arch/arm/mach-omap2/cpuidle34xx.c |   54 ++++++++++++++++++++++++++----------
 1 files changed, 39 insertions(+), 15 deletions(-)

diff --git a/arch/arm/mach-omap2/cpuidle34xx.c b/arch/arm/mach-omap2/cpuidle34xx.c
index ca6cb71..c086374 100644
--- a/arch/arm/mach-omap2/cpuidle34xx.c
+++ b/arch/arm/mach-omap2/cpuidle34xx.c
@@ -280,63 +280,87 @@ DEFINE_PER_CPU(struct cpuidle_device, omap3_idle_dev);
  *
  * - target_residency: required amount of time in the C state
  *  to break even on energy cost
+ *
+ * The MPU latency and threshold values for the C-states are the worst case
+ * values from the HW and SW, as described in details at
+ * http://www.omappedia.org/wiki/Power_Management_Device_Latencies_Measurement#cpuidle_results
+ *
+ * Measurements conditions and remarks:
+ *  . the measurements have been performed at OPP50
+ *  . the sys_offmode signal is not supported and so not used for the
+ *    measurements. Instead the latency and threshold values for C9 are
+ *    corrected with the value for Triton 2, which is 11.5ms
+ *  . the sys_clkreq signal is not used and so a correction is needed - TBD
+ *  . the sys_clkoff signal is supported, this value need to be corrected with
+ *    the correct value of SYSCLK on/off timings (1ms for sysclk on, 2.5ms
+ *    for sysclk off)
+ *  . the setup time of DPLLs is included in the measured values. However
+ *    this is only valid for DPLLs that are enabled to auto-idle at
+ *    measurement time. There currently is no provision for the dynamic
+ *    nature of the auto-idle setting
+ *  . in order to force the cpuidle algorithm to chose the power efficient
+ *    C-states (C1, C3, C5, C7) in preference, the other C-states have a
+ *    threshold value equal to the next power efficient C-state
+ *
+ * The latency and threshold values can be overriden by data from the board
+ * files, using omap3_pm_init_cpuidle.
  */
 struct cpuidle_driver omap3_idle_driver = {
 	.name = 	"omap3_idle",
 	.owner = 	THIS_MODULE,
 	.states = {
 		{
-			.enter		  = omap3_enter_idle_bm,
-			.exit_latency	  = 2 + 2,
-			.target_residency = 5,
+			.enter		  = omap3_enter_idle,
+			.exit_latency	  = 73 + 78,
+			.target_residency = 152,
 			.flags		  = CPUIDLE_FLAG_TIME_VALID,
 			.name		  = "C1",
 			.desc		  = "MPU ON + CORE ON",
 		},
 		{
 			.enter		  = omap3_enter_idle_bm,
-			.exit_latency	  = 10 + 10,
-			.target_residency = 30,
+			.exit_latency	  = 165 + 88,
+			.target_residency = 345,
 			.flags		  = CPUIDLE_FLAG_TIME_VALID,
 			.name		  = "C2",
 			.desc		  = "MPU ON + CORE ON",
 		},
 		{
 			.enter		  = omap3_enter_idle_bm,
-			.exit_latency	  = 50 + 50,
-			.target_residency = 300,
+			.exit_latency	  = 163 + 182,
+			.target_residency = 345,
 			.flags		  = CPUIDLE_FLAG_TIME_VALID,
 			.name		  = "C3",
 			.desc		  = "MPU RET + CORE ON",
 		},
 		{
 			.enter		  = omap3_enter_idle_bm,
-			.exit_latency	  = 1500 + 1800,
-			.target_residency = 4000,
+			.exit_latency	  = 2852 + 605,
+			.target_residency = 150000,
 			.flags		  = CPUIDLE_FLAG_TIME_VALID,
 			.name		  = "C4",
 			.desc		  = "MPU OFF + CORE ON",
 		},
 		{
 			.enter		  = omap3_enter_idle_bm,
-			.exit_latency	  = 2500 + 7500,
-			.target_residency = 12000,
+			.exit_latency	  = 800 + 366,
+			.target_residency = 2120,
 			.flags		  = CPUIDLE_FLAG_TIME_VALID,
 			.name		  = "C5",
 			.desc		  = "MPU RET + CORE RET",
 		},
 		{
 			.enter		  = omap3_enter_idle_bm,
-			.exit_latency	  = 3000 + 8500,
-			.target_residency = 15000,
+			.exit_latency	  = 4080 + 801,
+			.target_residency = 215000,
 			.flags		  = CPUIDLE_FLAG_TIME_VALID,
 			.name		  = "C6",
 			.desc		  = "MPU OFF + CORE RET",
 		},
 		{
 			.enter		  = omap3_enter_idle_bm,
-			.exit_latency	  = 10000 + 30000,
-			.target_residency = 30000,
+			.exit_latency	  = 4300 + 13000,
+			.target_residency = 215000,
 			.flags		  = CPUIDLE_FLAG_TIME_VALID,
 			.name		  = "C7",
 			.desc		  = "MPU OFF + CORE OFF",
-- 
1.7.7.6


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

* [PATCH 5/8] ARM: OMAP3: update cpuidle latency and threshold figures
@ 2012-09-18  8:52   ` Jean Pihet
  0 siblings, 0 replies; 28+ messages in thread
From: Jean Pihet @ 2012-09-18  8:52 UTC (permalink / raw)
  To: linux-arm-kernel

Update the data from the measurements performed at HW and SW levels.

Cf. http://www.omappedia.org/wiki/Power_Management_Device_Latencies_Measurement
for a detailed explanation on where are the numbers coming from.

ToDo:
- Measure the wake-up latencies for all power domains for OMAP3
- Correct some numbers when sys_clkreq and sys_offmode are supported

Signed-off-by: Jean Pihet <j-pihet@ti.com>
Reviewed-by: Kevin Hilman <khilman@ti.com>
---
 arch/arm/mach-omap2/cpuidle34xx.c |   54 ++++++++++++++++++++++++++----------
 1 files changed, 39 insertions(+), 15 deletions(-)

diff --git a/arch/arm/mach-omap2/cpuidle34xx.c b/arch/arm/mach-omap2/cpuidle34xx.c
index ca6cb71..c086374 100644
--- a/arch/arm/mach-omap2/cpuidle34xx.c
+++ b/arch/arm/mach-omap2/cpuidle34xx.c
@@ -280,63 +280,87 @@ DEFINE_PER_CPU(struct cpuidle_device, omap3_idle_dev);
  *
  * - target_residency: required amount of time in the C state
  *  to break even on energy cost
+ *
+ * The MPU latency and threshold values for the C-states are the worst case
+ * values from the HW and SW, as described in details at
+ * http://www.omappedia.org/wiki/Power_Management_Device_Latencies_Measurement#cpuidle_results
+ *
+ * Measurements conditions and remarks:
+ *  . the measurements have been performed at OPP50
+ *  . the sys_offmode signal is not supported and so not used for the
+ *    measurements. Instead the latency and threshold values for C9 are
+ *    corrected with the value for Triton 2, which is 11.5ms
+ *  . the sys_clkreq signal is not used and so a correction is needed - TBD
+ *  . the sys_clkoff signal is supported, this value need to be corrected with
+ *    the correct value of SYSCLK on/off timings (1ms for sysclk on, 2.5ms
+ *    for sysclk off)
+ *  . the setup time of DPLLs is included in the measured values. However
+ *    this is only valid for DPLLs that are enabled to auto-idle at
+ *    measurement time. There currently is no provision for the dynamic
+ *    nature of the auto-idle setting
+ *  . in order to force the cpuidle algorithm to chose the power efficient
+ *    C-states (C1, C3, C5, C7) in preference, the other C-states have a
+ *    threshold value equal to the next power efficient C-state
+ *
+ * The latency and threshold values can be overriden by data from the board
+ * files, using omap3_pm_init_cpuidle.
  */
 struct cpuidle_driver omap3_idle_driver = {
 	.name = 	"omap3_idle",
 	.owner = 	THIS_MODULE,
 	.states = {
 		{
-			.enter		  = omap3_enter_idle_bm,
-			.exit_latency	  = 2 + 2,
-			.target_residency = 5,
+			.enter		  = omap3_enter_idle,
+			.exit_latency	  = 73 + 78,
+			.target_residency = 152,
 			.flags		  = CPUIDLE_FLAG_TIME_VALID,
 			.name		  = "C1",
 			.desc		  = "MPU ON + CORE ON",
 		},
 		{
 			.enter		  = omap3_enter_idle_bm,
-			.exit_latency	  = 10 + 10,
-			.target_residency = 30,
+			.exit_latency	  = 165 + 88,
+			.target_residency = 345,
 			.flags		  = CPUIDLE_FLAG_TIME_VALID,
 			.name		  = "C2",
 			.desc		  = "MPU ON + CORE ON",
 		},
 		{
 			.enter		  = omap3_enter_idle_bm,
-			.exit_latency	  = 50 + 50,
-			.target_residency = 300,
+			.exit_latency	  = 163 + 182,
+			.target_residency = 345,
 			.flags		  = CPUIDLE_FLAG_TIME_VALID,
 			.name		  = "C3",
 			.desc		  = "MPU RET + CORE ON",
 		},
 		{
 			.enter		  = omap3_enter_idle_bm,
-			.exit_latency	  = 1500 + 1800,
-			.target_residency = 4000,
+			.exit_latency	  = 2852 + 605,
+			.target_residency = 150000,
 			.flags		  = CPUIDLE_FLAG_TIME_VALID,
 			.name		  = "C4",
 			.desc		  = "MPU OFF + CORE ON",
 		},
 		{
 			.enter		  = omap3_enter_idle_bm,
-			.exit_latency	  = 2500 + 7500,
-			.target_residency = 12000,
+			.exit_latency	  = 800 + 366,
+			.target_residency = 2120,
 			.flags		  = CPUIDLE_FLAG_TIME_VALID,
 			.name		  = "C5",
 			.desc		  = "MPU RET + CORE RET",
 		},
 		{
 			.enter		  = omap3_enter_idle_bm,
-			.exit_latency	  = 3000 + 8500,
-			.target_residency = 15000,
+			.exit_latency	  = 4080 + 801,
+			.target_residency = 215000,
 			.flags		  = CPUIDLE_FLAG_TIME_VALID,
 			.name		  = "C6",
 			.desc		  = "MPU OFF + CORE RET",
 		},
 		{
 			.enter		  = omap3_enter_idle_bm,
-			.exit_latency	  = 10000 + 30000,
-			.target_residency = 30000,
+			.exit_latency	  = 4300 + 13000,
+			.target_residency = 215000,
 			.flags		  = CPUIDLE_FLAG_TIME_VALID,
 			.name		  = "C7",
 			.desc		  = "MPU OFF + CORE OFF",
-- 
1.7.7.6

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

* [PATCH 6/8] ARM: OMAP3: powerdomain data: add wake-up latency figures
  2012-09-18  8:52 ` Jean Pihet
@ 2012-09-18  8:52   ` Jean Pihet
  -1 siblings, 0 replies; 28+ messages in thread
From: Jean Pihet @ 2012-09-18  8:52 UTC (permalink / raw)
  To: linux-omap, paul, linux-arm-kernel, khilman; +Cc: Jean Pihet

Figures are added to the power domains structs for RET and OFF modes.

Note: the latency figures for MPU, PER, CORE, NEON have been obtained
from actual measurements.
The latency figures for the other power domains are preliminary and
shall be added.

Cf. http://www.omappedia.org/wiki/Power_Management_Device_Latencies_Measurement
for a detailed explanation on where are the numbers coming from.

Tested on OMAP3 Beagleboard in RET/OFF using wake-up latency constraints
on MPU, CORE and PER.

Signed-off-by: Jean Pihet <j-pihet@ti.com>
Reviewed-by: Kevin Hilman <khilman@ti.com>
---
 arch/arm/mach-omap2/powerdomains3xxx_data.c |   83 +++++++++++++++++++++++++++
 1 files changed, 83 insertions(+), 0 deletions(-)

diff --git a/arch/arm/mach-omap2/powerdomains3xxx_data.c b/arch/arm/mach-omap2/powerdomains3xxx_data.c
index bb883e4..2195518 100644
--- a/arch/arm/mach-omap2/powerdomains3xxx_data.c
+++ b/arch/arm/mach-omap2/powerdomains3xxx_data.c
@@ -32,6 +32,19 @@
 
 /*
  * Powerdomains
+ *
+ * The wakeup_lat values are derived from HW and SW measurements on
+ * the actual target. For more details cf.
+ * http://www.omappedia.org/wiki/Power_Management_Device_Latencies_Measurement#Results_for_individual_power_domains
+ *
+ * Note: the latency figures for MPU, PER, CORE, NEON have been obtained
+ * from actual measurements.
+ * The latency figures for the other power domains are preliminary and
+ * shall be added.
+ *
+ * Note: only the SW restore timing values are taken into account.
+ * The HW impact of the sys_clkreq and sys_offmode signals is not taken
+ * into account - TDB
  */
 
 static struct powerdomain iva2_pwrdm = {
@@ -52,6 +65,13 @@ static struct powerdomain iva2_pwrdm = {
 		[2] = PWRSTS_OFF_ON,
 		[3] = PWRSTS_ON,
 	},
+	.wakeup_lat = {
+		[PWRDM_FUNC_PWRST_OFF] = 1100,
+		[PWRDM_FUNC_PWRST_OSWR] = UNSUP_STATE,
+		[PWRDM_FUNC_PWRST_CSWR] = 350,
+		[PWRDM_FUNC_PWRST_INACTIVE] = UNSUP_STATE,
+		[PWRDM_FUNC_PWRST_ON] = 0,
+	},
 	.voltdm           = { .name = "mpu_iva" },
 };
 
@@ -68,6 +88,13 @@ static struct powerdomain mpu_3xxx_pwrdm = {
 	.pwrsts_mem_on	  = {
 		[0] = PWRSTS_OFF_ON,
 	},
+	.wakeup_lat = {
+		[PWRDM_FUNC_PWRST_OFF] = 1830,
+		[PWRDM_FUNC_PWRST_OSWR] = UNSUP_STATE,
+		[PWRDM_FUNC_PWRST_CSWR] = 121,
+		[PWRDM_FUNC_PWRST_INACTIVE] = UNSUP_STATE,
+		[PWRDM_FUNC_PWRST_ON] = 0,
+	},
 	.voltdm           = { .name = "mpu_iva" },
 };
 
@@ -111,6 +138,13 @@ 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] = 3082,
+		[PWRDM_FUNC_PWRST_OSWR] = UNSUP_STATE,
+		[PWRDM_FUNC_PWRST_CSWR] = 153,
+		[PWRDM_FUNC_PWRST_INACTIVE] = UNSUP_STATE,
+		[PWRDM_FUNC_PWRST_ON] = 0,
+	},
 	.voltdm           = { .name = "core" },
 };
 
@@ -133,6 +167,13 @@ 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] = 3082,
+		[PWRDM_FUNC_PWRST_OSWR] = UNSUP_STATE,
+		[PWRDM_FUNC_PWRST_CSWR] = 153,
+		[PWRDM_FUNC_PWRST_INACTIVE] = UNSUP_STATE,
+		[PWRDM_FUNC_PWRST_ON] = 0,
+	},
 	.voltdm           = { .name = "core" },
 };
 
@@ -165,6 +206,13 @@ static struct powerdomain dss_pwrdm = {
 	.pwrsts_mem_on	  = {
 		[0] = PWRSTS_ON,  /* MEMONSTATE */
 	},
+	.wakeup_lat = {
+		[PWRDM_FUNC_PWRST_OFF] = 70,
+		[PWRDM_FUNC_PWRST_OSWR] = UNSUP_STATE,
+		[PWRDM_FUNC_PWRST_CSWR] = 20,
+		[PWRDM_FUNC_PWRST_INACTIVE] = UNSUP_STATE,
+		[PWRDM_FUNC_PWRST_ON] = 0,
+	},
 	.voltdm           = { .name = "core" },
 };
 
@@ -180,6 +228,13 @@ static struct powerdomain dss_am35x_pwrdm = {
 	.pwrsts_mem_on	  = {
 		[0] = PWRSTS_ON,  /* MEMONSTATE */
 	},
+	.wakeup_lat = {
+		[PWRDM_FUNC_PWRST_OFF] = 1000,
+		[PWRDM_FUNC_PWRST_OSWR] = UNSUP_STATE,
+		[PWRDM_FUNC_PWRST_CSWR] = UNSUP_STATE,
+		[PWRDM_FUNC_PWRST_INACTIVE] = UNSUP_STATE,
+		[PWRDM_FUNC_PWRST_ON] = 0,
+	},
 	.voltdm           = { .name = "core" },
 };
 
@@ -201,6 +256,13 @@ static struct powerdomain sgx_pwrdm = {
 	.pwrsts_mem_on	  = {
 		[0] = PWRSTS_ON,  /* MEMONSTATE */
 	},
+	.wakeup_lat = {
+		[PWRDM_FUNC_PWRST_OFF] = 850,
+		[PWRDM_FUNC_PWRST_OSWR] = UNSUP_STATE,
+		[PWRDM_FUNC_PWRST_CSWR] = 35,
+		[PWRDM_FUNC_PWRST_INACTIVE] = UNSUP_STATE,
+		[PWRDM_FUNC_PWRST_ON] = 0,
+	},
 	.voltdm           = { .name = "core" },
 };
 
@@ -216,6 +278,13 @@ static struct powerdomain sgx_am35x_pwrdm = {
 	.pwrsts_mem_on	  = {
 		[0] = PWRSTS_ON,  /* MEMONSTATE */
 	},
+	.wakeup_lat = {
+		[PWRDM_FUNC_PWRST_OFF] = 671,
+		[PWRDM_FUNC_PWRST_OSWR] = UNSUP_STATE,
+		[PWRDM_FUNC_PWRST_CSWR] = 31,
+		[PWRDM_FUNC_PWRST_INACTIVE] = UNSUP_STATE,
+		[PWRDM_FUNC_PWRST_ON] = 0,
+	},
 	.voltdm           = { .name = "core" },
 };
 
@@ -231,6 +300,13 @@ static struct powerdomain cam_pwrdm = {
 	.pwrsts_mem_on	  = {
 		[0] = PWRSTS_ON,  /* MEMONSTATE */
 	},
+	.wakeup_lat = {
+		[PWRDM_FUNC_PWRST_OFF] = 800,
+		[PWRDM_FUNC_PWRST_OSWR] = UNSUP_STATE,
+		[PWRDM_FUNC_PWRST_CSWR] = 150,
+		[PWRDM_FUNC_PWRST_INACTIVE] = UNSUP_STATE,
+		[PWRDM_FUNC_PWRST_ON] = 0,
+	},
 	.voltdm           = { .name = "core" },
 };
 
@@ -275,6 +351,13 @@ static struct powerdomain neon_pwrdm = {
 	.prcm_offs	  = OMAP3430_NEON_MOD,
 	.pwrsts		  = PWRSTS_OFF_RET_ON,
 	.pwrsts_logic_ret = PWRSTS_RET,
+	.wakeup_lat = {
+		[PWRDM_FUNC_PWRST_OFF] = 0,
+		[PWRDM_FUNC_PWRST_OSWR] = UNSUP_STATE,
+		[PWRDM_FUNC_PWRST_CSWR] = 0,
+		[PWRDM_FUNC_PWRST_INACTIVE] = UNSUP_STATE,
+		[PWRDM_FUNC_PWRST_ON] = 0,
+	},
 	.voltdm           = { .name = "mpu_iva" },
 };
 
-- 
1.7.7.6


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

* [PATCH 6/8] ARM: OMAP3: powerdomain data: add wake-up latency figures
@ 2012-09-18  8:52   ` Jean Pihet
  0 siblings, 0 replies; 28+ messages in thread
From: Jean Pihet @ 2012-09-18  8:52 UTC (permalink / raw)
  To: linux-arm-kernel

Figures are added to the power domains structs for RET and OFF modes.

Note: the latency figures for MPU, PER, CORE, NEON have been obtained
from actual measurements.
The latency figures for the other power domains are preliminary and
shall be added.

Cf. http://www.omappedia.org/wiki/Power_Management_Device_Latencies_Measurement
for a detailed explanation on where are the numbers coming from.

Tested on OMAP3 Beagleboard in RET/OFF using wake-up latency constraints
on MPU, CORE and PER.

Signed-off-by: Jean Pihet <j-pihet@ti.com>
Reviewed-by: Kevin Hilman <khilman@ti.com>
---
 arch/arm/mach-omap2/powerdomains3xxx_data.c |   83 +++++++++++++++++++++++++++
 1 files changed, 83 insertions(+), 0 deletions(-)

diff --git a/arch/arm/mach-omap2/powerdomains3xxx_data.c b/arch/arm/mach-omap2/powerdomains3xxx_data.c
index bb883e4..2195518 100644
--- a/arch/arm/mach-omap2/powerdomains3xxx_data.c
+++ b/arch/arm/mach-omap2/powerdomains3xxx_data.c
@@ -32,6 +32,19 @@
 
 /*
  * Powerdomains
+ *
+ * The wakeup_lat values are derived from HW and SW measurements on
+ * the actual target. For more details cf.
+ * http://www.omappedia.org/wiki/Power_Management_Device_Latencies_Measurement#Results_for_individual_power_domains
+ *
+ * Note: the latency figures for MPU, PER, CORE, NEON have been obtained
+ * from actual measurements.
+ * The latency figures for the other power domains are preliminary and
+ * shall be added.
+ *
+ * Note: only the SW restore timing values are taken into account.
+ * The HW impact of the sys_clkreq and sys_offmode signals is not taken
+ * into account - TDB
  */
 
 static struct powerdomain iva2_pwrdm = {
@@ -52,6 +65,13 @@ static struct powerdomain iva2_pwrdm = {
 		[2] = PWRSTS_OFF_ON,
 		[3] = PWRSTS_ON,
 	},
+	.wakeup_lat = {
+		[PWRDM_FUNC_PWRST_OFF] = 1100,
+		[PWRDM_FUNC_PWRST_OSWR] = UNSUP_STATE,
+		[PWRDM_FUNC_PWRST_CSWR] = 350,
+		[PWRDM_FUNC_PWRST_INACTIVE] = UNSUP_STATE,
+		[PWRDM_FUNC_PWRST_ON] = 0,
+	},
 	.voltdm           = { .name = "mpu_iva" },
 };
 
@@ -68,6 +88,13 @@ static struct powerdomain mpu_3xxx_pwrdm = {
 	.pwrsts_mem_on	  = {
 		[0] = PWRSTS_OFF_ON,
 	},
+	.wakeup_lat = {
+		[PWRDM_FUNC_PWRST_OFF] = 1830,
+		[PWRDM_FUNC_PWRST_OSWR] = UNSUP_STATE,
+		[PWRDM_FUNC_PWRST_CSWR] = 121,
+		[PWRDM_FUNC_PWRST_INACTIVE] = UNSUP_STATE,
+		[PWRDM_FUNC_PWRST_ON] = 0,
+	},
 	.voltdm           = { .name = "mpu_iva" },
 };
 
@@ -111,6 +138,13 @@ 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] = 3082,
+		[PWRDM_FUNC_PWRST_OSWR] = UNSUP_STATE,
+		[PWRDM_FUNC_PWRST_CSWR] = 153,
+		[PWRDM_FUNC_PWRST_INACTIVE] = UNSUP_STATE,
+		[PWRDM_FUNC_PWRST_ON] = 0,
+	},
 	.voltdm           = { .name = "core" },
 };
 
@@ -133,6 +167,13 @@ 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] = 3082,
+		[PWRDM_FUNC_PWRST_OSWR] = UNSUP_STATE,
+		[PWRDM_FUNC_PWRST_CSWR] = 153,
+		[PWRDM_FUNC_PWRST_INACTIVE] = UNSUP_STATE,
+		[PWRDM_FUNC_PWRST_ON] = 0,
+	},
 	.voltdm           = { .name = "core" },
 };
 
@@ -165,6 +206,13 @@ static struct powerdomain dss_pwrdm = {
 	.pwrsts_mem_on	  = {
 		[0] = PWRSTS_ON,  /* MEMONSTATE */
 	},
+	.wakeup_lat = {
+		[PWRDM_FUNC_PWRST_OFF] = 70,
+		[PWRDM_FUNC_PWRST_OSWR] = UNSUP_STATE,
+		[PWRDM_FUNC_PWRST_CSWR] = 20,
+		[PWRDM_FUNC_PWRST_INACTIVE] = UNSUP_STATE,
+		[PWRDM_FUNC_PWRST_ON] = 0,
+	},
 	.voltdm           = { .name = "core" },
 };
 
@@ -180,6 +228,13 @@ static struct powerdomain dss_am35x_pwrdm = {
 	.pwrsts_mem_on	  = {
 		[0] = PWRSTS_ON,  /* MEMONSTATE */
 	},
+	.wakeup_lat = {
+		[PWRDM_FUNC_PWRST_OFF] = 1000,
+		[PWRDM_FUNC_PWRST_OSWR] = UNSUP_STATE,
+		[PWRDM_FUNC_PWRST_CSWR] = UNSUP_STATE,
+		[PWRDM_FUNC_PWRST_INACTIVE] = UNSUP_STATE,
+		[PWRDM_FUNC_PWRST_ON] = 0,
+	},
 	.voltdm           = { .name = "core" },
 };
 
@@ -201,6 +256,13 @@ static struct powerdomain sgx_pwrdm = {
 	.pwrsts_mem_on	  = {
 		[0] = PWRSTS_ON,  /* MEMONSTATE */
 	},
+	.wakeup_lat = {
+		[PWRDM_FUNC_PWRST_OFF] = 850,
+		[PWRDM_FUNC_PWRST_OSWR] = UNSUP_STATE,
+		[PWRDM_FUNC_PWRST_CSWR] = 35,
+		[PWRDM_FUNC_PWRST_INACTIVE] = UNSUP_STATE,
+		[PWRDM_FUNC_PWRST_ON] = 0,
+	},
 	.voltdm           = { .name = "core" },
 };
 
@@ -216,6 +278,13 @@ static struct powerdomain sgx_am35x_pwrdm = {
 	.pwrsts_mem_on	  = {
 		[0] = PWRSTS_ON,  /* MEMONSTATE */
 	},
+	.wakeup_lat = {
+		[PWRDM_FUNC_PWRST_OFF] = 671,
+		[PWRDM_FUNC_PWRST_OSWR] = UNSUP_STATE,
+		[PWRDM_FUNC_PWRST_CSWR] = 31,
+		[PWRDM_FUNC_PWRST_INACTIVE] = UNSUP_STATE,
+		[PWRDM_FUNC_PWRST_ON] = 0,
+	},
 	.voltdm           = { .name = "core" },
 };
 
@@ -231,6 +300,13 @@ static struct powerdomain cam_pwrdm = {
 	.pwrsts_mem_on	  = {
 		[0] = PWRSTS_ON,  /* MEMONSTATE */
 	},
+	.wakeup_lat = {
+		[PWRDM_FUNC_PWRST_OFF] = 800,
+		[PWRDM_FUNC_PWRST_OSWR] = UNSUP_STATE,
+		[PWRDM_FUNC_PWRST_CSWR] = 150,
+		[PWRDM_FUNC_PWRST_INACTIVE] = UNSUP_STATE,
+		[PWRDM_FUNC_PWRST_ON] = 0,
+	},
 	.voltdm           = { .name = "core" },
 };
 
@@ -275,6 +351,13 @@ static struct powerdomain neon_pwrdm = {
 	.prcm_offs	  = OMAP3430_NEON_MOD,
 	.pwrsts		  = PWRSTS_OFF_RET_ON,
 	.pwrsts_logic_ret = PWRSTS_RET,
+	.wakeup_lat = {
+		[PWRDM_FUNC_PWRST_OFF] = 0,
+		[PWRDM_FUNC_PWRST_OSWR] = UNSUP_STATE,
+		[PWRDM_FUNC_PWRST_CSWR] = 0,
+		[PWRDM_FUNC_PWRST_INACTIVE] = UNSUP_STATE,
+		[PWRDM_FUNC_PWRST_ON] = 0,
+	},
 	.voltdm           = { .name = "mpu_iva" },
 };
 
-- 
1.7.7.6

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

* [PATCH 7/8] ARM: OMAP: convert I2C driver to PM QoS for latency constraints
  2012-09-18  8:52 ` Jean Pihet
@ 2012-09-18  8:52   ` Jean Pihet
  -1 siblings, 0 replies; 28+ messages in thread
From: Jean Pihet @ 2012-09-18  8:52 UTC (permalink / raw)
  To: linux-omap, paul, linux-arm-kernel, khilman; +Cc: Jean Pihet

Convert the driver from the outdated omap_pm_set_max_mpu_wakeup_lat
API to the new PM QoS API.
Since the constraint is on the MPU subsystem, use the PM_QOS_CPU_DMA_LATENCY
class of PM QoS. The resulting MPU constraints are used by cpuidle to
decide the next power state of the MPU subsystem.

The I2C device latency timing is derived from the FIFO size and the
clock speed and so is applicable to all OMAP SoCs.

Signed-off-by: Jean Pihet <j-pihet@ti.com>
---
 arch/arm/plat-omap/i2c.c      |   21 ---------------------
 drivers/i2c/busses/i2c-omap.c |   28 +++++++++++++++++-----------
 include/linux/i2c-omap.h      |    1 -
 3 files changed, 17 insertions(+), 33 deletions(-)

diff --git a/arch/arm/plat-omap/i2c.c b/arch/arm/plat-omap/i2c.c
index db071bc..dba8338 100644
--- a/arch/arm/plat-omap/i2c.c
+++ b/arch/arm/plat-omap/i2c.c
@@ -26,7 +26,6 @@
 #include <linux/kernel.h>
 #include <linux/platform_device.h>
 #include <linux/i2c.h>
-#include <linux/i2c-omap.h>
 #include <linux/slab.h>
 #include <linux/err.h>
 #include <linux/clk.h>
@@ -34,7 +33,6 @@
 #include <mach/irqs.h>
 #include <plat/mux.h>
 #include <plat/i2c.h>
-#include <plat/omap-pm.h>
 #include <plat/omap_device.h>
 
 #define OMAP_I2C_SIZE		0x3f
@@ -129,16 +127,6 @@ static inline int omap1_i2c_add_bus(int bus_id)
 
 
 #ifdef CONFIG_ARCH_OMAP2PLUS
-/*
- * XXX This function is a temporary compatibility wrapper - only
- * needed until the I2C driver can be converted to call
- * omap_pm_set_max_dev_wakeup_lat() and handle a return code.
- */
-static void omap_pm_set_max_mpu_wakeup_lat_compat(struct device *dev, long t)
-{
-	omap_pm_set_max_mpu_wakeup_lat(dev, t);
-}
-
 static inline int omap2_i2c_add_bus(int bus_id)
 {
 	int l;
@@ -170,15 +158,6 @@ static inline int omap2_i2c_add_bus(int bus_id)
 	dev_attr = (struct omap_i2c_dev_attr *)oh->dev_attr;
 	pdata->flags = dev_attr->flags;
 
-	/*
-	 * When waiting for completion of a i2c transfer, we need to
-	 * set a wake up latency constraint for the MPU. This is to
-	 * ensure quick enough wakeup from idle, when transfer
-	 * completes.
-	 * Only omap3 has support for constraints
-	 */
-	if (cpu_is_omap34xx())
-		pdata->set_mpu_wkup_lat = omap_pm_set_max_mpu_wakeup_lat_compat;
 	pdev = omap_device_build(name, bus_id, oh, pdata,
 			sizeof(struct omap_i2c_bus_platform_data),
 			NULL, 0, 0);
diff --git a/drivers/i2c/busses/i2c-omap.c b/drivers/i2c/busses/i2c-omap.c
index 5d19a49..bd45cee 100644
--- a/drivers/i2c/busses/i2c-omap.c
+++ b/drivers/i2c/busses/i2c-omap.c
@@ -43,6 +43,7 @@
 #include <linux/slab.h>
 #include <linux/i2c-omap.h>
 #include <linux/pm_runtime.h>
+#include <linux/pm_qos.h>
 
 /* I2C controller revisions */
 #define OMAP_I2C_OMAP1_REV_2		0x20
@@ -183,8 +184,7 @@ struct omap_i2c_dev {
 	struct completion	cmd_complete;
 	struct resource		*ioarea;
 	u32			latency;	/* maximum mpu wkup latency */
-	void			(*set_mpu_wkup_lat)(struct device *dev,
-						    long latency);
+	struct pm_qos_request	pm_qos_request;
 	u32			speed;		/* Speed of bus in kHz */
 	u32			dtrev;		/* extra revision from DT */
 	u32			flags;
@@ -590,8 +590,16 @@ omap_i2c_xfer(struct i2c_adapter *adap, struct i2c_msg msgs[], int num)
 	if (r < 0)
 		goto out;
 
-	if (dev->set_mpu_wkup_lat != NULL)
-		dev->set_mpu_wkup_lat(dev->dev, dev->latency);
+	/*
+	 * When waiting for completion of a i2c transfer, we need to
+	 * set a wake up latency constraint for the MPU. This is to
+	 * ensure quick enough wakeup from idle, when transfer
+	 * completes.
+	 */
+	if (dev->latency)
+		pm_qos_add_request(&dev->pm_qos_request,
+				   PM_QOS_CPU_DMA_LATENCY,
+				   dev->latency);
 
 	for (i = 0; i < num; i++) {
 		r = omap_i2c_xfer_msg(adap, &msgs[i], (i == (num - 1)));
@@ -599,8 +607,8 @@ omap_i2c_xfer(struct i2c_adapter *adap, struct i2c_msg msgs[], int num)
 			break;
 	}
 
-	if (dev->set_mpu_wkup_lat != NULL)
-		dev->set_mpu_wkup_lat(dev->dev, -1);
+	if (dev->latency)
+		pm_qos_remove_request(&dev->pm_qos_request);
 
 	if (r == 0)
 		r = num;
@@ -989,7 +997,6 @@ omap_i2c_probe(struct platform_device *pdev)
 	} else if (pdata != NULL) {
 		dev->speed = pdata->clkrate;
 		dev->flags = pdata->flags;
-		dev->set_mpu_wkup_lat = pdata->set_mpu_wkup_lat;
 		dev->dtrev = pdata->rev;
 	}
 
@@ -1046,10 +1053,9 @@ omap_i2c_probe(struct platform_device *pdev)
 		else
 			dev->b_hw = 1; /* Enable hardware fixes */
 
-		/* calculate wakeup latency constraint for MPU */
-		if (dev->set_mpu_wkup_lat != NULL)
-			dev->latency = (1000000 * dev->fifo_size) /
-				       (1000 * dev->speed / 8);
+		/* calculate wakeup latency constraint */
+		dev->latency = (1000000 * dev->fifo_size) /
+			       (1000 * dev->speed / 8);
 	}
 
 	/* reset ASAP, clearing any IRQs */
diff --git a/include/linux/i2c-omap.h b/include/linux/i2c-omap.h
index 92a0dc7..df804ba 100644
--- a/include/linux/i2c-omap.h
+++ b/include/linux/i2c-omap.h
@@ -34,7 +34,6 @@ struct omap_i2c_bus_platform_data {
 	u32		clkrate;
 	u32		rev;
 	u32		flags;
-	void		(*set_mpu_wkup_lat)(struct device *dev, long set);
 };
 
 #endif
-- 
1.7.7.6


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

* [PATCH 7/8] ARM: OMAP: convert I2C driver to PM QoS for latency constraints
@ 2012-09-18  8:52   ` Jean Pihet
  0 siblings, 0 replies; 28+ messages in thread
From: Jean Pihet @ 2012-09-18  8:52 UTC (permalink / raw)
  To: linux-arm-kernel

Convert the driver from the outdated omap_pm_set_max_mpu_wakeup_lat
API to the new PM QoS API.
Since the constraint is on the MPU subsystem, use the PM_QOS_CPU_DMA_LATENCY
class of PM QoS. The resulting MPU constraints are used by cpuidle to
decide the next power state of the MPU subsystem.

The I2C device latency timing is derived from the FIFO size and the
clock speed and so is applicable to all OMAP SoCs.

Signed-off-by: Jean Pihet <j-pihet@ti.com>
---
 arch/arm/plat-omap/i2c.c      |   21 ---------------------
 drivers/i2c/busses/i2c-omap.c |   28 +++++++++++++++++-----------
 include/linux/i2c-omap.h      |    1 -
 3 files changed, 17 insertions(+), 33 deletions(-)

diff --git a/arch/arm/plat-omap/i2c.c b/arch/arm/plat-omap/i2c.c
index db071bc..dba8338 100644
--- a/arch/arm/plat-omap/i2c.c
+++ b/arch/arm/plat-omap/i2c.c
@@ -26,7 +26,6 @@
 #include <linux/kernel.h>
 #include <linux/platform_device.h>
 #include <linux/i2c.h>
-#include <linux/i2c-omap.h>
 #include <linux/slab.h>
 #include <linux/err.h>
 #include <linux/clk.h>
@@ -34,7 +33,6 @@
 #include <mach/irqs.h>
 #include <plat/mux.h>
 #include <plat/i2c.h>
-#include <plat/omap-pm.h>
 #include <plat/omap_device.h>
 
 #define OMAP_I2C_SIZE		0x3f
@@ -129,16 +127,6 @@ static inline int omap1_i2c_add_bus(int bus_id)
 
 
 #ifdef CONFIG_ARCH_OMAP2PLUS
-/*
- * XXX This function is a temporary compatibility wrapper - only
- * needed until the I2C driver can be converted to call
- * omap_pm_set_max_dev_wakeup_lat() and handle a return code.
- */
-static void omap_pm_set_max_mpu_wakeup_lat_compat(struct device *dev, long t)
-{
-	omap_pm_set_max_mpu_wakeup_lat(dev, t);
-}
-
 static inline int omap2_i2c_add_bus(int bus_id)
 {
 	int l;
@@ -170,15 +158,6 @@ static inline int omap2_i2c_add_bus(int bus_id)
 	dev_attr = (struct omap_i2c_dev_attr *)oh->dev_attr;
 	pdata->flags = dev_attr->flags;
 
-	/*
-	 * When waiting for completion of a i2c transfer, we need to
-	 * set a wake up latency constraint for the MPU. This is to
-	 * ensure quick enough wakeup from idle, when transfer
-	 * completes.
-	 * Only omap3 has support for constraints
-	 */
-	if (cpu_is_omap34xx())
-		pdata->set_mpu_wkup_lat = omap_pm_set_max_mpu_wakeup_lat_compat;
 	pdev = omap_device_build(name, bus_id, oh, pdata,
 			sizeof(struct omap_i2c_bus_platform_data),
 			NULL, 0, 0);
diff --git a/drivers/i2c/busses/i2c-omap.c b/drivers/i2c/busses/i2c-omap.c
index 5d19a49..bd45cee 100644
--- a/drivers/i2c/busses/i2c-omap.c
+++ b/drivers/i2c/busses/i2c-omap.c
@@ -43,6 +43,7 @@
 #include <linux/slab.h>
 #include <linux/i2c-omap.h>
 #include <linux/pm_runtime.h>
+#include <linux/pm_qos.h>
 
 /* I2C controller revisions */
 #define OMAP_I2C_OMAP1_REV_2		0x20
@@ -183,8 +184,7 @@ struct omap_i2c_dev {
 	struct completion	cmd_complete;
 	struct resource		*ioarea;
 	u32			latency;	/* maximum mpu wkup latency */
-	void			(*set_mpu_wkup_lat)(struct device *dev,
-						    long latency);
+	struct pm_qos_request	pm_qos_request;
 	u32			speed;		/* Speed of bus in kHz */
 	u32			dtrev;		/* extra revision from DT */
 	u32			flags;
@@ -590,8 +590,16 @@ omap_i2c_xfer(struct i2c_adapter *adap, struct i2c_msg msgs[], int num)
 	if (r < 0)
 		goto out;
 
-	if (dev->set_mpu_wkup_lat != NULL)
-		dev->set_mpu_wkup_lat(dev->dev, dev->latency);
+	/*
+	 * When waiting for completion of a i2c transfer, we need to
+	 * set a wake up latency constraint for the MPU. This is to
+	 * ensure quick enough wakeup from idle, when transfer
+	 * completes.
+	 */
+	if (dev->latency)
+		pm_qos_add_request(&dev->pm_qos_request,
+				   PM_QOS_CPU_DMA_LATENCY,
+				   dev->latency);
 
 	for (i = 0; i < num; i++) {
 		r = omap_i2c_xfer_msg(adap, &msgs[i], (i == (num - 1)));
@@ -599,8 +607,8 @@ omap_i2c_xfer(struct i2c_adapter *adap, struct i2c_msg msgs[], int num)
 			break;
 	}
 
-	if (dev->set_mpu_wkup_lat != NULL)
-		dev->set_mpu_wkup_lat(dev->dev, -1);
+	if (dev->latency)
+		pm_qos_remove_request(&dev->pm_qos_request);
 
 	if (r == 0)
 		r = num;
@@ -989,7 +997,6 @@ omap_i2c_probe(struct platform_device *pdev)
 	} else if (pdata != NULL) {
 		dev->speed = pdata->clkrate;
 		dev->flags = pdata->flags;
-		dev->set_mpu_wkup_lat = pdata->set_mpu_wkup_lat;
 		dev->dtrev = pdata->rev;
 	}
 
@@ -1046,10 +1053,9 @@ omap_i2c_probe(struct platform_device *pdev)
 		else
 			dev->b_hw = 1; /* Enable hardware fixes */
 
-		/* calculate wakeup latency constraint for MPU */
-		if (dev->set_mpu_wkup_lat != NULL)
-			dev->latency = (1000000 * dev->fifo_size) /
-				       (1000 * dev->speed / 8);
+		/* calculate wakeup latency constraint */
+		dev->latency = (1000000 * dev->fifo_size) /
+			       (1000 * dev->speed / 8);
 	}
 
 	/* reset ASAP, clearing any IRQs */
diff --git a/include/linux/i2c-omap.h b/include/linux/i2c-omap.h
index 92a0dc7..df804ba 100644
--- a/include/linux/i2c-omap.h
+++ b/include/linux/i2c-omap.h
@@ -34,7 +34,6 @@ struct omap_i2c_bus_platform_data {
 	u32		clkrate;
 	u32		rev;
 	u32		flags;
-	void		(*set_mpu_wkup_lat)(struct device *dev, long set);
 };
 
 #endif
-- 
1.7.7.6

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

* [PATCH 8/8] ARM: OMAP: PM: remove the latency related functions from the API
  2012-09-18  8:52 ` Jean Pihet
@ 2012-09-18  8:52   ` Jean Pihet
  -1 siblings, 0 replies; 28+ messages in thread
From: Jean Pihet @ 2012-09-18  8:52 UTC (permalink / raw)
  To: linux-omap, paul, linux-arm-kernel, khilman; +Cc: Jean Pihet

Remove the following functions from the OMAP PM API:
 omap_pm_set_max_mpu_wakeup_lat
 omap_pm_set_max_dev_wakeup_lat
 omap_pm_set_max_sdma_lat
and updated the kernel Documentation accordingly.

The generic per-device PM QoS functions shall be used instead of the
OMAP PM API, cf. include/linux/pm_qos.h and
Documentation/power/pm_qos_interface.txt.

Signed-off-by: Jean Pihet <j-pihet@ti.com>
---
 Documentation/arm/OMAP/omap_pm            |   68 ++++++--------------
 arch/arm/plat-omap/include/plat/omap-pm.h |   99 -----------------------------
 arch/arm/plat-omap/omap-pm-noop.c         |   88 -------------------------
 3 files changed, 21 insertions(+), 234 deletions(-)

diff --git a/Documentation/arm/OMAP/omap_pm b/Documentation/arm/OMAP/omap_pm
index 9012bb0..90f67a3 100644
--- a/Documentation/arm/OMAP/omap_pm
+++ b/Documentation/arm/OMAP/omap_pm
@@ -5,8 +5,19 @@ The OMAP PM interface
 This document describes the temporary OMAP PM interface.  Driver
 authors use these functions to communicate minimum latency or
 throughput constraints to the kernel power management code.
+
 Over time, the intention is to merge features from the OMAP PM
 interface into the Linux PM QoS code.
+The following PM QoS features already migrated to the PM QoS framework:
+- PM QoS CPU and DMA latency: used to specify the maximum allowed wake-up
+  latency for the CPU,
+- per device PM QoS latency: used to specifiy the maximum allowed wake-up
+  latency for a given device.
+
+Please refer to the corresponding header file and kernel documentation
+for the PM QoS framework, respectively at include/linux/pm_qos.h and
+Documentation/power/pm_qos_interface.txt.
+
 
 Drivers need to express PM parameters which:
 
@@ -29,21 +40,12 @@ Drivers need to express PM parameters which:
 
 
 This document proposes the OMAP PM interface, including the following
-five power management functions for driver code:
-
-1. Set the maximum MPU wakeup latency:
-   (*pdata->set_max_mpu_wakeup_lat)(struct device *dev, unsigned long t)
+power management functions for driver code:
 
-2. Set the maximum device wakeup latency:
-   (*pdata->set_max_dev_wakeup_lat)(struct device *dev, unsigned long t)
-
-3. Set the maximum system DMA transfer start latency (CORE pwrdm):
-   (*pdata->set_max_sdma_lat)(struct device *dev, long t)
-
-4. Set the minimum bus throughput needed by a device:
+1. Set the minimum bus throughput needed by a device:
    (*pdata->set_min_bus_tput)(struct device *dev, u8 agent_id, unsigned long r)
 
-5. Return the number of times the device has lost context
+2. Return the number of times the device has lost context
    (*pdata->get_dev_context_loss_count)(struct device *dev)
 
 
@@ -55,10 +57,12 @@ The OMAP PM layer is intended to be temporary
 ---------------------------------------------
 
 The intention is that eventually the Linux PM QoS layer should support
-the range of power management features present in OMAP3.  As this
-happens, existing drivers using the OMAP PM interface can be modified
+the range of power management features present in OMAP3. As this
+happens, existing drivers using the OMAP PM interface shall be modified
 to use the Linux PM QoS code; and the OMAP PM interface can disappear.
 
+The set_min_bus_tput function shall be converted to a throughput PM QoS
+framework.
 
 Driver usage of the OMAP PM functions
 -------------------------------------
@@ -66,39 +70,9 @@ Driver usage of the OMAP PM functions
 As the 'pdata' in the above examples indicates, these functions are
 exposed to drivers through function pointers in driver .platform_data
 structures.  The function pointers are initialized by the board-*.c
-files to point to the corresponding OMAP PM functions:
-.set_max_dev_wakeup_lat will point to
-omap_pm_set_max_dev_wakeup_lat(), etc.  Other architectures which do
-not support these functions should leave these function pointers set
-to NULL.  Drivers should use the following idiom:
-
-        if (pdata->set_max_dev_wakeup_lat)
-            (*pdata->set_max_dev_wakeup_lat)(dev, t);
-
-The most common usage of these functions will probably be to specify
-the maximum time from when an interrupt occurs, to when the device
-becomes accessible.  To accomplish this, driver writers should use the
-set_max_mpu_wakeup_lat() function to to constrain the MPU wakeup
-latency, and the set_max_dev_wakeup_lat() function to constrain the
-device wakeup latency (from clk_enable() to accessibility).  For
-example,
-
-        /* Limit MPU wakeup latency */
-        if (pdata->set_max_mpu_wakeup_lat)
-            (*pdata->set_max_mpu_wakeup_lat)(dev, tc);
-
-        /* Limit device powerdomain wakeup latency */
-        if (pdata->set_max_dev_wakeup_lat)
-            (*pdata->set_max_dev_wakeup_lat)(dev, td);
-
-        /* total wakeup latency in this example: (tc + td) */
-
-The PM parameters can be overwritten by calling the function again
-with the new value.  The settings can be removed by calling the
-function with a t argument of -1 (except in the case of
-set_max_bus_tput(), which should be called with an r argument of 0).
-
-The fifth function above, omap_pm_get_dev_context_loss_count(),
+files to point to the corresponding OMAP PM functions.
+
+The omap_pm_get_dev_context_loss_count() function
 is intended as an optimization to allow drivers to determine whether the
 device has lost its internal context.  If context has been lost, the
 driver must restore its internal context before proceeding.
diff --git a/arch/arm/plat-omap/include/plat/omap-pm.h b/arch/arm/plat-omap/include/plat/omap-pm.h
index 67faa7b..a3ece28 100644
--- a/arch/arm/plat-omap/include/plat/omap-pm.h
+++ b/arch/arm/plat-omap/include/plat/omap-pm.h
@@ -62,44 +62,6 @@ void omap_pm_if_exit(void);
  * Device-driver-originated constraints (via board-*.c files, platform_data)
  */
 
-
-/**
- * omap_pm_set_max_mpu_wakeup_lat - set the maximum MPU wakeup latency
- * @dev: struct device * requesting the constraint
- * @t: maximum MPU wakeup latency in microseconds
- *
- * Request that the maximum interrupt latency for the MPU to be no
- * greater than @t microseconds. "Interrupt latency" in this case is
- * defined as the elapsed time from the occurrence of a hardware or
- * timer interrupt to the time when the device driver's interrupt
- * service routine has been entered by the MPU.
- *
- * It is intended that underlying PM code will use this information to
- * determine what power state to put the MPU powerdomain into, and
- * possibly the CORE powerdomain as well, since interrupt handling
- * code currently runs from SDRAM.  Advanced PM or board*.c code may
- * also configure interrupt controller priorities, OCP bus priorities,
- * CPU speed(s), etc.
- *
- * This function will not affect device wakeup latency, e.g., time
- * elapsed from when a device driver enables a hardware device with
- * clk_enable(), to when the device is ready for register access or
- * other use.  To control this device wakeup latency, use
- * omap_pm_set_max_dev_wakeup_lat()
- *
- * Multiple calls to omap_pm_set_max_mpu_wakeup_lat() will replace the
- * previous t value.  To remove the latency target for the MPU, call
- * with t = -1.
- *
- * XXX This constraint will be deprecated soon in favor of the more
- * general omap_pm_set_max_dev_wakeup_lat()
- *
- * Returns -EINVAL for an invalid argument, -ERANGE if the constraint
- * is not satisfiable, or 0 upon success.
- */
-int omap_pm_set_max_mpu_wakeup_lat(struct device *dev, long t);
-
-
 /**
  * omap_pm_set_min_bus_tput - set minimum bus throughput needed by device
  * @dev: struct device * requesting the constraint
@@ -131,67 +93,6 @@ int omap_pm_set_max_mpu_wakeup_lat(struct device *dev, long t);
  */
 int omap_pm_set_min_bus_tput(struct device *dev, u8 agent_id, unsigned long r);
 
-
-/**
- * omap_pm_set_max_dev_wakeup_lat - set the maximum device enable latency
- * @req_dev: struct device * requesting the constraint, or NULL if none
- * @dev: struct device * to set the constraint one
- * @t: maximum device wakeup latency in microseconds
- *
- * Request that the maximum amount of time necessary for a device @dev
- * to become accessible after its clocks are enabled should be no
- * greater than @t microseconds.  Specifically, this represents the
- * time from when a device driver enables device clocks with
- * clk_enable(), to when the register reads and writes on the device
- * will succeed.  This function should be called before clk_disable()
- * is called, since the power state transition decision may be made
- * during clk_disable().
- *
- * It is intended that underlying PM code will use this information to
- * determine what power state to put the powerdomain enclosing this
- * device into.
- *
- * Multiple calls to omap_pm_set_max_dev_wakeup_lat() will replace the
- * previous wakeup latency values for this device.  To remove the
- * wakeup latency restriction for this device, call with t = -1.
- *
- * Returns -EINVAL for an invalid argument, -ERANGE if the constraint
- * is not satisfiable, or 0 upon success.
- */
-int omap_pm_set_max_dev_wakeup_lat(struct device *req_dev, struct device *dev,
-				   long t);
-
-
-/**
- * omap_pm_set_max_sdma_lat - set the maximum system DMA transfer start latency
- * @dev: struct device *
- * @t: maximum DMA transfer start latency in microseconds
- *
- * Request that the maximum system DMA transfer start latency for this
- * device 'dev' should be no greater than 't' microseconds.  "DMA
- * transfer start latency" here is defined as the elapsed time from
- * when a device (e.g., McBSP) requests that a system DMA transfer
- * start or continue, to the time at which data starts to flow into
- * that device from the system DMA controller.
- *
- * It is intended that underlying PM code will use this information to
- * determine what power state to put the CORE powerdomain into.
- *
- * Since system DMA transfers may not involve the MPU, this function
- * will not affect MPU wakeup latency.  Use set_max_cpu_lat() to do
- * so.  Similarly, this function will not affect device wakeup latency
- * -- use set_max_dev_wakeup_lat() to affect that.
- *
- * Multiple calls to set_max_sdma_lat() will replace the previous t
- * value for this device.  To remove the maximum DMA latency for this
- * device, call with t = -1.
- *
- * Returns -EINVAL for an invalid argument, -ERANGE if the constraint
- * is not satisfiable, or 0 upon success.
- */
-int omap_pm_set_max_sdma_lat(struct device *dev, long t);
-
-
 /**
  * omap_pm_set_min_clk_rate - set minimum clock rate requested by @dev
  * @dev: struct device * requesting the constraint
diff --git a/arch/arm/plat-omap/omap-pm-noop.c b/arch/arm/plat-omap/omap-pm-noop.c
index 5a97b4d..e6c59d2 100644
--- a/arch/arm/plat-omap/omap-pm-noop.c
+++ b/arch/arm/plat-omap/omap-pm-noop.c
@@ -33,34 +33,6 @@ static int 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 &&
@@ -88,66 +60,6 @@ int omap_pm_set_min_bus_tput(struct device *dev, u8 agent_id, unsigned long r)
 	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) {
-- 
1.7.7.6


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

* [PATCH 8/8] ARM: OMAP: PM: remove the latency related functions from the API
@ 2012-09-18  8:52   ` Jean Pihet
  0 siblings, 0 replies; 28+ messages in thread
From: Jean Pihet @ 2012-09-18  8:52 UTC (permalink / raw)
  To: linux-arm-kernel

Remove the following functions from the OMAP PM API:
 omap_pm_set_max_mpu_wakeup_lat
 omap_pm_set_max_dev_wakeup_lat
 omap_pm_set_max_sdma_lat
and updated the kernel Documentation accordingly.

The generic per-device PM QoS functions shall be used instead of the
OMAP PM API, cf. include/linux/pm_qos.h and
Documentation/power/pm_qos_interface.txt.

Signed-off-by: Jean Pihet <j-pihet@ti.com>
---
 Documentation/arm/OMAP/omap_pm            |   68 ++++++--------------
 arch/arm/plat-omap/include/plat/omap-pm.h |   99 -----------------------------
 arch/arm/plat-omap/omap-pm-noop.c         |   88 -------------------------
 3 files changed, 21 insertions(+), 234 deletions(-)

diff --git a/Documentation/arm/OMAP/omap_pm b/Documentation/arm/OMAP/omap_pm
index 9012bb0..90f67a3 100644
--- a/Documentation/arm/OMAP/omap_pm
+++ b/Documentation/arm/OMAP/omap_pm
@@ -5,8 +5,19 @@ The OMAP PM interface
 This document describes the temporary OMAP PM interface.  Driver
 authors use these functions to communicate minimum latency or
 throughput constraints to the kernel power management code.
+
 Over time, the intention is to merge features from the OMAP PM
 interface into the Linux PM QoS code.
+The following PM QoS features already migrated to the PM QoS framework:
+- PM QoS CPU and DMA latency: used to specify the maximum allowed wake-up
+  latency for the CPU,
+- per device PM QoS latency: used to specifiy the maximum allowed wake-up
+  latency for a given device.
+
+Please refer to the corresponding header file and kernel documentation
+for the PM QoS framework, respectively at include/linux/pm_qos.h and
+Documentation/power/pm_qos_interface.txt.
+
 
 Drivers need to express PM parameters which:
 
@@ -29,21 +40,12 @@ Drivers need to express PM parameters which:
 
 
 This document proposes the OMAP PM interface, including the following
-five power management functions for driver code:
-
-1. Set the maximum MPU wakeup latency:
-   (*pdata->set_max_mpu_wakeup_lat)(struct device *dev, unsigned long t)
+power management functions for driver code:
 
-2. Set the maximum device wakeup latency:
-   (*pdata->set_max_dev_wakeup_lat)(struct device *dev, unsigned long t)
-
-3. Set the maximum system DMA transfer start latency (CORE pwrdm):
-   (*pdata->set_max_sdma_lat)(struct device *dev, long t)
-
-4. Set the minimum bus throughput needed by a device:
+1. Set the minimum bus throughput needed by a device:
    (*pdata->set_min_bus_tput)(struct device *dev, u8 agent_id, unsigned long r)
 
-5. Return the number of times the device has lost context
+2. Return the number of times the device has lost context
    (*pdata->get_dev_context_loss_count)(struct device *dev)
 
 
@@ -55,10 +57,12 @@ The OMAP PM layer is intended to be temporary
 ---------------------------------------------
 
 The intention is that eventually the Linux PM QoS layer should support
-the range of power management features present in OMAP3.  As this
-happens, existing drivers using the OMAP PM interface can be modified
+the range of power management features present in OMAP3. As this
+happens, existing drivers using the OMAP PM interface shall be modified
 to use the Linux PM QoS code; and the OMAP PM interface can disappear.
 
+The set_min_bus_tput function shall be converted to a throughput PM QoS
+framework.
 
 Driver usage of the OMAP PM functions
 -------------------------------------
@@ -66,39 +70,9 @@ Driver usage of the OMAP PM functions
 As the 'pdata' in the above examples indicates, these functions are
 exposed to drivers through function pointers in driver .platform_data
 structures.  The function pointers are initialized by the board-*.c
-files to point to the corresponding OMAP PM functions:
-.set_max_dev_wakeup_lat will point to
-omap_pm_set_max_dev_wakeup_lat(), etc.  Other architectures which do
-not support these functions should leave these function pointers set
-to NULL.  Drivers should use the following idiom:
-
-        if (pdata->set_max_dev_wakeup_lat)
-            (*pdata->set_max_dev_wakeup_lat)(dev, t);
-
-The most common usage of these functions will probably be to specify
-the maximum time from when an interrupt occurs, to when the device
-becomes accessible.  To accomplish this, driver writers should use the
-set_max_mpu_wakeup_lat() function to to constrain the MPU wakeup
-latency, and the set_max_dev_wakeup_lat() function to constrain the
-device wakeup latency (from clk_enable() to accessibility).  For
-example,
-
-        /* Limit MPU wakeup latency */
-        if (pdata->set_max_mpu_wakeup_lat)
-            (*pdata->set_max_mpu_wakeup_lat)(dev, tc);
-
-        /* Limit device powerdomain wakeup latency */
-        if (pdata->set_max_dev_wakeup_lat)
-            (*pdata->set_max_dev_wakeup_lat)(dev, td);
-
-        /* total wakeup latency in this example: (tc + td) */
-
-The PM parameters can be overwritten by calling the function again
-with the new value.  The settings can be removed by calling the
-function with a t argument of -1 (except in the case of
-set_max_bus_tput(), which should be called with an r argument of 0).
-
-The fifth function above, omap_pm_get_dev_context_loss_count(),
+files to point to the corresponding OMAP PM functions.
+
+The omap_pm_get_dev_context_loss_count() function
 is intended as an optimization to allow drivers to determine whether the
 device has lost its internal context.  If context has been lost, the
 driver must restore its internal context before proceeding.
diff --git a/arch/arm/plat-omap/include/plat/omap-pm.h b/arch/arm/plat-omap/include/plat/omap-pm.h
index 67faa7b..a3ece28 100644
--- a/arch/arm/plat-omap/include/plat/omap-pm.h
+++ b/arch/arm/plat-omap/include/plat/omap-pm.h
@@ -62,44 +62,6 @@ void omap_pm_if_exit(void);
  * Device-driver-originated constraints (via board-*.c files, platform_data)
  */
 
-
-/**
- * omap_pm_set_max_mpu_wakeup_lat - set the maximum MPU wakeup latency
- * @dev: struct device * requesting the constraint
- * @t: maximum MPU wakeup latency in microseconds
- *
- * Request that the maximum interrupt latency for the MPU to be no
- * greater than @t microseconds. "Interrupt latency" in this case is
- * defined as the elapsed time from the occurrence of a hardware or
- * timer interrupt to the time when the device driver's interrupt
- * service routine has been entered by the MPU.
- *
- * It is intended that underlying PM code will use this information to
- * determine what power state to put the MPU powerdomain into, and
- * possibly the CORE powerdomain as well, since interrupt handling
- * code currently runs from SDRAM.  Advanced PM or board*.c code may
- * also configure interrupt controller priorities, OCP bus priorities,
- * CPU speed(s), etc.
- *
- * This function will not affect device wakeup latency, e.g., time
- * elapsed from when a device driver enables a hardware device with
- * clk_enable(), to when the device is ready for register access or
- * other use.  To control this device wakeup latency, use
- * omap_pm_set_max_dev_wakeup_lat()
- *
- * Multiple calls to omap_pm_set_max_mpu_wakeup_lat() will replace the
- * previous t value.  To remove the latency target for the MPU, call
- * with t = -1.
- *
- * XXX This constraint will be deprecated soon in favor of the more
- * general omap_pm_set_max_dev_wakeup_lat()
- *
- * Returns -EINVAL for an invalid argument, -ERANGE if the constraint
- * is not satisfiable, or 0 upon success.
- */
-int omap_pm_set_max_mpu_wakeup_lat(struct device *dev, long t);
-
-
 /**
  * omap_pm_set_min_bus_tput - set minimum bus throughput needed by device
  * @dev: struct device * requesting the constraint
@@ -131,67 +93,6 @@ int omap_pm_set_max_mpu_wakeup_lat(struct device *dev, long t);
  */
 int omap_pm_set_min_bus_tput(struct device *dev, u8 agent_id, unsigned long r);
 
-
-/**
- * omap_pm_set_max_dev_wakeup_lat - set the maximum device enable latency
- * @req_dev: struct device * requesting the constraint, or NULL if none
- * @dev: struct device * to set the constraint one
- * @t: maximum device wakeup latency in microseconds
- *
- * Request that the maximum amount of time necessary for a device @dev
- * to become accessible after its clocks are enabled should be no
- * greater than @t microseconds.  Specifically, this represents the
- * time from when a device driver enables device clocks with
- * clk_enable(), to when the register reads and writes on the device
- * will succeed.  This function should be called before clk_disable()
- * is called, since the power state transition decision may be made
- * during clk_disable().
- *
- * It is intended that underlying PM code will use this information to
- * determine what power state to put the powerdomain enclosing this
- * device into.
- *
- * Multiple calls to omap_pm_set_max_dev_wakeup_lat() will replace the
- * previous wakeup latency values for this device.  To remove the
- * wakeup latency restriction for this device, call with t = -1.
- *
- * Returns -EINVAL for an invalid argument, -ERANGE if the constraint
- * is not satisfiable, or 0 upon success.
- */
-int omap_pm_set_max_dev_wakeup_lat(struct device *req_dev, struct device *dev,
-				   long t);
-
-
-/**
- * omap_pm_set_max_sdma_lat - set the maximum system DMA transfer start latency
- * @dev: struct device *
- * @t: maximum DMA transfer start latency in microseconds
- *
- * Request that the maximum system DMA transfer start latency for this
- * device 'dev' should be no greater than 't' microseconds.  "DMA
- * transfer start latency" here is defined as the elapsed time from
- * when a device (e.g., McBSP) requests that a system DMA transfer
- * start or continue, to the time at which data starts to flow into
- * that device from the system DMA controller.
- *
- * It is intended that underlying PM code will use this information to
- * determine what power state to put the CORE powerdomain into.
- *
- * Since system DMA transfers may not involve the MPU, this function
- * will not affect MPU wakeup latency.  Use set_max_cpu_lat() to do
- * so.  Similarly, this function will not affect device wakeup latency
- * -- use set_max_dev_wakeup_lat() to affect that.
- *
- * Multiple calls to set_max_sdma_lat() will replace the previous t
- * value for this device.  To remove the maximum DMA latency for this
- * device, call with t = -1.
- *
- * Returns -EINVAL for an invalid argument, -ERANGE if the constraint
- * is not satisfiable, or 0 upon success.
- */
-int omap_pm_set_max_sdma_lat(struct device *dev, long t);
-
-
 /**
  * omap_pm_set_min_clk_rate - set minimum clock rate requested by @dev
  * @dev: struct device * requesting the constraint
diff --git a/arch/arm/plat-omap/omap-pm-noop.c b/arch/arm/plat-omap/omap-pm-noop.c
index 5a97b4d..e6c59d2 100644
--- a/arch/arm/plat-omap/omap-pm-noop.c
+++ b/arch/arm/plat-omap/omap-pm-noop.c
@@ -33,34 +33,6 @@ static int 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 &&
@@ -88,66 +60,6 @@ int omap_pm_set_min_bus_tput(struct device *dev, u8 agent_id, unsigned long r)
 	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) {
-- 
1.7.7.6

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

* Re: [PATCH 7/8] ARM: OMAP: convert I2C driver to PM QoS for latency constraints
  2012-09-18  8:52   ` Jean Pihet
@ 2012-09-18  8:53     ` Felipe Balbi
  -1 siblings, 0 replies; 28+ messages in thread
From: Felipe Balbi @ 2012-09-18  8:53 UTC (permalink / raw)
  To: Jean Pihet
  Cc: linux-omap, paul, linux-arm-kernel, khilman, Jean Pihet,
	Shubhrajyoti Datta

[-- Attachment #1: Type: text/plain, Size: 5998 bytes --]

Hi,

On Tue, Sep 18, 2012 at 10:52:11AM +0200, Jean Pihet wrote:
> Convert the driver from the outdated omap_pm_set_max_mpu_wakeup_lat
> API to the new PM QoS API.
> Since the constraint is on the MPU subsystem, use the PM_QOS_CPU_DMA_LATENCY
> class of PM QoS. The resulting MPU constraints are used by cpuidle to
> decide the next power state of the MPU subsystem.
> 
> The I2C device latency timing is derived from the FIFO size and the
> clock speed and so is applicable to all OMAP SoCs.
> 
> Signed-off-by: Jean Pihet <j-pihet@ti.com>

this needs to be rebased on top of Wolfram's i2c-next branch which has a
big series of patches on top of the i2c driver.

Also, please keep Shubhro in Cc for any i2c and spi patches ;-)

> ---
>  arch/arm/plat-omap/i2c.c      |   21 ---------------------
>  drivers/i2c/busses/i2c-omap.c |   28 +++++++++++++++++-----------
>  include/linux/i2c-omap.h      |    1 -
>  3 files changed, 17 insertions(+), 33 deletions(-)
> 
> diff --git a/arch/arm/plat-omap/i2c.c b/arch/arm/plat-omap/i2c.c
> index db071bc..dba8338 100644
> --- a/arch/arm/plat-omap/i2c.c
> +++ b/arch/arm/plat-omap/i2c.c
> @@ -26,7 +26,6 @@
>  #include <linux/kernel.h>
>  #include <linux/platform_device.h>
>  #include <linux/i2c.h>
> -#include <linux/i2c-omap.h>
>  #include <linux/slab.h>
>  #include <linux/err.h>
>  #include <linux/clk.h>
> @@ -34,7 +33,6 @@
>  #include <mach/irqs.h>
>  #include <plat/mux.h>
>  #include <plat/i2c.h>
> -#include <plat/omap-pm.h>
>  #include <plat/omap_device.h>
>  
>  #define OMAP_I2C_SIZE		0x3f
> @@ -129,16 +127,6 @@ static inline int omap1_i2c_add_bus(int bus_id)
>  
>  
>  #ifdef CONFIG_ARCH_OMAP2PLUS
> -/*
> - * XXX This function is a temporary compatibility wrapper - only
> - * needed until the I2C driver can be converted to call
> - * omap_pm_set_max_dev_wakeup_lat() and handle a return code.
> - */
> -static void omap_pm_set_max_mpu_wakeup_lat_compat(struct device *dev, long t)
> -{
> -	omap_pm_set_max_mpu_wakeup_lat(dev, t);
> -}
> -
>  static inline int omap2_i2c_add_bus(int bus_id)
>  {
>  	int l;
> @@ -170,15 +158,6 @@ static inline int omap2_i2c_add_bus(int bus_id)
>  	dev_attr = (struct omap_i2c_dev_attr *)oh->dev_attr;
>  	pdata->flags = dev_attr->flags;
>  
> -	/*
> -	 * When waiting for completion of a i2c transfer, we need to
> -	 * set a wake up latency constraint for the MPU. This is to
> -	 * ensure quick enough wakeup from idle, when transfer
> -	 * completes.
> -	 * Only omap3 has support for constraints
> -	 */
> -	if (cpu_is_omap34xx())
> -		pdata->set_mpu_wkup_lat = omap_pm_set_max_mpu_wakeup_lat_compat;
>  	pdev = omap_device_build(name, bus_id, oh, pdata,
>  			sizeof(struct omap_i2c_bus_platform_data),
>  			NULL, 0, 0);
> diff --git a/drivers/i2c/busses/i2c-omap.c b/drivers/i2c/busses/i2c-omap.c
> index 5d19a49..bd45cee 100644
> --- a/drivers/i2c/busses/i2c-omap.c
> +++ b/drivers/i2c/busses/i2c-omap.c
> @@ -43,6 +43,7 @@
>  #include <linux/slab.h>
>  #include <linux/i2c-omap.h>
>  #include <linux/pm_runtime.h>
> +#include <linux/pm_qos.h>
>  
>  /* I2C controller revisions */
>  #define OMAP_I2C_OMAP1_REV_2		0x20
> @@ -183,8 +184,7 @@ struct omap_i2c_dev {
>  	struct completion	cmd_complete;
>  	struct resource		*ioarea;
>  	u32			latency;	/* maximum mpu wkup latency */
> -	void			(*set_mpu_wkup_lat)(struct device *dev,
> -						    long latency);
> +	struct pm_qos_request	pm_qos_request;
>  	u32			speed;		/* Speed of bus in kHz */
>  	u32			dtrev;		/* extra revision from DT */
>  	u32			flags;
> @@ -590,8 +590,16 @@ omap_i2c_xfer(struct i2c_adapter *adap, struct i2c_msg msgs[], int num)
>  	if (r < 0)
>  		goto out;
>  
> -	if (dev->set_mpu_wkup_lat != NULL)
> -		dev->set_mpu_wkup_lat(dev->dev, dev->latency);
> +	/*
> +	 * When waiting for completion of a i2c transfer, we need to
> +	 * set a wake up latency constraint for the MPU. This is to
> +	 * ensure quick enough wakeup from idle, when transfer
> +	 * completes.
> +	 */
> +	if (dev->latency)
> +		pm_qos_add_request(&dev->pm_qos_request,
> +				   PM_QOS_CPU_DMA_LATENCY,
> +				   dev->latency);
>  
>  	for (i = 0; i < num; i++) {
>  		r = omap_i2c_xfer_msg(adap, &msgs[i], (i == (num - 1)));
> @@ -599,8 +607,8 @@ omap_i2c_xfer(struct i2c_adapter *adap, struct i2c_msg msgs[], int num)
>  			break;
>  	}
>  
> -	if (dev->set_mpu_wkup_lat != NULL)
> -		dev->set_mpu_wkup_lat(dev->dev, -1);
> +	if (dev->latency)
> +		pm_qos_remove_request(&dev->pm_qos_request);
>  
>  	if (r == 0)
>  		r = num;
> @@ -989,7 +997,6 @@ omap_i2c_probe(struct platform_device *pdev)
>  	} else if (pdata != NULL) {
>  		dev->speed = pdata->clkrate;
>  		dev->flags = pdata->flags;
> -		dev->set_mpu_wkup_lat = pdata->set_mpu_wkup_lat;
>  		dev->dtrev = pdata->rev;
>  	}
>  
> @@ -1046,10 +1053,9 @@ omap_i2c_probe(struct platform_device *pdev)
>  		else
>  			dev->b_hw = 1; /* Enable hardware fixes */
>  
> -		/* calculate wakeup latency constraint for MPU */
> -		if (dev->set_mpu_wkup_lat != NULL)
> -			dev->latency = (1000000 * dev->fifo_size) /
> -				       (1000 * dev->speed / 8);
> +		/* calculate wakeup latency constraint */
> +		dev->latency = (1000000 * dev->fifo_size) /
> +			       (1000 * dev->speed / 8);
>  	}
>  
>  	/* reset ASAP, clearing any IRQs */
> diff --git a/include/linux/i2c-omap.h b/include/linux/i2c-omap.h
> index 92a0dc7..df804ba 100644
> --- a/include/linux/i2c-omap.h
> +++ b/include/linux/i2c-omap.h
> @@ -34,7 +34,6 @@ struct omap_i2c_bus_platform_data {
>  	u32		clkrate;
>  	u32		rev;
>  	u32		flags;
> -	void		(*set_mpu_wkup_lat)(struct device *dev, long set);
>  };
>  
>  #endif
> -- 
> 1.7.7.6
> 
> --
> 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

-- 
balbi

[-- Attachment #2: Digital signature --]
[-- Type: application/pgp-signature, Size: 836 bytes --]

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

* [PATCH 7/8] ARM: OMAP: convert I2C driver to PM QoS for latency constraints
@ 2012-09-18  8:53     ` Felipe Balbi
  0 siblings, 0 replies; 28+ messages in thread
From: Felipe Balbi @ 2012-09-18  8:53 UTC (permalink / raw)
  To: linux-arm-kernel

Hi,

On Tue, Sep 18, 2012 at 10:52:11AM +0200, Jean Pihet wrote:
> Convert the driver from the outdated omap_pm_set_max_mpu_wakeup_lat
> API to the new PM QoS API.
> Since the constraint is on the MPU subsystem, use the PM_QOS_CPU_DMA_LATENCY
> class of PM QoS. The resulting MPU constraints are used by cpuidle to
> decide the next power state of the MPU subsystem.
> 
> The I2C device latency timing is derived from the FIFO size and the
> clock speed and so is applicable to all OMAP SoCs.
> 
> Signed-off-by: Jean Pihet <j-pihet@ti.com>

this needs to be rebased on top of Wolfram's i2c-next branch which has a
big series of patches on top of the i2c driver.

Also, please keep Shubhro in Cc for any i2c and spi patches ;-)

> ---
>  arch/arm/plat-omap/i2c.c      |   21 ---------------------
>  drivers/i2c/busses/i2c-omap.c |   28 +++++++++++++++++-----------
>  include/linux/i2c-omap.h      |    1 -
>  3 files changed, 17 insertions(+), 33 deletions(-)
> 
> diff --git a/arch/arm/plat-omap/i2c.c b/arch/arm/plat-omap/i2c.c
> index db071bc..dba8338 100644
> --- a/arch/arm/plat-omap/i2c.c
> +++ b/arch/arm/plat-omap/i2c.c
> @@ -26,7 +26,6 @@
>  #include <linux/kernel.h>
>  #include <linux/platform_device.h>
>  #include <linux/i2c.h>
> -#include <linux/i2c-omap.h>
>  #include <linux/slab.h>
>  #include <linux/err.h>
>  #include <linux/clk.h>
> @@ -34,7 +33,6 @@
>  #include <mach/irqs.h>
>  #include <plat/mux.h>
>  #include <plat/i2c.h>
> -#include <plat/omap-pm.h>
>  #include <plat/omap_device.h>
>  
>  #define OMAP_I2C_SIZE		0x3f
> @@ -129,16 +127,6 @@ static inline int omap1_i2c_add_bus(int bus_id)
>  
>  
>  #ifdef CONFIG_ARCH_OMAP2PLUS
> -/*
> - * XXX This function is a temporary compatibility wrapper - only
> - * needed until the I2C driver can be converted to call
> - * omap_pm_set_max_dev_wakeup_lat() and handle a return code.
> - */
> -static void omap_pm_set_max_mpu_wakeup_lat_compat(struct device *dev, long t)
> -{
> -	omap_pm_set_max_mpu_wakeup_lat(dev, t);
> -}
> -
>  static inline int omap2_i2c_add_bus(int bus_id)
>  {
>  	int l;
> @@ -170,15 +158,6 @@ static inline int omap2_i2c_add_bus(int bus_id)
>  	dev_attr = (struct omap_i2c_dev_attr *)oh->dev_attr;
>  	pdata->flags = dev_attr->flags;
>  
> -	/*
> -	 * When waiting for completion of a i2c transfer, we need to
> -	 * set a wake up latency constraint for the MPU. This is to
> -	 * ensure quick enough wakeup from idle, when transfer
> -	 * completes.
> -	 * Only omap3 has support for constraints
> -	 */
> -	if (cpu_is_omap34xx())
> -		pdata->set_mpu_wkup_lat = omap_pm_set_max_mpu_wakeup_lat_compat;
>  	pdev = omap_device_build(name, bus_id, oh, pdata,
>  			sizeof(struct omap_i2c_bus_platform_data),
>  			NULL, 0, 0);
> diff --git a/drivers/i2c/busses/i2c-omap.c b/drivers/i2c/busses/i2c-omap.c
> index 5d19a49..bd45cee 100644
> --- a/drivers/i2c/busses/i2c-omap.c
> +++ b/drivers/i2c/busses/i2c-omap.c
> @@ -43,6 +43,7 @@
>  #include <linux/slab.h>
>  #include <linux/i2c-omap.h>
>  #include <linux/pm_runtime.h>
> +#include <linux/pm_qos.h>
>  
>  /* I2C controller revisions */
>  #define OMAP_I2C_OMAP1_REV_2		0x20
> @@ -183,8 +184,7 @@ struct omap_i2c_dev {
>  	struct completion	cmd_complete;
>  	struct resource		*ioarea;
>  	u32			latency;	/* maximum mpu wkup latency */
> -	void			(*set_mpu_wkup_lat)(struct device *dev,
> -						    long latency);
> +	struct pm_qos_request	pm_qos_request;
>  	u32			speed;		/* Speed of bus in kHz */
>  	u32			dtrev;		/* extra revision from DT */
>  	u32			flags;
> @@ -590,8 +590,16 @@ omap_i2c_xfer(struct i2c_adapter *adap, struct i2c_msg msgs[], int num)
>  	if (r < 0)
>  		goto out;
>  
> -	if (dev->set_mpu_wkup_lat != NULL)
> -		dev->set_mpu_wkup_lat(dev->dev, dev->latency);
> +	/*
> +	 * When waiting for completion of a i2c transfer, we need to
> +	 * set a wake up latency constraint for the MPU. This is to
> +	 * ensure quick enough wakeup from idle, when transfer
> +	 * completes.
> +	 */
> +	if (dev->latency)
> +		pm_qos_add_request(&dev->pm_qos_request,
> +				   PM_QOS_CPU_DMA_LATENCY,
> +				   dev->latency);
>  
>  	for (i = 0; i < num; i++) {
>  		r = omap_i2c_xfer_msg(adap, &msgs[i], (i == (num - 1)));
> @@ -599,8 +607,8 @@ omap_i2c_xfer(struct i2c_adapter *adap, struct i2c_msg msgs[], int num)
>  			break;
>  	}
>  
> -	if (dev->set_mpu_wkup_lat != NULL)
> -		dev->set_mpu_wkup_lat(dev->dev, -1);
> +	if (dev->latency)
> +		pm_qos_remove_request(&dev->pm_qos_request);
>  
>  	if (r == 0)
>  		r = num;
> @@ -989,7 +997,6 @@ omap_i2c_probe(struct platform_device *pdev)
>  	} else if (pdata != NULL) {
>  		dev->speed = pdata->clkrate;
>  		dev->flags = pdata->flags;
> -		dev->set_mpu_wkup_lat = pdata->set_mpu_wkup_lat;
>  		dev->dtrev = pdata->rev;
>  	}
>  
> @@ -1046,10 +1053,9 @@ omap_i2c_probe(struct platform_device *pdev)
>  		else
>  			dev->b_hw = 1; /* Enable hardware fixes */
>  
> -		/* calculate wakeup latency constraint for MPU */
> -		if (dev->set_mpu_wkup_lat != NULL)
> -			dev->latency = (1000000 * dev->fifo_size) /
> -				       (1000 * dev->speed / 8);
> +		/* calculate wakeup latency constraint */
> +		dev->latency = (1000000 * dev->fifo_size) /
> +			       (1000 * dev->speed / 8);
>  	}
>  
>  	/* reset ASAP, clearing any IRQs */
> diff --git a/include/linux/i2c-omap.h b/include/linux/i2c-omap.h
> index 92a0dc7..df804ba 100644
> --- a/include/linux/i2c-omap.h
> +++ b/include/linux/i2c-omap.h
> @@ -34,7 +34,6 @@ struct omap_i2c_bus_platform_data {
>  	u32		clkrate;
>  	u32		rev;
>  	u32		flags;
> -	void		(*set_mpu_wkup_lat)(struct device *dev, long set);
>  };
>  
>  #endif
> -- 
> 1.7.7.6
> 
> --
> To unsubscribe from this list: send the line "unsubscribe linux-omap" in
> the body of a message to majordomo at vger.kernel.org
> More majordomo info at  http://vger.kernel.org/majordomo-info.html

-- 
balbi
-------------- next part --------------
A non-text attachment was scrubbed...
Name: signature.asc
Type: application/pgp-signature
Size: 836 bytes
Desc: Digital signature
URL: <http://lists.infradead.org/pipermail/linux-arm-kernel/attachments/20120918/e61da966/attachment.sig>

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

* Re: [PATCH 7/8] ARM: OMAP: convert I2C driver to PM QoS for latency constraints
  2012-09-18  8:52   ` Jean Pihet
@ 2012-09-19 18:11     ` Shubhrajyoti
  -1 siblings, 0 replies; 28+ messages in thread
From: Shubhrajyoti @ 2012-09-19 18:11 UTC (permalink / raw)
  To: Jean Pihet; +Cc: linux-omap, paul, linux-arm-kernel, khilman, Jean Pihet

On Tuesday 18 September 2012 02:22 PM, Jean Pihet wrote:
> Convert the driver from the outdated omap_pm_set_max_mpu_wakeup_lat
> API to the new PM QoS API.
> Since the constraint is on the MPU subsystem, use the PM_QOS_CPU_DMA_LATENCY
> class of PM QoS. The resulting MPU constraints are used by cpuidle to
> decide the next power state of the MPU subsystem.
>
> The I2C device latency timing is derived from the FIFO size and the
> clock speed and so is applicable to all OMAP SoCs.
agree
thanks,
>
> Signed-off-by: Jean Pihet <j-pihet@ti.com>
> ---
>  arch/arm/plat-omap/i2c.c      |   21 ---------------------
>  drivers/i2c/busses/i2c-omap.c |   28 +++++++++++++++++-----------
>  include/linux/i2c-omap.h      |    1 -
>  3 files changed, 17 insertions(+), 33 deletions(-)
>
> diff --git a/arch/arm/plat-omap/i2c.c b/arch/arm/plat-omap/i2c.c
> index db071bc..dba8338 100644
> --- a/arch/arm/plat-omap/i2c.c
> +++ b/arch/arm/plat-omap/i2c.c
> @@ -26,7 +26,6 @@
>  #include <linux/kernel.h>
>  #include <linux/platform_device.h>
>  #include <linux/i2c.h>
> -#include <linux/i2c-omap.h>
>  #include <linux/slab.h>
>  #include <linux/err.h>
>  #include <linux/clk.h>
> @@ -34,7 +33,6 @@
>  #include <mach/irqs.h>
>  #include <plat/mux.h>
>  #include <plat/i2c.h>
> -#include <plat/omap-pm.h>
>  #include <plat/omap_device.h>
>  
>  #define OMAP_I2C_SIZE		0x3f
> @@ -129,16 +127,6 @@ static inline int omap1_i2c_add_bus(int bus_id)
>  
>  
>  #ifdef CONFIG_ARCH_OMAP2PLUS
> -/*
> - * XXX This function is a temporary compatibility wrapper - only
> - * needed until the I2C driver can be converted to call
> - * omap_pm_set_max_dev_wakeup_lat() and handle a return code.
> - */
> -static void omap_pm_set_max_mpu_wakeup_lat_compat(struct device *dev, long t)
> -{
> -	omap_pm_set_max_mpu_wakeup_lat(dev, t);
> -}
> -
>  static inline int omap2_i2c_add_bus(int bus_id)
>  {
>  	int l;
> @@ -170,15 +158,6 @@ static inline int omap2_i2c_add_bus(int bus_id)
>  	dev_attr = (struct omap_i2c_dev_attr *)oh->dev_attr;
>  	pdata->flags = dev_attr->flags;
>  
> -	/*
> -	 * When waiting for completion of a i2c transfer, we need to
> -	 * set a wake up latency constraint for the MPU. This is to
> -	 * ensure quick enough wakeup from idle, when transfer
> -	 * completes.
> -	 * Only omap3 has support for constraints
> -	 */
> -	if (cpu_is_omap34xx())
> -		pdata->set_mpu_wkup_lat = omap_pm_set_max_mpu_wakeup_lat_compat;
>  	pdev = omap_device_build(name, bus_id, oh, pdata,
>  			sizeof(struct omap_i2c_bus_platform_data),
>  			NULL, 0, 0);
> diff --git a/drivers/i2c/busses/i2c-omap.c b/drivers/i2c/busses/i2c-omap.c
> index 5d19a49..bd45cee 100644
> --- a/drivers/i2c/busses/i2c-omap.c
> +++ b/drivers/i2c/busses/i2c-omap.c
> @@ -43,6 +43,7 @@
>  #include <linux/slab.h>
>  #include <linux/i2c-omap.h>
>  #include <linux/pm_runtime.h>
> +#include <linux/pm_qos.h>
>  
>  /* I2C controller revisions */
>  #define OMAP_I2C_OMAP1_REV_2		0x20
> @@ -183,8 +184,7 @@ struct omap_i2c_dev {
>  	struct completion	cmd_complete;
>  	struct resource		*ioarea;
>  	u32			latency;	/* maximum mpu wkup latency */
> -	void			(*set_mpu_wkup_lat)(struct device *dev,
> -						    long latency);
> +	struct pm_qos_request	pm_qos_request;
>  	u32			speed;		/* Speed of bus in kHz */
>  	u32			dtrev;		/* extra revision from DT */
>  	u32			flags;
> @@ -590,8 +590,16 @@ omap_i2c_xfer(struct i2c_adapter *adap, struct i2c_msg msgs[], int num)
>  	if (r < 0)
>  		goto out;
>  
> -	if (dev->set_mpu_wkup_lat != NULL)
> -		dev->set_mpu_wkup_lat(dev->dev, dev->latency);
> +	/*
> +	 * When waiting for completion of a i2c transfer, we need to
> +	 * set a wake up latency constraint for the MPU. This is to
> +	 * ensure quick enough wakeup from idle, when transfer
> +	 * completes.
> +	 */
> +	if (dev->latency)
> +		pm_qos_add_request(&dev->pm_qos_request,
> +				   PM_QOS_CPU_DMA_LATENCY,
> +				   dev->latency);
>  
>  	for (i = 0; i < num; i++) {
>  		r = omap_i2c_xfer_msg(adap, &msgs[i], (i == (num - 1)));
> @@ -599,8 +607,8 @@ omap_i2c_xfer(struct i2c_adapter *adap, struct i2c_msg msgs[], int num)
>  			break;
>  	}
>  
> -	if (dev->set_mpu_wkup_lat != NULL)
> -		dev->set_mpu_wkup_lat(dev->dev, -1);
> +	if (dev->latency)
> +		pm_qos_remove_request(&dev->pm_qos_request);
>  
>  	if (r == 0)
>  		r = num;
> @@ -989,7 +997,6 @@ omap_i2c_probe(struct platform_device *pdev)
>  	} else if (pdata != NULL) {
>  		dev->speed = pdata->clkrate;
>  		dev->flags = pdata->flags;
> -		dev->set_mpu_wkup_lat = pdata->set_mpu_wkup_lat;
>  		dev->dtrev = pdata->rev;
>  	}
>  
> @@ -1046,10 +1053,9 @@ omap_i2c_probe(struct platform_device *pdev)
>  		else
>  			dev->b_hw = 1; /* Enable hardware fixes */
>  
> -		/* calculate wakeup latency constraint for MPU */
> -		if (dev->set_mpu_wkup_lat != NULL)
> -			dev->latency = (1000000 * dev->fifo_size) /
> -				       (1000 * dev->speed / 8);
> +		/* calculate wakeup latency constraint */
> +		dev->latency = (1000000 * dev->fifo_size) /
> +			       (1000 * dev->speed / 8);
>  	}
>  
>  	/* reset ASAP, clearing any IRQs */
> diff --git a/include/linux/i2c-omap.h b/include/linux/i2c-omap.h
> index 92a0dc7..df804ba 100644
> --- a/include/linux/i2c-omap.h
> +++ b/include/linux/i2c-omap.h
> @@ -34,7 +34,6 @@ struct omap_i2c_bus_platform_data {
>  	u32		clkrate;
>  	u32		rev;
>  	u32		flags;
> -	void		(*set_mpu_wkup_lat)(struct device *dev, long set);
>  };
>  
>  #endif


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

* [PATCH 7/8] ARM: OMAP: convert I2C driver to PM QoS for latency constraints
@ 2012-09-19 18:11     ` Shubhrajyoti
  0 siblings, 0 replies; 28+ messages in thread
From: Shubhrajyoti @ 2012-09-19 18:11 UTC (permalink / raw)
  To: linux-arm-kernel

On Tuesday 18 September 2012 02:22 PM, Jean Pihet wrote:
> Convert the driver from the outdated omap_pm_set_max_mpu_wakeup_lat
> API to the new PM QoS API.
> Since the constraint is on the MPU subsystem, use the PM_QOS_CPU_DMA_LATENCY
> class of PM QoS. The resulting MPU constraints are used by cpuidle to
> decide the next power state of the MPU subsystem.
>
> The I2C device latency timing is derived from the FIFO size and the
> clock speed and so is applicable to all OMAP SoCs.
agree
thanks,
>
> Signed-off-by: Jean Pihet <j-pihet@ti.com>
> ---
>  arch/arm/plat-omap/i2c.c      |   21 ---------------------
>  drivers/i2c/busses/i2c-omap.c |   28 +++++++++++++++++-----------
>  include/linux/i2c-omap.h      |    1 -
>  3 files changed, 17 insertions(+), 33 deletions(-)
>
> diff --git a/arch/arm/plat-omap/i2c.c b/arch/arm/plat-omap/i2c.c
> index db071bc..dba8338 100644
> --- a/arch/arm/plat-omap/i2c.c
> +++ b/arch/arm/plat-omap/i2c.c
> @@ -26,7 +26,6 @@
>  #include <linux/kernel.h>
>  #include <linux/platform_device.h>
>  #include <linux/i2c.h>
> -#include <linux/i2c-omap.h>
>  #include <linux/slab.h>
>  #include <linux/err.h>
>  #include <linux/clk.h>
> @@ -34,7 +33,6 @@
>  #include <mach/irqs.h>
>  #include <plat/mux.h>
>  #include <plat/i2c.h>
> -#include <plat/omap-pm.h>
>  #include <plat/omap_device.h>
>  
>  #define OMAP_I2C_SIZE		0x3f
> @@ -129,16 +127,6 @@ static inline int omap1_i2c_add_bus(int bus_id)
>  
>  
>  #ifdef CONFIG_ARCH_OMAP2PLUS
> -/*
> - * XXX This function is a temporary compatibility wrapper - only
> - * needed until the I2C driver can be converted to call
> - * omap_pm_set_max_dev_wakeup_lat() and handle a return code.
> - */
> -static void omap_pm_set_max_mpu_wakeup_lat_compat(struct device *dev, long t)
> -{
> -	omap_pm_set_max_mpu_wakeup_lat(dev, t);
> -}
> -
>  static inline int omap2_i2c_add_bus(int bus_id)
>  {
>  	int l;
> @@ -170,15 +158,6 @@ static inline int omap2_i2c_add_bus(int bus_id)
>  	dev_attr = (struct omap_i2c_dev_attr *)oh->dev_attr;
>  	pdata->flags = dev_attr->flags;
>  
> -	/*
> -	 * When waiting for completion of a i2c transfer, we need to
> -	 * set a wake up latency constraint for the MPU. This is to
> -	 * ensure quick enough wakeup from idle, when transfer
> -	 * completes.
> -	 * Only omap3 has support for constraints
> -	 */
> -	if (cpu_is_omap34xx())
> -		pdata->set_mpu_wkup_lat = omap_pm_set_max_mpu_wakeup_lat_compat;
>  	pdev = omap_device_build(name, bus_id, oh, pdata,
>  			sizeof(struct omap_i2c_bus_platform_data),
>  			NULL, 0, 0);
> diff --git a/drivers/i2c/busses/i2c-omap.c b/drivers/i2c/busses/i2c-omap.c
> index 5d19a49..bd45cee 100644
> --- a/drivers/i2c/busses/i2c-omap.c
> +++ b/drivers/i2c/busses/i2c-omap.c
> @@ -43,6 +43,7 @@
>  #include <linux/slab.h>
>  #include <linux/i2c-omap.h>
>  #include <linux/pm_runtime.h>
> +#include <linux/pm_qos.h>
>  
>  /* I2C controller revisions */
>  #define OMAP_I2C_OMAP1_REV_2		0x20
> @@ -183,8 +184,7 @@ struct omap_i2c_dev {
>  	struct completion	cmd_complete;
>  	struct resource		*ioarea;
>  	u32			latency;	/* maximum mpu wkup latency */
> -	void			(*set_mpu_wkup_lat)(struct device *dev,
> -						    long latency);
> +	struct pm_qos_request	pm_qos_request;
>  	u32			speed;		/* Speed of bus in kHz */
>  	u32			dtrev;		/* extra revision from DT */
>  	u32			flags;
> @@ -590,8 +590,16 @@ omap_i2c_xfer(struct i2c_adapter *adap, struct i2c_msg msgs[], int num)
>  	if (r < 0)
>  		goto out;
>  
> -	if (dev->set_mpu_wkup_lat != NULL)
> -		dev->set_mpu_wkup_lat(dev->dev, dev->latency);
> +	/*
> +	 * When waiting for completion of a i2c transfer, we need to
> +	 * set a wake up latency constraint for the MPU. This is to
> +	 * ensure quick enough wakeup from idle, when transfer
> +	 * completes.
> +	 */
> +	if (dev->latency)
> +		pm_qos_add_request(&dev->pm_qos_request,
> +				   PM_QOS_CPU_DMA_LATENCY,
> +				   dev->latency);
>  
>  	for (i = 0; i < num; i++) {
>  		r = omap_i2c_xfer_msg(adap, &msgs[i], (i == (num - 1)));
> @@ -599,8 +607,8 @@ omap_i2c_xfer(struct i2c_adapter *adap, struct i2c_msg msgs[], int num)
>  			break;
>  	}
>  
> -	if (dev->set_mpu_wkup_lat != NULL)
> -		dev->set_mpu_wkup_lat(dev->dev, -1);
> +	if (dev->latency)
> +		pm_qos_remove_request(&dev->pm_qos_request);
>  
>  	if (r == 0)
>  		r = num;
> @@ -989,7 +997,6 @@ omap_i2c_probe(struct platform_device *pdev)
>  	} else if (pdata != NULL) {
>  		dev->speed = pdata->clkrate;
>  		dev->flags = pdata->flags;
> -		dev->set_mpu_wkup_lat = pdata->set_mpu_wkup_lat;
>  		dev->dtrev = pdata->rev;
>  	}
>  
> @@ -1046,10 +1053,9 @@ omap_i2c_probe(struct platform_device *pdev)
>  		else
>  			dev->b_hw = 1; /* Enable hardware fixes */
>  
> -		/* calculate wakeup latency constraint for MPU */
> -		if (dev->set_mpu_wkup_lat != NULL)
> -			dev->latency = (1000000 * dev->fifo_size) /
> -				       (1000 * dev->speed / 8);
> +		/* calculate wakeup latency constraint */
> +		dev->latency = (1000000 * dev->fifo_size) /
> +			       (1000 * dev->speed / 8);
>  	}
>  
>  	/* reset ASAP, clearing any IRQs */
> diff --git a/include/linux/i2c-omap.h b/include/linux/i2c-omap.h
> index 92a0dc7..df804ba 100644
> --- a/include/linux/i2c-omap.h
> +++ b/include/linux/i2c-omap.h
> @@ -34,7 +34,6 @@ struct omap_i2c_bus_platform_data {
>  	u32		clkrate;
>  	u32		rev;
>  	u32		flags;
> -	void		(*set_mpu_wkup_lat)(struct device *dev, long set);
>  };
>  
>  #endif

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

* Re: [PATCH 7/8] ARM: OMAP: convert I2C driver to PM QoS for latency constraints
  2012-09-19 18:11     ` Shubhrajyoti
@ 2012-09-19 18:34       ` Jean Pihet
  -1 siblings, 0 replies; 28+ messages in thread
From: Jean Pihet @ 2012-09-19 18:34 UTC (permalink / raw)
  To: Shubhrajyoti, balbi
  Cc: linux-omap, paul, linux-arm-kernel, khilman, Jean Pihet

Hi!

On Wed, Sep 19, 2012 at 8:11 PM, Shubhrajyoti <shubhrajyoti@ti.com> wrote:
> On Tuesday 18 September 2012 02:22 PM, Jean Pihet wrote:
>> Convert the driver from the outdated omap_pm_set_max_mpu_wakeup_lat
>> API to the new PM QoS API.
>> Since the constraint is on the MPU subsystem, use the PM_QOS_CPU_DMA_LATENCY
>> class of PM QoS. The resulting MPU constraints are used by cpuidle to
>> decide the next power state of the MPU subsystem.
>>
>> The I2C device latency timing is derived from the FIFO size and the
>> clock speed and so is applicable to all OMAP SoCs.
> agree
> thanks,

The I2C patch will be rebased and submitted separately.

Thanks!
Jean

>>
>> Signed-off-by: Jean Pihet <j-pihet@ti.com>
>> ---
>>  arch/arm/plat-omap/i2c.c      |   21 ---------------------
>>  drivers/i2c/busses/i2c-omap.c |   28 +++++++++++++++++-----------
>>  include/linux/i2c-omap.h      |    1 -
>>  3 files changed, 17 insertions(+), 33 deletions(-)
>>
>> diff --git a/arch/arm/plat-omap/i2c.c b/arch/arm/plat-omap/i2c.c
>> index db071bc..dba8338 100644
>> --- a/arch/arm/plat-omap/i2c.c
>> +++ b/arch/arm/plat-omap/i2c.c
>> @@ -26,7 +26,6 @@
>>  #include <linux/kernel.h>
>>  #include <linux/platform_device.h>
>>  #include <linux/i2c.h>
>> -#include <linux/i2c-omap.h>
>>  #include <linux/slab.h>
>>  #include <linux/err.h>
>>  #include <linux/clk.h>
>> @@ -34,7 +33,6 @@
>>  #include <mach/irqs.h>
>>  #include <plat/mux.h>
>>  #include <plat/i2c.h>
>> -#include <plat/omap-pm.h>
>>  #include <plat/omap_device.h>
>>
>>  #define OMAP_I2C_SIZE                0x3f
>> @@ -129,16 +127,6 @@ static inline int omap1_i2c_add_bus(int bus_id)
>>
>>
>>  #ifdef CONFIG_ARCH_OMAP2PLUS
>> -/*
>> - * XXX This function is a temporary compatibility wrapper - only
>> - * needed until the I2C driver can be converted to call
>> - * omap_pm_set_max_dev_wakeup_lat() and handle a return code.
>> - */
>> -static void omap_pm_set_max_mpu_wakeup_lat_compat(struct device *dev, long t)
>> -{
>> -     omap_pm_set_max_mpu_wakeup_lat(dev, t);
>> -}
>> -
>>  static inline int omap2_i2c_add_bus(int bus_id)
>>  {
>>       int l;
>> @@ -170,15 +158,6 @@ static inline int omap2_i2c_add_bus(int bus_id)
>>       dev_attr = (struct omap_i2c_dev_attr *)oh->dev_attr;
>>       pdata->flags = dev_attr->flags;
>>
>> -     /*
>> -      * When waiting for completion of a i2c transfer, we need to
>> -      * set a wake up latency constraint for the MPU. This is to
>> -      * ensure quick enough wakeup from idle, when transfer
>> -      * completes.
>> -      * Only omap3 has support for constraints
>> -      */
>> -     if (cpu_is_omap34xx())
>> -             pdata->set_mpu_wkup_lat = omap_pm_set_max_mpu_wakeup_lat_compat;
>>       pdev = omap_device_build(name, bus_id, oh, pdata,
>>                       sizeof(struct omap_i2c_bus_platform_data),
>>                       NULL, 0, 0);
>> diff --git a/drivers/i2c/busses/i2c-omap.c b/drivers/i2c/busses/i2c-omap.c
>> index 5d19a49..bd45cee 100644
>> --- a/drivers/i2c/busses/i2c-omap.c
>> +++ b/drivers/i2c/busses/i2c-omap.c
>> @@ -43,6 +43,7 @@
>>  #include <linux/slab.h>
>>  #include <linux/i2c-omap.h>
>>  #include <linux/pm_runtime.h>
>> +#include <linux/pm_qos.h>
>>
>>  /* I2C controller revisions */
>>  #define OMAP_I2C_OMAP1_REV_2         0x20
>> @@ -183,8 +184,7 @@ struct omap_i2c_dev {
>>       struct completion       cmd_complete;
>>       struct resource         *ioarea;
>>       u32                     latency;        /* maximum mpu wkup latency */
>> -     void                    (*set_mpu_wkup_lat)(struct device *dev,
>> -                                                 long latency);
>> +     struct pm_qos_request   pm_qos_request;
>>       u32                     speed;          /* Speed of bus in kHz */
>>       u32                     dtrev;          /* extra revision from DT */
>>       u32                     flags;
>> @@ -590,8 +590,16 @@ omap_i2c_xfer(struct i2c_adapter *adap, struct i2c_msg msgs[], int num)
>>       if (r < 0)
>>               goto out;
>>
>> -     if (dev->set_mpu_wkup_lat != NULL)
>> -             dev->set_mpu_wkup_lat(dev->dev, dev->latency);
>> +     /*
>> +      * When waiting for completion of a i2c transfer, we need to
>> +      * set a wake up latency constraint for the MPU. This is to
>> +      * ensure quick enough wakeup from idle, when transfer
>> +      * completes.
>> +      */
>> +     if (dev->latency)
>> +             pm_qos_add_request(&dev->pm_qos_request,
>> +                                PM_QOS_CPU_DMA_LATENCY,
>> +                                dev->latency);
>>
>>       for (i = 0; i < num; i++) {
>>               r = omap_i2c_xfer_msg(adap, &msgs[i], (i == (num - 1)));
>> @@ -599,8 +607,8 @@ omap_i2c_xfer(struct i2c_adapter *adap, struct i2c_msg msgs[], int num)
>>                       break;
>>       }
>>
>> -     if (dev->set_mpu_wkup_lat != NULL)
>> -             dev->set_mpu_wkup_lat(dev->dev, -1);
>> +     if (dev->latency)
>> +             pm_qos_remove_request(&dev->pm_qos_request);
>>
>>       if (r == 0)
>>               r = num;
>> @@ -989,7 +997,6 @@ omap_i2c_probe(struct platform_device *pdev)
>>       } else if (pdata != NULL) {
>>               dev->speed = pdata->clkrate;
>>               dev->flags = pdata->flags;
>> -             dev->set_mpu_wkup_lat = pdata->set_mpu_wkup_lat;
>>               dev->dtrev = pdata->rev;
>>       }
>>
>> @@ -1046,10 +1053,9 @@ omap_i2c_probe(struct platform_device *pdev)
>>               else
>>                       dev->b_hw = 1; /* Enable hardware fixes */
>>
>> -             /* calculate wakeup latency constraint for MPU */
>> -             if (dev->set_mpu_wkup_lat != NULL)
>> -                     dev->latency = (1000000 * dev->fifo_size) /
>> -                                    (1000 * dev->speed / 8);
>> +             /* calculate wakeup latency constraint */
>> +             dev->latency = (1000000 * dev->fifo_size) /
>> +                            (1000 * dev->speed / 8);
>>       }
>>
>>       /* reset ASAP, clearing any IRQs */
>> diff --git a/include/linux/i2c-omap.h b/include/linux/i2c-omap.h
>> index 92a0dc7..df804ba 100644
>> --- a/include/linux/i2c-omap.h
>> +++ b/include/linux/i2c-omap.h
>> @@ -34,7 +34,6 @@ struct omap_i2c_bus_platform_data {
>>       u32             clkrate;
>>       u32             rev;
>>       u32             flags;
>> -     void            (*set_mpu_wkup_lat)(struct device *dev, long set);
>>  };
>>
>>  #endif
>

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

* [PATCH 7/8] ARM: OMAP: convert I2C driver to PM QoS for latency constraints
@ 2012-09-19 18:34       ` Jean Pihet
  0 siblings, 0 replies; 28+ messages in thread
From: Jean Pihet @ 2012-09-19 18:34 UTC (permalink / raw)
  To: linux-arm-kernel

Hi!

On Wed, Sep 19, 2012 at 8:11 PM, Shubhrajyoti <shubhrajyoti@ti.com> wrote:
> On Tuesday 18 September 2012 02:22 PM, Jean Pihet wrote:
>> Convert the driver from the outdated omap_pm_set_max_mpu_wakeup_lat
>> API to the new PM QoS API.
>> Since the constraint is on the MPU subsystem, use the PM_QOS_CPU_DMA_LATENCY
>> class of PM QoS. The resulting MPU constraints are used by cpuidle to
>> decide the next power state of the MPU subsystem.
>>
>> The I2C device latency timing is derived from the FIFO size and the
>> clock speed and so is applicable to all OMAP SoCs.
> agree
> thanks,

The I2C patch will be rebased and submitted separately.

Thanks!
Jean

>>
>> Signed-off-by: Jean Pihet <j-pihet@ti.com>
>> ---
>>  arch/arm/plat-omap/i2c.c      |   21 ---------------------
>>  drivers/i2c/busses/i2c-omap.c |   28 +++++++++++++++++-----------
>>  include/linux/i2c-omap.h      |    1 -
>>  3 files changed, 17 insertions(+), 33 deletions(-)
>>
>> diff --git a/arch/arm/plat-omap/i2c.c b/arch/arm/plat-omap/i2c.c
>> index db071bc..dba8338 100644
>> --- a/arch/arm/plat-omap/i2c.c
>> +++ b/arch/arm/plat-omap/i2c.c
>> @@ -26,7 +26,6 @@
>>  #include <linux/kernel.h>
>>  #include <linux/platform_device.h>
>>  #include <linux/i2c.h>
>> -#include <linux/i2c-omap.h>
>>  #include <linux/slab.h>
>>  #include <linux/err.h>
>>  #include <linux/clk.h>
>> @@ -34,7 +33,6 @@
>>  #include <mach/irqs.h>
>>  #include <plat/mux.h>
>>  #include <plat/i2c.h>
>> -#include <plat/omap-pm.h>
>>  #include <plat/omap_device.h>
>>
>>  #define OMAP_I2C_SIZE                0x3f
>> @@ -129,16 +127,6 @@ static inline int omap1_i2c_add_bus(int bus_id)
>>
>>
>>  #ifdef CONFIG_ARCH_OMAP2PLUS
>> -/*
>> - * XXX This function is a temporary compatibility wrapper - only
>> - * needed until the I2C driver can be converted to call
>> - * omap_pm_set_max_dev_wakeup_lat() and handle a return code.
>> - */
>> -static void omap_pm_set_max_mpu_wakeup_lat_compat(struct device *dev, long t)
>> -{
>> -     omap_pm_set_max_mpu_wakeup_lat(dev, t);
>> -}
>> -
>>  static inline int omap2_i2c_add_bus(int bus_id)
>>  {
>>       int l;
>> @@ -170,15 +158,6 @@ static inline int omap2_i2c_add_bus(int bus_id)
>>       dev_attr = (struct omap_i2c_dev_attr *)oh->dev_attr;
>>       pdata->flags = dev_attr->flags;
>>
>> -     /*
>> -      * When waiting for completion of a i2c transfer, we need to
>> -      * set a wake up latency constraint for the MPU. This is to
>> -      * ensure quick enough wakeup from idle, when transfer
>> -      * completes.
>> -      * Only omap3 has support for constraints
>> -      */
>> -     if (cpu_is_omap34xx())
>> -             pdata->set_mpu_wkup_lat = omap_pm_set_max_mpu_wakeup_lat_compat;
>>       pdev = omap_device_build(name, bus_id, oh, pdata,
>>                       sizeof(struct omap_i2c_bus_platform_data),
>>                       NULL, 0, 0);
>> diff --git a/drivers/i2c/busses/i2c-omap.c b/drivers/i2c/busses/i2c-omap.c
>> index 5d19a49..bd45cee 100644
>> --- a/drivers/i2c/busses/i2c-omap.c
>> +++ b/drivers/i2c/busses/i2c-omap.c
>> @@ -43,6 +43,7 @@
>>  #include <linux/slab.h>
>>  #include <linux/i2c-omap.h>
>>  #include <linux/pm_runtime.h>
>> +#include <linux/pm_qos.h>
>>
>>  /* I2C controller revisions */
>>  #define OMAP_I2C_OMAP1_REV_2         0x20
>> @@ -183,8 +184,7 @@ struct omap_i2c_dev {
>>       struct completion       cmd_complete;
>>       struct resource         *ioarea;
>>       u32                     latency;        /* maximum mpu wkup latency */
>> -     void                    (*set_mpu_wkup_lat)(struct device *dev,
>> -                                                 long latency);
>> +     struct pm_qos_request   pm_qos_request;
>>       u32                     speed;          /* Speed of bus in kHz */
>>       u32                     dtrev;          /* extra revision from DT */
>>       u32                     flags;
>> @@ -590,8 +590,16 @@ omap_i2c_xfer(struct i2c_adapter *adap, struct i2c_msg msgs[], int num)
>>       if (r < 0)
>>               goto out;
>>
>> -     if (dev->set_mpu_wkup_lat != NULL)
>> -             dev->set_mpu_wkup_lat(dev->dev, dev->latency);
>> +     /*
>> +      * When waiting for completion of a i2c transfer, we need to
>> +      * set a wake up latency constraint for the MPU. This is to
>> +      * ensure quick enough wakeup from idle, when transfer
>> +      * completes.
>> +      */
>> +     if (dev->latency)
>> +             pm_qos_add_request(&dev->pm_qos_request,
>> +                                PM_QOS_CPU_DMA_LATENCY,
>> +                                dev->latency);
>>
>>       for (i = 0; i < num; i++) {
>>               r = omap_i2c_xfer_msg(adap, &msgs[i], (i == (num - 1)));
>> @@ -599,8 +607,8 @@ omap_i2c_xfer(struct i2c_adapter *adap, struct i2c_msg msgs[], int num)
>>                       break;
>>       }
>>
>> -     if (dev->set_mpu_wkup_lat != NULL)
>> -             dev->set_mpu_wkup_lat(dev->dev, -1);
>> +     if (dev->latency)
>> +             pm_qos_remove_request(&dev->pm_qos_request);
>>
>>       if (r == 0)
>>               r = num;
>> @@ -989,7 +997,6 @@ omap_i2c_probe(struct platform_device *pdev)
>>       } else if (pdata != NULL) {
>>               dev->speed = pdata->clkrate;
>>               dev->flags = pdata->flags;
>> -             dev->set_mpu_wkup_lat = pdata->set_mpu_wkup_lat;
>>               dev->dtrev = pdata->rev;
>>       }
>>
>> @@ -1046,10 +1053,9 @@ omap_i2c_probe(struct platform_device *pdev)
>>               else
>>                       dev->b_hw = 1; /* Enable hardware fixes */
>>
>> -             /* calculate wakeup latency constraint for MPU */
>> -             if (dev->set_mpu_wkup_lat != NULL)
>> -                     dev->latency = (1000000 * dev->fifo_size) /
>> -                                    (1000 * dev->speed / 8);
>> +             /* calculate wakeup latency constraint */
>> +             dev->latency = (1000000 * dev->fifo_size) /
>> +                            (1000 * dev->speed / 8);
>>       }
>>
>>       /* reset ASAP, clearing any IRQs */
>> diff --git a/include/linux/i2c-omap.h b/include/linux/i2c-omap.h
>> index 92a0dc7..df804ba 100644
>> --- a/include/linux/i2c-omap.h
>> +++ b/include/linux/i2c-omap.h
>> @@ -34,7 +34,6 @@ struct omap_i2c_bus_platform_data {
>>       u32             clkrate;
>>       u32             rev;
>>       u32             flags;
>> -     void            (*set_mpu_wkup_lat)(struct device *dev, long set);
>>  };
>>
>>  #endif
>

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

* Re: [PATCH 1/8] ARM: OMAP2+: PM QoS: control the power domains next state from the constraints
  2012-09-18  8:52   ` Jean Pihet
@ 2012-12-11  0:05     ` Paul Walmsley
  -1 siblings, 0 replies; 28+ messages in thread
From: Paul Walmsley @ 2012-12-11  0:05 UTC (permalink / raw)
  To: Jean Pihet
  Cc: linux-omap, linux-arm-kernel, khilman, Jean Pihet, Djamil Elaidi

Hi

On Tue, 18 Sep 2012, Jean Pihet wrote:

> When a PM QoS device latency constraint is requested or removed the
> constraint is stored in the constraints list of the corresponding power
> domain, then the aggregated constraint value is applied by programming
> the next functional power state using pwrdm_set_fpwrst.
> 
> The per-device PM QoS locking requires a spinlock to be used. The reasons
> is that some drivers need to use the per-device PM QoS functionality from
> interrupt context or spinlock protected context, as reported by Djamil.
> An example of such a driver is the OMAP HSI (high-speed synchronous serial
> interface) driver which needs to control the IP block idle state from
> the FIFO empty state, from interrupt context.
> 
> Tested on OMAP3 Beagleboard and OMAP4 Pandaboard in RET/OFF using
> wake-up latency constraints on MPU, CORE and PER.
> 
> Signed-off-by: Jean Pihet <j-pihet@ti.com>
> Cc: Djamil Elaidi <d-elaidi@ti.com>

This patch has been changed here.  Details below, but the major changes 
were to fix the locking and to not attempt GFP_ATOMIC allocations.
Hopefully the reasons why are clear, but if not, feel free to ask.

The following patch has only been compile-tested, and a few more minor 
changes will probably be needed.


- Paul

From: Jean Pihet <jean.pihet@newoldbits.com>
Date: Sun, 9 Dec 2012 18:38:12 -0700
Subject: [PATCH] ARM: OMAP2+: PM QoS: control the power domains next state
 from the constraints

When a PM QoS device latency constraint is requested or removed the
constraint is stored in the constraints list of the corresponding power
domain, then the aggregated constraint value is applied by programming
the next functional power state using pwrdm_set_fpwrst.

The per-device PM QoS locking requires a spinlock to be used. The reasons
is that some drivers need to use the per-device PM QoS functionality from
interrupt context or spinlock protected context, as reported by Djamil.
An example of such a driver is the OMAP HSI (high-speed synchronous serial
interface) driver which needs to control the IP block idle state from
the FIFO empty state, from interrupt context.

Tested on OMAP3 Beagleboard and OMAP4 Pandaboard in RET/OFF using
wake-up latency constraints on MPU, CORE and PER.

Signed-off-by: Jean Pihet <j-pihet@ti.com>
Cc: Djamil Elaidi <d-elaidi@ti.com>
[paul@pwsan.com: updated to apply; renamed *state to *fpwrst and
 define as u8; use existing powerdomain spinlock; pack the struct powerdomain
 variables; removed the GFP_ATOMIC allocation; drop expensive operations
 from the _pwrdm_wakeuplat_update_pwrst() debug; ensure that the entire
 wakeuplat update process takes place under the spinlock; remove
 UNSUP_STATE; remove assumption that fpwrsts start at 0; remove
 unnecessary plist_empty test from update path; standardize 'wakeuplat'
 naming; add kerneldoc]

XXX conform debug to other powerdomain debugging
---
 arch/arm/mach-omap2/powerdomain.c |  208 +++++++++++++++++++++++++++++++++++++
 arch/arm/mach-omap2/powerdomain.h |   27 ++++-
 2 files changed, 233 insertions(+), 2 deletions(-)

diff --git a/arch/arm/mach-omap2/powerdomain.c b/arch/arm/mach-omap2/powerdomain.c
index 7caceaa..bcba0af 100644
--- a/arch/arm/mach-omap2/powerdomain.c
+++ b/arch/arm/mach-omap2/powerdomain.c
@@ -21,8 +21,10 @@
 #include <linux/kernel.h>
 #include <linux/types.h>
 #include <linux/list.h>
+#include <linux/slab.h>
 #include <linux/errno.h>
 #include <linux/string.h>
+#include <linux/pm_qos.h>
 #include <linux/spinlock.h>
 #include <linux/sched.h>
 #include <linux/seq_file.h>
@@ -125,6 +127,9 @@ static int _pwrdm_register(struct powerdomain *pwrdm)
 	for (i = 0; i < PWRDM_FPWRSTS_COUNT; i++)
 		pwrdm->fpwrst_counter[i] = 0;
 
+	plist_head_init(&pwrdm->wakeuplat_plist_head);
+	pwrdm->wakeuplat_next_fpwrst = PWRDM_FUNC_PWRST_OFF;
+
 	arch_pwrdm->pwrdm_wait_transition(pwrdm);
 	pwrdm->fpwrst = pwrdm_read_fpwrst(pwrdm);
 	pwrdm->fpwrst_counter[pwrdm->fpwrst - PWRDM_FPWRST_OFFSET] = 1;
@@ -765,6 +770,58 @@ static int _pwrdm_set_fpwrst(struct powerdomain *pwrdm,
 	return ret;
 }
 
+/**
+ * _pwrdm_wakeuplat_update_pwrst - Update power domain power state if needed
+ * @pwrdm: struct powerdomain * to which requesting device belongs to.
+ * @min_latency: the allowed wake-up latency for the given power domain. A
+ *  value of PM_QOS_DEV_LAT_DEFAULT_VALUE means 'no constraint' on the pwrdm.
+ *
+ * Finds the power domain next power state that fulfills the constraint.
+ * Programs a new target state if it is different from current power state.
+ * The power domains get the next power state programmed directly in the
+ * registers.
+ *
+ * Must be called with the powerdomain lock held.
+ *
+ * Returns 0 in case of success, -EINVAL in case of invalid parameters,
+ * or the return value from _pwrdm_set_fpwrst().
+ */
+static int _pwrdm_wakeuplat_update_pwrst(struct powerdomain *pwrdm,
+					 long min_latency)
+{
+	int ret = 0, new_fpwrst = PWRDM_FUNC_PWRST_ON;
+	int fpwrst, pwl_index;
+
+	/*
+	 * Find the next supported power state with
+	 *  wakeup latency <= min_latency.
+	 * Pick the lower state if no constraint on the pwrdm
+	 *  (min_latency == PM_QOS_DEV_LAT_DEFAULT_VALUE).
+	 * Skip unsupported functional power states.
+	 * If no power state found, fall back to PWRDM_FUNC_PWRST_ON.
+	 */
+	for (fpwrst = PWRDM_FUNC_PWRST_OFF; fpwrst < PWRDM_MAX_FUNC_PWRSTS;
+	     fpwrst++) {
+		if (!pwrdm_supports_fpwrst(pwrdm, fpwrst))
+			continue;
+
+		pwl_index = fpwrst - PWRDM_FPWRST_OFFSET;
+		if ((min_latency == PM_QOS_DEV_LAT_DEFAULT_VALUE) ||
+		    (pwrdm->wakeuplat[pwl_index] <= min_latency)) {
+			new_fpwrst = fpwrst;
+			break;
+		}
+	}
+
+	pwrdm->wakeuplat_next_fpwrst = new_fpwrst;
+	ret = _pwrdm_set_fpwrst(pwrdm, new_fpwrst);
+
+	pr_debug("%s: fpwrst for %s: min_latency=%ld, new_state=%d, ret=%d\n",
+		 __func__, pwrdm->name, min_latency, new_fpwrst, ret);
+
+	return ret;
+}
+
 /* Public functions */
 
 /**
@@ -1209,6 +1266,157 @@ int pwrdm_post_transition(struct powerdomain *pwrdm)
 }
 
 /**
+ * pwrdm_wakeuplat_update_constraint - Set or update a powerdomain wakeup
+ *  latency constraint and apply it
+ * @pwrdm: struct powerdomain * which the constraint applies to
+ * @cookie: constraint identifier, used for tracking
+ * @min_latency: minimum wakeup latency constraint (in microseconds) for
+ *  the given pwrdm
+ *
+ * Tracks the constraints by @cookie.
+ * Constraint set/update: Adds a new entry to powerdomain's wake-up latency
+ * constraint list. If the constraint identifier already exists in the list,
+ * the old value is overwritten.
+ *
+ * Applies the aggregated constraint value for the given pwrdm by calling
+ * _pwrdm_wakeuplat_update_pwrst().
+ *
+ * Returns 0 upon success, -ENOMEM in case of memory shortage, -EINVAL in
+ * case of invalid latency value, or the return value from
+ * _pwrdm_wakeuplat_update_pwrst().
+ *
+ * The caller must check the validity of the parameters.
+ */
+int pwrdm_wakeuplat_update_constraint(struct powerdomain *pwrdm, void *cookie,
+				      long min_latency)
+{
+	struct pwrdm_wkup_constraints_entry *tmp_user, *new_user;
+	struct pwrdm_wkup_constraints_entry *user = NULL;
+	long value = PM_QOS_DEV_LAT_DEFAULT_VALUE;
+	int ret = 0;
+	bool new_node = false;
+
+	pr_debug("powerdomain: %s: pwrdm %s, cookie=0x%p, min_latency=%ld\n",
+		 __func__, pwrdm->name, cookie, min_latency);
+
+	if (min_latency <= PM_QOS_DEV_LAT_DEFAULT_VALUE) {
+		pr_warn("%s: min_latency >= PM_QOS_DEV_LAT_DEFAULT_VALUE\n",
+			__func__);
+		return -EINVAL;
+	}
+
+	/* Allocate a new entry for insertion in the list */
+	new_user = kzalloc(sizeof(struct pwrdm_wkup_constraints_entry),
+			   GFP_KERNEL);
+	if (!new_user) {
+		pr_err("%s: FATAL ERROR: kzalloc failed\n", __func__);
+		return -ENOMEM;
+	}
+
+	pwrdm_lock(pwrdm);
+
+	/* Check if there already is a constraint for cookie */
+	plist_for_each_entry(tmp_user, &pwrdm->wakeuplat_plist_head, node) {
+		if (tmp_user->cookie == cookie) {
+			user = tmp_user;
+			break;
+		}
+	}
+
+	if (!user) {
+		new_user->cookie = cookie;
+		user = new_user;
+		new_node = true;
+	} else {
+		/* If nothing to update, job done */
+		if (user->node.prio == min_latency)
+			goto pwuc_out;
+		/* Update existing entry */
+		plist_del(&user->node, &pwrdm->wakeuplat_plist_head);
+	}
+
+	plist_node_init(&user->node, min_latency);
+	plist_add(&user->node, &pwrdm->wakeuplat_plist_head);
+
+	/* Find the aggregated constraint value from the list */
+	value = plist_first(&pwrdm->wakeuplat_plist_head)->prio;
+
+	pr_debug("powerdomain: %s: pwrdm %s, value=%ld\n",
+		 __func__, pwrdm->name, value);
+
+	/* Apply the constraint to the pwrdm */
+	ret = _pwrdm_wakeuplat_update_pwrst(pwrdm, value);
+
+pwuc_out:
+	pwrdm_unlock(pwrdm);
+	if (!new_node)
+		kfree(new_user);
+	return ret;
+}
+
+/**
+ * pwrdm_wakeuplat_remove_constraint - Release a powerdomain wakeup latency
+ *  constraint and apply it
+ * @pwrdm: struct powerdomain * which the constraint applies to
+ * @cookie: constraint identifier, used for tracking
+ *
+ * Tracks the constraints by @cookie.
+ * Constraint removal: Removes the identifier's entry from powerdomain's
+ * wakeup latency constraint list.
+ *
+ * Applies the aggregated constraint value for the given pwrdm by calling
+ * _pwrdm_wakeuplat_update_pwrst().
+ *
+ * Returns 0 upon success, -EINVAL in case the constraint to remove is not
+ * existing, or the return value from _pwrdm_wakeuplat_update_pwrst().
+ *
+ * The caller must check the validity of the parameters.
+ */
+int pwrdm_wakeuplat_remove_constraint(struct powerdomain *pwrdm, void *cookie)
+{
+	struct pwrdm_wkup_constraints_entry *tmp_user, *user = NULL;
+	long value = PM_QOS_DEV_LAT_DEFAULT_VALUE;
+
+	pr_debug("powerdomain: %s: pwrdm %s, cookie=0x%p\n",
+		 __func__, pwrdm->name, cookie);
+
+	pwrdm_lock(pwrdm);
+
+	/* Check if there is a constraint for cookie */
+	plist_for_each_entry(tmp_user, &pwrdm->wakeuplat_plist_head, node) {
+		if (tmp_user->cookie == cookie) {
+			user = tmp_user;
+			break;
+		}
+	}
+
+	/* If constraint not existing or list empty, return -EINVAL */
+	if (!user)
+		goto out;
+
+	/* Remove the constraint from the list */
+	plist_del(&user->node, &pwrdm->wakeuplat_plist_head);
+
+	/* Find the aggregated constraint value from the list */
+	if (!plist_head_empty(&pwrdm->wakeuplat_plist_head))
+		value = plist_first(&pwrdm->wakeuplat_plist_head)->prio;
+
+	pwrdm_unlock(pwrdm);
+
+	/* Release the constraint memory */
+	kfree(user);
+
+	/* Apply the constraint to the pwrdm */
+	pr_debug("powerdomain: %s: pwrdm %s, value=%ld\n",
+		 __func__, pwrdm->name, value);
+	return _pwrdm_wakeuplat_update_pwrst(pwrdm, value);
+
+out:
+	pwrdm_unlock(pwrdm);
+	return -EINVAL;
+}
+
+/**
  * 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 10941fd..3971ee8 100644
--- a/arch/arm/mach-omap2/powerdomain.h
+++ b/arch/arm/mach-omap2/powerdomain.h
@@ -19,6 +19,7 @@
 
 #include <linux/types.h>
 #include <linux/list.h>
+#include <linux/plist.h>
 #include <linux/spinlock.h>
 
 #include "voltage.h"
@@ -152,6 +153,9 @@ struct powerdomain;
  * @fpwrst_counter: estimated number of times the pwrdm entered the power states
  * @next_fpwrst: cache of the powerdomain's next-power-state
  * @prev_fpwrst: cache of the powerdomain's previous-power-state bitfield
+ * @wakeuplat: array of powerdomain wakeup latencies at OPP50, in ns
+ * @wakeuplat_plist_head: plist of active pwrdm wakeup lat constraints
+ * @wakeuplat_next_fpwrst: func power state lower bound, by wakeup lat constrs
  * @timer: sched_clock() timestamp of last pwrdm_state_switch()
  * @fpwrst_timer: estimated nanoseconds of residency in the various power states
  * @_lock: spinlock used to serialize powerdomain and some clockdomain ops
@@ -180,9 +184,12 @@ struct powerdomain {
 	const u8 pwrsts_mem_ret[PWRDM_MAX_MEM_BANKS];
 	const u8 pwrsts_mem_on[PWRDM_MAX_MEM_BANKS];
 	const u8 prcm_partition;
+	const u8 pwrstctrl_offs;
+	const u8 pwrstst_offs;
 	u8 fpwrst;
 	u8 next_fpwrst;
 	u8 prev_fpwrst;
+	u8 wakeuplat_next_fpwrst;
 	u8 _flags;
 	struct clockdomain *pwrdm_clkdms[PWRDM_MAX_CLKDMS];
 	struct list_head node;
@@ -190,13 +197,13 @@ struct powerdomain {
 	unsigned fpwrst_counter[PWRDM_FPWRSTS_COUNT];
 	spinlock_t _lock;
 	unsigned long _lock_flags;
-	const u8 pwrstctrl_offs;
-	const u8 pwrstst_offs;
 	const u32 logicretstate_mask;
 	const u32 mem_on_mask[PWRDM_MAX_MEM_BANKS];
 	const u32 mem_ret_mask[PWRDM_MAX_MEM_BANKS];
 	const u32 mem_pwrst_mask[PWRDM_MAX_MEM_BANKS];
 	const u32 mem_retst_mask[PWRDM_MAX_MEM_BANKS];
+	const s32 wakeuplat[PWRDM_MAX_FUNC_PWRSTS];
+	struct plist_head wakeuplat_plist_head;
 
 #ifdef CONFIG_PM_DEBUG
 	s64 timer;
@@ -204,6 +211,18 @@ struct powerdomain {
 #endif
 };
 
+
+/**
+ * struct pwrdm_wkup_constraints_entry - Linked list for the wake-up latency
+ * constraints
+ * @cookie: constraint identifier, used for tracking
+ * @node: plist node
+ */
+struct pwrdm_wkup_constraints_entry {
+	void			*cookie;
+	struct plist_node	node;
+};
+
 /**
  * struct pwrdm_ops - Arch specific function implementations
  * @pwrdm_set_next_pwrst: Set the target power state for a pd
@@ -295,6 +314,10 @@ extern void omap3xxx_powerdomains_init(void);
 extern void am33xx_powerdomains_init(void);
 extern void omap44xx_powerdomains_init(void);
 
+int pwrdm_wakeuplat_update_constraint(struct powerdomain *pwrdm, void *cookie,
+				      long min_latency);
+int pwrdm_wakeuplat_remove_constraint(struct powerdomain *pwrdm, void *cookie);
+
 extern struct pwrdm_ops omap2_pwrdm_operations;
 extern struct pwrdm_ops omap3_pwrdm_operations;
 extern struct pwrdm_ops am33xx_pwrdm_operations;
-- 
1.7.10.4


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

* [PATCH 1/8] ARM: OMAP2+: PM QoS: control the power domains next state from the constraints
@ 2012-12-11  0:05     ` Paul Walmsley
  0 siblings, 0 replies; 28+ messages in thread
From: Paul Walmsley @ 2012-12-11  0:05 UTC (permalink / raw)
  To: linux-arm-kernel

Hi

On Tue, 18 Sep 2012, Jean Pihet wrote:

> When a PM QoS device latency constraint is requested or removed the
> constraint is stored in the constraints list of the corresponding power
> domain, then the aggregated constraint value is applied by programming
> the next functional power state using pwrdm_set_fpwrst.
> 
> The per-device PM QoS locking requires a spinlock to be used. The reasons
> is that some drivers need to use the per-device PM QoS functionality from
> interrupt context or spinlock protected context, as reported by Djamil.
> An example of such a driver is the OMAP HSI (high-speed synchronous serial
> interface) driver which needs to control the IP block idle state from
> the FIFO empty state, from interrupt context.
> 
> Tested on OMAP3 Beagleboard and OMAP4 Pandaboard in RET/OFF using
> wake-up latency constraints on MPU, CORE and PER.
> 
> Signed-off-by: Jean Pihet <j-pihet@ti.com>
> Cc: Djamil Elaidi <d-elaidi@ti.com>

This patch has been changed here.  Details below, but the major changes 
were to fix the locking and to not attempt GFP_ATOMIC allocations.
Hopefully the reasons why are clear, but if not, feel free to ask.

The following patch has only been compile-tested, and a few more minor 
changes will probably be needed.


- Paul

From: Jean Pihet <jean.pihet@newoldbits.com>
Date: Sun, 9 Dec 2012 18:38:12 -0700
Subject: [PATCH] ARM: OMAP2+: PM QoS: control the power domains next state
 from the constraints

When a PM QoS device latency constraint is requested or removed the
constraint is stored in the constraints list of the corresponding power
domain, then the aggregated constraint value is applied by programming
the next functional power state using pwrdm_set_fpwrst.

The per-device PM QoS locking requires a spinlock to be used. The reasons
is that some drivers need to use the per-device PM QoS functionality from
interrupt context or spinlock protected context, as reported by Djamil.
An example of such a driver is the OMAP HSI (high-speed synchronous serial
interface) driver which needs to control the IP block idle state from
the FIFO empty state, from interrupt context.

Tested on OMAP3 Beagleboard and OMAP4 Pandaboard in RET/OFF using
wake-up latency constraints on MPU, CORE and PER.

Signed-off-by: Jean Pihet <j-pihet@ti.com>
Cc: Djamil Elaidi <d-elaidi@ti.com>
[paul at pwsan.com: updated to apply; renamed *state to *fpwrst and
 define as u8; use existing powerdomain spinlock; pack the struct powerdomain
 variables; removed the GFP_ATOMIC allocation; drop expensive operations
 from the _pwrdm_wakeuplat_update_pwrst() debug; ensure that the entire
 wakeuplat update process takes place under the spinlock; remove
 UNSUP_STATE; remove assumption that fpwrsts start at 0; remove
 unnecessary plist_empty test from update path; standardize 'wakeuplat'
 naming; add kerneldoc]

XXX conform debug to other powerdomain debugging
---
 arch/arm/mach-omap2/powerdomain.c |  208 +++++++++++++++++++++++++++++++++++++
 arch/arm/mach-omap2/powerdomain.h |   27 ++++-
 2 files changed, 233 insertions(+), 2 deletions(-)

diff --git a/arch/arm/mach-omap2/powerdomain.c b/arch/arm/mach-omap2/powerdomain.c
index 7caceaa..bcba0af 100644
--- a/arch/arm/mach-omap2/powerdomain.c
+++ b/arch/arm/mach-omap2/powerdomain.c
@@ -21,8 +21,10 @@
 #include <linux/kernel.h>
 #include <linux/types.h>
 #include <linux/list.h>
+#include <linux/slab.h>
 #include <linux/errno.h>
 #include <linux/string.h>
+#include <linux/pm_qos.h>
 #include <linux/spinlock.h>
 #include <linux/sched.h>
 #include <linux/seq_file.h>
@@ -125,6 +127,9 @@ static int _pwrdm_register(struct powerdomain *pwrdm)
 	for (i = 0; i < PWRDM_FPWRSTS_COUNT; i++)
 		pwrdm->fpwrst_counter[i] = 0;
 
+	plist_head_init(&pwrdm->wakeuplat_plist_head);
+	pwrdm->wakeuplat_next_fpwrst = PWRDM_FUNC_PWRST_OFF;
+
 	arch_pwrdm->pwrdm_wait_transition(pwrdm);
 	pwrdm->fpwrst = pwrdm_read_fpwrst(pwrdm);
 	pwrdm->fpwrst_counter[pwrdm->fpwrst - PWRDM_FPWRST_OFFSET] = 1;
@@ -765,6 +770,58 @@ static int _pwrdm_set_fpwrst(struct powerdomain *pwrdm,
 	return ret;
 }
 
+/**
+ * _pwrdm_wakeuplat_update_pwrst - Update power domain power state if needed
+ * @pwrdm: struct powerdomain * to which requesting device belongs to.
+ * @min_latency: the allowed wake-up latency for the given power domain. A
+ *  value of PM_QOS_DEV_LAT_DEFAULT_VALUE means 'no constraint' on the pwrdm.
+ *
+ * Finds the power domain next power state that fulfills the constraint.
+ * Programs a new target state if it is different from current power state.
+ * The power domains get the next power state programmed directly in the
+ * registers.
+ *
+ * Must be called with the powerdomain lock held.
+ *
+ * Returns 0 in case of success, -EINVAL in case of invalid parameters,
+ * or the return value from _pwrdm_set_fpwrst().
+ */
+static int _pwrdm_wakeuplat_update_pwrst(struct powerdomain *pwrdm,
+					 long min_latency)
+{
+	int ret = 0, new_fpwrst = PWRDM_FUNC_PWRST_ON;
+	int fpwrst, pwl_index;
+
+	/*
+	 * Find the next supported power state with
+	 *  wakeup latency <= min_latency.
+	 * Pick the lower state if no constraint on the pwrdm
+	 *  (min_latency == PM_QOS_DEV_LAT_DEFAULT_VALUE).
+	 * Skip unsupported functional power states.
+	 * If no power state found, fall back to PWRDM_FUNC_PWRST_ON.
+	 */
+	for (fpwrst = PWRDM_FUNC_PWRST_OFF; fpwrst < PWRDM_MAX_FUNC_PWRSTS;
+	     fpwrst++) {
+		if (!pwrdm_supports_fpwrst(pwrdm, fpwrst))
+			continue;
+
+		pwl_index = fpwrst - PWRDM_FPWRST_OFFSET;
+		if ((min_latency == PM_QOS_DEV_LAT_DEFAULT_VALUE) ||
+		    (pwrdm->wakeuplat[pwl_index] <= min_latency)) {
+			new_fpwrst = fpwrst;
+			break;
+		}
+	}
+
+	pwrdm->wakeuplat_next_fpwrst = new_fpwrst;
+	ret = _pwrdm_set_fpwrst(pwrdm, new_fpwrst);
+
+	pr_debug("%s: fpwrst for %s: min_latency=%ld, new_state=%d, ret=%d\n",
+		 __func__, pwrdm->name, min_latency, new_fpwrst, ret);
+
+	return ret;
+}
+
 /* Public functions */
 
 /**
@@ -1209,6 +1266,157 @@ int pwrdm_post_transition(struct powerdomain *pwrdm)
 }
 
 /**
+ * pwrdm_wakeuplat_update_constraint - Set or update a powerdomain wakeup
+ *  latency constraint and apply it
+ * @pwrdm: struct powerdomain * which the constraint applies to
+ * @cookie: constraint identifier, used for tracking
+ * @min_latency: minimum wakeup latency constraint (in microseconds) for
+ *  the given pwrdm
+ *
+ * Tracks the constraints by @cookie.
+ * Constraint set/update: Adds a new entry to powerdomain's wake-up latency
+ * constraint list. If the constraint identifier already exists in the list,
+ * the old value is overwritten.
+ *
+ * Applies the aggregated constraint value for the given pwrdm by calling
+ * _pwrdm_wakeuplat_update_pwrst().
+ *
+ * Returns 0 upon success, -ENOMEM in case of memory shortage, -EINVAL in
+ * case of invalid latency value, or the return value from
+ * _pwrdm_wakeuplat_update_pwrst().
+ *
+ * The caller must check the validity of the parameters.
+ */
+int pwrdm_wakeuplat_update_constraint(struct powerdomain *pwrdm, void *cookie,
+				      long min_latency)
+{
+	struct pwrdm_wkup_constraints_entry *tmp_user, *new_user;
+	struct pwrdm_wkup_constraints_entry *user = NULL;
+	long value = PM_QOS_DEV_LAT_DEFAULT_VALUE;
+	int ret = 0;
+	bool new_node = false;
+
+	pr_debug("powerdomain: %s: pwrdm %s, cookie=0x%p, min_latency=%ld\n",
+		 __func__, pwrdm->name, cookie, min_latency);
+
+	if (min_latency <= PM_QOS_DEV_LAT_DEFAULT_VALUE) {
+		pr_warn("%s: min_latency >= PM_QOS_DEV_LAT_DEFAULT_VALUE\n",
+			__func__);
+		return -EINVAL;
+	}
+
+	/* Allocate a new entry for insertion in the list */
+	new_user = kzalloc(sizeof(struct pwrdm_wkup_constraints_entry),
+			   GFP_KERNEL);
+	if (!new_user) {
+		pr_err("%s: FATAL ERROR: kzalloc failed\n", __func__);
+		return -ENOMEM;
+	}
+
+	pwrdm_lock(pwrdm);
+
+	/* Check if there already is a constraint for cookie */
+	plist_for_each_entry(tmp_user, &pwrdm->wakeuplat_plist_head, node) {
+		if (tmp_user->cookie == cookie) {
+			user = tmp_user;
+			break;
+		}
+	}
+
+	if (!user) {
+		new_user->cookie = cookie;
+		user = new_user;
+		new_node = true;
+	} else {
+		/* If nothing to update, job done */
+		if (user->node.prio == min_latency)
+			goto pwuc_out;
+		/* Update existing entry */
+		plist_del(&user->node, &pwrdm->wakeuplat_plist_head);
+	}
+
+	plist_node_init(&user->node, min_latency);
+	plist_add(&user->node, &pwrdm->wakeuplat_plist_head);
+
+	/* Find the aggregated constraint value from the list */
+	value = plist_first(&pwrdm->wakeuplat_plist_head)->prio;
+
+	pr_debug("powerdomain: %s: pwrdm %s, value=%ld\n",
+		 __func__, pwrdm->name, value);
+
+	/* Apply the constraint to the pwrdm */
+	ret = _pwrdm_wakeuplat_update_pwrst(pwrdm, value);
+
+pwuc_out:
+	pwrdm_unlock(pwrdm);
+	if (!new_node)
+		kfree(new_user);
+	return ret;
+}
+
+/**
+ * pwrdm_wakeuplat_remove_constraint - Release a powerdomain wakeup latency
+ *  constraint and apply it
+ * @pwrdm: struct powerdomain * which the constraint applies to
+ * @cookie: constraint identifier, used for tracking
+ *
+ * Tracks the constraints by @cookie.
+ * Constraint removal: Removes the identifier's entry from powerdomain's
+ * wakeup latency constraint list.
+ *
+ * Applies the aggregated constraint value for the given pwrdm by calling
+ * _pwrdm_wakeuplat_update_pwrst().
+ *
+ * Returns 0 upon success, -EINVAL in case the constraint to remove is not
+ * existing, or the return value from _pwrdm_wakeuplat_update_pwrst().
+ *
+ * The caller must check the validity of the parameters.
+ */
+int pwrdm_wakeuplat_remove_constraint(struct powerdomain *pwrdm, void *cookie)
+{
+	struct pwrdm_wkup_constraints_entry *tmp_user, *user = NULL;
+	long value = PM_QOS_DEV_LAT_DEFAULT_VALUE;
+
+	pr_debug("powerdomain: %s: pwrdm %s, cookie=0x%p\n",
+		 __func__, pwrdm->name, cookie);
+
+	pwrdm_lock(pwrdm);
+
+	/* Check if there is a constraint for cookie */
+	plist_for_each_entry(tmp_user, &pwrdm->wakeuplat_plist_head, node) {
+		if (tmp_user->cookie == cookie) {
+			user = tmp_user;
+			break;
+		}
+	}
+
+	/* If constraint not existing or list empty, return -EINVAL */
+	if (!user)
+		goto out;
+
+	/* Remove the constraint from the list */
+	plist_del(&user->node, &pwrdm->wakeuplat_plist_head);
+
+	/* Find the aggregated constraint value from the list */
+	if (!plist_head_empty(&pwrdm->wakeuplat_plist_head))
+		value = plist_first(&pwrdm->wakeuplat_plist_head)->prio;
+
+	pwrdm_unlock(pwrdm);
+
+	/* Release the constraint memory */
+	kfree(user);
+
+	/* Apply the constraint to the pwrdm */
+	pr_debug("powerdomain: %s: pwrdm %s, value=%ld\n",
+		 __func__, pwrdm->name, value);
+	return _pwrdm_wakeuplat_update_pwrst(pwrdm, value);
+
+out:
+	pwrdm_unlock(pwrdm);
+	return -EINVAL;
+}
+
+/**
  * 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 10941fd..3971ee8 100644
--- a/arch/arm/mach-omap2/powerdomain.h
+++ b/arch/arm/mach-omap2/powerdomain.h
@@ -19,6 +19,7 @@
 
 #include <linux/types.h>
 #include <linux/list.h>
+#include <linux/plist.h>
 #include <linux/spinlock.h>
 
 #include "voltage.h"
@@ -152,6 +153,9 @@ struct powerdomain;
  * @fpwrst_counter: estimated number of times the pwrdm entered the power states
  * @next_fpwrst: cache of the powerdomain's next-power-state
  * @prev_fpwrst: cache of the powerdomain's previous-power-state bitfield
+ * @wakeuplat: array of powerdomain wakeup latencies at OPP50, in ns
+ * @wakeuplat_plist_head: plist of active pwrdm wakeup lat constraints
+ * @wakeuplat_next_fpwrst: func power state lower bound, by wakeup lat constrs
  * @timer: sched_clock() timestamp of last pwrdm_state_switch()
  * @fpwrst_timer: estimated nanoseconds of residency in the various power states
  * @_lock: spinlock used to serialize powerdomain and some clockdomain ops
@@ -180,9 +184,12 @@ struct powerdomain {
 	const u8 pwrsts_mem_ret[PWRDM_MAX_MEM_BANKS];
 	const u8 pwrsts_mem_on[PWRDM_MAX_MEM_BANKS];
 	const u8 prcm_partition;
+	const u8 pwrstctrl_offs;
+	const u8 pwrstst_offs;
 	u8 fpwrst;
 	u8 next_fpwrst;
 	u8 prev_fpwrst;
+	u8 wakeuplat_next_fpwrst;
 	u8 _flags;
 	struct clockdomain *pwrdm_clkdms[PWRDM_MAX_CLKDMS];
 	struct list_head node;
@@ -190,13 +197,13 @@ struct powerdomain {
 	unsigned fpwrst_counter[PWRDM_FPWRSTS_COUNT];
 	spinlock_t _lock;
 	unsigned long _lock_flags;
-	const u8 pwrstctrl_offs;
-	const u8 pwrstst_offs;
 	const u32 logicretstate_mask;
 	const u32 mem_on_mask[PWRDM_MAX_MEM_BANKS];
 	const u32 mem_ret_mask[PWRDM_MAX_MEM_BANKS];
 	const u32 mem_pwrst_mask[PWRDM_MAX_MEM_BANKS];
 	const u32 mem_retst_mask[PWRDM_MAX_MEM_BANKS];
+	const s32 wakeuplat[PWRDM_MAX_FUNC_PWRSTS];
+	struct plist_head wakeuplat_plist_head;
 
 #ifdef CONFIG_PM_DEBUG
 	s64 timer;
@@ -204,6 +211,18 @@ struct powerdomain {
 #endif
 };
 
+
+/**
+ * struct pwrdm_wkup_constraints_entry - Linked list for the wake-up latency
+ * constraints
+ * @cookie: constraint identifier, used for tracking
+ * @node: plist node
+ */
+struct pwrdm_wkup_constraints_entry {
+	void			*cookie;
+	struct plist_node	node;
+};
+
 /**
  * struct pwrdm_ops - Arch specific function implementations
  * @pwrdm_set_next_pwrst: Set the target power state for a pd
@@ -295,6 +314,10 @@ extern void omap3xxx_powerdomains_init(void);
 extern void am33xx_powerdomains_init(void);
 extern void omap44xx_powerdomains_init(void);
 
+int pwrdm_wakeuplat_update_constraint(struct powerdomain *pwrdm, void *cookie,
+				      long min_latency);
+int pwrdm_wakeuplat_remove_constraint(struct powerdomain *pwrdm, void *cookie);
+
 extern struct pwrdm_ops omap2_pwrdm_operations;
 extern struct pwrdm_ops omap3_pwrdm_operations;
 extern struct pwrdm_ops am33xx_pwrdm_operations;
-- 
1.7.10.4

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

* Re: [PATCH 1/8] ARM: OMAP2+: PM QoS: control the power domains next state from the constraints
  2012-12-11  0:05     ` Paul Walmsley
@ 2012-12-14 15:50       ` Jean Pihet
  -1 siblings, 0 replies; 28+ messages in thread
From: Jean Pihet @ 2012-12-14 15:50 UTC (permalink / raw)
  To: Paul Walmsley
  Cc: linux-omap, linux-arm, Kevin Hilman, Jean Pihet, Djamil Elaidi

Hi Paul,

On Tue, Dec 11, 2012 at 1:05 AM, Paul Walmsley <paul@pwsan.com> wrote:
> Hi
>
> On Tue, 18 Sep 2012, Jean Pihet wrote:
>
>> When a PM QoS device latency constraint is requested or removed the
>> constraint is stored in the constraints list of the corresponding power
>> domain, then the aggregated constraint value is applied by programming
>> the next functional power state using pwrdm_set_fpwrst.
>>
>> The per-device PM QoS locking requires a spinlock to be used. The reasons
>> is that some drivers need to use the per-device PM QoS functionality from
>> interrupt context or spinlock protected context, as reported by Djamil.
>> An example of such a driver is the OMAP HSI (high-speed synchronous serial
>> interface) driver which needs to control the IP block idle state from
>> the FIFO empty state, from interrupt context.
>>
>> Tested on OMAP3 Beagleboard and OMAP4 Pandaboard in RET/OFF using
>> wake-up latency constraints on MPU, CORE and PER.
>>
>> Signed-off-by: Jean Pihet <j-pihet@ti.com>
>> Cc: Djamil Elaidi <d-elaidi@ti.com>
>
> This patch has been changed here.  Details below, but the major changes
Thanks for reworking this!

> were to fix the locking and to not attempt GFP_ATOMIC allocations.
> Hopefully the reasons why are clear, but if not, feel free to ask.
This is fine.

Note about the locking and the allocations:
The locking had been changed to spinlocks because of the potential
need to call the per-device PM QoS API from interrupt or spinlock
protected context.
Unfortunately the per-device PM QoS cannot be called from such a
context, because (1) it internally uses mutexes and (2) it relies on a
blocking notification mechanism.
However after discussion with Rafael W at ELCE, the PM QoS framework
will be reworked to get rid of the overly complex notification
mechanism [1]. The generic power domain framework will be used instead
to apply the constraints to the power domains at the time the power
domain is set up to transition to a lower power state (e.g. when the
last active device of a power domain is set up to idle automatically).
The implementation on OMAP needs some thinking. This very patch is ok
but the registration mechanism to the PM QoS framework [2] will need
to change.

[1] http://marc.info/?l=linux-pm&m=134856516908683&w=2
[2] http://marc.info/?l=linux-omap&m=134795836304294&w=2

>
> The following patch has only been compile-tested, and a few more minor
> changes will probably be needed.
Reviewed only from my side.

More comments here below.

>
>
> - Paul
>
> From: Jean Pihet <jean.pihet@newoldbits.com>
> Date: Sun, 9 Dec 2012 18:38:12 -0700
> Subject: [PATCH] ARM: OMAP2+: PM QoS: control the power domains next state
>  from the constraints
>
> When a PM QoS device latency constraint is requested or removed the
> constraint is stored in the constraints list of the corresponding power
> domain, then the aggregated constraint value is applied by programming
> the next functional power state using pwrdm_set_fpwrst.
>
> The per-device PM QoS locking requires a spinlock to be used. The reasons
> is that some drivers need to use the per-device PM QoS functionality from
> interrupt context or spinlock protected context, as reported by Djamil.
> An example of such a driver is the OMAP HSI (high-speed synchronous serial
> interface) driver which needs to control the IP block idle state from
> the FIFO empty state, from interrupt context.
>
> Tested on OMAP3 Beagleboard and OMAP4 Pandaboard in RET/OFF using
> wake-up latency constraints on MPU, CORE and PER.
Has it been tested already?

> Signed-off-by: Jean Pihet <j-pihet@ti.com>
> Cc: Djamil Elaidi <d-elaidi@ti.com>
> [paul@pwsan.com: updated to apply; renamed *state to *fpwrst and
>  define as u8; use existing powerdomain spinlock; pack the struct powerdomain
>  variables; removed the GFP_ATOMIC allocation; drop expensive operations
>  from the _pwrdm_wakeuplat_update_pwrst() debug; ensure that the entire
>  wakeuplat update process takes place under the spinlock; remove
>  UNSUP_STATE; remove assumption that fpwrsts start at 0; remove
>  unnecessary plist_empty test from update path; standardize 'wakeuplat'
>  naming; add kerneldoc]
>
> XXX conform debug to other powerdomain debugging
There are a few 'XXX' comments left.

> ---
>  arch/arm/mach-omap2/powerdomain.c |  208 +++++++++++++++++++++++++++++++++++++
>  arch/arm/mach-omap2/powerdomain.h |   27 ++++-
>  2 files changed, 233 insertions(+), 2 deletions(-)
>
> diff --git a/arch/arm/mach-omap2/powerdomain.c b/arch/arm/mach-omap2/powerdomain.c
> index 7caceaa..bcba0af 100644
> --- a/arch/arm/mach-omap2/powerdomain.c
> +++ b/arch/arm/mach-omap2/powerdomain.c
> @@ -21,8 +21,10 @@
>  #include <linux/kernel.h>
>  #include <linux/types.h>
>  #include <linux/list.h>
> +#include <linux/slab.h>
>  #include <linux/errno.h>
>  #include <linux/string.h>
> +#include <linux/pm_qos.h>
>  #include <linux/spinlock.h>
>  #include <linux/sched.h>
>  #include <linux/seq_file.h>
> @@ -125,6 +127,9 @@ static int _pwrdm_register(struct powerdomain *pwrdm)
>         for (i = 0; i < PWRDM_FPWRSTS_COUNT; i++)
>                 pwrdm->fpwrst_counter[i] = 0;
>
> +       plist_head_init(&pwrdm->wakeuplat_plist_head);
> +       pwrdm->wakeuplat_next_fpwrst = PWRDM_FUNC_PWRST_OFF;
> +
>         arch_pwrdm->pwrdm_wait_transition(pwrdm);
>         pwrdm->fpwrst = pwrdm_read_fpwrst(pwrdm);
>         pwrdm->fpwrst_counter[pwrdm->fpwrst - PWRDM_FPWRST_OFFSET] = 1;
> @@ -765,6 +770,58 @@ static int _pwrdm_set_fpwrst(struct powerdomain *pwrdm,
>         return ret;
>  }
>
> +/**
> + * _pwrdm_wakeuplat_update_pwrst - Update power domain power state if needed
> + * @pwrdm: struct powerdomain * to which requesting device belongs to.
> + * @min_latency: the allowed wake-up latency for the given power domain. A
> + *  value of PM_QOS_DEV_LAT_DEFAULT_VALUE means 'no constraint' on the pwrdm.
> + *
> + * Finds the power domain next power state that fulfills the constraint.
> + * Programs a new target state if it is different from current power state.
> + * The power domains get the next power state programmed directly in the
> + * registers.
> + *
> + * Must be called with the powerdomain lock held.
> + *
> + * Returns 0 in case of success, -EINVAL in case of invalid parameters,
> + * or the return value from _pwrdm_set_fpwrst().
> + */
> +static int _pwrdm_wakeuplat_update_pwrst(struct powerdomain *pwrdm,
> +                                        long min_latency)
> +{
> +       int ret = 0, new_fpwrst = PWRDM_FUNC_PWRST_ON;
> +       int fpwrst, pwl_index;
> +
> +       /*
> +        * Find the next supported power state with
> +        *  wakeup latency <= min_latency.
> +        * Pick the lower state if no constraint on the pwrdm
> +        *  (min_latency == PM_QOS_DEV_LAT_DEFAULT_VALUE).
> +        * Skip unsupported functional power states.
> +        * If no power state found, fall back to PWRDM_FUNC_PWRST_ON.
> +        */
> +       for (fpwrst = PWRDM_FUNC_PWRST_OFF; fpwrst < PWRDM_MAX_FUNC_PWRSTS;
> +            fpwrst++) {
> +               if (!pwrdm_supports_fpwrst(pwrdm, fpwrst))
> +                       continue;
> +
> +               pwl_index = fpwrst - PWRDM_FPWRST_OFFSET;
> +               if ((min_latency == PM_QOS_DEV_LAT_DEFAULT_VALUE) ||
> +                   (pwrdm->wakeuplat[pwl_index] <= min_latency)) {
> +                       new_fpwrst = fpwrst;
> +                       break;
> +               }
> +       }
> +
> +       pwrdm->wakeuplat_next_fpwrst = new_fpwrst;
> +       ret = _pwrdm_set_fpwrst(pwrdm, new_fpwrst);
> +
> +       pr_debug("%s: fpwrst for %s: min_latency=%ld, new_state=%d, ret=%d\n",
> +                __func__, pwrdm->name, min_latency, new_fpwrst, ret);
> +
> +       return ret;
> +}
> +
>  /* Public functions */
>
>  /**
> @@ -1209,6 +1266,157 @@ int pwrdm_post_transition(struct powerdomain *pwrdm)
>  }
>
>  /**
> + * pwrdm_wakeuplat_update_constraint - Set or update a powerdomain wakeup
> + *  latency constraint and apply it
> + * @pwrdm: struct powerdomain * which the constraint applies to
> + * @cookie: constraint identifier, used for tracking
> + * @min_latency: minimum wakeup latency constraint (in microseconds) for
> + *  the given pwrdm
> + *
> + * Tracks the constraints by @cookie.
> + * Constraint set/update: Adds a new entry to powerdomain's wake-up latency
> + * constraint list. If the constraint identifier already exists in the list,
> + * the old value is overwritten.
> + *
> + * Applies the aggregated constraint value for the given pwrdm by calling
> + * _pwrdm_wakeuplat_update_pwrst().
> + *
> + * Returns 0 upon success, -ENOMEM in case of memory shortage, -EINVAL in
> + * case of invalid latency value, or the return value from
> + * _pwrdm_wakeuplat_update_pwrst().
> + *
> + * The caller must check the validity of the parameters.
> + */
> +int pwrdm_wakeuplat_update_constraint(struct powerdomain *pwrdm, void *cookie,
> +                                     long min_latency)
> +{
> +       struct pwrdm_wkup_constraints_entry *tmp_user, *new_user;
> +       struct pwrdm_wkup_constraints_entry *user = NULL;
> +       long value = PM_QOS_DEV_LAT_DEFAULT_VALUE;
> +       int ret = 0;
> +       bool new_node = false;
> +
> +       pr_debug("powerdomain: %s: pwrdm %s, cookie=0x%p, min_latency=%ld\n",
> +                __func__, pwrdm->name, cookie, min_latency);
> +
> +       if (min_latency <= PM_QOS_DEV_LAT_DEFAULT_VALUE) {
> +               pr_warn("%s: min_latency >= PM_QOS_DEV_LAT_DEFAULT_VALUE\n",
> +                       __func__);
> +               return -EINVAL;
> +       }
> +
> +       /* Allocate a new entry for insertion in the list */
> +       new_user = kzalloc(sizeof(struct pwrdm_wkup_constraints_entry),
> +                          GFP_KERNEL);
> +       if (!new_user) {
> +               pr_err("%s: FATAL ERROR: kzalloc failed\n", __func__);
> +               return -ENOMEM;
> +       }
> +
> +       pwrdm_lock(pwrdm);
> +
> +       /* Check if there already is a constraint for cookie */
> +       plist_for_each_entry(tmp_user, &pwrdm->wakeuplat_plist_head, node) {
> +               if (tmp_user->cookie == cookie) {
> +                       user = tmp_user;
> +                       break;
> +               }
> +       }
> +
> +       if (!user) {
> +               new_user->cookie = cookie;
> +               user = new_user;
> +               new_node = true;
> +       } else {
> +               /* If nothing to update, job done */
> +               if (user->node.prio == min_latency)
> +                       goto pwuc_out;
> +               /* Update existing entry */
> +               plist_del(&user->node, &pwrdm->wakeuplat_plist_head);
> +       }
> +
> +       plist_node_init(&user->node, min_latency);
> +       plist_add(&user->node, &pwrdm->wakeuplat_plist_head);
> +
> +       /* Find the aggregated constraint value from the list */
> +       value = plist_first(&pwrdm->wakeuplat_plist_head)->prio;
A check is needed for emptiness, cf. plist_first in include/linux/plist.h.
The original patch has the following:
+    /* Find the aggregated constraint value from the list */
+    if (!plist_head_empty(&pwrdm->wkup_lat_plist_head))
+        value = plist_first(&pwrdm->wkup_lat_plist_head)->prio;

> +
> +       pr_debug("powerdomain: %s: pwrdm %s, value=%ld\n",
> +                __func__, pwrdm->name, value);
> +
> +       /* Apply the constraint to the pwrdm */
> +       ret = _pwrdm_wakeuplat_update_pwrst(pwrdm, value);
> +
> +pwuc_out:
> +       pwrdm_unlock(pwrdm);
> +       if (!new_node)
> +               kfree(new_user);
> +       return ret;
> +}
> +
> +/**
> + * pwrdm_wakeuplat_remove_constraint - Release a powerdomain wakeup latency
> + *  constraint and apply it
> + * @pwrdm: struct powerdomain * which the constraint applies to
> + * @cookie: constraint identifier, used for tracking
> + *
> + * Tracks the constraints by @cookie.
> + * Constraint removal: Removes the identifier's entry from powerdomain's
> + * wakeup latency constraint list.
> + *
> + * Applies the aggregated constraint value for the given pwrdm by calling
> + * _pwrdm_wakeuplat_update_pwrst().
> + *
> + * Returns 0 upon success, -EINVAL in case the constraint to remove is not
> + * existing, or the return value from _pwrdm_wakeuplat_update_pwrst().
> + *
> + * The caller must check the validity of the parameters.
> + */
> +int pwrdm_wakeuplat_remove_constraint(struct powerdomain *pwrdm, void *cookie)
> +{
> +       struct pwrdm_wkup_constraints_entry *tmp_user, *user = NULL;
> +       long value = PM_QOS_DEV_LAT_DEFAULT_VALUE;
> +
> +       pr_debug("powerdomain: %s: pwrdm %s, cookie=0x%p\n",
> +                __func__, pwrdm->name, cookie);
> +
> +       pwrdm_lock(pwrdm);
> +
> +       /* Check if there is a constraint for cookie */
> +       plist_for_each_entry(tmp_user, &pwrdm->wakeuplat_plist_head, node) {
> +               if (tmp_user->cookie == cookie) {
> +                       user = tmp_user;
> +                       break;
> +               }
> +       }
> +
> +       /* If constraint not existing or list empty, return -EINVAL */
> +       if (!user)
> +               goto out;
> +
> +       /* Remove the constraint from the list */
> +       plist_del(&user->node, &pwrdm->wakeuplat_plist_head);
> +
> +       /* Find the aggregated constraint value from the list */
> +       if (!plist_head_empty(&pwrdm->wakeuplat_plist_head))
> +               value = plist_first(&pwrdm->wakeuplat_plist_head)->prio;
> +
> +       pwrdm_unlock(pwrdm);
> +
> +       /* Release the constraint memory */
> +       kfree(user);
> +
> +       /* Apply the constraint to the pwrdm */
> +       pr_debug("powerdomain: %s: pwrdm %s, value=%ld\n",
> +                __func__, pwrdm->name, value);
> +       return _pwrdm_wakeuplat_update_pwrst(pwrdm, value);
> +
> +out:
> +       pwrdm_unlock(pwrdm);
> +       return -EINVAL;
> +}
> +
> +/**
>   * 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 10941fd..3971ee8 100644
> --- a/arch/arm/mach-omap2/powerdomain.h
> +++ b/arch/arm/mach-omap2/powerdomain.h
> @@ -19,6 +19,7 @@
>
>  #include <linux/types.h>
>  #include <linux/list.h>
> +#include <linux/plist.h>
>  #include <linux/spinlock.h>
>
>  #include "voltage.h"
> @@ -152,6 +153,9 @@ struct powerdomain;
>   * @fpwrst_counter: estimated number of times the pwrdm entered the power states
>   * @next_fpwrst: cache of the powerdomain's next-power-state
>   * @prev_fpwrst: cache of the powerdomain's previous-power-state bitfield
> + * @wakeuplat: array of powerdomain wakeup latencies at OPP50, in ns
The latency numbers and parameters in the rest of the code are in
microseconds so an alignement is needed. The choice of the unit is
arbitrary. The PM QoS units are microseconds though.

> + * @wakeuplat_plist_head: plist of active pwrdm wakeup lat constraints
> + * @wakeuplat_next_fpwrst: func power state lower bound, by wakeup lat constrs
>   * @timer: sched_clock() timestamp of last pwrdm_state_switch()
>   * @fpwrst_timer: estimated nanoseconds of residency in the various power states
>   * @_lock: spinlock used to serialize powerdomain and some clockdomain ops
> @@ -180,9 +184,12 @@ struct powerdomain {
>         const u8 pwrsts_mem_ret[PWRDM_MAX_MEM_BANKS];
>         const u8 pwrsts_mem_on[PWRDM_MAX_MEM_BANKS];
>         const u8 prcm_partition;
> +       const u8 pwrstctrl_offs;
> +       const u8 pwrstst_offs;
>         u8 fpwrst;
>         u8 next_fpwrst;
>         u8 prev_fpwrst;
> +       u8 wakeuplat_next_fpwrst;
>         u8 _flags;
>         struct clockdomain *pwrdm_clkdms[PWRDM_MAX_CLKDMS];
>         struct list_head node;
> @@ -190,13 +197,13 @@ struct powerdomain {
>         unsigned fpwrst_counter[PWRDM_FPWRSTS_COUNT];
>         spinlock_t _lock;
>         unsigned long _lock_flags;
> -       const u8 pwrstctrl_offs;
> -       const u8 pwrstst_offs;
>         const u32 logicretstate_mask;
>         const u32 mem_on_mask[PWRDM_MAX_MEM_BANKS];
>         const u32 mem_ret_mask[PWRDM_MAX_MEM_BANKS];
>         const u32 mem_pwrst_mask[PWRDM_MAX_MEM_BANKS];
>         const u32 mem_retst_mask[PWRDM_MAX_MEM_BANKS];
> +       const s32 wakeuplat[PWRDM_MAX_FUNC_PWRSTS];
> +       struct plist_head wakeuplat_plist_head;
>
>  #ifdef CONFIG_PM_DEBUG
>         s64 timer;
> @@ -204,6 +211,18 @@ struct powerdomain {
>  #endif
>  };
>
> +
> +/**
> + * struct pwrdm_wkup_constraints_entry - Linked list for the wake-up latency
> + * constraints
> + * @cookie: constraint identifier, used for tracking
> + * @node: plist node
> + */
> +struct pwrdm_wkup_constraints_entry {
> +       void                    *cookie;
> +       struct plist_node       node;
> +};
> +
>  /**
>   * struct pwrdm_ops - Arch specific function implementations
>   * @pwrdm_set_next_pwrst: Set the target power state for a pd
> @@ -295,6 +314,10 @@ extern void omap3xxx_powerdomains_init(void);
>  extern void am33xx_powerdomains_init(void);
>  extern void omap44xx_powerdomains_init(void);
>
> +int pwrdm_wakeuplat_update_constraint(struct powerdomain *pwrdm, void *cookie,
> +                                     long min_latency);
> +int pwrdm_wakeuplat_remove_constraint(struct powerdomain *pwrdm, void *cookie);
> +
>  extern struct pwrdm_ops omap2_pwrdm_operations;
>  extern struct pwrdm_ops omap3_pwrdm_operations;
>  extern struct pwrdm_ops am33xx_pwrdm_operations;
> --
> 1.7.10.4
>

Regards,
Jean

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

* [PATCH 1/8] ARM: OMAP2+: PM QoS: control the power domains next state from the constraints
@ 2012-12-14 15:50       ` Jean Pihet
  0 siblings, 0 replies; 28+ messages in thread
From: Jean Pihet @ 2012-12-14 15:50 UTC (permalink / raw)
  To: linux-arm-kernel

Hi Paul,

On Tue, Dec 11, 2012 at 1:05 AM, Paul Walmsley <paul@pwsan.com> wrote:
> Hi
>
> On Tue, 18 Sep 2012, Jean Pihet wrote:
>
>> When a PM QoS device latency constraint is requested or removed the
>> constraint is stored in the constraints list of the corresponding power
>> domain, then the aggregated constraint value is applied by programming
>> the next functional power state using pwrdm_set_fpwrst.
>>
>> The per-device PM QoS locking requires a spinlock to be used. The reasons
>> is that some drivers need to use the per-device PM QoS functionality from
>> interrupt context or spinlock protected context, as reported by Djamil.
>> An example of such a driver is the OMAP HSI (high-speed synchronous serial
>> interface) driver which needs to control the IP block idle state from
>> the FIFO empty state, from interrupt context.
>>
>> Tested on OMAP3 Beagleboard and OMAP4 Pandaboard in RET/OFF using
>> wake-up latency constraints on MPU, CORE and PER.
>>
>> Signed-off-by: Jean Pihet <j-pihet@ti.com>
>> Cc: Djamil Elaidi <d-elaidi@ti.com>
>
> This patch has been changed here.  Details below, but the major changes
Thanks for reworking this!

> were to fix the locking and to not attempt GFP_ATOMIC allocations.
> Hopefully the reasons why are clear, but if not, feel free to ask.
This is fine.

Note about the locking and the allocations:
The locking had been changed to spinlocks because of the potential
need to call the per-device PM QoS API from interrupt or spinlock
protected context.
Unfortunately the per-device PM QoS cannot be called from such a
context, because (1) it internally uses mutexes and (2) it relies on a
blocking notification mechanism.
However after discussion with Rafael W at ELCE, the PM QoS framework
will be reworked to get rid of the overly complex notification
mechanism [1]. The generic power domain framework will be used instead
to apply the constraints to the power domains at the time the power
domain is set up to transition to a lower power state (e.g. when the
last active device of a power domain is set up to idle automatically).
The implementation on OMAP needs some thinking. This very patch is ok
but the registration mechanism to the PM QoS framework [2] will need
to change.

[1] http://marc.info/?l=linux-pm&m=134856516908683&w=2
[2] http://marc.info/?l=linux-omap&m=134795836304294&w=2

>
> The following patch has only been compile-tested, and a few more minor
> changes will probably be needed.
Reviewed only from my side.

More comments here below.

>
>
> - Paul
>
> From: Jean Pihet <jean.pihet@newoldbits.com>
> Date: Sun, 9 Dec 2012 18:38:12 -0700
> Subject: [PATCH] ARM: OMAP2+: PM QoS: control the power domains next state
>  from the constraints
>
> When a PM QoS device latency constraint is requested or removed the
> constraint is stored in the constraints list of the corresponding power
> domain, then the aggregated constraint value is applied by programming
> the next functional power state using pwrdm_set_fpwrst.
>
> The per-device PM QoS locking requires a spinlock to be used. The reasons
> is that some drivers need to use the per-device PM QoS functionality from
> interrupt context or spinlock protected context, as reported by Djamil.
> An example of such a driver is the OMAP HSI (high-speed synchronous serial
> interface) driver which needs to control the IP block idle state from
> the FIFO empty state, from interrupt context.
>
> Tested on OMAP3 Beagleboard and OMAP4 Pandaboard in RET/OFF using
> wake-up latency constraints on MPU, CORE and PER.
Has it been tested already?

> Signed-off-by: Jean Pihet <j-pihet@ti.com>
> Cc: Djamil Elaidi <d-elaidi@ti.com>
> [paul at pwsan.com: updated to apply; renamed *state to *fpwrst and
>  define as u8; use existing powerdomain spinlock; pack the struct powerdomain
>  variables; removed the GFP_ATOMIC allocation; drop expensive operations
>  from the _pwrdm_wakeuplat_update_pwrst() debug; ensure that the entire
>  wakeuplat update process takes place under the spinlock; remove
>  UNSUP_STATE; remove assumption that fpwrsts start at 0; remove
>  unnecessary plist_empty test from update path; standardize 'wakeuplat'
>  naming; add kerneldoc]
>
> XXX conform debug to other powerdomain debugging
There are a few 'XXX' comments left.

> ---
>  arch/arm/mach-omap2/powerdomain.c |  208 +++++++++++++++++++++++++++++++++++++
>  arch/arm/mach-omap2/powerdomain.h |   27 ++++-
>  2 files changed, 233 insertions(+), 2 deletions(-)
>
> diff --git a/arch/arm/mach-omap2/powerdomain.c b/arch/arm/mach-omap2/powerdomain.c
> index 7caceaa..bcba0af 100644
> --- a/arch/arm/mach-omap2/powerdomain.c
> +++ b/arch/arm/mach-omap2/powerdomain.c
> @@ -21,8 +21,10 @@
>  #include <linux/kernel.h>
>  #include <linux/types.h>
>  #include <linux/list.h>
> +#include <linux/slab.h>
>  #include <linux/errno.h>
>  #include <linux/string.h>
> +#include <linux/pm_qos.h>
>  #include <linux/spinlock.h>
>  #include <linux/sched.h>
>  #include <linux/seq_file.h>
> @@ -125,6 +127,9 @@ static int _pwrdm_register(struct powerdomain *pwrdm)
>         for (i = 0; i < PWRDM_FPWRSTS_COUNT; i++)
>                 pwrdm->fpwrst_counter[i] = 0;
>
> +       plist_head_init(&pwrdm->wakeuplat_plist_head);
> +       pwrdm->wakeuplat_next_fpwrst = PWRDM_FUNC_PWRST_OFF;
> +
>         arch_pwrdm->pwrdm_wait_transition(pwrdm);
>         pwrdm->fpwrst = pwrdm_read_fpwrst(pwrdm);
>         pwrdm->fpwrst_counter[pwrdm->fpwrst - PWRDM_FPWRST_OFFSET] = 1;
> @@ -765,6 +770,58 @@ static int _pwrdm_set_fpwrst(struct powerdomain *pwrdm,
>         return ret;
>  }
>
> +/**
> + * _pwrdm_wakeuplat_update_pwrst - Update power domain power state if needed
> + * @pwrdm: struct powerdomain * to which requesting device belongs to.
> + * @min_latency: the allowed wake-up latency for the given power domain. A
> + *  value of PM_QOS_DEV_LAT_DEFAULT_VALUE means 'no constraint' on the pwrdm.
> + *
> + * Finds the power domain next power state that fulfills the constraint.
> + * Programs a new target state if it is different from current power state.
> + * The power domains get the next power state programmed directly in the
> + * registers.
> + *
> + * Must be called with the powerdomain lock held.
> + *
> + * Returns 0 in case of success, -EINVAL in case of invalid parameters,
> + * or the return value from _pwrdm_set_fpwrst().
> + */
> +static int _pwrdm_wakeuplat_update_pwrst(struct powerdomain *pwrdm,
> +                                        long min_latency)
> +{
> +       int ret = 0, new_fpwrst = PWRDM_FUNC_PWRST_ON;
> +       int fpwrst, pwl_index;
> +
> +       /*
> +        * Find the next supported power state with
> +        *  wakeup latency <= min_latency.
> +        * Pick the lower state if no constraint on the pwrdm
> +        *  (min_latency == PM_QOS_DEV_LAT_DEFAULT_VALUE).
> +        * Skip unsupported functional power states.
> +        * If no power state found, fall back to PWRDM_FUNC_PWRST_ON.
> +        */
> +       for (fpwrst = PWRDM_FUNC_PWRST_OFF; fpwrst < PWRDM_MAX_FUNC_PWRSTS;
> +            fpwrst++) {
> +               if (!pwrdm_supports_fpwrst(pwrdm, fpwrst))
> +                       continue;
> +
> +               pwl_index = fpwrst - PWRDM_FPWRST_OFFSET;
> +               if ((min_latency == PM_QOS_DEV_LAT_DEFAULT_VALUE) ||
> +                   (pwrdm->wakeuplat[pwl_index] <= min_latency)) {
> +                       new_fpwrst = fpwrst;
> +                       break;
> +               }
> +       }
> +
> +       pwrdm->wakeuplat_next_fpwrst = new_fpwrst;
> +       ret = _pwrdm_set_fpwrst(pwrdm, new_fpwrst);
> +
> +       pr_debug("%s: fpwrst for %s: min_latency=%ld, new_state=%d, ret=%d\n",
> +                __func__, pwrdm->name, min_latency, new_fpwrst, ret);
> +
> +       return ret;
> +}
> +
>  /* Public functions */
>
>  /**
> @@ -1209,6 +1266,157 @@ int pwrdm_post_transition(struct powerdomain *pwrdm)
>  }
>
>  /**
> + * pwrdm_wakeuplat_update_constraint - Set or update a powerdomain wakeup
> + *  latency constraint and apply it
> + * @pwrdm: struct powerdomain * which the constraint applies to
> + * @cookie: constraint identifier, used for tracking
> + * @min_latency: minimum wakeup latency constraint (in microseconds) for
> + *  the given pwrdm
> + *
> + * Tracks the constraints by @cookie.
> + * Constraint set/update: Adds a new entry to powerdomain's wake-up latency
> + * constraint list. If the constraint identifier already exists in the list,
> + * the old value is overwritten.
> + *
> + * Applies the aggregated constraint value for the given pwrdm by calling
> + * _pwrdm_wakeuplat_update_pwrst().
> + *
> + * Returns 0 upon success, -ENOMEM in case of memory shortage, -EINVAL in
> + * case of invalid latency value, or the return value from
> + * _pwrdm_wakeuplat_update_pwrst().
> + *
> + * The caller must check the validity of the parameters.
> + */
> +int pwrdm_wakeuplat_update_constraint(struct powerdomain *pwrdm, void *cookie,
> +                                     long min_latency)
> +{
> +       struct pwrdm_wkup_constraints_entry *tmp_user, *new_user;
> +       struct pwrdm_wkup_constraints_entry *user = NULL;
> +       long value = PM_QOS_DEV_LAT_DEFAULT_VALUE;
> +       int ret = 0;
> +       bool new_node = false;
> +
> +       pr_debug("powerdomain: %s: pwrdm %s, cookie=0x%p, min_latency=%ld\n",
> +                __func__, pwrdm->name, cookie, min_latency);
> +
> +       if (min_latency <= PM_QOS_DEV_LAT_DEFAULT_VALUE) {
> +               pr_warn("%s: min_latency >= PM_QOS_DEV_LAT_DEFAULT_VALUE\n",
> +                       __func__);
> +               return -EINVAL;
> +       }
> +
> +       /* Allocate a new entry for insertion in the list */
> +       new_user = kzalloc(sizeof(struct pwrdm_wkup_constraints_entry),
> +                          GFP_KERNEL);
> +       if (!new_user) {
> +               pr_err("%s: FATAL ERROR: kzalloc failed\n", __func__);
> +               return -ENOMEM;
> +       }
> +
> +       pwrdm_lock(pwrdm);
> +
> +       /* Check if there already is a constraint for cookie */
> +       plist_for_each_entry(tmp_user, &pwrdm->wakeuplat_plist_head, node) {
> +               if (tmp_user->cookie == cookie) {
> +                       user = tmp_user;
> +                       break;
> +               }
> +       }
> +
> +       if (!user) {
> +               new_user->cookie = cookie;
> +               user = new_user;
> +               new_node = true;
> +       } else {
> +               /* If nothing to update, job done */
> +               if (user->node.prio == min_latency)
> +                       goto pwuc_out;
> +               /* Update existing entry */
> +               plist_del(&user->node, &pwrdm->wakeuplat_plist_head);
> +       }
> +
> +       plist_node_init(&user->node, min_latency);
> +       plist_add(&user->node, &pwrdm->wakeuplat_plist_head);
> +
> +       /* Find the aggregated constraint value from the list */
> +       value = plist_first(&pwrdm->wakeuplat_plist_head)->prio;
A check is needed for emptiness, cf. plist_first in include/linux/plist.h.
The original patch has the following:
+    /* Find the aggregated constraint value from the list */
+    if (!plist_head_empty(&pwrdm->wkup_lat_plist_head))
+        value = plist_first(&pwrdm->wkup_lat_plist_head)->prio;

> +
> +       pr_debug("powerdomain: %s: pwrdm %s, value=%ld\n",
> +                __func__, pwrdm->name, value);
> +
> +       /* Apply the constraint to the pwrdm */
> +       ret = _pwrdm_wakeuplat_update_pwrst(pwrdm, value);
> +
> +pwuc_out:
> +       pwrdm_unlock(pwrdm);
> +       if (!new_node)
> +               kfree(new_user);
> +       return ret;
> +}
> +
> +/**
> + * pwrdm_wakeuplat_remove_constraint - Release a powerdomain wakeup latency
> + *  constraint and apply it
> + * @pwrdm: struct powerdomain * which the constraint applies to
> + * @cookie: constraint identifier, used for tracking
> + *
> + * Tracks the constraints by @cookie.
> + * Constraint removal: Removes the identifier's entry from powerdomain's
> + * wakeup latency constraint list.
> + *
> + * Applies the aggregated constraint value for the given pwrdm by calling
> + * _pwrdm_wakeuplat_update_pwrst().
> + *
> + * Returns 0 upon success, -EINVAL in case the constraint to remove is not
> + * existing, or the return value from _pwrdm_wakeuplat_update_pwrst().
> + *
> + * The caller must check the validity of the parameters.
> + */
> +int pwrdm_wakeuplat_remove_constraint(struct powerdomain *pwrdm, void *cookie)
> +{
> +       struct pwrdm_wkup_constraints_entry *tmp_user, *user = NULL;
> +       long value = PM_QOS_DEV_LAT_DEFAULT_VALUE;
> +
> +       pr_debug("powerdomain: %s: pwrdm %s, cookie=0x%p\n",
> +                __func__, pwrdm->name, cookie);
> +
> +       pwrdm_lock(pwrdm);
> +
> +       /* Check if there is a constraint for cookie */
> +       plist_for_each_entry(tmp_user, &pwrdm->wakeuplat_plist_head, node) {
> +               if (tmp_user->cookie == cookie) {
> +                       user = tmp_user;
> +                       break;
> +               }
> +       }
> +
> +       /* If constraint not existing or list empty, return -EINVAL */
> +       if (!user)
> +               goto out;
> +
> +       /* Remove the constraint from the list */
> +       plist_del(&user->node, &pwrdm->wakeuplat_plist_head);
> +
> +       /* Find the aggregated constraint value from the list */
> +       if (!plist_head_empty(&pwrdm->wakeuplat_plist_head))
> +               value = plist_first(&pwrdm->wakeuplat_plist_head)->prio;
> +
> +       pwrdm_unlock(pwrdm);
> +
> +       /* Release the constraint memory */
> +       kfree(user);
> +
> +       /* Apply the constraint to the pwrdm */
> +       pr_debug("powerdomain: %s: pwrdm %s, value=%ld\n",
> +                __func__, pwrdm->name, value);
> +       return _pwrdm_wakeuplat_update_pwrst(pwrdm, value);
> +
> +out:
> +       pwrdm_unlock(pwrdm);
> +       return -EINVAL;
> +}
> +
> +/**
>   * 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 10941fd..3971ee8 100644
> --- a/arch/arm/mach-omap2/powerdomain.h
> +++ b/arch/arm/mach-omap2/powerdomain.h
> @@ -19,6 +19,7 @@
>
>  #include <linux/types.h>
>  #include <linux/list.h>
> +#include <linux/plist.h>
>  #include <linux/spinlock.h>
>
>  #include "voltage.h"
> @@ -152,6 +153,9 @@ struct powerdomain;
>   * @fpwrst_counter: estimated number of times the pwrdm entered the power states
>   * @next_fpwrst: cache of the powerdomain's next-power-state
>   * @prev_fpwrst: cache of the powerdomain's previous-power-state bitfield
> + * @wakeuplat: array of powerdomain wakeup latencies at OPP50, in ns
The latency numbers and parameters in the rest of the code are in
microseconds so an alignement is needed. The choice of the unit is
arbitrary. The PM QoS units are microseconds though.

> + * @wakeuplat_plist_head: plist of active pwrdm wakeup lat constraints
> + * @wakeuplat_next_fpwrst: func power state lower bound, by wakeup lat constrs
>   * @timer: sched_clock() timestamp of last pwrdm_state_switch()
>   * @fpwrst_timer: estimated nanoseconds of residency in the various power states
>   * @_lock: spinlock used to serialize powerdomain and some clockdomain ops
> @@ -180,9 +184,12 @@ struct powerdomain {
>         const u8 pwrsts_mem_ret[PWRDM_MAX_MEM_BANKS];
>         const u8 pwrsts_mem_on[PWRDM_MAX_MEM_BANKS];
>         const u8 prcm_partition;
> +       const u8 pwrstctrl_offs;
> +       const u8 pwrstst_offs;
>         u8 fpwrst;
>         u8 next_fpwrst;
>         u8 prev_fpwrst;
> +       u8 wakeuplat_next_fpwrst;
>         u8 _flags;
>         struct clockdomain *pwrdm_clkdms[PWRDM_MAX_CLKDMS];
>         struct list_head node;
> @@ -190,13 +197,13 @@ struct powerdomain {
>         unsigned fpwrst_counter[PWRDM_FPWRSTS_COUNT];
>         spinlock_t _lock;
>         unsigned long _lock_flags;
> -       const u8 pwrstctrl_offs;
> -       const u8 pwrstst_offs;
>         const u32 logicretstate_mask;
>         const u32 mem_on_mask[PWRDM_MAX_MEM_BANKS];
>         const u32 mem_ret_mask[PWRDM_MAX_MEM_BANKS];
>         const u32 mem_pwrst_mask[PWRDM_MAX_MEM_BANKS];
>         const u32 mem_retst_mask[PWRDM_MAX_MEM_BANKS];
> +       const s32 wakeuplat[PWRDM_MAX_FUNC_PWRSTS];
> +       struct plist_head wakeuplat_plist_head;
>
>  #ifdef CONFIG_PM_DEBUG
>         s64 timer;
> @@ -204,6 +211,18 @@ struct powerdomain {
>  #endif
>  };
>
> +
> +/**
> + * struct pwrdm_wkup_constraints_entry - Linked list for the wake-up latency
> + * constraints
> + * @cookie: constraint identifier, used for tracking
> + * @node: plist node
> + */
> +struct pwrdm_wkup_constraints_entry {
> +       void                    *cookie;
> +       struct plist_node       node;
> +};
> +
>  /**
>   * struct pwrdm_ops - Arch specific function implementations
>   * @pwrdm_set_next_pwrst: Set the target power state for a pd
> @@ -295,6 +314,10 @@ extern void omap3xxx_powerdomains_init(void);
>  extern void am33xx_powerdomains_init(void);
>  extern void omap44xx_powerdomains_init(void);
>
> +int pwrdm_wakeuplat_update_constraint(struct powerdomain *pwrdm, void *cookie,
> +                                     long min_latency);
> +int pwrdm_wakeuplat_remove_constraint(struct powerdomain *pwrdm, void *cookie);
> +
>  extern struct pwrdm_ops omap2_pwrdm_operations;
>  extern struct pwrdm_ops omap3_pwrdm_operations;
>  extern struct pwrdm_ops am33xx_pwrdm_operations;
> --
> 1.7.10.4
>

Regards,
Jean

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

end of thread, other threads:[~2012-12-14 15:50 UTC | newest]

Thread overview: 28+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2012-09-18  8:52 [PATCH v9 0/8] PM QoS: implement the OMAP low level constraints management code Jean Pihet
2012-09-18  8:52 ` Jean Pihet
2012-09-18  8:52 ` [PATCH 1/8] ARM: OMAP2+: PM QoS: control the power domains next state from the constraints Jean Pihet
2012-09-18  8:52   ` Jean Pihet
2012-12-11  0:05   ` Paul Walmsley
2012-12-11  0:05     ` Paul Walmsley
2012-12-14 15:50     ` Jean Pihet
2012-12-14 15:50       ` Jean Pihet
2012-09-18  8:52 ` [PATCH 2/8] ARM: OMAP2+: hwmod: manage the wake-up latency constraints Jean Pihet
2012-09-18  8:52   ` Jean Pihet
2012-09-18  8:52 ` [PATCH 3/8] ARM: OMAP: omap_device: register to the per-device PM QoS framework Jean Pihet
2012-09-18  8:52   ` Jean Pihet
2012-09-18  8:52 ` [PATCH 4/8] ARM: OMAP3: cpuidle: next C-state decision depends on the PM QoS MPU and CORE constraints Jean Pihet
2012-09-18  8:52   ` Jean Pihet
2012-09-18  8:52 ` [PATCH 5/8] ARM: OMAP3: update cpuidle latency and threshold figures Jean Pihet
2012-09-18  8:52   ` Jean Pihet
2012-09-18  8:52 ` [PATCH 6/8] ARM: OMAP3: powerdomain data: add wake-up latency figures Jean Pihet
2012-09-18  8:52   ` Jean Pihet
2012-09-18  8:52 ` [PATCH 7/8] ARM: OMAP: convert I2C driver to PM QoS for latency constraints Jean Pihet
2012-09-18  8:52   ` Jean Pihet
2012-09-18  8:53   ` Felipe Balbi
2012-09-18  8:53     ` Felipe Balbi
2012-09-19 18:11   ` Shubhrajyoti
2012-09-19 18:11     ` Shubhrajyoti
2012-09-19 18:34     ` Jean Pihet
2012-09-19 18:34       ` Jean Pihet
2012-09-18  8:52 ` [PATCH 8/8] ARM: OMAP: PM: remove the latency related functions from the API Jean Pihet
2012-09-18  8:52   ` Jean Pihet

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.