summaryrefslogtreecommitdiff
path: root/chip/mec1322/watchdog.c
diff options
context:
space:
mode:
Diffstat (limited to 'chip/mec1322/watchdog.c')
-rw-r--r--chip/mec1322/watchdog.c86
1 files changed, 81 insertions, 5 deletions
diff --git a/chip/mec1322/watchdog.c b/chip/mec1322/watchdog.c
index f46360f152..87533ac571 100644
--- a/chip/mec1322/watchdog.c
+++ b/chip/mec1322/watchdog.c
@@ -5,23 +5,70 @@
/* Watchdog driver */
-/*
- * TODO(crosbug.com/p/24107): Use independent timer for warning before watchdog
- * timer expires.
- */
-
#include "hooks.h"
#include "registers.h"
+#include "task.h"
#include "watchdog.h"
+/*
+ * Fire auxiliary timer 50ms before watchdog timer expires. This leaves
+ * some time for debug trace to be printed.
+ */
+#define AUX_TIMER_PERIOD_MS (WATCHDOG_PERIOD_MS - 50)
+
void watchdog_reload(void)
{
MEC1322_WDG_KICK = 1;
+
+#ifdef CONFIG_WATCHDOG_HELP
+ /* Reload the auxiliary timer */
+ MEC1322_TMR16_CTL(0) &= ~(1 << 5);
+ MEC1322_TMR16_CNT(0) = AUX_TIMER_PERIOD_MS;
+ MEC1322_TMR16_CTL(0) |= 1 << 5;
+#endif
}
DECLARE_HOOK(HOOK_TICK, watchdog_reload, HOOK_PRIO_DEFAULT);
int watchdog_init(void)
{
+#ifdef CONFIG_WATCHDOG_HELP
+ uint32_t val;
+
+ /*
+ * Watchdog does not warn us before expiring. Let's use a 16-bit
+ * timer as an auxiliary timer.
+ */
+
+ /* Stop the auxiliary timer if it's running */
+ MEC1322_TMR16_CTL(0) &= ~(1 << 5);
+
+ /* Enable auxiliary timer */
+ MEC1322_TMR16_CTL(0) |= 1 << 0;
+
+ val = MEC1322_TMR16_CTL(0);
+
+ /* Pre-scale = 48000 -> 1kHz -> Period = 1ms */
+ val = (val & 0xffff) | (47999 << 16);
+
+ /* No auto restart */
+ val &= ~(1 << 3);
+
+ /* Count down */
+ val &= ~(1 << 2);
+
+ MEC1322_TMR16_CTL(0) = val;
+
+ /* Enable interrupt from auxiliary timer */
+ MEC1322_TMR16_IEN(0) |= 1;
+ task_enable_irq(MEC1322_IRQ_TIMER16_0);
+ MEC1322_INT_ENABLE(23) |= 1 << 0;
+ MEC1322_INT_BLK_EN |= 1 << 23;
+
+ /* Load and start the auxiliary timer */
+ MEC1322_TMR16_CNT(0) = AUX_TIMER_PERIOD_MS;
+ MEC1322_TMR16_CNT(0) |= 1 << 5;
+#endif
+
/* Set timeout. It takes 1007us to decrement WDG_CNT by 1. */
MEC1322_WDG_LOAD = WATCHDOG_PERIOD_MS * 1000 / 1007;
@@ -30,3 +77,32 @@ int watchdog_init(void)
return EC_SUCCESS;
}
+
+#ifdef CONFIG_WATCHDOG_HELP
+void watchdog_check(uint32_t excep_lr, uint32_t excep_sp)
+{
+ /* Clear status */
+ MEC1322_TMR16_STS(0) |= 1;
+
+ watchdog_trace(excep_lr, excep_sp);
+}
+
+void IRQ_HANDLER(MEC1322_IRQ_TIMER16_0)(void) __attribute__((naked));
+void IRQ_HANDLER(MEC1322_IRQ_TIMER16_0)(void)
+{
+ /* Naked call so we can extract raw LR and SP */
+ asm volatile("mov r0, lr\n"
+ "mov r1, sp\n"
+ /* Must push registers in pairs to keep 64-bit aligned
+ * stack for ARM EABI. This also conveninently saves
+ * R0=LR so we can pass it to task_resched_if_needed. */
+ "push {r0, lr}\n"
+ "bl watchdog_check\n"
+ "pop {r0, lr}\n"
+ "b task_resched_if_needed\n");
+}
+const struct irq_priority IRQ_PRIORITY(MEC1322_IRQ_TIMER16_0)
+ __attribute__((section(".rodata.irqprio")))
+ = {MEC1322_IRQ_TIMER16_0, 0}; /* put the watchdog at the
+ highest priority */
+#endif