diff options
author | Nicholas Piggin <npiggin@gmail.com> | 2017-11-29 15:36:52 +1000 |
---|---|---|
committer | Stewart Smith <stewart@linux.vnet.ibm.com> | 2017-12-03 21:49:12 -0600 |
commit | be43a0489e06f46ff645d9c88e28d75f6bf6ea1b (patch) | |
tree | 7576c973ef5eaa814ed8ff972483928cdc6622e1 /core/direct-controls.c | |
parent | 1647413d7c2fca509facec03bcb4488d6caaf81a (diff) | |
download | skiboot-be43a0489e06f46ff645d9c88e28d75f6bf6ea1b.zip skiboot-be43a0489e06f46ff645d9c88e28d75f6bf6ea1b.tar.gz skiboot-be43a0489e06f46ff645d9c88e28d75f6bf6ea1b.tar.bz2 |
fast-reboot: move sreset direct controls to direct-controls.c
Signed-off-by: Nicholas Piggin <npiggin@gmail.com>
Signed-off-by: Stewart Smith <stewart@linux.vnet.ibm.com>
Diffstat (limited to 'core/direct-controls.c')
-rw-r--r-- | core/direct-controls.c | 292 |
1 files changed, 292 insertions, 0 deletions
diff --git a/core/direct-controls.c b/core/direct-controls.c index c5ba80e..2f3c18d 100644 --- a/core/direct-controls.c +++ b/core/direct-controls.c @@ -14,6 +14,7 @@ * limitations under the License. */ +#include <direct-controls.h> #include <skiboot.h> #include <opal.h> #include <cpu.h> @@ -21,6 +22,297 @@ #include <timebase.h> #include <chip.h> + +/**************** mambo direct controls ****************/ + +extern unsigned long callthru_tcl(const char *str, int len); + +static void mambo_sreset_cpu(struct cpu_thread *cpu) +{ + uint32_t core_id = pir_to_core_id(cpu->pir); + uint32_t thread_id = pir_to_thread_id(cpu->pir); + char tcl_cmd[50]; + + snprintf(tcl_cmd, sizeof(tcl_cmd), "mysim cpu %i:%i set spr pc 0x100", core_id, thread_id); + callthru_tcl(tcl_cmd, strlen(tcl_cmd)); +} + +/**************** POWER8 direct controls ****************/ + +#define P8_EX_TCTL_DIRECT_CONTROLS(t) (0x10013000 + (t) * 0x10) +#define P8_DIRECT_CTL_STOP PPC_BIT(63) +#define P8_DIRECT_CTL_PRENAP PPC_BIT(47) +#define P8_DIRECT_CTL_SRESET PPC_BIT(60) + +static int p8_set_special_wakeup(struct cpu_thread *cpu) +{ + uint64_t val, poll_target, stamp; + uint32_t core_id; + int rc; + + /* + * Note: HWP checks for checkstops, but I assume we don't need to + * as we wouldn't be running if one was present + */ + + /* Grab core ID once */ + core_id = pir_to_core_id(cpu->pir); + + prlog(PR_DEBUG, "RESET Waking up core 0x%x\n", core_id); + + /* + * The original HWp reads the XSCOM first but ignores the result + * and error, let's do the same until I know for sure that is + * not necessary + */ + xscom_read(cpu->chip_id, + XSCOM_ADDR_P8_EX_SLAVE(core_id, EX_PM_SPECIAL_WAKEUP_PHYP), + &val); + + /* Then we write special wakeup */ + rc = xscom_write(cpu->chip_id, + XSCOM_ADDR_P8_EX_SLAVE(core_id, + EX_PM_SPECIAL_WAKEUP_PHYP), + PPC_BIT(0)); + if (rc) { + prerror("RESET: XSCOM error %d asserting special" + " wakeup on 0x%x\n", rc, cpu->pir); + return rc; + } + + /* + * HWP uses the history for Perf register here, dunno why it uses + * that one instead of the pHyp one, maybe to avoid clobbering it... + * + * In any case, it does that to check for run/nap vs.sleep/winkle/other + * to decide whether to poll on checkstop or not. Since we don't deal + * with checkstop conditions here, we ignore that part. + */ + + /* + * Now poll for completion of special wakeup. The HWP is nasty here, + * it will poll at 5ms intervals for up to 200ms. This is not quite + * acceptable for us at runtime, at least not until we have the + * ability to "context switch" HBRT. In practice, because we don't + * winkle, it will never take that long, so we increase the polling + * frequency to 1us per poll. However we do have to keep the same + * timeout. + * + * We don't use time_wait_ms() either for now as we don't want to + * poll the FSP here. + */ + stamp = mftb(); + poll_target = stamp + msecs_to_tb(200); + val = 0; + while (!(val & EX_PM_GP0_SPECIAL_WAKEUP_DONE)) { + /* Wait 1 us */ + time_wait_us(1); + + /* Read PM state */ + rc = xscom_read(cpu->chip_id, + XSCOM_ADDR_P8_EX_SLAVE(core_id, EX_PM_GP0), + &val); + if (rc) { + prerror("RESET: XSCOM error %d reading PM state on" + " 0x%x\n", rc, cpu->pir); + return rc; + } + /* Check timeout */ + if (mftb() > poll_target) + break; + } + + /* Success ? */ + if (val & EX_PM_GP0_SPECIAL_WAKEUP_DONE) { + uint64_t now = mftb(); + prlog(PR_TRACE, "RESET: Special wakeup complete after %ld us\n", + tb_to_usecs(now - stamp)); + return 0; + } + + /* + * We timed out ... + * + * HWP has a complex workaround for HW255321 which affects + * Murano DD1 and Venice DD1. Ignore that for now + * + * Instead we just dump some XSCOMs for error logging + */ + prerror("RESET: Timeout on special wakeup of 0x%0x\n", cpu->pir); + prerror("RESET: PM0 = 0x%016llx\n", val); + val = -1; + xscom_read(cpu->chip_id, + XSCOM_ADDR_P8_EX_SLAVE(core_id, EX_PM_SPECIAL_WAKEUP_PHYP), + &val); + prerror("RESET: SPC_WKUP = 0x%016llx\n", val); + val = -1; + xscom_read(cpu->chip_id, + XSCOM_ADDR_P8_EX_SLAVE(core_id, + EX_PM_IDLE_STATE_HISTORY_PHYP), + &val); + prerror("RESET: HISTORY = 0x%016llx\n", val); + + return OPAL_HARDWARE; +} + +static int p8_clr_special_wakeup(struct cpu_thread *cpu) +{ + uint64_t val; + uint32_t core_id; + int rc; + + /* + * Note: HWP checks for checkstops, but I assume we don't need to + * as we wouldn't be running if one was present + */ + + /* Grab core ID once */ + core_id = pir_to_core_id(cpu->pir); + + prlog(PR_DEBUG, "RESET: Releasing core 0x%x wakeup\n", core_id); + + /* + * The original HWp reads the XSCOM first but ignores the result + * and error, let's do the same until I know for sure that is + * not necessary + */ + xscom_read(cpu->chip_id, + XSCOM_ADDR_P8_EX_SLAVE(core_id, EX_PM_SPECIAL_WAKEUP_PHYP), + &val); + + /* Then we write special wakeup */ + rc = xscom_write(cpu->chip_id, + XSCOM_ADDR_P8_EX_SLAVE(core_id, + EX_PM_SPECIAL_WAKEUP_PHYP), 0); + if (rc) { + prerror("RESET: XSCOM error %d deasserting" + " special wakeup on 0x%x\n", rc, cpu->pir); + return rc; + } + + /* + * The original HWp reads the XSCOM again with the comment + * "This puts an inherent delay in the propagation of the reset + * transition" + */ + xscom_read(cpu->chip_id, + XSCOM_ADDR_P8_EX_SLAVE(core_id, EX_PM_SPECIAL_WAKEUP_PHYP), + &val); + + return 0; +} + +static void p8_set_direct_ctl(struct cpu_thread *cpu, uint64_t bits) +{ + uint32_t core_id = pir_to_core_id(cpu->pir); + uint32_t chip_id = pir_to_chip_id(cpu->pir); + uint32_t thread_id = pir_to_thread_id(cpu->pir); + uint32_t xscom_addr; + + xscom_addr = XSCOM_ADDR_P8_EX(core_id, + P8_EX_TCTL_DIRECT_CONTROLS(thread_id)); + + xscom_write(chip_id, xscom_addr, bits); +} + +static int p8_sreset_all_prepare(void) +{ + struct cpu_thread *cpu; + + prlog(PR_DEBUG, "RESET: Resetting from cpu: 0x%x (core 0x%x)\n", + this_cpu()->pir, pir_to_core_id(this_cpu()->pir)); + + /* Assert special wakup on all cores. Only on operational cores. */ + for_each_ungarded_primary(cpu) { + if (p8_set_special_wakeup(cpu) != OPAL_SUCCESS) + return OPAL_HARDWARE; + } + + prlog(PR_DEBUG, "RESET: Stopping the world...\n"); + + /* Put everybody in stop except myself */ + for_each_ungarded_cpu(cpu) { + if (cpu != this_cpu()) + p8_set_direct_ctl(cpu, P8_DIRECT_CTL_STOP); + } + + return OPAL_SUCCESS; +} + +static void p8_sreset_all_finish(void) +{ + struct cpu_thread *cpu; + + for_each_ungarded_primary(cpu) + p8_clr_special_wakeup(cpu); +} + +static void p8_sreset_all_others(void) +{ + struct cpu_thread *cpu; + + prlog(PR_DEBUG, "RESET: Pre-napping all threads but one...\n"); + + /* Put everybody in pre-nap except myself */ + for_each_ungarded_cpu(cpu) { + if (cpu != this_cpu()) + p8_set_direct_ctl(cpu, P8_DIRECT_CTL_PRENAP); + } + + prlog(PR_DEBUG, "RESET: Resetting all threads but one...\n"); + + /* Reset everybody except my own core threads */ + for_each_ungarded_cpu(cpu) { + if (cpu != this_cpu()) + p8_set_direct_ctl(cpu, P8_DIRECT_CTL_SRESET); + } +} + +int sreset_all_prepare(void) +{ + if (chip_quirk(QUIRK_MAMBO_CALLOUTS)) + return OPAL_SUCCESS; + + if (proc_gen == proc_gen_p8) + return p8_sreset_all_prepare(); + + return OPAL_UNSUPPORTED; +} + +void sreset_all_finish(void) +{ + if (chip_quirk(QUIRK_MAMBO_CALLOUTS)) + return; + + if (proc_gen == proc_gen_p8) + return p8_sreset_all_finish(); +} + +int sreset_all_others(void) +{ + if (chip_quirk(QUIRK_MAMBO_CALLOUTS)) { + struct cpu_thread *cpu; + + for_each_ungarded_cpu(cpu) { + if (cpu == this_cpu()) + continue; + mambo_sreset_cpu(cpu); + } + + return OPAL_SUCCESS; + } + + if (proc_gen == proc_gen_p8) { + p8_sreset_all_others(); + return OPAL_SUCCESS; + } + + return OPAL_UNSUPPORTED; +} + + +/**************** POWER9 direct controls ****************/ + #define P9_RAS_STATUS 0x10a02 #define P9_THREAD_QUIESCED(t) PPC_BITMASK(0 + 8*(t), 3 + 8*(t)) #define P9_QUIESCE_RETRIES 100 |