aboutsummaryrefslogtreecommitdiff
path: root/core
diff options
context:
space:
mode:
Diffstat (limited to 'core')
-rw-r--r--core/interrupts.c7
-rw-r--r--core/test/run-timer.c6
-rw-r--r--core/timer.c69
3 files changed, 55 insertions, 27 deletions
diff --git a/core/interrupts.c b/core/interrupts.c
index b91508f..32f43ef 100644
--- a/core/interrupts.c
+++ b/core/interrupts.c
@@ -296,9 +296,6 @@ static int64_t opal_handle_interrupt(uint32_t isn, uint64_t *outstanding_event_m
struct irq_source *is = irq_find_source(isn);
int64_t rc = OPAL_SUCCESS;
- /* We run the timers first */
- check_timers(true);
-
/* No source ? return */
if (!is || !is->ops->interrupt) {
rc = OPAL_PARAMETER;
@@ -308,6 +305,10 @@ static int64_t opal_handle_interrupt(uint32_t isn, uint64_t *outstanding_event_m
/* Run it */
is->ops->interrupt(is->data, isn);
+ /* Check timers if SLW timer isn't working */
+ if (!slw_timer_ok())
+ check_timers(true);
+
/* Update output events */
bail:
if (outstanding_event_mask)
diff --git a/core/test/run-timer.c b/core/test/run-timer.c
index 13b175e..e1a7cef 100644
--- a/core/test/run-timer.c
+++ b/core/test/run-timer.c
@@ -39,6 +39,12 @@ static void expiry(struct timer *t, void *data, uint64_t now)
count--;
}
+void slw_update_timer_expiry(uint64_t new_target)
+{
+ (void)new_target;
+ /* FIXME: do intersting SLW timer sim */
+}
+
int main(void)
{
unsigned int i;
diff --git a/core/timer.c b/core/timer.c
index d5a3477..e97dc07 100644
--- a/core/timer.c
+++ b/core/timer.c
@@ -12,6 +12,9 @@
#include <cpu.h>
#endif
+/* Heartbeat requested from Linux */
+#define HEARTBEAT_DEFAULT_MS 2000
+
static struct lock timer_lock = LOCK_UNLOCKED;
static LIST_HEAD(timer_list);
static LIST_HEAD(timer_poll_list);
@@ -72,28 +75,44 @@ void cancel_timer_async(struct timer *t)
unlock(&timer_lock);
}
-void schedule_timer_at(struct timer *t, uint64_t when)
+static void __schedule_timer_at(struct timer *t, uint64_t when)
{
struct timer *lt;
- lock(&timer_lock);
+ /* If the timer is already scheduled, take it out */
if (t->link.next)
__remove_timer(t);
+
+ /* Update target */
t->target = when;
+
if (when == TIMER_POLL) {
+ /* It's a poller, add it to the poller list */
t->gen = timer_poll_gen;
list_add_tail(&timer_poll_list, &t->link);
} else {
+ /* It's a real timer, add it in the right spot in the
+ * ordered timer list
+ */
list_for_each(&timer_list, lt, link) {
- if (when < lt->target) {
- list_add_before(&timer_list, &t->link,
- &lt->link);
- unlock(&timer_lock);
- return;
- }
+ if (when >= lt->target)
+ continue;
+ list_add_before(&timer_list, &t->link, &lt->link);
+ goto bail;
}
list_add_tail(&timer_list, &t->link);
}
+ bail:
+ /* 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);
+}
+
+void schedule_timer_at(struct timer *t, uint64_t when)
+{
+ lock(&timer_lock);
+ __schedule_timer_at(t, when);
unlock(&timer_lock);
}
@@ -134,18 +153,19 @@ static void __check_poll_timers(uint64_t now)
t = list_top(&timer_poll_list, struct timer, link);
/* Top timer has a different generation than current ? Must
- * be older
+ * be older, we are done.
*/
if (!t || t->gen == timer_poll_gen)
break;
- /* Top of list still running, we have to delay handling
- * it. For now just skip until the next poll, when we have
- * SLW interrupts, we'll probably want to trip another one
- * ASAP
+ /* Top of list still running, we have to delay handling it,
+ * let's reprogram the SLW with a small delay. We chose
+ * arbitrarily 1us.
*/
- if (t->running)
+ if (t->running) {
+ slw_update_timer_expiry(now + usecs_to_tb(1));
break;
+ }
/* Allright, first remove it and mark it running */
__remove_timer(t);
@@ -222,24 +242,25 @@ void check_timers(bool from_interrupt)
}
#ifndef __TEST__
+
void late_init_timers(void)
{
/* Add a property requesting the OS to call opal_poll_event() at
* a specified interval in order for us to run our background
- * low priority poller.
+ * low priority pollers.
*
- * When we have a working SLW based HW timer, we'll be able to
- * reduce this or even remove it, for now however, we want to be
- * called at least every couple of seconds on FSP based machines
- * and a bit faster on BMC based machines where the LPC and i2c
- * interrupts might not be functional.
+ * If we have an SLW 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
* faster than that.
*/
- if (fsp_present())
- dt_add_property_cells(opal_node, "ibm,heartbeat-ms", 2000);
- else
- dt_add_property_cells(opal_node, "ibm,heartbeat-ms", 250);
+ if (slw_timer_ok() || fsp_present()) {
+ dt_add_property_cells(opal_node, "ibm,heartbeat-ms",
+ HEARTBEAT_DEFAULT_MS);
+ } else {
+ dt_add_property_cells(opal_node, "ibm,heartbeat-ms",
+ HEARTBEAT_DEFAULT_MS / 10);
+ }
}
#endif