290 lines
7.1 KiB
C
290 lines
7.1 KiB
C
/* Copyright 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 "clock-f.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_STM32F0) || defined(CHIP_FAMILY_STM32H7)
|
|
if (n == 1) {
|
|
reg = &STM32_RCC_APB2ENR;
|
|
mask = STM32_RCC_PB2_TIM1;
|
|
}
|
|
#elif defined(CHIP_FAMILY_STM32L) || defined(CHIP_FAMILY_STM32F4)
|
|
if (n >= 9 && n <= 11) {
|
|
reg = &STM32_RCC_APB2ENR;
|
|
mask = STM32_RCC_PB2_TIM9 << (n - 9);
|
|
}
|
|
#endif
|
|
|
|
#if defined(CHIP_FAMILY_STM32F0) || defined(CHIP_FAMILY_STM32H7)
|
|
if (n >= 15 && n <= 17) {
|
|
reg = &STM32_RCC_APB2ENR;
|
|
mask = STM32_RCC_PB2_TIM15 << (n - 15);
|
|
}
|
|
#endif
|
|
|
|
#if defined(CHIP_FAMILY_STM32F0) || defined(CHIP_FAMILY_STM32F3) || \
|
|
defined(CHIP_FAMILY_STM32H7)
|
|
if (n == 14) {
|
|
reg = &STM32_RCC_APB1ENR;
|
|
mask = STM32_RCC_PB1_TIM14;
|
|
}
|
|
#endif
|
|
|
|
#if defined(CHIP_FAMILY_STM32F3) || defined(CHIP_FAMILY_STM32H7)
|
|
if (n == 12 || n == 13) {
|
|
reg = &STM32_RCC_APB1ENR;
|
|
mask = STM32_RCC_PB1_TIM12 << (n - 12);
|
|
}
|
|
#endif
|
|
#if defined(CHIP_FAMILY_STM32F3)
|
|
if (n == 18) {
|
|
reg = &STM32_RCC_APB1ENR;
|
|
mask = STM32_RCC_PB1_TIM18;
|
|
}
|
|
if (n == 19) {
|
|
reg = &STM32_RCC_APB2ENR;
|
|
mask = STM32_RCC_PB2_TIM19;
|
|
}
|
|
#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;
|
|
}
|
|
|
|
#if defined(CHIP_FAMILY_STM32L) || defined(CHIP_FAMILY_STM32L4) || \
|
|
defined(CHIP_FAMILY_STM32H7)
|
|
/* for families using a variable clock feeding the timer */
|
|
static void update_prescaler(void)
|
|
{
|
|
uint32_t t;
|
|
/*
|
|
* Pre-scaler value :
|
|
* the timer is incrementing every microsecond
|
|
*/
|
|
STM32_TIM_PSC(TIM_CLOCK32) = (clock_get_timer_freq() / SECOND) - 1;
|
|
/*
|
|
* Forcing reloading the pre-scaler,
|
|
* but try to maintain a sensible time-keeping while triggering
|
|
* the update event.
|
|
*/
|
|
interrupt_disable();
|
|
/* Ignore the next update */
|
|
STM32_TIM_DIER(TIM_CLOCK32) &= ~0x0001;
|
|
/*
|
|
* prepare to reload the counter with the current value
|
|
* to avoid rolling backward the microsecond counter.
|
|
*/
|
|
t = STM32_TIM32_CNT(TIM_CLOCK32) + 1;
|
|
/* issue an update event, reloads the pre-scaler and the counter */
|
|
STM32_TIM_EGR(TIM_CLOCK32) = 0x0001;
|
|
/* clear the 'spurious' update unless we were going to roll-over */
|
|
if (t)
|
|
STM32_TIM_SR(TIM_CLOCK32) = ~1;
|
|
/* restore a sensible time value */
|
|
STM32_TIM32_CNT(TIM_CLOCK32) = t;
|
|
/* restore roll-over events */
|
|
STM32_TIM_DIER(TIM_CLOCK32) |= 0x0001;
|
|
interrupt_enable();
|
|
|
|
#ifdef CONFIG_WATCHDOG_HELP
|
|
/* Watchdog timer runs at 1KHz */
|
|
STM32_TIM_PSC(TIM_WATCHDOG) =
|
|
(clock_get_timer_freq() / SECOND * MSEC)- 1;
|
|
#endif /* CONFIG_WATCHDOG_HELP */
|
|
}
|
|
DECLARE_HOOK(HOOK_FREQ_CHANGE, update_prescaler, HOOK_PRIO_DEFAULT);
|
|
#endif /* CHIP_FAMILY_STM32L || CHIP_FAMILY_STM32L4 || CHIP_FAMILY_STM32H7 */
|
|
|
|
int __hw_clock_source_init(uint32_t start_t)
|
|
{
|
|
/* Enable TIM peripheral block clocks */
|
|
__hw_timer_enable_clock(TIM_CLOCK32, 1);
|
|
/* Delay 1 APB clock cycle after the clock is enabled */
|
|
clock_wait_bus_cycles(BUS_APB, 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 to increment every microsecond */
|
|
STM32_TIM_PSC(TIM_CLOCK32) = (clock_get_timer_freq() / SECOND) - 1;
|
|
|
|
/* 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);
|
|
}
|
|
|
|
#ifdef CONFIG_WATCHDOG_HELP
|
|
|
|
#define IRQ_WD IRQ_TIM(TIM_WATCHDOG)
|
|
|
|
void __keep watchdog_check(uint32_t excep_lr, uint32_t excep_sp)
|
|
{
|
|
/* clear status */
|
|
STM32_TIM_SR(TIM_WATCHDOG) = 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. */
|
|
"push {r0, lr}\n"
|
|
"bl watchdog_check\n"
|
|
"pop {r0,pc}\n");
|
|
}
|
|
const struct irq_priority __keep IRQ_PRIORITY(IRQ_WD)
|
|
__attribute__((section(".rodata.irqprio")))
|
|
= {IRQ_WD, 0}; /* put the watchdog at the highest
|
|
priority */
|
|
|
|
void hwtimer_setup_watchdog(void)
|
|
{
|
|
/* Enable clock */
|
|
__hw_timer_enable_clock(TIM_WATCHDOG, 1);
|
|
/* Delay 1 APB clock cycle after the clock is enabled */
|
|
clock_wait_bus_cycles(BUS_APB, 1);
|
|
|
|
/*
|
|
* Timer configuration : Up counter, counter disabled, update
|
|
* event only on overflow.
|
|
*/
|
|
STM32_TIM_CR1(TIM_WATCHDOG) = 0x0004;
|
|
/* No special configuration */
|
|
STM32_TIM_CR2(TIM_WATCHDOG) = 0x0000;
|
|
STM32_TIM_SMCR(TIM_WATCHDOG) = 0x0000;
|
|
|
|
/* AUto-reload value */
|
|
STM32_TIM_ARR(TIM_WATCHDOG) = CONFIG_AUX_TIMER_PERIOD_MS;
|
|
|
|
/* Update prescaler: watchdog timer runs at 1KHz */
|
|
STM32_TIM_PSC(TIM_WATCHDOG) =
|
|
(clock_get_timer_freq() / SECOND * MSEC) - 1;
|
|
|
|
/* Reload the pre-scaler */
|
|
STM32_TIM_EGR(TIM_WATCHDOG) = 0x0001;
|
|
|
|
/* setup the overflow interrupt */
|
|
STM32_TIM_DIER(TIM_WATCHDOG) = 0x0001;
|
|
STM32_TIM_SR(TIM_WATCHDOG) = 0;
|
|
|
|
/* Start counting */
|
|
STM32_TIM_CR1(TIM_WATCHDOG) |= 1;
|
|
|
|
/* Enable timer interrupts */
|
|
task_enable_irq(IRQ_WD);
|
|
}
|
|
|
|
void hwtimer_reset_watchdog(void)
|
|
{
|
|
STM32_TIM_CNT(TIM_WATCHDOG) = 0x0000;
|
|
}
|
|
|
|
#endif /* CONFIG_WATCHDOG_HELP */
|