aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorNicholas Piggin <npiggin@gmail.com>2017-11-29 15:36:52 +1000
committerStewart Smith <stewart@linux.vnet.ibm.com>2017-12-03 21:49:12 -0600
commitbe43a0489e06f46ff645d9c88e28d75f6bf6ea1b (patch)
tree7576c973ef5eaa814ed8ff972483928cdc6622e1
parent1647413d7c2fca509facec03bcb4488d6caaf81a (diff)
downloadskiboot-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>
-rw-r--r--core/direct-controls.c292
-rw-r--r--core/fast-reboot.c284
-rw-r--r--include/direct-controls.h29
3 files changed, 322 insertions, 283 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
diff --git a/core/fast-reboot.c b/core/fast-reboot.c
index 84574db..4b7e9aa 100644
--- a/core/fast-reboot.c
+++ b/core/fast-reboot.c
@@ -27,295 +27,13 @@
#include <chip.h>
#include <chiptod.h>
#include <ipmi.h>
-
-#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)
-
+#include <direct-controls.h>
/* Flag tested by the OPAL entry code */
uint8_t reboot_in_progress;
static volatile bool fast_boot_release;
static struct lock reset_lock = LOCK_UNLOCKED;
-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);
- }
-}
-
-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));
-}
-
-static 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;
-}
-
-static void sreset_all_finish(void)
-{
- if (chip_quirk(QUIRK_MAMBO_CALLOUTS))
- return;
-
- if (proc_gen == proc_gen_p8)
- return p8_sreset_all_finish();
-}
-
-static 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;
-}
-
static bool cpu_state_wait_all_others(enum cpu_thread_state state,
unsigned long timeout_tb)
{
diff --git a/include/direct-controls.h b/include/direct-controls.h
new file mode 100644
index 0000000..9df1549
--- /dev/null
+++ b/include/direct-controls.h
@@ -0,0 +1,29 @@
+/* Copyright 2017 IBM Corp.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
+ * implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef __DIRECT_CONTROLS_H
+#define __DIRECT_CONTROLS_H
+
+#include <skiboot.h>
+#include <opal.h>
+#include <cpu.h>
+
+/* fast reboot APIs */
+extern int sreset_all_prepare(void);
+extern int sreset_all_others(void);
+extern void sreset_all_finish(void);
+
+#endif /* __DIRECT_CONTROLS_H */