[RFC,2/2] SLW : Support spr save-restore using new opal call

Message ID 20180802044507.7579-3-akshay.adiga@linux.vnet.ibm.com
State New
Headers show
Series
  • New device-tree format & Opal based idle save-restore
Related show

Checks

Context Check Description
snowpatch_ozlabs/apply_patch fail Failed to apply to any branch

Commit Message

Akshay Adiga Aug. 2, 2018, 4:45 a.m.
From: Abhishek Goel <huntbag@linux.vnet.ibm.com>

In an attempt to make the powernv idle code backward compatible,
and to some extent forward compatible, add support for pre-stop entry
and post-stop exit actions in OPAL. If a kernel knows about this
opal call, then just a firmware supporting newer hardware is required,
instead of waiting for kernel updates.
This opal support for stop can be indicated by a compatibility string
in the newly proposed device-tree format (Ref:
https://patchwork.ozlabs.org/patch/923120/). Thus the kernel can
enable a stop state, when the opal support for it exists, even if the
kernel support doesn't exist.

Signed-off-by: Abhishek Goel <huntbag@linux.vnet.ibm.com>
---
 hw/chiptod.c        |   7 +-
 hw/slw.c            | 152 +++++++++++++++++++++++++++++++++++++++++++-
 include/opal-api.h  |   6 +-
 include/processor.h |  13 ++++
 4 files changed, 174 insertions(+), 4 deletions(-)

Patch

diff --git a/hw/chiptod.c b/hw/chiptod.c
index df1274ca..7f52f6ac 100644
--- a/hw/chiptod.c
+++ b/hw/chiptod.c
@@ -1599,7 +1599,7 @@  error_out:
 	return rc;
 }
 
