712 lines
20 KiB
C
712 lines
20 KiB
C
/* systick.c - ARM systick device driver */
|
|
|
|
/*
|
|
* Copyright (c) 2013-2015 Wind River Systems, Inc.
|
|
*
|
|
* Redistribution and use in source and binary forms, with or without
|
|
* modification, are permitted provided that the following conditions are met:
|
|
*
|
|
* 1) Redistributions of source code must retain the above copyright notice,
|
|
* this list of conditions and the following disclaimer.
|
|
*
|
|
* 2) Redistributions in binary form must reproduce the above copyright notice,
|
|
* this list of conditions and the following disclaimer in the documentation
|
|
* and/or other materials provided with the distribution.
|
|
*
|
|
* 3) Neither the name of Wind River Systems nor the names of its contributors
|
|
* may be used to endorse or promote products derived from this software without
|
|
* specific prior written permission.
|
|
*
|
|
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
|
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
|
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
|
* ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
|
|
* LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
|
|
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
|
|
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
|
|
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
|
|
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
|
|
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
|
|
* POSSIBILITY OF SUCH DAMAGE.
|
|
*/
|
|
|
|
/*
|
|
DESCRIPTION
|
|
This module implements the VxMicro's CORTEX-M3 ARM's systick device driver.
|
|
It provides the standard VxMicro "system clock driver" interfaces.
|
|
|
|
The driver utilizes systick to provide kernel ticks.
|
|
|
|
\INTERNAL IMPLEMENTATION DETAILS
|
|
The systick device provides a 24-bit clear-on-write, decrementing,
|
|
wrap-on-zero counter. Only edge sensitive triggered interrupt is supported.
|
|
|
|
\INTERNAL PACKAGING DETAILS
|
|
The systick device driver is part of the microkernel in both a monolithic kernel
|
|
system and a split kernel system; it is not included in the nanokernel portion
|
|
of a split kernel.
|
|
|
|
The device driver is also part of a nanokernel-only system, but omits more
|
|
complex capabilities (such as tickless idle support) that are only used in
|
|
conjunction with a microkernel.
|
|
*/
|
|
|
|
#include <nanokernel.h>
|
|
#include <nanokernel/cpu.h>
|
|
#include <toolchain.h>
|
|
#include <sections.h>
|
|
#include <misc/__assert.h>
|
|
#include <clock_vars.h>
|
|
#include <drivers/system_timer.h>
|
|
|
|
#ifdef CONFIG_MICROKERNEL
|
|
|
|
#include <microkernel.h>
|
|
#include <cputype.h>
|
|
|
|
extern struct nano_stack _k_command_stack;
|
|
|
|
#endif /* CONFIG_MICROKERNEL */
|
|
|
|
/* running total of timer count */
|
|
static uint32_t clock_accumulated_count = 0;
|
|
|
|
/*
|
|
* A board support package's board.h header must provide definitions for the
|
|
* following constants:
|
|
*
|
|
* CONFIG_SYSTICK_CLOCK_FREQ
|
|
*
|
|
* This is the sysTick input clock frequency.
|
|
*/
|
|
|
|
#include <board.h>
|
|
|
|
/* defines */
|
|
|
|
/*
|
|
* When GDB_INFO is enabled, the handler installed in the vector table
|
|
* (__systick), can be found in systick_gdb.s. In this case, the handler
|
|
* in this file becomes _Systick() and will be called by __systick.
|
|
*/
|
|
#ifdef CONFIG_GDB_INFO
|
|
#define _TIMER_INT_HANDLER _real_timer_int_handler
|
|
#else
|
|
#define _TIMER_INT_HANDLER _timer_int_handler
|
|
#endif
|
|
|
|
#ifdef CONFIG_TICKLESS_IDLE
|
|
#define TIMER_MODE_PERIODIC 0 /* normal running mode */
|
|
#define TIMER_MODE_ONE_SHOT 1 /* emulated, since sysTick has 1 mode */
|
|
|
|
#define IDLE_NOT_TICKLESS 0 /* non-tickless idle mode */
|
|
#define IDLE_TICKLESS 1 /* tickless idle mode */
|
|
#endif /* CONFIG_TICKLESS_IDLE */
|
|
|
|
/* globals */
|
|
|
|
#ifdef CONFIG_INT_LATENCY_BENCHMARK
|
|
extern uint32_t _hw_irq_to_c_handler_latency;
|
|
#endif
|
|
|
|
#ifdef CONFIG_ADVANCED_POWER_MANAGEMENT
|
|
extern int32_t _NanoIdleValGet(void);
|
|
extern void _NanoIdleValClear(void);
|
|
extern void _sys_power_save_idle_exit(int32_t ticks);
|
|
#endif /* CONFIG_ADVANCED_POWER_MANAGEMENT */
|
|
|
|
#ifdef CONFIG_TICKLESS_IDLE
|
|
extern int32_t _sys_idle_elapsed_ticks;
|
|
#endif /* CONFIG_TICKLESS_IDLE */
|
|
|
|
/* locals */
|
|
|
|
#ifdef CONFIG_TICKLESS_IDLE
|
|
static uint32_t __noinit default_load_value; /* default count */
|
|
static uint32_t idle_original_count = 0;
|
|
static uint32_t __noinit max_system_ticks;
|
|
static uint32_t idle_original_ticks = 0;
|
|
static uint32_t __noinit max_load_value;
|
|
static uint32_t __noinit timer_idle_skew;
|
|
static unsigned char timer_mode = TIMER_MODE_PERIODIC;
|
|
static unsigned char idle_mode = IDLE_NOT_TICKLESS;
|
|
#endif /* CONFIG_TICKLESS_IDLE */
|
|
|
|
#if defined(CONFIG_TICKLESS_IDLE) || \
|
|
defined(CONFIG_SYSTEM_TIMER_DISABLE)
|
|
|
|
/*******************************************************************************
|
|
*
|
|
* sysTickStop - stop the timer
|
|
*
|
|
* This routine disables the systick counter.
|
|
*
|
|
* RETURNS: N/A
|
|
*
|
|
* \NOMANUAL
|
|
*/
|
|
|
|
static ALWAYS_INLINE void sysTickStop(void)
|
|
{
|
|
union __stcsr reg;
|
|
|
|
/*
|
|
* Disable the counter and its interrupt while preserving the
|
|
* remaining bits.
|
|
*/
|
|
reg.val = __scs.systick.stcsr.val;
|
|
reg.bit.enable = 0;
|
|
reg.bit.tickint = 0;
|
|
__scs.systick.stcsr.val = reg.val;
|
|
}
|
|
|
|
#endif /* CONFIG_TICKLESS_IDLE || CONFIG_SYSTEM_TIMER_DISABLE */
|
|
|
|
#ifdef CONFIG_TICKLESS_IDLE
|
|
|
|
/*******************************************************************************
|
|
*
|
|
* sysTickStart - start the timer
|
|
*
|
|
* This routine enables the systick counter.
|
|
*
|
|
* RETURNS: N/A
|
|
*
|
|
* \NOMANUAL
|
|
*/
|
|
|
|
static ALWAYS_INLINE void sysTickStart(void)
|
|
{
|
|
union __stcsr reg;
|
|
|
|
/*
|
|
* Enable the counter, its interrupt and set the clock source to be
|
|
* the system clock while preserving the remaining bits.
|
|
*/
|
|
reg.val =
|
|
__scs.systick.stcsr.val; /* countflag is cleared by this read */
|
|
reg.bit.enable = 1;
|
|
reg.bit.tickint = 1;
|
|
reg.bit.clksource = 1;
|
|
__scs.systick.stcsr.val = reg.val;
|
|
}
|
|
|
|
/*******************************************************************************
|
|
*
|
|
* sysTickCurrentGet - get the current counter value
|
|
*
|
|
* This routine gets the value from the timer's current value register. This
|
|
* value is the 'time' remaining to decrement before the timer triggers an
|
|
* interrupt.
|
|
*
|
|
* RETURNS: the current counter value
|
|
*
|
|
* \NOMANUAL
|
|
*/
|
|
static ALWAYS_INLINE uint32_t sysTickCurrentGet(void)
|
|
{
|
|
return __scs.systick.stcvr;
|
|
}
|
|
|
|
/*******************************************************************************
|
|
*
|
|
* sysTickReloadGet - get the reload/countdown value
|
|
*
|
|
* This routine returns the value from the reload value register.
|
|
*
|
|
* RETURNS: the counter's initial count/wraparound value
|
|
*
|
|
* \NOMANUAL
|
|
*/
|
|
static ALWAYS_INLINE uint32_t sysTickReloadGet(void)
|
|
{
|
|
return __scs.systick.strvr;
|
|
}
|
|
|
|
#endif /* CONFIG_TICKLESS_IDLE */
|
|
|
|
/*******************************************************************************
|
|
*
|
|
* sysTickReloadSet - set the reload/countdown value
|
|
*
|
|
* This routine sets value from which the timer will count down and also
|
|
* sets the timer's current value register to zero.
|
|
* Note that the value given is assumed to be valid (i.e., count < (1<<24)).
|
|
*
|
|
* RETURNS: N/A
|
|
*
|
|
* \NOMANUAL
|
|
*/
|
|
|
|
static ALWAYS_INLINE void sysTickReloadSet(
|
|
uint32_t count /* count from which timer is to count down */
|
|
)
|
|
{
|
|
/*
|
|
* Write the reload value and clear the current value in preparation
|
|
* for enabling the timer.
|
|
* The countflag in the control/status register is also cleared by
|
|
* this operation.
|
|
*/
|
|
__scs.systick.strvr = count;
|
|
__scs.systick.stcvr = 0; /* also clears the countflag */
|
|
}
|
|
|
|
/*******************************************************************************
|
|
*
|
|
* _TIMER_INT_HANDLER - system clock tick handler
|
|
*
|
|
* This routine handles the system clock tick interrupt. A TICK_EVENT event
|
|
* is pushed onto the microkernel stack.
|
|
*
|
|
* The symbol for this routine is either _timer_int_handler (for normal
|
|
* system operation) or _real_timer_int_handler (when GDB_INFO is enabled).
|
|
*
|
|
* RETURNS: N/A
|
|
*
|
|
* \NOMANUAL
|
|
*/
|
|
|
|
void _TIMER_INT_HANDLER(void *unused)
|
|
{
|
|
ARG_UNUSED(unused);
|
|
|
|
#ifdef CONFIG_INT_LATENCY_BENCHMARK
|
|
uint32_t value = __scs.systick.val;
|
|
uint32_t delta = __scs.systick.reload - value;
|
|
|
|
if (_hw_irq_to_c_handler_latency > delta) {
|
|
/* keep the lowest value observed */
|
|
_hw_irq_to_c_handler_latency = delta;
|
|
}
|
|
#endif
|
|
|
|
#ifdef CONFIG_ADVANCED_POWER_MANAGEMENT
|
|
int32_t numIdleTicks;
|
|
|
|
/*
|
|
* All interrupts are disabled when handling idle wakeup.
|
|
* For tickless idle, this ensures that the calculation and programming
|
|
* of
|
|
* the device for the next timer deadline is not interrupted.
|
|
* For non-tickless idle, this ensures that the clearing of the kernel
|
|
* idle
|
|
* state is not interrupted.
|
|
* In each case, _sys_power_save_idle_exit is called with interrupts
|
|
* disabled.
|
|
*/
|
|
__asm__(" cpsid i"); /* PRIMASK = 1 */
|
|
|
|
#ifdef CONFIG_TICKLESS_IDLE
|
|
/*
|
|
* If this a wakeup from a completed tickless idle or after
|
|
* _timer_idle_exit has processed a partial idle, return
|
|
* to the normal tick cycle.
|
|
*/
|
|
if (timer_mode == TIMER_MODE_ONE_SHOT) {
|
|
sysTickStop();
|
|
sysTickReloadSet(default_load_value);
|
|
sysTickStart();
|
|
timer_mode = TIMER_MODE_PERIODIC;
|
|
}
|
|
|
|
/* set the number of elapsed ticks and announce them to the kernel */
|
|
|
|
if (idle_mode == IDLE_TICKLESS) {
|
|
/* tickless idle completed without interruption */
|
|
idle_mode = IDLE_NOT_TICKLESS;
|
|
_sys_idle_elapsed_ticks =
|
|
idle_original_ticks + 1; /* actual # of idle ticks */
|
|
_sys_clock_tick_announce();
|
|
} else {
|
|
/*
|
|
* Increment the tick because _timer_idle_exit does not
|
|
* account for the tick due to the timer interrupt itself.
|
|
* Also, if not in tickless mode, _SysIdleElpasedTicks will be
|
|
* 0.
|
|
*/
|
|
_sys_idle_elapsed_ticks++;
|
|
|
|
/*
|
|
* If we transition from 0 elapsed ticks to 1 we need to
|
|
* announce the
|
|
* tick event to the microkernel. Other cases will be covered by
|
|
* _timer_idle_exit.
|
|
*/
|
|
|
|
if (_sys_idle_elapsed_ticks == 1) {
|
|
_sys_clock_tick_announce();
|
|
}
|
|
}
|
|
|
|
/* accumulate total counter value */
|
|
clock_accumulated_count += default_load_value * _sys_idle_elapsed_ticks;
|
|
#else /* !CONFIG_TICKLESS_IDLE */
|
|
/*
|
|
* No tickless idle:
|
|
* Update the total tick count and announce this tick to the kernel.
|
|
*/
|
|
clock_accumulated_count += sys_clock_hw_cycles_per_tick;
|
|
|
|
_sys_clock_tick_announce();
|
|
#endif /* CONFIG_TICKLESS_IDLE */
|
|
|
|
numIdleTicks = _NanoIdleValGet(); /* get # of idle ticks requested */
|
|
|
|
if (numIdleTicks) {
|
|
_NanoIdleValClear(); /* clear kernel idle setting */
|
|
|
|
/*
|
|
* Complete idle processing.
|
|
* Note that for tickless idle, nothing will be done in
|
|
* _timer_idle_exit.
|
|
*/
|
|
_sys_power_save_idle_exit(numIdleTicks);
|
|
}
|
|
|
|
__asm__(" cpsie i"); /* re-enable interrupts (PRIMASK = 0) */
|
|
|
|
#else /* !CONFIG_ADVANCED_POWER_MANAGEMENT */
|
|
|
|
/* accumulate total counter value */
|
|
clock_accumulated_count += sys_clock_hw_cycles_per_tick;
|
|
|
|
/*
|
|
* one more tick has occurred -- don't need to do anything special since
|
|
* timer is already configured to interrupt on the following tick
|
|
*/
|
|
_sys_clock_tick_announce();
|
|
|
|
#endif /* CONFIG_ADVANCED_POWER_MANAGEMENT */
|
|
|
|
extern void _ExcExit(void);
|
|
_ExcExit();
|
|
}
|
|
|
|
#ifdef CONFIG_TICKLESS_IDLE
|
|
|
|
/*******************************************************************************
|
|
*
|
|
* sysTickTicklessIdleInit - initialize the tickless idle feature
|
|
*
|
|
* This routine initializes the tickless idle feature by calculating the
|
|
* necessary hardware-specific parameters.
|
|
*
|
|
* Note that the maximum number of ticks that can elapse during a "tickless idle"
|
|
* is limited by <default_load_value>. The larger the value (the lower the
|
|
* tick frequency), the fewer elapsed ticks during a "tickless idle".
|
|
* Conversely, the smaller the value (the higher the tick frequency), the
|
|
* more elapsed ticks during a "tickless idle".
|
|
*
|
|
* RETURNS: N/A
|
|
*
|
|
* \NOMANUAL
|
|
*/
|
|
|
|
static void sysTickTicklessIdleInit(void)
|
|
{
|
|
/* enable counter, disable interrupt and set clock src to system clock
|
|
*/
|
|
union __stcsr stcsr = {.bit = {1, 0, 1, 0, 0, 0}};
|
|
volatile uint32_t dummy; /* used to help determine the 'skew time' */
|
|
|
|
/* store the default reload value (which has already been set) */
|
|
default_load_value = sysTickReloadGet();
|
|
|
|
/* calculate the max number of ticks with this 24-bit H/W counter */
|
|
max_system_ticks = 0x00ffffff / default_load_value;
|
|
|
|
/* determine the associated load value */
|
|
max_load_value = max_system_ticks * default_load_value;
|
|
|
|
/*
|
|
* Calculate the skew from switching the timer in and out of idle mode.
|
|
* The following sequence is emulated:
|
|
* 1. Stop the timer.
|
|
* 2. Read the current counter value.
|
|
* 3. Calculate the new/remaining counter reload value.
|
|
* 4. Load the new counter value.
|
|
* 5. Set the timer mode to periodic/one-shot.
|
|
* 6. Start the timer.
|
|
*
|
|
* The timer must be running for this to work, so enable the
|
|
* systick counter without generating interrupts, using the processor
|
|
*clock.
|
|
* Note that the reload value has already been set by the caller.
|
|
*/
|
|
|
|
__scs.systick.stcsr.val |= stcsr.val;
|
|
__asm__(" isb"); /* ensure the timer is started before reading */
|
|
|
|
timer_idle_skew = sysTickCurrentGet(); /* start of skew time */
|
|
|
|
__scs.systick.stcsr.val |= stcsr.val; /* normally sysTickStop() */
|
|
|
|
dummy = sysTickCurrentGet(); /* emulate sysTickReloadSet() */
|
|
|
|
/* emulate calculation of the new counter reload value */
|
|
if ((dummy == 1) || (dummy == default_load_value)) {
|
|
dummy = max_system_ticks - 1;
|
|
dummy += max_load_value - default_load_value;
|
|
} else {
|
|
dummy = dummy - 1;
|
|
dummy += dummy * default_load_value;
|
|
}
|
|
|
|
/* _sysTickStart() without interrupts */
|
|
__scs.systick.stcsr.val |= stcsr.val;
|
|
|
|
timer_mode = TIMER_MODE_PERIODIC;
|
|
|
|
/* skew time calculation for down counter (assumes no rollover) */
|
|
timer_idle_skew -= sysTickCurrentGet();
|
|
|
|
/* restore the previous sysTick state */
|
|
sysTickStop();
|
|
sysTickReloadSet(default_load_value);
|
|
}
|
|
|
|
/*******************************************************************************
|
|
*
|
|
* _timer_idle_enter - Place the system timer into idle state
|
|
*
|
|
* Re-program the timer to enter into the idle state for the given number of
|
|
* ticks. It is set to a "one shot" mode where it will fire in the number of
|
|
* ticks supplied or the maximum number of ticks that can be programmed into
|
|
* hardware. A value of -1 will result in the maximum number of ticks.
|
|
*
|
|
* RETURNS: N/A
|
|
*/
|
|
|
|
void _timer_idle_enter(int32_t ticks /* system ticks */
|
|
)
|
|
{
|
|
sysTickStop();
|
|
|
|
/*
|
|
* We're being asked to have the timer fire in "ticks" from now. To
|
|
* maintain accuracy we must account for the remaining time left in the
|
|
* timer. So we read the count out of it and add it to the requested
|
|
* time out
|
|
*/
|
|
idle_original_count = sysTickCurrentGet() - timer_idle_skew;
|
|
|
|
if ((ticks == -1) || (ticks > max_system_ticks)) {
|
|
/*
|
|
* We've been asked to fire the timer so far in the future that
|
|
* the
|
|
* required count value would not fit in the 24-bit reload
|
|
* register.
|
|
* Instead, we program for the maximum programmable interval
|
|
* minus one
|
|
* system tick to prevent overflow when the left over count read
|
|
* earlier
|
|
* is added.
|
|
*/
|
|
idle_original_count += max_load_value - default_load_value;
|
|
idle_original_ticks = max_system_ticks - 1;
|
|
} else {
|
|
/* leave one tick of buffer to have to time react when coming
|
|
* back */
|
|
idle_original_ticks = ticks - 1;
|
|
idle_original_count += idle_original_ticks * default_load_value;
|
|
}
|
|
|
|
/*
|
|
* Set timer to virtual "one shot" mode - sysTick does not have multiple
|
|
* modes, so the reload value is simply changed.
|
|
*/
|
|
timer_mode = TIMER_MODE_ONE_SHOT;
|
|
idle_mode = IDLE_TICKLESS;
|
|
sysTickReloadSet(idle_original_count);
|
|
sysTickStart();
|
|
}
|
|
|
|
/*******************************************************************************
|
|
*
|
|
* _timer_idle_exit - handling of tickless idle when interrupted
|
|
*
|
|
* The routine, called by _sys_power_save_idle_exit, is responsible for taking
|
|
* the timer out of idle mode and generating an interrupt at the next
|
|
* tick interval. It is expected that interrupts have been disabled.
|
|
*
|
|
* Note that in this routine, _sys_idle_elapsed_ticks must be zero because the
|
|
* ticker has done its work and consumed all the ticks. This has to be true
|
|
* otherwise idle mode wouldn't have been entered in the first place.
|
|
*
|
|
* RETURNS: N/A
|
|
*/
|
|
|
|
void _timer_idle_exit(void)
|
|
{
|
|
uint32_t count; /* timer's current count register value */
|
|
|
|
if (timer_mode == TIMER_MODE_PERIODIC) {
|
|
/*
|
|
* The timer interrupt handler is handling a completed tickless
|
|
* idle
|
|
* or this has been called by mistake; there's nothing to do
|
|
* here.
|
|
*/
|
|
return;
|
|
}
|
|
|
|
sysTickStop();
|
|
|
|
/* timer is in idle mode, adjust the ticks expired */
|
|
|
|
count = sysTickCurrentGet();
|
|
|
|
if ((count == 0) || (__scs.systick.stcsr.bit.countflag)) {
|
|
/*
|
|
* The timer expired and/or wrapped around. Re-set the timer to
|
|
* its default value and mode.
|
|
*/
|
|
sysTickReloadSet(default_load_value);
|
|
timer_mode = TIMER_MODE_PERIODIC;
|
|
|
|
/*
|
|
* Announce elapsed ticks to the microkernel. Note we are
|
|
* guaranteed
|
|
* that the timer ISR will execute before the tick event is
|
|
* serviced,
|
|
* so _sys_idle_elapsed_ticks is adjusted to account for it.
|
|
*/
|
|
_sys_idle_elapsed_ticks = idle_original_ticks - 1;
|
|
_sys_clock_tick_announce();
|
|
} else {
|
|
uint32_t elapsed; /* elapsed "counter time" */
|
|
uint32_t remaining; /* remaining "counter time" */
|
|
|
|
elapsed = idle_original_count - count;
|
|
|
|
remaining = elapsed % default_load_value;
|
|
|
|
/* ensure that the timer will interrupt at the next tick */
|
|
|
|
if (remaining == 0) {
|
|
/*
|
|
* Idle was interrupted on a tick boundary. Re-set the
|
|
* timer to
|
|
* its default value and mode.
|
|
*/
|
|
sysTickReloadSet(default_load_value);
|
|
timer_mode = TIMER_MODE_PERIODIC;
|
|
} else if (count > remaining) {
|
|
/*
|
|
* There is less time remaining to the next tick
|
|
* boundary than
|
|
* time left for idle. Leave in "one shot" mode.
|
|
*/
|
|
sysTickReloadSet(remaining);
|
|
}
|
|
|
|
_sys_idle_elapsed_ticks = elapsed / default_load_value;
|
|
|
|
if (_sys_idle_elapsed_ticks) {
|
|
_sys_clock_tick_announce();
|
|
}
|
|
}
|
|
|
|
idle_mode = IDLE_NOT_TICKLESS;
|
|
sysTickStart();
|
|
}
|
|
|
|
#endif /* CONFIG_TICKLESS_IDLE */
|
|
|
|
/*******************************************************************************
|
|
*
|
|
* timer_driver - initialize and enable the system clock
|
|
*
|
|
* This routine is used to program the systick to deliver interrupts at the
|
|
* rate specified via the 'sys_clock_us_per_tick' global variable.
|
|
*
|
|
* RETURNS: N/A
|
|
*/
|
|
void timer_driver(int priority /* priority parameter is ignored by this driver
|
|
*/
|
|
)
|
|
{
|
|
/* enable counter, interrupt and set clock src to system clock */
|
|
union __stcsr stcsr = {.bit = {1, 1, 1, 0, 0, 0}};
|
|
|
|
ARG_UNUSED(priority);
|
|
|
|
/*
|
|
* Determine the reload value to achieve the configured tick rate.
|
|
*/
|
|
|
|
/* systick supports 24-bit H/W counter */
|
|
__ASSERT(sys_clock_hw_cycles_per_tick <= (1 << 24),
|
|
"sys_clock_hw_cycles_per_tick too large");
|
|
sysTickReloadSet(sys_clock_hw_cycles_per_tick - 1);
|
|
|
|
#ifdef CONFIG_TICKLESS_IDLE
|
|
|
|
/* calculate hardware-specific parameters for tickless idle */
|
|
|
|
sysTickTicklessIdleInit();
|
|
|
|
#endif /* CONFIG_TICKLESS_IDLE */
|
|
|
|
#ifdef CONFIG_MICROKERNEL
|
|
|
|
/* specify the kernel routine that will handle the TICK_EVENT event */
|
|
|
|
task_event_set_handler(TICK_EVENT, K_ticker);
|
|
|
|
#endif /* CONFIG_MICROKERNEL */
|
|
|
|
_ScbExcPrioSet(_EXC_SYSTICK, _EXC_IRQ_DEFAULT_PRIO);
|
|
|
|
__scs.systick.stcsr.val = stcsr.val;
|
|
}
|
|
|
|
/*******************************************************************************
|
|
*
|
|
* timer_read - read the BSP timer hardware
|
|
*
|
|
* This routine returns the current time in terms of timer hardware clock cycles.
|
|
* Some VxMicro facilities (e.g. benchmarking code) directly call timer_read()
|
|
* instead of utilizing the 'timer_read_fptr' function pointer.
|
|
*
|
|
* RETURNS: up counter of elapsed clock cycles
|
|
*
|
|
* \INTERNAL WARNING
|
|
* systick counter is a 24-bit down counter which is reset to "reload" value
|
|
* once it reaches 0.
|
|
*/
|
|
|
|
uint32_t timer_read(void)
|
|
{
|
|
return clock_accumulated_count + (__scs.systick.strvr - __scs.systick.stcvr);
|
|
}
|
|
|
|
#ifdef CONFIG_SYSTEM_TIMER_DISABLE
|
|
|
|
/*******************************************************************************
|
|
*
|
|
* timer_disable - stop announcing ticks into the kernel
|
|
*
|
|
* This routine disables the systick so that timer interrupts are no
|
|
* longer delivered.
|
|
*
|
|
* RETURNS: N/A
|
|
*/
|
|
|
|
void timer_disable(void)
|
|
{
|
|
unsigned int key; /* interrupt lock level */
|
|
union __stcsr reg;
|
|
|
|
key = irq_lock();
|
|
|
|
/* disable the systick counter and systick interrupt */
|
|
|
|
sysTickStop();
|
|
|
|
irq_unlock(key);
|
|
}
|
|
|
|
#endif /* CONFIG_SYSTEM_TIMER_DISABLE */
|