diff options
author | Vincent Palatin <vpalatin@chromium.org> | 2014-12-05 08:10:34 -0800 |
---|---|---|
committer | chrome-internal-fetch <chrome-internal-fetch@google.com> | 2014-12-06 01:11:34 +0000 |
commit | d097e25bf4796a83b122876c70c21ab8d2daf256 (patch) | |
tree | 79c802b02b54a75a1464cb53112fa65c504e3ec7 | |
parent | 9b1b0cb2fc24ebb443adb348049cf2e1fc3a1788 (diff) | |
download | chrome-ec-d097e25bf4796a83b122876c70c21ab8d2daf256.tar.gz |
g: add watchdog driver
Implement a driver to trigger a watchdog reboot if we are stuck
somewhere. Also display a nice warning when we reach half of the
watchdog period.
Signed-off-by: Vincent Palatin <vpalatin@chromium.org>
BRANCH=none
BUG=none
TEST=On the console, type "waitms 500" and see nothing,
type "waitms 2000" and see the watchdog warning.
Type "waitms 4000" and see the warning, the platform rebooting.
Change-Id: Iac5d0100febd5eab1ae6cfac5a47ff728ebda3a6
Reviewed-on: https://chromium-review.googlesource.com/233430
Reviewed-by: Bill Richardson <wfrichar@chromium.org>
Commit-Queue: Vincent Palatin <vpalatin@chromium.org>
Tested-by: Vincent Palatin <vpalatin@chromium.org>
Trybot-Ready: Vincent Palatin <vpalatin@chromium.org>
-rw-r--r-- | board/cr50/board.h | 1 | ||||
-rw-r--r-- | chip/g/build.mk | 1 | ||||
-rw-r--r-- | chip/g/registers.h | 11 | ||||
-rw-r--r-- | chip/g/watchdog.c | 103 |
4 files changed, 115 insertions, 1 deletions
diff --git a/board/cr50/board.h b/board/cr50/board.h index ab82f50a8c..4bcaaa99e6 100644 --- a/board/cr50/board.h +++ b/board/cr50/board.h @@ -14,7 +14,6 @@ #undef CONFIG_FMAP #undef CONFIG_HIBERNATE #undef CONFIG_LID_SWITCH -#undef CONFIG_WATCHDOG /* How do I identify nonexistant GPIOs? */ #define DUMMY_GPIO_BANK -1 diff --git a/chip/g/build.mk b/chip/g/build.mk index 5263c412f2..b7e9d8908c 100644 --- a/chip/g/build.mk +++ b/chip/g/build.mk @@ -9,3 +9,4 @@ CFLAGS_CPU+=-march=armv7-m -mcpu=cortex-m3 # Required chip modules chip-y=clock.o gpio.o hwtimer.o jtag.o system.o uart.o +chip-$(CONFIG_WATCHDOG)+=watchdog.o diff --git a/chip/g/registers.h b/chip/g/registers.h index 16d58380ed..b257ec1cd4 100644 --- a/chip/g/registers.h +++ b/chip/g/registers.h @@ -77,6 +77,17 @@ static inline int x_timehs_addr(unsigned int module, unsigned int timer, #define GR_TIMEHS_MIS(m, t) X_TIMEHSREG(m, t, GC_TIMEHS_TIMER1MIS_OFFSET) #define GR_TIMEHS_BGLOAD(m, t) X_TIMEHSREG(m, t, GC_TIMEHS_TIMER1BGLOAD_OFFSET) +/* Watchdog */ +#define GR_WDOG_REG(off) REG32(GC_WATCHDOG0_BASE_ADDR + (off)) +#define GR_WATCHDOG_LOAD GR_WDOG_REG(GC_WATCHDOG_WDOGLOAD_OFFSET) +#define GR_WATCHDOG_VALUE GR_WDOG_REG(GC_WATCHDOG_WDOGVALUE_OFFSET) +#define GR_WATCHDOG_CTL GR_WDOG_REG(GC_WATCHDOG_WDOGCONTROL_OFFSET) +#define GR_WATCHDOG_ICR GR_WDOG_REG(GC_WATCHDOG_WDOGINTCLR_OFFSET) +#define GR_WATCHDOG_RIS GR_WDOG_REG(GC_WATCHDOG_WDOGRIS_OFFSET) +#define GR_WATCHDOG_LOCK GR_WDOG_REG(GC_WATCHDOG_WDOGLOCK_OFFSET) +#define GR_WATCHDOG_ITCR GR_WDOG_REG(GC_WATCHDOG_WDOGITCR_OFFSET) +#define GR_WATCHDOG_ITOP GR_WDOG_REG(GC_WATCHDOG_WDOGITOP_OFFSET) + /* Oscillator */ #define GR_XO_OSC_CLKOUT REG32(GC_XO0_BASE_ADDR + GC_XO_OSC_CLKOUT_OFFSET) #define GR_XO_OSC_ADC_CAL_FREQ2X REG32(GC_XO0_BASE_ADDR + GC_XO_OSC_ADC_CAL_FREQ2X_OFFSET) diff --git a/chip/g/watchdog.c b/chip/g/watchdog.c new file mode 100644 index 0000000000..e5c3f11d12 --- /dev/null +++ b/chip/g/watchdog.c @@ -0,0 +1,103 @@ +/* Copyright (c) 2014 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 "common.h" +#include "hooks.h" +#include "registers.h" +#include "task.h" +#include "util.h" +#include "watchdog.h" + +/* magic value to unlock the watchdog registers */ +#define WATCHDOG_MAGIC_WORD 0x1ACCE551 + +/* Watchdog expiration : assume 30 Mhz clock for now */ +#define WATCHDOG_PERIOD (CONFIG_WATCHDOG_PERIOD_MS * (30000000 / 1000)) + +/* Warning interrupt at the middle of the watchdog period */ +void IRQ_HANDLER(GC_IRQNUM_WATCHDOG0_WDOGINT)(void) __attribute__((naked)); +void IRQ_HANDLER(GC_IRQNUM_WATCHDOG0_WDOGINT)(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_trace\n" + /* 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. + */ + "mov r0, %[irq]\n" + "bl task_disable_irq\n" + "pop {r0, lr}\n" + "b task_resched_if_needed\n" + : : [irq] "i" (GC_IRQNUM_WATCHDOG0_WDOGINT)); +} +const struct irq_priority IRQ_PRIORITY(GC_IRQNUM_WATCHDOG0_WDOGINT) + __attribute__((section(".rodata.irqprio"))) + = {GC_IRQNUM_WATCHDOG0_WDOGINT, 0}; + /* put the watchdog at the highest priority */ + +void watchdog_reload(void) +{ + uint32_t status = GR_WATCHDOG_RIS; + + /* Unlock watchdog registers */ + GR_WATCHDOG_LOCK = WATCHDOG_MAGIC_WORD; + + /* As we reboot only on the second timeout, if we have already reached + * the first timeout we need to reset the interrupt bit. */ + if (status) { + GR_WATCHDOG_ICR = status; + /* That doesn't seem to unpend the watchdog interrupt (even if + * we do dummy writes to force the write to be committed), so + * explicitly unpend the interrupt before re-enabling it. */ + task_clear_pending_irq(GC_IRQNUM_WATCHDOG0_WDOGINT); + task_enable_irq(GC_IRQNUM_WATCHDOG0_WDOGINT); + } + + /* Reload the watchdog counter */ + GR_WATCHDOG_LOAD = WATCHDOG_PERIOD; + + /* Re-lock watchdog registers */ + GR_WATCHDOG_LOCK = 0xdeaddead; +} +DECLARE_HOOK(HOOK_TICK, watchdog_reload, HOOK_PRIO_DEFAULT); + +int watchdog_init(void) +{ + /* Enable clocks */ + REG_WRITE_MLV(GR_PMU_PERICLKSET0, + GC_PMU_PERICLKSET0_DWATCHDOG0_MASK, + GC_PMU_PERICLKSET0_DWATCHDOG0_LSB, 1); + + /* Unlock watchdog registers */ + GR_WATCHDOG_LOCK = WATCHDOG_MAGIC_WORD; + + /* Reload the watchdog counter */ + GR_WATCHDOG_LOAD = WATCHDOG_PERIOD; + + /* Reset after 2 time-out : activate both interrupt and reset. */ + GR_WATCHDOG_CTL = 0x3; + + /* Reset watchdog interrupt bits */ + GR_WATCHDOG_ICR = GR_WATCHDOG_RIS; + + /* Lock watchdog registers against unintended accesses */ + GR_WATCHDOG_LOCK = 0xdeaddead; + + /* Enable watchdog interrupt */ + task_enable_irq(GC_IRQNUM_WATCHDOG0_WDOGINT); + + return EC_SUCCESS; +} |