diff options
-rw-r--r-- | board/mec1322_evb/board.h | 1 | ||||
-rw-r--r-- | chip/mec1322/watchdog.c | 86 |
2 files changed, 82 insertions, 5 deletions
diff --git a/board/mec1322_evb/board.h b/board/mec1322_evb/board.h index c5f14a27f9..ab0c734657 100644 --- a/board/mec1322_evb/board.h +++ b/board/mec1322_evb/board.h @@ -10,6 +10,7 @@ /* Optional features */ #define CONFIG_SYSTEM_UNLOCKED /* Allow dangerous commands */ +#define CONFIG_WATCHDOG_HELP /* Modules we want to exclude */ #undef CONFIG_EEPROM 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 |