diff mbox series

[RFC,1/2] opal : Support for pre-entry and post-exit of stop state in opal

Message ID 20200427011703.65898-1-huntbag@linux.vnet.ibm.com
State New
Headers show
Series [RFC,1/2] opal : Support for pre-entry and post-exit of stop state in opal | expand

Checks

Context Check Description
snowpatch_ozlabs/snowpatch_job_snowpatch-skiboot-dco success Signed-off-by present
snowpatch_ozlabs/snowpatch_job_snowpatch-skiboot success Test snowpatch/job/snowpatch-skiboot on branch master
snowpatch_ozlabs/apply_patch success Successfully applied on branch master (0f1937ef40fca0c3212a9dff1010b832a24fb063)

Commit Message

Abhishek Goel April 27, 2020, 1:17 a.m. UTC
This patch provides opal support for save restore of sprs in idle stop
loop for LE opal. Opal support for stop states is needed to selectively
enable stop states or to introduce a quirk quickly in case a buggy
stop state is present.
We make a opal call from kernel if firmware-stop-support for stop
states is enabled. All the quirks for pre-entry of stop state is
handled inside opal. A call from opal is made into kernel where we
execute stop afer saving of NVGPRs.
After waking up from 0x100 vector in kernel, we enter back into opal.
All the quirks in post exit path, if any, are then handled in opal,
from where we return successfully back to kernel.

This patch provide support for shallow stop state only.

This idea was first proposed by Nick here:
https://patchwork.ozlabs.org/patch/1208159/

Signed-off-by: Abhishek Goel <huntbag@linux.vnet.ibm.com>
Signed-off-by: Nicholas Piggin <npiggin@gmail.com>
---

 v1->v2 : Added support for shallow stop states along with quirks.

 core/opal.c             | 111 ++++++++++++++++++++++++++++++++++++++++
 include/opal-api.h      |   8 ++-
 include/opal-internal.h |  10 ++++
 include/processor.h     |   4 ++
 4 files changed, 132 insertions(+), 1 deletion(-)
diff mbox series

Patch

diff --git a/core/opal.c b/core/opal.c
index 64fdfe62..2a82bdcf 100644
--- a/core/opal.c
+++ b/core/opal.c
@@ -44,6 +44,7 @@  static uint64_t opal_dynamic_events;
 extern uint32_t attn_trigger;
 extern uint32_t hir_trigger;
 
+struct os_ops os_ops;
 
 void opal_table_init(void)
 {
@@ -422,6 +423,116 @@  void add_opal_node(void)
 	memcons_add_properties();
 }
 
+/*
+ * Function to register all the os operations in opal.
+ * Currently registering a os_ops that will handle idle stop
+ * saving and restoring of sprs in kernel.
+ */
+static int64_t opal_register_os_ops(struct opal_os_ops *__os_ops)
+{
+	struct cpu_thread *cpu;
+
+	for_each_cpu(cpu) {
+		if (cpu == this_cpu())
+			continue;
+		if (cpu->state == cpu_state_os)
+			return OPAL_BUSY;
+	}
+
+	os_ops.os_idle_stop = (void *)be64_to_cpu(__os_ops->os_idle_stop);
+
+	return OPAL_SUCCESS;
+}
+opal_call(OPAL_REGISTER_OS_OPS, opal_register_os_ops, 1);
+
+struct p9_sprs {
+	/* per thread sprs that get lost in shallow states */
+	u64 amr;
+	u64 iamr;
+	u64 amor;
+	u64 uamor;
+};
+
+/*
+ * Opal function to handle idle stop in kernel.
+ */
+static uint64_t opal_cpu_idle(__be64 srr1_addr, uint64_t psscr)
+{
+	int pvr;
+	u64 mmcra;
+	u64 mmcr0 = 0;
+	struct p9_sprs sprs = {};
+	u64 *le_srr1 = (u64 *)be64_to_cpu(srr1_addr);
+
+	if (!os_ops.os_idle_stop)
+		return OPAL_UNSUPPORTED;
+
+	if (proc_gen != proc_gen_p9)
+		return OPAL_UNSUPPORTED;
+
+	/* Deep states are not supported for opal fallback */
+	if ((psscr & OPAL_PM_PSSCR_RL_MASK) >= 4)
+		return OPAL_UNSUPPORTED;
+
+	pvr = mfspr(SPR_PVR);
+	if (!(psscr & (OPAL_PM_PSSCR_EC|OPAL_PM_PSSCR_ESL))) {
+	        *le_srr1 = os_ops.os_idle_stop(psscr, false);
+		goto out;
+	}
+
+	/* EC=ESL=1 case */
+	if (PVR_VERS_MAJ(pvr) == 2 && (PVR_VERS_MIN(pvr) == 0))
+		/*
+		 * POWER9 DD2 can incorrectly set PMAO when waking up
+		 * after a state-loss idle. Saving and restoring MMCR0
+		 * over idle is a workaround.
+		 */
+		mmcr0	= mfspr(SPR_MMCR0);
+
+	/* Save sprs lost in shallow state */
+	sprs.amr	= mfspr(SPR_AMR);
+	sprs.iamr	= mfspr(SPR_IAMR);
+	sprs.amor	= mfspr(SPR_AMOR);
+	sprs.uamor	= mfspr(SPR_UAMOR);
+
+	*le_srr1 = os_ops.os_idle_stop(psscr, true);
+
+	if ((*le_srr1 & SPR_SRR1_PM_WAKE_MASK) != SPR_SRR1_PM_WAKE_NOLOSS) {
+
+		mtspr(SPR_AMR,		sprs.amr);
+		mtspr(SPR_IAMR,		sprs.iamr);
+		mtspr(SPR_AMOR,		sprs.amor);
+		mtspr(SPR_UAMOR,	sprs.uamor);
+
+		/*
+		 * Workaround for POWER9 DD2.0, if we lost resources, the ERAT
+		 * might have been corrupted and needs flushing. We also need
+		 * to reload MMCR0 (see mmcr0 comment above).
+		 */
+		if (PVR_VERS_MAJ(pvr) == 2 && (PVR_VERS_MIN(pvr) == 0)) {
+			/* Handle PPC_ISA_3_0_INVALIDATE_ERAT */
+			asm volatile (".long 0x7c1003e4": : : "memory");
+			mtspr(SPR_MMCR0, mmcr0);
+		}
+
+		/*
+		 * DD2.2 and earlier need to set then clear bit 60 in MMCRA
+		 * to ensure the PMU starts running.
+		 */
+		if (PVR_VERS_MAJ(pvr) == 2 && (PVR_VERS_MIN(pvr) <= 2)) {
+			mmcra = mfspr(SPR_MMCRA);
+			mmcra |= PPC_BIT(60);
+			mtspr(SPR_MMCRA, mmcra);
+			mmcra &= ~PPC_BIT(60);
+			mtspr(SPR_MMCRA, mmcra);
+		}
+	}
+
+out:
+	return OPAL_SUCCESS;
+}
+opal_call(OPAL_CPU_IDLE, opal_cpu_idle, 2);
+
 static struct lock evt_lock = LOCK_UNLOCKED;
 
 void opal_update_pending_evt(uint64_t evt_mask, uint64_t evt_values)
