Re: [PATCH v3 7/9] powerpc/powernv: Add platform support for stop instruction

From: Madhavan Srinivasan
Date: Tue May 24 2016 - 08:14:03 EST




On Monday 23 May 2016 08:48 PM, Shreyas B. Prabhu wrote:
> POWER ISA v3 defines a new idle processor core mechanism. In summary,
> a) new instruction named stop is added. This instruction replaces
> instructions like nap, sleep, rvwinkle.
> b) new per thread SPR named PSSCR is added which controls the behavior
> of stop instruction.

Kindly expand the register name at first use like
Processor Stop Status and Control Register (PSSCR)

>
> PSSCR has following key fields

Having the PSSCR layout here will help? something like this

----------------------------------------------------------
| PLS | /// | SD | ESL | EC | PSLL | /// | TR | MTL | RL |
----------------------------------------------------------
0 4 41 42 43 44 48 54 56 60

> Bits 0:3 - Power-Saving Level Status. This field indicates the lowest
> power-saving state the thread entered since stop instruction was last
> executed.
>
> Bit 42 - Enable State Loss
> 0 - No state is lost irrespective of other fields
> 1 - Allows state loss
>
> Bits 44:47 - Power-Saving Level Limit
> This limits the power-saving level that can be entered into.
>
> Bits 60:63 - Requested Level
> Used to specify which power-saving level must be entered on executing
> stop instruction
>
> This patch adds support for stop instruction and PSSCR handling.
>
> Signed-off-by: Shreyas B. Prabhu <shreyas@xxxxxxxxxxxxxxxxxx>
> ---
> Changes in v3:
> ==============
> - Instead of introducing new file idle_power_stop.S, P9 idle support
> is added to idle_power_common.S using CPU_FTR sections.
> - Fixed r4 reg clobbering in power_stop0
> - Improved comments
>
> Changes in v2:
> ==============
> - Using CPU_FTR_ARCH_300 bit instead of CPU_FTR_STOP_INST
>
>
> arch/powerpc/include/asm/cpuidle.h | 2 +
> arch/powerpc/include/asm/kvm_book3s_asm.h | 2 +-
> arch/powerpc/include/asm/machdep.h | 1 +
> arch/powerpc/include/asm/opal-api.h | 11 ++-
> arch/powerpc/include/asm/paca.h | 2 +
> arch/powerpc/include/asm/ppc-opcode.h | 4 +
> arch/powerpc/include/asm/processor.h | 1 +
> arch/powerpc/include/asm/reg.h | 11 +++
> arch/powerpc/kernel/asm-offsets.c | 2 +
> arch/powerpc/kernel/idle_power_common.S | 143 +++++++++++++++++++++++++++---
> arch/powerpc/platforms/powernv/idle.c | 82 ++++++++++++++---
> 11 files changed, 234 insertions(+), 27 deletions(-)
>
> diff --git a/arch/powerpc/include/asm/cpuidle.h b/arch/powerpc/include/asm/cpuidle.h
> index d2f99ca..3d7fc06 100644
> --- a/arch/powerpc/include/asm/cpuidle.h
> +++ b/arch/powerpc/include/asm/cpuidle.h
> @@ -13,6 +13,8 @@
> #ifndef __ASSEMBLY__
> extern u32 pnv_fastsleep_workaround_at_entry[];
> extern u32 pnv_fastsleep_workaround_at_exit[];
> +
> +extern u64 pnv_first_deep_stop_state;
> #endif
>
> #endif
> diff --git a/arch/powerpc/include/asm/kvm_book3s_asm.h b/arch/powerpc/include/asm/kvm_book3s_asm.h
> index 72b6225..d318d43 100644
> --- a/arch/powerpc/include/asm/kvm_book3s_asm.h
> +++ b/arch/powerpc/include/asm/kvm_book3s_asm.h
> @@ -162,7 +162,7 @@ struct kvmppc_book3s_shadow_vcpu {
>
> /* Values for kvm_state */
> #define KVM_HWTHREAD_IN_KERNEL 0
> -#define KVM_HWTHREAD_IN_NAP 1
> +#define KVM_HWTHREAD_IN_IDLE 1
> #define KVM_HWTHREAD_IN_KVM 2
>
> #endif /* __ASM_KVM_BOOK3S_ASM_H__ */
> diff --git a/arch/powerpc/include/asm/machdep.h b/arch/powerpc/include/asm/machdep.h
> index 6bdcd0d..ae3b155 100644
> --- a/arch/powerpc/include/asm/machdep.h
> +++ b/arch/powerpc/include/asm/machdep.h
> @@ -262,6 +262,7 @@ struct machdep_calls {
> extern void e500_idle(void);
> extern void power4_idle(void);
> extern void power7_idle(void);
> +extern void power_stop0(void);
> extern void ppc6xx_idle(void);
> extern void book3e_idle(void);
>
> diff --git a/arch/powerpc/include/asm/opal-api.h b/arch/powerpc/include/asm/opal-api.h
> index 9bb8ddf..7f3f8c6 100644
> --- a/arch/powerpc/include/asm/opal-api.h
> +++ b/arch/powerpc/include/asm/opal-api.h
> @@ -162,13 +162,20 @@
>
> /* Device tree flags */
>
> -/* Flags set in power-mgmt nodes in device tree if
> - * respective idle states are supported in the platform.
> +/*
> + * Flags set in power-mgmt nodes in device tree describing
> + * idle states that are supported in the platform.
> */
> +
> +#define OPAL_PM_TIMEBASE_STOP 0x00000002
> +#define OPAL_PM_LOSE_HYP_CONTEXT 0x00002000
> +#define OPAL_PM_LOSE_FULL_CONTEXT 0x00004000
> #define OPAL_PM_NAP_ENABLED 0x00010000
> #define OPAL_PM_SLEEP_ENABLED 0x00020000
> #define OPAL_PM_WINKLE_ENABLED 0x00040000
> #define OPAL_PM_SLEEP_ENABLED_ER1 0x00080000 /* with workaround */
> +#define OPAL_PM_STOP_INST_FAST 0x00100000
> +#define OPAL_PM_STOP_INST_DEEP 0x00200000
>
> /*
> * OPAL_CONFIG_CPU_IDLE_STATE parameters
> diff --git a/arch/powerpc/include/asm/paca.h b/arch/powerpc/include/asm/paca.h
> index 546540b..ef8e342 100644
> --- a/arch/powerpc/include/asm/paca.h
> +++ b/arch/powerpc/include/asm/paca.h
> @@ -171,6 +171,8 @@ struct paca_struct {
> /* Mask to denote subcore sibling threads */
> u8 subcore_sibling_mask;
> #endif
> + /* Template for PSSCR with EC, ESL, TR, PSLL, MTL fields set */

White space at start of the comment.

> + u64 thread_psscr;
>
> #ifdef CONFIG_PPC_BOOK3S_64
> /* Exclusive emergency stack pointer for machine check exception. */
> diff --git a/arch/powerpc/include/asm/ppc-opcode.h b/arch/powerpc/include/asm/ppc-opcode.h
> index 1d035c1..6a8e43b 100644
> --- a/arch/powerpc/include/asm/ppc-opcode.h
> +++ b/arch/powerpc/include/asm/ppc-opcode.h
> @@ -199,6 +199,8 @@
> #define PPC_INST_SLEEP 0x4c0003a4
> #define PPC_INST_WINKLE 0x4c0003e4
>
> +#define PPC_INST_STOP 0x4c0002e4
> +
> /* A2 specific instructions */
> #define PPC_INST_ERATWE 0x7c0001a6
> #define PPC_INST_ERATRE 0x7c000166
> @@ -370,6 +372,8 @@
> #define PPC_SLEEP stringify_in_c(.long PPC_INST_SLEEP)
> #define PPC_WINKLE stringify_in_c(.long PPC_INST_WINKLE)
>
> +#define PPC_STOP stringify_in_c(.long PPC_INST_STOP)
> +
> /* BHRB instructions */
> #define PPC_CLRBHRB stringify_in_c(.long PPC_INST_CLRBHRB)
> #define PPC_MFBHRBE(r, n) stringify_in_c(.long PPC_INST_BHRBE | \
> diff --git a/arch/powerpc/include/asm/processor.h b/arch/powerpc/include/asm/processor.h
> index 009fab1..7f92fc8 100644
> --- a/arch/powerpc/include/asm/processor.h
> +++ b/arch/powerpc/include/asm/processor.h
> @@ -457,6 +457,7 @@ 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 power_stop(unsigned long state);
> extern void flush_instruction_cache(void);
> extern void hard_reset_now(void);
> extern void poweroff_now(void);
> diff --git a/arch/powerpc/include/asm/reg.h b/arch/powerpc/include/asm/reg.h
> index c1e82e9..ea971b6 100644
> --- a/arch/powerpc/include/asm/reg.h
> +++ b/arch/powerpc/include/asm/reg.h
> @@ -145,6 +145,16 @@
> #define MSR_64BIT 0
> #endif
>
> +/* Power Management - PSSCR Fields */
> +#define PSSCR_RL_MASK 0x0000000F
> +#define PSSCR_MTL_MASK 0x000000F0
> +#define PSSCR_TR_MASK 0x00000300
> +#define PSSCR_PSLL_MASK 0x000F0000
> +#define PSSCR_EC 0x00100000
> +#define PSSCR_ESL 0x00200000
> +#define PSSCR_SD 0x00400000
> +
> +
> /* Floating Point Status and Control Register (FPSCR) Fields */
> #define FPSCR_FX 0x80000000 /* FPU exception summary */
> #define FPSCR_FEX 0x40000000 /* FPU enabled exception summary */
> @@ -288,6 +298,7 @@
> #define SPRN_PMICR 0x354 /* Power Management Idle Control Reg */
> #define SPRN_PMSR 0x355 /* Power Management Status Reg */
> #define SPRN_PMMAR 0x356 /* Power Management Memory Activity Register */
> +#define SPRN_PSSCR 0x357 /* Processor Stop Status and Control Register */
> #define SPRN_PMCR 0x374 /* Power Management Control Register */
>
> /* HFSCR and FSCR bit numbers are the same */
> diff --git a/arch/powerpc/kernel/asm-offsets.c b/arch/powerpc/kernel/asm-offsets.c
> index 9ea0955..670d2a7 100644
> --- a/arch/powerpc/kernel/asm-offsets.c
> +++ b/arch/powerpc/kernel/asm-offsets.c
> @@ -779,6 +779,8 @@ int main(void)
> offsetof(struct paca_struct, thread_mask));
> DEFINE(PACA_SUBCORE_SIBLING_MASK,
> offsetof(struct paca_struct, subcore_sibling_mask));
> + DEFINE(PACA_THREAD_PSSCR,
> + offsetof(struct paca_struct, thread_psscr));
> #endif
>
> DEFINE(PPC_DBELL_SERVER, PPC_DBELL_SERVER);
> diff --git a/arch/powerpc/kernel/idle_power_common.S b/arch/powerpc/kernel/idle_power_common.S
> index d931537..628586a 100644
> --- a/arch/powerpc/kernel/idle_power_common.S
> +++ b/arch/powerpc/kernel/idle_power_common.S
> @@ -1,5 +1,6 @@
> /*
> - * This file contains the power_save function for Power7 CPUs.
> + * This file contains the idle entry/exit functions for
> + * POWER7, POWER8 and POWER9 CPUs.
> *
> * This program is free software; you can redistribute it and/or
> * modify it under the terms of the GNU General Public License
> @@ -20,6 +21,7 @@
> #include <asm/opal.h>
> #include <asm/cpuidle.h>
> #include <asm/book3s/64/mmu-hash.h>
> +#include <asm/mmu.h>
>
> #undef DEBUG
>
> @@ -36,6 +38,7 @@
> #define _AMOR GPR9
> #define _WORT GPR10
> #define _WORC GPR11
> +#define _PTCR GPR12
>
> /* Idle state entry routines */
>
> @@ -49,6 +52,15 @@
> IDLE_INST; \
> b .
>
> +/*
> + * rA - Requested stop state
> + * rB - Spare reg that can be used
> + */
> +#define PSSCR_REQUEST_STATE(rA, rB) \
> + ld rB, PACA_THREAD_PSSCR(r13); \
> + or rB,rB,rA; \
> + mtspr SPRN_PSSCR, rB;
> +
> .text
>
> /*
> @@ -60,8 +72,13 @@ save_sprs_to_stack:
> * Note all register i.e per-core, per-subcore or per-thread is saved
> * here since any thread in the core might wake up first
> */
> +BEGIN_FTR_SECTION
> + mfspr r3,SPRN_PTCR
> + std r3,_PTCR(r1)
> +FTR_SECTION_ELSE
> mfspr r3,SPRN_SDR1
> std r3,_SDR1(r1)
> +ALT_FTR_SECTION_END_IFSET(CPU_FTR_ARCH_300)
> mfspr r3,SPRN_RPR
> std r3,_RPR(r1)
> mfspr r3,SPRN_SPURR
> @@ -99,7 +116,8 @@ core_idle_lock_held:
>
> /*
> * Pass requested state in r3:
> - * r3 - PNV_THREAD_NAP/SLEEP/WINKLE
> + * r3 - PNV_THREAD_NAP/SLEEP/WINKLE in POWER8
> + * - Requested STOP state in POWER9
> *
> * To check IRQ_HAPPENED in r4
> * 0 - don't check
> @@ -160,7 +178,7 @@ _GLOBAL(pnv_powersave_common)
>
> #ifdef CONFIG_KVM_BOOK3S_HV_POSSIBLE
> /* Tell KVM we're napping */
> - li r4,KVM_HWTHREAD_IN_NAP
> + li r4,KVM_HWTHREAD_IN_IDLE
> stb r4,HSTATE_HWTHREAD_STATE(r13)
> #endif
> /*
> @@ -241,6 +259,41 @@ enter_winkle:
>
> IDLE_STATE_ENTER_SEQ(PPC_WINKLE)
>
> +/*
> + * r3 - requested stop state
> + */
> +power_enter_stop:
> +/*
> + * Check if the requested state is a deep idle state.
> + */
> + LOAD_REG_ADDRBASE(r5,pnv_first_deep_stop_state)
> + ld r4,ADDROFF(pnv_first_deep_stop_state)(r5)
> + cmpd r3,r4
> + bge 2f
> + IDLE_STATE_ENTER_SEQ(PPC_STOP)
> +2:
> +/*
> + * Entering deep idle state.
> + * Clear thread bit in PACA_CORE_IDLE_STATE, save SPRs to
> + * stack and enter stop
> + */
> + lbz r7,PACA_THREAD_MASK(r13)
> + ld r14,PACA_CORE_IDLE_STATE_PTR(r13)
> +
> +lwarx_loop_stop:
> + lwarx r15,0,r14
> + andi. r9,r15,PNV_CORE_IDLE_LOCK_BIT
> + bnel core_idle_lock_held
> + andc r15,r15,r7 /* Clear thread bit */
> +
> + stwcx. r15,0,r14
> + bne- lwarx_loop_stop
> + isync
> +
> + bl save_sprs_to_stack
> +
> + IDLE_STATE_ENTER_SEQ(PPC_STOP)
> +
> _GLOBAL(power7_idle)
> /* Now check if user or arch enabled NAP mode */
> LOAD_REG_ADDRBASE(r3,powersave_nap)
> @@ -291,6 +344,21 @@ ALT_FTR_SECTION_END_NESTED_IFSET(CPU_FTR_ARCH_207S, 66); \
>
>
> /*
> + * r3 - requested stop state
> + */
> +_GLOBAL(power_stop)
> + PSSCR_REQUEST_STATE(r3,r4)
> + li r4, 1
> + LOAD_REG_ADDR(r5,power_enter_stop)
> + b pnv_powersave_common
> + /* No return */
> +

Can you add comments here to say why we
need power_stop0?

> +_GLOBAL(power_stop0)
> + li r3,0
> + b power_stop
> + /* No return */
> +
> +/*
> * Called from reset vector. Check whether we have woken up with
> * hypervisor state loss. If yes, restore hypervisor state and return
> * back to reset vector.
> @@ -299,7 +367,32 @@ ALT_FTR_SECTION_END_NESTED_IFSET(CPU_FTR_ARCH_207S, 66); \
> * cr3 - set to gt if waking up with partial/complete hypervisor state loss
> */
> _GLOBAL(pnv_restore_hyp_resource)
> +BEGIN_FTR_SECTION
> + /*
> + * POWER ISA 3. Use PSSCR to determine if we
> + * are waking up from deep idle state
> + */
> + LOAD_REG_ADDRBASE(r5,pnv_first_deep_stop_state)
> + ld r4,ADDROFF(pnv_first_deep_stop_state)(r5)
> +
> + mfspr r5,SPRN_PSSCR
> + /*
> + * 0-4 bits correspond to Power-Saving Level Status

PSL is bits 0-3 of PSSCR right?


> + * which indicates the idle state we are waking up from
> + */
> + rldicl r5,r5,4,60
> + cmpd cr4,r5,r4
> + bge cr4,pnv_wakeup_tb_loss
> + /*
> + * Waking up without hypervisor state loss. Return to
> + * reset vector
> + */
> + blr
> +
> +END_FTR_SECTION_IFSET(CPU_FTR_ARCH_300)
> +
> /*
> + * POWER ISA 2.07 or less.
> * Check if last bit of HSPGR0 is set. This indicates whether we are
> * waking up from winkle.
> */
> @@ -322,7 +415,16 @@ _GLOBAL(pnv_restore_hyp_resource)
> blr /* Return back to System Reset vector from where
> pnv_restore_hyp_resource was invoked */
>
> -
> +/*
> + * Called if waking up from idle state which can cause either partial or
> + * complete hyp state loss.
> + * In POWER8, called if waking up from fastsleep or winkle
> + * In POWER9, called if waking up from stop state >= pnv_first_deep_stop_state
> + *
> + * r13 - PACA
> + * cr3 - gt if waking up with partial/complete hypervisor state loss
> + * cr4 - eq if waking up from complete hypervisor state loss.
> + */
> _GLOBAL(pnv_wakeup_tb_loss)
> ld r2,PACATOC(r13);
> ld r1,PACAR1(r13)
> @@ -365,10 +467,10 @@ lwarx_loop2:
>
> /*
> * At this stage
> - * cr1 - 0b0100 if first thread to wakeup in subcore
> - * cr2 - 0b0100 if first thread to wakeup in core
> - * cr3- 0b0010 if waking up from sleep or winkle
> - * cr4 - 0b0100 if waking up from winkle
> + * cr1 - eq if first thread to wakeup in subcore
> + * cr2 - eq if first thread to wakeup in core
> + * cr3- gt if waking up with partial/complete hypervisor state loss
> + * cr4 - eq if waking up from complete hypervisor state loss.
> */
>
> or r15,r15,r7 /* Set thread bit */
> @@ -395,8 +497,11 @@ first_thread_in_subcore:
> bne cr4,subcore_state_restored
>
> /* Restore per-subcore state */
> +BEGIN_FTR_SECTION
> ld r4,_SDR1(r1)
> mtspr SPRN_SDR1,r4
> +END_FTR_SECTION_IFCLR(CPU_FTR_ARCH_300)
> +
> ld r4,_RPR(r1)
> mtspr SPRN_RPR,r4
> ld r4,_AMOR(r1)
> @@ -412,7 +517,8 @@ subcore_state_restored:
> first_thread_in_core:
>
> /*
> - * First thread in the core waking up from fastsleep. It needs to
> + * First thread in the core waking up from any state which can cause
> + * partial or complete hypervisor state loss. It needs to
> * call the fastsleep workaround code if the platform requires it.
> * Call it unconditionally here. The below branch instruction will
> * be patched out when the idle states are discovered if platform
> @@ -423,8 +529,10 @@ pnv_fastsleep_workaround_at_exit:
> b fastsleep_workaround_at_exit
>
> timebase_resync:
> - /* Do timebase resync if we are waking up from sleep. Use cr3 value
> - * set in exceptions-64s.S */
> + /*
> + * Use cr3 which indicates that we are waking up with atleast partial
> + * hypervisor state loss to determine if TIMEBASE RESYNC is needed.
> + */
> ble cr3,clear_lock
> /* Time base re-sync */
> li r0,OPAL_RESYNC_TIMEBASE
> @@ -437,7 +545,16 @@ timebase_resync:
> */
> bne cr4,clear_lock
>
> - /* Restore per core state */
> + /*
> + * First thread in the core to wake up and its waking up with
> + * complete hypervisor state loss. Restore per core hypervisor
> + * state.
> + */
> +BEGIN_FTR_SECTION
> + ld r4,_PTCR(r1)
> + mtspr SPRN_PTCR,r4
> +END_FTR_SECTION_IFSET(CPU_FTR_ARCH_300)
> +
> ld r4,_TSCR(r1)
> mtspr SPRN_TSCR,r4
> ld r4,_WORC(r1)
> @@ -462,6 +579,7 @@ common_exit:
> /* Restore per thread state */
> bl __restore_cpu_power8
>
> +BEGIN_MMU_FTR_SECTION
> /* Restore SLB from PACA */
> ld r8,PACA_SLBSHADOWPTR(r13)
>
> @@ -475,6 +593,7 @@ common_exit:
> slbmte r6,r5
> 1: addi r8,r8,16
> .endr
> +END_MMU_FTR_SECTION_IFCLR(MMU_FTR_RADIX)
>
> ld r4,_SPURR(r1)
> mtspr SPRN_SPURR,r4
> diff --git a/arch/powerpc/platforms/powernv/idle.c b/arch/powerpc/platforms/powernv/idle.c
> index fbb09fb..989cf5d 100644
> --- a/arch/powerpc/platforms/powernv/idle.c
> +++ b/arch/powerpc/platforms/powernv/idle.c
> @@ -29,7 +29,7 @@
>
> static u32 supported_cpuidle_states;
>
> -int pnv_save_sprs_for_winkle(void)
> +int pnv_save_sprs_for_deep_states(void)
> {
> int cpu;
> int rc;
> @@ -50,15 +50,19 @@ int pnv_save_sprs_for_winkle(void)
> uint64_t pir = get_hard_smp_processor_id(cpu);
> uint64_t hsprg0_val = (uint64_t)&paca[cpu];
>
> - /*
> - * HSPRG0 is used to store the cpu's pointer to paca. Hence last
> - * 3 bits are guaranteed to be 0. Program slw to restore HSPRG0
> - * with 63rd bit set, so that when a thread wakes up at 0x100 we
> - * can use this bit to distinguish between fastsleep and
> - * deep winkle.
> - */
> - hsprg0_val |= 1;
> -
> + if (!cpu_has_feature(CPU_FTR_ARCH_300)) {
> + /*
> + * HSPRG0 is used to store the cpu's pointer to paca.
> + * Hence last 3 bits are guaranteed to be 0. Program
> + * slw to restore HSPRG0 with 63rd bit set, so that
> + * when a thread wakes up at 0x100 we can use this bit
> + * to distinguish between fastsleep and deep winkle.
> + * This is not necessary with stop/psscr since PLS
> + * field of psscr indicates which state we are waking
> + * up from.
> + */
> + hsprg0_val |= 1;
> + }
> rc = opal_slw_set_reg(pir, SPRN_HSPRG0, hsprg0_val);
> if (rc != 0)
> return rc;
> @@ -130,8 +134,8 @@ static void pnv_alloc_idle_core_states(void)
>
> update_subcore_sibling_mask();
>
> - if (supported_cpuidle_states & OPAL_PM_WINKLE_ENABLED)
> - pnv_save_sprs_for_winkle();
> + if (supported_cpuidle_states & OPAL_PM_LOSE_FULL_CONTEXT)
> + pnv_save_sprs_for_deep_states();
> }
>
> u32 pnv_get_supported_cpuidle_states(void)
> @@ -230,11 +234,18 @@ static DEVICE_ATTR(fastsleep_workaround_applyonce, 0600,
> show_fastsleep_workaround_applyonce,
> store_fastsleep_workaround_applyonce);
>
> +/*
> + * First deep stop state. Used to figure out when to save/restore
> + * hypervisor context.
> + */
> +u64 pnv_first_deep_stop_state;
> +
> static int __init pnv_init_idle_states(void)
> {
> struct device_node *power_mgt;
> int dt_idle_states;
> u32 *flags;
> + u64 *psscr_val = NULL;
> int i;
>
> supported_cpuidle_states = 0;
> @@ -264,6 +275,32 @@ static int __init pnv_init_idle_states(void)
> goto out_free;
> }
>
> + if (cpu_has_feature(CPU_FTR_ARCH_300)) {
> + psscr_val = kcalloc(dt_idle_states, sizeof(*psscr_val),
> + GFP_KERNEL);
> + if (!psscr_val)
> + goto out_free;
> + if (of_property_read_u64_array(power_mgt,
> + "ibm,cpu-idle-state-psscr",
> + psscr_val, dt_idle_states)) {
> + pr_warn("cpuidle-powernv: missing ibm,cpu-idle-states-psscr in DT\n");
> + goto out_free_psscr;
> + }
> +
> + /*
> + * Set pnv_first_deep_stop_state to the first stop level
> + * to cause hypervisor state loss
> + */
> + pnv_first_deep_stop_state = 0xF;
> + for (i = 0; i < dt_idle_states; i++) {
> + u64 psscr_rl = psscr_val[i] & PSSCR_RL_MASK;
> +
> + if ((flags[i] & OPAL_PM_LOSE_FULL_CONTEXT) &&
> + (pnv_first_deep_stop_state > psscr_rl))
> + pnv_first_deep_stop_state = psscr_rl;
> + }
> + }
> +
> for (i = 0; i < dt_idle_states; i++)
> supported_cpuidle_states |= flags[i];
>
> @@ -286,8 +323,29 @@ static int __init pnv_init_idle_states(void)
>
> pnv_alloc_idle_core_states();
>
> + if (supported_cpuidle_states & OPAL_PM_STOP_INST_FAST)
> + for_each_possible_cpu(i) {
> +
> + u64 psscr_init_val = PSSCR_ESL | PSSCR_EC |
> + PSSCR_PSLL_MASK | PSSCR_TR_MASK |
> + PSSCR_MTL_MASK;
> +
> + paca[i].thread_psscr = psscr_init_val;
> + /*
> + * Memory barrier to ensure that the writes to PACA
> + * goes through before ppc_md.power_save is updated
> + * below.
> + */
> + mb();
> + }
> +
> 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 = power_stop0;
> +
> +out_free_psscr:
> + kfree(psscr_val);
> out_free:
> kfree(flags);
> out: