Skip to content

Commit

Permalink
powerpc/powernv/idle: Restore SPRs for deep idle states via stop API.
Browse files Browse the repository at this point in the history
Some of the SPR values (HID0, MSR, SPRG0) don't change during the run
time of a booted kernel, once they have been initialized.

The contents of these SPRs are lost when the CPUs enter deep stop
states. So instead saving and restoring SPRs from the kernel, use the
stop-api provided by the firmware by which the firmware can restore
the contents of these SPRs to their initialized values after wakeup
from a deep stop state.

Apart from these, program the PSSCR value to that of the deepest stop
state via the stop-api. This will be used to indicate to the
underlying firmware as to what stop state to put the threads that have
been woken up by a special-wakeup.

And while we are at programming SPRs via stop-api, ensure that HID1,
HID4 and HID5 registers which are only available on POWER8 are not
requested to be restored by the firware on POWER9.

Signed-off-by: Akshay Adiga <akshay.adiga@linux.vnet.ibm.com>
Signed-off-by: Gautham R. Shenoy <ego@linux.vnet.ibm.com>
Signed-off-by: Michael Ellerman <mpe@ellerman.id.au>
  • Loading branch information
Akshay Adiga authored and Michael Ellerman committed May 30, 2017
1 parent cb0be7e commit 1e1601b
Showing 1 changed file with 52 additions and 31 deletions.
83 changes: 52 additions & 31 deletions arch/powerpc/platforms/powernv/idle.c
Original file line number Diff line number Diff line change
Expand Up @@ -30,8 +30,33 @@
/* Power ISA 3.0 allows for stop states 0x0 - 0xF */
#define MAX_STOP_STATE 0xF

#define P9_STOP_SPR_MSR 2000
#define P9_STOP_SPR_PSSCR 855

static u32 supported_cpuidle_states;

/*
* The default stop state that will be used by ppc_md.power_save
* function on platforms that support stop instruction.
*/
static u64 pnv_default_stop_val;
static u64 pnv_default_stop_mask;
static bool default_stop_found;

/*
* First deep stop state. Used to figure out when to save/restore
* hypervisor context.
*/
u64 pnv_first_deep_stop_state = MAX_STOP_STATE;

/*
* psscr value and mask of the deepest stop idle state.
* Used when a cpu is offlined.
*/
static u64 pnv_deepest_stop_psscr_val;
static u64 pnv_deepest_stop_psscr_mask;
static bool deepest_stop_found;

static int pnv_save_sprs_for_deep_states(void)
{
int cpu;
Expand All @@ -48,6 +73,8 @@ static int pnv_save_sprs_for_deep_states(void)
uint64_t hid4_val = mfspr(SPRN_HID4);
uint64_t hid5_val = mfspr(SPRN_HID5);
uint64_t hmeer_val = mfspr(SPRN_HMEER);
uint64_t msr_val = MSR_IDLE;
uint64_t psscr_val = pnv_deepest_stop_psscr_val;

for_each_possible_cpu(cpu) {
uint64_t pir = get_hard_smp_processor_id(cpu);
Expand All @@ -61,6 +88,18 @@ static int pnv_save_sprs_for_deep_states(void)
if (rc != 0)
return rc;

if (cpu_has_feature(CPU_FTR_ARCH_300)) {
rc = opal_slw_set_reg(pir, P9_STOP_SPR_MSR, msr_val);
if (rc)
return rc;

rc = opal_slw_set_reg(pir,
P9_STOP_SPR_PSSCR, psscr_val);

if (rc)
return rc;
}

/* HIDs are per core registers */
if (cpu_thread_in_core(cpu) == 0) {

Expand All @@ -72,17 +111,21 @@ static int pnv_save_sprs_for_deep_states(void)
if (rc != 0)
return rc;

rc = opal_slw_set_reg(pir, SPRN_HID1, hid1_val);
if (rc != 0)
return rc;
/* Only p8 needs to set extra HID regiters */
if (!cpu_has_feature(CPU_FTR_ARCH_300)) {

rc = opal_slw_set_reg(pir, SPRN_HID4, hid4_val);
if (rc != 0)
return rc;
rc = opal_slw_set_reg(pir, SPRN_HID1, hid1_val);
if (rc != 0)
return rc;

rc = opal_slw_set_reg(pir, SPRN_HID5, hid5_val);
if (rc != 0)
return rc;
rc = opal_slw_set_reg(pir, SPRN_HID4, hid4_val);
if (rc != 0)
return rc;

rc = opal_slw_set_reg(pir, SPRN_HID5, hid5_val);
if (rc != 0)
return rc;
}
}
}

Expand Down Expand Up @@ -240,14 +283,6 @@ static DEVICE_ATTR(fastsleep_workaround_applyonce, 0600,
show_fastsleep_workaround_applyonce,
store_fastsleep_workaround_applyonce);

/*
* The default stop state that will be used by ppc_md.power_save
* function on platforms that support stop instruction.
*/
static u64 pnv_default_stop_val;
static u64 pnv_default_stop_mask;
static bool default_stop_found;

/*
* Used for ppc_md.power_save which needs a function with no parameters
*/
Expand All @@ -256,20 +291,6 @@ static void power9_idle(void)
power9_idle_stop(pnv_default_stop_val, pnv_default_stop_mask);
}

/*
* First deep stop state. Used to figure out when to save/restore
* hypervisor context.
*/
u64 pnv_first_deep_stop_state = MAX_STOP_STATE;

/*
* psscr value and mask of the deepest stop idle state.
* Used when a cpu is offlined.
*/
static u64 pnv_deepest_stop_psscr_val;
static u64 pnv_deepest_stop_psscr_mask;
static bool deepest_stop_found;

#ifdef CONFIG_HOTPLUG_CPU
/*
* pnv_cpu_offline: A function that puts the CPU into the deepest
Expand Down

0 comments on commit 1e1601b

Please sign in to comment.