Skip to contentSkip to conversation

OMAP: add PM CONSTRAINTS framework

PATCH Superseded Jean Pihet 2 participants 15 messages Mar 10, 2011 – Mar 18, 2011

Subsystems (5)

v2 of 4 Superseded Latest is v4 →

lore
Share & tools
lore

Jean Pihet

author
#0 lore raw

v2 cover OMAP: add PM CONSTRAINTS framework

Implement OMAP PM layer omap_pm_set_max_dev_wakeup_lat API by creating a unified API which calls omap_device_set_dev_constraint for all classes of constraints (devices wake-up latency, devices throughput...).
The implementation of the constraints framework is at the omap_device
level: management and storage of the constraints, dispatching of the constraints to the appropriate low level layers.

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

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

For the devices wake-up latency constraints, the power domains get the next power state programmed directly in the registers via pwrdm_wakeuplat_update_pwrst.

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

The bus throughput constraints framework is supported by still is a no-op for the moment.

Based on Vibhore's original patch and completely reworked using omap_device, omap_hwmod and PM QOS frameworks.
Cc: Vibhore Vardhan <[email protected]>

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

Based on khilman's pm-core branch

v2:
. updated after review comments from Kevin and Paul,
. initial patch split according to the layering,
. added support for ascending or descending order of constraints values, . added throughput constraints to the framework, although the tput implementation
currently is a no-op,

To Do:
. refine the wake-up latency figures by defining the conditions of measurements and by
measuring on actual hardware,
. define the effect of power domains states combinations,
. add the support for the INACTIVE power state,
. move the power domains state initialization from pwrdms_setup to an early init call, . debug: add some control knobs for the wake-up latencies in /sysfs,
. add a user-space API to request and remove constraints.

Jean Pihet (7):
OMAP PM: create a PM layer plugin for per-device constraints
OMAP: PM CONSTRAINTS: add an enum for the classes of constraint
OMAP: PM CONSTRAINTS: implement wake-up latency constraints
OMAP: PM CONSTRAINTS: implement the constraints management code
OMAP: PM CONSTRAINTS: add a power domains state update function in
hwmod
OMAP: PM CONSTRAINTS: control power domains next state
OMAP: PM CONSTRAINTS: add power domains wake-up latency figures

 arch/arm/mach-omap2/omap_hwmod.c              |   29 ++-
 arch/arm/mach-omap2/powerdomain.c             |  100 +++++++
 arch/arm/mach-omap2/powerdomain.h             |   24 ++-
 arch/arm/mach-omap2/powerdomains3xxx_data.c   |   63 +++++
 arch/arm/plat-omap/Kconfig                    |    7 +
 arch/arm/plat-omap/Makefile                   |    1 +
 arch/arm/plat-omap/include/plat/omap-pm.h     |    4 +
 arch/arm/plat-omap/include/plat/omap_device.h |   14 +
 arch/arm/plat-omap/include/plat/omap_hwmod.h  |    1 +
 arch/arm/plat-omap/omap-pm-constraints.c      |  371 +++++++++++++++++++++++++
 arch/arm/plat-omap/omap_device.c              |  202 ++++++++++++++
 11 files changed, 813 insertions(+), 3 deletions(-)
 create mode 100644 arch/arm/plat-omap/omap-pm-constraints.c
Signature · 1 line
1.7.2.3
author
#1 lore raw

PATCH v2 1/7 OMAP PM: create a PM layer plugin for per-device constraints

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

Based on the original patch from Vishwanath,
cf. https://patchwork.kernel.org/patch/327312/

Cc Vishwanath BS <[email protected]>
---
 arch/arm/plat-omap/Kconfig               |    7 +
 arch/arm/plat-omap/Makefile              |    1 +
 arch/arm/plat-omap/omap-pm-constraints.c |  363 ++++++++++++++++++++++++++++++
 3 files changed, 371 insertions(+), 0 deletions(-)
 create mode 100644 arch/arm/plat-omap/omap-pm-constraints.c
arch/arm/plat-omap/Kconfig +7 -0
index b6333ae..22199da 100644
@@ -215,6 +215,13 @@ config OMAP_PM_NONE
 config OMAP_PM_NOOP
 	bool "No-op/debug PM layer"
 
+config OMAP_PM_CONSTRAINTS
+	depends on PM
+	bool "Per device constraints"
+	help
+	  Select this option to enable the PM layer plugin for
+	  the per-device constraints support
+
 endchoice
 
 endmenu
arch/arm/plat-omap/Makefile +1 -0
index a4a1285..a293367 100644
@@ -32,3 +32,4 @@ obj-y += $(i2c-omap-m) $(i2c-omap-y)
 obj-$(CONFIG_OMAP_MBOX_FWK) += mailbox.o
 
 obj-$(CONFIG_OMAP_PM_NOOP) += omap-pm-noop.o
+obj-$(CONFIG_OMAP_PM_CONSTRAINTS) += omap-pm-constraints.o
arch/arm/plat-omap/omap-pm-constraints.c new file +363 -0

363 lines not on this page · raw message

Signature · 1 line
1.7.2.3
author
#2 lore raw

PATCH v2 2/7 OMAP: PM CONSTRAINTS: add an enum for the classes of constraint

In reply to Jean Pihet

Defined values in the enum:
- OMAP_PM_CONSTRAINT_WKUP_LAT
- OMAP_PM_CONSTRAINT_THROUGHPUT

More classes can be added later if needed.

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

---
 arch/arm/plat-omap/include/plat/omap-pm.h |    4 ++++
 1 files changed, 4 insertions(+), 0 deletions(-)
arch/arm/plat-omap/include/plat/omap-pm.h +4 -0
index c0a7520..5f19cb2 100644
@@ -70,6 +70,10 @@ void omap_pm_if_exit(void);
  * Device-driver-originated constraints (via board-*.c files, platform_data)
  */
 
+enum omap_pm_constraint_class {
+	OMAP_PM_CONSTRAINT_WKUP_LAT,
+	OMAP_PM_CONSTRAINT_THROUGHPUT
+};
 
 /**
  * omap_pm_set_max_mpu_wakeup_lat - set the maximum MPU wakeup latency
Signature · 1 line
1.7.2.3
author
#3 lore raw

PATCH v2 3/7 OMAP: PM CONSTRAINTS: implement wake-up latency constraints

In reply to Jean Pihet

Implement the wake-up latency constraints using an internal unified function _set_dev_constraint at OMAP PM level, which calls the corresponding function at omap device level.

The actual constraints management code is at the omap device level.

Note: the bus throughput function is implemented but currently is a no-op.

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

---
 arch/arm/plat-omap/omap-pm-constraints.c |  174 ++++++++++++++++--------------
 1 files changed, 91 insertions(+), 83 deletions(-)
arch/arm/plat-omap/omap-pm-constraints.c +91 -83
index c8b4e4c..c6735da 100644
@@ -24,6 +24,7 @@
 /* Interface documentation is in mach/omap-pm.h */
 #include <plat/omap-pm.h>
 #include <plat/omap_device.h>
+#include <plat/common.h>
 
 static bool off_mode_enabled;
 static u32 dummy_context_loss_counter;
@@ -32,119 +33,126 @@ static u32 dummy_context_loss_counter;
  * Device-driver-originated constraints (via board-*.c files)
  */
 
