aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--core/interrupts.c3
-rw-r--r--core/test/run-timer.c8
-rw-r--r--core/timer.c17
-rw-r--r--hw/sbe-p9.c185
-rw-r--r--include/sbe-p9.h6
5 files changed, 213 insertions, 6 deletions
diff --git a/core/interrupts.c b/core/interrupts.c
index 4452511..5d7a68c 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 */
@@ -489,7 +490,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 (!p8_sbe_timer_ok())
+ if (!p8_sbe_timer_ok() && !p9_sbe_timer_ok())
check_timers(true);
/* Update output events */
diff --git a/core/test/run-timer.c b/core/test/run-timer.c
index 986af28..159e007 100644
--- a/core/test/run-timer.c
+++ b/core/test/run-timer.c
@@ -4,12 +4,15 @@
#define __TEST__
#include <timer.h>
+#include <skiboot.h>
#define mftb() (stamp)
#define sync()
#define smt_lowest()
#define smt_medium()
+enum proc_gen proc_gen = proc_gen_p9;
+
static uint64_t stamp, last;
struct lock;
static inline void lock_caller(struct lock *l, const char *caller)
@@ -53,6 +56,11 @@ void p8_sbe_update_timer_expiry(uint64_t new_target)
/* FIXME: do intersting SLW timer sim */
}
+void p9_sbe_update_timer_expiry(uint64_t new_target)
+{
+ (void)new_target;
+}
+
int main(void)
{
unsigned int i;
diff --git a/core/timer.c b/core/timer.c
index 21f62a4..8eefb74 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)
- p8_sbe_update_timer_expiry(lt->target);
+ if (lt) {
+ if (proc_gen < proc_gen_p9)
+ p8_sbe_update_timer_expiry(lt->target);
+ else
+ p9_sbe_update_timer_expiry(lt->target);
+ }
}
void schedule_timer_at(struct timer *t, uint64_t when)
@@ -167,7 +172,11 @@ static void __check_poll_timers(uint64_t now)
* arbitrarily 1us.
*/
if (t->running) {
- p8_sbe_update_timer_expiry(now + usecs_to_tb(1));
+ if (proc_gen < proc_gen_p9)
+ p8_sbe_update_timer_expiry(now + usecs_to_tb(1));
+ else
+ p9_sbe_update_timer_expiry(now + usecs_to_tb(1));
+
break;
}
@@ -266,6 +275,8 @@ void late_init_timers(void)
*/
if (platform.heartbeat_time) {
heartbeat = platform.heartbeat_time();
+ } else if (p9_sbe_timer_ok()) {
+ heartbeat = HEARTBEAT_DEFAULT_MS * 10;
} else if (p8_sbe_timer_ok() || fsp_present()) {
heartbeat = HEARTBEAT_DEFAULT_MS * 10;
}
diff --git a/hw/sbe-p9.c b/hw/sbe-p9.c
index 3519fcd..d52ada1 100644
--- a/hw/sbe-p9.c
+++ b/hw/sbe-p9.c
@@ -53,6 +53,7 @@
#include <sbe-p9.h>
#include <skiboot.h>
#include <timebase.h>
+#include <timer.h>
#include <trace.h>
#include <xscom.h>
@@ -80,11 +81,34 @@ struct p9_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;
+static bool has_new_target = false;
+
+/* Inflight and next timer in TB */
+static uint64_t sbe_last_gen_stamp;
+static uint64_t sbe_timer_target;
+
+/* Timer lock */
+struct lock sbe_timer_lock;
+
+/*
+ * Minimum timeout value for P9 is 500 microseconds. After that
+ * SBE timer can handle granularity of 1 microsecond.
+ */
+#define SBE_TIMER_DEFAULT_US 500
+static uint64_t sbe_timer_def_tb;
+
+/* Timer control message */
+static struct p9_sbe_msg *timer_ctrl_msg;
+
#define SBE_STATUS_PRI_SHIFT 0x30
#define SBE_STATUS_SEC_SHIFT 0x20
/* Forward declaration */
static void p9_sbe_timeout_poll_one(struct p9_sbe *sbe);
+static void p9_sbe_timer_schedule(void);
/* bit 0-15 : Primary status code */
static inline u16 p9_sbe_get_primary_rc(struct p9_sbe_msg *resp)
@@ -507,6 +531,19 @@ static int p9_sbe_clear_interrupt(struct p9_sbe *sbe, u64 bits)
}
/* WARNING: This will drop sbe->lock */
+static void p9_sbe_timer_response(struct p9_sbe *sbe)
+{
+ if (sbe->chip_id != sbe_default_chip_id)
+ return;
+
+ sbe_timer_in_progress = false;
+ /* Drop lock and call timers */
+ unlock(&sbe->lock);
+ check_timers(true);
+ lock(&sbe->lock);
+}
+
+/* WARNING: This will drop sbe->lock */
static void __p9_sbe_interrupt(struct p9_sbe *sbe)
{
bool has_response;
@@ -590,6 +627,15 @@ again:
goto again;
}
+ /* Timer expired */
+ if (data & SBE_HOST_TIMER_EXPIRY) {
+ rc = p9_sbe_clear_interrupt(sbe, SBE_HOST_TIMER_EXPIRY);
+ if (rc)
+ return;
+ p9_sbe_timer_response(sbe);
+ goto again;
+ }
+
/* Unhandled bits */
val = data & ~(SBE_HOST_RESPONSE_MASK);
if (val) {
@@ -618,12 +664,42 @@ void p9_sbe_interrupt(uint32_t chip_id)
unlock(&sbe->lock);
}
+/*
+ * Check if the timer is working. If at least 10ms elapsed since
+ * last scheduled timer expiry.
+ */
+static void p9_sbe_timer_poll(struct p9_sbe *sbe)
+{
+ if (sbe->chip_id != sbe_default_chip_id)
+ return;
+
+ if (!sbe_has_timer || !sbe_timer_in_progress)
+ return;
+
+ if (tb_compare(mftb(), sbe_last_gen_stamp + msecs_to_tb(10))
+ != 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");
+ p9_sbe_reg_dump(sbe->chip_id);
+ sbe_has_timer = false;
+ sbe_timer_in_progress = false;
+}
+
static void p9_sbe_timeout_poll_one(struct p9_sbe *sbe)
{
struct p9_sbe_msg *msg;
- if (list_empty_nocheck(&sbe->msg_list))
- return;
+ if (sbe->chip_id == sbe_default_chip_id) {
+ if (list_empty_nocheck(&sbe->msg_list) &&
+ !sbe_timer_in_progress)
+ return;
+ } else {
+ if (list_empty_nocheck(&sbe->msg_list))
+ return;
+ }
lock(&sbe->lock);
@@ -634,6 +710,7 @@ static void p9_sbe_timeout_poll_one(struct p9_sbe *sbe)
* Hence check for SBE response.
*/
__p9_sbe_interrupt(sbe);
+ p9_sbe_timer_poll(sbe);
if (list_empty(&sbe->msg_list))
goto out;
@@ -683,6 +760,107 @@ static void p9_sbe_timeout_poll(void *user_data __unused)
}
}
+static void p9_sbe_timer_resp(struct p9_sbe_msg *msg)
+{
+ if (msg->state != sbe_msg_done) {
+ prlog(PR_DEBUG, "Failed to schedule timer [chip id %x]\n",
+ sbe_default_chip_id);
+ } else {
+ /* Update last scheduled timer value */
+ sbe_last_gen_stamp = mftb() +
+ usecs_to_tb(timer_ctrl_msg->reg[1]);
+ sbe_timer_in_progress = true;
+ }
+
+ if (!has_new_target)
+ return;
+
+ lock(&sbe_timer_lock);
+ if (has_new_target) {
+ has_new_target = false;
+ p9_sbe_timer_schedule();
+ }
+ unlock(&sbe_timer_lock);
+}
+
+static void p9_sbe_timer_schedule(void)
+{
+ int rc;
+ u32 tick_us = SBE_TIMER_DEFAULT_US;
+ u64 tb_cnt, now = mftb();
+
+ if (sbe_timer_in_progress) {
+ if (now >= sbe_last_gen_stamp)
+ return;
+
+ /* Remaining time of inflight timer <= sbe_timer_def_tb */
+ if ((sbe_last_gen_stamp - now) <= sbe_timer_def_tb)
+ return;
+ }
+
+ if (now < sbe_timer_target) {
+ /* Calculate how many microseconds from now, rounded up */
+ if ((sbe_timer_target - now) > sbe_timer_def_tb) {
+ tb_cnt = sbe_timer_target - now + usecs_to_tb(1) - 1;
+ tick_us = tb_to_usecs(tb_cnt);
+ }
+ }
+
+ /* Clear sequence number. p9_sbe_queue_msg will add new sequene ID */
+ timer_ctrl_msg->reg[0] &= ~(PPC_BITMASK(32, 47));
+ /* Update timeout value */
+ timer_ctrl_msg->reg[1] = tick_us;
+ rc = p9_sbe_queue_msg(sbe_default_chip_id, timer_ctrl_msg,
+ p9_sbe_timer_resp);
+ if (rc != OPAL_SUCCESS) {
+ prlog(PR_ERR, "Failed to start timer [chip id = %x]\n",
+ sbe_default_chip_id);
+ return;
+ }
+}
+
+/*
+ * This is called with the timer lock held, so there is no
+ * issue with re-entrancy or concurrence
+ */
+void p9_sbe_update_timer_expiry(uint64_t new_target)
+{
+ if (!sbe_has_timer || new_target == sbe_timer_target)
+ return;
+
+ lock(&sbe_timer_lock);
+ /* Timer message is in flight. Record new timer and schedule later */
+ if (p9_sbe_msg_busy(timer_ctrl_msg) || has_new_target) {
+ if (new_target < sbe_timer_target) {
+ sbe_timer_target = new_target;
+ has_new_target = true;
+ }
+ } else {
+ sbe_timer_target = new_target;
+ p9_sbe_timer_schedule();
+ }
+ unlock(&sbe_timer_lock);
+}
+
+/* Initialize SBE timer */
+static void p9_sbe_timer_init(void)
+{
+ timer_ctrl_msg = p9_sbe_mkmsg(SBE_CMD_CONTROL_TIMER,
+ CONTROL_TIMER_START, 0, 0, 0);
+ assert(timer_ctrl_msg);
+ init_lock(&sbe_timer_lock);
+ sbe_has_timer = true;
+ sbe_timer_target = mftb();
+ sbe_last_gen_stamp = ~0ull;
+ sbe_timer_def_tb = usecs_to_tb(SBE_TIMER_DEFAULT_US);
+ prlog(PR_INFO, "Timer facility on chip %x\n", sbe_default_chip_id);
+}
+
+bool p9_sbe_timer_ok(void)
+{
+ return sbe_has_timer;
+}
+
void p9_sbe_init(void)
{
struct dt_node *xn;
@@ -716,6 +894,9 @@ void p9_sbe_init(void)
return;
}
+ /* Initiate SBE timer */
+ p9_sbe_timer_init();
+
/* Initiate SBE timeout poller */
opal_add_poller(p9_sbe_timeout_poll, NULL);
}
diff --git a/include/sbe-p9.h b/include/sbe-p9.h
index 329afff..4b839d8 100644
--- a/include/sbe-p9.h
+++ b/include/sbe-p9.h
@@ -231,4 +231,10 @@ extern void p9_sbe_init(void);
/* SBE interrupt */
extern void p9_sbe_interrupt(uint32_t chip_id);
+/* Is SBE timer available ? */
+extern bool p9_sbe_timer_ok(void);
+
+/* Update SBE timer expiry */
+extern void p9_sbe_update_timer_expiry(uint64_t new_target);
+
#endif /* __SBE_P9_H */