/**
* @file
*
* @ingroup RTEMSAPIClassicTimecounter
*
* @brief This header file provides the Timecounter Support API.
*/
/*
* Copyright (c) 2015 embedded brains GmbH. All rights reserved.
*
* embedded brains GmbH
* Dornierstr. 4
* 82178 Puchheim
* Germany
* <rtems@embedded-brains.de>
*
* 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.
*/
#ifndef _RTEMS_TIMECOUNTER_H
#define _RTEMS_TIMECOUNTER_H
#include <rtems/score/timecounter.h>
#include <rtems/score/basedefs.h>
#ifdef __cplusplus
extern "C" {
#endif /* __cplusplus */
/**
* @defgroup RTEMSAPIClassicTimecounter Timecounter Support
*
* @ingroup RTEMSAPIClassic
*
* @brief The timecounter support provides directives to implement clock device
* drivers.
*
* @{
*/
/**
* @brief Timecounter quality for the clock drivers.
*
* Timecounter with higher quality value are used in favour of those with lower
* quality value.
*/
#define RTEMS_TIMECOUNTER_QUALITY_CLOCK_DRIVER 100
/**
* @copydoc _Timecounter_Install()
*
* Below is an exemplary code snippet that shows the adjustable parameters and
* the following call of the install routine.
*
* @code
* struct timecounter tc;
*
* uint32_t get_timecount( struct timecounter *tc )
* {
* return some_free_running_counter;
* }
*
* void install( void )
* {
* tc.tc_get_timecount = get_timecount;
* tc.tc_counter_mask = 0xffffffff;
* tc.tc_frequency = 123456;
* tc.tc_quality = RTEMS_TIMECOUNTER_QUALITY_CLOCK_DRIVER;
* rtems_timecounter_install( &tc );
* }
* @endcode
*/
RTEMS_INLINE_ROUTINE void rtems_timecounter_install(
struct timecounter *tc
)
{
_Timecounter_Install( tc );
}
/**
* @copydoc _Timecounter_Tick()
*/
RTEMS_INLINE_ROUTINE void rtems_timecounter_tick(void)
{
_Timecounter_Tick();
}
/**
* @brief Simple timecounter to support legacy clock drivers.
*/
typedef struct {
struct timecounter tc;
uint64_t scaler;
uint32_t real_interval;
uint32_t binary_interval;
} rtems_timecounter_simple;
/**
* @brief At tick handling done under protection of the timecounter lock.
*/
typedef void rtems_timecounter_simple_at_tick(
rtems_timecounter_simple *tc
);
/**
* @brief Returns the current value of a simple timecounter.
*/
typedef uint32_t rtems_timecounter_simple_get(
rtems_timecounter_simple *tc
);
/**
* @brief Returns true if the interrupt of a simple timecounter is pending, and
* false otherwise.
*/
typedef bool rtems_timecounter_simple_is_pending(
rtems_timecounter_simple *tc
);
/**
* @brief Initializes and installs a simple timecounter.
*
* A simple timecounter can be used if the hardware provides no free running
* counter. A periodic hardware counter must be provided. The counter period
* must be synchronous to the clock tick. The counter ticks per clock tick is
* scaled up to the next power of two.
*
* @param[in] tc Zero initialized simple timecounter.
* @param[in] counter_frequency_in_hz The hardware counter frequency in Hz.
* @param[in] counter_ticks_per_clock_tick The hardware counter ticks per clock
* tick.
* @param[in] get_timecount The method to get the current time count.
*
* @code
* #include <rtems/timecounter.h>
*
* static rtems_timecounter_simple some_tc;
*
* static uint32_t some_tc_get( rtems_timecounter_simple *tc )
* {
* return some.value;
* }
*
* static bool some_tc_is_pending( rtems_timecounter_simple *tc )
* {
* return some.is_pending;
* }
*
* static uint32_t some_tc_get_timecount( struct timecounter *tc )
* {
* return rtems_timecounter_simple_downcounter_get(
* tc,
* some_tc_get,
* some_tc_is_pending
* );
* }
*
* static void some_tc_tick( void )
* {
* rtems_timecounter_simple_downcounter_tick( &some_tc, some_tc_get );
* }
*
* void some_tc_init( void )
* {
* uint64_t us_per_tick;
* uint32_t counter_frequency_in_hz;
* uint32_t counter_ticks_per_clock_tick;
*
* us_per_tick = rtems_configuration_get_microseconds_per_tick();
* counter_frequency_in_hz = some_tc_get_frequency();
* counter_ticks_per_clock_tick =
* (uint32_t) ( counter_frequency_in_hz * us_per_tick ) / 1000000;
*
* some_tc_init_hardware( counter_ticks_per_clock_tick );
* some_tc_init_clock_tick_interrupt( some_tc_tick );
*
* rtems_timecounter_simple_install(
* &some_tc,
* counter_frequency_in_hz,
* counter_ticks_per_clock_tick,
* some_tc_get_timecount
* );
* }
* @endcode
*
* @see rtems_timecounter_simple_downcounter_get(),
* rtems_timecounter_simple_downcounter_tick(),
* rtems_timecounter_simple_upcounter_get() and
* rtems_timecounter_simple_upcounter_tick().
*/
void rtems_timecounter_simple_install(
rtems_timecounter_simple *tc,
uint32_t counter_frequency_in_hz,
uint32_t counter_ticks_per_clock_tick,
timecounter_get_t *get_timecount
);
/**
* @brief Maps a simple timecounter value into its binary frequency domain.
*
* @param[in] tc The simple timecounter.
* @param[in] value The value of the simple timecounter.
*
* @return The scaled value.
*/
RTEMS_INLINE_ROUTINE uint32_t rtems_timecounter_simple_scale(
const rtems_timecounter_simple *tc,
uint32_t value
)
{
return (uint32_t) ( ( value * tc->scaler ) >> 32 );
}
/**
* @brief Performs a simple timecounter tick for downcounters.
*
* @param[in] tc The simple timecounter.
* @param[in] get The method to get the value of the simple timecounter.
* @param[in] at_tick The method to perform work under timecounter lock
* protection at this tick, e.g. clear a pending flag.
*/
RTEMS_INLINE_ROUTINE void rtems_timecounter_simple_downcounter_tick(
rtems_timecounter_simple *tc,
rtems_timecounter_simple_get get,
rtems_timecounter_simple_at_tick at_tick
)
{
ISR_lock_Context lock_context;
uint32_t current;
_Timecounter_Acquire( &lock_context );
( *at_tick )( tc );
current = rtems_timecounter_simple_scale(
tc,
tc->real_interval - ( *get )( tc )
);
_Timecounter_Tick_simple( tc->binary_interval, current, &lock_context );
}
/**
* @brief Performs a simple timecounter tick for upcounters.
*
* @param[in] tc The simple timecounter.
* @param[in] get The method to get the value of the simple timecounter.
* @param[in] at_tick The method to perform work under timecounter lock
* protection at this tick, e.g. clear a pending flag.
*/
RTEMS_INLINE_ROUTINE void rtems_timecounter_simple_upcounter_tick(
rtems_timecounter_simple *tc,
rtems_timecounter_simple_get get,
rtems_timecounter_simple_at_tick at_tick
)
{
ISR_lock_Context lock_context;
uint32_t current;
_Timecounter_Acquire( &lock_context );
( *at_tick )( tc );
current = rtems_timecounter_simple_scale( tc, ( *get )( tc ) );
_Timecounter_Tick_simple( tc->binary_interval, current, &lock_context );
}
/**
* @brief Gets the simple timecounter value mapped to its binary frequency
* domain for downcounters.
*
* @param[in] tc The simple timecounter.
* @param[in] get The method to get the value of the simple timecounter.
* @param[in] is_pending The method which indicates if the interrupt of the
* simple timecounter is pending.
*/
RTEMS_INLINE_ROUTINE uint32_t rtems_timecounter_simple_downcounter_get(
struct timecounter *tc_base,
rtems_timecounter_simple_get get,
rtems_timecounter_simple_is_pending is_pending
)
{
rtems_timecounter_simple *tc;
uint32_t counter;
uint32_t interval;
tc = (rtems_timecounter_simple *) tc_base;
counter = ( *get )( tc );
interval = tc->real_interval;
if ( ( *is_pending )( tc ) ) {
counter = ( *get )( tc );
interval *= 2;
}
return rtems_timecounter_simple_scale( tc, interval - counter );
}
/**
* @brief Gets the simple timecounter value mapped to its binary frequency
* domain for upcounters.
*
* @param[in] tc The simple timecounter.
* @param[in] get The method to get the value of the simple timecounter.
* @param[in] is_pending The method which indicates if the interrupt of the
* simple timecounter is pending.
*/
RTEMS_INLINE_ROUTINE uint32_t rtems_timecounter_simple_upcounter_get(
struct timecounter *tc_base,
rtems_timecounter_simple_get get,
rtems_timecounter_simple_is_pending is_pending
)
{
rtems_timecounter_simple *tc;
uint32_t counter;
uint32_t interval;
tc = (rtems_timecounter_simple *) tc_base;
counter = ( *get )( tc );
interval = 0;
if ( ( *is_pending )( tc ) ) {
counter = ( *get )( tc );
interval = tc->real_interval;
}
return rtems_timecounter_simple_scale( tc, interval + counter );
}
/** @} */
#ifdef __cplusplus
}
#endif /* __cplusplus */
#endif /* _RTEMS_TIMECOUNTER_H */