[PATCH 2/2] powernv:idle:Implement lite variant of power_enter_stop

From: Gautham R. Shenoy
Date: Fri Sep 16 2016 - 05:48:43 EST


From: "Gautham R. Shenoy" <ego@xxxxxxxxxxxxxxxxxx>

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 <ego@xxxxxxxxxxxxxxxxxx>
---
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