Subject: [PATCH 07/12] ARM: OMAP2+: powerdomain: cache the powerdomain next power state

From: [email-masked] (Paul Walmsley)
Date: Sun, 09 Dec 2012 13:03:26 -0700

Cache the powerdomain next power state registers.  The objective here
is to avoid unneeded reads and writes to the next power state
registers, which are slow compared to RAM & CPU cache.

Signed-off-by: Paul Walmsley <[email-masked]>
Cc: Kevin Hilman <[email-masked]>
---
 arch/arm/mach-omap2/powerdomain.c |   64 +++++++++++++++++--------------------
 arch/arm/mach-omap2/powerdomain.h |   17 ++++++++++
 2 files changed, 46 insertions(+), 35 deletions(-)

diff --git a/arch/arm/mach-omap2/powerdomain.c b/arch/arm/mach-omap2/powerdomain.c
index 62e2f75..425c868 100644
--- a/arch/arm/mach-omap2/powerdomain.c
+++ b/arch/arm/mach-omap2/powerdomain.c
@@ -376,7 +376,7 @@ static int _pwrdm_pwrst_to_fpwrst(struct powerdomain *pwrdm, u8 pwrst, u8 logic,
  * software-controllable, returns 0; otherwise, passes along the
  * return value from pwrdm_set_logic_retst() if there is an error
  * returned by that function, otherwise, passes along the return value
- * from pwrdm_set_next_pwrst()
+ * from pwrdm_set_next_fpwrst()
  */
 static int _set_logic_retst_and_pwrdm_pwrst(struct powerdomain *pwrdm,
 					    u8 logic, u8 pwrst)
@@ -426,6 +426,9 @@ static int _pwrdm_read_next_fpwrst(struct powerdomain *pwrdm)
 	int next_pwrst, next_logic, ret;
 	u8 fpwrst;
 
+	if (pwrdm->_flags & _PWRDM_NEXT_FPWRST_IS_VALID)
+		return pwrdm->next_fpwrst;
+
 	next_pwrst = arch_pwrdm->pwrdm_read_next_pwrst(pwrdm);
 	if (next_pwrst < 0)
 		return next_pwrst;
@@ -438,6 +441,10 @@ static int _pwrdm_read_next_fpwrst(struct powerdomain *pwrdm)
 			return next_logic;
 	}
 	ret = _pwrdm_pwrst_to_fpwrst(pwrdm, next_pwrst, next_logic, &fpwrst);
+	if (!ret) {
+		pwrdm->next_fpwrst = fpwrst;
+		pwrdm->_flags |= _PWRDM_NEXT_FPWRST_IS_VALID;
+	}
 
 	return (ret) ? ret : fpwrst;
 }
