gustavonihei commented on a change in pull request #5723:
URL: https://github.com/apache/incubator-nuttx/pull/5723#discussion_r825117266



##########
File path: arch/xtensa/src/esp32s3/esp32s3_rt_timer.c
##########
@@ -0,0 +1,1054 @@
+/****************************************************************************
+ * arch/xtensa/src/esp32s3/esp32s3_rt_timer.c
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this args 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.
+ *
+ ****************************************************************************/
+
+/****************************************************************************
+ * Included Files
+ ****************************************************************************/
+
+#include <nuttx/config.h>
+
+#include <assert.h>
+#include <debug.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <stdbool.h>
+#include <stdint.h>
+#include <string.h>
+#include <sys/types.h>
+
+#include <nuttx/irq.h>
+#include <nuttx/kmalloc.h>
+#include <nuttx/kthread.h>
+#include <nuttx/semaphore.h>
+#include <nuttx/spinlock.h>
+
+#include "xtensa.h"
+#include "xtensa_attr.h"
+#include "esp32s3_irq.h"
+#include "esp32s3_rt_timer.h"
+#include "hardware/esp32s3_soc.h"
+#include "hardware/esp32s3_system.h"
+#include "hardware/esp32s3_systimer.h"
+
+/****************************************************************************
+ * Pre-processor Definitions
+ ****************************************************************************/
+
+#define RT_TIMER_TASK_NAME        CONFIG_ESP32S3_RT_TIMER_TASK_NAME
+#define RT_TIMER_TASK_PRIORITY    CONFIG_ESP32S3_RT_TIMER_TASK_PRIORITY
+#define RT_TIMER_TASK_STACK_SIZE  CONFIG_ESP32S3_RT_TIMER_TASK_STACK_SIZE
+
+#ifdef CONFIG_SCHED_HPWORKPRIORITY
+static_assert(RT_TIMER_TASK_PRIORITY < CONFIG_SCHED_HPWORKPRIORITY,
+  "RT Timer priority should be smaller than high-prio workqueue");
+#endif
+
+/* Timer running at 16 MHz */
+
+#define CYCLES_PER_USEC           16
+#define USEC_TO_CYCLES(u)         ((u) * CYCLES_PER_USEC)
+#define CYCLES_TO_USEC(c)         ((c) / CYCLES_PER_USEC)
+
+/****************************************************************************
+ * Private Types
+ ****************************************************************************/
+
+struct esp32s3_rt_priv_s
+{
+  int pid;                    /* PID of RT Timer kernel thread */
+  int cpuint;                 /* CPU interrupt assigned to this timer */
+  int core;                   /* Core that is taking care of the timer
+                               * interrupts
+                               */
+  sem_t toutsem;              /* Semaphore for synchronizing access to list
+                               * of timed-out timers
+                               */
+  struct list_node runlist;   /* List of timers in the running state */
+  struct list_node toutlist;  /* List of timed-out timers */
+  spinlock_t lock;            /* Device-specific lock */
+};
+
+/****************************************************************************
+ * Private Data
+ ****************************************************************************/
+
+static struct esp32s3_rt_priv_s g_rt_priv =
+{
+  .pid    = -EINVAL,
+  .cpuint = -ENOMEM,
+  .core   = -ENODEV
+};
+
+/****************************************************************************
+ * Private Function Prototypes
+ ****************************************************************************/
+
+/****************************************************************************
+ * Name: rt_timer_getcounter
+ *
+ * Description:
+ *   Get the current counter value.
+ *
+ * Input Parameters:
+ *   None.
+ *
+ * Returned Value:
+ *   Current counter value.
+ *
+ ****************************************************************************/
+
+static inline uint64_t rt_timer_getcounter(void)
+{
+  uint32_t lo;
+  uint32_t lo_start;
+  uint32_t hi;
+  uint64_t counter;
+
+  /* Trigger an update event */
+
+  modifyreg32(SYSTIMER_UNIT1_OP_REG, 0, SYSTIMER_TIMER_UNIT1_UPDATE);
+
+  /* Wait until the value is valid */
+
+  while ((getreg32(SYSTIMER_UNIT1_OP_REG) &
+          SYSTIMER_TIMER_UNIT1_VALUE_VALID) !=
+         SYSTIMER_TIMER_UNIT1_VALUE_VALID);
+
+  /* Read LO, HI, then LO again, check that LO returns the same value.
+   * This accounts for the case when an interrupt may happen between reading
+   * HI and LO values, and this function may get called from the ISR.
+   * In this case, the repeated read will return consistent values.
+   */
+
+  lo_start = getreg32(SYSTIMER_UNIT1_VALUE_LO_REG);
+  do
+    {
+      lo = lo_start;
+      hi = getreg32(SYSTIMER_UNIT1_VALUE_HI_REG);
+      lo_start = getreg32(SYSTIMER_UNIT1_VALUE_LO_REG);
+    }
+  while (lo_start != lo);
+
+  counter = ((uint64_t) hi << 32) | lo;
+
+  return counter;
+}
+
+/****************************************************************************
+ * Name: rt_timer_setcounter
+ *
+ * Description:
+ *   Set the counter value.
+ *
+ * Input Parameters:
+ *   value         - The value to be loaded to the counter.
+ *
+ * Returned Value:
+ *   None.
+ *
+ ****************************************************************************/
+
+static inline void rt_timer_setcounter(uint64_t value)
+{
+  /* Set counter value */
+
+  putreg32(value & SYSTIMER_TIMER_UNIT1_VALUE_LO_V,
+           SYSTIMER_UNIT1_VALUE_LO_REG);
+  putreg32((value >> 32) & SYSTIMER_TIMER_UNIT1_VALUE_HI_V,
+           SYSTIMER_UNIT1_VALUE_HI_REG);
+
+  /* Apply counter value */
+
+  putreg32(SYSTIMER_TIMER_UNIT1_LOAD, SYSTIMER_UNIT1_LOAD_REG);
+}
+
+/****************************************************************************
+ * Name: rt_timer_getalarmvalue
+ *
+ * Description:
+ *   Get the alarm value.
+ *
+ * Input Parameters:
+ *   None.
+ *
+ * Returned Value:
+ *   Remaining ticks for expiration.
+ *
+ ****************************************************************************/
+
+static inline uint64_t rt_timer_getalarmvalue(void)
+{
+  uint32_t hi = getreg32(SYSTIMER_TARGET2_HI_REG);
+  uint32_t lo = getreg32(SYSTIMER_TARGET2_LO_REG);
+  uint64_t ticks = ((uint64_t) hi << 32) | lo;
+
+  return ticks;
+}
+
+/****************************************************************************
+ * Name: rt_timer_setalarmvalue
+ *
+ * Description:
+ *   Set the value that will trigger an alarm when the counter value matches
+ *   this value.
+ *
+ * Input Parameters:
+ *   value         - The alarm value.
+ *
+ * Returned Value:
+ *   None.
+ *
+ ****************************************************************************/
+
+static inline void rt_timer_setalarmvalue(uint64_t value)
+{
+  /* Set alarm value */
+
+  putreg32(value & 0xffffffff, SYSTIMER_TARGET2_LO_REG);
+  putreg32((value >> 32) & 0xfffff, SYSTIMER_TARGET2_HI_REG);
+
+  /* Apply alarm value */
+
+  putreg32(SYSTIMER_TIMER_COMP2_LOAD, SYSTIMER_COMP2_LOAD_REG);
+}
+
+/****************************************************************************
+ * Name: rt_timer_setalarm
+ *
+ * Description:
+ *   Enable/Disable the alarm.
+ *
+ * Input Parameters:
+ *   enable        - A variable to indicate the action. If true, enable
+ *                   the alarm, otherwise disable it.
+ *
+ * Returned Value:
+ *   None.
+ *
+ ****************************************************************************/
+
+static void rt_timer_setalarm(bool enable)
+{
+  if (enable)
+    {
+      modifyreg32(SYSTIMER_CONF_REG, 0, SYSTIMER_TARGET2_WORK_EN);
+    }
+  else
+    {
+      modifyreg32(SYSTIMER_CONF_REG, SYSTIMER_TARGET2_WORK_EN, 0);
+    }
+}
+
+/****************************************************************************
+ * Name: rt_timer_setisr
+ *
+ * Description:
+ *   Allocate a CPU Interrupt, connect the peripheral source to this
+ *   Interrupt, register the callback and enable the CPU Interrupt.
+ *   In case a NULL handler is provided, deallocate the interrupt and
+ *   unregister the previously provided handler.
+ *
+ * Input Parameters:
+ *   handler       - Callback to be invoked on timer interrupt.
+ *   arg           - Argument to be passed to the handler callback.
+ *
+ * Returned Values:
+ *   Zero (OK) is returned on success. A negated errno value is returned to
+ *   indicate the nature of any failure.
+ *
+ ****************************************************************************/
+
+static int rt_timer_setisr(xcpt_t handler, void *arg)
+{
+  struct esp32s3_rt_priv_s *priv = &g_rt_priv;
+  int ret = OK;
+
+  /* Disable interrupt when callback is removed. */
+
+  if (handler == NULL)
+    {
+      /* If a CPU Interrupt was previously allocated, then deallocate it */
+
+      if (priv->cpuint != -ENOMEM)
+        {
+          /* Disable CPU Interrupt, free a previously allocated
+           * CPU Interrupt
+           */
+
+          up_disable_irq(ESP32S3_IRQ_SYSTIMER_TARGET2);
+          esp32s3_teardown_irq(priv->core, ESP32S3_PERIPH_SYSTIMER_TARGET2,
+                               priv->cpuint);
+          irq_detach(ESP32S3_IRQ_SYSTIMER_TARGET2);
+
+          priv->cpuint = -ENOMEM;
+          priv->core   = -ENODEV;
+        }
+    }
+
+  /* Otherwise set callback and enable interrupt */
+
+  else
+    {
+      if (priv->cpuint != -ENOMEM)
+        {
+          /* Disable the previous IRQ */
+
+          up_disable_irq(ESP32S3_IRQ_SYSTIMER_TARGET2);
+        }
+
+      /* Set up to receive peripheral interrupts on the current CPU */
+
+      priv->core = up_cpu_index();
+      priv->cpuint = esp32s3_setup_irq(priv->core,
+                                       ESP32S3_PERIPH_SYSTIMER_TARGET2,
+                                       1, ESP32S3_CPUINT_LEVEL);
+      if (priv->cpuint < 0)
+        {
+          tmrerr("ERROR: No CPU Interrupt available");
+          ret = priv->cpuint;
+          goto errout;
+        }
+
+      /* Associate an IRQ Number (from the timer) to an ISR */
+
+      ret = irq_attach(ESP32S3_IRQ_SYSTIMER_TARGET2, handler, arg);
+      if (ret != OK)
+        {
+          esp32s3_teardown_irq(priv->core, ESP32S3_PERIPH_SYSTIMER_TARGET2,
+                               priv->cpuint);
+          tmrerr("ERROR: Failed to associate an IRQ Number");
+          goto errout;
+        }
+
+      /* Enable the CPU Interrupt that is linked to the timer */
+
+      up_enable_irq(ESP32S3_IRQ_SYSTIMER_TARGET2);
+    }
+
+errout:
+  return ret;
+}
+
+/****************************************************************************
+ * Name: start_rt_timer
+ *
+ * Description:
+ *   Start the timer by inserting it into the running list and reset the
+ *   hardware timer alarm value if this timer is at the head of the list.
+ *   Larger timeouts go to the end of the list (tail).
+ *
+ * Input Parameters:
+ *   timer         - Pointer to the RT Timer state structure.
+ *   timeout       - Timeout value.
+ *   repeat        - Repeat mode (true: enabled, false: disabled).
+ *
+ * Returned Value:
+ *   None.
+ *
+ ****************************************************************************/
+
+static void start_rt_timer(struct rt_timer_s *timer,
+                           uint64_t timeout,
+                           bool repeat)
+{
+  irqstate_t flags;
+  struct esp32s3_rt_priv_s *priv = &g_rt_priv;
+
+  flags = spin_lock_irqsave(&priv->lock);
+
+  /* Only idle timer can be started */
+
+  if (timer->state == RT_TIMER_IDLE)
+    {
+      struct rt_timer_s *temp_p;
+      bool inserted = false;
+
+      /* Calculate the timer's alarm value */
+
+      uint64_t counter = rt_timer_getcounter();
+      counter = CYCLES_TO_USEC(counter);
+      timer->timeout = timeout;
+      timer->alarm = timer->timeout + counter;
+
+      if (repeat)
+        {
+          timer->flags |= RT_TIMER_REPEAT;
+        }
+      else
+        {
+          timer->flags &= ~RT_TIMER_REPEAT;
+        }
+
+      /* Scan the timer list and insert the new timer into previous node of
+       * timer whose alarm value is larger than new one.
+       */
+
+      list_for_every_entry(&priv->runlist, temp_p, struct rt_timer_s, list)
+        {
+          if (temp_p->alarm > timer->alarm)
+            {
+              list_add_before(&temp_p->list, &timer->list);
+              inserted = true;
+              break;
+            }
+        }
+
+      /* If we didn't find a larger one, insert the new timer at the tail of
+       * the list.
+       */
+
+      if (!inserted)
+        {
+          list_add_tail(&priv->runlist, &timer->list);
+        }
+
+      timer->state = RT_TIMER_READY;
+
+      /* Check if this timer is at the head of the list */
+
+      if (timer == container_of(priv->runlist.next, struct rt_timer_s, list))
+        {
+          /* Reset the hardware timer alarm */
+
+          rt_timer_setalarm(false);
+          rt_timer_setalarmvalue(USEC_TO_CYCLES(timer->alarm));
+          rt_timer_setalarm(true);
+        }
+    }
+  else
+    {
+      tmrwarn("Timer not in idle mode. Only idle timer can be started!\n");
+    }
+
+  spin_unlock_irqrestore(&priv->lock, flags);
+}
+
+/****************************************************************************
+ * Name: stop_rt_timer
+ *
+ * Description:
+ *   Stop the timer by removing it from the running list and reset the
+ *   hardware timer alarm value if this timer is at the head of list.
+ *
+ * Input Parameters:
+ *   timer         - Pointer to the RT Timer state structure.
+ *
+ * Returned Value:
+ *   None.
+ *
+ ****************************************************************************/
+
+static void stop_rt_timer(struct rt_timer_s *timer)
+{
+  irqstate_t flags;
+  struct esp32s3_rt_priv_s *priv = &g_rt_priv;
+
+  flags = spin_lock_irqsave(&priv->lock);
+
+  /* "start" function can set the timer's repeat flag, and "stop" function
+   * should remove this flag.
+   */
+
+  timer->flags &= ~RT_TIMER_REPEAT;
+
+  /* Only timers in "ready" state can be stopped */
+
+  if (timer->state == RT_TIMER_READY)
+    {
+      bool ishead;
+
+      /* Check if the timer is at the head of the list */
+
+      if (timer == container_of(priv->runlist.next,
+                                struct rt_timer_s, list))
+        {
+          ishead = true;
+        }
+      else
+        {
+          ishead = false;
+        }
+
+      list_delete(&timer->list);
+      timer->state = RT_TIMER_IDLE;
+
+      if (ishead)
+        {
+          if (!list_is_empty(&priv->runlist))
+            {
+              /* Set the value from the next timer as the new hardware timer
+               * alarm value.
+               */
+
+              struct rt_timer_s *next_timer =
+                container_of(priv->runlist.next, struct rt_timer_s, list);
+
+              rt_timer_setalarm(false);
+              rt_timer_setalarmvalue(USEC_TO_CYCLES(next_timer->alarm));
+              rt_timer_setalarm(true);
+            }
+        }
+    }
+
+  spin_unlock_irqrestore(&priv->lock, flags);
+}
+
+/****************************************************************************
+ * Name: rt_timer_thread
+ *
+ * Description:
+ *   RT Timer working thread: Waits for a timeout semaphore, scans the
+ *   timeout list and processes all the timers in the list.
+ *
+ * Input Parameters:
+ *   argc          - Not used.
+ *   argv          - Not used.
+ *
+ * Returned Value:
+ *   Zero (OK) is returned on success. A negated errno value is returned to
+ *   indicate the nature of any failure.
+ *
+ ****************************************************************************/
+
+static int rt_timer_thread(int argc, char *argv[])
+{
+  struct esp32s3_rt_priv_s *priv = &g_rt_priv;
+
+  while (1)
+    {
+      /* Waiting for all timers to time out */
+
+      int ret = nxsem_wait(&priv->toutsem);
+      if (ret < 0)
+        {
+          tmrerr("ERROR: Wait priv->toutsem error=%d\n", ret);
+          assert(0);
+        }
+
+      irqstate_t flags = spin_lock_irqsave(&priv->lock);
+
+      /* Process all the timers in list */
+
+      while (!list_is_empty(&priv->toutlist))
+        {
+          /* Get the first timer in the list */
+
+          struct rt_timer_s *timer = container_of(priv->toutlist.next,
+                                                  struct rt_timer_s, list);
+
+          /* Cache the raw state to decide how to deal with this timer */
+
+          enum rt_timer_state_e raw_state = timer->state;
+
+          /* Delete the timer from the list */
+
+          list_delete(&timer->list);
+
+          /* Set timer's state to idle so it can be restarted by the user. */
+
+          timer->state = RT_TIMER_IDLE;
+
+          spin_unlock_irqrestore(&priv->lock, flags);
+
+          if (raw_state == RT_TIMER_TIMEOUT)
+            {
+              timer->callback(timer->arg);
+            }
+          else if (raw_state == RT_TIMER_DELETE)
+            {
+              kmm_free(timer);
+            }
+
+          /* Enter critical section for next scanning list */
+
+          flags = spin_lock_irqsave(&priv->lock);
+
+          if (raw_state == RT_TIMER_TIMEOUT)
+            {
+              /* Check if the timer is in "repeat" mode */
+
+              if (timer->flags & RT_TIMER_REPEAT)

Review comment:
       Thanks!




-- 
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.

To unsubscribe, e-mail: commits-unsubscr...@nuttx.apache.org

For queries about this service, please contact Infrastructure at:
us...@infra.apache.org


Reply via email to