[RFC v2 1/2] powerpc/powernv : Add support for pre-entry and post-exit of stop state using OPAL V4 OS services
From: Abhishek Goel
Date: Mon Jul 06 2020 - 00:50:40 EST
This patch provides kernel framework fro opal support of save restore
of sprs in idle stop loop. 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 present and 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.
Signed-off-by: Abhishek Goel <huntbag@xxxxxxxxxxxxxxxxxx>
---
v1->v2 : Rebased the patch on Nick's Opal V4 OS patchset
arch/powerpc/include/asm/opal-api.h | 4 +++-
arch/powerpc/include/asm/opal.h | 1 +
arch/powerpc/platforms/powernv/idle.c | 12 ++++++++++++
arch/powerpc/platforms/powernv/opal-call.c | 1 +
arch/powerpc/platforms/powernv/opal.c | 15 +++++++++++++++
5 files changed, 32 insertions(+), 1 deletion(-)
diff --git a/arch/powerpc/include/asm/opal-api.h b/arch/powerpc/include/asm/opal-api.h
index 97c5e5423827..437b6937685d 100644
--- a/arch/powerpc/include/asm/opal-api.h
+++ b/arch/powerpc/include/asm/opal-api.h
@@ -219,7 +219,8 @@
#define OPAL_REPORT_TRAP 183
#define OPAL_FIND_VM_AREA 184
#define OPAL_REGISTER_OS_OPS 185
-#define OPAL_LAST 185
+#define OPAL_CPU_IDLE 186
+#define OPAL_LAST 186
#define QUIESCE_HOLD 1 /* Spin all calls at entry */
#define QUIESCE_REJECT 2 /* Fail all calls with OPAL_BUSY */
@@ -1207,6 +1208,7 @@ struct opal_os_ops {
__be64 os_printf; /* void printf(int32_t level, const char *str) */
__be64 os_vm_map; /* int64_t os_vm_map(uint64_t ea, uint64_t pa, uint64_t flags) */
__be64 os_vm_unmap; /* void os_vm_unmap(uint64_t ea) */
+ __be64 os_idle_stop; /* void os_idle_stop(uint64_t srr1_addr, uint64_t psscr) */
};
#endif /* __ASSEMBLY__ */
diff --git a/arch/powerpc/include/asm/opal.h b/arch/powerpc/include/asm/opal.h
index 09985b7718b3..1774c056acb8 100644
--- a/arch/powerpc/include/asm/opal.h
+++ b/arch/powerpc/include/asm/opal.h
@@ -407,6 +407,7 @@ void opal_sensor_groups_init(void);
int64_t opal_find_vm_area(uint64_t addr, struct opal_vm_area *opal_vm_area);
int64_t opal_register_os_ops(struct opal_os_ops *ops, uint64_t size);
+int64_t opal_cpu_idle(uint64_t srr1_addr, uint64_t psscr);
#endif /* __ASSEMBLY__ */
diff --git a/arch/powerpc/platforms/powernv/idle.c b/arch/powerpc/platforms/powernv/idle.c
index 78599bca66c2..3afd4293f729 100644
--- a/arch/powerpc/platforms/powernv/idle.c
+++ b/arch/powerpc/platforms/powernv/idle.c
@@ -805,6 +805,18 @@ static unsigned long power9_idle_stop(unsigned long psscr, bool mmu_on)
return srr1;
}
+static unsigned long power9_firmware_idle_stop(unsigned long psscr, bool mmu_on)
+{
+ unsigned long srr1;
+ int rc;
+
+ rc = opal_cpu_idle(cpu_to_be64(&srr1), (uint64_t) psscr);
+
+ if (mmu_on)
+ mtmsr(MSR_KERNEL);
+ return srr1;
+}
+
#ifdef CONFIG_HOTPLUG_CPU
static unsigned long power9_offline_stop(unsigned long psscr)
{
diff --git a/arch/powerpc/platforms/powernv/opal-call.c b/arch/powerpc/platforms/powernv/opal-call.c
index 11f419e76059..79076ca2de03 100644
--- a/arch/powerpc/platforms/powernv/opal-call.c
+++ b/arch/powerpc/platforms/powernv/opal-call.c
@@ -351,3 +351,4 @@ OPAL_CALL(opal_sym_to_addr, OPAL_SYM_TO_ADDR);
OPAL_CALL(opal_report_trap, OPAL_REPORT_TRAP);
OPAL_CALL(opal_find_vm_area, OPAL_FIND_VM_AREA);
OPAL_CALL(opal_register_os_ops, OPAL_REGISTER_OS_OPS);
+OPAL_CALL(opal_cpu_idle, OPAL_CPU_IDLE);
diff --git a/arch/powerpc/platforms/powernv/opal.c b/arch/powerpc/platforms/powernv/opal.c
index 93b9afaf33b3..1fbf7065f918 100644
--- a/arch/powerpc/platforms/powernv/opal.c
+++ b/arch/powerpc/platforms/powernv/opal.c
@@ -1150,6 +1150,20 @@ static void os_vm_unmap(uint64_t ea)
local_flush_tlb_mm(mm);
}
+int64_t os_idle_stop(uint64_t srr1_addr, uint64_t psscr)
+{
+ /*
+ * For lite state which does not lose even GPRS we call
+ * idle_stop_noloss while for all other states we call
+ * idle_stop_mayloss. Saving and restoration of other additional
+ * SPRs if required is handled in OPAL. All the quirks are also
+ * handled in OPAL.
+ */
+ if (!(psscr & (PSSCR_EC|PSSCR_ESL)))
+ return isa300_idle_stop_noloss(psscr);
+ return isa300_idle_stop_mayloss(psscr);
+}
+
static int __init opal_init_mm(void)
{
struct mm_struct *mm;
@@ -1231,6 +1245,7 @@ static int __init opal_init_early(void)
opal_os_ops.os_printf = cpu_to_be64(&os_printf);
opal_os_ops.os_vm_map = cpu_to_be64(&os_vm_map);
opal_os_ops.os_vm_unmap = cpu_to_be64(&os_vm_unmap);
+ opal_os_ops.os_idle_stop = cpu_to_be64(&os_idle_stop);
if (opal_register_os_ops(&opal_os_ops, sizeof(opal_os_ops))) {
pr_warn("OPAL register OS ops failed, firmware will run in v3 mode.\n");
} else {
--
2.17.1