summaryrefslogtreecommitdiff
path: root/common
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
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')
-rw-r--r--common/build.mk2
-rw-r--r--common/panic_output.c169
-rw-r--r--common/timer.c278
3 files changed, 449 insertions, 0 deletions
diff --git a/common/build.mk b/common/build.mk
index dc64e4bcd9..18da04ae78 100644
--- a/common/build.mk
+++ b/common/build.mk
@@ -22,6 +22,8 @@ common-$(CONFIG_CHARGER)+=charge_state.o charger.o
# TODO(crosbug.com/p/23815): This is really the charge state machine
# for ARM, not the charger driver for the tps65090. Rename.
common-$(CONFIG_CHARGER_TPS65090)+=pmu_tps65090_charger.o
+common-$(CONFIG_COMMON_PANIC_OUTPUT)+=panic_output.o
+common-$(CONFIG_COMMON_TIMER)+=timer.o
common-$(CONFIG_PMU_POWERINFO)+=pmu_tps65090_powerinfo.o
common-$(CONFIG_PMU_TPS65090)+=pmu_tps65090.o
common-$(CONFIG_EOPTION)+=eoption.o
diff --git a/common/panic_output.c b/common/panic_output.c
new file mode 100644
index 0000000000..f77277ab33
--- /dev/null
+++ b/common/panic_output.c
@@ -0,0 +1,169 @@
+/* Copyright (c) 2013 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.
+ */
+
+#include "common.h"
+#include "console.h"
+#include "cpu.h"
+#include "host_command.h"
+#include "panic.h"
+#include "printf.h"
+#include "system.h"
+#include "task.h"
+#include "timer.h"
+#include "uart.h"
+#include "util.h"
+
+/* Panic data goes at the end of RAM. */
+static struct panic_data * const pdata_ptr = PANIC_DATA_PTR;
+
+/**
+ * Add a character directly to the UART buffer.
+ *
+ * @param context Context; ignored.
+ * @param c Character to write.
+ * @return 0 if the character was transmitted, 1 if it was dropped.
+ */
+static int panic_txchar(void *context, int c)
+{
+ if (c == '\n')
+ panic_txchar(context, '\r');
+
+ /* Wait for space in transmit FIFO */
+ while (!uart_tx_ready())
+ ;
+
+ /* Write the character directly to the transmit FIFO */
+ uart_write_char(c);
+
+ return 0;
+}
+
+void panic_puts(const char *outstr)
+{
+ /* Flush the output buffer */
+ uart_flush_output();
+
+ /* Put all characters in the output buffer */
+ while (*outstr)
+ panic_txchar(NULL, *outstr++);
+
+ /* Flush the transmit FIFO */
+ uart_tx_flush();
+}
+
+void panic_printf(const char *format, ...)
+{
+ va_list args;
+
+ /* Flush the output buffer */
+ uart_flush_output();
+
+ va_start(args, format);
+ vfnprintf(panic_txchar, NULL, format, args);
+ va_end(args);
+
+ /* Flush the transmit FIFO */
+ uart_tx_flush();
+}
+
+/**
+ * Display a message and reboot
+ */
+void panic_reboot(void)
+{
+ panic_puts("\n\nRebooting...\n");
+ system_reset(0);
+}
+
+#ifdef CONFIG_DEBUG_ASSERT_REBOOTS
+void panic_assert_fail(const char *msg, const char *func, const char *fname,
+ int linenum)
+{
+ panic_printf("\nASSERTION FAILURE '%s' in %s() at %s:%d\n",
+ msg, func, fname, linenum);
+
+ panic_reboot();
+}
+#endif
+
+void panic(const char *msg)
+{
+ panic_printf("\n** PANIC: %s\n", msg);
+ panic_reboot();
+}
+
+struct panic_data *panic_get_data(void)
+{
+ return pdata_ptr->magic == PANIC_DATA_MAGIC ? pdata_ptr : NULL;
+}
+
+/*****************************************************************************/
+/* Console commands */
+
+static int command_crash(int argc, char **argv)
+{
+ if (argc < 2)
+ return EC_ERROR_PARAM1;
+
+ if (!strcasecmp(argv[1], "divzero")) {
+ int a = 1, b = 0;
+
+ cflush();
+ ccprintf("%08x", a / b);
+ } else if (!strcasecmp(argv[1], "unaligned")) {
+ cflush();
+ ccprintf("%08x", *(int *)0xcdef);
+ } else {
+ return EC_ERROR_PARAM1;
+ }
+
+ /* Everything crashes, so shouldn't get back here */
+ return EC_ERROR_UNKNOWN;
+}
+DECLARE_CONSOLE_COMMAND(crash, command_crash,
+ "[divzero | unaligned]",
+ "Crash the system (for testing)",
+ NULL);
+
+static int command_panicinfo(int argc, char **argv)
+{
+ if (pdata_ptr->magic == PANIC_DATA_MAGIC) {
+ ccprintf("Saved panic data:%s\n",
+ (pdata_ptr->flags & PANIC_DATA_FLAG_OLD_CONSOLE ?
+ "" : " (NEW)"));
+
+ panic_data_print(pdata_ptr);
+
+ /* Data has now been printed */
+ pdata_ptr->flags |= PANIC_DATA_FLAG_OLD_CONSOLE;
+ } else {
+ ccprintf("No saved panic data available.\n");
+ }
+ return EC_SUCCESS;
+}
+DECLARE_CONSOLE_COMMAND(panicinfo, command_panicinfo,
+ NULL,
+ "Print info from a previous panic",
+ NULL);
+
+/*****************************************************************************/
+/* Host commands */
+
+int host_command_panic_info(struct host_cmd_handler_args *args)
+{
+ if (pdata_ptr->magic == PANIC_DATA_MAGIC) {
+ ASSERT(pdata_ptr->struct_size <= args->response_max);
+ memcpy(args->response, pdata_ptr, pdata_ptr->struct_size);
+ args->response_size = pdata_ptr->struct_size;
+
+ /* Data has now been returned */
+ pdata_ptr->flags |= PANIC_DATA_FLAG_OLD_HOSTCMD;
+ }
+
+ return EC_RES_SUCCESS;
+}
+DECLARE_HOST_COMMAND(EC_CMD_GET_PANIC_INFO,
+ host_command_panic_info,
+ EC_VER_MASK(0));
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);