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 <paul@xxxxxxxxx> Cc: Kevin Hilman <khilman@xxxxxxxxxxxxxxxxxxx> --- 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; -- To unsubscribe from this list: send the line "unsubscribe linux-omap" in the body of a message to majordomo@xxxxxxxxxxxxxxx More majordomo info at http://vger.kernel.org/majordomo-info.html