Message ID | 20211220122252.986542-7-npiggin@gmail.com |
---|---|
State | Accepted |
Headers | show |
Series | hwprobe patches | expand |
On 12/20/21 13:22, Nicholas Piggin wrote: > 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> Thanks for doing this cleanup. Looks good to me. C. > --- > core/fast-reboot.c | 1 + > core/init.c | 1 + > hw/Makefile.inc | 2 +- > hw/imc.c | 1 + > hw/nx.c | 1 + > hw/slw-p8.c | 508 +++++++++++++++++++++++++++++++++++++++++++++ > hw/slw.c | 491 +------------------------------------------ > hw/xive.c | 1 + > hw/xive2.c | 1 + > include/skiboot.h | 12 -- > include/slw.h | 48 +++++ > 11 files changed, 574 insertions(+), 493 deletions(-) > create mode 100644 hw/slw-p8.c > create mode 100644 include/slw.h > > diff --git a/core/fast-reboot.c b/core/fast-reboot.c > index 2696348af..fedfa88cc 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 c3e0c494d..deead5ecc 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 8f509739d..7327cec35 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 cbd68edc4..97e0809f0 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 fdadf53c7..13c681b2c 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 000000000..0a27a8fcc > --- /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 bc53960b7..6d7c8619c 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,10 @@ > #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> > +//#include <sbe_xip_image.h> > > enum wakeup_engine_states wakeup_engine_state = WAKEUP_ENGINE_NOT_PRESENT; > bool has_deep_states = false; > @@ -40,158 +38,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 +91,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 +559,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 +660,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 +848,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 +889,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 +924,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 51b03549a..34b92f1e9 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 30293cdf1..ea55423bb 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 e2a1b5fc6..db08f45f4 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 000000000..299cbf191 > --- /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 */ >
diff --git a/core/fast-reboot.c b/core/fast-reboot.c index 2696348af..fedfa88cc 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 c3e0c494d..deead5ecc 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 8f509739d..7327cec35 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 cbd68edc4..97e0809f0 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 fdadf53c7..13c681b2c 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 000000000..0a27a8fcc --- /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 bc53960b7..6d7c8619c 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,10 @@ #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> +//#include <sbe_xip_image.h> enum wakeup_engine_states wakeup_engine_state = WAKEUP_ENGINE_NOT_PRESENT; bool has_deep_states = false; @@ -40,158 +38,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 +91,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 +559,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 +660,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 +848,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 +889,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 +924,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 51b03549a..34b92f1e9 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 30293cdf1..ea55423bb 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 e2a1b5fc6..db08f45f4 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 000000000..299cbf191 --- /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 */
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> --- core/fast-reboot.c | 1 + core/init.c | 1 + hw/Makefile.inc | 2 +- hw/imc.c | 1 + hw/nx.c | 1 + hw/slw-p8.c | 508 +++++++++++++++++++++++++++++++++++++++++++++ hw/slw.c | 491 +------------------------------------------ hw/xive.c | 1 + hw/xive2.c | 1 + include/skiboot.h | 12 -- include/slw.h | 48 +++++ 11 files changed, 574 insertions(+), 493 deletions(-) create mode 100644 hw/slw-p8.c create mode 100644 include/slw.h