aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorNicholas Piggin <npiggin@gmail.com>2021-12-20 22:22:49 +1000
committerCédric Le Goater <clg@kaod.org>2022-01-03 16:12:45 +0100
commit65d9909a7556eb94cb4ef07f636a37c326ebaade (patch)
tree20cc42f88ae067a60ce1057d531e6be429fd7f7f
parent402b822a7074053d59c8c49ffb5e58b2d5028f54 (diff)
downloadskiboot-65d9909a7556eb94cb4ef07f636a37c326ebaade.zip
skiboot-65d9909a7556eb94cb4ef07f636a37c326ebaade.tar.gz
skiboot-65d9909a7556eb94cb4ef07f636a37c326ebaade.tar.bz2
hw/slw: split P8 specific code into its own file
POWER8 support is large and significantly different than P9/10 code. This change prepares to make P8 support configurable. Signed-off-by: Nicholas Piggin <npiggin@gmail.com> [ clg: Removed commented headers in slw.c ] Signed-off-by: Cédric Le Goater <clg@kaod.org>
-rw-r--r--core/fast-reboot.c1
-rw-r--r--core/init.c1
-rw-r--r--hw/Makefile.inc2
-rw-r--r--hw/imc.c1
-rw-r--r--hw/nx.c1
-rw-r--r--hw/slw-p8.c508
-rw-r--r--hw/slw.c490
-rw-r--r--hw/xive.c1
-rw-r--r--hw/xive2.c1
-rw-r--r--include/skiboot.h12
-rw-r--r--include/slw.h48
11 files changed, 573 insertions, 493 deletions
diff --git a/core/fast-reboot.c b/core/fast-reboot.c
index 2696348..fedfa88 100644
--- a/core/fast-reboot.c
+++ b/core/fast-reboot.c
@@ -6,6 +6,7 @@
*/
#include <skiboot.h>
+#include <slw.h>
#include <cpu.h>
#include <console.h>
#include <fsp.h>
diff --git a/core/init.c b/core/init.c
index c3e0c49..deead5e 100644
--- a/core/init.c
+++ b/core/init.c
@@ -6,6 +6,7 @@
*/
#include <skiboot.h>
+#include <slw.h>
#include <psi.h>
#include <chiptod.h>
#include <nx.h>
diff --git a/hw/Makefile.inc b/hw/Makefile.inc
index 8f50973..7327cec 100644
--- a/hw/Makefile.inc
+++ b/hw/Makefile.inc
@@ -10,7 +10,7 @@ HW_OBJS += npu2-common.o npu2-opencapi.o phys-map.o sbe-p9.o capp.o
HW_OBJS += occ-sensor.o vas.o dio-p9.o lpc-port80h.o cache-p9.o
HW_OBJS += npu-opal.o ocmb.o xive2.o pau.o pau-hw-procedures.o
ifeq ($(CONFIG_P8),1)
-HW_OBJS += phb3.o sbe-p8.o
+HW_OBJS += phb3.o sbe-p8.o slw-p8.o
endif
HW=hw/built-in.a
diff --git a/hw/imc.c b/hw/imc.c
index cbd68ed..97e0809 100644
--- a/hw/imc.c
+++ b/hw/imc.c
@@ -8,6 +8,7 @@
#define pr_fmt(fmt) "IMC: " fmt
#include <skiboot.h>
+#include <slw.h>
#include <xscom.h>
#include <imc.h>
#include <chip.h>
diff --git a/hw/nx.c b/hw/nx.c
index fdadf53..13c681b 100644
--- a/hw/nx.c
+++ b/hw/nx.c
@@ -6,6 +6,7 @@
*/
#include <skiboot.h>
+#include <slw.h>
#include <xscom.h>
#include <io.h>
#include <cpu.h>
diff --git a/hw/slw-p8.c b/hw/slw-p8.c
new file mode 100644
index 0000000..8b0a1db
--- /dev/null
+++ b/hw/slw-p8.c
@@ -0,0 +1,508 @@
+// SPDX-License-Identifier: Apache-2.0 OR GPL-2.0-or-later
+
+#include <skiboot.h>
+#include <slw.h>
+#include <xscom.h>
+#include <xscom-p8-regs.h>
+#include <cpu.h>
+#include <chip.h>
+#include <interrupts.h>
+#include <timebase.h>
+#include <errorlog.h>
+#include <libfdt/libfdt.h>
+#include <opal-api.h>
+#include <sbe-p8.h>
+
+#include <p8_pore_table_gen_api.H>
+#include <sbe_xip_image.h>
+
+/*
+ * It would be nice to be able to define non-static log entry types and share
+ * these with slw.c
+ */
+DEFINE_LOG_ENTRY(OPAL_RC_SLW_INIT, OPAL_PLATFORM_ERR_EVT, OPAL_SLW,
+ OPAL_PLATFORM_FIRMWARE, OPAL_PREDICTIVE_ERR_GENERAL,
+ OPAL_NA);
+
+DEFINE_LOG_ENTRY(OPAL_RC_SLW_SET, OPAL_PLATFORM_ERR_EVT, OPAL_SLW,
+ OPAL_PLATFORM_FIRMWARE, OPAL_INFO,
+ OPAL_NA);
+
+DEFINE_LOG_ENTRY(OPAL_RC_SLW_GET, OPAL_PLATFORM_ERR_EVT, OPAL_SLW,
+ OPAL_PLATFORM_FIRMWARE, OPAL_INFO,
+ OPAL_NA);
+
+DEFINE_LOG_ENTRY(OPAL_RC_SLW_REG, OPAL_PLATFORM_ERR_EVT, OPAL_SLW,
+ OPAL_PLATFORM_FIRMWARE, OPAL_INFO,
+ OPAL_NA);
+
+static bool slw_general_init(struct proc_chip *chip, struct cpu_thread *c)
+{
+ uint32_t core = pir_to_core_id(c->pir);
+ uint64_t tmp;
+ int rc;
+
+ /* PowerManagement GP0 clear PM_DISABLE */
+ rc = xscom_read(chip->id, XSCOM_ADDR_P8_EX_SLAVE(core, EX_PM_GP0), &tmp);
+ if (rc) {
+ log_simple_error(&e_info(OPAL_RC_SLW_INIT),
+ "SLW: Failed to read PM_GP0\n");
+ return false;
+ }
+ tmp = tmp & ~0x8000000000000000ULL;
+ rc = xscom_write(chip->id, XSCOM_ADDR_P8_EX_SLAVE(core, EX_PM_GP0), tmp);
+ if (rc) {
+ log_simple_error(&e_info(OPAL_RC_SLW_INIT),
+ "SLW: Failed to write PM_GP0\n");
+ return false;
+ }
+ prlog(PR_TRACE, "SLW: PMGP0 set to 0x%016llx\n", tmp);
+
+ /* Read back for debug */
+ rc = xscom_read(chip->id, XSCOM_ADDR_P8_EX_SLAVE(core, EX_PM_GP0), &tmp);
+ if (rc)
+ log_simple_error(&e_info(OPAL_RC_SLW_INIT),
+ "SLW: Failed to re-read PM_GP0. Continuing...\n");
+
+ prlog(PR_TRACE, "SLW: PMGP0 read 0x%016llx\n", tmp);
+
+ return true;
+}
+
+static bool slw_set_overrides(struct proc_chip *chip, struct cpu_thread *c)
+{
+ uint32_t core = pir_to_core_id(c->pir);
+ int rc;
+
+ rc = xscom_write(chip->id,
+ XSCOM_ADDR_P8_EX_SLAVE(core, EX_PM_SPECIAL_WAKEUP_PHYP),
+ 0);
+ if (rc) {
+ log_simple_error(&e_info(OPAL_RC_SLW_SET),
+ "SLW: Failed to write PM_SPECIAL_WAKEUP_PHYP\n");
+ return false;
+ }
+
+ return true;
+}
+
+static bool slw_set_idle_mode(struct proc_chip *chip, struct cpu_thread *c)
+{
+ uint32_t core = pir_to_core_id(c->pir);
+ uint64_t tmp;
+ int rc;
+
+ /*
+ * PM GP1 allows fast/deep mode to be selected independently for sleep
+ * and winkle. Init PM GP1 so that sleep happens in fast mode and
+ * winkle happens in deep mode.
+ * Make use of the OR XSCOM for this since the OCC might be manipulating
+ * the PM_GP1 register as well. Before doing this ensure that the bits
+ * managing idle states are cleared so as to override any bits set at
+ * init time.
+ */
+
+ tmp = ~EX_PM_GP1_SLEEP_WINKLE_MASK;
+ rc = xscom_write(chip->id, XSCOM_ADDR_P8_EX_SLAVE(core, EX_PM_CLEAR_GP1),
+ tmp);
+ if (rc) {
+ log_simple_error(&e_info(OPAL_RC_SLW_SET),
+ "SLW: Failed to write PM_GP1\n");
+ return false;
+ }
+
+ rc = xscom_write(chip->id, XSCOM_ADDR_P8_EX_SLAVE(core, EX_PM_SET_GP1),
+ EX_PM_SETUP_GP1_FAST_SLEEP_DEEP_WINKLE);
+ if (rc) {
+ log_simple_error(&e_info(OPAL_RC_SLW_SET),
+ "SLW: Failed to write PM_GP1\n");
+ return false;
+ }
+
+ /* Read back for debug */
+ xscom_read(chip->id, XSCOM_ADDR_P8_EX_SLAVE(core, EX_PM_GP1), &tmp);
+ prlog(PR_TRACE, "SLW: PMGP1 read 0x%016llx\n", tmp);
+ return true;
+}
+
+static bool slw_get_idle_state_history(struct proc_chip *chip, struct cpu_thread *c)
+{
+ uint32_t core = pir_to_core_id(c->pir);
+ uint64_t tmp;
+ int rc;
+
+ /* Cleanup history */
+ rc = xscom_read(chip->id,
+ XSCOM_ADDR_P8_EX_SLAVE(core, EX_PM_IDLE_STATE_HISTORY_PHYP),
+ &tmp);
+ if (rc) {
+ log_simple_error(&e_info(OPAL_RC_SLW_GET),
+ "SLW: Failed to read PM_IDLE_STATE_HISTORY\n");
+ return false;
+ }
+
+ prlog(PR_TRACE, "SLW: core %x:%x history: 0x%016llx (old1)\n",
+ chip->id, core, tmp);
+
+ rc = xscom_read(chip->id,
+ XSCOM_ADDR_P8_EX_SLAVE(core, EX_PM_IDLE_STATE_HISTORY_PHYP),
+ &tmp);
+
+ if (rc) {
+ log_simple_error(&e_info(OPAL_RC_SLW_GET),
+ "SLW: Failed to read PM_IDLE_STATE_HISTORY\n");
+ return false;
+ }
+
+ prlog(PR_TRACE, "SLW: core %x:%x history: 0x%016llx (old2)\n",
+ chip->id, core, tmp);
+
+ return true;
+}
+
+static bool idle_prepare_core(struct proc_chip *chip, struct cpu_thread *c)
+{
+ prlog(PR_TRACE, "FASTSLEEP: Prepare core %x:%x\n",
+ chip->id, pir_to_core_id(c->pir));
+
+ if(!slw_general_init(chip, c))
+ return false;
+ if(!slw_set_overrides(chip, c))
+ return false;
+ if(!slw_set_idle_mode(chip, c))
+ return false;
+ if(!slw_get_idle_state_history(chip, c))
+ return false;
+
+ return true;
+
+}
+
+static struct cpu_idle_states nap_only_cpu_idle_states[] = {
+ { /* nap */
+ .name = "nap",
+ .latency_ns = 4000,
+ .residency_ns = 100000,
+ .flags = 0*OPAL_PM_DEC_STOP \
+ | 0*OPAL_PM_TIMEBASE_STOP \
+ | 1*OPAL_PM_LOSE_USER_CONTEXT \
+ | 0*OPAL_PM_LOSE_HYP_CONTEXT \
+ | 0*OPAL_PM_LOSE_FULL_CONTEXT \
+ | 1*OPAL_PM_NAP_ENABLED \
+ | 0*OPAL_PM_SLEEP_ENABLED \
+ | 0*OPAL_PM_WINKLE_ENABLED \
+ | 0*OPAL_USE_PMICR,
+ .pm_ctrl_reg_val = 0,
+ .pm_ctrl_reg_mask = 0 },
+};
+
+static struct cpu_idle_states power8_cpu_idle_states[] = {
+ { /* nap */
+ .name = "nap",
+ .latency_ns = 4000,
+ .residency_ns = 100000,
+ .flags = 0*OPAL_PM_DEC_STOP \
+ | 0*OPAL_PM_TIMEBASE_STOP \
+ | 1*OPAL_PM_LOSE_USER_CONTEXT \
+ | 0*OPAL_PM_LOSE_HYP_CONTEXT \
+ | 0*OPAL_PM_LOSE_FULL_CONTEXT \
+ | 1*OPAL_PM_NAP_ENABLED \
+ | 0*OPAL_USE_PMICR,
+ .pm_ctrl_reg_val = 0,
+ .pm_ctrl_reg_mask = 0 },
+ { /* fast sleep (with workaround) */
+ .name = "fastsleep_",
+ .latency_ns = 40000,
+ .residency_ns = 300000000,
+ .flags = 1*OPAL_PM_DEC_STOP \
+ | 1*OPAL_PM_TIMEBASE_STOP \
+ | 1*OPAL_PM_LOSE_USER_CONTEXT \
+ | 0*OPAL_PM_LOSE_HYP_CONTEXT \
+ | 0*OPAL_PM_LOSE_FULL_CONTEXT \
+ | 1*OPAL_PM_SLEEP_ENABLED_ER1 \
+ | 0*OPAL_USE_PMICR, /* Not enabled until deep
+ states are available */
+ .pm_ctrl_reg_val = OPAL_PM_FASTSLEEP_PMICR,
+ .pm_ctrl_reg_mask = OPAL_PM_SLEEP_PMICR_MASK },
+ { /* Winkle */
+ .name = "winkle",
+ .latency_ns = 10000000,
+ .residency_ns = 1000000000, /* Educated guess (not measured).
+ * Winkle is not currently used by
+ * linux cpuidle subsystem so we
+ * don't have real world user.
+ * However, this should be roughly
+ * accurate for when linux does
+ * use it. */
+ .flags = 1*OPAL_PM_DEC_STOP \
+ | 1*OPAL_PM_TIMEBASE_STOP \
+ | 1*OPAL_PM_LOSE_USER_CONTEXT \
+ | 1*OPAL_PM_LOSE_HYP_CONTEXT \
+ | 1*OPAL_PM_LOSE_FULL_CONTEXT \
+ | 1*OPAL_PM_WINKLE_ENABLED \
+ | 0*OPAL_USE_PMICR, /* Currently choosing deep vs
+ fast via EX_PM_GP1 reg */
+ .pm_ctrl_reg_val = 0,
+ .pm_ctrl_reg_mask = 0 },
+};
+
+void find_cpu_idle_state_properties_p8(struct cpu_idle_states **states,
+ int *nr_states, bool *can_sleep)
+{
+ struct proc_chip *chip;
+
+ chip = next_chip(NULL);
+ assert(chip);
+
+ *can_sleep = true;
+
+ if (chip->type == PROC_CHIP_P8_MURANO ||
+ chip->type == PROC_CHIP_P8_VENICE ||
+ chip->type == PROC_CHIP_P8_NAPLES) {
+ const struct dt_property *p;
+
+ p = dt_find_property(dt_root, "ibm,enabled-idle-states");
+ if (p)
+ prlog(PR_NOTICE,
+ "SLW: HB-provided idle states property found\n");
+ *states = power8_cpu_idle_states;
+ *nr_states = ARRAY_SIZE(power8_cpu_idle_states);
+
+ /* Check if hostboot say we can sleep */
+ if (!p || !dt_prop_find_string(p, "fast-sleep")) {
+ prlog(PR_WARNING, "SLW: Sleep not enabled by HB"
+ " on this platform\n");
+ *can_sleep = false;
+ }
+
+ /* Clip to NAP only on Murano and Venice DD1.x */
+ if ((chip->type == PROC_CHIP_P8_MURANO ||
+ chip->type == PROC_CHIP_P8_VENICE) &&
+ chip->ec_level < 0x20) {
+ prlog(PR_NOTICE, "SLW: Sleep not enabled on P8 DD1.x\n");
+ *can_sleep = false;
+ }
+
+ } else {
+ *states = nap_only_cpu_idle_states;
+ *nr_states = ARRAY_SIZE(nap_only_cpu_idle_states);
+ }
+}
+
+static void slw_patch_regs(struct proc_chip *chip)
+{
+ struct cpu_thread *c;
+ void *image = (void *)chip->slw_base;
+ int rc;
+
+ for_each_available_cpu(c) {
+ if (c->chip_id != chip->id)
+ continue;
+
+ /* Clear HRMOR */
+ rc = p8_pore_gen_cpureg_fixed(image, P8_SLW_MODEBUILD_SRAM,
+ P8_SPR_HRMOR, 0,
+ cpu_get_core_index(c),
+ cpu_get_thread_index(c));
+ if (rc) {
+ log_simple_error(&e_info(OPAL_RC_SLW_REG),
+ "SLW: Failed to set HRMOR for CPU %x\n",
+ c->pir);
+ }
+
+ /* XXX Add HIDs etc... */
+ }
+}
+
+static bool slw_image_check_p8(struct proc_chip *chip)
+{
+ int64_t rc;
+
+ prlog(PR_DEBUG, "SLW: slw_check chip 0x%x\n", chip->id);
+ if (!chip->slw_base) {
+ prerror("SLW: No image found !\n");
+ return false;
+ }
+
+ /* Check actual image size */
+ rc = sbe_xip_get_scalar((void *)chip->slw_base, "image_size",
+ &chip->slw_image_size);
+ if (rc != 0) {
+ log_simple_error(&e_info(OPAL_RC_SLW_INIT),
+ "SLW: Error %lld reading SLW image size\n", rc);
+ /* XXX Panic ? */
+ chip->slw_base = 0;
+ chip->slw_bar_size = 0;
+ chip->slw_image_size = 0;
+ return false;
+ }
+ prlog(PR_DEBUG, "SLW: Image size from image: 0x%llx\n",
+ chip->slw_image_size);
+
+ if (chip->slw_image_size > chip->slw_bar_size) {
+ log_simple_error(&e_info(OPAL_RC_SLW_INIT),
+ "SLW: Built-in image size larger than BAR size !\n");
+ /* XXX Panic ? */
+ return false;
+ }
+ return true;
+
+}
+
+static void slw_late_init_p8(struct proc_chip *chip)
+{
+
+ prlog(PR_DEBUG, "SLW: late Init chip 0x%x\n", chip->id);
+
+ /* Patch SLW image */
+ slw_patch_regs(chip);
+
+}
+static void slw_init_chip_p8(struct proc_chip *chip)
+{
+ struct cpu_thread *c;
+
+ prlog(PR_DEBUG, "SLW: Init chip 0x%x\n", chip->id);
+ /* At power ON setup inits for fast-sleep */
+ for_each_available_core_in_chip(c, chip->id) {
+ idle_prepare_core(chip, c);
+ }
+}
+
+/* Workarounds while entering fast-sleep */
+
+static void fast_sleep_enter(void)
+{
+ uint32_t core = pir_to_core_id(this_cpu()->pir);
+ uint32_t chip_id = this_cpu()->chip_id;
+ struct cpu_thread *primary_thread;
+ uint64_t tmp;
+ int rc;
+
+ primary_thread = this_cpu()->primary;
+
+ rc = xscom_read(chip_id, XSCOM_ADDR_P8_EX(core, L2_FIR_ACTION1),
+ &tmp);
+ if (rc) {
+ prlog(PR_WARNING, "fast_sleep_enter XSCOM failed(1):"
+ " rc=%d chip_id=%d core=%d\n",
+ rc, chip_id, core);
+ return;
+ }
+
+ primary_thread->save_l2_fir_action1 = tmp;
+ primary_thread->in_fast_sleep = true;
+
+ tmp = tmp & ~0x0200000000000000ULL;
+ rc = xscom_write(chip_id, XSCOM_ADDR_P8_EX(core, L2_FIR_ACTION1),
+ tmp);
+ if (rc) {
+ prlog(PR_WARNING, "fast_sleep_enter XSCOM failed(2):"
+ " rc=%d chip_id=%d core=%d\n",
+ rc, chip_id, core);
+ return;
+ }
+ rc = xscom_read(chip_id, XSCOM_ADDR_P8_EX(core, L2_FIR_ACTION1),
+ &tmp);
+ if (rc) {
+ prlog(PR_WARNING, "fast_sleep_enter XSCOM failed(3):"
+ " rc=%d chip_id=%d core=%d\n",
+ rc, chip_id, core);
+ return;
+ }
+
+}
+
+/* Workarounds while exiting fast-sleep */
+
+void fast_sleep_exit(void)
+{
+ uint32_t core = pir_to_core_id(this_cpu()->pir);
+ uint32_t chip_id = this_cpu()->chip_id;
+ struct cpu_thread *primary_thread;
+ int rc;
+
+ primary_thread = this_cpu()->primary;
+ primary_thread->in_fast_sleep = false;
+
+ rc = xscom_write(chip_id, XSCOM_ADDR_P8_EX(core, L2_FIR_ACTION1),
+ primary_thread->save_l2_fir_action1);
+ if (rc) {
+ prlog(PR_WARNING, "fast_sleep_exit XSCOM failed:"
+ " rc=%d chip_id=%d core=%d\n",
+ rc, chip_id, core);
+ return;
+ }
+}
+
+/*
+ * Setup and cleanup method for fast-sleep workarounds
+ * state = 1 fast-sleep
+ * enter = 1 Enter state
+ * exit = 0 Exit state
+ */
+
+static int64_t opal_config_cpu_idle_state(uint64_t state, uint64_t enter)
+{
+ /* Only fast-sleep for now */
+ if (state != 1)
+ return OPAL_PARAMETER;
+
+ switch(enter) {
+ case 1:
+ fast_sleep_enter();
+ break;
+ case 0:
+ fast_sleep_exit();
+ break;
+ default:
+ return OPAL_PARAMETER;
+ }
+
+ return OPAL_SUCCESS;
+}
+
+opal_call(OPAL_CONFIG_CPU_IDLE_STATE, opal_config_cpu_idle_state, 2);
+
+int64_t opal_slw_set_reg_p8(struct cpu_thread *c, struct proc_chip *chip,
+ uint64_t sprn, uint64_t val)
+{
+ int spr_is_supported = 0;
+ void *image;
+ int i;
+ int rc;
+
+ /* Check of the SPR is supported by libpore */
+ for (i = 0; i < SLW_SPR_REGS_SIZE ; i++) {
+ if (sprn == SLW_SPR_REGS[i].value) {
+ spr_is_supported = 1;
+ break;
+ }
+ }
+ if (!spr_is_supported) {
+ log_simple_error(&e_info(OPAL_RC_SLW_REG),
+ "SLW: Trying to set unsupported spr for CPU %x\n",
+ c->pir);
+ return OPAL_UNSUPPORTED;
+ }
+ image = (void *)chip->slw_base;
+ rc = p8_pore_gen_cpureg_fixed(image, P8_SLW_MODEBUILD_SRAM,
+ sprn, val,
+ cpu_get_core_index(c),
+ cpu_get_thread_index(c));
+ return rc;
+}
+
+void slw_p8_init(void)
+{
+ struct proc_chip *chip;
+
+ for_each_chip(chip) {
+ slw_init_chip_p8(chip);
+ if (slw_image_check_p8(chip))
+ wakeup_engine_state = WAKEUP_ENGINE_PRESENT;
+ if (wakeup_engine_state == WAKEUP_ENGINE_PRESENT)
+ slw_late_init_p8(chip);
+ }
+ p8_sbe_init_timer();
+}
diff --git a/hw/slw.c b/hw/slw.c
index bc53960..67b9422 100644
--- a/hw/slw.c
+++ b/hw/slw.c
@@ -7,8 +7,8 @@
*/
#include <skiboot.h>
+#include <slw.h>
#include <xscom.h>
-#include <xscom-p8-regs.h>
#include <xscom-p9-regs.h>
#include <xscom-p10-regs.h>
#include <io.h>
@@ -22,12 +22,9 @@
#include <libfdt/libfdt.h>
#include <opal-api.h>
#include <nvram.h>
-#include <sbe-p8.h>
#include <xive.h>
#include <p10_stop_api.H>
-#include <p8_pore_table_gen_api.H>
-#include <sbe_xip_image.h>
enum wakeup_engine_states wakeup_engine_state = WAKEUP_ENGINE_NOT_PRESENT;
bool has_deep_states = false;
@@ -40,158 +37,6 @@ DEFINE_LOG_ENTRY(OPAL_RC_SLW_REG, OPAL_PLATFORM_ERR_EVT, OPAL_SLW,
OPAL_PLATFORM_FIRMWARE, OPAL_INFO,
OPAL_NA);
-#ifdef CONFIG_P8
-DEFINE_LOG_ENTRY(OPAL_RC_SLW_INIT, OPAL_PLATFORM_ERR_EVT, OPAL_SLW,
- OPAL_PLATFORM_FIRMWARE, OPAL_PREDICTIVE_ERR_GENERAL,
- OPAL_NA);
-
-DEFINE_LOG_ENTRY(OPAL_RC_SLW_GET, OPAL_PLATFORM_ERR_EVT, OPAL_SLW,
- OPAL_PLATFORM_FIRMWARE, OPAL_INFO,
- OPAL_NA);
-
-static bool slw_general_init(struct proc_chip *chip, struct cpu_thread *c)
-{
- uint32_t core = pir_to_core_id(c->pir);
- uint64_t tmp;
- int rc;
-
- /* PowerManagement GP0 clear PM_DISABLE */
- rc = xscom_read(chip->id, XSCOM_ADDR_P8_EX_SLAVE(core, EX_PM_GP0), &tmp);
- if (rc) {
- log_simple_error(&e_info(OPAL_RC_SLW_INIT),
- "SLW: Failed to read PM_GP0\n");
- return false;
- }
- tmp = tmp & ~0x8000000000000000ULL;
- rc = xscom_write(chip->id, XSCOM_ADDR_P8_EX_SLAVE(core, EX_PM_GP0), tmp);
- if (rc) {
- log_simple_error(&e_info(OPAL_RC_SLW_INIT),
- "SLW: Failed to write PM_GP0\n");
- return false;
- }
- prlog(PR_TRACE, "SLW: PMGP0 set to 0x%016llx\n", tmp);
-
- /* Read back for debug */
- rc = xscom_read(chip->id, XSCOM_ADDR_P8_EX_SLAVE(core, EX_PM_GP0), &tmp);
- if (rc)
- log_simple_error(&e_info(OPAL_RC_SLW_INIT),
- "SLW: Failed to re-read PM_GP0. Continuing...\n");
-
- prlog(PR_TRACE, "SLW: PMGP0 read 0x%016llx\n", tmp);
-
- return true;
-}
-
-static bool slw_set_overrides(struct proc_chip *chip, struct cpu_thread *c)
-{
- uint32_t core = pir_to_core_id(c->pir);
- int rc;
-
- rc = xscom_write(chip->id,
- XSCOM_ADDR_P8_EX_SLAVE(core, EX_PM_SPECIAL_WAKEUP_PHYP),
- 0);
- if (rc) {
- log_simple_error(&e_info(OPAL_RC_SLW_SET),
- "SLW: Failed to write PM_SPECIAL_WAKEUP_PHYP\n");
- return false;
- }
-
- return true;
-}
-
-static bool slw_set_idle_mode(struct proc_chip *chip, struct cpu_thread *c)
-{
- uint32_t core = pir_to_core_id(c->pir);
- uint64_t tmp;
- int rc;
-
- /*
- * PM GP1 allows fast/deep mode to be selected independently for sleep
- * and winkle. Init PM GP1 so that sleep happens in fast mode and
- * winkle happens in deep mode.
- * Make use of the OR XSCOM for this since the OCC might be manipulating
- * the PM_GP1 register as well. Before doing this ensure that the bits
- * managing idle states are cleared so as to override any bits set at
- * init time.
- */
-
- tmp = ~EX_PM_GP1_SLEEP_WINKLE_MASK;
- rc = xscom_write(chip->id, XSCOM_ADDR_P8_EX_SLAVE(core, EX_PM_CLEAR_GP1),
- tmp);
- if (rc) {
- log_simple_error(&e_info(OPAL_RC_SLW_SET),
- "SLW: Failed to write PM_GP1\n");
- return false;
- }
-
- rc = xscom_write(chip->id, XSCOM_ADDR_P8_EX_SLAVE(core, EX_PM_SET_GP1),
- EX_PM_SETUP_GP1_FAST_SLEEP_DEEP_WINKLE);
- if (rc) {
- log_simple_error(&e_info(OPAL_RC_SLW_SET),
- "SLW: Failed to write PM_GP1\n");
- return false;
- }
-
- /* Read back for debug */
- xscom_read(chip->id, XSCOM_ADDR_P8_EX_SLAVE(core, EX_PM_GP1), &tmp);
- prlog(PR_TRACE, "SLW: PMGP1 read 0x%016llx\n", tmp);
- return true;
-}
-
-static bool slw_get_idle_state_history(struct proc_chip *chip, struct cpu_thread *c)
-{
- uint32_t core = pir_to_core_id(c->pir);
- uint64_t tmp;
- int rc;
-
- /* Cleanup history */
- rc = xscom_read(chip->id,
- XSCOM_ADDR_P8_EX_SLAVE(core, EX_PM_IDLE_STATE_HISTORY_PHYP),
- &tmp);
- if (rc) {
- log_simple_error(&e_info(OPAL_RC_SLW_GET),
- "SLW: Failed to read PM_IDLE_STATE_HISTORY\n");
- return false;
- }
-
- prlog(PR_TRACE, "SLW: core %x:%x history: 0x%016llx (old1)\n",
- chip->id, core, tmp);
-
- rc = xscom_read(chip->id,
- XSCOM_ADDR_P8_EX_SLAVE(core, EX_PM_IDLE_STATE_HISTORY_PHYP),
- &tmp);
-
- if (rc) {
- log_simple_error(&e_info(OPAL_RC_SLW_GET),
- "SLW: Failed to read PM_IDLE_STATE_HISTORY\n");
- return false;
- }
-
- prlog(PR_TRACE, "SLW: core %x:%x history: 0x%016llx (old2)\n",
- chip->id, core, tmp);
-
- return true;
-}
-
-static bool idle_prepare_core(struct proc_chip *chip, struct cpu_thread *c)
-{
- prlog(PR_TRACE, "FASTSLEEP: Prepare core %x:%x\n",
- chip->id, pir_to_core_id(c->pir));
-
- if(!slw_general_init(chip, c))
- return false;
- if(!slw_set_overrides(chip, c))
- return false;
- if(!slw_set_idle_mode(chip, c))
- return false;
- if(!slw_get_idle_state_history(chip, c))
- return false;
-
- return true;
-
-}
-#endif
-
static bool slw_set_overrides_p10(struct proc_chip *chip, struct cpu_thread *c)
{
uint64_t tmp;
@@ -245,89 +90,6 @@ static bool slw_set_overrides_p9(struct proc_chip *chip, struct cpu_thread *c)
return true;
}
-/* Define device-tree fields */
-#define MAX_NAME_LEN 16
-struct cpu_idle_states {
- char name[MAX_NAME_LEN];
- u32 latency_ns;
- u32 residency_ns;
- /*
- * Register value/mask used to select different idle states.
- * PMICR in POWER8 and PSSCR in POWER9
- */
- u64 pm_ctrl_reg_val;
- u64 pm_ctrl_reg_mask;
- u32 flags;
-};
-
-static struct cpu_idle_states nap_only_cpu_idle_states[] = {
- { /* nap */
- .name = "nap",
- .latency_ns = 4000,
- .residency_ns = 100000,
- .flags = 0*OPAL_PM_DEC_STOP \
- | 0*OPAL_PM_TIMEBASE_STOP \
- | 1*OPAL_PM_LOSE_USER_CONTEXT \
- | 0*OPAL_PM_LOSE_HYP_CONTEXT \
- | 0*OPAL_PM_LOSE_FULL_CONTEXT \
- | 1*OPAL_PM_NAP_ENABLED \
- | 0*OPAL_PM_SLEEP_ENABLED \
- | 0*OPAL_PM_WINKLE_ENABLED \
- | 0*OPAL_USE_PMICR,
- .pm_ctrl_reg_val = 0,
- .pm_ctrl_reg_mask = 0 },
-};
-
-static struct cpu_idle_states power8_cpu_idle_states[] = {
- { /* nap */
- .name = "nap",
- .latency_ns = 4000,
- .residency_ns = 100000,
- .flags = 0*OPAL_PM_DEC_STOP \
- | 0*OPAL_PM_TIMEBASE_STOP \
- | 1*OPAL_PM_LOSE_USER_CONTEXT \
- | 0*OPAL_PM_LOSE_HYP_CONTEXT \
- | 0*OPAL_PM_LOSE_FULL_CONTEXT \
- | 1*OPAL_PM_NAP_ENABLED \
- | 0*OPAL_USE_PMICR,
- .pm_ctrl_reg_val = 0,
- .pm_ctrl_reg_mask = 0 },
- { /* fast sleep (with workaround) */
- .name = "fastsleep_",
- .latency_ns = 40000,
- .residency_ns = 300000000,
- .flags = 1*OPAL_PM_DEC_STOP \
- | 1*OPAL_PM_TIMEBASE_STOP \
- | 1*OPAL_PM_LOSE_USER_CONTEXT \
- | 0*OPAL_PM_LOSE_HYP_CONTEXT \
- | 0*OPAL_PM_LOSE_FULL_CONTEXT \
- | 1*OPAL_PM_SLEEP_ENABLED_ER1 \
- | 0*OPAL_USE_PMICR, /* Not enabled until deep
- states are available */
- .pm_ctrl_reg_val = OPAL_PM_FASTSLEEP_PMICR,
- .pm_ctrl_reg_mask = OPAL_PM_SLEEP_PMICR_MASK },
- { /* Winkle */
- .name = "winkle",
- .latency_ns = 10000000,
- .residency_ns = 1000000000, /* Educated guess (not measured).
- * Winkle is not currently used by
- * linux cpuidle subsystem so we
- * don't have real world user.
- * However, this should be roughly
- * accurate for when linux does
- * use it. */
- .flags = 1*OPAL_PM_DEC_STOP \
- | 1*OPAL_PM_TIMEBASE_STOP \
- | 1*OPAL_PM_LOSE_USER_CONTEXT \
- | 1*OPAL_PM_LOSE_HYP_CONTEXT \
- | 1*OPAL_PM_LOSE_FULL_CONTEXT \
- | 1*OPAL_PM_WINKLE_ENABLED \
- | 0*OPAL_USE_PMICR, /* Currently choosing deep vs
- fast via EX_PM_GP1 reg */
- .pm_ctrl_reg_val = 0,
- .pm_ctrl_reg_mask = 0 },
-};
-
/*
* cpu_idle_states for key idle states of POWER9 that we want to
* exploit.
@@ -796,9 +558,9 @@ static void slw_late_init_p10(struct proc_chip *chip)
void add_cpu_idle_state_properties(void)
{
struct dt_node *power_mgt;
- struct cpu_idle_states *states;
+ struct cpu_idle_states *states = NULL;
struct proc_chip *chip;
- int nr_states;
+ int nr_states = 0;
bool can_sleep = true;
bool has_stop_inst = false;
@@ -897,39 +659,12 @@ void add_cpu_idle_state_properties(void)
}
if (wakeup_engine_state != WAKEUP_ENGINE_PRESENT)
has_deep_states = false;
- } else if (chip->type == PROC_CHIP_P8_MURANO ||
- chip->type == PROC_CHIP_P8_VENICE ||
- chip->type == PROC_CHIP_P8_NAPLES) {
- const struct dt_property *p;
-
- p = dt_find_property(dt_root, "ibm,enabled-idle-states");
- if (p)
- prlog(PR_NOTICE,
- "SLW: HB-provided idle states property found\n");
- states = power8_cpu_idle_states;
- nr_states = ARRAY_SIZE(power8_cpu_idle_states);
-
- /* Check if hostboot say we can sleep */
- if (!p || !dt_prop_find_string(p, "fast-sleep")) {
- prlog(PR_WARNING, "SLW: Sleep not enabled by HB"
- " on this platform\n");
- can_sleep = false;
- }
-
- /* Clip to NAP only on Murano and Venice DD1.x */
- if ((chip->type == PROC_CHIP_P8_MURANO ||
- chip->type == PROC_CHIP_P8_VENICE) &&
- chip->ec_level < 0x20) {
- prlog(PR_NOTICE, "SLW: Sleep not enabled on P8 DD1.x\n");
- can_sleep = false;
- }
-
+#ifdef CONFIG_P8
} else {
- states = nap_only_cpu_idle_states;
- nr_states = ARRAY_SIZE(nap_only_cpu_idle_states);
+ find_cpu_idle_state_properties_p8(&states, &nr_states, &can_sleep);
+#endif
}
-
/*
* Currently we can't append strings and cells to dt properties.
* So create buffers to which you can append values, then create
@@ -1112,183 +847,6 @@ static bool slw_image_check_p9(struct proc_chip *chip)
}
-#ifdef CONFIG_P8
-static void slw_patch_regs(struct proc_chip *chip)
-{
- struct cpu_thread *c;
- void *image = (void *)chip->slw_base;
- int rc;
-
- for_each_available_cpu(c) {
- if (c->chip_id != chip->id)
- continue;
-
- /* Clear HRMOR */
- rc = p8_pore_gen_cpureg_fixed(image, P8_SLW_MODEBUILD_SRAM,
- P8_SPR_HRMOR, 0,
- cpu_get_core_index(c),
- cpu_get_thread_index(c));
- if (rc) {
- log_simple_error(&e_info(OPAL_RC_SLW_REG),
- "SLW: Failed to set HRMOR for CPU %x\n",
- c->pir);
- }
-
- /* XXX Add HIDs etc... */
- }
-}
-
-static bool slw_image_check_p8(struct proc_chip *chip)
-{
- int64_t rc;
-
- prlog(PR_DEBUG, "SLW: slw_check chip 0x%x\n", chip->id);
- if (!chip->slw_base) {
- prerror("SLW: No image found !\n");
- return false;
- }
-
- /* Check actual image size */
- rc = sbe_xip_get_scalar((void *)chip->slw_base, "image_size",
- &chip->slw_image_size);
- if (rc != 0) {
- log_simple_error(&e_info(OPAL_RC_SLW_INIT),
- "SLW: Error %lld reading SLW image size\n", rc);
- /* XXX Panic ? */
- chip->slw_base = 0;
- chip->slw_bar_size = 0;
- chip->slw_image_size = 0;
- return false;
- }
- prlog(PR_DEBUG, "SLW: Image size from image: 0x%llx\n",
- chip->slw_image_size);
-
- if (chip->slw_image_size > chip->slw_bar_size) {
- log_simple_error(&e_info(OPAL_RC_SLW_INIT),
- "SLW: Built-in image size larger than BAR size !\n");
- /* XXX Panic ? */
- return false;
- }
- return true;
-
-}
-
-static void slw_late_init_p8(struct proc_chip *chip)
-{
-
- prlog(PR_DEBUG, "SLW: late Init chip 0x%x\n", chip->id);
-
- /* Patch SLW image */
- slw_patch_regs(chip);
-
-}
-static void slw_init_chip_p8(struct proc_chip *chip)
-{
- struct cpu_thread *c;
-
- prlog(PR_DEBUG, "SLW: Init chip 0x%x\n", chip->id);
- /* At power ON setup inits for fast-sleep */
- for_each_available_core_in_chip(c, chip->id) {
- idle_prepare_core(chip, c);
- }
-}
-
-/* Workarounds while entering fast-sleep */
-
-static void fast_sleep_enter(void)
-{
- uint32_t core = pir_to_core_id(this_cpu()->pir);
- uint32_t chip_id = this_cpu()->chip_id;
- struct cpu_thread *primary_thread;
- uint64_t tmp;
- int rc;
-
- primary_thread = this_cpu()->primary;
-
- rc = xscom_read(chip_id, XSCOM_ADDR_P8_EX(core, L2_FIR_ACTION1),
- &tmp);
- if (rc) {
- prlog(PR_WARNING, "fast_sleep_enter XSCOM failed(1):"
- " rc=%d chip_id=%d core=%d\n",
- rc, chip_id, core);
- return;
- }
-
- primary_thread->save_l2_fir_action1 = tmp;
- primary_thread->in_fast_sleep = true;
-
- tmp = tmp & ~0x0200000000000000ULL;
- rc = xscom_write(chip_id, XSCOM_ADDR_P8_EX(core, L2_FIR_ACTION1),
- tmp);
- if (rc) {
- prlog(PR_WARNING, "fast_sleep_enter XSCOM failed(2):"
- " rc=%d chip_id=%d core=%d\n",
- rc, chip_id, core);
- return;
- }
- rc = xscom_read(chip_id, XSCOM_ADDR_P8_EX(core, L2_FIR_ACTION1),
- &tmp);
- if (rc) {
- prlog(PR_WARNING, "fast_sleep_enter XSCOM failed(3):"
- " rc=%d chip_id=%d core=%d\n",
- rc, chip_id, core);
- return;
- }
-
-}
-
-/* Workarounds while exiting fast-sleep */
-
-void fast_sleep_exit(void)
-{
- uint32_t core = pir_to_core_id(this_cpu()->pir);
- uint32_t chip_id = this_cpu()->chip_id;
- struct cpu_thread *primary_thread;
- int rc;
-
- primary_thread = this_cpu()->primary;
- primary_thread->in_fast_sleep = false;
-
- rc = xscom_write(chip_id, XSCOM_ADDR_P8_EX(core, L2_FIR_ACTION1),
- primary_thread->save_l2_fir_action1);
- if (rc) {
- prlog(PR_WARNING, "fast_sleep_exit XSCOM failed:"
- " rc=%d chip_id=%d core=%d\n",
- rc, chip_id, core);
- return;
- }
-}
-
-/*
- * Setup and cleanup method for fast-sleep workarounds
- * state = 1 fast-sleep
- * enter = 1 Enter state
- * exit = 0 Exit state
- */
-
-static int64_t opal_config_cpu_idle_state(uint64_t state, uint64_t enter)
-{
- /* Only fast-sleep for now */
- if (state != 1)
- return OPAL_PARAMETER;
-
- switch(enter) {
- case 1:
- fast_sleep_enter();
- break;
- case 0:
- fast_sleep_exit();
- break;
- default:
- return OPAL_PARAMETER;
- }
-
- return OPAL_SUCCESS;
-}
-
-opal_call(OPAL_CONFIG_CPU_IDLE_STATE, opal_config_cpu_idle_state, 2);
-#endif
-
int64_t opal_slw_set_reg(uint64_t cpu_pir, uint64_t sprn, uint64_t val)
{
@@ -1330,28 +888,7 @@ int64_t opal_slw_set_reg(uint64_t cpu_pir, uint64_t sprn, uint64_t val)
#ifdef CONFIG_P8
} else if (proc_gen == proc_gen_p8) {
- int spr_is_supported = 0;
- void *image;
- int i;
-
- /* Check of the SPR is supported by libpore */
- for (i = 0; i < SLW_SPR_REGS_SIZE ; i++) {
- if (sprn == SLW_SPR_REGS[i].value) {
- spr_is_supported = 1;
- break;
- }
- }
- if (!spr_is_supported) {
- log_simple_error(&e_info(OPAL_RC_SLW_REG),
- "SLW: Trying to set unsupported spr for CPU %x\n",
- c->pir);
- return OPAL_UNSUPPORTED;
- }
- image = (void *)chip->slw_base;
- rc = p8_pore_gen_cpureg_fixed(image, P8_SLW_MODEBUILD_SRAM,
- sprn, val,
- cpu_get_core_index(c),
- cpu_get_thread_index(c));
+ rc = opal_slw_set_reg_p8(c, chip, sprn, val);
#endif
} else {
log_simple_error(&e_info(OPAL_RC_SLW_REG),
@@ -1386,18 +923,11 @@ void slw_init(void)
return;
}
- if (proc_gen == proc_gen_p8) {
#ifdef CONFIG_P8
- for_each_chip(chip) {
- slw_init_chip_p8(chip);
- if(slw_image_check_p8(chip))
- wakeup_engine_state = WAKEUP_ENGINE_PRESENT;
- if (wakeup_engine_state == WAKEUP_ENGINE_PRESENT)
- slw_late_init_p8(chip);
- }
- p8_sbe_init_timer();
+ if (proc_gen == proc_gen_p8)
+ slw_p8_init();
#endif
- } else if (proc_gen == proc_gen_p9) {
+ if (proc_gen == proc_gen_p9) {
for_each_chip(chip) {
slw_init_chip_p9(chip);
if(slw_image_check_p9(chip))
diff --git a/hw/xive.c b/hw/xive.c
index 51b0354..34b92f1 100644
--- a/hw/xive.c
+++ b/hw/xive.c
@@ -7,6 +7,7 @@
*/
#include <skiboot.h>
+#include <slw.h>
#include <xscom.h>
#include <chip.h>
#include <io.h>
diff --git a/hw/xive2.c b/hw/xive2.c
index 30293cd..ea55423 100644
--- a/hw/xive2.c
+++ b/hw/xive2.c
@@ -9,6 +9,7 @@
#define pr_fmt(fmt) "XIVE: " fmt
#include <skiboot.h>
+#include <slw.h>
#include <xscom.h>
#include <chip.h>
#include <io.h>
diff --git a/include/skiboot.h b/include/skiboot.h
index e2a1b5f..db08f45 100644
--- a/include/skiboot.h
+++ b/include/skiboot.h
@@ -216,7 +216,6 @@ extern void uart_init(void);
extern void mbox_init(void);
extern void early_uart_init(void);
extern void homer_init(void);
-extern void slw_init(void);
extern void add_cpu_idle_state_properties(void);
extern void lpc_rtc_init(void);
@@ -303,19 +302,8 @@ extern int prd_hbrt_fsp_msg_notify(void *data, u32 dsize);
/* Flatten device-tree */
extern void *create_dtb(const struct dt_node *root, bool exclusive);
-/* Track failure in Wakup engine */
-enum wakeup_engine_states {
- WAKEUP_ENGINE_NOT_PRESENT,
- WAKEUP_ENGINE_PRESENT,
- WAKEUP_ENGINE_FAILED
-};
-extern enum wakeup_engine_states wakeup_engine_state;
-extern bool has_deep_states;
extern void nx_p9_rng_late_init(void);
-/* Patch SPR in SLW image */
-extern int64_t opal_slw_set_reg(uint64_t cpu_pir, uint64_t sprn, uint64_t val);
-
extern void fast_sleep_exit(void);
/* Fallback fake RTC */
diff --git a/include/slw.h b/include/slw.h
new file mode 100644
index 0000000..299cbf1
--- /dev/null
+++ b/include/slw.h
@@ -0,0 +1,48 @@
+// SPDX-License-Identifier: Apache-2.0 OR GPL-2.0-or-later
+
+#ifndef __SLW_H
+#define __SLW_H
+
+#include <stdint.h>
+#include <stdbool.h>
+
+#include <ccan/short_types/short_types.h>
+
+/* Define device-tree fields */
+#define MAX_NAME_LEN 16
+struct cpu_idle_states {
+ char name[MAX_NAME_LEN];
+ u32 latency_ns;
+ u32 residency_ns;
+ /*
+ * Register value/mask used to select different idle states.
+ * PMICR in POWER8 and PSSCR in POWER9
+ */
+ u64 pm_ctrl_reg_val;
+ u64 pm_ctrl_reg_mask;
+ u32 flags;
+};
+
+/* Track failure in Wakup engine */
+enum wakeup_engine_states {
+ WAKEUP_ENGINE_NOT_PRESENT,
+ WAKEUP_ENGINE_PRESENT,
+ WAKEUP_ENGINE_FAILED
+};
+extern enum wakeup_engine_states wakeup_engine_state;
+extern bool has_deep_states;
+
+/* Patch SPR in SLW image */
+extern int64_t opal_slw_set_reg(uint64_t cpu_pir, uint64_t sprn, uint64_t val);
+
+extern void slw_init(void);
+
+/* P8 specific */
+struct cpu_thread;
+struct proc_chip;
+extern int64_t opal_slw_set_reg_p8(struct cpu_thread *c, struct proc_chip *chip,
+ uint64_t sprn, uint64_t val);
+extern void slw_p8_init(void);
+extern void find_cpu_idle_state_properties_p8(struct cpu_idle_states **states, int *nr_states, bool *can_sleep);
+
+#endif /* __SKIBOOT_H */