diff --git a/core/interrupts.c b/core/interrupts.c
index 439e1fb..73833d2 100644
--- a/core/interrupts.c
+++ b/core/interrupts.c
@@ -26,6 +26,7 @@
 #include <ccan/str/str.h>
 #include <timer.h>
 #include <sbe-p8.h>
+#include <sbe-p9.h>
 
 /* ICP registers */
 #define ICP_XIRR		0x4	/* 32-bit access */
@@ -470,7 +471,7 @@ static int64_t opal_handle_interrupt(uint32_t isn, __be64 *outstanding_event_mas
 	is->ops->interrupt(is, isn);
 
 	/* Check timers if SBE timer isn't working */
-	if (!sbe_p8_timer_ok())
+	if (!sbe_p8_timer_ok() && !sbe_timer_ok())
 		check_timers(true);
 
 	/* Update output events */
diff --git a/core/timer.c b/core/timer.c
index cb4d07b..46b1894 100644
--- a/core/timer.c
+++ b/core/timer.c
@@ -5,6 +5,7 @@
 #include <device.h>
 #include <opal.h>
 #include <sbe-p8.h>
+#include <sbe-p9.h>
 
 #ifdef __TEST__
 #define this_cpu()	((void *)-1)
@@ -109,8 +110,12 @@ static void __schedule_timer_at(struct timer *t, uint64_t when)
  bail:
 	/* Pick up the next timer and upddate the SBE HW timer */
 	lt = list_top(&timer_list, struct timer, link);
-	if (lt)
-		sbe_p8_update_timer_expiry(lt->target);
+	if (lt) {
+		if (proc_gen < proc_gen_p9)
+			sbe_p8_update_timer_expiry(lt->target);
+		else
+			sbe_update_timer_expiry(lt->target);
+	}
 }
 
 void schedule_timer_at(struct timer *t, uint64_t when)
@@ -167,7 +172,10 @@ static void __check_poll_timers(uint64_t now)
 		 * arbitrarily 1us.
 		 */
 		if (t->running) {
-			sbe_p8_update_timer_expiry(now + usecs_to_tb(1));
+			if (proc_gen < proc_gen_p9)
+				sbe_p8_update_timer_expiry(now + usecs_to_tb(1));
+			else
+				sbe_update_timer_expiry(now + usecs_to_tb(1));
 			break;
 		}
 
@@ -265,6 +273,8 @@ void late_init_timers(void)
 	 */
 	if (platform.heartbeat_time) {
 		heartbeat = platform.heartbeat_time();
+	} else if (sbe_timer_ok()) {
+		heartbeat = HEARTBEAT_DEFAULT_MS * 10;
 	} else if (sbe_p8_timer_ok() || fsp_present()) {
 		heartbeat = HEARTBEAT_DEFAULT_MS * 10;
 	}
diff --git a/hw/sbe-p9.c b/hw/sbe-p9.c
index 3af1094..4ae4d7b 100644
--- a/hw/sbe-p9.c
+++ b/hw/sbe-p9.c
@@ -23,6 +23,7 @@
 #include <sbe-p9.h>
 #include <skiboot.h>
 #include <timebase.h>
+#include <timer.h>
 #include <trace.h>
 #include <xscom.h>
 
@@ -48,6 +49,21 @@ struct sbe {
 /* Default SBE chip ID */
 static int sbe_default_chip_id = -1;
 
+/* Is SBE timer running? */
+static bool sbe_has_timer = false;
+static bool sbe_timer_in_progress = false;
+
+/* Inflight and next timer in TB */
+static uint64_t sbe_last_gen_stamp;
+static uint64_t sbe_timer_target;
+
+/* Default timeout value */
+#define SBE_TIMER_DEFAULT_MS	10
+static uint64_t sbe_timer_def_tb;
+
+/* Timer control message */
+static struct sbe_msg *timer_ctrl_msg;
+
 #define SBE_STATUS_PRI_SHIFT	0x30
 #define SBE_STATUS_SEC_SHIFT	0x20
 #define SBE_FFDC_PRESENT	PPC_BIT(1)
@@ -529,6 +545,12 @@ void sbe_interrupt(uint32_t chip_id)
 		prd_sbe_passthrough(chip_id);
 	}
 
+	/* Timer expired */
+	if (chip_id == sbe_default_chip_id && (data & SBE_HOST_TIMER_EXPIRY)) {
+		sbe_timer_in_progress = false;
+		check_timers(true);
+	}
+
 	/* Handle SBE response */
 	if (msg_complete) {
 		lock(&sbe->lock);
@@ -594,6 +616,124 @@ static void sbe_timeout_poll(void *user_data __unused)
 		sbe_poke_queue(sbe);
 		unlock(&sbe->lock);
 	}
