diff options
author | Vincent Palatin <vpalatin@chromium.org> | 2013-12-04 17:15:12 -0800 |
---|---|---|
committer | chrome-internal-fetch <chrome-internal-fetch@google.com> | 2013-12-05 22:30:58 +0000 |
commit | 1762de9d19d2671cc56e5a479055379a346030d3 (patch) | |
tree | 21f6ec886e9abcf9638c8a88767b8299d7eddfe7 /common | |
parent | b45f3b9f348c550a7389973d916b8b7cb6b25a88 (diff) | |
download | chrome-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.mk | 2 | ||||
-rw-r--r-- | common/panic_output.c | 169 | ||||
-rw-r--r-- | common/timer.c | 278 |
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); |