diff --git a/arch/tricore/Kconfig b/arch/tricore/Kconfig index 07c479d35e1..25885ded5d9 100644 --- a/arch/tricore/Kconfig +++ b/arch/tricore/Kconfig @@ -41,6 +41,7 @@ config ARCH_CHIP_TC397 select ARCH_TC3XX select ALARM_ARCH select ONESHOT + select ONESHOT_COUNT ---help--- AURIX TC39x family: TC397 diff --git a/arch/tricore/src/common/tricore_internal.h b/arch/tricore/src/common/tricore_internal.h index e90dbe25510..b6df70c1ace 100644 --- a/arch/tricore/src/common/tricore_internal.h +++ b/arch/tricore/src/common/tricore_internal.h @@ -46,6 +46,8 @@ * Pre-processor Definitions ****************************************************************************/ +#define SCU_FREQUENCY 100000000UL + /* Determine which (if any) console driver to use. If a console is enabled * and no other console device is specified, then a serial console is * assumed. diff --git a/arch/tricore/src/common/tricore_systimer.c b/arch/tricore/src/common/tricore_systimer.c index c3d574dc92e..e54345962a6 100644 --- a/arch/tricore/src/common/tricore_systimer.c +++ b/arch/tricore/src/common/tricore_systimer.c @@ -34,6 +34,24 @@ #include "IfxStm.h" +/**************************************************************************** + * Pre-processor Definitions + ****************************************************************************/ + +/* Since the tricore hardware timer triggers an interrupt only when the + * compare value is equal to the counter, setting a compare value that has + * already timed out will not trigger an interrupt. To avoid missing + * interrupts when setting the timer, we should set a minimum delay. + * The minimum delay is calculated based on the CPU frequency and the timer + * frequency. We assume that the worst-case execution time for setting the + * timer does not exceed 40 CPU cycles, and calculate the minimum timer + * delay accordingly. + * 40 CPU cycles (100ns at 400Mhz) ~ 10 timer cycles (for 100 Mhz timer). + */ + +#define TRICORE_SYSTIMER_MIN_DELAY \ + (40ull * SCU_FREQUENCY / IFX_CFG_CPU_CLOCK_FREQUENCY) + /**************************************************************************** * Private Types ****************************************************************************/ @@ -46,40 +64,7 @@ struct tricore_systimer_lowerhalf_s { struct oneshot_lowerhalf_s lower; - volatile void *tbase; - uint64_t freq; - uint64_t alarm; - spinlock_t lock; -}; - -/**************************************************************************** - * Private Function Prototypes - ****************************************************************************/ - -static int tricore_systimer_max_delay(struct oneshot_lowerhalf_s *lower, - struct timespec *ts); -static int tricore_systimer_start(struct oneshot_lowerhalf_s *lower, - const struct timespec *ts); -static int tricore_systimer_cancel(struct oneshot_lowerhalf_s *lower, - struct timespec *ts); -static int tricore_systimer_current(struct oneshot_lowerhalf_s *lower, - struct timespec *ts); - -/**************************************************************************** - * Private Data - ****************************************************************************/ - -static const struct oneshot_operations_s g_tricore_systimer_ops = -{ - .max_delay = tricore_systimer_max_delay, - .start = tricore_systimer_start, - .cancel = tricore_systimer_cancel, - .current = tricore_systimer_current, -}; - -static struct tricore_systimer_lowerhalf_s g_systimer_lower = -{ - .lower.ops = &g_tricore_systimer_ops, + volatile void *tbase; }; /**************************************************************************** @@ -124,59 +109,87 @@ tricore_systimer_set_timecmp(struct tricore_systimer_lowerhalf_s *priv, * lower An instance of the lower-half oneshot state structure. This * structure must have been previously initialized via a call to * oneshot_initialize(); - * ts The location in which to return the maximum delay. * * Returned Value: - * Zero (OK) is returned on success; a negated errno value is returned - * on failure. + * The maximum delay value. * ****************************************************************************/ -static int tricore_systimer_max_delay(struct oneshot_lowerhalf_s *lower, - struct timespec *ts) +static clkcnt_t tricore_systimer_max_delay(struct oneshot_lowerhalf_s *lower) { - ts->tv_sec = UINT32_MAX; - ts->tv_nsec = NSEC_PER_SEC - 1; - - return 0; + return UINT32_MAX; } /**************************************************************************** * Name: tricore_systimer_start * * Description: - * Start the oneshot timer + * Start the oneshot timer. Note that the tricore systimer is special, the + * IRQ is only triggered when timecmp == mtime, so we should avoid the case + * that we miss the timecmp. * * Input Parameters: * lower An instance of the lower-half oneshot state structure. This * structure must have been previously initialized via a call to * oneshot_initialize(); - * handler The function to call when when the oneshot timer expires. - * arg An opaque argument that will accompany the callback. - * ts Provides the duration of the one shot timer. + * delta Provides the duration of delta count. * * Returned Value: - * Zero (OK) is returned on success; a negated errno value is returned - * on failure. + * None. * ****************************************************************************/ -static int tricore_systimer_start(struct oneshot_lowerhalf_s *lower, - const struct timespec *ts) +static void tricore_systimer_start(struct oneshot_lowerhalf_s *lower, + clkcnt_t delta) { struct tricore_systimer_lowerhalf_s *priv = (struct tricore_systimer_lowerhalf_s *)lower; - uint64_t mtime = tricore_systimer_get_time(priv); + irqstate_t flags; + uint64_t mtime; - priv->alarm = mtime + ts->tv_sec * priv->freq + - ts->tv_nsec * priv->freq / NSEC_PER_SEC; - if (priv->alarm < mtime) - { - priv->alarm = UINT64_MAX; - } + delta = delta < TRICORE_SYSTIMER_MIN_DELAY ? + TRICORE_SYSTIMER_MIN_DELAY : delta; + flags = up_irq_save(); + mtime = tricore_systimer_get_time(priv); - tricore_systimer_set_timecmp(priv, priv->alarm); - return 0; + tricore_systimer_set_timecmp(priv, mtime + delta); + + up_irq_restore(flags); +} + +/**************************************************************************** + * Name: tricore_systimer_start_absolute + * + * Description: + * Start the oneshot timer. Note that the tricore systimer is special, the + * IRQ is only triggered when timecmp == mtime, so we should avoid the case + * that we miss the timecmp. + * + * Input Parameters: + * lower An instance of the lower-half oneshot state structure. This + * structure must have been previously initialized via a call to + * oneshot_initialize(); + * expected Target + * + * Returned Value: + * None. + * + ****************************************************************************/ + +static void +tricore_systimer_start_absolute(struct oneshot_lowerhalf_s *lower, + clkcnt_t expected) +{ + struct tricore_systimer_lowerhalf_s *priv = + (struct tricore_systimer_lowerhalf_s *)lower; + + irqstate_t flags = up_irq_save(); + uint64_t min_expected = tricore_systimer_get_time(priv) + + TRICORE_SYSTIMER_MIN_DELAY; + expected = expected < min_expected ? min_expected : expected; + tricore_systimer_set_timecmp(priv, expected); + + up_irq_restore(flags); } /**************************************************************************** @@ -192,44 +205,18 @@ static int tricore_systimer_start(struct oneshot_lowerhalf_s *lower, * lower Caller allocated instance of the oneshot state structure. This * structure must have been previously initialized via a call to * oneshot_initialize(); - * ts The location in which to return the time remaining on the - * oneshot timer. A time of zero is returned if the timer is - * not running. * * Returned Value: - * Zero (OK) is returned on success. A call to up_timer_cancel() when - * the timer is not active should also return success; a negated errno - * value is returned on any failure. + * None. * ****************************************************************************/ -static int tricore_systimer_cancel(struct oneshot_lowerhalf_s *lower, - struct timespec *ts) +static void tricore_systimer_cancel(struct oneshot_lowerhalf_s *lower) { struct tricore_systimer_lowerhalf_s *priv = (struct tricore_systimer_lowerhalf_s *)lower; - uint64_t mtime; tricore_systimer_set_timecmp(priv, UINT64_MAX); - - mtime = tricore_systimer_get_time(priv); - if (priv->alarm > mtime) - { - uint64_t nsec = (priv->alarm - mtime) * - NSEC_PER_SEC / priv->freq; - - ts->tv_sec = nsec / NSEC_PER_SEC; - ts->tv_nsec = nsec % NSEC_PER_SEC; - } - else - { - ts->tv_sec = 0; - ts->tv_nsec = 0; - } - - priv->alarm = 0; - - return 0; } /**************************************************************************** @@ -242,27 +229,18 @@ static int tricore_systimer_cancel(struct oneshot_lowerhalf_s *lower, * lower Caller allocated instance of the oneshot state structure. This * structure must have been previously initialized via a call to * oneshot_initialize(); - * ts The location in which to return the current time. A time of zero - * is returned for the initialization moment. * * Returned Value: - * Zero (OK) is returned on success, a negated errno value is returned on - * any failure. + * Current timer count. * ****************************************************************************/ -static int tricore_systimer_current(struct oneshot_lowerhalf_s *lower, - struct timespec *ts) +static clkcnt_t tricore_systimer_current(struct oneshot_lowerhalf_s *lower) { struct tricore_systimer_lowerhalf_s *priv = (struct tricore_systimer_lowerhalf_s *)lower; - uint64_t mtime = tricore_systimer_get_time(priv); - uint64_t nsec = mtime / (priv->freq / USEC_PER_SEC) * NSEC_PER_USEC; - ts->tv_sec = nsec / NSEC_PER_SEC; - ts->tv_nsec = nsec % NSEC_PER_SEC; - - return 0; + return tricore_systimer_get_time(priv); } /**************************************************************************** @@ -278,12 +256,31 @@ static int tricore_systimer_interrupt(int irq, void *context, void *arg) { struct tricore_systimer_lowerhalf_s *priv = arg; - tricore_systimer_set_timecmp(priv, UINT64_MAX); + /* We do not need to clear the compare register here. */ + oneshot_process_callback(&priv->lower); return 0; } +/**************************************************************************** + * Private Data + ****************************************************************************/ + +static const struct oneshot_operations_s g_tricore_oneshot_ops = +{ + .current = tricore_systimer_current, + .start = tricore_systimer_start, + .start_absolute = tricore_systimer_start_absolute, + .cancel = tricore_systimer_cancel, + .max_delay = tricore_systimer_max_delay +}; + +static struct tricore_systimer_lowerhalf_s g_tricore_oneshot_lowerhalf = +{ + .lower.ops = &g_tricore_oneshot_ops +}; + /**************************************************************************** * Public Functions ****************************************************************************/ @@ -300,11 +297,13 @@ static int tricore_systimer_interrupt(int irq, void *context, void *arg) struct oneshot_lowerhalf_s * tricore_systimer_initialize(volatile void *tbase, int irq, uint64_t freq) { - struct tricore_systimer_lowerhalf_s *priv = &g_systimer_lower; + struct tricore_systimer_lowerhalf_s *priv = &g_tricore_oneshot_lowerhalf; priv->tbase = tbase; - priv->freq = freq; - spin_lock_init(&priv->lock); + + ASSERT(freq <= UINT32_MAX); + + oneshot_count_init(&priv->lower, (uint32_t)freq); IfxStm_setCompareControl(tbase, IfxStm_Comparator_0, diff --git a/arch/tricore/src/tc3xx/tc3xx_timerisr.c b/arch/tricore/src/tc3xx/tc3xx_timerisr.c index c52f77adf1a..554cc429765 100644 --- a/arch/tricore/src/tc3xx/tc3xx_timerisr.c +++ b/arch/tricore/src/tc3xx/tc3xx_timerisr.c @@ -34,12 +34,6 @@ #include "IfxStm.h" -/**************************************************************************** - * Pre-processor Definitions - ****************************************************************************/ - -#define SCU_FREQUENCY 100000000UL - /**************************************************************************** * Public Functions ****************************************************************************/