diff options
author | Sebastian Huber <sebastian.huber@embedded-brains.de> | 2018-12-22 18:31:04 +0100 |
---|---|---|
committer | Sebastian Huber <sebastian.huber@embedded-brains.de> | 2019-01-22 12:46:33 +0100 |
commit | 7eb606d393306da25fd6e6aa7f8595ffb2e924fc (patch) | |
tree | 085befd6fe5e29d229fec9683735516d48e9d41e /bsps/shared/grlib/btimer/tlib_ckinit.c | |
parent | grlib: Move header files (diff) | |
download | rtems-7eb606d393306da25fd6e6aa7f8595ffb2e924fc.tar.bz2 |
grlib: Move source files
Update #3678.
Diffstat (limited to 'bsps/shared/grlib/btimer/tlib_ckinit.c')
-rw-r--r-- | bsps/shared/grlib/btimer/tlib_ckinit.c | 442 |
1 files changed, 442 insertions, 0 deletions
diff --git a/bsps/shared/grlib/btimer/tlib_ckinit.c b/bsps/shared/grlib/btimer/tlib_ckinit.c new file mode 100644 index 0000000000..e43c8fdd8e --- /dev/null +++ b/bsps/shared/grlib/btimer/tlib_ckinit.c @@ -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 */ + |