-static int64_t opal_resync_timebase(void)
+int64_t __opal_resync_timebase(void)
 {
 	if (!chiptod_wakeup_resync()) {
 		prerror("OPAL: Resync timebase failed on CPU 0x%04x\n",
@@ -1608,6 +1608,11 @@  static int64_t opal_resync_timebase(void)
 	}
 	return OPAL_SUCCESS;
 }
+
+static int64_t opal_resync_timebase(void)
+{
+	return __opal_resync_timebase();
+}
 opal_call(OPAL_RESYNC_TIMEBASE, opal_resync_timebase, 0);
 
 static void chiptod_print_tb(void *data __unused)
diff --git a/hw/slw.c b/hw/slw.c
index b00790ef..ec9e56a2 100644
--- a/hw/slw.c
+++ b/hw/slw.c
@@ -414,7 +414,7 @@  static bool idle_prepare_core(struct proc_chip *chip, struct cpu_thread *c)
 
 /* Define device-tree fields */
 #define MAX_NAME_LEN	16
-#define MAX_VERSION_LEN	25
+#define MAX_VERSION_LEN	50
 struct cpu_idle_states {
 	char name[MAX_NAME_LEN];
 	char version[MAX_VERSION_LEN];
@@ -429,6 +429,7 @@  struct cpu_idle_states {
 	u64 pm_ctrl_reg_val;
 	u64 pm_ctrl_reg_mask;
 	u32 flags;
+	bool opal_supported;
 };
 
 static struct cpu_idle_states power7_cpu_idle_states[] = {
@@ -631,7 +632,6 @@  static struct cpu_idle_states power9_cpu_idle_states[] = {
 	{
 		.name = "stop8",
 		.dt_node_type = 1,
-		.version = "ibm,cpuidle-state-v1",
 		.version = "ibm,state-v1",
 		.type = "cpuoffline",
 		.opal_supported = true,
@@ -851,6 +851,154 @@  static void slw_late_init_p9(struct proc_chip *chip)
 	}
 }
 
+#define PTCR_IDX	0
+#define RPR_IDX		1
+#define SPURR_IDX	2
+#define PURR_IDX	3
+#define TSCR_IDX	4
+#define DSCR_IDX	5
+#define AMOR_IDX	6
+#define WORT_IDX	7
+#define WORC_IDX	8
+#define LPCR_IDX	9
+#define PID_IDX		10
+#define LDBAR_IDX	11
+#define FSCR_IDX	12
+#define HFSCR_IDX	13
+#define MMCRA_IDX	14
+#define MMCR0_IDX	15
+#define MMCR1_IDX	16
+#define MMCR2_IDX	17
+#define SPRG3_IDX	18
+#define PSSCR_RL_MASK	0xF
+#define PSSCR_PLS_MASK	0xF000000000000000UL
+#define PSSCR_PLS_SHIFT	60
+#define SRR1_WS_HVLOSS  0x30000
+#define SCOPE_CORE	0
+#define SCOPE_THREAD	1
+
+/*
+ * opal_cpuidle_save: Save the SPRs and any other resources
+ *			when going to a deep idle stop states.
+ * @stop_sprs : Pointer to a array where the SPR values of
+ *		the relevant SPRs of this CPU have to be saved.
+ * @scope     : Defines if the saving needs to be done
+ *		for per-thread resources or per-core resources.
+ * @psscr     : The requested psscr values
+ * @srr1      : The SRR1 value
+ * Returns OPAL_EMPTY when stop_sprs is NULL.
+ * Returns OPAL_UNSUPPORTED if we are going into a shallow state.
+ * Returns OPAL_SUCCESS in all the other cases.
+ * Returns OPAL_PARAMETER if scope is incorrectly passed.
+ */
+static int opal_cpuidle_save(u64 *stop_sprs, int scope, u64 psscr)
+{
+	if (scope != SCOPE_CORE && scope != SCOPE_THREAD) {
+		prlog(PR_ERR, "opal_cpuidle_save : invalid scope\n");
+		return OPAL_PARAMETER;
+	}
+	if (!stop_sprs) {
+		prlog(PR_ERR, "opal_cpuidle_save : unallocated memory pointer\n");
+		return OPAL_EMPTY;
+	}
+	/*
+	 * TODO Fix this to use the RL value of the first thread
+	 * that loses hypervisor resources.
+	 */
+	if ((psscr & PSSCR_RL_MASK) < 4) {
+		prlog(PR_ERR, "opal_cpuidle_save : unexpected opal call\n");
+		return OPAL_UNSUPPORTED;
+	}
+	/*
+	 * Saving all the sprs. In future, We may save core resources
+	 * only if the thread entering in deep stop is the last in the
+	 * core. scope can be used to take that decision.
+	 */
+	stop_sprs[RPR_IDX]   = mfspr(SPR_RPR);
+	stop_sprs[WORC_IDX]  = mfspr(SPR_WORC);
+	stop_sprs[PTCR_IDX]  = mfspr(SPR_PTCR);
+	stop_sprs[TSCR_IDX]  = mfspr(SPR_TSCR);
+	stop_sprs[AMOR_IDX]  = mfspr(SPR_AMOR);
+
+	stop_sprs[WORT_IDX]  = mfspr(SPR_WORT);
+	stop_sprs[PURR_IDX]  = mfspr(SPR_PURR);
+	stop_sprs[SPURR_IDX] = mfspr(SPR_SPURR);
+	stop_sprs[DSCR_IDX]  = mfspr(SPR_DSCR);
+	stop_sprs[LPCR_IDX]  = mfspr(SPR_LPCR);
+	stop_sprs[PID_IDX]   = mfspr(SPR_PID);
+	stop_sprs[LDBAR_IDX] = mfspr(SPR_LDBAR);
+	stop_sprs[FSCR_IDX]  = mfspr(SPR_FSCR);
+	stop_sprs[HFSCR_IDX] = mfspr(SPR_HFSCR);
+	stop_sprs[MMCRA_IDX] = mfspr(SPR_MMCRA);
+	stop_sprs[MMCR0_IDX] = mfspr(SPR_MMCR0);
+	stop_sprs[MMCR1_IDX] = mfspr(SPR_MMCR1);
+	stop_sprs[MMCR2_IDX] = mfspr(SPR_MMCR2);
+	stop_sprs[SPRG3_IDX] = mfspr(SPR_SPRG3);
+	return OPAL_SUCCESS;
+}
+
+opal_call(OPAL_IDLE_SAVE, opal_cpuidle_save, 3);
+
+/*
+ * opal_cpuidle_restore: Restore the SPRs and any other resources
+ *			on wakeup from a  deep idle stop states.
+ * @stop_sprs : Pointer to a array where the SPR values of
+ *		the relevant SPRs of this CPU have been stored.
+ * @scope     : Defines if the restoration needs to be done
+ *		for per-thread resources of per-core resources.
+ * @psscr     : The psscr value at wakeup from stop.
+ * @srr1      : The SRR1 value at wakeup from stop.
+ * Returns OPAL_EMPTY when stop_sprs is NULL.
+ * Returns OPAL_UNSUPPORTED if we woke up from a shallow state.
+ * Returns OPAL_SUCCESS in all the other cases.
+ * Returns OPAL_PARAMETER if scope is incorrectly passed.
+ */
+static int opal_cpuidle_restore(u64 *stop_sprs, int scope, u64 psscr, u64 srr1)
+{
+	if (scope != SCOPE_CORE && scope != SCOPE_THREAD) {
+		prlog(PR_ERR, "opal_cpuidle_save : invalid scope\n");
+		return OPAL_PARAMETER;
+	}
+	if (!stop_sprs) {
+		prlog(PR_ERR, "opal_cpuidle_save : incorrect pointer to save area\n");
+		return OPAL_EMPTY;
+	}
+	if ((psscr & PSSCR_PLS_MASK) >> PSSCR_PLS_SHIFT < 4) {
+		prlog(PR_ERR, "opal_cpuidle_save : unexpected opal call\n");
+		return OPAL_UNSUPPORTED;
+	}
+	/* if CORE scope, restore core resources as well as thread resources */
+	if (scope == SCOPE_CORE) {
+		/* In case of complete hypervisor state loss
+		 * we need to resync timebase
+		 */
+		if (srr1 & SRR1_WS_HVLOSS)
+			__opal_resync_timebase();
+		mtspr(SPR_RPR,  stop_sprs[RPR_IDX]);
+		mtspr(SPR_WORC, stop_sprs[WORC_IDX]);
+		mtspr(SPR_PTCR, stop_sprs[PTCR_IDX]);
+		mtspr(SPR_TSCR, stop_sprs[TSCR_IDX]);
+		mtspr(SPR_AMOR, stop_sprs[AMOR_IDX]);
+	}
+	mtspr(SPR_WORT,  stop_sprs[WORT_IDX]);
+	mtspr(SPR_PURR,  stop_sprs[PURR_IDX]);
+	mtspr(SPR_SPURR, stop_sprs[SPURR_IDX]);
+	mtspr(SPR_DSCR,  stop_sprs[DSCR_IDX]);
+	mtspr(SPR_LPCR,  stop_sprs[LPCR_IDX]);
+	mtspr(SPR_PID,   stop_sprs[PID_IDX]);
+	mtspr(SPR_LDBAR, stop_sprs[LDBAR_IDX]);
+	mtspr(SPR_FSCR,  stop_sprs[FSCR_IDX]);
+	mtspr(SPR_HFSCR, stop_sprs[HFSCR_IDX]);
+	mtspr(SPR_MMCRA, stop_sprs[MMCRA_IDX]);
+	mtspr(SPR_MMCR0, stop_sprs[MMCR0_IDX]);
+	mtspr(SPR_MMCR1, stop_sprs[MMCR1_IDX]);
+	mtspr(SPR_MMCR2, stop_sprs[MMCR2_IDX]);
+	mtspr(SPR_SPRG3, stop_sprs[SPRG3_IDX]);
+	return OPAL_SUCCESS;
+}
+
+opal_call(OPAL_IDLE_RESTORE, opal_cpuidle_restore, 4);
+
 /* Add device tree properties to describe idle states */
 void add_cpu_idle_state_properties(void)
 {
diff --git a/include/opal-api.h b/include/opal-api.h
index 09c77c18..fbd69700 100644
--- a/include/opal-api.h
+++ b/include/opal-api.h
@@ -223,7 +223,9 @@ 
 #define OPAL_PCI_GET_PBCQ_TUNNEL_BAR		164
 #define OPAL_PCI_SET_PBCQ_TUNNEL_BAR		165
 #define OPAL_HANDLE_HMI2			166
-#define OPAL_LAST				166
+#define OPAL_IDLE_SAVE				168
+#define OPAL_IDLE_RESTORE			169
+#define OPAL_LAST				169
 
 #define QUIESCE_HOLD			1 /* Spin all calls at entry */
 #define QUIESCE_REJECT			2 /* Fail all calls with OPAL_BUSY */
@@ -1311,6 +1313,8 @@  enum {
 	OPAL_PCI_P2P_TARGET	= 1,
 };
 
+extern int64_t __opal_resync_timebase(void);
+
 #endif /* __ASSEMBLY__ */
 
 #endif /* __OPAL_API_H */
diff --git a/include/processor.h b/include/processor.h
index 6b262b45..bab309a2 100644
--- a/include/processor.h
+++ b/include/processor.h
@@ -87,6 +87,19 @@ 
 #define SPR_HID4	0x3f4
 #define SPR_HID5	0x3f6
 #define SPR_PIR		0x3ff	/* RO: Processor Identification */
+#define SPR_PTCR	0x1D0	/* Partition table control Register */
+#define SPR_WORT	0x37f	/* Workload optimization register - thread */
+#define SPR_WORC	0x35f	/* Workload optimization register - core */
+#define SPR_FSCR	0x099	/* Facility Status & Control Register */
+#define SPR_HFSCR	0xbe	/* HV=1 Facility Status & Control Register */
+#define SPR_LDBAR	0x352	/* LD Base Address Register */
+#define SPR_PID		0x030	/* Process ID */
+#define SPR_SPRG3      0x113   /* Special Purpose Register General 3 */
+/* Performance counter Registers */
+#define SPR_MMCR0	0x31b
+#define SPR_MMCRA	0x312
+#define SPR_MMCR1	0x31e
+#define SPR_MMCR2	0x311
 
 
 /* Bits in LPCR */