From: "Gautham R. Shenoy" <e...@linux.vnet.ibm.com> This patch adds a function named power_enter_stop_lite() that can execute a stop instruction when ESL and EC bits are set to zero in the PSSCR. The function handles the wake-up from idle at the instruction immediately after the stop instruction.
If the flag OPAL_PM_WAKEUP_AT_NEXT_INST[1] is set in the device tree for a stop state, then use the lite variant for that particular stop state. [1] : The corresponding patch in skiboot that defines OPAL_PM_WAKEUP_AT_NEXT_INST and enables it in the device tree can be found here: https://lists.ozlabs.org/pipermail/skiboot/2016-September/004805.html Signed-off-by: Gautham R. Shenoy <e...@linux.vnet.ibm.com> --- arch/powerpc/include/asm/opal-api.h | 1 + arch/powerpc/include/asm/processor.h | 3 ++- arch/powerpc/kernel/idle_book3s.S | 28 +++++++++++++++++++++++++--- arch/powerpc/platforms/powernv/idle.c | 17 ++++++++++++++--- arch/powerpc/platforms/powernv/smp.c | 2 +- drivers/cpuidle/cpuidle-powernv.c | 24 ++++++++++++++++++++++-- 6 files changed, 65 insertions(+), 10 deletions(-) diff --git a/arch/powerpc/include/asm/opal-api.h b/arch/powerpc/include/asm/opal-api.h index 0e2e57b..6e5741e 100644 --- a/arch/powerpc/include/asm/opal-api.h +++ b/arch/powerpc/include/asm/opal-api.h @@ -179,6 +179,7 @@ #define OPAL_PM_TIMEBASE_STOP 0x00000002 #define OPAL_PM_LOSE_HYP_CONTEXT 0x00002000 #define OPAL_PM_LOSE_FULL_CONTEXT 0x00004000 +#define OPAL_PM_WAKEUP_AT_NEXT_INST 0x00008000 #define OPAL_PM_NAP_ENABLED 0x00010000 #define OPAL_PM_SLEEP_ENABLED 0x00020000 #define OPAL_PM_WINKLE_ENABLED 0x00040000 diff --git a/arch/powerpc/include/asm/processor.h b/arch/powerpc/include/asm/processor.h index 68e3bf5..e0549a0 100644 --- a/arch/powerpc/include/asm/processor.h +++ b/arch/powerpc/include/asm/processor.h @@ -460,7 +460,8 @@ extern int powersave_nap; /* set if nap mode can be used in idle loop */ extern unsigned long power7_nap(int check_irq); extern unsigned long power7_sleep(void); extern unsigned long power7_winkle(void); -extern unsigned long power9_idle_stop(unsigned long stop_level); +extern unsigned long power9_idle_stop(unsigned long stop_level, + unsigned long exec_lite); extern void flush_instruction_cache(void); extern void hard_reset_now(void); diff --git a/arch/powerpc/kernel/idle_book3s.S b/arch/powerpc/kernel/idle_book3s.S index 32d666b..47ee106 100644 --- a/arch/powerpc/kernel/idle_book3s.S +++ b/arch/powerpc/kernel/idle_book3s.S @@ -43,6 +43,8 @@ #define PSSCR_HV_TEMPLATE PSSCR_ESL | PSSCR_EC | \ PSSCR_PSLL_MASK | PSSCR_TR_MASK | \ PSSCR_MTL_MASK +#define PSSCR_HV_TEMPLATE_LITE PSSCR_PSLL_MASK | PSSCR_TR_MASK | \ + PSSCR_MTL_MASK .text @@ -246,6 +248,20 @@ enter_winkle: IDLE_STATE_ENTER_SEQ_NORET(PPC_WINKLE) + +/* + * power_enter_stop_lite : This will resume the wake up from + * idle at the subsequent instruction. + * + * Caller should set ESL=EC=0 in PSSCR before calling + * this function. + * + */ +power_enter_stop_lite: + IDLE_STATE_ENTER_SEQ(PPC_STOP) +7: li r3,0 /* Since we didn't lose state, return 0 */ + b pnv_wakeup_noloss + /* * r3 - requested stop state */ @@ -333,13 +349,19 @@ ALT_FTR_SECTION_END_NESTED_IFSET(CPU_FTR_ARCH_207S, 66); \ /* * r3 - requested stop state + * r4 - Indicates if the lite variant with ESL=EC=0 should be executed. */ _GLOBAL(power9_idle_stop) - LOAD_REG_IMMEDIATE(r4, PSSCR_HV_TEMPLATE) - or r4,r4,r3 + cmpdi r4, 1 + bne 4f + LOAD_REG_IMMEDIATE(r4, PSSCR_HV_TEMPLATE_LITE) + LOAD_REG_ADDR(r5,power_enter_stop_lite) + b 5f +4: LOAD_REG_IMMEDIATE(r4, PSSCR_HV_TEMPLATE) + LOAD_REG_ADDR(r5,power_enter_stop) +5: or r4,r4,r3 mtspr SPRN_PSSCR, r4 li r4, 1 - LOAD_REG_ADDR(r5,power_enter_stop) b pnv_powersave_common /* No return */ /* diff --git a/arch/powerpc/platforms/powernv/idle.c b/arch/powerpc/platforms/powernv/idle.c index 479c256..c3d3fed 100644 --- a/arch/powerpc/platforms/powernv/idle.c +++ b/arch/powerpc/platforms/powernv/idle.c @@ -244,8 +244,15 @@ static DEVICE_ATTR(fastsleep_workaround_applyonce, 0600, static void power9_idle(void) { /* Requesting stop state 0 */ - power9_idle_stop(0); + power9_idle_stop(0, 0); } + +static void power9_idle_lite(void) +{ + /* Requesting stop state 0 with ESL=EC=0 */ + power9_idle_stop(0, 1); +} + /* * First deep stop state. Used to figure out when to save/restore * hypervisor context. @@ -414,8 +421,12 @@ static int __init pnv_init_idle_states(void) if (supported_cpuidle_states & OPAL_PM_NAP_ENABLED) ppc_md.power_save = power7_idle; - else if (supported_cpuidle_states & OPAL_PM_STOP_INST_FAST) - ppc_md.power_save = power9_idle; + else if (supported_cpuidle_states & OPAL_PM_STOP_INST_FAST) { + if (supported_cpuidle_states & OPAL_PM_WAKEUP_AT_NEXT_INST) + ppc_md.power_save = power9_idle_lite; + else + ppc_md.power_save = power9_idle; + } out: return 0; diff --git a/arch/powerpc/platforms/powernv/smp.c b/arch/powerpc/platforms/powernv/smp.c index c789258..6587b96 100644 --- a/arch/powerpc/platforms/powernv/smp.c +++ b/arch/powerpc/platforms/powernv/smp.c @@ -183,7 +183,7 @@ static void pnv_smp_cpu_kill_self(void) ppc64_runlatch_off(); if (cpu_has_feature(CPU_FTR_ARCH_300)) - srr1 = power9_idle_stop(pnv_deepest_stop_state); + srr1 = power9_idle_stop(pnv_deepest_stop_state, 0); else if (idle_states & OPAL_PM_WINKLE_ENABLED) srr1 = power7_winkle(); else if ((idle_states & OPAL_PM_SLEEP_ENABLED) || diff --git a/drivers/cpuidle/cpuidle-powernv.c b/drivers/cpuidle/cpuidle-powernv.c index f7ca891..7021ddf 100644 --- a/drivers/cpuidle/cpuidle-powernv.c +++ b/drivers/cpuidle/cpuidle-powernv.c @@ -102,7 +102,21 @@ static int stop_loop(struct cpuidle_device *dev, int index) { ppc64_runlatch_off(); - power9_idle_stop(stop_psscr_table[index]); + power9_idle_stop(stop_psscr_table[index], 0); + ppc64_runlatch_on(); + return index; +} + +/* + * This function calls stop instruction with + * ESL and EC bits set to 0. + */ +static int stop_lite_loop(struct cpuidle_device *dev, + struct cpuidle_driver *drv, + int index) +{ + ppc64_runlatch_off(); + power9_idle_stop(stop_psscr_table[index], 1); ppc64_runlatch_on(); return index; } @@ -273,7 +287,13 @@ static int powernv_add_idle_states(void) names[i], CPUIDLE_NAME_LEN); powernv_states[nr_idle_states].flags = 0; - powernv_states[nr_idle_states].enter = stop_loop; + if (flags[i] & OPAL_PM_WAKEUP_AT_NEXT_INST) { + powernv_states[nr_idle_states].enter = + stop_lite_loop; + } else { + powernv_states[nr_idle_states].enter = + stop_loop; + } stop_psscr_table[nr_idle_states] = psscr_val[i]; } -- 1.9.4