[Skiboot] [RFC 1/1] OPAL : Support spr save-restore using new opal call
Abhishek Goel
huntbag at linux.vnet.ibm.com
Fri Jul 20 21:00:51 AEST 2018
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 at linux.vnet.ibm.com>
---
hw/chiptod.c | 7 ++-
hw/slw.c | 145 ++++++++++++++++++++++++++++++++++++++++++++++++++++
include/opal-api.h | 6 ++-
include/processor.h | 12 +++++
4 files changed, 168 insertions(+), 2 deletions(-)
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 dfa9189b..7acecbb4 100644
--- a/hw/slw.c
+++ b/hw/slw.c
@@ -818,6 +818,151 @@ 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 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);
+ 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]);
+ 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 f766dce9..24c98ed0 100644
--- a/include/opal-api.h
+++ b/include/opal-api.h
@@ -224,7 +224,9 @@
#define OPAL_PCI_SET_PBCQ_TUNNEL_BAR 165
#define OPAL_HANDLE_HMI2 166
#define OPAL_NX_COPROC_INIT 167
-#define OPAL_LAST 167
+#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 */
@@ -1312,6 +1314,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..cfda56d3 100644
--- a/include/processor.h
+++ b/include/processor.h
@@ -87,6 +87,18 @@
#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 */
+/* Performance counter Registers */
+#define SPR_MMCR0 0x31b
+#define SPR_MMCRA 0x312
+#define SPR_MMCR1 0x31e
+#define SPR_MMCR2 0x311
/* Bits in LPCR */
--
2.14.1
More information about the Skiboot
mailing list