This is an automated email from the ASF dual-hosted git repository. acassis pushed a commit to branch master in repository https://gitbox.apache.org/repos/asf/nuttx.git
The following commit(s) were added to refs/heads/master by this push: new 63782c7ff2 arch/xmc4 Add tickless support 63782c7ff2 is described below commit 63782c7ff277e861aad41e4c43a362304ab615df Author: Nicolas Lemblé <nicolas.lem...@gmail.com> AuthorDate: Wed Feb 21 15:01:38 2024 +0100 arch/xmc4 Add tickless support --- arch/arm/Kconfig | 1 + arch/arm/src/xmc4/Make.defs | 3 + arch/arm/src/xmc4/xmc4_ccu4.c | 195 +++++++++++++ arch/arm/src/xmc4/xmc4_ccu4.h | 64 +++++ arch/arm/src/xmc4/xmc4_tickless.c | 574 ++++++++++++++++++++++++++++++++++++++ 5 files changed, 837 insertions(+) diff --git a/arch/arm/Kconfig b/arch/arm/Kconfig index b663dd3f49..d442eb6946 100644 --- a/arch/arm/Kconfig +++ b/arch/arm/Kconfig @@ -600,6 +600,7 @@ config ARCH_CHIP_XMC4 select ARCH_HAVE_I2CRESET select ARM_HAVE_MPU_UNIFIED select ARMV7M_HAVE_STACKCHECK + select ARCH_HAVE_TICKLESS ---help--- Infineon XMC4xxx(ARM Cortex-M4) architectures diff --git a/arch/arm/src/xmc4/Make.defs b/arch/arm/src/xmc4/Make.defs index 367ad7e629..2a3cb30852 100644 --- a/arch/arm/src/xmc4/Make.defs +++ b/arch/arm/src/xmc4/Make.defs @@ -25,6 +25,7 @@ include armv7-m/Make.defs CHIP_CSRCS = xmc4_allocateheap.c xmc4_clockconfig.c xmc4_clockutils.c CHIP_CSRCS += xmc4_clrpend.c xmc4_flash.c xmc4_gpio.c xmc4_irq.c CHIP_CSRCS += xmc4_lowputc.c xmc4_serial.c xmc4_start.c xmc4_usic.c +CHIP_CSRCS += xmc4_ccu4.c # Configuration-dependent Kinetis files @@ -34,6 +35,8 @@ endif ifneq ($(CONFIG_SCHED_TICKLESS),y) CHIP_CSRCS += xmc4_timerisr.c +else +CHIP_CSRCS += xmc4_tickless.c endif ifeq ($(CONFIG_BUILD_PROTECTED),y) diff --git a/arch/arm/src/xmc4/xmc4_ccu4.c b/arch/arm/src/xmc4/xmc4_ccu4.c new file mode 100644 index 0000000000..8d8519d099 --- /dev/null +++ b/arch/arm/src/xmc4/xmc4_ccu4.c @@ -0,0 +1,195 @@ +/**************************************************************************** + * arch/arm/src/xmc4/xmc4_ccu4.c + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. The + * ASF licenses this file to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance with the + * License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations + * under the License. + * + ****************************************************************************/ + +/**************************************************************************** + * XMC CCU Driver + * + * For now, this file contains only helper methods mandatory for xmc tickless + * feature. Contibutions are welcomed. + * + ****************************************************************************/ + +/**************************************************************************** + * Included Files + ****************************************************************************/ + +#include <nuttx/config.h> +#include <arch/board/board.h> + +#include <assert.h> +#include <errno.h> +#include <debug.h> + +#include "arm_internal.h" +#include "hardware/xmc4_ccu4.h" +#include "xmc4_ccu4.h" + +#define CCU4_NDIVIDERS 15 + +static const uint8_t g_log2divider[CCU4_NDIVIDERS] = +{ + 1, /* TIMER_CLOCK1 -> div2 */ + 2, /* TIMER_CLOCK1 -> div4 */ + 3, /* TIMER_CLOCK2 -> div8 */ + 4, /* TIMER_CLOCK2 -> div16 */ + 5, /* TIMER_CLOCK3 -> div32 */ + 6, /* TIMER_CLOCK3 -> div64 */ + 7, /* TIMER_CLOCK4 -> div128 */ + 8, /* TIMER_CLOCK4 -> div256 */ + 9, /* TIMER_CLOCK4 -> div512 */ + 10, /* TIMER_CLOCK4 -> div1024 */ + 11, /* TIMER_CLOCK4 -> div2048 */ + 12, /* TIMER_CLOCK4 -> div4096 */ + 13, /* TIMER_CLOCK4 -> div8192 */ + 14, /* TIMER_CLOCK4 -> div16384 */ + 15 /* TIMER_CLOCK4 -> div32769 */ +}; + +/**************************************************************************** + * Private Functions + ****************************************************************************/ + +/**************************************************************************** + * Name: xmc4_ccu4_divfreq_lookup + * + * Description: + * Given the TC input frequency (Ftcin) and a divider index, return the + * value of the divided frequency + * + * Input Parameters: + * ftcin - TC input frequency + * ndx - Divider index + * + * Returned Value: + * The divided frequency value + * + ****************************************************************************/ + +static uint32_t xmc4_ccu4_divfreq_lookup(uint32_t ftcin, int ndx) +{ + return ftcin >> g_log2divider[ndx]; +} + +/**************************************************************************** + * Name: xmc4_ccu4_freqdiv_lookup + * + * Description: + * Given the TC input frequency (Ftcin) and a divider index, return the + * value of the Ftcin divider. + * + * Input Parameters: + * ftcin - TC input frequency + * ndx - Divider index + * + * Returned Value: + * The Ftcin input divider value + * + ****************************************************************************/ + +static int xmc4_ccu4_freqdiv_lookup(uint32_t ftcin, int ndx) +{ + return 1 << g_log2divider[ndx]; +} + +/**************************************************************************** + * Public Functions + ****************************************************************************/ + +/**************************************************************************** + * Name: xmc4_ccu4_divisor + * + * Description: + * Finds the best MCK divisor given the timer frequency and MCK. The + * result is guaranteed to satisfy the following equation: + * + * (Ftcin / (div * 65536)) <= freq <= (Ftcin / dev) + * + * where: + * freq - the desired frequency + * Ftcin - The timer/counter input frequency + * div - With DIV being the highest possible value. + * + * Input Parameters: + * frequency Desired timer frequency. + * div Divisor value. + * pssiv PSSIV field value for divisor. + * + * Returned Value: + * Zero (OK) if a proper divisor has been found, otherwise a negated errno + * value indicating the nature of the failure. + * + ****************************************************************************/ + +int xmc4_ccu4_divisor(uint32_t frequency, uint32_t *div, uint32_t *pssiv) +{ + uint32_t ftcin = BOARD_CCU_FREQUENCY; + int ndx = 0; + + tmrinfo("frequency=%" PRIu32 "\n", frequency); + + /* Satisfy lower bound. That is, the value of the divider such that: + * + * frequency >= (tc_input_frequency * 65536) / divider. + */ + + while (frequency < (xmc4_ccu4_divfreq_lookup(ftcin, ndx) >> 16)) + { + if (++ndx > CCU4_NDIVIDERS) + { + /* If no divisor can be found, return -ERANGE */ + + tmrerr("ERROR: Lower bound search failed\n"); + return -ERANGE; + } + } + + /* Try to maximize DIV while still satisfying upper bound. That the + * value of the divider such that: + * + * frequency < tc_input_frequency / divider. + */ + + for (; ndx < (CCU4_NDIVIDERS - 1); ndx++) + { + if (frequency > xmc4_ccu4_divfreq_lookup(ftcin, ndx + 1)) + { + break; + } + } + + /* Return the divider value */ + + if (div) + { + uint32_t value = xmc4_ccu4_freqdiv_lookup(ftcin, ndx); + tmrinfo("return div=%lu\n", (unsigned long)value); + *div = value; + } + + /* Return the PSSIV selection */ + + if (pssiv) + { + tmrinfo("return pssiv=%d\n", ndx + 1); + *pssiv = ndx + 1; + } + + return OK; +} diff --git a/arch/arm/src/xmc4/xmc4_ccu4.h b/arch/arm/src/xmc4/xmc4_ccu4.h new file mode 100644 index 0000000000..c334d020b3 --- /dev/null +++ b/arch/arm/src/xmc4/xmc4_ccu4.h @@ -0,0 +1,64 @@ +/**************************************************************************** + * arch/arm/src/xmc4/xmc4_ccu4.h + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. The + * ASF licenses this file to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance with the + * License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations + * under the License. + * + ****************************************************************************/ + +#ifndef __ARCH_ARM_SRC_XMC4_XMC4_CCU4_H +#define __ARCH_ARM_SRC_XMC4_XMC4_CCU4_H + +/**************************************************************************** + * Included Files + ****************************************************************************/ + +#include <nuttx/config.h> + +#include <stdint.h> +#include <stdbool.h> + +/**************************************************************************** + * Public Functions Prototypes + ****************************************************************************/ + +/**************************************************************************** + * Name: xmc4_ccu4_divisor + * + * Description: + * Finds the best MCK divisor given the timer frequency and MCK. The + * result is guaranteed to satisfy the following equation: + * + * (Ftcin / (div * 65536)) <= freq <= (Ftcin / dev) + * + * where: + * freq - the desired frequency + * Ftcin - The timer/counter input frequency + * div - With DIV being the highest possible value. + * + * Input Parameters: + * frequency Desired timer frequency. + * div Divisor value. + * pssiv PSSIV field value for divisor. + * + * Returned Value: + * Zero (OK) if a proper divisor has been found, otherwise a negated errno + * value indicating the nature of the failure. + * + ****************************************************************************/ + +int xmc4_ccu4_divisor(uint32_t frequency, uint32_t *div, uint32_t *pssiv); + +#endif diff --git a/arch/arm/src/xmc4/xmc4_tickless.c b/arch/arm/src/xmc4/xmc4_tickless.c new file mode 100644 index 0000000000..953c9a56c3 --- /dev/null +++ b/arch/arm/src/xmc4/xmc4_tickless.c @@ -0,0 +1,574 @@ +/**************************************************************************** + * arch/arm/src/xmc4/xmc4_tickless.c + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. The + * ASF licenses this file to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance with the + * License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations + * under the License. + * + ****************************************************************************/ + +/**************************************************************************** + * Tickless OS Support. + * + * When CONFIG_SCHED_TICKLESS is enabled, all support for timer interrupts + * is suppressed and the platform specific code is expected to provide the + * following custom functions. + * + * void up_timer_initialize(void): Initializes the timer facilities. + * Called early in the initialization sequence (by up_initialize()). + * int up_timer_gettime(struct timespec *ts): Returns the current + * time from the platform specific time source. + * int up_timer_cancel(void): Cancels the interval timer. + * int up_timer_start(const struct timespec *ts): Start (or re-starts) + * the interval timer. + * + * The RTOS will provide the following interfaces for use by the platform- + * specific interval timer implementation: + * + * void nxsched_timer_expiration(void): Called by the platform-specific + * logic when the interval timer expires. + * + * NOTE + * Alarm option is NOT supported by XMC and never will. + * Hardware restrictions. + * + ****************************************************************************/ + +/**************************************************************************** + * XMC Timer Usage + * + * This implementation uses two timers: + * - One freerun timer to get the time since startup + * - One oneshoe timer to wait the desired delay + * + * For now, user cannot choose the CCU used. This implementation uses the + * CCU40 for timing, and CCU41 for interval. Contributions are welcome + * + ****************************************************************************/ + +/**************************************************************************** + * Included Files + ****************************************************************************/ + +#include <nuttx/config.h> + +#include <stdint.h> +#include <stdbool.h> +#include <errno.h> +#include <assert.h> +#include <stdio.h> + +#include <nuttx/arch.h> +#include <nuttx/irq.h> +#include <debug.h> + +#include "arm_internal.h" +#include <arch/board/board.h> +#include "hardware/xmc4_scu.h" +#include "hardware/xmc4_ccu4.h" +#include "xmc4_ccu4.h" + +#ifdef CONFIG_SCHED_TICKLESS + +/**************************************************************************** + * Pre-processor Definitions + ****************************************************************************/ + +/* Only alarm option can be supporter by xmc. + * Indeed, xmc CCU compare value cannot be updated + * on the flight. It's updated via shadow registers and + * these are loaded to compare value register only on overflow + */ + +#ifdef CONFIG_SCHED_TICKLESS_ALARM +# error Alarm support is not supported by xmc +#endif + +/* The XMC only have 16 bits timers, so whatever the resolution this max is + * reached very quickly. Therefore we force the user to enable this max. + */ + +#ifndef CONFIG_SCHED_TICKLESS_LIMIT_MAX_SLEEP +# error XMC tickless feature need to have a max delay for sleep +#endif + +/**************************************************************************** + * Private Types + ****************************************************************************/ + +struct xmc4_tickless_s +{ + uint32_t overflow; /* Timer counter overflow */ + uint32_t frequency; /* Frequency Timers */ + volatile bool pending; /* True: pending task */ + uint32_t period; /* Interval period */ +}; + +/**************************************************************************** + * Private Data + ****************************************************************************/ + +static struct xmc4_tickless_s g_tickless; + +/**************************************************************************** + * Private Functions + ****************************************************************************/ + +/**************************************************************************** + * Name: xmc4_interval_handler + * + * Description: + * Called when the oneshot timer reaches its period + * + * Input Parameters: + * None + * + * Returned Value: + * None + * + ****************************************************************************/ + +static void xmc4_interval_handler(void *arg) +{ + tmrinfo("Expired...\n"); + + /* Stop the timer */ + + putreg32(CCU4_CC4_TCCLR_TRBC_MASK, XMC4_CCU41_CC40TCCLR); + + g_tickless.pending = false; + nxsched_timer_expiration(); +} + +/**************************************************************************** + * Name: xmc_timing_handler + * + * Description: + * Timer interrupt callback. When the freerun timer counter overflows, + * this interrupt will occur. We will just increment an overflow count. + * + * Input Parameters: + * None + * + * Returned Value: + * None + * + ****************************************************************************/ + +static void xmc4_timing_handler(void) +{ + tmrinfo("Overflow"); + g_tickless.overflow++; +} + +/**************************************************************************** + * Public Functions + ****************************************************************************/ + +/**************************************************************************** + * Name: up_timer_initialize + * + * Description: + * Initializes all platform-specific timer facilities. This function is + * called early in the initialization sequence by up_initialize(). + * On return, the current up-time should be available from + * up_timer_gettime() and the interval timer is ready for use (but not + * actively timing. + * + * Provided by platform-specific code and called from the architecture- + * specific logic. + * + * Input Parameters: + * None + * + * Returned Value: + * None + * + * Assumptions: + * Called early in the initialization sequence before any special + * concurrency protections are required. + * + ****************************************************************************/ + +void up_timer_initialize(void) +{ +#ifdef CONFIG_SCHED_TICKLESS_LIMIT_MAX_SLEEP + uint64_t max_delay; +#endif + int ret; + + g_tickless.pending = false; + g_tickless.overflow = 0; + g_tickless.frequency = USEC_PER_SEC / (uint32_t)CONFIG_USEC_PER_TICK; + + /* Enable CCU clock */ + + putreg32(SCU_CLK_CCUC, XMC4_SCU_CLKSET); + + /* Enable CCU clock during sleep */ + + putreg32(SCU_SLEEPCR_CCUCR | SCU_SLEEPCR_SYSSEL, XMC4_SCU_SLEEPCR); + + uint32_t divisor; + uint32_t pssiv; + ret = xmc4_ccu4_divisor(USEC_PER_SEC / (uint32_t)CONFIG_USEC_PER_TICK, + &divisor, + &pssiv); + g_tickless.frequency = BOARD_CCU_FREQUENCY / divisor; + if (ret < 0) + { + tmrerr("ERROR: xmc4_ccu4_divisor failed: %d\n", ret); + return ret; + } + + tmrinfo("frequency=%lu, divisor=%lu, cmr=%08lx\n", + (unsigned long)g_tickless.frequency, (unsigned long)divisor, + (unsigned long)pssiv); + + /* Initialize Interval Timer + * + * Ths timer is configured to be a oneshot timer, that has + * a resolution that matches the USEC_PER_TICK, and + * will be started in up_timer_start and uses its period + * (not compare value) to trigger an interrupt. + */ + + /* Apply reset */ + + putreg32(SCU_PR0_CCU41RS, XMC4_SCU_PRSET0); + putreg32(SCU_PR0_CCU41RS, XMC4_SCU_PRCLR0); + + /* Enable CC40 Slice */ + + putreg32(CCU4_GIDLC_CS0I_MASK, XMC4_CCU41_GIDLC); + + /* Enable the prescaler and set value */ + + putreg32(CCU4_GIDLC_SPRB_MASK, XMC4_CCU41_GIDLC); + putreg32(pssiv, XMC4_CCU41_CC40PSC); + +#ifdef CONFIG_SCHED_TICKLESS_LIMIT_MAX_SLEEP + max_delay = (float)UINT16_MAX * USEC_PER_SEC / g_tickless.frequency; + g_oneshot_maxticks = max_delay / CONFIG_USEC_PER_TICK; +#endif + + /* Enable Single shot mode */ + + putreg32(CCU4_CC4_TC_TSSM_MASK, XMC4_CCU41_CC40TC); + + /* Attach the interrupt handler */ + + irq_attach(XMC4_IRQ_CCU41_SR0, (xcpt_t)xmc4_interval_handler, NULL); + + /* Enable Interrupt */ + + up_enable_irq(XMC4_IRQ_CCU41_SR0); + + /* Initialize Timing Timer + * + * This timer is configure to be a freerun timer that + * has a resolution that matches the USEC_PER_TICK. + */ + + /* Apply reset */ + + putreg32(SCU_PR0_CCU40RS, XMC4_SCU_PRSET0); + putreg32(SCU_PR0_CCU40RS, XMC4_SCU_PRCLR0); + + /* Enable CC40 */ + + putreg32(CCU4_GIDLC_CS0I_MASK, XMC4_CCU40_GIDLC); + + /* Enable the prescaler and set value */ + + putreg32(CCU4_GIDLC_SPRB_MASK, XMC4_CCU40_GIDLC); + putreg32(pssiv, XMC4_CCU40_CC40PSC); + + /* Set Period of the timer to max */ + + putreg32(UINT16_MAX, XMC4_CCU40_CC40PRS); + + /* Enable Period Match Interrupt */ + + putreg32(CCU4_CC4_INTE_PME_MASK, XMC4_CCU40_CC40INTE); + up_enable_irq(XMC4_IRQ_CCU40_SR0); + + /* Attach the interrupt handler */ + + irq_attach(XMC4_IRQ_CCU40_SR0, (xcpt_t)xmc4_timing_handler, NULL); + + /* Enable shadow transfer */ + + putreg32(CCU4_GCSS_S0SE_MASK, XMC4_CCU40_GCSS); + + /* Start the timing timer */ + + putreg32(CCU4_CC4_TCSET_TRBS_MASK, XMC4_CCU40_CC40TCSET); +} + +/**************************************************************************** + * Name: up_timer_gettime + * + * Description: + * Return the elapsed time since power-up (or, more correctly, since + * up_timer_initialize() was called). This function is functionally + * equivalent to: + * + * int clock_gettime(clockid_t clockid, struct timespec *ts); + * + * when clockid is CLOCK_MONOTONIC. + * + * This function provides the basis for reporting the current time and + * also is used to eliminate error build-up from small errors in interval + * time calculations. + * + * Provided by platform-specific code and called from the RTOS base code. + * + * Input Parameters: + * ts - Provides the location in which to return the up-time. + * + * Returned Value: + * Zero (OK) is returned on success; a negated errno value is returned on + * any failure. + * + * Assumptions: + * Called from the normal tasking context. The implementation must + * provide whatever mutual exclusion is necessary for correct operation. + * This can include disabling interrupts in order to assure atomic register + * operations. + * + ****************************************************************************/ + +int up_timer_gettime(struct timespec *ts) +{ + uint64_t usec; + uint32_t counter; + uint32_t overflow; + uint32_t sec; + irqstate_t flags; + + /* Temporarily disable the overflow counter */ + + flags = enter_critical_section(); + + counter = getreg32(XMC4_CCU40_CC40TIMER); + overflow = g_tickless.overflow; + + leave_critical_section(flags); + + usec = (uint64_t)(overflow * UINT16_MAX + counter) + * USEC_PER_SEC / g_tickless.frequency; + sec = usec / USEC_PER_SEC; + ts->tv_sec = sec; + ts->tv_nsec = (usec - (sec * USEC_PER_SEC)) * NSEC_PER_USEC; + + tmrinfo("usec=%llu ts=(%lu, %lu)\n", + usec, (unsigned long)ts->tv_sec, (unsigned long)ts->tv_nsec); + + return OK; +} + +/**************************************************************************** + * Name: up_alarm_start + * + * Description: + * Start the alarm. nxsched_alarm_expiration() will be called when the + * alarm occurs (unless up_alaram_cancel is called to stop it). + * + * Provided by platform-specific code and called from the RTOS base code. + * + * Input Parameters: + * ts - The time in the future at the alarm is expected to occur. When + * the alarm occurs the timer logic will call + * nxsched_alarm_expiration(). + * + * Returned Value: + * Zero (OK) is returned on success; a negated errno value is returned on + * any failure. + * + * Assumptions: + * May be called from interrupt level handling or from the normal tasking + * level. Interrupts may need to be disabled internally to assure + * non-reentrancy. + * + ****************************************************************************/ + +int up_timer_start(const struct timespec *ts) +{ + uint64_t usec; + uint64_t period; + irqstate_t flags; + + tmrinfo("ts=(%lu, %lu)\n", + (unsigned long)ts->tv_sec, (unsigned long)ts->tv_nsec); + DEBUGASSERT(ts); + + /* Was an interval already running? */ + + flags = enter_critical_section(); + if (g_tickless.pending) + { + /* Yes.. then cancel it */ + + tmrinfo("Already running... cancelling\n"); + up_timer_cancel(NULL); + } + + /* Express the delay in microseconds */ + + usec = (uint64_t)ts->tv_sec * USEC_PER_SEC + + (uint64_t)(ts->tv_nsec / NSEC_PER_USEC); + + /* Compute periods of the timers to match delay to wait */ + + period = usec * (uint64_t)g_tickless.frequency / USEC_PER_SEC; + putreg32(period, XMC4_CCU41_CC40PRS); + + tmrinfo("usec=%llu period=%08llx\n", usec, period); + + DEBUGASSERT(period <= UINT16_MAX); + g_tickless.period = period; + + /* Enable interrupt */ + + putreg32(CCU4_CC4_INTE_PME_MASK, XMC4_CCU41_CC40INTE); + + /* Enable shadow transfer */ + + putreg32(CCU4_GCSS_S0SE_MASK, XMC4_CCU41_GCSS); + + /* Start timer */ + + putreg32(CCU4_CC4_TCSET_TRBS_MASK, XMC4_CCU41_CC40TCSET); + + g_tickless.pending = true; + leave_critical_section(flags); + + return OK; +} + +/**************************************************************************** + * Name: up_alarm_cancel + * + * Description: + * Cancel the alarm and return the time of cancellation of the alarm. + * These two steps need to be as nearly atomic as possible. + * nxsched_alarm_expiration() will not be called unless the alarm is + * restarted with up_alarm_start(). + * + * If, as a race condition, the alarm has already expired when this + * function is called, then time returned is the current time. + * + * NOTE: This function may execute at a high rate with no timer running (as + * when pre-emption is enabled and disabled). + * + * Provided by platform-specific code and called from the RTOS base code. + * + * Input Parameters: + * ts - Location to return the expiration time. The current time should + * returned if the alarm is not active. ts may be NULL in which + * case the time is not returned. + * + * Returned Value: + * Zero (OK) is returned on success. A call to up_alarm_cancel() when + * the timer is not active should also return success; a negated errno + * value is returned on any failure. + * + * Assumptions: + * May be called from interrupt level handling or from the normal tasking + * level. Interrupts may need to be disabled internally to assure + * non-reentrancy. + * + ****************************************************************************/ + +int up_timer_cancel(struct timespec *ts) +{ + uint32_t regval; + uint64_t usec; + uint64_t sec; + uint64_t nsec; + irqstate_t flags; + uint32_t period; + uint32_t count; + + /* Was the timer running? */ + + flags = enter_critical_section(); + if (!g_tickless.pending) + { + /* No.. Just return zero timer remaining and successful cancellation. + * This function may execute at a high rate with no timer running + * (as when pre-emption is enabled and disabled). + */ + + if (ts != NULL) + { + ts->tv_sec = 0; + ts->tv_nsec = 0; + } + + leave_critical_section(flags); + return OK; + } + + /* Yes.. Get the timer counter and period registers and disable the compare + * interrupt. + */ + + regval = getreg32(XMC4_CCU41_CC40INTE); + regval &= ~CCU4_CC4_INTE_PME_MASK; + putreg32(regval, XMC4_CCU41_CC40INTE); + putreg32(CCU4_CC4_TCCLR_TCC_MASK | CCU4_CC4_TCCLR_TRBC_MASK, + XMC4_CCU41_CC40TCCLR); + + count = getreg32(XMC4_CCU41_CC40TIMER); + period = g_tickless.period; + g_tickless.pending = false; + leave_critical_section(flags); + + tmrinfo("Cancelling...\n"); + + if (ts != NULL) + { + /* Yes.. then calculate and return the time remaining on the + * oneshot timer. + */ + + tmrinfo("period=%lu count=%lu\n", + (unsigned long)period, (unsigned long)count); + + /* The total time remaining is the difference. Convert that + * to units of microseconds. + * + * frequency = ticks / second + * seconds = ticks * frequency + * usecs = (ticks * USEC_PER_SEC) / frequency; + */ + + usec = (((uint64_t)(period - count)) * USEC_PER_SEC) / + g_tickless.frequency; + + sec = usec / USEC_PER_SEC; + nsec = ((usec) - (sec * USEC_PER_SEC)) * NSEC_PER_USEC; + ts->tv_sec = (time_t)sec; + ts->tv_nsec = (unsigned long)nsec; + + tmrinfo("remaining count : %lu (%lu, %lu)\n", count, + (unsigned long)ts->tv_sec, (unsigned long)ts->tv_nsec); + } + + return OK; +} + +#endif /* CONFIG_SCHED_TICKLESS */