diff --git a/core/direct-controls.c b/core/direct-controls.c
index 512cb364..79fa3b5a 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,294 @@
 #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)
+			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 62635e17..8da1dd1b 100644
--- a/core/fast-reboot.c
+++ b/core/fast-reboot.c
@@ -27,303 +27,13 @@
 #include <chip.h>
 #include <chiptod.h>
 #include <ipmi.h>
-
-#define for_each_ungarded_primary(cpu)				\
-	for_each_cpu(cpu)					\
-		if ((cpu->state != cpu_state_unavailable) &&	\
-				(cpu->primary == cpu))
-
-#define for_each_ungarded_cpu(cpu)				\
-	for_each_cpu(cpu)					\
-		if (cpu->state != cpu_state_unavailable)
-
-
-#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)
-			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 fast_reset(void)
 {
 	struct cpu_thread *cpu;
diff --git a/include/cpu.h b/include/cpu.h
index 168fa994..dd004cd0 100644
--- a/include/cpu.h
+++ b/include/cpu.h
@@ -189,6 +189,15 @@ extern struct cpu_thread *next_present_cpu(struct cpu_thread *cpu);
 #define for_each_present_cpu(cpu)	\
 	for (cpu = first_present_cpu(); cpu; cpu = next_present_cpu(cpu))
 
+#define for_each_ungarded_primary(cpu)				\
+	for_each_cpu(cpu)					\
+		if ((cpu->state != cpu_state_unavailable) &&	\
+				(cpu->primary == cpu))
+
+#define for_each_ungarded_cpu(cpu)				\
+	for_each_cpu(cpu)					\
+		if (cpu->state != cpu_state_unavailable)
+
 extern struct cpu_thread *first_available_core_in_chip(u32 chip_id);
 extern struct cpu_thread *next_available_core_in_chip(struct cpu_thread *cpu, u32 chip_id);
 extern u8 get_available_nr_cores_in_chip(u32 chip_id);
diff --git a/include/direct-controls.h b/include/direct-controls.h
new file mode 100644
index 00000000..9df15490
--- /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 */
