diff options
-rw-r--r-- | chip/stm32/hwtimer.c | 101 | ||||
-rw-r--r-- | chip/stm32/jtag-stm32f100.c | 4 | ||||
-rw-r--r-- | chip/stm32/jtag-stm32l15x.c | 4 | ||||
-rw-r--r-- | chip/stm32/watchdog.c | 59 | ||||
-rw-r--r-- | include/hwtimer.h | 44 |
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 */ |