-int omap_pm_set_max_mpu_wakeup_lat(struct device *dev, long t)
+/*
+ * Generic function to omap_device layer for the constraints API.
+ */
+static int _set_dev_constraint(enum omap_pm_constraint_class class,
+			       struct device *req_dev, struct device *dev,
+			       long t)
 {
-	if (!dev || t < -1) {
+	int ret = 0;
+
+	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 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.
-	 */
+	/* Try to catch non omap_device for dev */
+	if (dev->parent == &omap_device_parent) {
+		if (t == -1)
+			pr_debug("OMAP PM: remove constraint of class %d "
+				 "from req_dev %s on dev %s\n",
+				 class, dev_name(req_dev), dev_name(dev));
+		else
+			pr_debug("OMAP PM: add constraint of class %d "
+				 "from req_dev %s on dev %s, t = %ld\n",
+				 class, dev_name(req_dev), dev_name(dev), t);
+
+		/* Call the omap_device API */
+		ret = omap_device_set_dev_constraint(class, req_dev, dev, t);
+	} else {
+		pr_err("OMAP-PM set_wakeup_lat: Error: platform device "
+		       "not valid\n");
+		return -EINVAL;
+	}
 
-	return 0;
+	return ret;
 }
 
+/*
+ * omap_pm_set_min_bus_tput - set/release bus throughput constraints
+ */
 int omap_pm_set_min_bus_tput(struct device *dev, u8 agent_id, unsigned long r)
 {
+	long t;
+	struct device *req_dev = NULL;
+
 	if (!dev || (agent_id != OCP_INITIATOR_AGENT &&
 	    agent_id != OCP_TARGET_AGENT)) {
 		WARN(1, "OMAP PM: %s: invalid parameter(s)", __func__);
 		return -EINVAL;
 	};
 
+	/*
+	 * This code calls the generic omap_device API function
+	 * _set_dev_constraint with the class OMAP_PM_CONSTRAINT_THROUGHPUT.
+	 * _set_dev_constraint should manage the constraints lists and call
+	 * the appropriate low level code that models the interconnect,
+	 * computes the required clock frequency, converts that to a VDD2
+	 * OPP ID and sets the VDD2 OPP appropriately.
+	 */
+
+	/*
+	 * A value of r == 0 removes the constraint. Convert it to the
+	 * generic _set_dev_constraint convention (-1 for constraint removal)
+	 */
 	if (r == 0)
-		pr_debug("OMAP PM: remove min bus tput constraint: "
-			 "dev %s for agent_id %d\n", dev_name(dev), agent_id);
+		t = -1;
 	else
-		pr_debug("OMAP PM: add min bus tput constraint: "
-			 "dev %s for agent_id %d: rate %ld KiB\n",
-			 dev_name(dev), agent_id, r);
+		t = r;
 
 	/*
-	 * This code should model the interconnect and compute the
-	 * required clock frequency, convert that to a VDD2 OPP ID, then
-	 * set the VDD2 OPP appropriately.
-	 *
-	 * TI CDP code can call constraint_set here on the VDD2 OPP.
+	 * Assign the device for L3 or L4 interconnect to req_dev,
+	 * based on the value of agent_id
 	 */
+	switch (agent_id) {
+	case OCP_INITIATOR_AGENT:
+		req_dev = omap2_get_l3_device();
+		break;
+	case OCP_TARGET_AGENT:
+		/* Fixme: need the device for L4 interconnect */
+		break;
+	}
 
-	return 0;
+	return _set_dev_constraint(OMAP_PM_CONSTRAINT_THROUGHPUT,
+				   req_dev, dev, t);
 }
 
+/*
+ * omap_pm_set_max_dev_wakeup_lat - set/release devices wake-up latency
+ * constraints
+ */
 int omap_pm_set_max_dev_wakeup_lat(struct device *req_dev, struct device *dev,
 				   long t)
 {
-	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;
+	return _set_dev_constraint(OMAP_PM_CONSTRAINT_WKUP_LAT, req_dev,
+				   dev, t);
 }
 
-int omap_pm_set_max_sdma_lat(struct device *dev, long t)
+/*
+ * omap_pm_set_max_mpu_wakeup_lat - set/release MPU wake-up latency
+ * constraints
+ *
+ * Maps to _set_dev_constraint with OMAP_PM_CONSTRAINT_WKUP_LAT
+ * as constraint class and the MPU device as constraints target.
+ */
+int omap_pm_set_max_mpu_wakeup_lat(struct device *req_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 _set_dev_constraint(OMAP_PM_CONSTRAINT_WKUP_LAT, req_dev,
+				   omap2_get_mpuss_device(), t);
+}
 
-	return 0;
+/*
+ * omap_pm_set_max_sdma_lat - set/release SDMA start latency
+ * constraints
+ *
+ * Currently maps to _set_dev_constraint with OMAP_PM_CONSTRAINT_WKUP_LAT
+ * as constraint class and the L3 device as constraints target.
+ */
+int omap_pm_set_max_sdma_lat(struct device *req_dev, long t)
+{
+	return _set_dev_constraint(OMAP_PM_CONSTRAINT_WKUP_LAT, req_dev,
+				   omap2_get_l3_device(), t);
 }
 
 int omap_pm_set_min_clk_rate(struct device *dev, struct clk *c, long r)
Signature · 1 line
1.7.2.3
#4 lore raw

PATCH v2 3/7 OMAP: PM CONSTRAINTS: implement wake-up latency constraints

Quoted 58 lines · Jean Pihet

Implement the wake-up latency constraints using an internal unified function _set_dev_constraint at OMAP PM level, which calls the corresponding function at omap device level.

The actual constraints management code is at the omap device level.

Note: the bus throughput function is implemented but currently is a no-op.

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

---
 arch/arm/plat-omap/omap-pm-constraints.c |  174 ++++++++++++++++--------------
 1 files changed, 91 insertions(+), 83 deletions(-)
arch/arm/plat-omap/omap-pm-constraints.c +11 -11
index c8b4e4c..c6735da 100644
@@ -24,6 +24,7 @@
 /* Interface documentation is in mach/omap-pm.h */
 #include <plat/omap-pm.h>
 #include <plat/omap_device.h>
+#include <plat/common.h>
 
 static bool off_mode_enabled;
 static u32 dummy_context_loss_counter;
@@ -32,119 +33,126 @@ static u32 dummy_context_loss_counter;
  * Device-driver-originated constraints (via board-*.c files)
  */
 
-int omap_pm_set_max_mpu_wakeup_lat(struct device *dev, long t)
+/*
+ * Generic function to omap_device layer for the constraints API.
+ */
+static int _set_dev_constraint(enum omap_pm_constraint_class class,
+			       struct device *req_dev, struct device *dev,
+			       long t)
 {
-	if (!dev || t < -1) {
+	int ret = 0;
+
+	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 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);
+	}
 
-	/*

arch/arm/plat-omap/omap-pm-constraints.c static u32 dummy_context_loss_counter; · line 50

Quoted 10 lines
arch/arm/plat-omap/omap-pm-constraints.c +1 -9
-	 * 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.
-	 */
+	/* Try to catch non omap_device for dev */

comment should be 'only valid for omap_devices'

arch/arm/plat-omap/omap-pm-constraints.c static u32 dummy_context_loss_counter; · line 61

Quoted 11 lines
arch/arm/plat-omap/omap-pm-constraints.c +11 -0
+	if (dev->parent == &omap_device_parent) {
+		if (t == -1)
+			pr_debug("OMAP PM: remove constraint of class %d "
+				 "from req_dev %s on dev %s\n",
+				 class, dev_name(req_dev), dev_name(dev));
+		else
+			pr_debug("OMAP PM: add constraint of class %d "
+				 "from req_dev %s on dev %s, t = %ld\n",
+				 class, dev_name(req_dev), dev_name(dev), t);
+
+		/* Call the omap_device API */

comment not needed

arch/arm/plat-omap/omap-pm-constraints.c static u32 dummy_context_loss_counter; · line 62

Quoted 1 line
arch/arm/plat-omap/omap-pm-constraints.c +1 -0
+		ret = omap_device_set_dev_constraint(class, req_dev, dev, t);

Calling a function which doesn't yet exist.

Patches should be ordered such that the kernel still compiles after each patch.

arch/arm/plat-omap/omap-pm-constraints.c static u32 dummy_context_loss_counter; · line 65

Quoted 3 lines
arch/arm/plat-omap/omap-pm-constraints.c +3 -0
+	} else {
+		pr_err("OMAP-PM set_wakeup_lat: Error: platform device "
+		       "not valid\n");

comment should be 'Error: not an omap_device'.

arch/arm/plat-omap/omap-pm-constraints.c static u32 dummy_context_loss_counter; · line 70

Quoted 6 lines
arch/arm/plat-omap/omap-pm-constraints.c +3 -1
+		return -EINVAL;
+	}
 
-	return 0;
+	return ret;
 }

Kevin

author
#5 lore raw

PATCH v2 4/7 OMAP: PM CONSTRAINTS: implement the constraints management code

In reply to Jean Pihet

The code at omap device level manages the constraints: storage, tracking of requesters and dispatching to the low level code (e.g. powerdomain for the wake-up latency constraints).

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

---
 arch/arm/plat-omap/include/plat/omap_device.h |   14 ++
 arch/arm/plat-omap/omap_device.c              |  202 +++++++++++++++++++++++++
 2 files changed, 216 insertions(+), 0 deletions(-)
arch/arm/plat-omap/include/plat/omap_device.h +14 -0
index e4c349f..d4766c4 100644
@@ -32,9 +32,11 @@
 #define __ARCH_ARM_PLAT_OMAP_INCLUDE_MACH_OMAP_DEVICE_H
 
 #include <linux/kernel.h>
+#include <linux/plist.h>
 #include <linux/platform_device.h>
 
 #include <plat/omap_hwmod.h>
+#include <plat/omap-pm.h>
 
 extern struct device omap_device_parent;
 
@@ -73,6 +75,15 @@ struct omap_device {
 	s8				pm_lat_level;
 	u8				hwmods_cnt;
 	u8				_state;
+
+};
+
+/* Linked list for the devices constraints entries */
+struct omap_device_constraints_entry {
+	struct device			*req_dev;
+	void				*target;
+	unsigned long			constraint_value;
+	struct plist_node		node;
 };
 
 /* Device driver interface (call via platform_data fn ptrs) */
@@ -107,6 +118,9 @@ void __iomem *omap_device_get_rt_va(struct omap_device *od);
 int omap_device_align_pm_lat(struct platform_device *pdev,
 			     u32 new_wakeup_lat_limit);
 struct powerdomain *omap_device_get_pwrdm(struct omap_device *od);
+int omap_device_set_dev_constraint(enum omap_pm_constraint_class class,
+				   struct device *req_dev,
+				   struct device *dev, long t);
 u32 omap_device_get_context_loss_count(struct platform_device *pdev);
 
 /* Other */
arch/arm/plat-omap/omap_device.c +202 -0

218 lines not on this page · raw message

Signature · 1 line
1.7.2.3
#6 lore raw

PATCH v2 4/7 OMAP: PM CONSTRAINTS: implement the constraints management code

Quoted 8 lines · Jean Pihet

The code at omap device level manages the constraints: storage, tracking of requesters and dispatching to the low level code (e.g. powerdomain for the wake-up latency constraints).

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

Subject prefix should be: OMAP2+: omap_device: ...

Quoted 23 lines
---
 arch/arm/plat-omap/include/plat/omap_device.h |   14 ++
 arch/arm/plat-omap/omap_device.c              |  202 +++++++++++++++++++++++++
 2 files changed, 216 insertions(+), 0 deletions(-)
arch/arm/plat-omap/include/plat/omap_device.h +2 -0
index e4c349f..d4766c4 100644
@@ -32,9 +32,11 @@
 #define __ARCH_ARM_PLAT_OMAP_INCLUDE_MACH_OMAP_DEVICE_H
 
 #include <linux/kernel.h>
+#include <linux/plist.h>
 #include <linux/platform_device.h>
 
 #include <plat/omap_hwmod.h>
+#include <plat/omap-pm.h>
 
 extern struct device omap_device_parent;
 
