diff --git a/core/interrupts.c b/core/interrupts.c
index 006d555..ce1df95 100644
--- a/core/interrupts.c
+++ b/core/interrupts.c
@@ -25,6 +25,7 @@
 #include <device.h>
 #include <ccan/str/str.h>
 #include <timer.h>
+#include <sbe-p8.h>
 
 /* ICP registers */
 #define ICP_XIRR		0x4	/* 32-bit access */
@@ -456,8 +457,8 @@ static int64_t opal_handle_interrupt(uint32_t isn, __be64 *outstanding_event_mas
 	/* Run it */
 	is->ops->interrupt(is, isn);
 
-	/* Check timers if SLW timer isn't working */
-	if (!slw_timer_ok())
+	/* Check timers if SBE timer isn't working */
+	if (!sbe_p8_timer_ok())
 		check_timers(true);
 
 	/* Update output events */
diff --git a/core/timer.c b/core/timer.c
index 7548996..2b4c921 100644
--- a/core/timer.c
+++ b/core/timer.c
@@ -4,6 +4,7 @@
 #include <fsp.h>
 #include <device.h>
 #include <opal.h>
+#include <sbe-p8.h>
 
 #ifdef __TEST__
 #define this_cpu()	((void *)-1)
@@ -106,7 +107,7 @@ static void __schedule_timer_at(struct timer *t, uint64_t when)
 	/* Pick up the next timer and upddate the SBE HW timer */
 	lt = list_top(&timer_list, struct timer, link);
 	if (lt)
-		slw_update_timer_expiry(lt->target);
+		sbe_p8_update_timer_expiry(lt->target);
 }
 
 void schedule_timer_at(struct timer *t, uint64_t when)
@@ -163,7 +164,7 @@ static void __check_poll_timers(uint64_t now)
 		 * arbitrarily 1us.
 		 */
 		if (t->running) {
-			slw_update_timer_expiry(now + usecs_to_tb(1));
+			sbe_p8_update_timer_expiry(now + usecs_to_tb(1));
 			break;
 		}
 
@@ -253,7 +254,7 @@ void late_init_timers(void)
 	 *
 	 * If a platform quirk exists, use that, else use the default.
 	 *
-	 * If we have an SLW timer facility, we run this 10 times slower,
+	 * If we have an SBE timer facility, we run this 10 times slower,
 	 * we could possibly completely get rid of it.
 	 *
 	 * We use a value in milliseconds, we don't want this to ever be
@@ -261,7 +262,7 @@ void late_init_timers(void)
 	 */
 	if (platform.heartbeat_time) {
 		heartbeat = platform.heartbeat_time();
-	} else if (slw_timer_ok() || fsp_present()) {
+	} else if (sbe_p8_timer_ok() || fsp_present()) {
 		heartbeat = HEARTBEAT_DEFAULT_MS * 10;
 	}
 
diff --git a/hw/Makefile.inc b/hw/Makefile.inc
index b0a8b7c..1c48ab9 100644
--- a/hw/Makefile.inc
+++ b/hw/Makefile.inc
@@ -7,6 +7,7 @@ HW_OBJS += p7ioc.o p7ioc-inits.o p7ioc-phb.o
 HW_OBJS += phb3.o sfc-ctrl.o fake-rtc.o bt.o p8-i2c.o prd.o
 HW_OBJS += dts.o lpc-rtc.o npu.o npu-hw-procedures.o xive.o phb4.o
 HW_OBJS += fake-nvram.o lpc-mbox.o npu2.o npu2-hw-procedures.o
+HW_OBJS += sbe-p8.o
 HW=hw/built-in.o
 
 # FIXME hack this for now
