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 */

Reply via email to