@@ -73,6 +75,15 @@ struct omap_device {
 	s8				pm_lat_level;

arch/arm/plat-omap/include/plat/omap_device.h struct omap_device { · line 85

Quoted 10 lines
arch/arm/plat-omap/include/plat/omap_device.h +8 -0
 	u8				hwmods_cnt;
 	u8				_state;
+
+};
+
+/* Linked list for the devices constraints entries */
+struct omap_device_constraints_entry {
+	struct device			*req_dev;
+	void				*target;
+	unsigned long			constraint_value;

constratint_ prefix not needed

Also, should this be 'long' (instead of unsigned long' to match the value from the API below?

Quoted 52 lines
arch/arm/plat-omap/include/plat/omap_device.h +4 -0
+	struct plist_node		node;
 };
 
 /* Device driver interface (call via platform_data fn ptrs) */
@@ -107,6 +118,9 @@ void __iomem *omap_device_get_rt_va(struct omap_device *od);
 int omap_device_align_pm_lat(struct platform_device *pdev,
 			     u32 new_wakeup_lat_limit);
 struct powerdomain *omap_device_get_pwrdm(struct omap_device *od);
+int omap_device_set_dev_constraint(enum omap_pm_constraint_class class,
+				   struct device *req_dev,
+				   struct device *dev, long t);
 u32 omap_device_get_context_loss_count(struct platform_device *pdev);
 
 /* Other */
arch/arm/plat-omap/omap_device.c +30 -0
index 9bbda9a..9199d3e 100644
@@ -292,10 +292,205 @@ static void _add_optional_clock_clkdev(struct omap_device *od,
 	}
 }
 
+/* plist that stores the devices wake-up latency constraints */
+static struct plist_head _wkup_lat_constraints_list;
+/* Spinlock that protects the constraints lists */
+static spinlock_t _constraints_lock;
+/* Mutex that protects the constraints lists ops */
+static struct mutex _constraints_mutex;
+
+/*
+ * _store_constraint: add/update/remove a constraint from a plist
+ *
+ * @constraints_list: plist to use
+ * @req_dev: constraint requester, used to track the requests
+ * @target: target which the constraint applies to (e.g. power domain ID or
+ *  ptr for wake-up latency constraints)
+ * @value: constraint value. The plist is sorted by the value. -1 remove the
+ *  constraint from the list
+ * @ascending: return the lowest constraint value if set to 1, return the
+ *  highest value if not.
+ *
+ * Returns the strongest constraint value for the given target, 0 in the
+ * case there is no constraint on the given target or a negative value in
+ * case of error.
+ * The caller must check the validity of the parameters.
+ */
+static long _store_constraint(struct plist_head *constraints_list,
+			      struct device *req_dev, void *target,
+			      long value, int ascending)
+{
+	struct omap_device_constraints_entry *user;
+	int found = 0, ret = 0;

arch/arm/plat-omap/omap_device.c static void _add_optional_clock_clkdev(struct omap_device *od, · line 334

Quoted 10 lines
arch/arm/plat-omap/omap_device.c +10 -0
+
+	mutex_lock(&_constraints_mutex);
+
+	/* Check if the constraint requester is already in the list */
+	plist_for_each_entry(user, constraints_list, node) {
+		if (user->req_dev == req_dev) {
+			found = 1;
+			break;
+		}
+	}

You should probably release the mutex here so it's not held over the kzalloc (which can sleep.)

Minor: I'm not a fan of this common 'found = 1' technique. Rather, use a temporary list iterator, e.g:

	struct omap_device_constraints_entry *user = NULL, *tmp_user;

	plist_for_each_entry(tmp_user, constraints_list, node) {
		if (user->req_dev == req_dev) {
			user = tmp_user;
			break;
		}
	}

Then, rather than checking 'found' below, check 'user'.

Quoted 51 lines
arch/arm/plat-omap/omap_device.c +51 -0
+	if (value >= 0) {
+		/* Add new entry to the list or update existing request */
+		if (found &&
+		    user->constraint_value == value &&
+		    user->target == target) {
+			goto exit_ok;
+		} else if (!found) {
+			user = kzalloc(
+				sizeof(struct omap_device_constraints_entry),
+				GFP_KERNEL);
+			if (!user) {
+				pr_err("%s: FATAL ERROR: kzalloc failed\n",
+				       __func__);
+				ret = -ENOMEM;
+				goto exit_error;
+			}
+			user->req_dev = req_dev;
+		} else {
+			plist_del(&user->node, constraints_list);
+		}
+
+		plist_node_init(&user->node, value);
+		plist_add(&user->node, constraints_list);
+		user->node.prio = user->constraint_value = value;
+		user->target = target;
+	} else {
+		/* Remove the constraint from the list */
+		if (!found) {
+			pr_err("%s: Error: no prior constraint to release\n",
+			       __func__);
+			ret = -EINVAL;
+			goto exit_error;
+		}
+
+		plist_del(&user->node, constraints_list);
+		kfree(user);
+	}
+
+exit_ok:
+	/* Find the strongest constraint for the given target */
+	ret = 0;
+	if (ascending) {
+		list_for_each_entry(user, &(constraints_list)->node_list,
+				    node.plist.node_list) {
+			/* Find the lowest (i.e. first) value for the target */
+			if (user->target == target) {
+				ret = user->constraint_value;
+				break;
+			}
+		}
+	} else {

arch/arm/plat-omap/omap_device.c static void _add_optional_clock_clkdev(struct omap_device *od, · line 396

Quoted 10 lines
arch/arm/plat-omap/omap_device.c +10 -0
+		list_for_each_entry_reverse(user,
+					    &(constraints_list)->node_list,
+					    node.plist.node_list) {
+			/* Find the highest (i.e. last) value for the target */
+			if (user->target == target) {
+				ret = user->constraint_value;
+				break;
+			}
+		}
+	}

Hmm, why can't you use plist_first() and plist_last() here?

Quoted 29 lines
arch/arm/plat-omap/omap_device.c +25 -0
+exit_error:
+	mutex_unlock(&_constraints_mutex);
+
+	return ret;
+}
 
 /* Public functions for use by core code */
 
 /**
+ * omap_device_set_dev_constraint - set/release a device constraint
+ * @class: constraint class
+ * @req_dev: constraint requester, used for tracking the constraints
+ * @dev: device to apply the constraint to. Must have an associated omap_device
+ * @t: constraint value. A value of -1 removes the constraint.
+ *
+ * Using the primary hwmod, set/release a device constraint for the dev
+ * device, requested by the req_dev device. Depending of the constraint class
+ * this code calls the appropriate low level code, e.g. power domain for
+ * the wake-up latency constraints.
+ *
+ * If any hwmods exist for the omap_device assoiated with @dev,
+ * set/release the constraint for the corresponding hwmods, otherwise return
+ * -EINVAL.
+ */
+int omap_device_set_dev_constraint(enum omap_pm_constraint_class class,
+				   struct device *req_dev,
+				   struct device *dev, long t)
+{
+	struct omap_device *od;

arch/arm/plat-omap/omap_device.c static void _add_optional_clock_clkdev(struct omap_device *od, · line 436

Quoted 10 lines
arch/arm/plat-omap/omap_device.c +10 -0
+	struct omap_hwmod *oh;
+	struct platform_device *pdev;
+	struct powerdomain *pwrdm = NULL;
+	u32 ret = -EINVAL;
+
+	/* Look for the platform device for dev */
+	pdev = to_platform_device(dev);
+
+	/* Try to catch non platform devices. */
+	if (pdev->name == NULL) {

This should check for a valid omap_device, not platform_device.

Quoted 9 lines
arch/arm/plat-omap/omap_device.c +9 -0
+		pr_err("%s: Error: platform device for device %s not valid\n",
+		       __func__, dev_name(dev));
+		return -EINVAL;
+	}
+
+	/* Find the associated omap_device for dev */
+	od = _find_by_pdev(pdev);
+	if (!(od->hwmods_cnt)) {
+		pr_err("%s: Error: No hwmod for device %s\n",

arch/arm/plat-omap/omap_device.c static void _add_optional_clock_clkdev(struct omap_device *od, · line 455

Quoted 10 lines
arch/arm/plat-omap/omap_device.c +10 -0
+		       __func__, dev_name(dev));
+		return -EINVAL;
+	}
+
+	/* Find the associated omap_hwmod for dev */
+	oh = od->hwmods[0];
+
+	switch (class) {
+	case OMAP_PM_CONSTRAINT_WKUP_LAT:
+		/* Find the pwrdm associated to dev */

comment doesn't add anything to code

Quoted 8 lines
arch/arm/plat-omap/omap_device.c +8 -0
+		pwrdm = omap_device_get_pwrdm(od);
+		if (!pwrdm) {
+			pr_err("%s: Error: No pwrdm for device %s\n",
+			       __func__, dev_name(dev));
+			ret = -EINVAL;
+			break;
+		}
+

arch/arm/plat-omap/omap_device.c static void _add_optional_clock_clkdev(struct omap_device *od, · line 473

Quoted 10 lines
arch/arm/plat-omap/omap_device.c +10 -0
+		/*
+		 * Store the constraint in the appropriate list and find the
+		 * strongest constraint for the given pwrdm
+		 */
+		ret = _store_constraint(&_wkup_lat_constraints_list,
+					req_dev, (void *) pwrdm, t, 1);
+
+		/* Apply the constraint to the corresponding pwrdm */
+		if (ret >= 0) {
+			ret = omap_hwmod_update_power_state(oh, ret);

This function doesn't exist yet, so the series up to here will not compile.

Quoted 27 lines
arch/arm/plat-omap/omap_device.c +20 -0
+		} else {
+			pr_err("%s: Error storing the constraint for device "
+			       "%s\n", __func__, dev_name(dev));
+		}
+
+		break;
+	case OMAP_PM_CONSTRAINT_THROUGHPUT:
+		WARN(1, "OMAP PM: %s: Bus throughput constraint class \
+		     not implemented\n", __func__);
+		ret = -EINVAL;
+		break;
+	default:
+		WARN(1, "OMAP PM: %s: invalid constraint class %d",
+		     __func__, class);
+	}
+
+	return ret;
+}
+
+/**
  * omap_device_get_context_loss_count - get lost context count
  * @od: struct omap_device *
  *
@@ -824,6 +1019,13 @@ struct device omap_device_parent = {
 
 static int __init omap_device_init(void)
 {

arch/arm/plat-omap/omap_device.c struct device omap_device_parent = { · line 1031

Quoted 10 lines
arch/arm/plat-omap/omap_device.c +7 -0
+	/* Initialize priority ordered list for wakeup latency constraint */
+	spin_lock_init(&_constraints_lock);
+	plist_head_init(&_wkup_lat_constraints_list, &_constraints_lock);
+
+	/* res_mutex protects res_list add and del ops */
+	mutex_init(&_constraints_mutex);
+
 	return device_register(&omap_device_parent);
 }
 core_initcall(omap_device_init);

Kevin

author
#7 lore raw

PATCH v2 4/7 OMAP: PM CONSTRAINTS: implement the constraints management code

Kevin,

Thanks for reviewing!
All comments below fixed in the code. The updated series is coming asap.

More comments inlined below.

Quoted 109 lines · Kevin Hilman
Quoted 8 lines · Jean Pihet

The code at omap device level manages the constraints: storage, tracking of requesters and dispatching to the low level code (e.g. powerdomain for the wake-up latency constraints).

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

Subject prefix should be: OMAP2+: omap_device: ...

Quoted 23 lines

---
?arch/arm/plat-omap/include/plat/omap_device.h | ? 14 ++ ?arch/arm/plat-omap/omap_device.c ? ? ? ? ? ? ?| ?202 +++++++++++++++++++++++++ ?2 files changed, 216 insertions(+), 0 deletions(-)

arch/arm/plat-omap/include/plat/omap_device.h +0 -0
index e4c349f..d4766c4 100644
@@ -32,9 +32,11 @@

?#define __ARCH_ARM_PLAT_OMAP_INCLUDE_MACH_OMAP_DEVICE_H

?#include <linux/kernel.h>
+#include <linux/plist.h>
?#include <linux/platform_device.h>

?#include <plat/omap_hwmod.h>
+#include <plat/omap-pm.h>

?extern struct device omap_device_parent;

arch/arm/plat-omap/include/plat/omap_device.h +0 -0
@@ -73,6 +75,15 @@ struct omap_device {
 ? ? ? s8 ? ? ? ? ? ? ? ? ? ? ? ? ? ? ?pm_lat_level;

arch/arm/plat-omap/include/plat/omap_device.h struct omap_device { · line 85

Quoted 10 lines
arch/arm/plat-omap/include/plat/omap_device.h +8 -0
 ? ? ? u8 ? ? ? ? ? ? ? ? ? ? ? ? ? ? ?hwmods_cnt;
 ? ? ? u8 ? ? ? ? ? ? ? ? ? ? ? ? ? ? ?_state;
+
+};
+
+/* Linked list for the devices constraints entries */
+struct omap_device_constraints_entry {
+ ? ? struct device ? ? ? ? ? ? ? ? ? *req_dev;
+ ? ? void ? ? ? ? ? ? ? ? ? ? ? ? ? ?*target;
+ ? ? unsigned long ? ? ? ? ? ? ? ? ? constraint_value;

constratint_ prefix not needed

Also, should this be 'long' (instead of unsigned long' to match the value from the API below?

Quoted 57 lines
arch/arm/plat-omap/include/plat/omap_device.h +1 -0
+ ? ? struct plist_node ? ? ? ? ? ? ? node;
 ?};
 

?/* Device driver interface (call via platform_data fn ptrs) */

arch/arm/plat-omap/include/plat/omap_device.h +3 -0
@@ -107,6 +118,9 @@ void __iomem *omap_device_get_rt_va(struct omap_device *od);
 ?int omap_device_align_pm_lat(struct platform_device *pdev,
 ? ? ? ? ? ? ? ? ? ? ? ? ? ?u32 new_wakeup_lat_limit);
 ?struct powerdomain *omap_device_get_pwrdm(struct omap_device *od);
+int omap_device_set_dev_constraint(enum omap_pm_constraint_class class,
+ ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ?struct device *req_dev,
+ ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ?struct device *dev, long t);
 ?u32 omap_device_get_context_loss_count(struct platform_device *pdev);
 

?/* Other */

arch/arm/plat-omap/omap_device.c +35 -0
index 9bbda9a..9199d3e 100644
@@ -292,10 +292,205 @@ static void _add_optional_clock_clkdev(struct omap_device *od,
 ? ? ? }
 ?}
 
+/* plist that stores the devices wake-up latency constraints */
+static struct plist_head _wkup_lat_constraints_list;
+/* Spinlock that protects the constraints lists */
+static spinlock_t _constraints_lock;
+/* Mutex that protects the constraints lists ops */
+static struct mutex _constraints_mutex;
+
+/*
+ * _store_constraint: add/update/remove a constraint from a plist
+ *
+ * @constraints_list: plist to use
+ * @req_dev: constraint requester, used to track the requests
+ * @target: target which the constraint applies to (e.g. power domain ID or
+ * ?ptr for wake-up latency constraints)
+ * @value: constraint value. The plist is sorted by the value. -1 remove the
+ * ?constraint from the list
+ * @ascending: return the lowest constraint value if set to 1, return the
+ * ?highest value if not.
+ *
+ * Returns the strongest constraint value for the given target, 0 in the
+ * case there is no constraint on the given target or a negative value in
+ * case of error.
+ * The caller must check the validity of the parameters.
+ */
+static long _store_constraint(struct plist_head *constraints_list,
+ ? ? ? ? ? ? ? ? ? ? ? ? ? struct device *req_dev, void *target,
+ ? ? ? ? ? ? ? ? ? ? ? ? ? long value, int ascending)
+{
+ ? ? struct omap_device_constraints_entry *user;
+ ? ? int found = 0, ret = 0;
+
+ ? ? mutex_lock(&_constraints_mutex);
+
+ ? ? /* Check if the constraint requester is already in the list */
+ ? ? plist_for_each_entry(user, constraints_list, node) {
Quoted 8 lines

arch/arm/plat-omap/omap_device.c static void _add_optional_clock_clkdev(struct omap_device *od, · line 334

Quoted 5 lines
arch/arm/plat-omap/omap_device.c +5 -0
+ ? ? ? ? ? ? if (user->req_dev == req_dev) {
+ ? ? ? ? ? ? ? ? ? ? found = 1;
+ ? ? ? ? ? ? ? ? ? ? break;
+ ? ? ? ? ? ? }
+ ? ? }

You should probably release the mutex here so it's not held over the kzalloc (which can sleep.)

The mutex is removed and only the spinlock is used to protect the list ops.

Quoted 6 lines

Minor: I'm not a fan of this common 'found = 1' technique. ?Rather, use a temporary list iterator, e.g:

? ? ? ?struct omap_device_constraints_entry *user = NULL, *tmp_user;

Quoted 8 lines

? ? ? ?plist_for_each_entry(tmp_user, constraints_list, node) { ? ? ? ? ? ? ? ?if (user->req_dev == req_dev) {
? ? ? ? ? ? ? ? ? ? ? ?user = tmp_user;
? ? ? ? ? ? ? ? ? ? ? ?break;
? ? ? ? ? ? ? ?}
? ? ? ?}

Then, rather than checking 'found' below, check 'user'.

That is indeed cleaner.

Quoted 56 lines
Quoted 56 lines
arch/arm/plat-omap/omap_device.c +55 -0
+ ? ? if (value >= 0) {
+ ? ? ? ? ? ? /* Add new entry to the list or update existing request */
+ ? ? ? ? ? ? if (found &&
+ ? ? ? ? ? ? ? ? user->constraint_value == value &&
+ ? ? ? ? ? ? ? ? user->target == target) {
+ ? ? ? ? ? ? ? ? ? ? goto exit_ok;
+ ? ? ? ? ? ? } else if (!found) {
+ ? ? ? ? ? ? ? ? ? ? user = kzalloc(
+ ? ? ? ? ? ? ? ? ? ? ? ? ? ? sizeof(struct omap_device_constraints_entry),
+ ? ? ? ? ? ? ? ? ? ? ? ? ? ? GFP_KERNEL);
+ ? ? ? ? ? ? ? ? ? ? if (!user) {
+ ? ? ? ? ? ? ? ? ? ? ? ? ? ? pr_err("%s: FATAL ERROR: kzalloc failed\n",
+ ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ?__func__);
+ ? ? ? ? ? ? ? ? ? ? ? ? ? ? ret = -ENOMEM;
+ ? ? ? ? ? ? ? ? ? ? ? ? ? ? goto exit_error;
+ ? ? ? ? ? ? ? ? ? ? }
+ ? ? ? ? ? ? ? ? ? ? user->req_dev = req_dev;
+ ? ? ? ? ? ? } else {
+ ? ? ? ? ? ? ? ? ? ? plist_del(&user->node, constraints_list);
+ ? ? ? ? ? ? }
+
+ ? ? ? ? ? ? plist_node_init(&user->node, value);
+ ? ? ? ? ? ? plist_add(&user->node, constraints_list);
+ ? ? ? ? ? ? user->node.prio = user->constraint_value = value;
+ ? ? ? ? ? ? user->target = target;
+ ? ? } else {
+ ? ? ? ? ? ? /* Remove the constraint from the list */
+ ? ? ? ? ? ? if (!found) {
+ ? ? ? ? ? ? ? ? ? ? pr_err("%s: Error: no prior constraint to release\n",
+ ? ? ? ? ? ? ? ? ? ? ? ? ? ?__func__);
+ ? ? ? ? ? ? ? ? ? ? ret = -EINVAL;
+ ? ? ? ? ? ? ? ? ? ? goto exit_error;
+ ? ? ? ? ? ? }
+
+ ? ? ? ? ? ? plist_del(&user->node, constraints_list);
+ ? ? ? ? ? ? kfree(user);
+ ? ? }
+
+exit_ok:
+ ? ? /* Find the strongest constraint for the given target */
+ ? ? ret = 0;
+ ? ? if (ascending) {
+ ? ? ? ? ? ? list_for_each_entry(user, &(constraints_list)->node_list,
+ ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? node.plist.node_list) {
+ ? ? ? ? ? ? ? ? ? ? /* Find the lowest (i.e. first) value for the target */
+ ? ? ? ? ? ? ? ? ? ? if (user->target == target) {
+ ? ? ? ? ? ? ? ? ? ? ? ? ? ? ret = user->constraint_value;
+ ? ? ? ? ? ? ? ? ? ? ? ? ? ? break;
+ ? ? ? ? ? ? ? ? ? ? }
+ ? ? ? ? ? ? }
+ ? ? } else {
+ ? ? ? ? ? ? list_for_each_entry_reverse(user,
+ ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? &(constraints_list)->node_list,
+ ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? node.plist.node_list) {
+ ? ? ? ? ? ? ? ? ? ? /* Find the highest (i.e. last) value for the target */
Quoted 8 lines

arch/arm/plat-omap/omap_device.c static void _add_optional_clock_clkdev(struct omap_device *od, · line 395

Quoted 6 lines
arch/arm/plat-omap/omap_device.c +6 -0
+ ? ? ? ? ? ? ? ? ? ? if (user->target == target) {
+ ? ? ? ? ? ? ? ? ? ? ? ? ? ? ret = user->constraint_value;
+ ? ? ? ? ? ? ? ? ? ? ? ? ? ? break;
+ ? ? ? ? ? ? ? ? ? ? }
+ ? ? ? ? ? ? }
+ ? ? }

Hmm, why can't you use plist_first() and plist_last() here?

Because the plist is sorted by the 'prio' field (which is 'value' in this code) and this function searches for the strongest constraint for a given target. So it is needed to iterate through the list in order to find the first (or last) constraint with the right target.

Quoted 34 lines
Quoted 34 lines
arch/arm/plat-omap/omap_device.c +5 -0
+exit_error:
+ ? ? mutex_unlock(&_constraints_mutex);
+
+ ? ? return ret;
+}
 

?/* Public functions for use by core code */

?/**
+ * omap_device_set_dev_constraint - set/release a device constraint
+ * @class: constraint class
+ * @req_dev: constraint requester, used for tracking the constraints
+ * @dev: device to apply the constraint to. Must have an associated omap_device
+ * @t: constraint value. A value of -1 removes the constraint.
+ *
+ * Using the primary hwmod, set/release a device constraint for the dev
+ * device, requested by the req_dev device. Depending of the constraint class
+ * this code calls the appropriate low level code, e.g. power domain for
+ * the wake-up latency constraints.
+ *
+ * If any hwmods exist for the omap_device assoiated with @dev,
+ * set/release the constraint for the corresponding hwmods, otherwise return
+ * -EINVAL.
+ */

arch/arm/plat-omap/omap_device.c +9 -0
+int omap_device_set_dev_constraint(enum omap_pm_constraint_class class,
+ ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ?struct device *req_dev,
+ ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ?struct device *dev, long t)
+{
+ ? ? struct omap_device *od;
+ ? ? struct omap_hwmod *oh;
+ ? ? struct platform_device *pdev;
+ ? ? struct powerdomain *pwrdm = NULL;
+ ? ? u32 ret = -EINVAL;
Quoted 8 lines

arch/arm/plat-omap/omap_device.c

Quoted 6 lines
arch/arm/plat-omap/omap_device.c +6 -0
+
+ ? ? /* Look for the platform device for dev */
+ ? ? pdev = to_platform_device(dev);
+
+ ? ? /* Try to catch non platform devices. */
+ ? ? if (pdev->name == NULL) {

This should check for a valid omap_device, not platform_device.

This check remains but the check below is changed, see below.

Quoted 13 lines
Quoted 13 lines

+ ? ? ? ? ? ? pr_err("%s: Error: platform device for device %s not valid\n",
+ ? ? ? ? ? ? ? ? ? ?__func__, dev_name(dev));
+ ? ? ? ? ? ? return -EINVAL;
+ ? ? }
+
+ ? ? /* Find the associated omap_device for dev */
+ ? ? od = _find_by_pdev(pdev);
+ ? ? if (!(od->hwmods_cnt)) {
+ ? ? ? ? ? ? pr_err("%s: Error: No hwmod for device %s\n",
+ ? ? ? ? ? ? ? ? ? ?__func__, dev_name(dev));
+ ? ? ? ? ? ? return -EINVAL;
+ ? ? }

Changed to:
+ if (!od || (od->hwmods_cnt != 1)) {
+ pr_err("%s: Error: No unique hwmod for device %s\n",
+ __func__, dev_name(dev));
+ return -EINVAL;
+ }

Quoted 57 lines

arch/arm/plat-omap/omap_device.c

Quoted 7 lines
arch/arm/plat-omap/omap_device.c +7 -0
+
+ ? ? /* Find the associated omap_hwmod for dev */
+ ? ? oh = od->hwmods[0];
+
+ ? ? switch (class) {
+ ? ? case OMAP_PM_CONSTRAINT_WKUP_LAT:
+ ? ? ? ? ? ? /* Find the pwrdm associated to dev */

comment doesn't add anything to code

Quoted 8 lines

+ ? ? ? ? ? ? pwrdm = omap_device_get_pwrdm(od);
+ ? ? ? ? ? ? if (!pwrdm) {
+ ? ? ? ? ? ? ? ? ? ? pr_err("%s: Error: No pwrdm for device %s\n",
+ ? ? ? ? ? ? ? ? ? ? ? ? ? ?__func__, dev_name(dev));
+ ? ? ? ? ? ? ? ? ? ? ret = -EINVAL;
+ ? ? ? ? ? ? ? ? ? ? break;
+ ? ? ? ? ? ? }

arch/arm/plat-omap/omap_device.c +1 -0
+

arch/arm/plat-omap/omap_device.c

Quoted 10 lines
arch/arm/plat-omap/omap_device.c +10 -0
+ ? ? ? ? ? ? /*
+ ? ? ? ? ? ? ?* Store the constraint in the appropriate list and find the
+ ? ? ? ? ? ? ?* strongest constraint for the given pwrdm
+ ? ? ? ? ? ? ?*/
+ ? ? ? ? ? ? ret = _store_constraint(&_wkup_lat_constraints_list,
+ ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? req_dev, (void *) pwrdm, t, 1);
+
+ ? ? ? ? ? ? /* Apply the constraint to the corresponding pwrdm */
+ ? ? ? ? ? ? if (ret >= 0) {
+ ? ? ? ? ? ? ? ? ? ? ret = omap_hwmod_update_power_state(oh, ret);

This function doesn't exist yet, so the series up to here will not compile.

Quoted 25 lines

+ ? ? ? ? ? ? } else {
+ ? ? ? ? ? ? ? ? ? ? pr_err("%s: Error storing the constraint for device "
+ ? ? ? ? ? ? ? ? ? ? ? ? ? ?"%s\n", __func__, dev_name(dev));
+ ? ? ? ? ? ? }

arch/arm/plat-omap/omap_device.c +16 -0
+
+ ? ? ? ? ? ? break;
+ ? ? case OMAP_PM_CONSTRAINT_THROUGHPUT:
+ ? ? ? ? ? ? WARN(1, "OMAP PM: %s: Bus throughput constraint class \
+ ? ? ? ? ? ? ? ? ?not implemented\n", __func__);
+ ? ? ? ? ? ? ret = -EINVAL;
+ ? ? ? ? ? ? break;
+ ? ? default:
+ ? ? ? ? ? ? WARN(1, "OMAP PM: %s: invalid constraint class %d",
+ ? ? ? ? ? ? ? ? ?__func__, class);
+ ? ? }
+
+ ? ? return ret;
+}
+
+/**

? * omap_device_get_context_loss_count - get lost context count ? * @od: struct omap_device *
? *

arch/arm/plat-omap/omap_device.c +0 -0
@@ -824,6 +1019,13 @@ struct device omap_device_parent = {
 
Quoted 15 lines

arch/arm/plat-omap/omap_device.c struct device omap_device_parent = { · line 1031

Quoted 12 lines
arch/arm/plat-omap/omap_device.c +7 -0
 ?static int __init omap_device_init(void)
 ?{
+ ? ? /* Initialize priority ordered list for wakeup latency constraint */
+ ? ? spin_lock_init(&_constraints_lock);
+ ? ? plist_head_init(&_wkup_lat_constraints_list, &_constraints_lock);
+
+ ? ? /* res_mutex protects res_list add and del ops */
+ ? ? mutex_init(&_constraints_mutex);
+
 ? ? ? return device_register(&omap_device_parent);
 ?}
 ?core_initcall(omap_device_init);

Kevin

Jean

#8 lore raw

PATCH v2 4/7 OMAP: PM CONSTRAINTS: implement the constraints management code

Jean Pihet <[email protected]> writes:

[...]

Quoted 24 lines
Quoted 24 lines
Quoted 23 lines

+exit_ok:
+ ? ? /* Find the strongest constraint for the given target */
+ ? ? ret = 0;
+ ? ? if (ascending) {
+ ? ? ? ? ? ? list_for_each_entry(user, &(constraints_list)->node_list,
+ ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? node.plist.node_list) {
+ ? ? ? ? ? ? ? ? ? ? /* Find the lowest (i.e. first) value for the target */
+ ? ? ? ? ? ? ? ? ? ? if (user->target == target) {
+ ? ? ? ? ? ? ? ? ? ? ? ? ? ? ret = user->constraint_value;
+ ? ? ? ? ? ? ? ? ? ? ? ? ? ? break;
+ ? ? ? ? ? ? ? ? ? ? }
+ ? ? ? ? ? ? }
+ ? ? } else {
+ ? ? ? ? ? ? list_for_each_entry_reverse(user,
+ ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? &(constraints_list)->node_list,
+ ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? node.plist.node_list) {
+ ? ? ? ? ? ? ? ? ? ? /* Find the highest (i.e. last) value for the target */
+ ? ? ? ? ? ? ? ? ? ? if (user->target == target) {
+ ? ? ? ? ? ? ? ? ? ? ? ? ? ? ret = user->constraint_value;
+ ? ? ? ? ? ? ? ? ? ? ? ? ? ? break;
+ ? ? ? ? ? ? ? ? ? ? }
+ ? ? ? ? ? ? }
+ ? ? }

Quoted 5 lines
Quoted 1 line

Hmm, why can't you use plist_first() and plist_last() here?

Because the plist is sorted by the 'prio' field (which is 'value' in this code) and this function searches for the strongest constraint for a given target. So it is needed to iterate through the list in order to find the first (or last) constraint with the right target.

Hmm, still confusing.

The main thing I don't get is why you need the 'target' field in the first place. You should just keep a list of constraints per target omap_device. IOW, currently your _wkup_lat_constraints_list is a global list, but instead it should be connected to the omap_device, and each omap_device would have a plist for each class.

Quoted 42 lines
Quoted 34 lines
Quoted 34 lines
arch/arm/plat-omap/omap_device.c +5 -0
+exit_error:
+ ? ? mutex_unlock(&_constraints_mutex);
+
+ ? ? return ret;
+}

?/* Public functions for use by core code */

?/**
+ * omap_device_set_dev_constraint - set/release a device constraint
+ * @class: constraint class
+ * @req_dev: constraint requester, used for tracking the constraints
+ * @dev: device to apply the constraint to. Must have an associated omap_device
+ * @t: constraint value. A value of -1 removes the constraint.
+ *
+ * Using the primary hwmod, set/release a device constraint for the dev
+ * device, requested by the req_dev device. Depending of the constraint class
+ * this code calls the appropriate low level code, e.g. power domain for
+ * the wake-up latency constraints.
+ *
+ * If any hwmods exist for the omap_device assoiated with @dev,
+ * set/release the constraint for the corresponding hwmods, otherwise return
+ * -EINVAL.
+ */

(quoted diff) +9 -0
+int omap_device_set_dev_constraint(enum omap_pm_constraint_class class,
+ ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ?struct device *req_dev,
+ ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ?struct device *dev, long t)
+{
+ ? ? struct omap_device *od;
+ ? ? struct omap_hwmod *oh;
+ ? ? struct platform_device *pdev;
+ ? ? struct powerdomain *pwrdm = NULL;
+ ? ? u32 ret = -EINVAL;
Quoted 8 lines

Review

Quoted 6 lines
(quoted diff) +6 -0
+
+ ? ? /* Look for the platform device for dev */
+ ? ? pdev = to_platform_device(dev);
+
+ ? ? /* Try to catch non platform devices. */
+ ? ? if (pdev->name == NULL) {

This should check for a valid omap_device, not platform_device.

Quoted 1 line

This check remains but the check below is changed, see below.

What I mean is this should check for omap_device_parent to see if the
struct device is an omap_device. IOW, you care whether or not it's an
omap_device, not whether or not it's a valid platform_device.

Kevin

author
#9 lore raw

PATCH v2 5/7 OMAP: PM CONSTRAINTS: add a power domains state update function in hwmod

In reply to Jean Pihet

Hwmod is queried from the omap device layer to change the power domains next power state. Hwmod retrieves the correct power domain and if it exists it calls the corresponding power domain function.

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

---
 arch/arm/mach-omap2/omap_hwmod.c             |   29 +++++++++++++++++++++++++-
 arch/arm/plat-omap/include/plat/omap_hwmod.h |    1 +
 2 files changed, 29 insertions(+), 1 deletions(-)
arch/arm/mach-omap2/omap_hwmod.c +28 -1
index cf8cc9b..8caf2c5 100644
@@ -142,6 +142,7 @@
 #include "powerdomain.h"
 #include <plat/clock.h>
 #include <plat/omap_hwmod.h>
+#include <plat/omap_device.h>
 #include <plat/prcm.h>
 
 #include "cm2xxx_3xxx.h"
@@ -2277,11 +2278,37 @@ ohsps_unlock:
 	return ret;
 }
 
+/*
+ * omap_hwmod_update_power_state - Update the power domain power state of
+ * @oh
+ *
+ * @oh: struct omap_hwmod* to which the requesting device belongs to.
+ * @min_latency: the allowed wake-up latency for the power domain of @oh.
+ *
+ * Finds the power domain next power state that fulfills the constraint.
+ * Applies the constraint to the power domain by calling
+ * pwrdm_wakeuplat_update_pwrst.
+ *
+ * Returns 0 upon success.
+ */
+int omap_hwmod_update_power_state(struct omap_hwmod *oh, long min_latency)
+{
+	struct powerdomain *pwrdm = omap_hwmod_get_pwrdm(oh);
+
+	if (!PTR_ERR(pwrdm)) {
+		pr_err("omap_hwmod: Error: could not find parent "
+			"powerdomain for %s\n", oh->name);
+		return -EINVAL;
+	}
+
+	return pwrdm_wakeuplat_update_pwrst(pwrdm, min_latency);
+}
+
 /**
  * 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
arch/arm/plat-omap/include/plat/omap_hwmod.h +1 -0
index 65bcad4..f27110e 100644
@@ -597,6 +597,7 @@ 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_update_power_state(struct omap_hwmod *oh, long min_latency);
 u32 omap_hwmod_get_context_loss_count(struct omap_hwmod *oh);
 
 /*
Signature · 1 line
1.7.2.3
#10 lore raw

PATCH v2 5/7 OMAP: PM CONSTRAINTS: add a power domains state update function in hwmod

Quoted 8 lines · Jean Pihet

Hwmod is queried from the omap device layer to change the power domains next power state. Hwmod retrieves the correct power domain and if it exists it calls the corresponding power domain function.

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

Subject prefix should be: OMAP2+: omap_hwmod: ...

Quoted 18 lines
---
 arch/arm/mach-omap2/omap_hwmod.c             |   29 +++++++++++++++++++++++++-
 arch/arm/plat-omap/include/plat/omap_hwmod.h |    1 +
 2 files changed, 29 insertions(+), 1 deletions(-)
arch/arm/mach-omap2/omap_hwmod.c +1 -0
index cf8cc9b..8caf2c5 100644
@@ -142,6 +142,7 @@
 #include "powerdomain.h"
 #include <plat/clock.h>
 #include <plat/omap_hwmod.h>
+#include <plat/omap_device.h>
 #include <plat/prcm.h>
 
 #include "cm2xxx_3xxx.h"

arch/arm/mach-omap2/omap_hwmod.c ohsps_unlock: · line 2282

Quoted 5 lines
arch/arm/mach-omap2/omap_hwmod.c +2 -0
@@ -2277,11 +2278,37 @@ ohsps_unlock:
 	return ret;
 }
 
+/*
+ * omap_hwmod_update_power_state - Update the power domain power state of

I think 'set_next_power_state' is a better name for this function.

arch/arm/mach-omap2/omap_hwmod.c ohsps_unlock: · line 2290

Quoted 8 lines
arch/arm/mach-omap2/omap_hwmod.c +8 -0
+ * @oh
+ *
+ * @oh: struct omap_hwmod* to which the requesting device belongs to.
+ * @min_latency: the allowed wake-up latency for the power domain of @oh.
+ *
+ * Finds the power domain next power state that fulfills the constraint.
+ * Applies the constraint to the power domain by calling
+ * pwrdm_wakeuplat_update_pwrst.

This needs to be updated as this function doesn't know anything about constraints.

arch/arm/mach-omap2/omap_hwmod.c ohsps_unlock: · line 2299

Quoted 8 lines
arch/arm/mach-omap2/omap_hwmod.c +8 -0
+ * Returns 0 upon success.
+ */
+int omap_hwmod_update_power_state(struct omap_hwmod *oh, long min_latency)
+{
+	struct powerdomain *pwrdm = omap_hwmod_get_pwrdm(oh);
+
+	if (!PTR_ERR(pwrdm)) {
+		pr_err("omap_hwmod: Error: could not find parent "

s/parent//

instead of "omap_hwmod:", use "%s" with __func__ so the function name is printed on error.

arch/arm/mach-omap2/omap_hwmod.c ohsps_unlock: · line 2304

Quoted 5 lines
arch/arm/mach-omap2/omap_hwmod.c +5 -0
+			"powerdomain for %s\n", oh->name);
+		return -EINVAL;
+	}
+
+	return pwrdm_wakeuplat_update_pwrst(pwrdm, min_latency);

Again, this function doesn't exist yet in the series.

Quoted 16 lines
arch/arm/mach-omap2/omap_hwmod.c +3 -1
+}
+
 /**
  * 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
arch/arm/plat-omap/include/plat/omap_hwmod.h +0 -0
index 65bcad4..f27110e 100644

arch/arm/plat-omap/include/plat/omap_hwmod.h int omap_hwmod_for_each_by_class(const char *classname, · line 603

Quoted 7 lines
arch/arm/plat-omap/include/plat/omap_hwmod.h +1 -0
@@ -597,6 +597,7 @@ 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_update_power_state(struct omap_hwmod *oh, long min_latency);
 u32 omap_hwmod_get_context_loss_count(struct omap_hwmod *oh);
 
 /*

Kevin

author
#11 lore raw

PATCH v2 6/7 OMAP: PM CONSTRAINTS: control power domains next state

In reply to Jean Pihet

When a wake-up constraint is requested or removed the omap device layer dispatches the updated strongest constraint value.
The power domains get the next power state programmed directly in the registers via pwrdm_wakeuplat_update_pwrst.

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

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

---
 arch/arm/mach-omap2/powerdomain.c |  100 +++++++++++++++++++++++++++++++++++++
 arch/arm/mach-omap2/powerdomain.h |   24 ++++++++-
 2 files changed, 122 insertions(+), 2 deletions(-)
arch/arm/mach-omap2/powerdomain.c +100 -0
index eaed0df..54ac955 100644
@@ -19,6 +19,7 @@
 #include <linux/list.h>
 #include <linux/errno.h>
 #include <linux/string.h>
+
 #include "cm2xxx_3xxx.h"
 #include "prcm44xx.h"
 #include "cm44xx.h"
@@ -176,6 +177,105 @@ static int _pwrdm_post_transition_cb(struct powerdomain *pwrdm, void *unused)
 	return 0;
 }
 
+/*
+ * Test if the current powerdomain is MPU or CORE
+ *
+ * This function is only used by pwrdm_wakeuplat_update_pwrst
+ * which applies the MPU and CORE power domains constraints using PM QOS.
+ *
+ * This will disappear when all power domains will be controlled directly
+ * from the wake-up latency constraints framework
+ */
+static inline int is_pwrdm_mpu_or_core(struct powerdomain *pwrdm)
+{
+	if ((pwrdm == pwrdm_lookup("mpu_pwrdm")) ||
+	    (pwrdm == pwrdm_lookup("core_pwrdm")))
+		return 1;
+	else
+		return 0;
+}
+
+/**
+ * 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 0 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.
+ *
+ * Returns 0 upon success.
+ */
+int pwrdm_wakeuplat_update_pwrst(struct powerdomain *pwrdm, long min_latency)
+{
+	int ret = 0, new_state = 0;
+
+	if (!pwrdm) {
+		WARN(1, "powerdomain: %s: invalid parameter(s)", __func__);
+		return -EINVAL;
+	}
+
+	/*
+	 * Apply constraints to MPU and CORE via the PM QOS API.
+	 * Every power domain struct has a pm_qos_request_list field
+	 */
+	if (is_pwrdm_mpu_or_core(pwrdm)) {
+		if (pm_qos_request_active(&(pwrdm->pm_qos_request)))
+			pm_qos_remove_request(&(pwrdm->pm_qos_request));
+
+		if (min_latency > 0)
+			pm_qos_add_request(&(pwrdm->pm_qos_request),
+					   PM_QOS_CPU_DMA_LATENCY,
+					   min_latency);
+
+		return 0;
+	}
+
+	/*
+	 * Apply constraints to pwrdm other than MPU and CORE by programming
+	 * the pwrdm next power state.
+	 */
+
+	/* Find power state with wakeup latency < minimum constraint */
+	for (new_state = 0x0; new_state < PWRDM_MAX_PWRSTS; new_state++) {
+		if (min_latency == 0 ||
+		    pwrdm->wakeup_lat[new_state] <= min_latency)
+			break;
+	}
+
+	switch (new_state) {
+	case PWRDM_FUNC_PWRST_OFF:
+		new_state = PWRDM_POWER_OFF;
+		break;
+	case PWRDM_FUNC_PWRST_OSWR:
+		pwrdm_set_logic_retst(pwrdm, PWRDM_POWER_OFF);
+		new_state = PWRDM_POWER_RET;
+		break;
+	case PWRDM_FUNC_PWRST_CSWR:
+		pwrdm_set_logic_retst(pwrdm, PWRDM_POWER_RET);
+		new_state = PWRDM_POWER_RET;
+		break;
+	case PWRDM_FUNC_PWRST_ON:
+		new_state = PWRDM_POWER_ON;
+		break;
+	default:
+		pr_warn("powerdomain: requested latency constraint not "
+			"supported %s set to ON state\n", pwrdm->name);
+		new_state = PWRDM_POWER_ON;
+		break;
+	}
+
+	if (pwrdm_read_pwrst(pwrdm) != new_state)
+		ret = omap_set_pwrdm_state(pwrdm, new_state);
+
+	pr_debug("powerdomain: %s pwrst: curr=%d, prev=%d next=%d "
+		 "min_latency=%ld, set_state=%d\n", pwrdm->name,
+		 pwrdm_read_pwrst(pwrdm), pwrdm_read_prev_pwrst(pwrdm),
+		 pwrdm_read_next_pwrst(pwrdm), min_latency, new_state);
+
+	return ret;
+}
+
 /* Public functions */
 
 /**
arch/arm/mach-omap2/powerdomain.h +22 -2
index 0b7a357..23b3408 100644
@@ -19,7 +19,10 @@
 
 #include <linux/types.h>
 #include <linux/list.h>
-
+#include <linux/plist.h>
+#include <linux/mutex.h>
+#include <linux/spinlock.h>
+#include <linux/pm_qos_params.h>
 #include <linux/atomic.h>
 
 #include <plat/cpu.h>
@@ -46,6 +49,15 @@
 
 #define PWRSTS_OFF_RET_ON	(PWRSTS_OFF_RET | (1 << PWRDM_POWER_ON))
 
+/* Powerdomain functional power states */
+#define PWRDM_FUNC_PWRST_OFF	0x0
+#define PWRDM_FUNC_PWRST_OSWR	0x1
+#define PWRDM_FUNC_PWRST_CSWR	0x2
+#define PWRDM_FUNC_PWRST_ON	0x3
+
+#define PWRDM_MAX_FUNC_PWRSTS	4
+
+#define UNSUP_STATE		-1
 
 /* Powerdomain flags */
 #define PWRDM_HAS_HDWR_SAR	(1 << 0) /* hardware save-and-restore support */
@@ -96,7 +108,11 @@ struct powerdomain;
  * @state_counter:
  * @timer:
  * @state_timer:
- *
+ * @wakeup_lat: wakeup latencies (in us) for possible powerdomain power states
+ * Note about the wakeup latencies ordering: the values must be sorted
+ *  in decremental order
+ * @pm_qos_request: PM QOS handle, only used to control the MPU and CORE power
+ *  domains states; to be removed when a generic solution is in place.
  * @prcm_partition possible values are defined in mach-omap2/prcm44xx.h.
  */
 struct powerdomain {
@@ -121,6 +137,8 @@ struct powerdomain {
 	s64 timer;
 	s64 state_timer[PWRDM_MAX_PWRSTS];
 #endif
+	const u32 wakeup_lat[PWRDM_MAX_FUNC_PWRSTS];
+	struct pm_qos_request_list pm_qos_request;
 };
 
 /**
@@ -210,6 +228,8 @@ int pwrdm_clkdm_state_switch(struct clockdomain *clkdm);
 int pwrdm_pre_transition(void);
 int pwrdm_post_transition(void);
 int pwrdm_set_lowpwrstchange(struct powerdomain *pwrdm);
+
+int pwrdm_wakeuplat_update_pwrst(struct powerdomain *pwrdm, long min_latency);
 u32 pwrdm_get_context_loss_count(struct powerdomain *pwrdm);
 
 extern void omap2xxx_powerdomains_init(void);
Signature · 1 line
1.7.2.3
#12 lore raw

PATCH v2 6/7 OMAP: PM CONSTRAINTS: control power domains next state

Quoted 10 lines · Jean Pihet

When a wake-up constraint is requested or removed the omap device layer dispatches the updated strongest constraint value.
The power domains get the next power state programmed directly in the registers via pwrdm_wakeuplat_update_pwrst.

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

Quoted 4 lines

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

Subject prefix should be: OMAP2+: powerdomain: ...

Quoted 28 lines
---
 arch/arm/mach-omap2/powerdomain.c |  100 +++++++++++++++++++++++++++++++++++++
 arch/arm/mach-omap2/powerdomain.h |   24 ++++++++-
 2 files changed, 122 insertions(+), 2 deletions(-)
arch/arm/mach-omap2/powerdomain.c +8 -0
index eaed0df..54ac955 100644
@@ -19,6 +19,7 @@
 #include <linux/list.h>
 #include <linux/errno.h>
 #include <linux/string.h>
+
 #include "cm2xxx_3xxx.h"
 #include "prcm44xx.h"
 #include "cm44xx.h"
@@ -176,6 +177,105 @@ static int _pwrdm_post_transition_cb(struct powerdomain *pwrdm, void *unused)
 	return 0;
 }
 
+/*
+ * Test if the current powerdomain is MPU or CORE
+ *
+ * This function is only used by pwrdm_wakeuplat_update_pwrst
+ * which applies the MPU and CORE power domains constraints using PM QOS.
+ *
+ * This will disappear when all power domains will be controlled directly

arch/arm/mach-omap2/powerdomain.c static int _pwrdm_post_transition_cb(struct powerdomain *pwrdm, void *unused) · line 196

Quoted 10 lines
arch/arm/mach-omap2/powerdomain.c +10 -0
+ * from the wake-up latency constraints framework
+ */
+static inline int is_pwrdm_mpu_or_core(struct powerdomain *pwrdm)
+{
+	if ((pwrdm == pwrdm_lookup("mpu_pwrdm")) ||
+	    (pwrdm == pwrdm_lookup("core_pwrdm")))
+		return 1;
+	else
+		return 0;
+}

I think Paul pointed this out in the earlier review, but instead of doing this using string compare each time, set a flag in the powerdomain at init time can be checked here with a simple check.

Quoted 103 lines
arch/arm/mach-omap2/powerdomain.c +81 -0
+/**
+ * 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 0 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.
+ *
+ * Returns 0 upon success.
+ */
+int pwrdm_wakeuplat_update_pwrst(struct powerdomain *pwrdm, long min_latency)
+{
+	int ret = 0, new_state = 0;
+
+	if (!pwrdm) {
+		WARN(1, "powerdomain: %s: invalid parameter(s)", __func__);
+		return -EINVAL;
+	}
+
+	/*
+	 * Apply constraints to MPU and CORE via the PM QOS API.
+	 * Every power domain struct has a pm_qos_request_list field
+	 */
+	if (is_pwrdm_mpu_or_core(pwrdm)) {
+		if (pm_qos_request_active(&(pwrdm->pm_qos_request)))
+			pm_qos_remove_request(&(pwrdm->pm_qos_request));
+
+		if (min_latency > 0)
+			pm_qos_add_request(&(pwrdm->pm_qos_request),
+					   PM_QOS_CPU_DMA_LATENCY,
+					   min_latency);
+
+		return 0;
+	}
+
+	/*
+	 * Apply constraints to pwrdm other than MPU and CORE by programming
+	 * the pwrdm next power state.
+	 */
+
+	/* Find power state with wakeup latency < minimum constraint */
+	for (new_state = 0x0; new_state < PWRDM_MAX_PWRSTS; new_state++) {
+		if (min_latency == 0 ||
+		    pwrdm->wakeup_lat[new_state] <= min_latency)
+			break;
+	}
+
+	switch (new_state) {
+	case PWRDM_FUNC_PWRST_OFF:
+		new_state = PWRDM_POWER_OFF;
+		break;
+	case PWRDM_FUNC_PWRST_OSWR:
+		pwrdm_set_logic_retst(pwrdm, PWRDM_POWER_OFF);
+		new_state = PWRDM_POWER_RET;
+		break;
+	case PWRDM_FUNC_PWRST_CSWR:
+		pwrdm_set_logic_retst(pwrdm, PWRDM_POWER_RET);
+		new_state = PWRDM_POWER_RET;
+		break;
+	case PWRDM_FUNC_PWRST_ON:
+		new_state = PWRDM_POWER_ON;
+		break;
+	default:
+		pr_warn("powerdomain: requested latency constraint not "
+			"supported %s set to ON state\n", pwrdm->name);
+		new_state = PWRDM_POWER_ON;
+		break;
+	}
+
+	if (pwrdm_read_pwrst(pwrdm) != new_state)
+		ret = omap_set_pwrdm_state(pwrdm, new_state);
+
+	pr_debug("powerdomain: %s pwrst: curr=%d, prev=%d next=%d "
+		 "min_latency=%ld, set_state=%d\n", pwrdm->name,
+		 pwrdm_read_pwrst(pwrdm), pwrdm_read_prev_pwrst(pwrdm),
+		 pwrdm_read_next_pwrst(pwrdm), min_latency, new_state);
+
+	return ret;
+}
+
 /* Public functions */
 
 /**
arch/arm/mach-omap2/powerdomain.h +4 -1
index 0b7a357..23b3408 100644
@@ -19,7 +19,10 @@
 
 #include <linux/types.h>
 #include <linux/list.h>
-
+#include <linux/plist.h>
+#include <linux/mutex.h>
+#include <linux/spinlock.h>
+#include <linux/pm_qos_params.h>
 #include <linux/atomic.h>
 
 #include <plat/cpu.h>
@@ -46,6 +49,15 @@
 
 #define PWRSTS_OFF_RET_ON	(PWRSTS_OFF_RET | (1 << PWRDM_POWER_ON))

arch/arm/mach-omap2/powerdomain.h line 60

Quoted 10 lines
arch/arm/mach-omap2/powerdomain.h +9 -0
 
+/* Powerdomain functional power states */
+#define PWRDM_FUNC_PWRST_OFF	0x0
+#define PWRDM_FUNC_PWRST_OSWR	0x1
+#define PWRDM_FUNC_PWRST_CSWR	0x2
+#define PWRDM_FUNC_PWRST_ON	0x3
+
+#define PWRDM_MAX_FUNC_PWRSTS	4
+
+#define UNSUP_STATE		-1

I think Paul mentioned this in earlier review, but we need to handle (or be prepared to handle) the inactive state too.

Quoted 25 lines
arch/arm/mach-omap2/powerdomain.h +7 -1
 /* Powerdomain flags */
 #define PWRDM_HAS_HDWR_SAR	(1 << 0) /* hardware save-and-restore support */
@@ -96,7 +108,11 @@ struct powerdomain;
  * @state_counter:
  * @timer:
  * @state_timer:
- *
+ * @wakeup_lat: wakeup latencies (in us) for possible powerdomain power states
+ * Note about the wakeup latencies ordering: the values must be sorted
+ *  in decremental order
+ * @pm_qos_request: PM QOS handle, only used to control the MPU and CORE power
+ *  domains states; to be removed when a generic solution is in place.
  * @prcm_partition possible values are defined in mach-omap2/prcm44xx.h.
  */
 struct powerdomain {
@@ -121,6 +137,8 @@ struct powerdomain {
 	s64 timer;
 	s64 state_timer[PWRDM_MAX_PWRSTS];
 #endif
+	const u32 wakeup_lat[PWRDM_MAX_FUNC_PWRSTS];
+	struct pm_qos_request_list pm_qos_request;
 };
 
 /**

arch/arm/mach-omap2/powerdomain.h int pwrdm_clkdm_state_switch(struct clockdomain *clkdm); · line 235

Quoted 8 lines
arch/arm/mach-omap2/powerdomain.h +2 -0
@@ -210,6 +228,8 @@ int pwrdm_clkdm_state_switch(struct clockdomain *clkdm);
 int pwrdm_pre_transition(void);
 int pwrdm_post_transition(void);
 int pwrdm_set_lowpwrstchange(struct powerdomain *pwrdm);
+
+int pwrdm_wakeuplat_update_pwrst(struct powerdomain *pwrdm, long min_latency);
 u32 pwrdm_get_context_loss_count(struct powerdomain *pwrdm);
 
 extern void omap2xxx_powerdomains_init(void);

Kevin

author
#13 lore raw

PATCH v2 7/7 OMAP: PM CONSTRAINTS: add power domains wake-up latency figures

In reply to Jean Pihet

Figures are added to the power domains structs.

Note: the figures are preliminary figures. More accurate measurements are needed. Also the conditions of measurements shall be investigated and described.

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

---
 arch/arm/mach-omap2/powerdomains3xxx_data.c |   63 +++++++++++++++++++++++++++
 1 files changed, 63 insertions(+), 0 deletions(-)
arch/arm/mach-omap2/powerdomains3xxx_data.c +63 -0
index e1bec56..64973d1 100644
@@ -31,6 +31,9 @@
 
 /*
  * Powerdomains
+ *
+ * The wakeup_lat values are derived from measurements on
+ * the actual target.
  */
 
 static struct powerdomain iva2_pwrdm = {
@@ -52,6 +55,12 @@ static struct powerdomain iva2_pwrdm = {
 		[2] = PWRSTS_OFF_ON,
 		[3] = PWRDM_POWER_ON,
 	},
+	.wakeup_lat = {
+		[PWRDM_FUNC_PWRST_OFF] = 1100,
+		[PWRDM_FUNC_PWRST_OSWR] = UNSUP_STATE,
+		[PWRDM_FUNC_PWRST_CSWR] = 350,
+		[PWRDM_FUNC_PWRST_ON] = 0,
+	},
 };
 
 static struct powerdomain mpu_3xxx_pwrdm = {
@@ -68,6 +77,12 @@ static struct powerdomain mpu_3xxx_pwrdm = {
 	.pwrsts_mem_on	  = {
 		[0] = PWRSTS_OFF_ON,
 	},
+	.wakeup_lat = {
+		[PWRDM_FUNC_PWRST_OFF] = 95,
+		[PWRDM_FUNC_PWRST_OSWR] = UNSUP_STATE,
+		[PWRDM_FUNC_PWRST_CSWR] = 45,
+		[PWRDM_FUNC_PWRST_ON] = 0,
+	},
 };
 
 /*
@@ -98,6 +113,12 @@ static struct powerdomain core_3xxx_pre_es3_1_pwrdm = {
 		[0] = PWRSTS_OFF_RET_ON, /* MEM1ONSTATE */
 		[1] = PWRSTS_OFF_RET_ON, /* MEM2ONSTATE */
 	},
+	.wakeup_lat = {
+		[PWRDM_FUNC_PWRST_OFF] = 100,
+		[PWRDM_FUNC_PWRST_OSWR] = UNSUP_STATE,
+		[PWRDM_FUNC_PWRST_CSWR] = 60,
+		[PWRDM_FUNC_PWRST_ON] = 0,
+	},
 };
 
 static struct powerdomain core_3xxx_es3_1_pwrdm = {
@@ -121,6 +142,12 @@ static struct powerdomain core_3xxx_es3_1_pwrdm = {
 		[0] = PWRSTS_OFF_RET_ON, /* MEM1ONSTATE */
 		[1] = PWRSTS_OFF_RET_ON, /* MEM2ONSTATE */
 	},
+	.wakeup_lat = {
+		[PWRDM_FUNC_PWRST_OFF] = 100,
+		[PWRDM_FUNC_PWRST_OSWR] = UNSUP_STATE,
+		[PWRDM_FUNC_PWRST_CSWR] = 60,
+		[PWRDM_FUNC_PWRST_ON] = 0,
+	},
 };
 
 static struct powerdomain dss_pwrdm = {
@@ -136,6 +163,12 @@ static struct powerdomain dss_pwrdm = {
 	.pwrsts_mem_on	  = {
 		[0] = PWRDM_POWER_ON,  /* MEMONSTATE */
 	},
+	.wakeup_lat = {
+		[PWRDM_FUNC_PWRST_OFF] = 70,
+		[PWRDM_FUNC_PWRST_OSWR] = UNSUP_STATE,
+		[PWRDM_FUNC_PWRST_CSWR] = 20,
+		[PWRDM_FUNC_PWRST_ON] = 0,
+	},
 };
 
 /*
@@ -157,6 +190,12 @@ static struct powerdomain sgx_pwrdm = {
 	.pwrsts_mem_on	  = {
 		[0] = PWRDM_POWER_ON,  /* MEMONSTATE */
 	},
+	.wakeup_lat = {
+		[PWRDM_FUNC_PWRST_OFF] = 1000,
+		[PWRDM_FUNC_PWRST_OSWR] = UNSUP_STATE,
+		[PWRDM_FUNC_PWRST_CSWR] = UNSUP_STATE,
+		[PWRDM_FUNC_PWRST_ON] = 0,
+	},
 };
 
 static struct powerdomain cam_pwrdm = {
@@ -172,6 +211,12 @@ static struct powerdomain cam_pwrdm = {
 	.pwrsts_mem_on	  = {
 		[0] = PWRDM_POWER_ON,  /* MEMONSTATE */
 	},
+	.wakeup_lat = {
+		[PWRDM_FUNC_PWRST_OFF] = 850,
+		[PWRDM_FUNC_PWRST_OSWR] = UNSUP_STATE,
+		[PWRDM_FUNC_PWRST_CSWR] = 35,
+		[PWRDM_FUNC_PWRST_ON] = 0,
+	},
 };
 
 static struct powerdomain per_pwrdm = {
@@ -187,6 +232,12 @@ static struct powerdomain per_pwrdm = {
 	.pwrsts_mem_on	  = {
 		[0] = PWRDM_POWER_ON,  /* MEMONSTATE */
 	},
+	.wakeup_lat = {
+		[PWRDM_FUNC_PWRST_OFF] = 200,
+		[PWRDM_FUNC_PWRST_OSWR] = UNSUP_STATE,
+		[PWRDM_FUNC_PWRST_CSWR] = 110,
+		[PWRDM_FUNC_PWRST_ON] = 0,
+	},
 };
 
 static struct powerdomain emu_pwrdm = {
@@ -201,6 +252,12 @@ static struct powerdomain neon_pwrdm = {
 	.omap_chip	  = OMAP_CHIP_INIT(CHIP_IS_OMAP3430),
 	.pwrsts		  = PWRSTS_OFF_RET_ON,
 	.pwrsts_logic_ret = PWRDM_POWER_RET,
+	.wakeup_lat = {
+		[PWRDM_FUNC_PWRST_OFF] = 200,
+		[PWRDM_FUNC_PWRST_OSWR] = UNSUP_STATE,
+		[PWRDM_FUNC_PWRST_CSWR] = 35,
+		[PWRDM_FUNC_PWRST_ON] = 0,
+	},
 };
 
 static struct powerdomain usbhost_pwrdm = {
@@ -223,6 +280,12 @@ static struct powerdomain usbhost_pwrdm = {
 	.pwrsts_mem_on	  = {
 		[0] = PWRDM_POWER_ON,  /* MEMONSTATE */
 	},
+	.wakeup_lat = {
+		[PWRDM_FUNC_PWRST_OFF] = 800,
+		[PWRDM_FUNC_PWRST_OSWR] = UNSUP_STATE,
+		[PWRDM_FUNC_PWRST_CSWR] = 150,
+		[PWRDM_FUNC_PWRST_ON] = 0,
+	},
 };
 
 static struct powerdomain dpll1_pwrdm = {
Signature · 1 line
1.7.2.3
#14 lore raw

PATCH v2 7/7 OMAP: PM CONSTRAINTS: add power domains wake-up latency figures

Quoted 10 lines · Jean Pihet

Figures are added to the power domains structs.

Note: the figures are preliminary figures. More accurate measurements are needed. Also the conditions of measurements shall be investigated and described.

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

Subject should be: OMAP3: powerdomain data: ...

Quoted 9 lines
---
 arch/arm/mach-omap2/powerdomains3xxx_data.c |   63 +++++++++++++++++++++++++++
 1 files changed, 63 insertions(+), 0 deletions(-)
arch/arm/mach-omap2/powerdomains3xxx_data.c +0 -0
index e1bec56..64973d1 100644

arch/arm/mach-omap2/powerdomains3xxx_data.c line 36

Quoted 6 lines
arch/arm/mach-omap2/powerdomains3xxx_data.c +3 -0
@@ -31,6 +31,9 @@
 
 /*
  * Powerdomains
+ *
+ * The wakeup_lat values are derived from measurements on
+ * the actual target.

Which actual target.

Exactly how were they measured? and between which events?

Kevin

Quoted 133 lines
arch/arm/mach-omap2/powerdomains3xxx_data.c +60 -0
  */
 
 static struct powerdomain iva2_pwrdm = {
@@ -52,6 +55,12 @@ static struct powerdomain iva2_pwrdm = {
 		[2] = PWRSTS_OFF_ON,
 		[3] = PWRDM_POWER_ON,
 	},
+	.wakeup_lat = {
+		[PWRDM_FUNC_PWRST_OFF] = 1100,
+		[PWRDM_FUNC_PWRST_OSWR] = UNSUP_STATE,
+		[PWRDM_FUNC_PWRST_CSWR] = 350,
+		[PWRDM_FUNC_PWRST_ON] = 0,
+	},
 };
 
 static struct powerdomain mpu_3xxx_pwrdm = {
@@ -68,6 +77,12 @@ static struct powerdomain mpu_3xxx_pwrdm = {
 	.pwrsts_mem_on	  = {
 		[0] = PWRSTS_OFF_ON,
 	},
+	.wakeup_lat = {
+		[PWRDM_FUNC_PWRST_OFF] = 95,
+		[PWRDM_FUNC_PWRST_OSWR] = UNSUP_STATE,
+		[PWRDM_FUNC_PWRST_CSWR] = 45,
+		[PWRDM_FUNC_PWRST_ON] = 0,
+	},
 };
 
 /*
@@ -98,6 +113,12 @@ static struct powerdomain core_3xxx_pre_es3_1_pwrdm = {
 		[0] = PWRSTS_OFF_RET_ON, /* MEM1ONSTATE */
 		[1] = PWRSTS_OFF_RET_ON, /* MEM2ONSTATE */
 	},
+	.wakeup_lat = {
+		[PWRDM_FUNC_PWRST_OFF] = 100,
+		[PWRDM_FUNC_PWRST_OSWR] = UNSUP_STATE,
+		[PWRDM_FUNC_PWRST_CSWR] = 60,
+		[PWRDM_FUNC_PWRST_ON] = 0,
+	},
 };
 
 static struct powerdomain core_3xxx_es3_1_pwrdm = {
@@ -121,6 +142,12 @@ static struct powerdomain core_3xxx_es3_1_pwrdm = {
 		[0] = PWRSTS_OFF_RET_ON, /* MEM1ONSTATE */
 		[1] = PWRSTS_OFF_RET_ON, /* MEM2ONSTATE */
 	},
+	.wakeup_lat = {
+		[PWRDM_FUNC_PWRST_OFF] = 100,
+		[PWRDM_FUNC_PWRST_OSWR] = UNSUP_STATE,
+		[PWRDM_FUNC_PWRST_CSWR] = 60,
+		[PWRDM_FUNC_PWRST_ON] = 0,
+	},
 };
 
 static struct powerdomain dss_pwrdm = {
@@ -136,6 +163,12 @@ static struct powerdomain dss_pwrdm = {
 	.pwrsts_mem_on	  = {
 		[0] = PWRDM_POWER_ON,  /* MEMONSTATE */
 	},
+	.wakeup_lat = {
+		[PWRDM_FUNC_PWRST_OFF] = 70,
+		[PWRDM_FUNC_PWRST_OSWR] = UNSUP_STATE,
+		[PWRDM_FUNC_PWRST_CSWR] = 20,
+		[PWRDM_FUNC_PWRST_ON] = 0,
+	},
 };
 
 /*
@@ -157,6 +190,12 @@ static struct powerdomain sgx_pwrdm = {
 	.pwrsts_mem_on	  = {
 		[0] = PWRDM_POWER_ON,  /* MEMONSTATE */
 	},
+	.wakeup_lat = {
+		[PWRDM_FUNC_PWRST_OFF] = 1000,
+		[PWRDM_FUNC_PWRST_OSWR] = UNSUP_STATE,
+		[PWRDM_FUNC_PWRST_CSWR] = UNSUP_STATE,
+		[PWRDM_FUNC_PWRST_ON] = 0,
+	},
 };
 
 static struct powerdomain cam_pwrdm = {
@@ -172,6 +211,12 @@ static struct powerdomain cam_pwrdm = {
 	.pwrsts_mem_on	  = {
 		[0] = PWRDM_POWER_ON,  /* MEMONSTATE */
 	},
+	.wakeup_lat = {
+		[PWRDM_FUNC_PWRST_OFF] = 850,
+		[PWRDM_FUNC_PWRST_OSWR] = UNSUP_STATE,
+		[PWRDM_FUNC_PWRST_CSWR] = 35,
+		[PWRDM_FUNC_PWRST_ON] = 0,
+	},
 };
 
 static struct powerdomain per_pwrdm = {
@@ -187,6 +232,12 @@ static struct powerdomain per_pwrdm = {
 	.pwrsts_mem_on	  = {
 		[0] = PWRDM_POWER_ON,  /* MEMONSTATE */
 	},
+	.wakeup_lat = {
+		[PWRDM_FUNC_PWRST_OFF] = 200,
+		[PWRDM_FUNC_PWRST_OSWR] = UNSUP_STATE,
+		[PWRDM_FUNC_PWRST_CSWR] = 110,
+		[PWRDM_FUNC_PWRST_ON] = 0,
+	},
 };
 
 static struct powerdomain emu_pwrdm = {
@@ -201,6 +252,12 @@ static struct powerdomain neon_pwrdm = {
 	.omap_chip	  = OMAP_CHIP_INIT(CHIP_IS_OMAP3430),
 	.pwrsts		  = PWRSTS_OFF_RET_ON,
 	.pwrsts_logic_ret = PWRDM_POWER_RET,
+	.wakeup_lat = {
+		[PWRDM_FUNC_PWRST_OFF] = 200,
+		[PWRDM_FUNC_PWRST_OSWR] = UNSUP_STATE,
+		[PWRDM_FUNC_PWRST_CSWR] = 35,
+		[PWRDM_FUNC_PWRST_ON] = 0,
+	},
 };
 
 static struct powerdomain usbhost_pwrdm = {
@@ -223,6 +280,12 @@ static struct powerdomain usbhost_pwrdm = {
 	.pwrsts_mem_on	  = {
 		[0] = PWRDM_POWER_ON,  /* MEMONSTATE */
 	},
+	.wakeup_lat = {
+		[PWRDM_FUNC_PWRST_OFF] = 800,
+		[PWRDM_FUNC_PWRST_OSWR] = UNSUP_STATE,
+		[PWRDM_FUNC_PWRST_CSWR] = 150,
+		[PWRDM_FUNC_PWRST_ON] = 0,
+	},
 };
 
 static struct powerdomain dpll1_pwrdm = {