summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorSimon Glass <sjg@chromium.org>2012-06-24 11:54:47 -0700
committerGerrit <chrome-bot@google.com>2012-07-02 22:35:51 -0700
commit9b48067b09aaace6e1d5526a65473392f12389c1 (patch)
tree5424744113f2ee5318194ec258de305fa5bef796
parent184eeb65acea77989673920a05f279f5b4fbaef2 (diff)
downloadchrome-ec-9b48067b09aaace6e1d5526a65473392f12389c1.tar.gz
stm32: Use a timer as the watchdog warning
The WWDG is not ideal for this purpose, since if we fail to handle its interrupt withint 60ms or so, we get a reset. This can be a problem when we are reporting a panic, since the uart output takes a long time. Change to using timer 9, which is free, and make it print a watchdog warning after one second. BUG=chrome-os-partner:10146 TEST=manual: build and boot on snow; waitms 1200 and see that a watchdog timeout is reported correctly. See that the panic message is displayed in full without a reset happening before the end. Change-Id: Ifc3671098e1f3a7ef409b8d1fb919d22eaa90358 Signed-off-by: Simon Glass <sjg@chromium.org> Reviewed-on: https://gerrit.chromium.org/gerrit/26172
-rw-r--r--chip/stm32/hwtimer.c101
-rw-r--r--chip/stm32/jtag-stm32f100.c4
-rw-r--r--chip/stm32/jtag-stm32l15x.c4
-rw-r--r--chip/stm32/watchdog.c59
-rw-r--r--include/hwtimer.h44
5 files changed, 155 insertions, 57 deletions
diff --git a/chip/stm32/hwtimer.c b/chip/stm32/hwtimer.c
index d24f07e5e0..f43a444088 100644
--- a/chip/stm32/hwtimer.c
+++ b/chip/stm32/hwtimer.c
@@ -10,14 +10,25 @@
#include "board.h"
#include "common.h"
#include "hwtimer.h"
+#include "panic.h"
#include "registers.h"
#include "task.h"
+#include "watchdog.h"
#define US_PER_SECOND 1000000
/* Divider to get microsecond for the clock */
#define CLOCKSOURCE_DIVIDER (CPU_CLOCK/US_PER_SECOND)
+#ifdef CHIP_VARIANT_stm32f100
+#define TIM_WD_IRQ STM32_IRQ_TIM1_UP_TIM16
+#define TIM_WD 1 /* Timer to use for watchdog */
+#endif
+
+enum {
+ TIM_WD_BASE = STM32_TIM1_BASE,
+};
+
static uint32_t last_deadline;
void __hw_clock_event_set(uint32_t deadline)
@@ -145,3 +156,93 @@ int __hw_clock_source_init(uint32_t start_t)
return STM32_IRQ_TIM4;
}
+
+/*
+ * We don't have TIM1 on STM32L, so don't support this function for now.
+ * TIM5 doesn't appear to exist in either variant, and TIM9 cannot be
+ * triggered as a slave from TIM4. We could perhaps use TIM9 as our
+ * fast counter on STM32L.
+ */
+#ifdef CHIP_VARIANT_stm32f100
+
+void watchdog_check(uint32_t excep_lr, uint32_t excep_sp)
+{
+ struct timer_ctlr *timer = (struct timer_ctlr *)TIM_WD_BASE;
+
+ /* clear status */
+ timer->sr = 0;
+
+ watchdog_trace(excep_lr, excep_sp);
+}
+
+void IRQ_HANDLER(TIM_WD_IRQ)(void) __attribute__((naked));
+void IRQ_HANDLER(TIM_WD_IRQ)(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_BUILD_NAME(prio_, TIM_WD_IRQ, )
+ __attribute__((section(".rodata.irqprio")))
+ = {TIM_WD_IRQ, 0}; /* put the watchdog at the highest
+ priority */
+
+void hwtimer_setup_watchdog(void)
+{
+ struct timer_ctlr *timer = (struct timer_ctlr *)TIM_WD_BASE;
+
+ /* Enable clock */
+#if TIM_WD == 1
+ STM32_RCC_APB2ENR |= 1 << 11;
+#else
+ STM32_RCC_APB1ENR |= 1 << (TIM_WD - 2);
+#endif
+
+ /*
+ * Timer configuration : Down counter, counter disabled, update
+ * event only on overflow.
+ */
+ timer->cr1 = 0x0014 | (1 << 7);
+
+ /* TIM (slave mode) uses ITR3 as internal trigger */
+ timer->smcr = 0x0037;
+
+ /*
+ * The auto-reload value is based on the period between rollovers
+ * for TIM4. Since TIM4 runs at 1MHz, it will overflow in 65.536ms.
+ * We divide our required watchdog period by this amount to obtain
+ * the number of times TIM4 can overflow before we generate an
+ * interrupt.
+ */
+ timer->arr = timer->cnt = WATCHDOG_PERIOD_MS * 1000 / (1 << 16);
+
+ /* count on every TIM4 overflow */
+ timer->psc = 0;
+
+ /* Reload the pre-scaler from arr when it goes below zero */
+ timer->egr = 0x0000;
+
+ /* setup the overflow interrupt */
+ timer->dier = 0x0001;
+
+ /* Start counting */
+ timer->cr1 |= 1;
+
+ /* Enable timer interrupts */
+ task_enable_irq(TIM_WD_IRQ);
+}
+
+void hwtimer_reset_watchdog(void)
+{
+ struct timer_ctlr *timer = (struct timer_ctlr *)TIM_WD_BASE;
+
+ timer->cnt = timer->arr;
+}
+#endif
diff --git a/chip/stm32/jtag-stm32f100.c b/chip/stm32/jtag-stm32f100.c
index 444d00f016..17b4e08ff1 100644
--- a/chip/stm32/jtag-stm32f100.c
+++ b/chip/stm32/jtag-stm32f100.c
@@ -9,8 +9,8 @@
int jtag_pre_init(void)
{
- /* stop TIM2, TIM3 and watchdogs when the JTAG stops the CPU */
- STM32_DBGMCU_CR |= 0x00001b00;
+ /* stop TIM1-4 and watchdogs when the JTAG stops the CPU */
+ STM32_DBGMCU_CR |= 0x00003f00;
return EC_SUCCESS;
}
diff --git a/chip/stm32/jtag-stm32l15x.c b/chip/stm32/jtag-stm32l15x.c
index 234e2aa967..7b65400b1c 100644
--- a/chip/stm32/jtag-stm32l15x.c
+++ b/chip/stm32/jtag-stm32l15x.c
@@ -9,8 +9,8 @@
int jtag_pre_init(void)
{
- /* stop TIM2, TIM3 and watchdogs when the JTAG stops the CPU */
- STM32_DBGMCU_APB1FZ |= 0x00001803;
+ /* stop TIM2-4 and watchdogs when the JTAG stops the CPU */
+ STM32_DBGMCU_APB1FZ |= 0x00001807;
return EC_SUCCESS;
}
diff --git a/chip/stm32/watchdog.c b/chip/stm32/watchdog.c
index a25e2576ca..d93d0a6b96 100644
--- a/chip/stm32/watchdog.c
+++ b/chip/stm32/watchdog.c
@@ -10,6 +10,7 @@
#include "config.h"
#include "registers.h"
#include "gpio.h"
+#include "hwtimer.h"
#include "task.h"
#include "timer.h"
#include "util.h"
@@ -51,52 +52,11 @@ void watchdog_reload(void)
STM32_IWDG_KR = 0xaaaa;
watchdog_reset_count();
+#ifdef CONFIG_WATCHDOG_HELP
+ hwtimer_reset_watchdog();
+#endif
}
-
-/**
- * Chcek if a watchdog interrupt needs to be reported.
- *
- * If so, this function should call watchdog_trace()
- *
- * @param excep_lr Value of lr to indicate caller return
- * @param excep_sp Value of sp to indicate caller task id
- */
-void watchdog_check(uint32_t excep_lr, uint32_t excep_sp)
-{
- /* Reset the windowed watchdog here */
- STM32_WWDG_CR = 0xff;
- STM32_WWDG_SR = 0;
-
- /* If the count has expired, output a trace */
- if (!--watchdog_count) {
- /* Reset here, to give the UART enough time to send trace */
- watchdog_reset_count();
- watchdog_trace(excep_lr, excep_sp);
- watchdog_reset_count();
- }
-}
-
-
-void IRQ_HANDLER(STM32_IRQ_WWDG)(void) __attribute__((naked));
-void IRQ_HANDLER(STM32_IRQ_WWDG)(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_BUILD_NAME(prio_, STM32_IRQ_WWDG, )
- __attribute__((section(".rodata.irqprio")))
- = {STM32_IRQ_WWDG, 0}; /* put the watchdog at the highest
- priority */
-
int watchdog_init(void)
{
uint32_t watchdog_period;
@@ -119,15 +79,8 @@ int watchdog_init(void)
watchdog_reset_count();
#ifdef CONFIG_WATCHDOG_HELP
- /* enable clock */
- STM32_RCC_APB1ENR |= 1 << 11;
-
- STM32_WWDG_SR = 0;
- STM32_WWDG_CR = 0xff;
- STM32_WWDG_CFR = 0x7f | STM32_WWDG_TB_8 | STM32_WWDG_EWI;
-
- /* Enable watchdog interrupt */
- task_enable_irq(IRQ_WATCHDOG);
+ /* Use a harder timer to warn about an impending watchdog reset */
+ hwtimer_setup_watchdog();
#endif
return EC_SUCCESS;
diff --git a/include/hwtimer.h b/include/hwtimer.h
index 4ebc254fa5..fdf1d94852 100644
--- a/include/hwtimer.h
+++ b/include/hwtimer.h
@@ -8,6 +8,35 @@
#ifndef __CROS_EC_HWTIMER_H
#define __CROS_EC_HWTIMER_H
+struct timer_ctlr {
+ unsigned cr1;
+ unsigned cr2;
+ unsigned smcr;
+ unsigned dier;
+
+ unsigned sr;
+ unsigned egr;
+ unsigned ccmr1;
+ unsigned ccmr2;
+
+ unsigned ccer;
+ unsigned cnt;
+ unsigned psc;
+ unsigned arr;
+
+ unsigned reserved30;
+ unsigned ccr1;
+ unsigned ccr2;
+ unsigned ccr3;
+
+ unsigned ccr4;
+ unsigned reserved44;
+ unsigned dcr;
+ unsigned dmar;
+
+ unsigned or;
+};
+
/**
* Programs when the next timer should fire an interrupt.
* deadline: timestamp of the event.
@@ -41,4 +70,19 @@ int __hw_clock_source_init(uint32_t start_t);
*/
void process_timers(int overflow);
+/**
+ * Set up the timer that we will use as a watchdog warning.
+ *
+ * Once this has been set up, we will print a warning shortly before the
+ * real watchdog fires. To avoid this, hwtimer_reset_watchdog() must be
+ * called periodically.
+ *
+ * This is needed since the real watchdog timer (IWDG) does not provide
+ * an interrupt to warn of an impending watchdog reset.
+ */
+void hwtimer_setup_watchdog(void);
+
+/* Reset the watchdog timer, to avoid the watchdog warning */
+void hwtimer_reset_watchdog(void);
+
#endif /* __CROS_EC_HWTIMER_H */