--- v1
+++ v4
@@ -1,364 +1,158 @@
From: "Gautham R. Shenoy" <ego@linux.vnet.ibm.com>
-The power9_idle_stop method currently takes only the requested stop
-level as a parameter and picks up the rest of the PSSCR bits from a
-hand-coded macro. This is not a very flexible design, especially when
-the firmware has the capability to communicate the psscr value and the
-mask associated with a particular stop state via device tree.
+In the current code for powernv_add_idle_states, there is a lot of code
+duplication while initializing an idle state in powernv_states table.
-This patch modifies the power9_idle_stop API to take as parameters the
-PSSCR value and the PSSCR mask corresponding to the stop state that
-needs to be set. These PSSCR value and mask are respectively obtained
-by parsing the "ibm,cpu-idle-state-psscr" and
-"ibm,cpu-idle-state-psscr-mask" fields from the device tree.
-
-In addition to this, the patch adds support for handling stop states
-for which ESL and EC bits in the PSSCR are zero. As per the
-architecture, a wakeup from these stop states resumes execution from
-the subsequent instruction as opposed to waking up at the System
-Vector.
-
-This patch depends on the following skiboot patch that exports the
-PSSCR values and the mask for all the stop states:
-https://lists.ozlabs.org/pipermail/skiboot/2016-September/004869.html
+Add an inline helper function to populate the powernv_states[] table for
+a given idle state. Invoke this for populating the "Nap", "Fastsleep"
+and the stop states in powernv_add_idle_states.
Signed-off-by: Gautham R. Shenoy <ego@linux.vnet.ibm.com>
---
- arch/powerpc/include/asm/processor.h | 3 +-
- arch/powerpc/kernel/idle_book3s.S | 31 ++++++++-----
- arch/powerpc/platforms/powernv/idle.c | 76 +++++++++++++++++++++++++++-----
- arch/powerpc/platforms/powernv/powernv.h | 3 +-
- arch/powerpc/platforms/powernv/smp.c | 7 +--
- drivers/cpuidle/cpuidle-powernv.c | 30 ++++++++++---
- 6 files changed, 115 insertions(+), 35 deletions(-)
+ drivers/cpuidle/cpuidle-powernv.c | 85 ++++++++++++++++++++++-----------------
+ include/linux/cpuidle.h | 1 +
+ 2 files changed, 50 insertions(+), 36 deletions(-)
-diff --git a/arch/powerpc/include/asm/processor.h b/arch/powerpc/include/asm/processor.h
-index 68e3bf5..4ead497 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_psscr_val,
-+ unsigned long stop_psscr_mask);
-
- 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..9f77cd4 100644
---- a/arch/powerpc/kernel/idle_book3s.S
-+++ b/arch/powerpc/kernel/idle_book3s.S
-@@ -40,9 +40,7 @@
- #define _WORC GPR11
- #define _PTCR GPR12
-
--#define PSSCR_HV_TEMPLATE PSSCR_ESL | PSSCR_EC | \
-- PSSCR_PSLL_MASK | PSSCR_TR_MASK | \
-- PSSCR_MTL_MASK
-+#define PSSCR_EC_ESL_MASK_SHIFTED (PSSCR_EC | PSSCR_ESL) >> 16
-
- .text
-
-@@ -247,13 +245,23 @@ enter_winkle:
- IDLE_STATE_ENTER_SEQ_NORET(PPC_WINKLE)
-
- /*
-- * r3 - requested stop state
-+ * r3 - PSSCR value corresponding to the requested stop state.
- */
- power_enter_stop:
- /*
-+ * Check if we are executing the lite variant with ESL=EC=0
-+ */
-+ andis. r4, r3, PSSCR_EC_ESL_MASK_SHIFTED
-+ andi. r3, r3, PSSCR_RL_MASK /* r3 = requested stop state */
-+ cmpdi r4, 0
-+ bne 1f
-+ IDLE_STATE_ENTER_SEQ(PPC_STOP)
-+ li r3,0 /* Since we didn't lose state, return 0 */
-+ b pnv_wakeup_noloss
-+/*
- * Check if the requested state is a deep idle state.
- */
-- LOAD_REG_ADDRBASE(r5,pnv_first_deep_stop_state)
-+1: LOAD_REG_ADDRBASE(r5,pnv_first_deep_stop_state)
- ld r4,ADDROFF(pnv_first_deep_stop_state)(r5)
- cmpd r3,r4
- bge 2f
-@@ -330,16 +338,17 @@ ALT_FTR_SECTION_END_NESTED_IFSET(CPU_FTR_ARCH_207S, 66); \
- ld r3,ORIG_GPR3(r1); /* Restore original r3 */ \
- 20: nop;
-
--
- /*
-- * r3 - requested stop state
-+ * r3 - The PSSCR value corresponding to the stop state.
-+ * r4 - The PSSCR mask corrresonding to the stop state.
- */
- _GLOBAL(power9_idle_stop)
-- LOAD_REG_IMMEDIATE(r4, PSSCR_HV_TEMPLATE)
-- or r4,r4,r3
-- mtspr SPRN_PSSCR, r4
-- li r4, 1
-+ mfspr r5, SPRN_PSSCR
-+ andc r5, r5, r4
-+ or r3, r3, r5
-+ mtspr SPRN_PSSCR, r3
- LOAD_REG_ADDR(r5,power_enter_stop)
-+ li r4, 1
- b pnv_powersave_common
- /* No return */
- /*
-diff --git a/arch/powerpc/platforms/powernv/idle.c b/arch/powerpc/platforms/powernv/idle.c
-index 479c256..575f2aa 100644
---- a/arch/powerpc/platforms/powernv/idle.c
-+++ b/arch/powerpc/platforms/powernv/idle.c
-@@ -237,15 +237,21 @@ 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.
-+ */
-+u64 pnv_default_stop_val;
-+u64 pnv_default_stop_mask;
-
- /*
- * Used for ppc_md.power_save which needs a function with no parameters
- */
- static void power9_idle(void)
- {
-- /* Requesting stop state 0 */
-- power9_idle_stop(0);
-+ 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.
-@@ -253,9 +259,11 @@ static void power9_idle(void)
- u64 pnv_first_deep_stop_state = MAX_STOP_STATE;
-
- /*
-- * Deepest stop idle state. Used when a cpu is offlined
-+ * psscr value and mask of the deepest stop idle state.
-+ * Used when a cpu is offlined.
- */
--u64 pnv_deepest_stop_state;
-+u64 pnv_deepest_stop_psscr_val;
-+u64 pnv_deepest_stop_psscr_mask;
-
- /*
- * Power ISA 3.0 idle initialization.
-@@ -302,28 +310,61 @@ static int __init pnv_arch300_idle_init(struct device_node *np, u32 *flags,
- int dt_idle_states)
- {
- u64 *psscr_val = NULL;
-+ u64 *psscr_mask = NULL;
-+ u32 *residency_ns = NULL;
-+ u64 max_residency_ns = 0;
- int rc = 0, i;
-+ bool default_stop_found = false;
-
- psscr_val = kcalloc(dt_idle_states, sizeof(*psscr_val),
- GFP_KERNEL);
-- if (!psscr_val) {
-+ psscr_mask = kcalloc(dt_idle_states, sizeof(*psscr_mask),
-+ GFP_KERNEL);
-+ residency_ns = kcalloc(dt_idle_states, sizeof(*residency_ns),
-+ GFP_KERNEL);
-+
-+ if (!psscr_val || !psscr_mask || !residency_ns) {
- rc = -1;
- goto out;
- }
-+
- if (of_property_read_u64_array(np,
- "ibm,cpu-idle-state-psscr",
- psscr_val, dt_idle_states)) {
-- pr_warn("cpuidle-powernv: missing ibm,cpu-idle-states-psscr in DT\n");
-+ pr_warn("cpuidle-powernv: missing ibm,cpu-idle-state-psscr in DT\n");
-+ rc = -1;
-+ goto out;
-+ }
-+
-+ if (of_property_read_u64_array(np,
-+ "ibm,cpu-idle-state-psscr-mask",
-+ psscr_mask, dt_idle_states)) {
-+ pr_warn("cpuidle-powernv: missing ibm,cpu-idle-state-psscr-mask in DT\n");
- rc = -1;
- goto out;
- }
-
-+ if (of_property_read_u32_array(np,
-+ "ibm,cpu-idle-state-residency-ns",
-+ residency_ns, dt_idle_states)) {
-+ pr_warn("cpuidle-powernv: missing ibm,cpu-idle-state-residency-ns in DT\n");
-+ rc = -1;
-+ goto out;
-+
-+ }
-+
- /*
-- * Set pnv_first_deep_stop_state and pnv_deepest_stop_state.
-+ * Set pnv_first_deep_stop_state, pnv_deepest_stop_psscr_{val,mask},
-+ * and the pnv_default_stop_{val,mask}.
-+ *
- * pnv_first_deep_stop_state should be set to the first stop
- * level to cause hypervisor state loss.
-- * pnv_deepest_stop_state should be set to the deepest stop
-- * stop state.
-+ *
-+ * pnv_deepest_stop_{val,mask} should be set to values corresponding to
-+ * the deepest stop state.
-+ *
-+ * pnv_default_stop_{val,mask} should be set to values corresponding to
-+ * the shallowest (OPAL_PM_STOP_INST_FAST) loss-less stop state.
- */
- pnv_first_deep_stop_state = MAX_STOP_STATE;
- for (i = 0; i < dt_idle_states; i++) {
-@@ -333,12 +374,23 @@ static int __init pnv_arch300_idle_init(struct device_node *np, u32 *flags,
- (pnv_first_deep_stop_state > psscr_rl))
- pnv_first_deep_stop_state = psscr_rl;
-
-- if (pnv_deepest_stop_state < psscr_rl)
-- pnv_deepest_stop_state = psscr_rl;
-- }
-+ if (max_residency_ns < residency_ns[i]) {
-+ max_residency_ns = residency_ns[i];
-+ pnv_deepest_stop_psscr_val = psscr_val[i];
-+ pnv_deepest_stop_psscr_mask = psscr_mask[i];
-+ }
-
-+ if (!default_stop_found &&
-+ (flags[i] & OPAL_PM_STOP_INST_FAST)) {
-+ pnv_default_stop_val = psscr_val[i];
-+ pnv_default_stop_mask = psscr_mask[i];
-+ default_stop_found = true;
-+ }
-+ }
- out:
- kfree(psscr_val);
-+ kfree(psscr_mask);
-+ kfree(residency_ns);
- return rc;
+diff --git a/drivers/cpuidle/cpuidle-powernv.c b/drivers/cpuidle/cpuidle-powernv.c
+index 7fe442c..db18af1 100644
+--- a/drivers/cpuidle/cpuidle-powernv.c
++++ b/drivers/cpuidle/cpuidle-powernv.c
+@@ -167,6 +167,24 @@ static int powernv_cpuidle_driver_init(void)
+ return 0;
}
-diff --git a/arch/powerpc/platforms/powernv/powernv.h b/arch/powerpc/platforms/powernv/powernv.h
-index da7c843..6130522 100644
---- a/arch/powerpc/platforms/powernv/powernv.h
-+++ b/arch/powerpc/platforms/powernv/powernv.h
-@@ -18,7 +18,8 @@ static inline void pnv_pci_shutdown(void) { }
- #endif
++static inline void add_powernv_state(int index, const char *name,
++ unsigned int flags,
++ int (*idle_fn)(struct cpuidle_device *,
++ struct cpuidle_driver *,
++ int),
++ unsigned int target_residency,
++ unsigned int exit_latency,
++ u64 psscr_val)
++{
++ strlcpy(powernv_states[index].name, name, CPUIDLE_NAME_LEN);
++ strlcpy(powernv_states[index].desc, name, CPUIDLE_NAME_LEN);
++ powernv_states[index].flags = flags;
++ powernv_states[index].target_residency = target_residency;
++ powernv_states[index].exit_latency = exit_latency;
++ powernv_states[index].enter = idle_fn;
++ stop_psscr_table[index] = psscr_val;
++}
++
+ static int powernv_add_idle_states(void)
+ {
+ struct device_node *power_mgt;
+@@ -236,6 +254,7 @@ static int powernv_add_idle_states(void)
+ "ibm,cpu-idle-state-residency-ns", residency_ns, dt_idle_states);
- extern u32 pnv_get_supported_cpuidle_states(void);
--extern u64 pnv_deepest_stop_state;
-+extern u64 pnv_deepest_stop_psscr_val;
-+extern u64 pnv_deepest_stop_psscr_mask;
+ for (i = 0; i < dt_idle_states; i++) {
++ unsigned int exit_latency, target_residency;
+ /*
+ * If an idle state has exit latency beyond
+ * POWERNV_THRESHOLD_LATENCY_NS then don't use it
+@@ -243,28 +262,33 @@ static int powernv_add_idle_states(void)
+ */
+ if (latency_ns[i] > POWERNV_THRESHOLD_LATENCY_NS)
+ continue;
++ /*
++ * Firmware passes residency and latency values in ns.
++ * cpuidle expects it in us.
++ */
++ exit_latency = ((unsigned int)latency_ns[i]) / 1000;
++ if (!rc)
++ target_residency = residency_ns[i] / 1000;
++ else
++ target_residency = 0;
- extern void pnv_lpc_init(void);
-
-diff --git a/arch/powerpc/platforms/powernv/smp.c b/arch/powerpc/platforms/powernv/smp.c
-index c789258..55c4aaa 100644
---- a/arch/powerpc/platforms/powernv/smp.c
-+++ b/arch/powerpc/platforms/powernv/smp.c
-@@ -182,9 +182,10 @@ 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);
-- else if (idle_states & OPAL_PM_WINKLE_ENABLED)
-+ if (cpu_has_feature(CPU_FTR_ARCH_300)) {
-+ srr1 = power9_idle_stop(pnv_deepest_stop_psscr_val,
-+ pnv_deepest_stop_psscr_mask);
-+ } else if (idle_states & OPAL_PM_WINKLE_ENABLED)
- srr1 = power7_winkle();
- else if ((idle_states & OPAL_PM_SLEEP_ENABLED) ||
- (idle_states & OPAL_PM_SLEEP_ENABLED_ER1))
-diff --git a/drivers/cpuidle/cpuidle-powernv.c b/drivers/cpuidle/cpuidle-powernv.c
-index f7ca891..6f2bee0 100644
---- a/drivers/cpuidle/cpuidle-powernv.c
-+++ b/drivers/cpuidle/cpuidle-powernv.c
-@@ -30,7 +30,11 @@ struct cpuidle_driver powernv_idle_driver = {
- static int max_idle_state;
- static struct cpuidle_state *cpuidle_state_table;
-
--static u64 stop_psscr_table[CPUIDLE_STATE_MAX];
-+struct stop_psscr_table {
-+ u64 val;
-+ u64 mask;
-+};
-+static struct stop_psscr_table stop_psscr_table[CPUIDLE_STATE_MAX];
-
- static u64 snooze_timeout;
- static bool snooze_timeout_en;
-@@ -102,7 +106,8 @@ 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].val,
-+ stop_psscr_table[index].mask);
- ppc64_runlatch_on();
- return index;
- }
-@@ -186,6 +191,7 @@ static int powernv_add_idle_states(void)
- u32 residency_ns[CPUIDLE_STATE_MAX];
- u32 flags[CPUIDLE_STATE_MAX];
- u64 psscr_val[CPUIDLE_STATE_MAX];
-+ u64 psscr_mask[CPUIDLE_STATE_MAX];
- const char *names[CPUIDLE_STATE_MAX];
- int i, rc;
-
-@@ -233,14 +239,22 @@ static int powernv_add_idle_states(void)
-
- /*
- * If the idle states use stop instruction, probe for psscr values
-- * which are necessary to specify required stop level.
-+ * and psscr mask which are necessary to specify required stop level.
- */
-- if (flags[0] & (OPAL_PM_STOP_INST_FAST | OPAL_PM_STOP_INST_DEEP))
-+ if (flags[0] & (OPAL_PM_STOP_INST_FAST | OPAL_PM_STOP_INST_DEEP)) {
- 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");
-+ pr_warn("cpuidle-powernv: missing ibm,cpu-idle-state-psscr in DT\n");
-+ goto out;
-+ }
-+
-+ if (of_property_read_u64_array(power_mgt,
-+ "ibm,cpu-idle-state-psscr-mask", psscr_mask,
-+ dt_idle_states)) {
-+ pr_warn("cpuidle-powernv:Missing ibm,cpu-idle-state-psscr-mask in DT\n");
- goto out;
- }
-+ }
-
- rc = of_property_read_u32_array(power_mgt,
- "ibm,cpu-idle-state-residency-ns", residency_ns, dt_idle_states);
-@@ -274,7 +288,8 @@ static int powernv_add_idle_states(void)
- powernv_states[nr_idle_states].flags = 0;
-
- powernv_states[nr_idle_states].enter = stop_loop;
+ /*
+- * Cpuidle accepts exit_latency and target_residency in us.
+- * Use default target_residency values if f/w does not expose it.
++ * For nap and fastsleep, use default target_residency
++ * values if f/w does not expose it.
+ */
+ if (flags[i] & OPAL_PM_NAP_ENABLED) {
++ if (!rc)
++ target_residency = 100;
+ /* Add NAP state */
+- strcpy(powernv_states[nr_idle_states].name, "Nap");
+- strcpy(powernv_states[nr_idle_states].desc, "Nap");
+- powernv_states[nr_idle_states].flags = 0;
+- powernv_states[nr_idle_states].target_residency = 100;
+- powernv_states[nr_idle_states].enter = nap_loop;
++ add_powernv_state(nr_idle_states, "Nap",
++ CPUIDLE_FLAG_NONE, nap_loop,
++ target_residency, exit_latency, 0);
+ } else if ((flags[i] & OPAL_PM_STOP_INST_FAST) &&
+ !(flags[i] & OPAL_PM_TIMEBASE_STOP)) {
+- strncpy(powernv_states[nr_idle_states].name,
+- names[i], CPUIDLE_NAME_LEN);
+- strncpy(powernv_states[nr_idle_states].desc,
+- names[i], CPUIDLE_NAME_LEN);
+- powernv_states[nr_idle_states].flags = 0;
+-
+- powernv_states[nr_idle_states].enter = stop_loop;
- stop_psscr_table[nr_idle_states] = psscr_val[i];
-+ stop_psscr_table[nr_idle_states].val = psscr_val[i];
-+ stop_psscr_table[nr_idle_states].mask = psscr_mask[i];
++ add_powernv_state(nr_idle_states, names[i],
++ CPUIDLE_FLAG_NONE, stop_loop,
++ target_residency, exit_latency,
++ psscr_val[i]);
}
/*
-@@ -299,7 +314,8 @@ static int powernv_add_idle_states(void)
-
- powernv_states[nr_idle_states].flags = CPUIDLE_FLAG_TIMER_STOP;
- powernv_states[nr_idle_states].enter = stop_loop;
+@@ -274,32 +298,21 @@ static int powernv_add_idle_states(void)
+ #ifdef CONFIG_TICK_ONESHOT
+ if (flags[i] & OPAL_PM_SLEEP_ENABLED ||
+ flags[i] & OPAL_PM_SLEEP_ENABLED_ER1) {
++ if (!rc)
++ target_residency = 300000;
+ /* Add FASTSLEEP state */
+- strcpy(powernv_states[nr_idle_states].name, "FastSleep");
+- strcpy(powernv_states[nr_idle_states].desc, "FastSleep");
+- powernv_states[nr_idle_states].flags = CPUIDLE_FLAG_TIMER_STOP;
+- powernv_states[nr_idle_states].target_residency = 300000;
+- powernv_states[nr_idle_states].enter = fastsleep_loop;
++ add_powernv_state(nr_idle_states, "FastSleep",
++ CPUIDLE_FLAG_TIMER_STOP,
++ fastsleep_loop,
++ target_residency, exit_latency, 0);
+ } else if ((flags[i] & OPAL_PM_STOP_INST_DEEP) &&
+ (flags[i] & OPAL_PM_TIMEBASE_STOP)) {
+- strncpy(powernv_states[nr_idle_states].name,
+- names[i], CPUIDLE_NAME_LEN);
+- strncpy(powernv_states[nr_idle_states].desc,
+- names[i], CPUIDLE_NAME_LEN);
+-
+- powernv_states[nr_idle_states].flags = CPUIDLE_FLAG_TIMER_STOP;
+- powernv_states[nr_idle_states].enter = stop_loop;
- stop_psscr_table[nr_idle_states] = psscr_val[i];
-+ stop_psscr_table[nr_idle_states].val = psscr_val[i];
-+ stop_psscr_table[nr_idle_states].mask = psscr_mask[i];
++ add_powernv_state(nr_idle_states, names[i],
++ CPUIDLE_FLAG_TIMER_STOP, stop_loop,
++ target_residency, exit_latency,
++ psscr_val[i]);
}
#endif
- powernv_states[nr_idle_states].exit_latency =
+- powernv_states[nr_idle_states].exit_latency =
+- ((unsigned int)latency_ns[i]) / 1000;
+-
+- if (!rc) {
+- powernv_states[nr_idle_states].target_residency =
+- ((unsigned int)residency_ns[i]) / 1000;
+- }
+-
+ nr_idle_states++;
+ }
+ out:
+diff --git a/include/linux/cpuidle.h b/include/linux/cpuidle.h
+index bb31373..c4e10f8 100644
+--- a/include/linux/cpuidle.h
++++ b/include/linux/cpuidle.h
+@@ -62,6 +62,7 @@ struct cpuidle_state {
+ };
+
+ /* Idle State Flags */
++#define CPUIDLE_FLAG_NONE (0x00)
+ #define CPUIDLE_FLAG_COUPLED (0x02) /* state applies to multiple cpus */
+ #define CPUIDLE_FLAG_TIMER_STOP (0x04) /* timer is stopped on this state */
+
--
1.9.4