summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorVincent Palatin <vpalatin@chromium.org>2014-12-05 08:10:34 -0800
committerchrome-internal-fetch <chrome-internal-fetch@google.com>2014-12-06 01:11:34 +0000
commitd097e25bf4796a83b122876c70c21ab8d2daf256 (patch)
tree79c802b02b54a75a1464cb53112fa65c504e3ec7
parent9b1b0cb2fc24ebb443adb348049cf2e1fc3a1788 (diff)
downloadchrome-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.h1
-rw-r--r--chip/g/build.mk1
-rw-r--r--chip/g/registers.h11
-rw-r--r--chip/g/watchdog.c103
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;
+}