aboutsummaryrefslogtreecommitdiff
path: root/core/direct-controls.c
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 /core/direct-controls.c
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>
Diffstat (limited to 'core/direct-controls.c')
-rw-r--r--core/direct-controls.c292
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