summaryrefslogtreecommitdiff
path: root/common/timer.c
diff options
context:
space:
mode:
authorVincent Palatin <vpalatin@chromium.org>2013-12-04 17:15:12 -0800
committerchrome-internal-fetch <chrome-internal-fetch@google.com>2013-12-05 22:30:58 +0000
commit1762de9d19d2671cc56e5a479055379a346030d3 (patch)
tree21f6ec886e9abcf9638c8a88767b8299d7eddfe7 /common/timer.c
parentb45f3b9f348c550a7389973d916b8b7cb6b25a88 (diff)
downloadchrome-ec-1762de9d19d2671cc56e5a479055379a346030d3.tar.gz
extract common core code
Move the non-core dependent code out of core/$(CORE) directory to common/ directory. Put all panic printing code in common/panic_output.c Put timer management code in common/timer.c Signed-off-by: Vincent Palatin <vpalatin@chromium.org> BRANCH=none BUG=chrome-os-partner:23574 TEST=./util/make_all.sh use "crash divzero" and "panicinfo" on Link. Change-Id: Ia4e1ebc74cd53da55fe24f69e96f39f512b9336d Reviewed-on: https://chromium-review.googlesource.com/178871 Reviewed-by: Randall Spangler <rspangler@chromium.org> Tested-by: Vincent Palatin <vpalatin@chromium.org> Reviewed-by: Jeremy Thorpe <jeremyt@chromium.org> Commit-Queue: Vincent Palatin <vpalatin@chromium.org>
Diffstat (limited to 'common/timer.c')
-rw-r--r--common/timer.c278
1 files changed, 278 insertions, 0 deletions
diff --git a/common/timer.c b/common/timer.c
new file mode 100644
index 0000000000..0256d673bc
--- /dev/null
+++ b/common/timer.c
@@ -0,0 +1,278 @@
+/* Copyright (c) 2012 The Chromium OS Authors. All rights reserved.
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file.
+ */
+
+/* Timer module for Chrome EC operating system */
+
+#include "atomic.h"
+#include "console.h"
+#include "hooks.h"
+#include "hwtimer.h"
+#include "system.h"
+#include "uart.h"
+#include "util.h"
+#include "task.h"
+#include "timer.h"
+
+#define TIMER_SYSJUMP_TAG 0x4d54 /* "TM" */
+
+/* High word of the 64-bit timestamp counter */
+static volatile uint32_t clksrc_high;
+
+/* Bitmap of currently running timers */
+static uint32_t timer_running = 0;
+
+/* Deadlines of all timers */
+static timestamp_t timer_deadline[TASK_ID_COUNT];
+static uint32_t next_deadline = 0xffffffff;
+
+/* Hardware timer routine IRQ number */
+static int timer_irq;
+
+static void expire_timer(task_id_t tskid)
+{
+ /* we are done with this timer */
+ atomic_clear(&timer_running, 1 << tskid);
+ /* wake up the taks waiting for this timer */
+ task_set_event(tskid, TASK_EVENT_TIMER, 0);
+}
+
+int timestamp_expired(timestamp_t deadline, const timestamp_t *now)
+{
+ timestamp_t now_val;
+
+ if (!now) {
+ now_val = get_time();
+ now = &now_val;
+ }
+
+ return ((int64_t)(now->val - deadline.val) >= 0);
+}
+
+void process_timers(int overflow)
+{
+ uint32_t check_timer, running_t0;
+ timestamp_t next;
+ timestamp_t now;
+
+ if (overflow)
+ clksrc_high++;
+
+ do {
+ next.val = -1ull;
+ now = get_time();
+ do {
+ /* read atomically the current state of timer running */
+ check_timer = running_t0 = timer_running;
+ while (check_timer) {
+ int tskid = 31 - __builtin_clz(check_timer);
+
+ /* timer has expired ? */
+ if (timer_deadline[tskid].val < now.val)
+ expire_timer(tskid);
+ else if ((timer_deadline[tskid].le.hi ==
+ now.le.hi) &&
+ (timer_deadline[tskid].le.lo <
+ next.le.lo))
+ next.val = timer_deadline[tskid].val;
+
+ check_timer &= ~(1 << tskid);
+ }
+ /* if there is a new timer, let's retry */
+ } while (timer_running & ~running_t0);
+
+ if (next.le.hi == 0xffffffff) {
+ /* no deadline to set */
+ __hw_clock_event_clear();
+ next_deadline = 0xffffffff;
+ return;
+ }
+
+ __hw_clock_event_set(next.le.lo);
+ next_deadline = next.le.lo;
+ } while (next.val <= get_time().val);
+}
+
+void udelay(unsigned us)
+{
+ unsigned t0 = __hw_clock_source_read();
+
+ /*
+ * udelay() may be called with interrupts disabled, so we can't rely on
+ * process_timers() updating the top 32 bits. So handle wraparound
+ * ourselves rather than calling get_time() and comparing with a
+ * deadline.
+ *
+ * This may fail for delays close to 2^32 us (~4000 sec), because the
+ * subtraction below can overflow. That's acceptable, because the
+ * watchdog timer would have tripped long before that anyway.
+ */
+ while (__hw_clock_source_read() - t0 < us)
+ ;
+}
+
+int timer_arm(timestamp_t tstamp, task_id_t tskid)
+{
+ ASSERT(tskid < TASK_ID_COUNT);
+
+ if (timer_running & (1<<tskid))
+ return EC_ERROR_BUSY;
+
+ timer_deadline[tskid] = tstamp;
+ atomic_or(&timer_running, 1<<tskid);
+
+ /* Modify the next event if needed */
+ if ((tstamp.le.hi < clksrc_high) ||
+ ((tstamp.le.hi == clksrc_high) && (tstamp.le.lo <= next_deadline)))
+ task_trigger_irq(timer_irq);
+
+ return EC_SUCCESS;
+}
+
+void timer_cancel(task_id_t tskid)
+{
+ ASSERT(tskid < TASK_ID_COUNT);
+
+ atomic_clear(&timer_running, 1 << tskid);
+ /*
+ * Don't need to cancel the interrupt: it would be slow, just do it on
+ * the next IT
+ */
+}
+
+void usleep(unsigned us)
+{
+ uint32_t evt = 0;
+
+ /* If task scheduling has not started, just delay */
+ if (!task_start_called()) {
+ udelay(us);
+ return;
+ }
+
+ ASSERT(us);
+ do {
+ evt |= task_wait_event(us);
+ } while (!(evt & TASK_EVENT_TIMER));
+
+ /* Re-queue other events which happened in the meanwhile */
+ if (evt)
+ atomic_or(task_get_event_bitmap(task_get_current()),
+ evt & ~TASK_EVENT_TIMER);
+}
+
+timestamp_t get_time(void)
+{
+ timestamp_t ts;
+ ts.le.hi = clksrc_high;
+ ts.le.lo = __hw_clock_source_read();
+ if (ts.le.hi != clksrc_high) {
+ ts.le.hi = clksrc_high;
+ ts.le.lo = __hw_clock_source_read();
+ }
+ return ts;
+}
+
+void force_time(timestamp_t ts)
+{
+ clksrc_high = ts.le.hi;
+ __hw_clock_source_set(ts.le.lo);
+ /* some timers might be already expired : process them */
+ task_trigger_irq(timer_irq);
+}
+
+void timer_print_info(void)
+{
+ uint64_t t = get_time().val;
+ uint64_t deadline = (uint64_t)clksrc_high << 32 |
+ __hw_clock_event_get();
+ int tskid;
+
+ ccprintf("Time: 0x%016lx us\n"
+ "Deadline: 0x%016lx -> %11.6ld s from now\n"
+ "Active timers:\n",
+ t, deadline, deadline - t);
+ cflush();
+
+ for (tskid = 0; tskid < TASK_ID_COUNT; tskid++) {
+ if (timer_running & (1<<tskid)) {
+ ccprintf(" Tsk %2d 0x%016lx -> %11.6ld\n", tskid,
+ timer_deadline[tskid].val,
+ timer_deadline[tskid].val - t);
+ cflush();
+ }
+ }
+}
+
+void timer_init(void)
+{
+ const timestamp_t *ts;
+ int size, version;
+
+ BUILD_ASSERT(TASK_ID_COUNT < sizeof(timer_running) * 8);
+
+ /* Restore time from before sysjump */
+ ts = (const timestamp_t *)system_get_jump_tag(TIMER_SYSJUMP_TAG,
+ &version, &size);
+ if (ts && version == 1 && size == sizeof(timestamp_t)) {
+ clksrc_high = ts->le.hi;
+ timer_irq = __hw_clock_source_init(ts->le.lo);
+ } else {
+ clksrc_high = 0;
+ timer_irq = __hw_clock_source_init(0);
+ }
+}
+
+/* Preserve time across a sysjump */
+static void timer_sysjump(void)
+{
+ timestamp_t ts = get_time();
+
+ system_add_jump_tag(TIMER_SYSJUMP_TAG, 1, sizeof(ts), &ts);
+}
+DECLARE_HOOK(HOOK_SYSJUMP, timer_sysjump, HOOK_PRIO_DEFAULT);
+
+static int command_wait(int argc, char **argv)
+{
+ char *e;
+ int i;
+
+ if (argc < 2)
+ return EC_ERROR_PARAM_COUNT;
+
+ i = strtoi(argv[1], &e, 0);
+ if (*e)
+ return EC_ERROR_PARAM1;
+
+ udelay(i * 1000);
+
+ return EC_SUCCESS;
+}
+DECLARE_CONSOLE_COMMAND(waitms, command_wait,
+ "msec",
+ "Busy-wait for msec",
+ NULL);
+
+static int command_get_time(int argc, char **argv)
+{
+ timestamp_t ts = get_time();
+ ccprintf("Time: 0x%016lx = %.6ld s\n", ts.val, ts.val);
+
+ return EC_SUCCESS;
+}
+DECLARE_CONSOLE_COMMAND(gettime, command_get_time,
+ NULL,
+ "Print current time",
+ NULL);
+
+int command_timer_info(int argc, char **argv)
+{
+ timer_print_info();
+
+ return EC_SUCCESS;
+}
+DECLARE_CONSOLE_COMMAND(timerinfo, command_timer_info,
+ NULL,
+ "Print timer info",
+ NULL);