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

Reply via email to