Files
lk/platform/stm32f0xx/timer_capture.c
Onath Claridge 3c4a0b96c1 [stm32f0xx] Fix type of overflow counter.
stm32_timer_calc_value was using uint32_t for the overflow counter,
ultimately causing timestamps to wrap as uint32_t.
2021-08-12 13:22:03 -07:00

374 lines
9.4 KiB
C

/*
* Copyright (c) 2017 The Fuchsia Authors.
*
* Use of this source code is governed by a MIT-style
* license that can be found in the LICENSE file or at
* https://opensource.org/licenses/MIT
*/
#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[] = {
#ifdef TIM1
[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,
},
#endif
#ifdef TIM2
[STM32_TIM2_INDEX] = {
.regs = TIM2,
.flags = STM32_TIMER_FLAGS_32_BIT,
.clock = STM32_RCC_CLK_TIM2,
.irq = TIM2_IRQn,
},
#endif
#ifdef TIM3
[STM32_TIM3_INDEX] = {
.regs = TIM3,
.flags = 0,
.clock = STM32_RCC_CLK_TIM3,
.irq = TIM3_IRQn,
},
#endif
#ifdef TIM6
[STM32_TIM6_INDEX] = {
.regs = TIM6,
.flags = 0,
.clock = STM32_RCC_CLK_TIM6,
.irq = TIM6_IRQn,
},
#endif
#ifdef TIM7
[STM32_TIM7_INDEX] = {
.regs = TIM7,
.flags = 0,
.clock = STM32_RCC_CLK_TIM7,
.irq = TIM7_IRQn,
},
#endif
#ifdef TIM14
[STM32_TIM14_INDEX] = {
.regs = TIM14,
.flags = 0,
.clock = STM32_RCC_CLK_TIM14,
.irq = TIM14_IRQn,
},
#endif
#ifdef TIM15
[STM32_TIM15_INDEX] = {
.regs = TIM15,
.flags = 0,
.clock = STM32_RCC_CLK_TIM15,
.irq = TIM15_IRQn,
},
#endif
#ifdef TIM16
[STM32_TIM16_INDEX] = {
.regs = TIM16,
.flags = 0,
.clock = STM32_RCC_CLK_TIM16,
.irq = TIM16_IRQn,
},
#endif
#ifdef TIM17
[STM32_TIM17_INDEX] = {
.regs = TIM17,
.flags = 0,
.clock = STM32_RCC_CLK_TIM17,
.irq = TIM17_IRQn,
},
#endif
};
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) {
uint64_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) {
assert(prescaler > 0);
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;
// The prescaler value applied by hardware is PSC + 1.
tc->config->regs->PSC = prescaler > 0 ? prescaler - 1 : 0;
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) {
}