leon, clock: new driver manager clock driver

- Compatible with SMP
- Selects timecounter depending on features available
- Fixes problem with time going to fast on SMP

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.

GR740:
 RTEMS TESTSUITE FAILURE SUMMARY

 Result Test ExecRes ConsoleRes ExitCode1 ExitCode2
 FAIL: ./fstests/imfs_fsscandir01 OK FAIL 5 0
 FAIL: ./fstests/jffs2_fsscandir01 OK FAIL 5 0
 FAIL: ./fstests/mdosfs_fsscandir01 OK FAIL 5 0
 FAIL: ./fstests/mimfs_fsscandir01 OK FAIL 5 0
 FAIL: ./fstests/mrfs_fsscandir01 OK FAIL 5 0
 FAIL: ./psxtests/psxshm01 FAIL FAIL N/A N/A
 FAIL: ./psxtests/psxshm02 FAIL FAIL N/A N/A
 FAIL: ./sptests/spinternalerror01 OK N/A -559038737 1611526157
 FAIL: ./sptests/sptimecounter01 OK N/A 5 0

 SUMMARY
  Tests failing:    9
  Tests successful: 578

---
GR712RC:
 RTEMS TESTSUITE FAILURE SUMMARY

 Result Test ExecRes ConsoleRes ExitCode1 ExitCode2
 FAIL: ./smptests/smpipi01 FAIL FAIL N/A N/A
 FAIL: ./smptests/smpthreadlife01 FAIL FAIL N/A N/A
 FAIL: ./fstests/imfs_fsscandir01 OK FAIL 5 0
 FAIL: ./fstests/jffs2_fsscandir01 OK FAIL 5 0
 FAIL: ./fstests/mdosfs_fsscandir01 OK FAIL 5 0
 FAIL: ./fstests/mimfs_fsscandir01 OK FAIL 5 0
 FAIL: ./fstests/mrfs_fsscandir01 OK FAIL 5 0
 FAIL: ./psxtests/psxshm01 FAIL FAIL N/A N/A
 FAIL: ./psxtests/psxshm02 FAIL FAIL N/A N/A
 FAIL: ./sptests/spinternalerror01 OK N/A -559038737 1611526157
 FAIL: ./sptests/sptimecounter01 OK N/A 5 0

 SUMMARY
  Tests failing:    11
  Tests successful: 576
This commit is contained in:
Martin Aberg
2017-02-23 16:33:21 +01:00
committed by Daniel Hellstrom
parent 78e56096c8
commit f600458d05
3 changed files with 423 additions and 244 deletions

View File