diff --git a/hw/sbe-p8.c b/hw/sbe-p8.c
new file mode 100644
index 0000000..6edb117
--- /dev/null
+++ b/hw/sbe-p8.c
@@ -0,0 +1,203 @@
+/* Copyright 2013-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.
+ */
+
+#include <device.h>
+#include <sbe-p8.h>
+#include <skiboot.h>
+#include <timebase.h>
+#include <xscom.h>
+
+/* SLW timer related stuff */
+static bool sbe_has_timer;
+static uint64_t sbe_timer_inc;
+static uint64_t sbe_timer_target;
+static uint32_t sbe_timer_chip;
+static uint64_t sbe_last_gen;
+static uint64_t sbe_last_gen_stamp;
+
+static void sbe_p8_dump_timer_ffdc(void)
+{
+	uint64_t i, val;
+	int64_t rc;
+
+	static const uint32_t dump_regs[] = {
+		0xe0000, 0xe0001, 0xe0002, 0xe0003,
+		0xe0004, 0xe0005, 0xe0006, 0xe0007,
+		0xe0008, 0xe0009, 0xe000a, 0xe000b,
+		0xe000c, 0xe000d, 0xe000e, 0xe000f,
+		0xe0010, 0xe0011, 0xe0012, 0xe0013,
+		0xe0014, 0xe0015, 0xe0016, 0xe0017,
+		0xe0018, 0xe0019,
+		0x5001c,
+		0x50038, 0x50039, 0x5003a, 0x5003b
+	};
+
+	/**
+	 * @fwts-label SLWRegisterDump
+	 * @fwts-advice An error condition occurred in sleep/winkle
+	 * engines timer state machine. Dumping debug information to
+	 * root-cause. OPAL/skiboot may be stuck on some operation that
+	 * requires SLW timer state machine (e.g. core powersaving)
+	 */
+	prlog(PR_DEBUG, "SLW: Register state:\n");
+
+	for (i = 0; i < ARRAY_SIZE(dump_regs); i++) {
+		uint32_t reg = dump_regs[i];
+		rc = xscom_read(sbe_timer_chip, reg, &val);
+		if (rc) {
+			prlog(PR_DEBUG, "SLW: XSCOM error %lld reading"
+			      " reg 0x%x\n", rc, reg);
+			break;
+		}
+		prlog(PR_DEBUG, "SLW:  %5x = %016llx\n", reg, val);
+	}
+}
+
+/* This is called with the timer lock held, so there is no
+ * issue with re-entrancy or concurrence
+ */
+void sbe_p8_update_timer_expiry(uint64_t new_target)
+{
+	uint64_t count, gen, gen2, req, now = mftb();
+	int64_t rc;
+
+	if (!sbe_has_timer || new_target == sbe_timer_target)
+		return;
+
+	sbe_timer_target = new_target;
+
+	/* Calculate how many increments from now, rounded up */
+	if (now < new_target)
+		count = (new_target - now + sbe_timer_inc - 1) / sbe_timer_inc;
+	else
+		count = 1;
+
+	/* Max counter is 24-bit */
+	if (count > 0xffffff)
+		count = 0xffffff;
+	/* Fabricate update request */
+	req = (1ull << 63) | (count << 32);
+
+	prlog(PR_TRACE, "SLW: TMR expiry: 0x%llx, req: %016llx\n", count, req);
+
+	do {
+		/* Grab generation and spin if odd */
+		_xscom_lock();
+		for (;;) {
+			rc = _xscom_read(sbe_timer_chip, 0xE0006, &gen, false);
+			if (rc) {
+				prerror("SLW: Error %lld reading tmr gen "
+					" count\n", rc);
+				_xscom_unlock();
+				return;
+			}
+			if (!(gen & 1))
+				break;
+			if (tb_compare(now + msecs_to_tb(1), mftb()) == TB_ABEFOREB) {
+				/**
+				 * @fwts-label SLWTimerStuck
+				 * @fwts-advice The SLeep/Winkle Engine (SLW)
+				 * failed to increment the generation number
+				 * within our timeout period (it *should* have
+				 * done so within ~10us, not >1ms. OPAL uses
+				 * the SLW timer to schedule some operations,
+				 * but can fall back to the (much less frequent
+				 * OPAL poller, which although does not affect
+				 * functionality, runs *much* less frequently.
+				 * This could have the effect of slow I2C
+				 * operations (for example). It may also mean
+				 * that you *had* an increase in jitter, due
+				 * to slow interactions with SLW.
+				 * This error may also occur if the machine
+				 * is connected to via soft FSI.
+				 */
+				prerror("SLW: timer stuck, falling back to OPAL pollers. You will likely have slower I2C and may have experienced increased jitter.\n");
+				prlog(PR_DEBUG, "SLW: Stuck with odd generation !\n");
+				_xscom_unlock();
+				sbe_has_timer = false;
+				sbe_p8_dump_timer_ffdc();
+				return;
+			}
+		}
+
+		rc = _xscom_write(sbe_timer_chip, 0x5003A, req, false);
+		if (rc) {
+			prerror("SLW: Error %lld writing tmr request\n", rc);
+			_xscom_unlock();
+			return;
+		}
+
+		/* Re-check gen count */
+		rc = _xscom_read(sbe_timer_chip, 0xE0006, &gen2, false);
+		if (rc) {
+			prerror("SLW: Error %lld re-reading tmr gen "
+				" count\n", rc);
+			_xscom_unlock();
+			return;
+		}
+		_xscom_unlock();
+	} while(gen != gen2);
+
+	/* Check if the timer is working. If at least 1ms has elapsed
+	 * since the last call to this function, check that the gen
+	 * count has changed
+	 */
+	if (tb_compare(sbe_last_gen_stamp + msecs_to_tb(1), now)
+	    == TB_ABEFOREB) {
+		if (sbe_last_gen == gen) {
+			prlog(PR_ERR,
+			      "SLW: Timer appears to not be running !\n");
+			sbe_has_timer = false;
+			sbe_p8_dump_timer_ffdc();
+		}
+		sbe_last_gen = gen;
+		sbe_last_gen_stamp = mftb();
+	}
+
+	prlog(PR_TRACE, "SLW: gen: %llx\n", gen);
+}
+
+bool sbe_p8_timer_ok(void)
+{
+	return sbe_has_timer;
+}
+
+void sbe_p8_init_timer(void)
+{
+	struct dt_node *np;
+	int64_t rc;
+	uint32_t tick_us;
+
+	np = dt_find_compatible_node(dt_root, NULL, "ibm,power8-sbe-timer");
+	if (!np)
+		return;
+
+	sbe_timer_chip = dt_get_chip_id(np);
+	tick_us = dt_prop_get_u32(np, "tick-time-us");
+	sbe_timer_inc = usecs_to_tb(tick_us);
+	sbe_timer_target = ~0ull;
+
+	rc = xscom_read(sbe_timer_chip, 0xE0006, &sbe_last_gen);
+	if (rc) {
+		prerror("SLW: Error %lld reading tmr gen count\n", rc);
+		return;
+	}
+	sbe_last_gen_stamp = mftb();
+
+	prlog(PR_INFO, "SLW: Timer facility on chip %d, resolution %dus\n",
+	      sbe_timer_chip, tick_us);
+	sbe_has_timer = true;
+}
diff --git a/hw/slw.c b/hw/slw.c
index 6503fa7..a32526a 100644
--- a/hw/slw.c
+++ b/hw/slw.c
@@ -29,6 +29,7 @@
 #include <errorlog.h>
 #include <libfdt/libfdt.h>
 #include <opal-api.h>
