Subject: [RFC PATCH] OPAL v4 cpu idle driver skeleton

From: Nicholas Piggin <[email-masked]>
Date: Thu, 12 Dec 2019 14:12:37 +1000

With OPAL using the same endianness, same stack, and with OS
callbacks, it looks relatively easy to provide a CPU idle driver.

The Linux sreset interrupt won't have to change, if it registers
almost like isa300_idle_stop_mayloss as the os_stop function,
then skiboot will call that to stop, and it will return like a
normal function call returning the srr1 wakeup value.

This allows the firmware to deal with supported stop states and
psscr and consequences for saving and restoring various resources,
and the kernel can implement a simple OPAL idle driver which has
some interface like wakeup latency requested or something.

Calls in and out of OPAL (once it's running with MMU=on) are not
much more expensive calling a function in a kernel module, so
performance should be okay. Kernel can still choose to implement
an optimised CPU specific driver as it does today.

The patch is just a hack with no actual policy or SPR saving in
it at the moment and only does stop0, but illustrates the mechanism.

Thanks,
Nick
---
 core/Makefile.inc       |  2 +-
 core/opal.c             |  3 +++
 core/stop.c             | 35 +++++++++++++++++++++++++++++++++++
 include/opal-api.h      | 10 ++++++----
 include/opal-internal.h |  1 +
 5 files changed, 46 insertions(+), 5 deletions(-)
 create mode 100644 core/stop.c

diff --git a/core/Makefile.inc b/core/Makefile.inc
index cc90fb958..653ca544e 100644
--- a/core/Makefile.inc
+++ b/core/Makefile.inc
@@ -7,7 +7,7 @@ CORE_OBJS = relocate.o console.o stack.o init.o chip.o mem_region.o vm.o
 CORE_OBJS += malloc.o lock.o cpu.o utils.o fdt.o opal.o interrupts.o timebase.o
 CORE_OBJS += opal-msg.o pci.o pci-virt.o pci-slot.o pcie-slot.o
 CORE_OBJS += pci-opal.o fast-reboot.o device.o exceptions.o trace.o affinity.o
-CORE_OBJS += vpd.o platform.o nvram.o nvram-format.o hmi.o mce.o
+CORE_OBJS += vpd.o platform.o nvram.o nvram-format.o hmi.o mce.o stop.o
 CORE_OBJS += console-log.o ipmi.o time-utils.o pel.o pool.o errorlog.o
 CORE_OBJS += timer.o i2c.o rtc.o flash.o sensor.o ipmi-opal.o
 CORE_OBJS += flash-subpartition.o bitmap.o buddy.o pci-quirk.o powercap.o psr.o
diff --git a/core/opal.c b/core/opal.c
index bb88d7710..d5c1d057b 100644
--- a/core/opal.c
+++ b/core/opal.c
@@ -444,6 +444,9 @@ static int64_t opal_register_opal_ops(struct opal_os_ops *__os_ops)
 	/* v4 must provide printf */
 	os_ops.os_printf = (void *)be64_to_cpu(__os_ops->os_printf);
 
+	/* v4 may provide stop (or NULL) */
+	os_ops.os_stop = (void *)be64_to_cpu(__os_ops->os_stop);
+
 	set_opal_console_to_raw();
 
 	checksum_romem();
diff --git a/core/stop.c b/core/stop.c
new file mode 100644
index 000000000..6d98d68e6
--- /dev/null
+++ b/core/stop.c
@@ -0,0 +1,35 @@
+// SPDX-License-Identifier: Apache-2.0
+/*
+ * Stop idle driver
+ *
+ * Copyright 2019 IBM Corp.
+ */
+
+#define pr_fmt(fmt)	"IDLE: " fmt
+
+#include <skiboot.h>
+#include <opal.h>
+#include <processor.h>
+#include <cpu.h>
+#include <cpu.h>
+
+static int64_t opal_cpu_idle(uint64_t latency, bool radix, __be64 *srr1)
+{
+	uint64_t psscr;
+
+	if (!os_ops.os_stop)
+		return OPAL_UNSUPPORTED;
+
+	if (proc_gen != proc_gen_p9)
+		return OPAL_UNSUPPORTED;
+
+	(void)latency;
+	(void)radix;
+	psscr = OPAL_PM_PSSCR_RL(0) \
+		 | OPAL_PM_PSSCR_MTL(3) \
+		 | OPAL_PM_PSSCR_TR(3);
+	*srr1 = os_ops.os_stop(psscr, true);
+
+	return OPAL_SUCCESS;
+}
+opal_call(OPAL_CPU_IDLE, opal_cpu_idle, 3);
diff --git a/include/opal-api.h b/include/opal-api.h
index 169061a26..03f323628 100644
--- a/include/opal-api.h
+++ b/include/opal-api.h
@@ -231,6 +231,7 @@
 #define OPAL_LOOKUP_SYMBOL			182
 #define OPAL_REGISTER_OS_OPS			183
 #define OPAL_HANDLE_MCE				184
+#define OPAL_CPU_IDLE				185
 #define OPAL_LAST				184
 
 #define QUIESCE_HOLD			1 /* Spin all calls at entry */
@@ -1259,10 +1260,11 @@ struct opal_mpipl_fadump {
 };
 
 struct opal_os_ops {
-        __be16  version;
-        __be16  reserved0;
-        __be32  reserved1;
-        __be64  os_printf;      /* void printf(int32_t level, const char *str) */
+	__be16  version;
+	__be16  reserved0;
+	__be32  reserved1;
+	__be64  os_printf;	/* void printf(int32_t level, const char *str) */
+	__be64	os_stop;	/* uint64_t stop(uint64_t psscr, bool save_gprs) */
 };
 
 #define MCE_HANDLE_CORRECT		0x0001	/* Attempt to correct */
diff --git a/include/opal-internal.h b/include/opal-internal.h
index cd968a0fe..2baf79a53 100644
--- a/include/opal-internal.h
+++ b/include/opal-internal.h
@@ -20,6 +20,7 @@ struct opal_table_entry {
 
 struct os_ops {
         void (*os_printf)(uint32_t log_level, const char *str);
+	uint64_t (*os_stop)(uint64_t psscr, bool save_gprs);
 };
 
 extern bool opal_v4_os;
-- 
2.23.0