Inter-revision diff: patch 2

Comparing v5 (message) to v1 (message)

--- v5
+++ v1
@@ -1,133 +1,364 @@
 From: "Gautham R. Shenoy" <ego@linux.vnet.ibm.com>
 
-Balbir pointed out that in idle_book3s.S and powernv/idle.c some
-functions and variables had power9 in their names while some others
-had arch300.
-
-This patch uniformly renames all instances of "power9" in the
-variables/function/comments occuring in these files to "arch300" in
-order to make them consistent.
+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.
+
+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
 
 Signed-off-by: Gautham R. Shenoy <ego@linux.vnet.ibm.com>
 ---
-New patch in v5
-
- arch/powerpc/include/asm/processor.h  |  2 +-
- arch/powerpc/kernel/idle_book3s.S     | 13 +++++++------
- arch/powerpc/platforms/powernv/idle.c |  6 +++---
- arch/powerpc/platforms/powernv/smp.c  |  2 +-
- drivers/cpuidle/cpuidle-powernv.c     |  2 +-
- 5 files changed, 13 insertions(+), 12 deletions(-)
+ 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(-)
 
 diff --git a/arch/powerpc/include/asm/processor.h b/arch/powerpc/include/asm/processor.h
-index c07c31b..4b47308 100644
+index 68e3bf5..4ead497 100644
 --- a/arch/powerpc/include/asm/processor.h
 +++ b/arch/powerpc/include/asm/processor.h
-@@ -458,7 +458,7 @@ static inline unsigned long get_clean_sp(unsigned long sp, int is_32)
+@@ -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 arch300_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 be90e2f..7f6657f 100644
+index 32d666b..9f77cd4 100644
 --- a/arch/powerpc/kernel/idle_book3s.S
 +++ b/arch/powerpc/kernel/idle_book3s.S
-@@ -106,8 +106,9 @@ core_idle_lock_held:
- 
- /*
-  * Pass requested state in r3:
-- *	r3 - PNV_THREAD_NAP/SLEEP/WINKLE in POWER8
-- *	   - Requested STOP state in POWER9
-+ *	r3 - PNV_THREAD_NAP/SLEEP/WINKLE on ISA 2.07 or less
-+	     (POWER7,POWER8)
-+ *	   - Requested STOP state on ISA 3.0 CPUs
-  *
-  * To check IRQ_HAPPENED in r4
-  * 	0 - don't check
-@@ -357,7 +358,7 @@ ALT_FTR_SECTION_END_NESTED_IFSET(CPU_FTR_ARCH_207S, 66);		\
- /*
-  * r3 - requested stop state
-  */
--_GLOBAL(power9_idle_stop)
-+_GLOBAL(arch300_idle_stop)
- 	LOAD_REG_IMMEDIATE(r4, PSSCR_HV_TEMPLATE)
- 	or	r4,r4,r3
- 	mtspr	SPRN_PSSCR, r4
-@@ -377,7 +378,7 @@ _GLOBAL(pnv_restore_hyp_resource)
- BEGIN_FTR_SECTION
- 	ld	r2,PACATOC(r13);
- 	/*
--	 * POWER ISA 3. Use PSSCR to determine if we
-+	 * POWER ISA 3.0. Use PSSCR to determine if we
- 	 * are waking up from deep idle state
- 	 */
- 	LOAD_REG_ADDRBASE(r5,pnv_first_deep_stop_state)
-@@ -429,8 +430,8 @@ END_FTR_SECTION_IFSET(CPU_FTR_ARCH_300)
- /*
-  * 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
-+ * In ISA 2.07 (POWER8),called if waking up from fastsleep or winkle
-+ * In ISA 3.0, 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
+@@ -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..c3a2fac 100644
+index 479c256..575f2aa 100644
 --- a/arch/powerpc/platforms/powernv/idle.c
 +++ b/arch/powerpc/platforms/powernv/idle.c
-@@ -241,10 +241,10 @@ static DEVICE_ATTR(fastsleep_workaround_applyonce, 0600,
+@@ -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)
-+static void arch300_idle(void)
+ static void power9_idle(void)
  {
- 	/* Requesting stop state 0 */
+-	/* Requesting stop state 0 */
 -	power9_idle_stop(0);
-+	arch300_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
-@@ -415,7 +415,7 @@ 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;
-+		ppc_md.power_save = arch300_idle;
- 
+  * 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:
- 	return 0;
+ 	kfree(psscr_val);
++	kfree(psscr_mask);
++	kfree(residency_ns);
+ 	return rc;
+ }
+ 
+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
+ 
+ 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;
+ 
+ extern void pnv_lpc_init(void);
+ 
 diff --git a/arch/powerpc/platforms/powernv/smp.c b/arch/powerpc/platforms/powernv/smp.c
-index c789258..c931bb2 100644
+index c789258..55c4aaa 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)
+@@ -182,9 +182,10 @@ static void pnv_smp_cpu_kill_self(void)
+ 
  		ppc64_runlatch_off();
  
- 		if (cpu_has_feature(CPU_FTR_ARCH_300))
+-		if (cpu_has_feature(CPU_FTR_ARCH_300))
 -			srr1 = power9_idle_stop(pnv_deepest_stop_state);
-+			srr1 = arch300_idle_stop(pnv_deepest_stop_state);
- 		else if (idle_states & OPAL_PM_WINKLE_ENABLED)
+-		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 7fe442c..a7f6528 100644
+index f7ca891..6f2bee0 100644
 --- a/drivers/cpuidle/cpuidle-powernv.c
 +++ b/drivers/cpuidle/cpuidle-powernv.c
-@@ -102,7 +102,7 @@ static int stop_loop(struct cpuidle_device *dev,
+@@ -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]);
-+	arch300_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;
+-			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];
+ 		}
+ 
+ 		/*
+@@ -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;
+-			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];
+ 		}
+ #endif
+ 		powernv_states[nr_idle_states].exit_latency =
 -- 
 1.9.4
Keyboard shortcuts
hback out one level
jnext message in thread
kprevious message in thread
ldrill in
Escclose help / fold thread tree
?toggle this help