summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorRandall Spangler <rspangler@chromium.org>2012-04-25 13:04:23 -0700
committerRandall Spangler <rspangler@chromium.org>2012-04-25 14:49:49 -0700
commit1aa57e140e3c8ef02c0140730dde3962d3eb821f (patch)
tree18b39676fa0b9cbae24e3cb50a712336ed3a7528
parent212784b5fab0a1ba30c45e66a9329423a6d998e5 (diff)
downloadchrome-ec-1aa57e140e3c8ef02c0140730dde3962d3eb821f.tar.gz
Watchdog fixes
1) When frequency changes, reload the watchdog timer right away, or it may expire before the next reload. (Only matters when re-enabling the PLL.) 2) Split out the timer/task debug output used by the watchdog into their own routines, instead of assuming it's safe to call the command handlers. Also make the flushes in those print routines safe to call from interrupt level. Signed-off-by: Randall Spangler <rspangler@chromium.org> BUG=none TEST=waitms 1500; should print task dump again Change-Id: I07e0ed24a526ae499566dab0bbeb0f5755cd5be6
-rw-r--r--chip/lm4/watchdog.c44
-rw-r--r--common/main.c4
-rw-r--r--core/cortex-m/task.c47
-rw-r--r--core/cortex-m/timer.c54
-rw-r--r--include/task.h9
-rw-r--r--include/timer.h43
6 files changed, 113 insertions, 88 deletions
diff --git a/chip/lm4/watchdog.c b/chip/lm4/watchdog.c
index 45eb3a36e9..7ef2b36b97 100644
--- a/chip/lm4/watchdog.c
+++ b/chip/lm4/watchdog.c
@@ -32,11 +32,6 @@
static uint32_t watchdog_period; /* Watchdog counter initial value */
-/* console debug command prototypes */
-int command_task_info(int argc, char **argv);
-int command_timer_info(int argc, char **argv);
-
-
/* Watchdog debug trace. This is triggered if the watchdog has not been
* reloaded after 1x the timeout period, after 2x the period an hardware reset
* is triggering. */
@@ -45,11 +40,10 @@ void watchdog_trace(uint32_t excep_lr, uint32_t excep_sp)
uint32_t psp;
uint32_t *stack;
- /* we do NOT reset the watchdog interrupt here, it will be done in
- * watchdog_reload() or fire the reset
- * instead de-activate the interrupt in the NVIC :
- * so, we will get the trace only once
- */
+ /* Do NOT reset the watchdog interrupt here; it will be done in
+ * watchdog_reload(), or reset will be triggered if we don't call that
+ * by the next watchdog period. Instead, de-activate the interrupt in
+ * the NVIC, so the watchdog trace will only be printed once. */
task_disable_irq(LM4_IRQ_WATCHDOG);
asm("mrs %0, psp":"=r"(psp));
@@ -61,17 +55,20 @@ void watchdog_trace(uint32_t excep_lr, uint32_t excep_sp)
stack = (uint32_t *)psp;
}
- uart_printf("### WATCHDOG PC=%08x / LR=%08x / pSP=%08x ###\n",
+ uart_printf("### WATCHDOG PC=%08x / LR=%08x / pSP=%08x ",
stack[6], stack[5], psp);
- /* ensure this debug message is always flushed to the UART */
+ if ((excep_lr & 0xf) == 1)
+ uart_puts("(exc) ###\n");
+ else
+ uart_printf("(task %d) ###\n", task_from_addr(psp));
+ /* Ensure this debug message is always flushed to the UART */
uart_emergency_flush();
- /* if we are blocked in a high priority IT handler, the following
- * debug messages might not appear but they are useless in that
- * situation.
- */
- command_task_info(0, NULL);
+
+ /* If we are blocked in a high priority IT handler, the following debug
+ * messages might not appear but they are useless in that situation. */
+ timer_print_info();
uart_emergency_flush();
- command_timer_info(0, NULL);
+ task_print_list();
uart_emergency_flush();
}
@@ -125,6 +122,10 @@ static int watchdog_freq_changed(void)
{
/* Set the timeout period */
watchdog_period = WATCHDOG_PERIOD_MS * (clock_get_freq() / 1000);
+
+ /* Reload the watchdog timer now */
+ watchdog_reload();
+
return EC_SUCCESS;
}
DECLARE_HOOK(HOOK_FREQ_CHANGE, watchdog_freq_changed, HOOK_PRIO_DEFAULT);
@@ -139,12 +140,11 @@ int watchdog_init(void)
/* Wait 3 clock cycles before using the module */
scratch = LM4_SYSTEM_RCGCWD;
- /* Unlock watchdog registers */
- LM4_WATCHDOG_LOCK(0) = LM4_WATCHDOG_MAGIC_WORD;
-
/* Set initial timeout period */
watchdog_freq_changed();
- LM4_WATCHDOG_LOAD(0) = watchdog_period;
+
+ /* Unlock watchdog registers */
+ LM4_WATCHDOG_LOCK(0) = LM4_WATCHDOG_MAGIC_WORD;
/* De-activate the watchdog when the JTAG stops the CPU */
LM4_WATCHDOG_TEST(0) |= 1 << 8;
diff --git a/common/main.c b/common/main.c
index 8252a231d0..044ae45027 100644
--- a/common/main.c
+++ b/common/main.c
@@ -107,8 +107,8 @@ int main(void)
/* Print the init time and reset cause. Init time isn't completely
* accurate because it can't take into account the time for the first
* few module inits, but it'll at least catch the majority of them. */
- uart_printf("\n\n--- Chrome EC initialized in %d us ---\n",
- get_time().le.lo);
+ uart_printf("\n\n--- Chrome EC initialized in %ld us ---\n",
+ get_time().val);
uart_printf("build: %s\n", system_get_build_info());
uart_printf("(image: %s, last reset: %s)\n",
system_get_image_copy_string(),
diff --git a/core/cortex-m/task.c b/core/cortex-m/task.c
index 3f63797146..b21d2cd59c 100644
--- a/core/cortex-m/task.c
+++ b/core/cortex-m/task.c
@@ -12,6 +12,7 @@
#include "link_defs.h"
#include "task.h"
#include "timer.h"
+#include "uart.h"
#include "util.h"
/**
@@ -191,9 +192,9 @@ inline int get_interrupt_context(void)
}
-task_id_t task_get_current(void)
+task_id_t task_from_addr(uint32_t addr)
{
- task_id_t id = __get_current() - tasks;
+ task_id_t id = (addr - (uint32_t)tasks) >> TASK_SIZE_LOG2;
if (id >= TASK_ID_COUNT)
id = TASK_ID_INVALID;
@@ -201,6 +202,12 @@ task_id_t task_get_current(void)
}
+task_id_t task_get_current(void)
+{
+ return task_from_addr((uint32_t)__get_current());
+}
+
+
uint32_t *task_get_event_bitmap(task_id_t tskid)
{
task_ *tsk = __task_id_to_ptr(tskid);
@@ -208,9 +215,7 @@ uint32_t *task_get_event_bitmap(task_id_t tskid)
}
-/**
- * scheduling system call
- */
+/* Scheduling system call */
void svc_handler(int desched, task_id_t resched)
{
task_ *current, *next;
@@ -484,24 +489,34 @@ void mutex_unlock(struct mutex *mtx)
}
-#ifdef CONFIG_DEBUG
-
-
-int command_task_info(int argc, char **argv)
+void task_print_list(void)
{
-#ifdef CONFIG_TASK_PROFILING
- int total = 0;
-#endif
int i;
-
ccputs("Task Ready Name Events Time (us)\n");
for (i = 0; i < TASK_ID_COUNT; i++) {
char is_ready = (tasks_ready & (1<<i)) ? 'R' : ' ';
ccprintf("%4d %c %-16s %08x %10ld\n", i, is_ready,
task_names[i], tasks[i].events, tasks[i].runtime);
- cflush();
+ if (in_interrupt_context())
+ uart_emergency_flush();
+ else
+ cflush();
}
+}
+
+
+#ifdef CONFIG_DEBUG
+
+
+int command_task_info(int argc, char **argv)
+{
+#ifdef CONFIG_TASK_PROFILING
+ int total = 0;
+ int i;
+#endif
+
+ task_print_list();
#ifdef CONFIG_TASK_PROFILING
ccputs("IRQ counts by type:\n");
@@ -539,7 +554,9 @@ static int command_task_ready(int argc, char **argv)
return EC_SUCCESS;
}
DECLARE_CONSOLE_COMMAND(taskready, command_task_ready);
-#endif
+
+
+#endif /* CONFIG_DEBUG */
int task_pre_init(void)
diff --git a/core/cortex-m/timer.c b/core/cortex-m/timer.c
index 1501b0e269..d8123d1686 100644
--- a/core/cortex-m/timer.c
+++ b/core/cortex-m/timer.c
@@ -23,12 +23,12 @@ 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 */
@@ -37,11 +37,7 @@ static void expire_timer(task_id_t tskid)
task_set_event(tskid, TASK_EVENT_TIMER, 0);
}
-/**
- * Search the next deadline and program it in the timer hardware
- *
- * overflow: if true, the 32-bit counter as overflowed since the last call.
- */
+
void process_timers(int overflow)
{
uint32_t check_timer, running_t0;
@@ -86,6 +82,7 @@ reprocess_timers:
//TODO narrow race: deadline might have been reached before
}
+
void udelay(unsigned us)
{
timestamp_t deadline = get_time();
@@ -94,6 +91,7 @@ void udelay(unsigned us)
while (get_time().val < deadline.val) {}
}
+
int timer_arm(timestamp_t tstamp, task_id_t tskid)
{
ASSERT(tskid < TASK_ID_COUNT);
@@ -112,6 +110,7 @@ int timer_arm(timestamp_t tstamp, task_id_t tskid)
return EC_SUCCESS;
}
+
int timer_cancel(task_id_t tskid)
{
ASSERT(tskid < TASK_ID_COUNT);
@@ -152,6 +151,31 @@ timestamp_t get_time(void)
}
+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 -> %10ld us from now\n"
+ "Active timers:\n",
+ t, deadline, deadline - t);
+ for (tskid = 0; tskid < TASK_ID_COUNT; tskid++) {
+ if (timer_running & (1<<tskid)) {
+ ccprintf(" Tsk %2d 0x%016lx -> %10ld %x\n", tskid,
+ timer_deadline[tskid].val,
+ timer_deadline[tskid].val - t, 0xabcd);
+ if (in_interrupt_context())
+ uart_emergency_flush();
+ else
+ cflush();
+ }
+ }
+}
+
+
static int command_wait(int argc, char **argv)
{
if (argc < 2)
@@ -176,23 +200,7 @@ DECLARE_CONSOLE_COMMAND(gettime, command_get_time);
int command_timer_info(int argc, char **argv)
{
- 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 -> %10ld us from now\n"
- "Active timers:\n",
- t, deadline, deadline - t);
- for (tskid = 0; tskid < TASK_ID_COUNT; tskid++) {
- if (timer_running & (1<<tskid)) {
- ccprintf(" Tsk %2d 0x%016lx -> %10ld\n", tskid,
- timer_deadline[tskid].val,
- timer_deadline[tskid].val - t);
- cflush();
- }
- }
+ timer_print_info();
return EC_SUCCESS;
}
DECLARE_CONSOLE_COMMAND(timerinfo, command_timer_info);
diff --git a/include/task.h b/include/task.h
index 79289b1c08..660195460e 100644
--- a/include/task.h
+++ b/include/task.h
@@ -55,6 +55,11 @@ static inline void task_wake(task_id_t tskid)
* When called in interrupt context, returns TASK_ID_INVALID. */
task_id_t task_get_current(void);
+/* Convert an address to the corresponding task ID. The address may be a stack
+ * pointer or the task data for a task. Returns TASK_ID_INVALID if the address
+ * does not correspond to a task. */
+task_id_t task_from_addr(uint32_t addr);
+
/* Return a pointer to the bitmap of events of the task. */
uint32_t *task_get_event_bitmap(task_id_t tsk);
@@ -70,6 +75,10 @@ uint32_t *task_get_event_bitmap(task_id_t tsk);
* Returns the bitmap of received events (and clears it atomically). */
uint32_t task_wait_event(int timeout_us);
+/* Prints the list of tasks using the command output channel. This may be
+ * called from interrupt level. */
+void task_print_list(void);
+
#ifdef CONFIG_TASK_PROFILING
/* Start tracking an interrupt.
*
diff --git a/include/timer.h b/include/timer.h
index b73ac292bb..4e132ebd6f 100644
--- a/include/timer.h
+++ b/include/timer.h
@@ -1,17 +1,17 @@
-/* Copyright (c) 2011 The Chromium OS Authors. All rights reserved.
+/* 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 */
-#ifndef __EC_TIMER_H
-#define __EC_TIMER_H
+#ifndef __CROS_EC_TIMER_H
+#define __CROS_EC_TIMER_H
#include "common.h"
#include "task_id.h"
-/* Micro-second timestamp. */
+/* Microsecond timestamp. */
typedef union {
uint64_t val;
struct {
@@ -23,38 +23,29 @@ typedef union {
/* Initializes the Timer module. */
int timer_init(void);
-/**
- * Launches a one-shot timer.
- *
- * tstamp : timestamp in micro-seconds when the timer expires
- * tskid : identifier of the task owning the timer
- */
+/* Launch a one-shot timer for task <tskid> which expires at timestamp
+ * <tstamp>. */
int timer_arm(timestamp_t tstamp, task_id_t tskid);
-/**
- * Cancels a running timer.
- *
- * tskid : identifier of the task owning the timer
- */
+/* Cancel a running timer for the specified task id. */
int timer_cancel(task_id_t tskid);
-/**
- * Busy wait the selected number of micro-seconds
- */
+/* Busy-wait the selected number of microseconds. Note that calling this
+ * with us>1000 may impact system performance; use usleep for longer delays. */
void udelay(unsigned us);
-/**
- * Sleep during the selected number of micro-seconds
- *
- * The current task will be de-scheduled until the delay expired
+/* Sleep during the selected number of microseconds. The current task will be
+ * de-scheduled until the delay expires.
*
* Note: if an event happens before the end of sleep, the function will return.
*/
void usleep(unsigned us);
-/**
- * Get the current timestamp from the system timer
- */
+/* Get the current timestamp from the system timer. */
timestamp_t get_time(void);
-#endif /* __EC_TIMER_H */
+/* Print the current timer information using the command output channel. This
+ * may be called from interrupt level. */
+void timer_print_info(void);
+
+#endif /* __CROS_EC_TIMER_H */