From f600458d05ee3f15f75292e15aea4d913862d132 Mon Sep 17 00:00:00 2001 From: Martin Aberg Date: Thu, 23 Feb 2017 16:33:21 +0100 Subject: 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 --- c/src/lib/libbsp/sparc/shared/include/tlib.h | 10 +- c/src/lib/libbsp/sparc/shared/timer/gptimer.c | 14 +- c/src/lib/libbsp/sparc/shared/timer/tlib_ckinit.c | 543 ++++++++++++++-------- 3 files changed, 373 insertions(+), 194 deletions(-) diff --git a/c/src/lib/libbsp/sparc/shared/include/tlib.h b/c/src/lib/libbsp/sparc/shared/include/tlib.h index 5e49dd488e..debb8c8215 100644 --- a/c/src/lib/libbsp/sparc/shared/include/tlib.h +++ b/c/src/lib/libbsp/sparc/shared/include/tlib.h @@ -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. diff --git a/c/src/lib/libbsp/sparc/shared/timer/gptimer.c b/c/src/lib/libbsp/sparc/shared/timer/gptimer.c index f47952ff74..e701211ee5 100644 --- a/c/src/lib/libbsp/sparc/shared/timer/gptimer.c +++ b/c/src/lib/libbsp/sparc/shared/timer/gptimer.c @@ -48,6 +48,11 @@ #include #endif +#ifdef RTEMS_SMP +#include +#include +#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; } 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 #include -#include +#include #include #include #include #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 +#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 */ + -- cgit v1.2.3