[stm32f0xx] Add timer capture driver.

Right now only capture is supported with the timers.  As more modes
are supported, the lower level bits of the driver should be
refactored.
This commit is contained in:
Erik Gilling
2017-12-22 11:12:29 -08:00
committed by Erik Gilling
parent cf3b7a15df
commit 8ae85bd301
3 changed files with 397 additions and 54 deletions

View File

@@ -1,5 +1,5 @@
/*
* Copyright (c) 2012 Travis Geiselbrecht
* Copyright (c) 2017 The Fuchsia Authors.
*
* Permission is hereby granted, free of charge, to any person obtaining
* a copy of this software and associated documentation files
@@ -20,61 +20,38 @@
* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
*/
#include <debug.h>
#include <trace.h>
#ifndef __PLATFORM_STM32_TIMER_CAPTURE_H
#define __PLATFORM_STM32_TIMER_CAPTURE_H
#include <err.h>
#include <sys/types.h>
#include <kernel/thread.h>
#include <platform.h>
#include <platform/timer.h>
#include <arch/arm/cm.h>
#include <kernel/spinlock.h>
#include <stdbool.h>
#include <stdint.h>
#include <stm32f0xx.h>
#define LOCAL_TRACE 0
#define STM32_TIMER_CAPTURE_CHAN_FLAG_ENABLE (1 << 0)
#define STM32_TIMER_CAPTURE_CHAN_FLAG_RISING (1 << 1)
#define STM32_TIMER_CAPTURE_CHAN_FLAG_FALLING (1 << 2)
#define TIME_BASE_COUNT 0xffff
#define TICK_RATE 1000000
typedef struct {
uint32_t flags;
bool (*cb)(uint64_t val);
} stm32_timer_capture_channel_t;
static void stm32_tim_irq(uint num)
{
TRACEF("tim irq %d\n", num);
PANIC_UNIMPLEMENTED;
}
typedef struct stm32_timer_config_ stm32_timer_config_t;
void stm32_TIM3_IRQ(void)
{
stm32_tim_irq(3);
}
typedef struct {
const stm32_timer_config_t *config;
stm32_timer_capture_channel_t chan[4];
void stm32_TIM4_IRQ(void)
{
stm32_tim_irq(4);
}
volatile uint64_t overflow;
spin_lock_t overflow_lock;
} stm32_timer_capture_t;
void stm32_TIM5_IRQ(void)
{
stm32_tim_irq(5);
}
// Set tc->chan[] cb and flags before calling.
status_t stm32_timer_capture_setup(stm32_timer_capture_t *tc, int timer, uint16_t prescaler);
void stm32_TIM6_IRQ(void)
{
stm32_tim_irq(6);
}
uint64_t stm32_timer_capture_get_counter(stm32_timer_capture_t *tc);
void stm32_TIM7_IRQ(void)
{
stm32_tim_irq(7);
}
/* time base */
void stm32_TIM2_IRQ(void)
{
stm32_tim_irq(2);
}
void stm32_timer_early_init(void)
{
}
void stm32_timer_init(void)
{
}
#endif // __PLATFORM_STM32_TIMER_CAPTURE_H

View File

@@ -35,14 +35,14 @@ MODULE_SRCS += \
$(LOCAL_DIR)/i2c.c \
$(LOCAL_DIR)/rcc.c \
$(LOCAL_DIR)/spi.c \
$(LOCAL_DIR)/timer.c \
$(LOCAL_DIR)/timer_capture.c \
$(LOCAL_DIR)/uart.c \
$(LOCAL_DIR)/usbc.c \
$(LOCAL_DIR)/vectab.c
# use a two segment memory layout, where all of the read-only sections
# of the binary reside in rom, and the read/write are in memory. The
# ROMBASE, MEMBASE, and MEMSIZE make variables are required to be set
# use a two segment memory layout, where all of the read-only sections
# of the binary reside in rom, and the read/write are in memory. The
# ROMBASE, MEMBASE, and MEMSIZE make variables are required to be set
# for the linker script to be generated properly.
#
LINKER_SCRIPT += \

