summaryrefslogtreecommitdiff
path: root/chip/lm4/watchdog.c
diff options
context:
space:
mode:
Diffstat (limited to 'chip/lm4/watchdog.c')
-rw-r--r--chip/lm4/watchdog.c132
1 files changed, 132 insertions, 0 deletions
diff --git a/chip/lm4/watchdog.c b/chip/lm4/watchdog.c
new file mode 100644
index 0000000000..d42716abf5
--- /dev/null
+++ b/chip/lm4/watchdog.c
@@ -0,0 +1,132 @@
+/* Copyright (c) 2011 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.
+ */
+
+/* Watchdog driver */
+
+#include <stdint.h>
+
+#include "board.h"
+#include "common.h"
+#include "config.h"
+#include "registers.h"
+#include "task.h"
+#include "uart.h"
+#include "util.h"
+
+/*
+ * We use watchdog 0 which is clocked on the system clock
+ * to avoid the penalty cycles on each write access
+ */
+
+/* magic value to unlock the watchdog registers */
+#define LM4_WATCHDOG_MAGIC_WORD 0x1ACCE551
+
+/* watchdog counter initial value */
+static uint32_t watchdog_period;
+
+/* console debug command prototypes */
+int command_task_info(int argc, char **argv);
+int command_timer_info(int argc, char **argv);
+
+/**
+ * watchdog debug trace.
+ *
+ * It is triggered if the watchdog has not been reloaded after 1x the timeout
+ * period, after 2x the period an hardware reset is triggering.
+ */
+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
+ */
+ LM4_NVIC_DIS(0) = 1 << 18;
+
+ asm("mrs %0, psp":"=r"(psp));
+ if ((excep_lr & 0xf) == 1) {
+ /* we were already in exception context */
+ stack = (uint32_t *)excep_sp;
+ } else {
+ /* we were in task context */
+ stack = (uint32_t *)psp;
+ }
+
+ uart_printf("### WATCHDOG PC=%08x / LR=%08x / pSP=%08x ###\n",
+ stack[6], stack[5], 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);
+ command_timer_info(0, NULL);
+}
+
+void irq_18_handler(void) __attribute__((naked));
+void irq_18_handler(void)
+{
+ asm volatile("mov r0, lr\n"
+ "mov r1, sp\n"
+ "push {lr}\n"
+ "bl watchdog_trace\n"
+ "pop {lr}\n"
+ "mov r0, lr\n"
+ "b task_resched_if_needed\n");
+}
+const struct irq_priority prio_18 __attribute__((section(".rodata.irqprio")))
+ = {18, 0}; /* put the watchdog at the highest priority */
+
+void watchdog_reload(void)
+{
+ uint32_t status = LM4_WATCHDOG_RIS(0);
+
+ /* unlock watchdog registers */
+ LM4_WATCHDOG_LOCK(0) = LM4_WATCHDOG_MAGIC_WORD;
+
+ /* As we reboot only on the second time-out,
+ * if we have already reached 1 time-out
+ * we need to reset the interrupt bit.
+ */
+ if (status)
+ LM4_WATCHDOG_ICR(0) = status;
+
+ /* reload the watchdog counter */
+ LM4_WATCHDOG_LOAD(0) = watchdog_period;
+
+ /* re-lock watchdog registers */
+ LM4_WATCHDOG_LOCK(0) = 0xdeaddead;
+}
+
+int watchdog_init(int period_ms)
+{
+ volatile uint32_t scratch __attribute__((unused));
+
+ /* Enable watchdog 0 clock */
+ LM4_SYSTEM_RCGCWD |= 0x1;
+ /* wait 3 clock cycles before using the module */
+ scratch = LM4_SYSTEM_RCGCWD;
+
+ /* set the time-out period */
+ watchdog_period = period_ms * (CPU_CLOCK / 1000);
+ LM4_WATCHDOG_LOAD(0) = watchdog_period;
+
+ /* de-activate the watchdog when the JTAG stops the CPU */
+ LM4_WATCHDOG_TEST(0) |= 1 << 8;
+
+ /* reset after 2 time-out,
+ * activate the watchdog and lock the control register
+ */
+ LM4_WATCHDOG_CTL(0) = 0x3;
+
+ /* lock watchdog registers against unintended accesses */
+ LM4_WATCHDOG_LOCK(0) = 0xdeaddead;
+
+ return EC_SUCCESS;
+}