+
+	/*
+	 * Check if the timer is working. If at least SBE_TIMER_DEFAULT_MS
+	 * milliseconds elapsed since last scheduled timer expiry.
+	 */
+	if (sbe_has_timer && sbe_timer_in_progress) {
+		if (tb_compare(mftb(), sbe_last_gen_stamp + sbe_timer_def_tb)
+		    != TB_AAFTERB)
+			return;
+
+		/*
+		 * In some cases there will be a delay in calling OPAL interrupt
+		 * handler routine (opal_handle_interrupt). In such cases its
+		 * possible that SBE has responded, but OPAL didn't act on that.
+		 * Hence check for SBE response before disabling timer.
+		 */
+		sbe_interrupt(sbe_default_chip_id);
+
+		if (!sbe_timer_in_progress)
+			return;
+
+		if (tb_compare(mftb(), sbe_last_gen_stamp + sbe_timer_def_tb)
+		    != TB_AAFTERB)
+			return;
+
+		prlog(PR_ERR, "Timer stuck, falling back to OPAL pollers.\n");
+		prlog(PR_ERR, "You will likely have slower I2C and may have "
+		      "experienced increased jitter.\n");
+		sbe_reg_dump(sbe_default_chip_id);
+		sbe_has_timer = false;
+	}
+}
+
+static void sbe_timer_start(void)
+{
+	int rc;
+	u32 ms = SBE_TIMER_DEFAULT_MS;
+	u64 tb_cnt, now = mftb();
+
+	if (!sbe_has_timer)
+		return;
+
+	if (sbe_timer_in_progress) {
+		if (sbe_timer_target < now ||
+		    sbe_timer_target > sbe_last_gen_stamp)
+			return;
+
+		/* Update timer control message */
+		timer_ctrl_msg->reg[0] &= 0xffff;
+		timer_ctrl_msg->reg[0] |= ((u64)CONTROL_TIMER_STOP << 32);
+
+		rc = sbe_sync_msg(sbe_default_chip_id, timer_ctrl_msg, false);
+		if (rc != OPAL_SUCCESS) {
+			/*
+			 * Lets hope SBE will respond whenever original
+			 * timer expires.
+			 */
+			prlog(PR_ERR, "Failed to stop timer [chip id = %x]\n",
+			      sbe_default_chip_id);
+			return;
+		}
+	}
+
+	now = mftb();
+	if (now < sbe_timer_target) {
+		/* Calculate how many ms from now, rounded up */
+		tb_cnt = sbe_timer_target - now + msecs_to_tb(1) - 1;
+		ms = tb_to_msecs(tb_cnt);
+	}
+
+	/* Update timer control message */
+	timer_ctrl_msg->reg[0] &= 0xffff;
+	timer_ctrl_msg->reg[0] |= ((u64)CONTROL_TIMER_START << 32);
+	timer_ctrl_msg->reg[1] = ms;
+
+	rc = sbe_sync_msg(sbe_default_chip_id, timer_ctrl_msg, false);
+	if (rc != OPAL_SUCCESS) {
+		prlog(PR_ERR, "Failed to start timer [chip id = %x]\n",
+		      sbe_default_chip_id);
+		return;
+	}
+
+	/* Update last scheduled timer value */
+	sbe_last_gen_stamp = mftb() + msecs_to_tb(ms);
+	sbe_timer_in_progress = true;
+}
+
+/*
+ * This is called with the timer lock held, so there is no
+ * issue with re-entrancy or concurrence
+ */
+void sbe_update_timer_expiry(uint64_t new_target)
+{
+	if (!sbe_has_timer || new_target == sbe_timer_target)
+		return;
+
+	sbe_timer_target = new_target;
+	sbe_timer_start();
+}
+
+/* Initialize SBE timer */
+static void sbe_timer_init(void)
+{
+	/* Do not request ack/response for timer message */
+	timer_ctrl_msg = sbe_mkmsg(SBE_CMD_CONTROL_TIMER,
+				   CONTROL_TIMER_START, 0, 0, 0);
+	assert(timer_ctrl_msg);
+
+	sbe_has_timer = true;
+	sbe_timer_target = mftb();
+	sbe_last_gen_stamp = ~0ull;
+	sbe_timer_def_tb = msecs_to_tb(SBE_TIMER_DEFAULT_MS);
+	prlog(PR_INFO, "Timer facility on chip %x\n", sbe_default_chip_id);
+}
+
+bool sbe_timer_ok(void)
+{
+	return sbe_has_timer;
 }
 
 void sbe_init(void)
@@ -630,6 +770,9 @@ void sbe_init(void)
 		return;
 	}
 
+	/* Initiate SBE timer */
+	sbe_timer_init();
+
 	/* Initiate SBE timeout poller */
 	opal_add_poller(sbe_timeout_poll, NULL);
 }
diff --git a/include/sbe-p9.h b/include/sbe-p9.h
index c5616ba..6ba2759 100644
--- a/include/sbe-p9.h
+++ b/include/sbe-p9.h
@@ -227,4 +227,10 @@ extern void sbe_init(void);
 /* SBE interrupt */
 extern void sbe_interrupt(uint32_t chip_id);
 
+/* Is SBE timer available ? */
+extern bool sbe_timer_ok(void);
+
+/* Update SBE timer expiry */
+extern void sbe_update_timer_expiry(uint64_t new_target);
+
 #endif	/* __SBE_P9_H */
