forked from Imagelibrary/rtems
291 lines
5.7 KiB
C
291 lines
5.7 KiB
C
/*
|
|
* Clock Tick Device Driver using Timer Library implemented
|
|
* by the GRLIB GPTIMER / LEON2 Timer drivers.
|
|
*
|
|
* COPYRIGHT (c) 2010.
|
|
* Cobham Gaisler AB.
|
|
*
|
|
* The license and distribution terms for this file may be
|
|
* found in the file LICENSE in this distribution or at
|
|
* http://www.rtems.org/license/LICENSE.
|
|
*
|
|
*/
|
|
|
|
#include <rtems.h>
|
|
#include <rtems/timecounter.h>
|
|
#include <rtems/score/percpu.h>
|
|
#include <stdlib.h>
|
|
#include <bsp.h>
|
|
#include <bsp/tlib.h>
|
|
|
|
#ifdef RTEMS_DRVMGR_STARTUP
|
|
|
|
/* Undefine this to save space in standard LEON configurations,
|
|
* it will assume that Prescaler is running at 1MHz.
|
|
*/
|
|
#undef CLOCK_DRIVER_DONT_ASSUME_PRESCALER_1MHZ
|
|
|
|
/* Set the below defines from bsp.h if function needed.
|
|
#undef CLOCK_DRIVER_ISRS_PER_TICK
|
|
#undef CLOCK_DRIVER_USE_FAST_IDLE
|
|
*/
|
|
|
|
/*
|
|
* Number of Clock ticks since initialization
|
|
*/
|
|
volatile uint32_t Clock_driver_ticks;
|
|
|
|
/*
|
|
* Timer Number in Timer Library. Defaults to the first Timer in
|
|
* the System.
|
|
*/
|
|
int Clock_timer = 0;
|
|
|
|
/*
|
|
* Timer Handle in Timer Library
|
|
*/
|
|
void *Clock_handle = NULL;
|
|
|
|
#ifdef CLOCK_DRIVER_DONT_ASSUME_PRESCALER_1MHZ
|
|
unsigned int Clock_basefreq;
|
|
#endif
|
|
|
|
void Clock_exit(void);
|
|
void Clock_isr(void *arg_unused);
|
|
|
|
static rtems_timecounter_simple tlib_tc;
|
|
|
|
static uint32_t tlib_tc_get(rtems_timecounter_simple *tc)
|
|
{
|
|
unsigned int clicks = 0;
|
|
|
|
if (Clock_handle != NULL) {
|
|
tlib_get_counter(Clock_handle, &clicks);
|
|
}
|
|
|
|
return clicks;
|
|
}
|
|
|
|
static bool tlib_tc_is_pending(rtems_timecounter_simple *tc)
|
|
{
|
|
bool pending = false;
|
|
|
|
if (Clock_handle != NULL) {
|
|
pending = tlib_interrupt_pending(Clock_handle, 0) != 0;
|
|
}
|
|
|
|
return pending;
|
|
}
|
|
|
|
static uint32_t tlib_tc_get_timecount(struct timecounter *tc)
|
|
{
|
|
return rtems_timecounter_simple_downcounter_get(
|
|
tc,
|
|
tlib_tc_get,
|
|
tlib_tc_is_pending
|
|
);
|
|
}
|
|
|
|
static void tlib_tc_at_tick(rtems_timecounter_simple *tc)
|
|
{
|
|
/* Nothing to do? */
|
|
}
|
|
|
|
static void tlib_tc_tick(void)
|
|
{
|
|
rtems_timecounter_simple_downcounter_tick(
|
|
&tlib_tc,
|
|
tlib_tc_get,
|
|
tlib_tc_at_tick
|
|
);
|
|
}
|
|
|
|
/*
|
|
* Clock_isr
|
|
*
|
|
* This is the clock tick interrupt handler.
|
|
*
|
|
* Input parameters:
|
|
* vector - vector number
|
|
*
|
|
* Output parameters: NONE
|
|
*
|
|
* Return values: NONE
|
|
*
|
|
*/
|
|
|
|
void Clock_isr(void *arg_unused)
|
|
{
|
|
/*
|
|
* Support for shared interrupts. Ack IRQ at source, only handle
|
|
* interrupts generated from the tick-timer.
|
|
*/
|
|
if ( tlib_interrupt_pending(Clock_handle, 1) == 0 )
|
|
return;
|
|
|
|
/*
|
|
* Accurate count of ISRs
|
|
*/
|
|
|
|
Clock_driver_ticks += 1;
|
|
|
|
#ifdef CLOCK_DRIVER_USE_FAST_IDLE
|
|
do {
|
|
tlib_tc_tick();
|
|
} while ( _Thread_Heir == _Thread_Executing && _Thread_Executing->is_idle );
|
|
|
|
return;
|
|
|
|
#else
|
|
|
|
#ifdef CLOCK_DRIVER_ISRS_PER_TICK
|
|
/*
|
|
* The driver is multiple ISRs per clock tick.
|
|
*/
|
|
|
|
if ( !Clock_driver_isrs ) {
|
|
|
|
tlib_tc_tick();
|
|
|
|
Clock_driver_isrs = CLOCK_DRIVER_ISRS_PER_TICK;
|
|
}
|
|
Clock_driver_isrs--;
|
|
#else
|
|
|
|
/*
|
|
* The driver is one ISR per clock tick.
|
|
*/
|
|
tlib_tc_tick();
|
|
#endif
|
|
#endif
|
|
}
|
|
|
|
/*
|
|
* Clock_exit
|
|
*
|
|
* This routine allows the clock driver to exit by masking the interrupt and
|
|
* disabling the clock's counter.
|
|
*
|
|
* Input parameters: NONE
|
|
*
|
|
* Output parameters: NONE
|
|
*
|
|
* Return values: NONE
|
|
*
|
|
*/
|
|
|
|
void Clock_exit( void )
|
|
{
|
|
/* Stop all activity of the Timer, no more ISRs.
|
|
* We could be using tlib_close(), however tlib_stop() is quicker
|
|
* and independent of IRQ unregister code.
|
|
*/
|
|
if ( Clock_handle ) {
|
|
tlib_stop(Clock_handle);
|
|
Clock_handle = NULL;
|
|
}
|
|
}
|
|
|
|
/*
|
|
* Clock_initialize
|
|
*
|
|
* This routine initializes the clock driver and starts the Clock.
|
|
*
|
|
* Input parameters:
|
|
* major - clock device major number
|
|
* minor - clock device minor number
|
|
* parg - pointer to optional device driver arguments
|
|
*
|
|
* Output parameters: NONE
|
|
*
|
|
* Return values:
|
|
* rtems_device_driver status code
|
|
*/
|
|
|
|
rtems_device_driver Clock_initialize(
|
|
rtems_device_major_number major,
|
|
rtems_device_minor_number minor,
|
|
void *pargp
|
|
)
|
|
{
|
|
uint64_t frequency;
|
|
unsigned int tick_hz;
|
|
|
|
/*
|
|
* Take Timer that should be used as system timer.
|
|
*
|
|
*/
|
|
Clock_handle = tlib_open(Clock_timer);
|
|
if ( Clock_handle == NULL ) {
|
|
/* System Clock Timer not found */
|
|
return RTEMS_NOT_DEFINED;
|
|
}
|
|
|
|
/*
|
|
* Install Clock ISR before starting timer
|
|
*/
|
|
tlib_irq_register(Clock_handle, Clock_isr, NULL);
|
|
|
|
/* Set Timer Frequency to tick at Configured value. The Timer
|
|
* Frequency is set in multiples of the timer base frequency.
|
|
*
|
|
* In standard LEON3 designs the base frequency is is 1MHz, to
|
|
* save instructions undefine CLOCK_DRIVER_DONT_ASSUME_PRESCALER_1MHZ
|
|
* to avoid 64-bit calculation.
|
|
*/
|
|
#ifdef CLOCK_DRIVER_DONT_ASSUME_PRESCALER_1MHZ
|
|
{
|
|
uint64_t tmp;
|
|
|
|
tlib_get_freq(Clock_handle, &Clock_basefreq, NULL);
|
|
|
|
frequency = Clock_basefreq
|
|
tmp = frequency * (uint64_t)rtems_configuration_get_microseconds_per_tick();
|
|
tick_hz = tmp / 1000000;
|
|
}
|
|
#else
|
|
frequency = 1000000;
|
|
tick_hz = rtems_configuration_get_microseconds_per_tick();
|
|
#endif
|
|
|
|
tlib_set_freq(Clock_handle, tick_hz);
|
|
|
|
rtems_timecounter_simple_install(
|
|
&tlib_tc,
|
|
frequency,
|
|
tick_hz,
|
|
tlib_tc_get_timecount
|
|
);
|
|
|
|
/*
|
|
* IRQ and Frequency is setup, now we start the Timer. IRQ is still
|
|
* disabled globally during startup, so IRQ will hold for a while.
|
|
*/
|
|
tlib_start(Clock_handle, 0);
|
|
|
|
/*
|
|
* Register function called at system shutdown
|
|
*/
|
|
atexit( Clock_exit );
|
|
|
|
/*
|
|
* If we are counting ISRs per tick, then initialize the counter.
|
|
*/
|
|
|
|
#ifdef CLOCK_DRIVER_ISRS_PER_TICK
|
|
Clock_driver_isrs = CLOCK_DRIVER_ISRS_PER_TICK;
|
|
#endif
|
|
|
|
return RTEMS_SUCCESSFUL;
|
|
}
|
|
|
|
/*** Timer Driver Interface ***/
|
|
|
|
/* Set system clock timer instance */
|
|
void Clock_timer_register(int timer_number)
|
|
{
|
|
Clock_timer = timer_number;
|
|
}
|
|
|
|
#endif
|