@@ -663,7 +670,7 @@ static int _pwrdm_pre_transition_cb(struct powerdomain *pwrdm, void *unused)
  */
 static int _pwrdm_post_transition_cb(struct powerdomain *pwrdm, void *unused)
 {
-	int prev, next, fpwrst;
+	int prev, fpwrst;
 	int trace_state = 0;
 
 	prev = _pwrdm_read_prev_fpwrst(pwrdm);
@@ -676,10 +683,9 @@ static int _pwrdm_post_transition_cb(struct powerdomain *pwrdm, void *unused)
 	 * If the power domain did not hit the desired state,
 	 * generate a trace event with both the desired and hit states
 	 */
-	next = _pwrdm_read_next_fpwrst(pwrdm);
-	if (next != prev) {
-		trace_state = (PWRDM_TRACE_STATES_FLAG | next << 8 |
-			       prev);
+	if (pwrdm->next_fpwrst != prev) {
+		trace_state = (PWRDM_TRACE_STATES_FLAG |
+			       pwrdm->next_fpwrst << 8 | prev);
 		trace_power_domain_target(pwrdm->name, trace_state,
 					  smp_processor_id());
 	}
@@ -1256,6 +1262,10 @@ int pwrdm_set_next_fpwrst(struct powerdomain *pwrdm, u8 fpwrst)
 	if (ret)
 		return ret;
 
+	if (pwrdm->_flags & _PWRDM_NEXT_FPWRST_IS_VALID &&
+	    pwrdm->next_fpwrst == fpwrst)
+		return 0;
+
 	pr_debug("%s: set fpwrst %0x to pwrdm %s\n", __func__, fpwrst,
 		 pwrdm->name);
 
@@ -1264,6 +1274,10 @@ int pwrdm_set_next_fpwrst(struct powerdomain *pwrdm, u8 fpwrst)
 
 	pwrdm_lock(pwrdm);
 	ret = _set_logic_retst_and_pwrdm_pwrst(pwrdm, logic, pwrst);
+	if (!ret) {
+		pwrdm->next_fpwrst = fpwrst;
+		pwrdm->_flags |= _PWRDM_NEXT_FPWRST_IS_VALID;
+	}
 	pwrdm_unlock(pwrdm);
 
 	return ret;
@@ -1279,36 +1293,16 @@ int pwrdm_set_next_fpwrst(struct powerdomain *pwrdm, u8 fpwrst)
  */
 int pwrdm_read_next_fpwrst(struct powerdomain *pwrdm)
 {
-	int next_pwrst, next_logic, ret;
-	u8 fpwrst;
+	int ret;
 
-	if (!arch_pwrdm)
+	if (!pwrdm)
 		return -EINVAL;
 
 	pwrdm_lock(pwrdm);
-
-	next_pwrst = arch_pwrdm->pwrdm_read_next_pwrst(pwrdm);
-	if (next_pwrst < 0) {
-		ret = next_pwrst;
-		goto prnf_out;
-	}
-
-	next_logic = next_pwrst;
-	if (_pwrdm_logic_retst_can_change(pwrdm) &&
-	    arch_pwrdm->pwrdm_read_logic_pwrst) {
-		next_logic = arch_pwrdm->pwrdm_read_logic_pwrst(pwrdm);
-		if (next_logic < 0) {
-			ret = next_logic;
-			goto prnf_out;
-		}
-	}
-
-	ret = _pwrdm_pwrst_to_fpwrst(pwrdm, next_pwrst, next_logic, &fpwrst);
-
-prnf_out:
+	ret = _pwrdm_read_next_fpwrst(pwrdm);
 	pwrdm_unlock(pwrdm);
 
-	return (ret) ? ret : fpwrst;
+	return ret;
 }
 
 /**
@@ -1344,10 +1338,6 @@ int pwrdm_set_fpwrst(struct powerdomain *pwrdm, enum pwrdm_func_state fpwrst)
 
 	pwrdm_lock(pwrdm);
 
-	/*
-	 * XXX quite heavyweight for what this is intended to do; the
-	 * next fpwrst should simply be cached
-	 */
 	next_fpwrst = _pwrdm_read_next_fpwrst(pwrdm);
 	if (next_fpwrst == fpwrst)
 		goto psf_out;
@@ -1364,9 +1354,13 @@ int pwrdm_set_fpwrst(struct powerdomain *pwrdm, enum pwrdm_func_state fpwrst)
 	}
 
 	ret = _set_logic_retst_and_pwrdm_pwrst(pwrdm, logic, pwrst);
-	if (ret)
+	if (ret) {
 		pr_err("%s: unable to set power state of powerdomain: %s\n",
 		       __func__, pwrdm->name);
+	} else {
+		pwrdm->next_fpwrst = fpwrst;
+		pwrdm->_flags |= _PWRDM_NEXT_FPWRST_IS_VALID;
+	}
 
 	_pwrdm_restore_clkdm_state(pwrdm, sleep_switch, hwsup);
 
diff --git a/arch/arm/mach-omap2/powerdomain.h b/arch/arm/mach-omap2/powerdomain.h
index c19bdc3..1fb21f5 100644
--- a/arch/arm/mach-omap2/powerdomain.h
+++ b/arch/arm/mach-omap2/powerdomain.h
@@ -84,6 +84,16 @@ enum pwrdm_func_state {
 #define PWRDM_HAS_LOWPOWERSTATECHANGE	BIT(2)
 
 /*
+ * Powerdomain internal flags (struct powerdomain._flags)
+ *
+ * _PWRDM_NEXT_FPWRST_IS_VALID: the locally-cached copy of the
+ *    powerdomain's next-functional-power-state -- struct
+ *    powerdomain.next_fpwrst -- is valid.  If this bit is not set,
+ *    the code needs to load the current value from the hardware.
+ */
+#define _PWRDM_NEXT_FPWRST_IS_VALID	BIT(0)
+
+/*
  * Number of memory banks that are power-controllable.	On OMAP4430, the
  * maximum is 5.
  */
@@ -127,12 +137,17 @@ struct powerdomain;
  *	in @pwrstctrl_offs
  * @fpwrst: current func power state (set in pwrdm_state_switch() or post_trans)
  * @fpwrst_counter: estimated number of times the pwrdm entered the power states
+ * @next_fpwrst: cache of the powerdomain's next-power-state
  * @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
  * @_lock_flags: stored flags when @_lock is taken
+ * @_flags: flags (for internal use only)
  *
  * @prcm_partition possible values are defined in mach-omap2/prcm44xx.h.
+ *
+ * Possible values for @_flags are documented above in the
+ * "Powerdomain internal flags (struct powerdomain._flags)" comments.
  */
 struct powerdomain {
 	const char *name;
@@ -149,6 +164,8 @@ struct powerdomain {
 	const u8 pwrsts_mem_on[PWRDM_MAX_MEM_BANKS];
 	const u8 prcm_partition;
 	u8 fpwrst;
+	u8 next_fpwrst;
+	u8 _flags;
 	struct clockdomain *pwrdm_clkdms[PWRDM_MAX_CLKDMS];
 	struct list_head node;
 	struct list_head voltdm_node;