+#include <sbe-p8.h>
 
 #ifdef __HAVE_LIBPORE__
 #include <p8_pore_table_gen_api.H>
@@ -41,14 +42,6 @@ static uint32_t slw_saved_reset[MAX_RESET_PATCH_SIZE];
 static bool slw_current_le = false;
 #endif /* __HAVE_LIBPORE__ */
 
-/* SLW timer related stuff */
-static bool slw_has_timer;
-static uint64_t slw_timer_inc;
-static uint64_t slw_timer_target;
-static uint32_t slw_timer_chip;
-static uint64_t slw_last_gen;
-static uint64_t slw_last_gen_stamp;
-
 DEFINE_LOG_ENTRY(OPAL_RC_SLW_INIT, OPAL_PLATFORM_ERR_EVT, OPAL_SLW,
 		 OPAL_PLATFORM_FIRMWARE, OPAL_PREDICTIVE_ERR_GENERAL,
 		 OPAL_NA);
@@ -1358,180 +1351,6 @@ int64_t opal_slw_set_reg(uint64_t cpu_pir, uint64_t sprn, uint64_t val)
 opal_call(OPAL_SLW_SET_REG, opal_slw_set_reg, 3);
 #endif /* __HAVE_LIBPORE__ */
 
-static void slw_dump_timer_ffdc(void)
-{
-	uint64_t i, val;
-	int64_t rc;
-
-	static const uint32_t dump_regs[] = {
-		0xe0000, 0xe0001, 0xe0002, 0xe0003,
-		0xe0004, 0xe0005, 0xe0006, 0xe0007,
-		0xe0008, 0xe0009, 0xe000a, 0xe000b,
-		0xe000c, 0xe000d, 0xe000e, 0xe000f,
-		0xe0010, 0xe0011, 0xe0012, 0xe0013,
-		0xe0014, 0xe0015, 0xe0016, 0xe0017,
-		0xe0018, 0xe0019,
-		0x5001c,
-		0x50038, 0x50039, 0x5003a, 0x5003b
-	};
-
-	/**
-	 * @fwts-label SLWRegisterDump
-	 * @fwts-advice An error condition occurred in sleep/winkle
-	 * engines timer state machine. Dumping debug information to
-	 * root-cause. OPAL/skiboot may be stuck on some operation that
-	 * requires SLW timer state machine (e.g. core powersaving)
-	 */
-	prlog(PR_DEBUG, "SLW: Register state:\n");
-
-	for (i = 0; i < ARRAY_SIZE(dump_regs); i++) {
-		uint32_t reg = dump_regs[i];
-		rc = xscom_read(slw_timer_chip, reg, &val);
-		if (rc) {
-			prlog(PR_DEBUG, "SLW: XSCOM error %lld reading"
-			      " reg 0x%x\n", rc, reg);
-			break;
-		}
-		prlog(PR_DEBUG, "SLW:  %5x = %016llx\n", reg, val);
-	}
-}
-
-/* This is called with the timer lock held, so there is no
- * issue with re-entrancy or concurrence
- */
-void slw_update_timer_expiry(uint64_t new_target)
-{
-	uint64_t count, gen, gen2, req, now = mftb();
-	int64_t rc;
-
-	if (!slw_has_timer || new_target == slw_timer_target)
-		return;
-
-	slw_timer_target = new_target;
-
-	/* Calculate how many increments from now, rounded up */
-	if (now < new_target)
-		count = (new_target - now + slw_timer_inc - 1) / slw_timer_inc;
-	else
-		count = 1;
-
-	/* Max counter is 24-bit */
-	if (count > 0xffffff)
-		count = 0xffffff;
-	/* Fabricate update request */
-	req = (1ull << 63) | (count << 32);
-
-	prlog(PR_TRACE, "SLW: TMR expiry: 0x%llx, req: %016llx\n", count, req);
-
-	do {
-		/* Grab generation and spin if odd */
-		_xscom_lock();
-		for (;;) {
-			rc = _xscom_read(slw_timer_chip, 0xE0006, &gen, false);
-			if (rc) {
-				prerror("SLW: Error %lld reading tmr gen "
-					" count\n", rc);
-				_xscom_unlock();
-				return;
-			}
-			if (!(gen & 1))
-				break;
-			if (tb_compare(now + msecs_to_tb(1), mftb()) == TB_ABEFOREB) {
-				/**
-				 * @fwts-label SLWTimerStuck
-				 * @fwts-advice The SLeep/Winkle Engine (SLW)
-				 * failed to increment the generation number
-				 * within our timeout period (it *should* have
-				 * done so within ~10us, not >1ms. OPAL uses
-				 * the SLW timer to schedule some operations,
-				 * but can fall back to the (much less frequent
-				 * OPAL poller, which although does not affect
-				 * functionality, runs *much* less frequently.
-				 * This could have the effect of slow I2C
-				 * operations (for example). It may also mean
-				 * that you *had* an increase in jitter, due
-				 * to slow interactions with SLW.
-				 * This error may also occur if the machine
-				 * is connected to via soft FSI.
-				 */
-				prerror("SLW: timer stuck, falling back to OPAL pollers. You will likely have slower I2C and may have experienced increased jitter.\n");
-				prlog(PR_DEBUG, "SLW: Stuck with odd generation !\n");
-				_xscom_unlock();
-				slw_has_timer = false;
-				slw_dump_timer_ffdc();
-				return;
-			}
-		}
-
-		rc = _xscom_write(slw_timer_chip, 0x5003A, req, false);
-		if (rc) {
-			prerror("SLW: Error %lld writing tmr request\n", rc);
-			_xscom_unlock();
-			return;
-		}
-
-		/* Re-check gen count */
-		rc = _xscom_read(slw_timer_chip, 0xE0006, &gen2, false);
-		if (rc) {
-			prerror("SLW: Error %lld re-reading tmr gen "
-				" count\n", rc);
-			_xscom_unlock();
-			return;
-		}
-		_xscom_unlock();
-	} while(gen != gen2);
-
-	/* Check if the timer is working. If at least 1ms has elapsed
-	 * since the last call to this function, check that the gen
-	 * count has changed
-	 */
-	if (tb_compare(slw_last_gen_stamp + msecs_to_tb(1), now)
-	    == TB_ABEFOREB) {
-		if (slw_last_gen == gen) {
-			prlog(PR_ERR,
-			      "SLW: Timer appears to not be running !\n");
-			slw_has_timer = false;
-			slw_dump_timer_ffdc();
-		}
-		slw_last_gen = gen;
-		slw_last_gen_stamp = mftb();
-	}
-
-	prlog(PR_TRACE, "SLW: gen: %llx\n", gen);
-}
-
-bool slw_timer_ok(void)
-{
-	return slw_has_timer;
-}
-
-static void slw_init_timer(void)
-{
-	struct dt_node *np;
-	int64_t rc;
-	uint32_t tick_us;
-
-	np = dt_find_compatible_node(dt_root, NULL, "ibm,power8-sbe-timer");
-	if (!np)
-		return;
-
-	slw_timer_chip = dt_get_chip_id(np);
-	tick_us = dt_prop_get_u32(np, "tick-time-us");
-	slw_timer_inc = usecs_to_tb(tick_us);
-	slw_timer_target = ~0ull;
-
-	rc = xscom_read(slw_timer_chip, 0xE0006, &slw_last_gen);
-	if (rc) {
-		prerror("SLW: Error %lld reading tmr gen count\n", rc);
-		return;
-	}
-	slw_last_gen_stamp = mftb();
-
-	prlog(PR_INFO, "SLW: Timer facility on chip %d, resolution %dus\n",
-	      slw_timer_chip, tick_us);
-	slw_has_timer = true;
-}
-
 void slw_init(void)
 {
 	struct proc_chip *chip;
@@ -1539,7 +1358,7 @@ void slw_init(void)
 	if (proc_gen == proc_gen_p8) {
 		for_each_chip(chip)
 			slw_init_chip(chip);
-		slw_init_timer();
+		sbe_p8_init_timer();
 	} else if (proc_gen == proc_gen_p9) {
 		for_each_chip(chip)
 			slw_init_chip_p9(chip);
diff --git a/include/sbe-p8.h b/include/sbe-p8.h
new file mode 100644
index 0000000..a30cb34
--- /dev/null
+++ b/include/sbe-p8.h
@@ -0,0 +1,29 @@
+/* Copyright 2013-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 __SBE_P8_H
+#define __SBE_P8_H
+
+/* P8 SBE update timer function */
+extern void sbe_p8_update_timer_expiry(uint64_t new_target);
+
+/* Is SBE timer available ? */
+extern bool sbe_p8_timer_ok(void);
+
+/* Initialize SBE timer */
+extern void sbe_p8_init_timer(void);
+
+#endif /* __SBE_P8_H */
diff --git a/include/skiboot.h b/include/skiboot.h
index 2b1f8a5..eb05951 100644
--- a/include/skiboot.h
+++ b/include/skiboot.h
@@ -284,12 +284,6 @@ extern void *create_dtb(const struct dt_node *root, bool exclusive);
 /* SLW reinit function for switching core settings */
 extern int64_t slw_reinit(uint64_t flags);
 
-/* SLW update timer function */
-extern void slw_update_timer_expiry(uint64_t new_target);
-
-/* Is SLW timer available ? */
-extern bool slw_timer_ok(void);
-
 /* Patch SPR in SLW image */
 extern int64_t opal_slw_set_reg(uint64_t cpu_pir, uint64_t sprn, uint64_t val);
 
