diff options
author | Vincent Palatin <vpalatin@chromium.org> | 2012-02-01 22:44:09 +0000 |
---|---|---|
committer | Vincent Palatin <vpalatin@chromium.org> | 2012-02-01 22:49:22 +0000 |
commit | 75b2bcf9b440735208851cf53bbee4f5e30acd38 (patch) | |
tree | 71d90cd72ecefe44742d39a887be540b9e6c9805 /chip | |
parent | 6986ea134c7f526b8c251dfcc2a3e77ccd8329ba (diff) | |
download | chrome-ec-75b2bcf9b440735208851cf53bbee4f5e30acd38.tar.gz |
stm32l: add timer support
As the STM32L doesn't have any 32-bit timer, we use 2 chained 16-bit
counters to emulate a 32-bit one :
* TIM2 is the MSB half-word (Slave timer)
* TIM3 is the LSB half-word (Master time)
Signed-off-by: Vincent Palatin <vpalatin@chromium.org>
BUG=None
TEST=run timer_calib and timer_dos on the Discovery board, and check
waitms and gettime console functions against wall clock.
Change-Id: I8917207384d967fd87321797856e3d58b237f837
Diffstat (limited to 'chip')
-rw-r--r-- | chip/stm32l/hwtimer.c | 113 |
1 files changed, 110 insertions, 3 deletions
diff --git a/chip/stm32l/hwtimer.c b/chip/stm32l/hwtimer.c index c305d3f2b9..9f5a7c4921 100644 --- a/chip/stm32l/hwtimer.c +++ b/chip/stm32l/hwtimer.c @@ -8,28 +8,135 @@ #include <stdint.h> #include "board.h" +#include "common.h" #include "hwtimer.h" +#include "registers.h" #include "task.h" +#define US_PER_SECOND 1000000 + +/* Divider to get microsecond for the clock */ +#define CLOCKSOURCE_DIVIDER (CPU_CLOCK/US_PER_SECOND) + +static uint32_t last_deadline; + void __hw_clock_event_set(uint32_t deadline) { + last_deadline = deadline; + + if ((deadline >> 16) == STM32L_TIM_CNT(2)) { + /* we can set a match on the LSB only */ + STM32L_TIM_CCR1(3) = deadline & 0xffff; + /* disable MSB match */ + STM32L_TIM_DIER(2) &= ~2; + /* Clear the match flags */ + STM32L_TIM_SR(2) = ~2; + STM32L_TIM_SR(3) = ~2; + /* Set the match interrupt */ + STM32L_TIM_DIER(3) |= 2; + } else if ((deadline >> 16) > STM32L_TIM_CNT(2)) { + /* first set a match on the MSB */ + STM32L_TIM_CCR1(2) = deadline >> 16; + /* disable LSB match */ + STM32L_TIM_DIER(3) &= ~2; + /* Clear the match flags */ + STM32L_TIM_SR(2) = ~2; + STM32L_TIM_SR(3) = ~2; + /* Set the match interrupt */ + STM32L_TIM_DIER(2) |= 2; + } } uint32_t __hw_clock_event_get(void) { - return 0; + return last_deadline; } void __hw_clock_event_clear(void) { + /* Disable the match interrupts */ + STM32L_TIM_DIER(3) &= ~2; + STM32L_TIM_DIER(2) &= ~2; } uint32_t __hw_clock_source_read(void) { - return 0; + uint32_t hi; + uint32_t lo; + + /* ensure the two half-words are coherent */ + do { + hi = STM32L_TIM_CNT(2); + lo = STM32L_TIM_CNT(3); + } while (hi != STM32L_TIM_CNT(2)); + + return (hi << 16) | lo; +} + +static void __hw_clock_source_irq(void) +{ + uint32_t stat_tim2 = STM32L_TIM_SR(2); + + /* clear status */ + STM32L_TIM_SR(3) = 0; + STM32L_TIM_SR(2) = 0; + + /* + * Find expired timers and set the new timer deadline + * signal overflow if the 16-bit MSB counter has overflowed. + */ + process_timers(stat_tim2 & 0x01); } +DECLARE_IRQ(STM32L_IRQ_TIM2, __hw_clock_source_irq, 1); +DECLARE_IRQ(STM32L_IRQ_TIM3, __hw_clock_source_irq, 1); int __hw_clock_source_init(void) { - return -1; + /* + * we use 2 chained 16-bit counters to emulate a 32-bit one : + * TIM2 is the MSB (Slave) + * TIM3 is the LSB (Master) + */ + + /* Enable TIM2 and TIM3 clocks */ + STM32L_RCC_APB1ENR |= 0x3; + + /* + * Timer configuration : Upcounter, counter disabled, update event only + * on overflow. + */ + STM32L_TIM_CR1(2) = 0x0004; + STM32L_TIM_CR1(3) = 0x0004; + /* TIM3 (master mode) generates a periodic trigger signal on each UEV */ + STM32L_TIM_CR2(2) = 0x0000; + STM32L_TIM_CR2(3) = 0x0020; + /* TIM2 (slave mode) uses ITR2 as internal trigger */ + STM32L_TIM_SMCR(2) = 0x0027; + STM32L_TIM_SMCR(3) = 0x0000; + /* Auto-reload value : 16-bit free-running counters */ + STM32L_TIM_ARR(2) = 0xffff; + STM32L_TIM_ARR(3) = 0xffff; + /* Pre-scaler value : + * TIM3 is counting microseconds, TIM2 is counting every TIM3 overflow. + */ + STM32L_TIM_PSC(2) = 0; + STM32L_TIM_PSC(3) = CLOCKSOURCE_DIVIDER - 1; + + /* Reload the pre-scaler */ + STM32L_TIM_EGR(2) = 0x0000; + STM32L_TIM_EGR(3) = 0x0000; + + /* setup the overflow interrupt on TIM2 */ + STM32L_TIM_DIER(2) = 0x0001; + STM32L_TIM_DIER(3) = 0x0000; + + /* Start counting */ + STM32L_TIM_CR1(2) |= 1; + STM32L_TIM_CR1(3) |= 1; + + /* Enable timer interrupts */ + task_enable_irq(STM32L_IRQ_TIM2); + task_enable_irq(STM32L_IRQ_TIM3); + + return STM32L_IRQ_TIM3; } |