diff --git a/include/opal-api.h b/include/opal-api.h
index e90cab1e..a1e7d122 100644
--- a/include/opal-api.h
+++ b/include/opal-api.h
@@ -227,7 +227,9 @@ 
 #define OPAL_SECVAR_ENQUEUE_UPDATE		178
 #define OPAL_PHB_SET_OPTION			179
 #define OPAL_PHB_GET_OPTION			180
-#define OPAL_LAST				180
+#define OPAL_REGISTER_OS_OPS			181
+#define OPAL_CPU_IDLE				182
+#define OPAL_LAST				182
 
 #define QUIESCE_HOLD			1 /* Spin all calls at entry */
 #define QUIESCE_REJECT			2 /* Fail all calls with OPAL_BUSY */
@@ -1255,6 +1257,10 @@  struct opal_mpipl_fadump {
 	struct	opal_mpipl_region region[];
 };
 
+struct opal_os_ops {
+	__be64 os_idle_stop;
+};
+
 #endif /* __ASSEMBLY__ */
 
 #endif /* __OPAL_API_H */
diff --git a/include/opal-internal.h b/include/opal-internal.h
index f6ca7ac3..9368fb79 100644
--- a/include/opal-internal.h
+++ b/include/opal-internal.h
@@ -18,6 +18,14 @@  struct opal_table_entry {
 	u32	nargs;
 };
 
+struct os_ops {
+	/*
+	 * save_gprs help us distinguish between lite states and
+	 * non-lite states.
+	 */
+	int64_t (*os_idle_stop)(uint64_t psscr, bool save_gprs);
+};
+
 #ifdef __CHECKER__
 #define __opal_func_test_arg(__func, __nargs) 0
 #else
@@ -75,6 +83,8 @@  extern void opal_run_pollers(void);
 extern void opal_add_host_sync_notifier(bool (*notify)(void *data), void *data);
 extern void opal_del_host_sync_notifier(bool (*notify)(void *data), void *data);
 
+extern int64_t os_idle_stop(uint64_t psscr, bool save_gprs);
+
 /*
  * Opal internal function prototype
  */
diff --git a/include/processor.h b/include/processor.h
index 57c2ee13..ce86017f 100644
--- a/include/processor.h
+++ b/include/processor.h
@@ -73,12 +73,16 @@ 
 #define SPR_HID4	0x3f4
 #define SPR_HID5	0x3f6
 #define SPR_PIR		0x3ff	/* RO: Processor Identification */
+#define SPR_UAMOR	0x9d	/* User Authority Mask Override Register */
+#define SPR_MMCR0	795
+#define SPR_MMCRA	0x312
 
 /* Bits in SRR1 */
 
 #define SPR_SRR1_PM_WAKE_MASK	0x3c0000	/* PM wake reason for P8/9 */
 #define SPR_SRR1_PM_WAKE_SRESET	0x100000
 #define SPR_SRR1_PM_WAKE_MCE	0x3c0000	/* Use reserved value for MCE */
+#define SPR_SRR1_PM_WAKE_NOLOSS	0x100000
 
 /* Bits in LPCR */