| /* Copyright (c) 2012 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. |
| */ |
| |
| /* Hardware timers driver */ |
| |
| #include "clock.h" |
| #include "common.h" |
| #include "hooks.h" |
| #include "hwtimer.h" |
| #include "panic.h" |
| #include "registers.h" |
| #include "task.h" |
| #include "timer.h" |
| #include "watchdog.h" |
| |
| /* |
| * Trigger select mapping for slave timer from master timer. This is |
| * unfortunately not very straightforward; there's no tidy way to do this |
| * algorithmically. To avoid burning memory for a lookup table, use macros to |
| * compute the offset. This also has the benefit that compilation will fail if |
| * an unsupported master/slave pairing is used. |
| * |
| * Slave Master |
| * 1 15 2 3 4 (STM32F100 only) |
| * 2 9 10 3 4 |
| * 3 9 2 11 4 |
| * 4 10 2 3 9 |
| * 9 2 3 10 11 (STM32L15x only) |
| * -------------------- |
| * ts = 0 1 2 3 |
| */ |
| #define STM32_TIM_TS_SLAVE_1_MASTER_15 0 |
| #define STM32_TIM_TS_SLAVE_1_MASTER_2 1 |
| #define STM32_TIM_TS_SLAVE_1_MASTER_3 2 |
| #define STM32_TIM_TS_SLAVE_1_MASTER_4 3 |
| #define STM32_TIM_TS_SLAVE_2_MASTER_9 0 |
| #define STM32_TIM_TS_SLAVE_2_MASTER_10 1 |
| #define STM32_TIM_TS_SLAVE_2_MASTER_3 2 |
| #define STM32_TIM_TS_SLAVE_2_MASTER_4 3 |
| #define STM32_TIM_TS_SLAVE_3_MASTER_9 0 |
| #define STM32_TIM_TS_SLAVE_3_MASTER_2 1 |
| #define STM32_TIM_TS_SLAVE_3_MASTER_11 2 |
| #define STM32_TIM_TS_SLAVE_3_MASTER_4 3 |
| #define STM32_TIM_TS_SLAVE_4_MASTER_10 0 |
| #define STM32_TIM_TS_SLAVE_4_MASTER_2 1 |
| #define STM32_TIM_TS_SLAVE_4_MASTER_3 2 |
| #define STM32_TIM_TS_SLAVE_4_MASTER_9 3 |
| #define STM32_TIM_TS_SLAVE_9_MASTER_2 0 |
| #define STM32_TIM_TS_SLAVE_9_MASTER_3 1 |
| #define STM32_TIM_TS_SLAVE_9_MASTER_10 2 |
| #define STM32_TIM_TS_SLAVE_9_MASTER_11 3 |
| #define TSMAP1(slave, master) STM32_TIM_TS_SLAVE_ ## slave ## _MASTER_ ## master |
| #define TSMAP(slave, master) TSMAP1(slave, master) |
| |
| /* |
| * Timers are defined per board. This gives us flexibility to work around |
| * timers which are dedicated to board-specific PWM sources. |
| */ |
| #define IRQ_TIM(n) STM32_CAT(STM32_IRQ_TIM, n, ) |
| #define IRQ_MSB IRQ_TIM(TIM_CLOCK_MSB) |
| #define IRQ_LSB IRQ_TIM(TIM_CLOCK_LSB) |
| #define IRQ_WD IRQ_TIM(TIM_WATCHDOG) |
| |
| /* TIM1 has fancy names for its IRQs; remap count-up IRQ for the macro above */ |
| #define STM32_IRQ_TIM1 STM32_IRQ_TIM1_UP_TIM16 |
| |
| #define TIM_BASE(n) STM32_CAT(STM32_TIM, n, _BASE) |
| #define TIM_WD_BASE TIM_BASE(TIM_WATCHDOG) |
| |
| static uint32_t last_deadline; |
| |
| void __hw_clock_event_set(uint32_t deadline) |
| { |
| last_deadline = deadline; |
| |
| if ((deadline >> 16) > STM32_TIM_CNT(TIM_CLOCK_MSB)) { |
| /* first set a match on the MSB */ |
| STM32_TIM_CCR1(TIM_CLOCK_MSB) = deadline >> 16; |
| /* disable LSB match */ |
| STM32_TIM_DIER(TIM_CLOCK_LSB) &= ~2; |
| /* Clear the match flags */ |
| STM32_TIM_SR(TIM_CLOCK_MSB) = ~2; |
| STM32_TIM_SR(TIM_CLOCK_LSB) = ~2; |
| /* Set the match interrupt */ |
| STM32_TIM_DIER(TIM_CLOCK_MSB) |= 2; |
| } |
| /* |
| * In the unlikely case where the MSB has increased and matched |
| * the deadline MSB before we set the match interrupt, as the STM |
| * hardware timer won't trigger an interrupt, we fall back to the |
| * following LSB event code to set another interrupt. |
| */ |
| if ((deadline >> 16) == STM32_TIM_CNT(TIM_CLOCK_MSB)) { |
| /* we can set a match on the LSB only */ |
| STM32_TIM_CCR1(TIM_CLOCK_LSB) = deadline & 0xffff; |
| /* disable MSB match */ |
| STM32_TIM_DIER(TIM_CLOCK_MSB) &= ~2; |
| /* Clear the match flags */ |
| STM32_TIM_SR(TIM_CLOCK_MSB) = ~2; |
| STM32_TIM_SR(TIM_CLOCK_LSB) = ~2; |
| /* Set the match interrupt */ |
| STM32_TIM_DIER(TIM_CLOCK_LSB) |= 2; |
| } |
| /* |
| * If the LSB deadline is already in the past and won't trigger an |
| * interrupt, the common code in process_timers will deal with the |
| * expired timer and automatically set the next deadline, we don't need |
| * to do anything here. |
| */ |
| } |
| |
| uint32_t __hw_clock_event_get(void) |
| { |
| return last_deadline; |
| } |
| |
| void __hw_clock_event_clear(void) |
| { |
| /* Disable the match interrupts */ |
| STM32_TIM_DIER(TIM_CLOCK_LSB) &= ~2; |
| STM32_TIM_DIER(TIM_CLOCK_MSB) &= ~2; |
| } |
| |
| uint32_t __hw_clock_source_read(void) |
| { |
| uint32_t hi; |
| uint32_t lo; |
| |
| /* Ensure the two half-words are coherent */ |
| do { |
| hi = STM32_TIM_CNT(TIM_CLOCK_MSB); |
| lo = STM32_TIM_CNT(TIM_CLOCK_LSB); |
| } while (hi != STM32_TIM_CNT(TIM_CLOCK_MSB)); |
| |
| return (hi << 16) | lo; |
| } |
| |
| void __hw_clock_source_set(uint32_t ts) |
| { |
| STM32_TIM_CNT(TIM_CLOCK_MSB) = ts >> 16; |
| STM32_TIM_CNT(TIM_CLOCK_LSB) = ts & 0xffff; |
| } |
| |
| static void __hw_clock_source_irq(void) |
| { |
| uint32_t stat_tim_msb = STM32_TIM_SR(TIM_CLOCK_MSB); |
| |
| /* Clear status */ |
| STM32_TIM_SR(TIM_CLOCK_LSB) = 0; |
| STM32_TIM_SR(TIM_CLOCK_MSB) = 0; |
| |
| /* |
| * Find expired timers and set the new timer deadline |
| * signal overflow if the 16-bit MSB counter has overflowed. |
| */ |
| process_timers(stat_tim_msb & 0x01); |
| } |
| DECLARE_IRQ(IRQ_MSB, __hw_clock_source_irq, 1); |
| DECLARE_IRQ(IRQ_LSB, __hw_clock_source_irq, 1); |
| |
| void __hw_timer_enable_clock(int n, int enable) |
| { |
| volatile uint32_t *reg; |
| uint32_t mask = 0; |
| |
| /* |
| * Mapping of timers to reg/mask is split into a few different ranges, |
| * some specific to individual chips. |
| */ |
| #if defined(CHIP_FAMILY_stm32f) |
| if (n == 1) { |
| reg = &STM32_RCC_APB2ENR; |
| mask = STM32_RCC_PB2_TIM1; |
| } |
| #elif defined(CHIP_FAMILY_stm32l) |
| if (n >= 9 && n <= 11) { |
| reg = &STM32_RCC_APB2ENR; |
| mask = STM32_RCC_PB2_TIM9 << (n - 9); |
| } |
| #endif |
| |
| if (n >= 2 && n <= 7) { |
| reg = &STM32_RCC_APB1ENR; |
| mask = STM32_RCC_PB1_TIM2 << (n - 2); |
| } |
| |
| if (!mask) |
| return; |
| |
| if (enable) |
| *reg |= mask; |
| else |
| *reg &= ~mask; |
| } |
| |
| static void update_prescaler(void) |
| { |
| /* |
| * Pre-scaler value : |
| * TIM_CLOCK_LSB is counting microseconds; |
| * TIM_CLOCK_MSB is counting every TIM_CLOCK_LSB overflow. |
| * |
| * This will take effect at the next update event (when the current |
| * prescaler counter ticks down, or if forced via EGR). |
| */ |
| STM32_TIM_PSC(TIM_CLOCK_MSB) = 0; |
| STM32_TIM_PSC(TIM_CLOCK_LSB) = (clock_get_freq() / SECOND) - 1; |
| } |
| DECLARE_HOOK(HOOK_FREQ_CHANGE, update_prescaler, HOOK_PRIO_DEFAULT); |
| |
| int __hw_clock_source_init(uint32_t start_t) |
| { |
| /* |
| * we use 2 chained 16-bit counters to emulate a 32-bit one : |
| * TIM_CLOCK_MSB is the MSB (Slave) |
| * TIM_CLOCK_LSB is the LSB (Master) |
| */ |
| |
| /* Enable TIM_CLOCK_MSB and TIM_CLOCK_LSB clocks */ |
| __hw_timer_enable_clock(TIM_CLOCK_MSB, 1); |
| __hw_timer_enable_clock(TIM_CLOCK_LSB, 1); |
| |
| /* |
| * Timer configuration : Upcounter, counter disabled, update event only |
| * on overflow. |
| */ |
| STM32_TIM_CR1(TIM_CLOCK_MSB) = 0x0004; |
| STM32_TIM_CR1(TIM_CLOCK_LSB) = 0x0004; |
| /* |
| * TIM_CLOCK_LSB (master mode) generates a periodic trigger signal on |
| * each UEV |
| */ |
| STM32_TIM_CR2(TIM_CLOCK_MSB) = 0x0000; |
| STM32_TIM_CR2(TIM_CLOCK_LSB) = 0x0020; |
| |
| STM32_TIM_SMCR(TIM_CLOCK_MSB) = 0x0007 | |
| (TSMAP(TIM_CLOCK_MSB, TIM_CLOCK_LSB) << 4); |
| STM32_TIM_SMCR(TIM_CLOCK_LSB) = 0x0000; |
| |
| /* Auto-reload value : 16-bit free-running counters */ |
| STM32_TIM_ARR(TIM_CLOCK_MSB) = 0xffff; |
| STM32_TIM_ARR(TIM_CLOCK_LSB) = 0xffff; |
| |
| /* Update prescaler */ |
| update_prescaler(); |
| |
| /* Reload the pre-scaler */ |
| STM32_TIM_EGR(TIM_CLOCK_MSB) = 0x0001; |
| STM32_TIM_EGR(TIM_CLOCK_LSB) = 0x0001; |
| |
| /* Set up the overflow interrupt on TIM_CLOCK_MSB */ |
| STM32_TIM_DIER(TIM_CLOCK_MSB) = 0x0001; |
| STM32_TIM_DIER(TIM_CLOCK_LSB) = 0x0000; |
| |
| /* Start counting */ |
| STM32_TIM_CR1(TIM_CLOCK_MSB) |= 1; |
| STM32_TIM_CR1(TIM_CLOCK_LSB) |= 1; |
| |
| /* Override the count with the start value now that counting has |
| * started. */ |
| __hw_clock_source_set(start_t); |
| |
| /* Enable timer interrupts */ |
| task_enable_irq(IRQ_MSB); |
| task_enable_irq(IRQ_LSB); |
| |
| return IRQ_LSB; |
| } |
| |
| #ifdef CONFIG_WATCHDOG_HELP |
| |
| 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(IRQ_WD)(void) __attribute__((naked)); |
| void IRQ_HANDLER(IRQ_WD)(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_, IRQ_WD, ) |
| __attribute__((section(".rodata.irqprio"))) |
| = {IRQ_WD, 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 */ |
| __hw_timer_enable_clock(TIM_WATCHDOG, 1); |
| |
| /* |
| * Timer configuration : Down counter, counter disabled, update |
| * event only on overflow. |
| */ |
| timer->cr1 = 0x0014 | (1 << 7); |
| |
| /* TIM (slave mode) uses TIM_CLOCK_LSB as internal trigger */ |
| timer->smcr = 0x0007 | (TSMAP(TIM_WATCHDOG, TIM_CLOCK_LSB) << 4); |
| |
| /* |
| * The auto-reload value is based on the period between rollovers for |
| * TIM_CLOCK_LSB. Since TIM_CLOCK_LSB runs at 1MHz, it will overflow |
| * in 65.536ms. We divide our required watchdog period by this amount |
| * to obtain the number of times TIM_CLOCK_LSB can overflow before we |
| * generate an interrupt. |
| */ |
| timer->arr = timer->cnt = WATCHDOG_PERIOD_MS * MSEC / (1 << 16); |
| |
| /* count on every TIM_CLOCK_LSB 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(IRQ_WD); |
| } |
| |
| void hwtimer_reset_watchdog(void) |
| { |
| struct timer_ctlr *timer = (struct timer_ctlr *)TIM_WD_BASE; |
| |
| timer->cnt = timer->arr; |
| } |
| |
| #endif /* defined(CONFIG_WATCHDOG) */ |