mirror of
https://gitlab.rtems.org/rtems/rtos/rtems.git
synced 2025-12-20 03:05:47 +08:00
545
bsps/shared/grlib/btimer/gptimer.c
Normal file
545
bsps/shared/grlib/btimer/gptimer.c
Normal file
File diff suppressed because it is too large
Load Diff
77
bsps/shared/grlib/btimer/tlib.c
Normal file
77
bsps/shared/grlib/btimer/tlib.c
Normal file
@@ -0,0 +1,77 @@
|
||||
/*
|
||||
* Timer Library (TLIB)
|
||||
*
|
||||
* COPYRIGHT (c) 2011.
|
||||
* 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 <grlib/tlib.h>
|
||||
|
||||
struct tlib_dev *tlib_dev_head = NULL;
|
||||
struct tlib_dev *tlib_dev_tail = NULL;
|
||||
static int tlib_dev_cnt = 0;
|
||||
|
||||
/* Register Timer device to Timer Library */
|
||||
int tlib_dev_reg(struct tlib_dev *newdev)
|
||||
{
|
||||
/* Reset device */
|
||||
newdev->status = 0;
|
||||
newdev->isr_func = NULL;
|
||||
newdev->index = tlib_dev_cnt;
|
||||
|
||||
/* Insert last in queue */
|
||||
newdev->next = NULL;
|
||||
if ( tlib_dev_tail == NULL ) {
|
||||
tlib_dev_head = newdev;
|
||||
} else {
|
||||
tlib_dev_tail->next = newdev;
|
||||
}
|
||||
tlib_dev_tail = newdev;
|
||||
|
||||
/* Return Index of Registered Timer */
|
||||
return tlib_dev_cnt++;
|
||||
}
|
||||
|
||||
void *tlib_open(int timer_no)
|
||||
{
|
||||
struct tlib_dev *dev;
|
||||
|
||||
if ( timer_no < 0 )
|
||||
return NULL;
|
||||
|
||||
dev = tlib_dev_head;
|
||||
while ( (timer_no > 0) && dev ) {
|
||||
timer_no--;
|
||||
dev = dev->next;
|
||||
}
|
||||
if ( dev ) {
|
||||
if ( dev->status )
|
||||
return NULL;
|
||||
dev->status = 1;
|
||||
/* Reset Timer to initial state */
|
||||
tlib_reset(dev);
|
||||
}
|
||||
return dev;
|
||||
}
|
||||
|
||||
void tlib_close(void *hand)
|
||||
{
|
||||
struct tlib_dev *dev = hand;
|
||||
|
||||
/* Stop any ongoing timer operation and unregister IRQ if registered */
|
||||
tlib_stop(dev);
|
||||
tlib_irq_unregister(dev);
|
||||
|
||||
/* Mark not open */
|
||||
dev->status = 0;
|
||||
}
|
||||
|
||||
int tlib_ntimer(void)
|
||||
{
|
||||
return tlib_dev_cnt;
|
||||
}
|
||||
442
bsps/shared/grlib/btimer/tlib_ckinit.c
Normal file
442
bsps/shared/grlib/btimer/tlib_ckinit.c
Normal file
@@ -0,0 +1,442 @@
|
||||
/*
|
||||
* Clock Tick Device Driver using Timer Library implemented
|
||||
* by the GRLIB GPTIMER / LEON2 Timer drivers.
|
||||
*
|
||||
* COPYRIGHT (c) 2010 - 2017.
|
||||
* 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.
|
||||
*
|
||||
*/
|
||||
|
||||
/*
|
||||
* This is an implementation of the RTEMS "clockdrv_shell" interface for
|
||||
* LEON2/3/4 systems using the Driver Manager. It is clock hardware agnostic
|
||||
* and compatible with SMP and UP. Availability of free running counters is
|
||||
* probed and selected as needed.
|
||||
*/
|
||||
#include <rtems.h>
|
||||
#include <rtems/timecounter.h>
|
||||
#include <rtems/clockdrv.h>
|
||||
#include <stdlib.h>
|
||||
#include <bsp.h>
|
||||
#include <grlib/tlib.h>
|
||||
|
||||
#ifdef RTEMS_DRVMGR_STARTUP
|
||||
|
||||
#if defined(LEON3)
|
||||
#include <leon.h>
|
||||
#endif
|
||||
|
||||
struct ops {
|
||||
/*
|
||||
* Set up the free running counter using the Timecounter or Simple
|
||||
* Timecounter interface.
|
||||
*/
|
||||
rtems_device_driver (*initialize_counter)(void);
|
||||
|
||||
/*
|
||||
* Hardware-specific support at tick interrupt which runs early in Clock_isr.
|
||||
* It can for example be used to check if interrupt was actually caused by
|
||||
* the timer hardware. If return value is not RTEMS_SUCCESSFUL then Clock_isr
|
||||
* returns immediately. at_tick can be initialized with NULL.
|
||||
*/
|
||||
rtems_device_driver (*at_tick)(void);
|
||||
|
||||
/*
|
||||
* Typically calls rtems_timecounter_tick(). A specialized clock driver may
|
||||
* use for example rtems_timecounter_tick_simple() instead.
|
||||
*/
|
||||
void (*timecounter_tick)(void);
|
||||
|
||||
/*
|
||||
* Called when the clock driver exits. It can be used to stop functionality
|
||||
* started by initialize_counter. The tick timer is stopped by default.
|
||||
* shutdown_hardware can be initialized with NULL
|
||||
*/
|
||||
void (*shutdown_hardware)(void);
|
||||
};
|
||||
|
||||
/*
|
||||
* Different implementation depending on available free running counter for the
|
||||
* timecounter.
|
||||
*
|
||||
* NOTE: The clock interface is not compatible with shared interrupts on the
|
||||
* clock (tick) timer in SMP configuration.
|
||||
*/
|
||||
|
||||
#ifndef RTEMS_SMP
|
||||
/* "simple timecounter" interface. Only for non-SMP. */
|
||||
static const struct ops ops_simple;
|
||||
#else
|
||||
/* Hardware support up-counter using LEON3 %asr23. */
|
||||
static const struct ops ops_timetag;
|
||||
/* Timestamp counter available in some IRQ(A)MP instantiations. */
|
||||
static const struct ops ops_irqamp;
|
||||
/* Separate GPTIMER subtimer as timecounter */
|
||||
static const struct ops ops_subtimer;
|
||||
#endif
|
||||
|
||||
struct clock_priv {
|
||||
const struct ops *ops;
|
||||
/*
|
||||
* Timer number in Timer Library for tick timer used by this interface.
|
||||
* Defaults to the first Timer in the System.
|
||||
*/
|
||||
int tlib_tick_index;
|
||||
/* Timer number for timecounter timer if separate GPTIMER subtimer is used */
|
||||
int tlib_counter_index;
|
||||
void *tlib_tick;
|
||||
void *tlib_counter;
|
||||
rtems_timecounter_simple tc_simple;
|
||||
struct timecounter tc;
|
||||
};
|
||||
static struct clock_priv priv;
|
||||
|
||||
/** Common interface **/
|
||||
|
||||
/* Set system clock timer instance */
|
||||
void Clock_timer_register(int timer_number)
|
||||
{
|
||||
priv.tlib_tick_index = timer_number;
|
||||
priv.tlib_counter_index = timer_number + 1;
|
||||
}
|
||||
|
||||
static rtems_device_driver tlib_clock_find_timer(void)
|
||||
{
|
||||
/* Take Timer that should be used as system timer. */
|
||||
priv.tlib_tick = tlib_open(priv.tlib_tick_index);
|
||||
if (priv.tlib_tick == NULL) {
|
||||
/* System Clock Timer not found */
|
||||
return RTEMS_NOT_DEFINED;
|
||||
}
|
||||
|
||||
/* Select which operation set to use */
|
||||
#ifndef RTEMS_SMP
|
||||
priv.ops = &ops_simple;
|
||||
#else
|
||||
/* When on LEON3 try to use dedicated hardware free running counter. */
|
||||
leon3_up_counter_enable();
|
||||
if (leon3_up_counter_is_available()) {
|
||||
priv.ops = &ops_timetag;
|
||||
return RTEMS_SUCCESSFUL;
|
||||
} else {
|
||||
volatile struct irqmp_timestamp_regs *irqmp_ts;
|
||||
|
||||
irqmp_ts = &LEON3_IrqCtrl_Regs->timestamp[0];
|
||||
if (leon3_irqmp_has_timestamp(irqmp_ts)) {
|
||||
priv.ops = &ops_irqamp;
|
||||
return RTEMS_SUCCESSFUL;
|
||||
}
|
||||
}
|
||||
|
||||
/* Take another subtimer as the final option. */
|
||||
priv.ops = &ops_subtimer;
|
||||
#endif
|
||||
|
||||
return RTEMS_SUCCESSFUL;
|
||||
}
|
||||
|
||||
static rtems_device_driver tlib_clock_initialize_hardware(void)
|
||||
{
|
||||
/* Set tick rate in number of "Base-Frequency ticks" */
|
||||
tlib_set_freq(priv.tlib_tick, rtems_configuration_get_microseconds_per_tick());
|
||||
priv.ops->initialize_counter();
|
||||
tlib_start(priv.tlib_tick, 0);
|
||||
|
||||
return RTEMS_SUCCESSFUL;
|
||||
}
|
||||
|
||||
static rtems_device_driver tlib_clock_at_tick(void)
|
||||
{
|
||||
if (priv.ops->at_tick) {
|
||||
return priv.ops->at_tick();
|
||||
}
|
||||
|
||||
return RTEMS_SUCCESSFUL;
|
||||
}
|
||||
|
||||
static void tlib_clock_timecounter_tick(void)
|
||||
{
|
||||
priv.ops->timecounter_tick();
|
||||
}
|
||||
|
||||
/* Return a value not equal to RTEMS_SUCCESFUL to make Clock_initialize fail. */
|
||||
static rtems_device_driver tlib_clock_install_isr(rtems_isr *isr)
|
||||
{
|
||||
int flags = 0;
|
||||
|
||||
#ifdef RTEMS_SMP
|
||||
/* We shall broadcast the clock interrupt to all processors. */
|
||||
flags = TLIB_FLAGS_BROADCAST;
|
||||
#endif
|
||||
tlib_irq_register(priv.tlib_tick, isr, NULL, flags);
|
||||
|
||||
return RTEMS_SUCCESSFUL;
|
||||
}
|
||||
|
||||
#ifndef RTEMS_SMP
|
||||
/** Simple counter **/
|
||||
static uint32_t simple_tlib_tc_get(rtems_timecounter_simple *tc)
|
||||
{
|
||||
unsigned int clicks = 0;
|
||||
|
||||
if (priv.tlib_tick != NULL) {
|
||||
tlib_get_counter(priv.tlib_tick, &clicks);
|
||||
}
|
||||
|
||||
return clicks;
|
||||
}
|
||||
|
||||
static bool simple_tlib_tc_is_pending(rtems_timecounter_simple *tc)
|
||||
{
|
||||
bool pending = false;
|
||||
|
||||
if (priv.tlib_tick != NULL) {
|
||||
pending = tlib_interrupt_pending(priv.tlib_tick, 0) != 0;
|
||||
}
|
||||
|
||||
return pending;
|
||||
}
|
||||
|
||||
static uint32_t simple_tlib_tc_get_timecount(struct timecounter *tc)
|
||||
{
|
||||
return rtems_timecounter_simple_downcounter_get(
|
||||
tc,
|
||||
simple_tlib_tc_get,
|
||||
simple_tlib_tc_is_pending
|
||||
);
|
||||
}
|
||||
|
||||
static rtems_device_driver simple_initialize_counter(void)
|
||||
{
|
||||
uint64_t frequency;
|
||||
unsigned int tick_hz;
|
||||
|
||||
frequency = 1000000;
|
||||
tick_hz = rtems_configuration_get_microseconds_per_tick();
|
||||
|
||||
rtems_timecounter_simple_install(
|
||||
&priv.tc_simple,
|
||||
frequency,
|
||||
tick_hz,
|
||||
simple_tlib_tc_get_timecount
|
||||
);
|
||||
|
||||
return RTEMS_NOT_DEFINED;
|
||||
}
|
||||
|
||||
static void simple_tlib_tc_at_tick(rtems_timecounter_simple *tc)
|
||||
{
|
||||
/* Nothing to do */
|
||||
}
|
||||
|
||||
/*
|
||||
* Support for shared interrupts. Ack IRQ at source, only handle interrupts
|
||||
* generated from the tick-timer. This is called early in Clock_isr.
|
||||
*/
|
||||
static rtems_device_driver simple_at_tick(void)
|
||||
{
|
||||
if (tlib_interrupt_pending(priv.tlib_tick, 1) == 0) {
|
||||
return RTEMS_NOT_DEFINED;
|
||||
}
|
||||
return RTEMS_SUCCESSFUL;
|
||||
}
|
||||
|
||||
static void simple_timecounter_tick(void)
|
||||
{
|
||||
rtems_timecounter_simple_downcounter_tick(
|
||||
&priv.tc_simple,
|
||||
simple_tlib_tc_get,
|
||||
simple_tlib_tc_at_tick
|
||||
);
|
||||
}
|
||||
|
||||
static const struct ops ops_simple = {
|
||||
.initialize_counter = simple_initialize_counter,
|
||||
.at_tick = simple_at_tick,
|
||||
.timecounter_tick = simple_timecounter_tick,
|
||||
.shutdown_hardware = NULL,
|
||||
};
|
||||
|
||||
#else
|
||||
|
||||
/** Subtimer as counter **/
|
||||
static uint32_t subtimer_get_timecount(struct timecounter *tc)
|
||||
{
|
||||
unsigned int counter;
|
||||
|
||||
tlib_get_counter(priv.tlib_counter, &counter);
|
||||
|
||||
return 0xffffffff - counter;
|
||||
}
|
||||
|
||||
static rtems_device_driver subtimer_initialize_counter(void)
|
||||
{
|
||||
unsigned int mask;
|
||||
unsigned int basefreq;
|
||||
|
||||
if (priv.tlib_counter_index == priv.tlib_tick_index) {
|
||||
priv.tlib_counter_index = priv.tlib_tick_index + 1;
|
||||
}
|
||||
/* Take Timer that should be used as timecounter upcounter timer. */
|
||||
priv.tlib_counter = tlib_open(priv.tlib_counter_index);
|
||||
if (priv.tlib_counter == NULL) {
|
||||
/* Timecounter timer not found */
|
||||
return RTEMS_NOT_DEFINED;
|
||||
}
|
||||
|
||||
/* Configure free running counter: GPTIMER */
|
||||
tlib_get_freq(priv.tlib_counter, &basefreq, NULL);
|
||||
tlib_get_widthmask(priv.tlib_counter, &mask);
|
||||
|
||||
priv.tc.tc_get_timecount = subtimer_get_timecount;
|
||||
priv.tc.tc_counter_mask = mask;
|
||||
priv.tc.tc_frequency = basefreq;
|
||||
priv.tc.tc_quality = RTEMS_TIMECOUNTER_QUALITY_CLOCK_DRIVER;
|
||||
rtems_timecounter_install(&priv.tc);
|
||||
/* Start free running counter */
|
||||
tlib_start(priv.tlib_counter, 0);
|
||||
|
||||
return RTEMS_SUCCESSFUL;
|
||||
}
|
||||
|
||||
static void subtimer_timecounter_tick(void)
|
||||
{
|
||||
rtems_timecounter_tick();
|
||||
}
|
||||
|
||||
static void subtimer_shutdown_hardware(void)
|
||||
{
|
||||
if (priv.tlib_counter) {
|
||||
tlib_stop(priv.tlib_counter);
|
||||
priv.tlib_counter = NULL;
|
||||
}
|
||||
}
|
||||
|
||||
static const struct ops ops_subtimer = {
|
||||
.initialize_counter = subtimer_initialize_counter,
|
||||
.timecounter_tick = subtimer_timecounter_tick,
|
||||
.shutdown_hardware = subtimer_shutdown_hardware,
|
||||
};
|
||||
|
||||
/** DSU timetag as counter **/
|
||||
static uint32_t timetag_get_timecount(struct timecounter *tc)
|
||||
{
|
||||
return leon3_up_counter_low();
|
||||
}
|
||||
|
||||
static rtems_device_driver timetag_initialize_counter(void)
|
||||
{
|
||||
/* Configure free running counter: timetag */
|
||||
priv.tc.tc_get_timecount = timetag_get_timecount;
|
||||
priv.tc.tc_counter_mask = 0xffffffff;
|
||||
priv.tc.tc_frequency = leon3_up_counter_frequency();
|
||||
priv.tc.tc_quality = RTEMS_TIMECOUNTER_QUALITY_CLOCK_DRIVER;
|
||||
rtems_timecounter_install(&priv.tc);
|
||||
|
||||
return RTEMS_SUCCESSFUL;
|
||||
}
|
||||
|
||||
static void timetag_timecounter_tick(void)
|
||||
{
|
||||
rtems_timecounter_tick();
|
||||
}
|
||||
|
||||
static const struct ops ops_timetag = {
|
||||
.initialize_counter = timetag_initialize_counter,
|
||||
.at_tick = NULL,
|
||||
.timecounter_tick = timetag_timecounter_tick,
|
||||
.shutdown_hardware = NULL,
|
||||
};
|
||||
|
||||
/** IRQ(A)MP timestamp as counter **/
|
||||
static uint32_t irqamp_get_timecount(struct timecounter *tc)
|
||||
{
|
||||
return LEON3_IrqCtrl_Regs->timestamp[0].counter;
|
||||
}
|
||||
|
||||
static rtems_device_driver irqamp_initialize_counter(void)
|
||||
{
|
||||
volatile struct irqmp_timestamp_regs *irqmp_ts;
|
||||
static const uint32_t A_TSISEL_FIELD = 0xf;
|
||||
|
||||
/* Configure free running counter: timetag */
|
||||
priv.tc.tc_get_timecount = irqamp_get_timecount;
|
||||
priv.tc.tc_counter_mask = 0xffffffff;
|
||||
priv.tc.tc_frequency = leon3_up_counter_frequency();
|
||||
priv.tc.tc_quality = RTEMS_TIMECOUNTER_QUALITY_CLOCK_DRIVER;
|
||||
rtems_timecounter_install(&priv.tc);
|
||||
|
||||
/*
|
||||
* The counter increments whenever a TSISEL field in a Timestamp Control
|
||||
* Register is non-zero.
|
||||
*/
|
||||
irqmp_ts = &LEON3_IrqCtrl_Regs->timestamp[0];
|
||||
irqmp_ts->control = A_TSISEL_FIELD;
|
||||
|
||||
return RTEMS_SUCCESSFUL;
|
||||
}
|
||||
|
||||
static void irqamp_timecounter_tick(void)
|
||||
{
|
||||
rtems_timecounter_tick();
|
||||
}
|
||||
|
||||
static const struct ops ops_irqamp = {
|
||||
.initialize_counter = irqamp_initialize_counter,
|
||||
.at_tick = NULL,
|
||||
.timecounter_tick = irqamp_timecounter_tick,
|
||||
.shutdown_hardware = NULL,
|
||||
};
|
||||
#endif
|
||||
|
||||
/** Interface to the Clock Driver Shell (dev/clock/clockimpl.h) **/
|
||||
#define Clock_driver_support_find_timer() \
|
||||
do { \
|
||||
rtems_device_driver ret; \
|
||||
ret = tlib_clock_find_timer(); \
|
||||
if (RTEMS_SUCCESSFUL != ret) { \
|
||||
return ret; \
|
||||
} \
|
||||
} while (0)
|
||||
|
||||
#define Clock_driver_support_install_isr( isr ) \
|
||||
do { \
|
||||
rtems_device_driver ret; \
|
||||
ret = tlib_clock_install_isr( isr ); \
|
||||
if (RTEMS_SUCCESSFUL != ret) { \
|
||||
return ret; \
|
||||
} \
|
||||
} while (0)
|
||||
|
||||
#define Clock_driver_support_set_interrupt_affinity(online_processors) \
|
||||
/* Done by tlib_clock_install_isr() */
|
||||
|
||||
#define Clock_driver_support_initialize_hardware() \
|
||||
do { \
|
||||
rtems_device_driver ret; \
|
||||
ret = tlib_clock_initialize_hardware(); \
|
||||
if (RTEMS_SUCCESSFUL != ret) { \
|
||||
return ret; \
|
||||
} \
|
||||
} while (0)
|
||||
|
||||
#define Clock_driver_timecounter_tick() \
|
||||
tlib_clock_timecounter_tick()
|
||||
|
||||
#define Clock_driver_support_at_tick() \
|
||||
do { \
|
||||
rtems_device_driver ret; \
|
||||
ret = tlib_clock_at_tick(); \
|
||||
if (RTEMS_SUCCESSFUL != ret) { \
|
||||
return; \
|
||||
} \
|
||||
} while (0)
|
||||
|
||||
#include "../../../shared/dev/clock/clockimpl.h"
|
||||
|
||||
#endif /* RTEMS_DRVMGR_STARTUP */
|
||||
|
||||
Reference in New Issue
Block a user