View File

@@ -0,0 +1,366 @@
/*
* Copyright (c) 2017 The Fuchsia Authors.
*
* Permission is hereby granted, free of charge, to any person obtaining
* a copy of this software and associated documentation files
* (the "Software"), to deal in the Software without restriction,
* including without limitation the rights to use, copy, modify, merge,
* publish, distribute, sublicense, and/or sell copies of the Software,
* and to permit persons to whom the Software is furnished to do so,
* subject to the following conditions:
*
* The above copyright notice and this permission notice shall be
* included in all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
*/
#include <platform/timer_capture.h>
#include <arch/arm/cm.h>
#include <assert.h>
#include <platform/rcc.h>
typedef enum {
STM32_TIM_NOT_FOUND = -1,
STM32_TIM1_INDEX = 0,
STM32_TIM2_INDEX,
STM32_TIM3_INDEX,
STM32_TIM6_INDEX,
STM32_TIM7_INDEX,
STM32_TIM14_INDEX,
STM32_TIM15_INDEX,
STM32_TIM16_INDEX,
STM32_TIM17_INDEX,
STM32_NUM_TIMERS,
} stm32_timer_index_t;
typedef TIM_TypeDef stm32_timer_regs_t;
#define STM32_TIMER_FLAGS_32_BIT (1 << 0)
typedef struct stm32_timer_config_ {
stm32_timer_regs_t *regs;
uint32_t flags;
stm32_rcc_clk_t clock;
int irq;
} stm32_timer_config_t;
static const stm32_timer_config_t stm32_timer_config[] = {
[STM32_TIM1_INDEX] = {
.regs = TIM1,
.flags = 0,
.clock = STM32_RCC_CLK_TIM1,
// TIM1 has two interrupts. This one and TIM1_BRK_UP_TRG_COM_IRQn.
// This will need to be supported when more than just capture is
// supported.
.irq = TIM1_CC_IRQn,
},
[STM32_TIM2_INDEX] = {
.regs = TIM2,
.flags = STM32_TIMER_FLAGS_32_BIT,
.clock = STM32_RCC_CLK_TIM2,
.irq = TIM2_IRQn,
},
[STM32_TIM3_INDEX] = {
.regs = TIM3,
.flags = 0,
.clock = STM32_RCC_CLK_TIM3,
.irq = TIM3_IRQn,
},
[STM32_TIM6_INDEX] = {
.regs = TIM6,
.flags = 0,
.clock = STM32_RCC_CLK_TIM6,
.irq = TIM6_IRQn,
},
[STM32_TIM7_INDEX] = {
.regs = TIM7,
.flags = 0,
.clock = STM32_RCC_CLK_TIM7,
.irq = TIM7_IRQn,
},
[STM32_TIM14_INDEX] = {
.regs = TIM14,
.flags = 0,
.clock = STM32_RCC_CLK_TIM14,
.irq = TIM14_IRQn,
},
[STM32_TIM15_INDEX] = {
.regs = TIM15,
.flags = 0,
.clock = STM32_RCC_CLK_TIM15,
.irq = TIM15_IRQn,
},
[STM32_TIM16_INDEX] = {
.regs = TIM16,
.flags = 0,
.clock = STM32_RCC_CLK_TIM16,
.irq = TIM16_IRQn,
},
[STM32_TIM17_INDEX] = {
.regs = TIM17,
.flags = 0,
.clock = STM32_RCC_CLK_TIM17,
.irq = TIM17_IRQn,
},
};
static stm32_timer_capture_t *stm32_timer_capture_data[STM32_NUM_TIMERS];
static void stm32_timer_enable(const stm32_timer_config_t *config) {
stm32_rcc_set_enable(config->clock, true);
}
static stm32_timer_index_t stm32_timer_get_index(int timer) {
switch (timer) {
case 1:
return STM32_TIM1_INDEX;
case 2:
return STM32_TIM2_INDEX;
case 3:
return STM32_TIM3_INDEX;
case 6:
return STM32_TIM6_INDEX;
case 7:
return STM32_TIM7_INDEX;
case 14:
return STM32_TIM14_INDEX;
case 15:
return STM32_TIM15_INDEX;
case 16:
return STM32_TIM16_INDEX;
case 17:
return STM32_TIM17_INDEX;
default:
return STM32_TIM_NOT_FOUND;
}
}
static const stm32_timer_config_t *stm32_timer_get_config(int timer) {
stm32_timer_index_t index = stm32_timer_get_index(timer);
if (index == STM32_TIM_NOT_FOUND) {
return NULL;
}
return &stm32_timer_config[index];
}
static bool stm32_timer_is_32_bit(int timer) {
switch (timer) {
case 2:
return true;
default:
return false;
}
}
static void stm32_timer_capture_setup_chan(stm32_timer_capture_t *tc, int chan) {
stm32_timer_regs_t *regs = tc->config->regs;
volatile uint32_t *ccmr = chan & 0x2 ? &regs->CCMR2 : &regs->CCMR1;
int shift = 8 * (chan & 1);
uint32_t val = *ccmr;
val &= 0xff << shift;
val |= TIM_CCMR1_CC1S_0 << shift;
*ccmr = val;
uint32_t config = TIM_CCER_CC1E;
uint32_t flags = tc->chan[chan].flags;
if (flags & STM32_TIMER_CAPTURE_CHAN_FLAG_FALLING) {
config |= TIM_CCER_CC1P;
if (flags & STM32_TIMER_CAPTURE_CHAN_FLAG_RISING) {
config |= TIM_CCER_CC1NP;
}
}
shift = 4 * (chan & 3);
val = regs->CCER;
val &= 0xf << shift;
val |= config << shift;
regs->CCER = val;
}
static uint32_t stm32_timer_get_ccr(stm32_timer_capture_t *tc, int chan) {
assert(0 <= chan && chan <= 3);
switch (chan) {
case 0:
return tc->config->regs->CCR1;
case 1:
return tc->config->regs->CCR2;
case 2:
return tc->config->regs->CCR3;
default: // 3
return tc->config->regs->CCR4;
}
}
static uint64_t stm32_timer_inc_overflow(stm32_timer_capture_t *tc, uint64_t overflow) {
uint64_t inc;
if (tc->config->flags & STM32_TIMER_FLAGS_32_BIT) {
inc = 0x100000000ULL;
} else {
inc = 0x10000ULL;
}
return overflow + inc;
}
static uint32_t stm32_timer_median_val(stm32_timer_capture_t *tc) {
if (tc->config->flags & STM32_TIMER_FLAGS_32_BIT) {
return 0x7fffffff;
} else {
return 0x7fff;
}
}
// Assumes interrupts are disabled.
static uint64_t stm32_timer_calc_value(stm32_timer_capture_t *tc, uint32_t sr, uint32_t val) {
uint32_t overflow = tc->overflow;
// Since we could be processing an overflow and capture interrupts at the
// same time, we don't know the ordering of the two. Here we assume
// that if the capture event occurred in the lower half of the counter
// range, the overflow happened before the capture.
if (sr & TIM_SR_UIF && val < stm32_timer_median_val(tc)) {
overflow = stm32_timer_inc_overflow(tc, overflow);
}
return overflow | val;
}
static bool stm32_timer_capture_chan_irq(stm32_timer_capture_t *tc, uint32_t sr, int chan) {
if (tc->chan[chan].cb == NULL) {
return false;
}
if (sr & (TIM_SR_CC1IF << chan)) {
uint32_t val = stm32_timer_get_ccr(tc, chan);
return tc->chan[chan].cb(stm32_timer_calc_value(tc, sr, val));
}
return false;
}
static void stm32_timer_capture_irq(stm32_timer_index_t index) {
arm_cm_irq_entry();
bool resched = false;
stm32_timer_capture_t *tc = stm32_timer_capture_data[index];
if (tc == NULL) {
goto out;
}
uint32_t sr = tc->config->regs->SR;
uint32_t irq_ack = ~0U;
// Since these read their corresponding CCR registers, the status flags
// do not need to be explicitly cleared.
resched |= stm32_timer_capture_chan_irq(tc, sr, 0);
resched |= stm32_timer_capture_chan_irq(tc, sr, 1);
resched |= stm32_timer_capture_chan_irq(tc, sr, 2);
resched |= stm32_timer_capture_chan_irq(tc, sr, 3);
// We process overflow after compares in order to handle overflow race
// detection there.
spin_lock_saved_state_t state;
spin_lock_irqsave(&tc->overflow_lock, state);
if (sr & TIM_SR_UIF) {
tc->overflow = stm32_timer_inc_overflow(tc, tc->overflow);
irq_ack &= ~TIM_SR_UIF;
}
tc->config->regs->SR = irq_ack;
spin_unlock_irqrestore(&tc->overflow_lock, state);
out:
arm_cm_irq_exit(resched);
}
void stm32_TIM1_IRQ(void) {
stm32_timer_capture_irq(STM32_TIM1_INDEX);
}
void stm32_TIM2_IRQ(void) {
stm32_timer_capture_irq(STM32_TIM2_INDEX);
}
void stm32_TIM3_IRQ(void) {
stm32_timer_capture_irq(STM32_TIM3_INDEX);
}
void stm32_TIM6_IRQ(void) {
stm32_timer_capture_irq(STM32_TIM6_INDEX);
}
void stm32_TIM7_IRQ(void) {
stm32_timer_capture_irq(STM32_TIM7_INDEX);
}
void stm32_TIM14_IRQ(void) {
stm32_timer_capture_irq(STM32_TIM14_INDEX);
}
void stm32_TIM15_IRQ(void) {
stm32_timer_capture_irq(STM32_TIM15_INDEX);
}
void stm32_TIM16_IRQ(void) {
stm32_timer_capture_irq(STM32_TIM16_INDEX);
}
void stm32_TIM17_IRQ(void) {
stm32_timer_capture_irq(STM32_TIM17_INDEX);
}
status_t stm32_timer_capture_setup(stm32_timer_capture_t *tc, int timer, uint16_t prescaler) {
tc->config = stm32_timer_get_config(timer);
if (tc->config == NULL) {
return ERR_NOT_FOUND;
}
stm32_timer_enable(tc->config);
stm32_timer_capture_data[stm32_timer_get_index(timer)] = tc;
tc->overflow = 0;
spin_lock_init(&tc->overflow_lock);
tc->config->regs->CR1 = 0;
tc->config->regs->PSC = prescaler;
uint32_t dier = TIM_DIER_UIE;
int i;
for (i = 0; i < 4; i++) {
if (tc->chan[i].flags & STM32_TIMER_CAPTURE_CHAN_FLAG_ENABLE) {
dier |= TIM_DIER_CC1IE << i;
stm32_timer_capture_setup_chan(tc, i);
}
}
tc->config->regs->DIER = dier;
tc->config->regs->CR1 |= TIM_CR1_CEN;
NVIC_EnableIRQ(tc->config->irq);
return NO_ERROR;
}
uint64_t stm32_timer_capture_get_counter(stm32_timer_capture_t *tc) {
// Protect against tc->overflow being updated while we calculate the value.
spin_lock_saved_state_t state;
spin_lock_irqsave(&tc->overflow_lock, state);
uint32_t cnt = tc->config->regs->CNT;
uint32_t sr = tc->config->regs->SR;
uint64_t value = stm32_timer_calc_value(tc, sr, cnt);
spin_unlock_irqrestore(&tc->overflow_lock, state);
return value;
}
void stm32_timer_init(void) {
}
void stm32_timer_early_init(void) {
}