summaryrefslogtreecommitdiffstats
path: root/c/src/lib/libbsp/sparc/shared/timer/tlib_ckinit.c
diff options
context:
space:
mode:
Diffstat (limited to 'c/src/lib/libbsp/sparc/shared/timer/tlib_ckinit.c')
-rw-r--r--c/src/lib/libbsp/sparc/shared/timer/tlib_ckinit.c543
1 files changed, 353 insertions, 190 deletions
diff --git a/c/src/lib/libbsp/sparc/shared/timer/tlib_ckinit.c b/c/src/lib/libbsp/sparc/shared/timer/tlib_ckinit.c
index 2848f4cb3c..7880e86bae 100644
--- a/c/src/lib/libbsp/sparc/shared/timer/tlib_ckinit.c
+++ b/c/src/lib/libbsp/sparc/shared/timer/tlib_ckinit.c
@@ -2,7 +2,7 @@
* Clock Tick Device Driver using Timer Library implemented
* by the GRLIB GPTIMER / LEON2 Timer drivers.
*
- * COPYRIGHT (c) 2010.
+ * COPYRIGHT (c) 2010 - 2017.
* Cobham Gaisler AB.
*
* The license and distribution terms for this file may be
@@ -11,280 +11,443 @@
*
*/
+/*
+ * 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/score/percpu.h>
+#include <rtems/clockdrv.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
+#if defined(LEON3)
+#include <leon.h>
+#endif
-/* Set the below defines from bsp.h if function needed.
-#undef CLOCK_DRIVER_ISRS_PER_TICK
-#undef CLOCK_DRIVER_USE_FAST_IDLE
-*/
+struct ops {
+ /*
+ * Set up the free running counter using the Timecounter or Simple
+ * Timecounter interface.
+ */
+ rtems_device_driver (*initialize_counter)(void);
-/*
- * Number of Clock ticks since initialization
- */
-volatile uint32_t Clock_driver_ticks;
+ /*
+ * 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);
-/*
- * Timer Number in Timer Library. Defaults to the first Timer in
- * the System.
- */
-int Clock_timer = 0;
+ /*
+ * 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);
+};
/*
- * Timer Handle in Timer Library
+ * 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.
*/
-void *Clock_handle = NULL;
-#ifdef CLOCK_DRIVER_DONT_ASSUME_PRESCALER_1MHZ
-unsigned int Clock_basefreq;
+/* "simple timecounter" interface. Only for non-SMP. */
+static const struct ops ops_simple;
+/* 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;
+
+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);
-void Clock_exit(void);
-void Clock_isr(void *arg_unused);
+ return RTEMS_SUCCESSFUL;
+}
-static rtems_timecounter_simple tlib_tc;
+static void tlib_clock_shutdown_hardware(void)
+{
+ if (priv.tlib_tick) {
+ tlib_stop(priv.tlib_tick);
+ priv.tlib_tick = NULL;
+ }
+ if (priv.ops->shutdown_hardware) {
+ priv.ops->shutdown_hardware();
+ }
+}
-static uint32_t tlib_tc_get(rtems_timecounter_simple *tc)
+/** Simple counter **/
+static uint32_t simple_tlib_tc_get(rtems_timecounter_simple *tc)
{
unsigned int clicks = 0;
- if (Clock_handle != NULL) {
- tlib_get_counter(Clock_handle, &clicks);
+ if (priv.tlib_tick != NULL) {
+ tlib_get_counter(priv.tlib_tick, &clicks);
}
return clicks;
}
-static bool tlib_tc_is_pending(rtems_timecounter_simple *tc)
+static bool simple_tlib_tc_is_pending(rtems_timecounter_simple *tc)
{
bool pending = false;
- if (Clock_handle != NULL) {
- pending = tlib_interrupt_pending(Clock_handle, 0) != 0;
+ if (priv.tlib_tick != NULL) {
+ pending = tlib_interrupt_pending(priv.tlib_tick, 0) != 0;
}
return pending;
}
-static uint32_t tlib_tc_get_timecount(struct timecounter *tc)
+static uint32_t simple_tlib_tc_get_timecount(struct timecounter *tc)
{
return rtems_timecounter_simple_downcounter_get(
tc,
- tlib_tc_get,
- tlib_tc_is_pending
+ simple_tlib_tc_get,
+ simple_tlib_tc_is_pending
);
}
-static void tlib_tc_at_tick(rtems_timecounter_simple *tc)
+static rtems_device_driver simple_initialize_counter(void)
{
- /* Nothing to do? */
+ 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 tlib_tc_tick(void)
+static void simple_tlib_tc_at_tick(rtems_timecounter_simple *tc)
{
- rtems_timecounter_simple_downcounter_tick(
- &tlib_tc,
- tlib_tc_get,
- tlib_tc_at_tick
- );
+ /* Nothing to do */
}
/*
- * Clock_isr
- *
- * This is the clock tick interrupt handler.
- *
- * Input parameters:
- * vector - vector number
- *
- * Output parameters: NONE
- *
- * Return values: NONE
- *
+ * Support for shared interrupts. Ack IRQ at source, only handle interrupts
+ * generated from the tick-timer. This is called early in Clock_isr.
*/
-
-void Clock_isr(void *arg_unused)
+static rtems_device_driver simple_at_tick(void)
{
- /*
- * 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;
+ if (tlib_interrupt_pending(priv.tlib_tick, 1) == 0) {
+ return RTEMS_NOT_DEFINED;
+ }
+ return RTEMS_SUCCESSFUL;
+}
- /*
- * Accurate count of ISRs
- */
+static void simple_timecounter_tick(void)
+{
+ rtems_timecounter_simple_downcounter_tick(
+ &priv.tc_simple,
+ simple_tlib_tc_get,
+ simple_tlib_tc_at_tick
+ );
+}
- Clock_driver_ticks += 1;
+static const struct ops ops_simple = {
+ .initialize_counter = simple_initialize_counter,
+ .at_tick = simple_at_tick,
+ .timecounter_tick = simple_timecounter_tick,
+ .shutdown_hardware = NULL,
+};
-#ifdef CLOCK_DRIVER_USE_FAST_IDLE
- do {
- tlib_tc_tick();
- } while ( _Thread_Heir == _Thread_Executing && _Thread_Executing->is_idle );
+/** Subtimer as counter **/
+static uint32_t subtimer_get_timecount(struct timecounter *tc)
+{
+ unsigned int counter;
- return;
+ tlib_get_counter(priv.tlib_counter, &counter);
-#else
+ return 0xffffffff - counter;
+}
-#ifdef CLOCK_DRIVER_ISRS_PER_TICK
- /*
- * The driver is multiple ISRs per clock tick.
- */
+static rtems_device_driver subtimer_initialize_counter(void)
+{
+ unsigned int mask;
+ unsigned int basefreq;
- if ( !Clock_driver_isrs ) {
+ 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;
+ }
- tlib_tc_tick();
+ /* Configure free running counter: GPTIMER */
+ tlib_get_freq(priv.tlib_counter, &basefreq, NULL);
+ tlib_get_widthmask(priv.tlib_counter, &mask);
- Clock_driver_isrs = CLOCK_DRIVER_ISRS_PER_TICK;
- }
- Clock_driver_isrs--;
-#else
+ 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);
- /*
- * The driver is one ISR per clock tick.
- */
- tlib_tc_tick();
-#endif
-#endif
+ return RTEMS_SUCCESSFUL;
}
-/*
- * 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
- *
- */
+static void subtimer_timecounter_tick(void)
+{
+ rtems_timecounter_tick();
+}
-void Clock_exit( void )
+static void subtimer_shutdown_hardware(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;
+ if (priv.tlib_counter) {
+ tlib_stop(priv.tlib_counter);
+ priv.tlib_counter = 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
- */
+static const struct ops ops_subtimer = {
+ .initialize_counter = subtimer_initialize_counter,
+ .timecounter_tick = subtimer_timecounter_tick,
+ .shutdown_hardware = subtimer_shutdown_hardware,
+};
-rtems_device_driver Clock_initialize(
- rtems_device_major_number major,
- rtems_device_minor_number minor,
- void *pargp
-)
+#if defined(LEON3)
+/** DSU timetag as counter **/
+static uint32_t timetag_get_timecount(struct timecounter *tc)
{
- uint64_t frequency;
- unsigned int tick_hz;
+ return leon3_up_counter_low();
+}
- /*
- * 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;
- }
+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);
- /*
- * 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;
+ return RTEMS_SUCCESSFUL;
+}
- tlib_get_freq(Clock_handle, &Clock_basefreq, NULL);
+static void timetag_timecounter_tick(void)
+{
+ rtems_timecounter_tick();
+}
- 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();
+static const struct ops ops_timetag = {
+ .initialize_counter = timetag_initialize_counter,
+ .at_tick = NULL,
+ .timecounter_tick = timetag_timecounter_tick,
+ .shutdown_hardware = NULL,
+};
#endif
- tlib_set_freq(Clock_handle, tick_hz);
-
- rtems_timecounter_simple_install(
- &tlib_tc,
- frequency,
- tick_hz,
- tlib_tc_get_timecount
- );
+#if defined(LEON3)
+/** IRQ(A)MP timestamp as counter **/
+static uint32_t irqamp_get_timecount(struct timecounter *tc)
+{
+ return LEON3_IrqCtrl_Regs->timestamp[0].counter;
+}
- /*
- * 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);
+static rtems_device_driver irqamp_initialize_counter(void)
+{
+ volatile struct irqmp_timestamp_regs *irqmp_ts;
+ static const uint32_t A_TSISEL_FIELD = 0xf;
- /*
- * Register function called at system shutdown
- */
- atexit( Clock_exit );
+ /* 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);
/*
- * If we are counting ISRs per tick, then initialize the counter.
+ * The counter increments whenever a TSISEL field in a Timestamp Control
+ * Register is non-zero.
*/
-
-#ifdef CLOCK_DRIVER_ISRS_PER_TICK
- Clock_driver_isrs = CLOCK_DRIVER_ISRS_PER_TICK;
-#endif
+ irqmp_ts = &LEON3_IrqCtrl_Regs->timestamp[0];
+ irqmp_ts->control = A_TSISEL_FIELD;
return RTEMS_SUCCESSFUL;
}
-/*** Timer Driver Interface ***/
-
-/* Set system clock timer instance */
-void Clock_timer_register(int timer_number)
+static void irqamp_timecounter_tick(void)
{
- Clock_timer = timer_number;
+ 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 (clockdrv_shell.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, old ) \
+ 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_support_shutdown_hardware() \
+ tlib_clock_shutdown_hardware()
+
+#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/clockdrv_shell.h"
+
+#endif /* RTEMS_DRVMGR_STARTUP */
+