stm32: add 32-bit timer support

Some STM32 variants have a 32-bit timer in addition the bunch of 16-bit
timers.
Add the option to use the 32-bit timer as the system clock source to
lower the overhead of the timer code compared to a pair of 16-bit
timers.

Signed-off-by: Vincent Palatin <vpalatin@chromium.org>

BRANCH=none
BUG=none
TEST=run the EC on STM32F072 Discovery board with 32-bit TIM2 as the
clock source.

Change-Id: If55c4e23a3f68dd8f6ca32e93f3a27c1743c767b
Reviewed-on: https://chromium-review.googlesource.com/189861
Reviewed-by: Vic Yang <victoryang@chromium.org>
Commit-Queue: Vincent Palatin <vpalatin@chromium.org>
Tested-by: Vincent Palatin <vpalatin@chromium.org>
This commit is contained in:
Vincent Palatin
2014-03-13 12:38:55 -07:00
committed by chrome-internal-fetch
parent c00675b909
commit 8dc20291b2
3 changed files with 172 additions and 1 deletions

View File

@@ -22,8 +22,10 @@ endif
FLASH_FAMILY=$(subst stm32f0,stm32f,$(CHIP_FAMILY))
# STM32F0xx chips will re-use STM32L I2C code
I2C_FAMILY=$(subst stm32f0,stm32l,$(CHIP_FAMILY))
# Select between 16-bit and 32-bit timer for clock source
TIMER_TYPE=$(if $(CONFIG_STM_HWTIMER32),32,)
chip-y=dma.o hwtimer.o system.o uart.o
chip-y=dma.o hwtimer$(TIMER_TYPE).o system.o uart.o
chip-y+=jtag-$(CHIP_FAMILY).o clock-$(CHIP_FAMILY).o gpio-$(CHIP_FAMILY).o
chip-$(CONFIG_SPI)+=spi.o
chip-$(CONFIG_I2C)+=i2c-$(I2C_FAMILY).o

161
chip/stm32/hwtimer32.c Normal file
View File

@@ -0,0 +1,161 @@
/* 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.
*/
/* Hardware 32-bit timer 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"
#define IRQ_TIM(n) CONCAT2(STM32_IRQ_TIM, n)
void __hw_clock_event_set(uint32_t deadline)
{
/* set the match on the deadline */
STM32_TIM32_CCR1(TIM_CLOCK32) = deadline;
/* Clear the match flags */
STM32_TIM_SR(TIM_CLOCK32) = ~2;
/* Set the match interrupt */
STM32_TIM_DIER(TIM_CLOCK32) |= 2;
}
uint32_t __hw_clock_event_get(void)
{
return STM32_TIM32_CCR1(TIM_CLOCK32);
}
void __hw_clock_event_clear(void)
{
/* Disable the match interrupts */
STM32_TIM_DIER(TIM_CLOCK32) &= ~2;
}
uint32_t __hw_clock_source_read(void)
{
return STM32_TIM32_CNT(TIM_CLOCK32);
}
void __hw_clock_source_set(uint32_t ts)
{
STM32_TIM32_CNT(TIM_CLOCK32) = ts;
}
void __hw_clock_source_irq(void)
{
uint32_t stat_tim = STM32_TIM_SR(TIM_CLOCK32);
/* Clear status */
STM32_TIM_SR(TIM_CLOCK32) = 0;
/*
* Find expired timers and set the new timer deadline
* signal overflow if the update interrupt flag is set.
*/
process_timers(stat_tim & 0x01);
}
DECLARE_IRQ(IRQ_TIM(TIM_CLOCK32), __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) || defined(CHIP_FAMILY_STM32F0)
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 defined(CHIP_FAMILY_STM32F0)
if (n >= 15 && n <= 17) {
reg = &STM32_RCC_APB2ENR;
mask = STM32_RCC_PB2_TIM15 << (n - 15);
}
if (n == 14) {
reg = &STM32_RCC_APB1ENR;
mask = STM32_RCC_PB1_TIM14;
}
#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 :
* the timer is incrementing every microsecond
*
* 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_CLOCK32) = (clock_get_freq() / SECOND) - 1;
}
DECLARE_HOOK(HOOK_FREQ_CHANGE, update_prescaler, HOOK_PRIO_DEFAULT);
int __hw_clock_source_init(uint32_t start_t)
{
/* Enable TIM peripheral block clocks */
__hw_timer_enable_clock(TIM_CLOCK32, 1);
/*
* Timer configuration : Upcounter, counter disabled, update event only
* on overflow.
*/
STM32_TIM_CR1(TIM_CLOCK32) = 0x0004;
/* No special configuration */
STM32_TIM_CR2(TIM_CLOCK32) = 0x0000;
STM32_TIM_SMCR(TIM_CLOCK32) = 0x0000;
/* Auto-reload value : 32-bit free-running counter */
STM32_TIM32_ARR(TIM_CLOCK32) = 0xffffffff;
/* Update prescaler */
update_prescaler();
/* Reload the pre-scaler */
STM32_TIM_EGR(TIM_CLOCK32) = 0x0001;
/* Set up the overflow interrupt */
STM32_TIM_DIER(TIM_CLOCK32) = 0x0001;
/* Start counting */
STM32_TIM_CR1(TIM_CLOCK32) |= 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_TIM(TIM_CLOCK32));
return IRQ_TIM(TIM_CLOCK32);
}

View File

@@ -220,6 +220,8 @@
#define STM32_TIM_REG(n, offset) \
REG16(STM32_TIM_BASE(n) + (offset))
#define STM32_TIM_REG32(n, offset) \
REG32(STM32_TIM_BASE(n) + (offset))
#define STM32_TIM_CR1(n) STM32_TIM_REG(n, 0x00)
#define STM32_TIM_CR2(n) STM32_TIM_REG(n, 0x04)
@@ -242,6 +244,12 @@
#define STM32_TIM_DMAR(n) STM32_TIM_REG(n, 0x4C)
#define STM32_TIM_OR(n) STM32_TIM_REG(n, 0x50)
#define STM32_TIM32_CNT(n) STM32_TIM_REG32(n, 0x24)
#define STM32_TIM32_ARR(n) STM32_TIM_REG32(n, 0x2C)
#define STM32_TIM32_CCR1(n) STM32_TIM_REG32(n, 0x34)
#define STM32_TIM32_CCR2(n) STM32_TIM_REG32(n, 0x38)
#define STM32_TIM32_CCR3(n) STM32_TIM_REG32(n, 0x3C)
#define STM32_TIM32_CCR4(n) STM32_TIM_REG32(n, 0x40)
/* Timer registers as struct */
struct timer_ctlr {
unsigned cr1;