@@ -27,6 +27,10 @@ struct tlib_dev;
typedef void (*tlib_isr_t)(void *data);
enum {
TLIB_FLAGS_BROADCAST = 0x01
};
struct tlib_drv {
/*** Functions ***/
void (*reset)(struct tlib_dev *hand);
@@ -35,7 +39,7 @@ struct tlib_drv {
unsigned int *basefreq,
unsigned int *tickrate);
int (*set_freq)(struct tlib_dev *hand, unsigned int tickrate);
void (*irq_reg)(struct tlib_dev *hand, tlib_isr_t func, void *data);
void (*irq_reg)(struct tlib_dev *hand, tlib_isr_t func, void *data, int flags);
void (*irq_unreg)(struct tlib_dev *hand, tlib_isr_t func,void *data);
void (*start)(struct tlib_dev *hand, int once);
void (*stop)(struct tlib_dev *hand);
@@ -122,7 +126,7 @@ static inline void tlib_irq_unregister(void *hand)
}
/* Register ISR at Timer ISR */
static inline void tlib_irq_register(void *hand, tlib_isr_t func, void *data)
static inline void tlib_irq_register(void *hand, tlib_isr_t func, void *data, int flags)
{
struct tlib_dev *dev = hand;
@@ -130,7 +134,7 @@ static inline void tlib_irq_register(void *hand, tlib_isr_t func, void *data)
tlib_irq_unregister(hand);
dev->isr_func = func;
dev->isr_data = data;
dev->drv->irq_reg(dev, func, data);
dev->drv->irq_reg(dev, func, data, flags);
}
/* Start Timer, ISRs will be generated if enabled.

View File

@@ -48,6 +48,11 @@
#include <stdio.h>
#endif
#ifdef RTEMS_SMP
#include <rtems/score/processormask.h>
#include <rtems/score/smpimpl.h>
#endif
/* GPTIMER Core Configuration Register (READ-ONLY) */
#define GPTIMER_CFG_TIMERS_BIT 0
#define GPTIMER_CFG_IRQ_BIT 3
@@ -416,7 +421,7 @@ static int gptimer_tlib_set_freq(struct tlib_dev *hand, unsigned int tickrate)
return 0;
}
static void gptimer_tlib_irq_reg(struct tlib_dev *hand, tlib_isr_t func, void *data)
static void gptimer_tlib_irq_reg(struct tlib_dev *hand, tlib_isr_t func, void *data, int flags)
{
struct gptimer_timer *timer = (struct gptimer_timer *)hand;
struct gptimer_priv *priv = priv_from_timer(timer);
@@ -437,6 +442,13 @@ static void gptimer_tlib_irq_reg(struct tlib_dev *hand, tlib_isr_t func, void *d
priv->isr_installed++;
}
#if RTEMS_SMP
if (flags & TLIB_FLAGS_BROADCAST) {
drvmgr_interrupt_set_affinity(priv->dev, timer->tindex,
_SMP_Online_processors);
}
#endif
timer->tregs->ctrl |= GPTIMER_CTRL_IE;
}

View File

@@ -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
/* 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;
#if defined(LEON3)
#include <leon.h>
#endif
void Clock_exit(void);
void Clock_isr(void *arg_unused);
struct ops {
/*
* Set up the free running counter using the Timecounter or Simple
* Timecounter interface.
*/
rtems_device_driver (*initialize_counter)(void);
static rtems_timecounter_simple tlib_tc;
/*
* 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);
static uint32_t tlib_tc_get(rtems_timecounter_simple *tc)
{
unsigned int clicks = 0;
/*
* Typically calls rtems_timecounter_tick(). A specialized clock driver may
* use for example rtems_timecounter_tick_simple() instead.
*/
void (*timecounter_tick)(void);
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
);
}
/*
* 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);
};
/*
* Clock_isr
*
* This is the clock tick interrupt handler.
*
* Input parameters:
* vector - vector number
*
* Output parameters: NONE
*
* Return values: NONE
* 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_isr(void *arg_unused)
/* "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)
{
/*
* 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
priv.tlib_tick_index = timer_number;
priv.tlib_counter_index = timer_number + 1;
}
/*
* 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 )
static rtems_device_driver tlib_clock_find_timer(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 ) {
/* 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;
}
/*
* 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;
}
/* Select which operation set to use */
#ifndef RTEMS_SMP
priv.ops = &ops_simple;
#else
frequency = 1000000;
tick_hz = rtems_configuration_get_microseconds_per_tick();
#endif
/* 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;
tlib_set_freq(Clock_handle, tick_hz);
irqmp_ts = &LEON3_IrqCtrl_Regs->timestamp[0];
if (leon3_irqmp_has_timestamp(irqmp_ts)) {
priv.ops = &ops_irqamp;
return RTEMS_SUCCESSFUL;
}
}
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;
/* Take another subtimer as the final option. */
priv.ops = &ops_subtimer;
#endif
return RTEMS_SUCCESSFUL;
}
/*** Timer Driver Interface ***/
/* Set system clock timer instance */
void Clock_timer_register(int timer_number)
static rtems_device_driver tlib_clock_initialize_hardware(void)
{
Clock_timer = timer_number;
/* 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;
}
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();
}
}
/** 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,
};
/** 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,
};
#if defined(LEON3)
/** 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,
};
#endif
#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;
}
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 (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 */