From add99fead3605c366cc57b88b385b0bb07d72359 Mon Sep 17 00:00:00 2001 From: Gustavo Henrique Nihei Date: Wed, 23 Feb 2022 14:42:49 -0300 Subject: [PATCH] xtensa/esp32s3: Add support for Oneshot timer Signed-off-by: Gustavo Henrique Nihei --- arch/xtensa/src/esp32s3/Kconfig | 16 +- arch/xtensa/src/esp32s3/Make.defs | 7 + arch/xtensa/src/esp32s3/esp32s3_oneshot.c | 467 ++++++++++++++++++ arch/xtensa/src/esp32s3/esp32s3_oneshot.h | 212 ++++++++ .../src/esp32s3/esp32s3_oneshot_lowerhalf.c | 370 ++++++++++++++ 5 files changed, 1070 insertions(+), 2 deletions(-) create mode 100644 arch/xtensa/src/esp32s3/esp32s3_oneshot.c create mode 100644 arch/xtensa/src/esp32s3/esp32s3_oneshot.h create mode 100644 arch/xtensa/src/esp32s3/esp32s3_oneshot_lowerhalf.c diff --git a/arch/xtensa/src/esp32s3/Kconfig b/arch/xtensa/src/esp32s3/Kconfig index e87578664e..e0bb0681e1 100644 --- a/arch/xtensa/src/esp32s3/Kconfig +++ b/arch/xtensa/src/esp32s3/Kconfig @@ -388,7 +388,7 @@ config ESP32S3_RWDT endmenu # ESP32-S3 Peripheral Selection -menu "UART configuration" +menu "UART Configuration" depends on ESP32S3_UART if ESP32S3_UART0 @@ -469,7 +469,19 @@ config ESP32S3_UART2_CTSPIN endif # ESP32S3_UART2 -endmenu # UART configuration +endmenu # UART Configuration + +menu "Timer/Counter Configuration" + depends on ESP32S3_TIMER + +config ESP32S3_ONESHOT + bool "One-shot wrapper" + default n + ---help--- + Enable a wrapper around the low level timer/counter functions to + support one-shot timer. + +endmenu # Timer/Counter Configuration menu "Application Image Configuration" diff --git a/arch/xtensa/src/esp32s3/Make.defs b/arch/xtensa/src/esp32s3/Make.defs index f645ec1efb..339edfeffc 100644 --- a/arch/xtensa/src/esp32s3/Make.defs +++ b/arch/xtensa/src/esp32s3/Make.defs @@ -83,3 +83,10 @@ endif ifeq ($(CONFIG_WATCHDOG),y) CHIP_CSRCS += esp32s3_wdt_lowerhalf.c endif + +ifeq ($(CONFIG_ESP32S3_ONESHOT),y) +CHIP_CSRCS += esp32s3_oneshot.c +ifeq ($(CONFIG_ONESHOT),y) +CHIP_CSRCS += esp32s3_oneshot_lowerhalf.c +endif +endif diff --git a/arch/xtensa/src/esp32s3/esp32s3_oneshot.c b/arch/xtensa/src/esp32s3/esp32s3_oneshot.c new file mode 100644 index 0000000000..ee2105e8c5 --- /dev/null +++ b/arch/xtensa/src/esp32s3/esp32s3_oneshot.c @@ -0,0 +1,467 @@ +/**************************************************************************** + * arch/xtensa/src/esp32s3/esp32s3_oneshot.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. + * + ****************************************************************************/ + +/**************************************************************************** + * Included Files + ****************************************************************************/ + +#include + +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include + +#include "esp32s3_clockconfig.h" +#include "esp32s3_oneshot.h" +#include "esp32s3_tim.h" +#include "hardware/esp32s3_soc.h" + +#ifdef CONFIG_ESP32S3_ONESHOT + +/**************************************************************************** + * Pre-processor Definitions + ****************************************************************************/ + +#define MAX_TIMER_COUNTER (UINT64_C(1) << 53) + +/**************************************************************************** + * Private Function Prototypes + ****************************************************************************/ + +static int oneshot_handler(int irq, void *context, void *arg); + +/**************************************************************************** + * Private Data + ****************************************************************************/ + +/**************************************************************************** + * Private Functions + ****************************************************************************/ + +/**************************************************************************** + * Name: oneshot_handler + * + * Description: + * Oneshot interrupt handler. When any oneshot timer interrupt + * expires, this function will be triggered. It will forward the call to + * the next level up. + * + * Input Parameters: + * irq - IRQ associated to that interrupt + * context - Interrupt register state save info + * arg - A pointer to the argument provided when the interrupt was + * registered. + * + * Returned Value: + * Zero on success; a negated errno value on failure. + * + ****************************************************************************/ + +static int oneshot_handler(int irq, void *context, void *arg) +{ + int ret = OK; + struct esp32s3_oneshot_s *oneshot = (struct esp32s3_oneshot_s *)arg; + oneshot_handler_t handler; + void *handler_arg; + + DEBUGASSERT(oneshot != NULL); + DEBUGASSERT(oneshot->handler != NULL); + + tmrinfo("Oneshot handler triggered\n"); + + /* Disable interrupts */ + + ESP32S3_TIM_DISABLEINT(oneshot->tim); + + /* Detach handler */ + + ret = ESP32S3_TIM_SETISR(oneshot->tim, NULL, NULL); + + /* Clear the Interrupt */ + + ESP32S3_TIM_ACKINT(oneshot->tim); + + /* The timer is no longer running */ + + oneshot->running = false; + + /* Forward the event, clearing out any vestiges */ + + handler = (oneshot_handler_t)oneshot->handler; + oneshot->handler = NULL; + handler_arg = (void *)oneshot->arg; + oneshot->arg = NULL; + + /* Call the callback */ + + handler(handler_arg); + + return ret; +} + +/**************************************************************************** + * Public Functions + ****************************************************************************/ + +/**************************************************************************** + * Name: esp32s3_oneshot_initialize + * + * Description: + * Initialize the oneshot timer wrapper. + * + * Input Parameters: + * oneshot Caller allocated instance of the oneshot state structure. + * chan Timer counter channel to be used. + * resolution The required resolution of the timer in units of + * microseconds. NOTE that the range is restricted to the + * range of uint16_t (excluding zero). + * + * Returned Value: + * Zero (OK) is returned on success; a negated errno value is returned + * on failure. + * + ****************************************************************************/ + +int esp32s3_oneshot_initialize(struct esp32s3_oneshot_s *oneshot, + int chan, uint16_t resolution) +{ + int ret = OK; + + tmrinfo("chan=%d resolution=%d usecs\n", chan, resolution); + + DEBUGASSERT(oneshot != NULL); + DEBUGASSERT(resolution > 0); + + oneshot->chan = chan; + + oneshot->tim = esp32s3_tim_init(chan); + if (oneshot->tim == NULL) + { + tmrerr("ERROR: Failed to allocate TIM %d\n", chan); + ret = -EBUSY; + } + else + { + uint16_t pre; + + /* Initialize the remaining fields in the state structure. */ + + oneshot->running = false; + oneshot->handler = NULL; + oneshot->arg = NULL; + oneshot->resolution = resolution; + + /* Ensure timer is disabled. + * Change the prescaler divider with the timer enabled can lead to + * unpredictable results. + */ + + ESP32S3_TIM_STOP(oneshot->tim); + + ESP32S3_TIM_CLK_SRC(oneshot->tim, ESP32S3_TIM_APB_CLK); + + /* Calculate the suitable prescaler according to the current APB + * frequency to generate a period equals to resolution. + */ + + pre = (esp_clk_apb_freq() * resolution) / USEC_PER_SEC; + + /* Configure TIMER prescaler */ + + ESP32S3_TIM_SETPRE(oneshot->tim, pre); + } + + return ret; +} + +/**************************************************************************** + * Name: esp32s3_oneshot_max_delay + * + * Description: + * Return the maximum delay supported by the timer. + * + * Input Parameters: + * oneshot Caller allocated instance of the oneshot state structure. This + * structure must have been previously initialized via a call to + * esp32s3_oneshot_initialize(); + * + * Output Parameters: + * usec The location in which to return the maximum delay in us. + * + * Returned Value: + * Zero (OK). + * + ****************************************************************************/ + +int esp32s3_oneshot_max_delay(struct esp32s3_oneshot_s *oneshot, + uint64_t *usec) +{ + DEBUGASSERT(oneshot != NULL); + DEBUGASSERT(usec != NULL); + + /* In theory, Maximum delay (us) = resolution (us) * MAX_TIMER_COUNTER + * But if the resolution is bigger than 1 us, the value will not fit + * in a uint64_t. So, this function assumes the max delay using a + * resolution of 1 us. + */ + + *usec = MAX_TIMER_COUNTER; + + return OK; +} + +/**************************************************************************** + * Name: esp32s3_oneshot_start + * + * Description: + * Start the oneshot timer + * + * Input Parameters: + * oneshot Caller allocated instance of the oneshot state structure. This + * structure must have been previously initialized via a call to + * esp32s3_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. + * + * Returned Value: + * Zero (OK) is returned on success; a negated errno value is returned + * on failure. + * + ****************************************************************************/ + +int esp32s3_oneshot_start(struct esp32s3_oneshot_s *oneshot, + oneshot_handler_t handler, void *arg, + const struct timespec *ts) +{ + uint64_t timeout_us; + int ret = OK; + + tmrinfo("handler=%p arg=%p, ts=(%lu, %lu)\n", + handler, arg, (unsigned long)ts->tv_sec, + (unsigned long)ts->tv_nsec); + + DEBUGASSERT(oneshot != NULL); + DEBUGASSERT(handler != NULL); + DEBUGASSERT(ts != NULL); + + if (oneshot->running) + { + tmrinfo("Oneshot timer already in use. Cancelling it...\n"); + + /* If the oneshot timer was already started, cancel it and then + * restart. + */ + + esp32s3_oneshot_cancel(oneshot, NULL); + } + + /* Save the new callback and its argument */ + + oneshot->handler = handler; + oneshot->arg = arg; + + /* Retrieve the duration from timespec in microsecond */ + + timeout_us = (uint64_t)ts->tv_sec * USEC_PER_SEC + + (uint64_t)(ts->tv_nsec / NSEC_PER_USEC); + + /* Verify if it is a multiple of the configured resolution. + * In case it isn't, warn the user. + */ + + if ((timeout_us % oneshot->resolution) != 0) + { + tmrwarn("Warning: The interval is not multiple of the resolution.\n" + "Adjust the resolution in your bringup file.\n"); + } + + /* Set the timer */ + + /* Ensure timer is stopped */ + + ESP32S3_TIM_STOP(oneshot->tim); + + /* Configure TIMER mode */ + + ESP32S3_TIM_SETMODE(oneshot->tim, ESP32S3_TIM_MODE_UP); + + /* Clear TIMER counter value */ + + ESP32S3_TIM_CLEAR(oneshot->tim); + + /* Disable autoreload */ + + ESP32S3_TIM_SETARLD(oneshot->tim, false); + + /* Set the timeout */ + + ESP32S3_TIM_SETALRVL(oneshot->tim, timeout_us / oneshot->resolution); + + /* Enable TIMER alarm */ + + ESP32S3_TIM_SETALRM(oneshot->tim, true); + + /* Clear Interrupt Bits Status */ + + ESP32S3_TIM_ACKINT(oneshot->tim); + + /* Set the interrupt */ + + /* Register the handler that calls the callback */ + + ret = ESP32S3_TIM_SETISR(oneshot->tim, oneshot_handler, oneshot); + if (ret == OK) + { + ESP32S3_TIM_ENABLEINT(oneshot->tim); + + /* Finally, start the TIMER */ + + ESP32S3_TIM_START(oneshot->tim); + + oneshot->running = true; + } + + return ret; +} + +/**************************************************************************** + * Name: esp32s3_oneshot_cancel + * + * Description: + * Cancel the oneshot timer and return the time remaining on the timer. + * + * Input Parameters: + * oneshot Caller allocated instance of the oneshot state structure. This + * structure must have been previously initialized via a call to + * esp32s3_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. ts may be zero in which case the time remaining + * is not returned. + * + * 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. + * + ****************************************************************************/ + +int esp32s3_oneshot_cancel(struct esp32s3_oneshot_s *oneshot, + struct timespec *ts) +{ + int ret = OK; + uint64_t current_us; + uint64_t remaining_us; + uint64_t timeout_us; + uint64_t counter_value; + uint64_t alarm_value; + + DEBUGASSERT(oneshot != NULL); + + if (!oneshot->running) + { + tmrinfo("Trying to cancel a non started oneshot timer.\n"); + ts->tv_sec = 0; + ts->tv_nsec = 0; + } + else + { + /* Stop timer */ + + ESP32S3_TIM_STOP(oneshot->tim); + + /* Disable int */ + + ESP32S3_TIM_DISABLEINT(oneshot->tim); + + /* Detach handler */ + + ret = ESP32S3_TIM_SETISR(oneshot->tim, NULL, NULL); + + if (ts != NULL) + { + /* Get the current counter value */ + + ESP32S3_TIM_GETCTR(oneshot->tim, &counter_value); + + /* Get the current configured timeout */ + + ESP32S3_TIM_GETALRVL(oneshot->tim, &alarm_value); + + current_us = counter_value * oneshot->resolution; + timeout_us = alarm_value * oneshot->resolution; + + /* Remaining time (us) = timeout (us) - current (us) */ + + remaining_us = timeout_us - current_us; + ts->tv_sec = remaining_us / USEC_PER_SEC; + remaining_us = remaining_us - ts->tv_sec * USEC_PER_SEC; + ts->tv_nsec = remaining_us * NSEC_PER_USEC; + } + + oneshot->running = false; + oneshot->handler = NULL; + oneshot->arg = NULL; + } + + return ret; +} + +/**************************************************************************** + * Name: esp32s3_oneshot_current + * + * Description: + * Get the current time. + * + * Input Parameters: + * oneshot Caller allocated instance of the oneshot state structure. This + * structure must have been previously initialized via a call to + * esp32s3_oneshot_initialize(); + * + * Output Parameters: + * usec The location in which to return the current time in us. + * + * Returned Value: + * Zero (OK). + * + ****************************************************************************/ + +int esp32s3_oneshot_current(struct esp32s3_oneshot_s *oneshot, + uint64_t *usec) +{ + /* Get the current counter value */ + + ESP32S3_TIM_GETCTR(oneshot->tim, usec); + + *usec = *usec * (uint64_t)oneshot->resolution; + + return OK; +} + +#endif /* CONFIG_ESP32S3_ONESHOT */ diff --git a/arch/xtensa/src/esp32s3/esp32s3_oneshot.h b/arch/xtensa/src/esp32s3/esp32s3_oneshot.h new file mode 100644 index 0000000000..a9d3cfa0f8 --- /dev/null +++ b/arch/xtensa/src/esp32s3/esp32s3_oneshot.h @@ -0,0 +1,212 @@ +/**************************************************************************** + * arch/xtensa/src/esp32s3/esp32s3_oneshot.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_XTENSA_SRC_ESP32S3_ONESHOT_H +#define __ARCH_XTENSA_SRC_ESP32S3_ONESHOT_H + +/**************************************************************************** + * Included Files + ****************************************************************************/ + +#include + +#include +#include + +#include "esp32s3_tim.h" + +#ifdef CONFIG_ESP32S3_ONESHOT + +/**************************************************************************** + * Pre-processor Definitions + ****************************************************************************/ + +/* ONESHOT */ + +#define ONESHOT_RESOLUTION_US 1 + +/**************************************************************************** + * Public Types + ****************************************************************************/ + +/* This describes the callback function that will be invoked when the oneshot + * timer expires. The oneshot fires, the client will receive: + * + * arg - The opaque argument provided when the interrupt was registered + */ + +typedef void (*oneshot_handler_t)(void *arg); + +/* The oneshot client must allocate an instance of this structure and call + * esp32s3_oneshot_initialize() before using the oneshot facilities. The + * client should not access the contents of this structure directly since + * the contents are subject to change. + */ + +struct esp32s3_oneshot_s +{ + uint8_t chan; /* The timer/counter in use */ + volatile bool running; /* True: the timer is running */ + struct esp32s3_tim_dev_s *tim; /* Pointer returned by + * esp32s3_tim_init() */ + volatile oneshot_handler_t handler; /* Oneshot expiration callback */ + volatile void *arg; /* The argument that will accompany + * the callback */ + uint32_t resolution; /* us */ +}; + +/**************************************************************************** + * Public Data + ****************************************************************************/ + +#undef EXTERN +#if defined(__cplusplus) +#define EXTERN extern "C" +extern "C" +{ +#else +#define EXTERN extern +#endif + +/**************************************************************************** + * Public Function Prototypes + ****************************************************************************/ + +/**************************************************************************** + * Name: esp32s3_oneshot_initialize + * + * Description: + * Initialize the oneshot timer wrapper. + * + * Input Parameters: + * oneshot Allocated instance of the oneshot state structure. + * chan Timer counter channel to be used. + * resolution The required resolution of the timer in units of + * microseconds. NOTE that the range is restricted to the + * range of uint16_t (excluding zero). + * + * Returned Value: + * Zero (OK) is returned on success; a negated errno value is returned + * on failure. + * + ****************************************************************************/ + +int esp32s3_oneshot_initialize(struct esp32s3_oneshot_s *oneshot, + int chan, uint16_t resolution); + +/**************************************************************************** + * Name: esp32s3_oneshot_max_delay + * + * Description: + * Determine the maximum delay of the one-shot timer (in microseconds). + * + * Input Parameters: + * oneshot Allocated instance of the oneshot state structure. + * + * Output Parameters: + * usec The location in which to return the maximum delay in us. + * + * Returned Value: + * Zero (OK) is returned on success; a negated errno value is returned + * on failure. + * + ****************************************************************************/ + +int esp32s3_oneshot_max_delay(struct esp32s3_oneshot_s *oneshot, + uint64_t *usec); + +/**************************************************************************** + * Name: esp32s3_oneshot_start + * + * Description: + * Start the oneshot timer + * + * Input Parameters: + * oneshot Allocated instance of the oneshot state structure. This + * structure must have been previously initialized via a call to + * esp32s3_oneshot_initialize(); + * handler The function to call when the oneshot timer expires. + * arg An opaque argument that will accompany the callback. + * ts Provides the duration of the one shot timer. + * + * Returned Value: + * Zero (OK) is returned on success; a negated errno value is returned + * on failure. + * + ****************************************************************************/ + +int esp32s3_oneshot_start(struct esp32s3_oneshot_s *oneshot, + oneshot_handler_t handler, void *arg, + const struct timespec *ts); + +/**************************************************************************** + * Name: esp32s3_oneshot_cancel + * + * Description: + * Cancel the oneshot timer and return the time remaining on the timer. + * + * + * Input Parameters: + * oneshot Allocated instance of the oneshot state structure. This + * structure must have been previously initialized via a call to + * esp32s3_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. + * + ****************************************************************************/ + +int esp32s3_oneshot_cancel(struct esp32s3_oneshot_s *oneshot, + struct timespec *ts); + +/**************************************************************************** + * Name: esp32s3_oneshot_current + * + * Description: + * Get the current time. + * + * Input Parameters: + * oneshot Caller allocated instance of the oneshot state structure. This + * structure must have been previously initialized via a call to + * esp32s3_oneshot_initialize(); + * + * Output Parameters: + * usec The location in which to return the current time in us. + * + * Returned Value: + * Zero (OK). + * + ****************************************************************************/ + +int esp32s3_oneshot_current(struct esp32s3_oneshot_s *oneshot, + uint64_t *usec); + +#undef EXTERN +#ifdef __cplusplus +} +#endif + +#endif /* CONFIG_ESP32S3_ONESHOT */ +#endif /* __ARCH_XTENSA_SRC_ESP32S3_ONESHOT_H */ diff --git a/arch/xtensa/src/esp32s3/esp32s3_oneshot_lowerhalf.c b/arch/xtensa/src/esp32s3/esp32s3_oneshot_lowerhalf.c new file mode 100644 index 0000000000..215fe9ca1a --- /dev/null +++ b/arch/xtensa/src/esp32s3/esp32s3_oneshot_lowerhalf.c @@ -0,0 +1,370 @@ +/**************************************************************************** + * arch/xtensa/src/esp32s3/esp32s3_oneshot_lowerhalf.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. + * + ****************************************************************************/ + +/**************************************************************************** + * Included Files + ****************************************************************************/ + +#include + +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include + +#include "esp32s3_oneshot.h" + +/**************************************************************************** + * Pre-processor Definitions + ****************************************************************************/ + +/**************************************************************************** + * Private Types + ****************************************************************************/ + +struct esp32s3_oneshot_lowerhalf_s +{ + /* This is the part of the lower-half driver that is visible to the upper- + * half client of the driver. This must be the first thing in this + * struct so that pointers to struct oneshot_lowerhalf_s are cast + * compatible to struct esp32s3_oneshot_lowerhalf_s and vice versa. + */ + + struct oneshot_lowerhalf_s lh; /* Lower-half instance */ + struct esp32s3_oneshot_s oneshot; /* ESP32-S3-specific oneshot state */ + oneshot_callback_t callback; /* Upper-half Interrupt callback */ + void *arg; /* Argument passed to handler */ + uint16_t resolution; +}; + +/**************************************************************************** + * Private Function Prototypes + ****************************************************************************/ + +static void oneshot_lh_handler(void *arg); + +/* "Lower-half" driver methods **********************************************/ + +static int oneshot_lh_max_delay(struct oneshot_lowerhalf_s *lower, + struct timespec *ts); +static int oneshot_lh_start(struct oneshot_lowerhalf_s *lower, + oneshot_callback_t callback, + void *arg, + const struct timespec *ts); +static int oneshot_lh_cancel(struct oneshot_lowerhalf_s *lower, + struct timespec *ts); +static int oneshot_lh_current(struct oneshot_lowerhalf_s *lower, + struct timespec *ts); + +/**************************************************************************** + * Private Data + ****************************************************************************/ + +/* "Lower-half" driver methods */ + +static const struct oneshot_operations_s g_esp32s3_timer_ops = +{ + .max_delay = oneshot_lh_max_delay, + .start = oneshot_lh_start, + .cancel = oneshot_lh_cancel, + .current = oneshot_lh_current +}; + +/**************************************************************************** + * Private Functions + ****************************************************************************/ + +/**************************************************************************** + * Name: oneshot_lh_handler + * + * Description: + * Timer expiration handler. + * + * Input Parameters: + * arg - Should be the same argument provided when esp32s3_oneshot_start() + * was called. + * + ****************************************************************************/ + +static void oneshot_lh_handler(void *arg) +{ + struct esp32s3_oneshot_lowerhalf_s *priv = + (struct esp32s3_oneshot_lowerhalf_s *)arg; + oneshot_callback_t callback; + FAR void *cb_arg; + + DEBUGASSERT(priv != NULL); + DEBUGASSERT(priv->callback != NULL); + + tmrinfo("Oneshot LH handler triggered\n"); + + /* Sample and nullify BEFORE executing callback (in case the callback + * restarts the oneshot). + */ + + callback = priv->callback; + cb_arg = priv->arg; + priv->callback = NULL; + priv->arg = NULL; + + /* Then perform the callback */ + + callback(&priv->lh, cb_arg); +} + +/**************************************************************************** + * Name: oneshot_lh_max_delay + * + * Description: + * Determine the maximum delay of the oneshot timer (in microseconds). + * + * 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(). + * 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. + * + ****************************************************************************/ + +static int oneshot_lh_max_delay(struct oneshot_lowerhalf_s *lower, + struct timespec *ts) +{ + DEBUGASSERT(ts != NULL); + + /* The real maximum delay surpass the limit that timespec can represent. + * Even using the better case: a resolution of 1 us. + * Therefore, here, set the timespec with the maximum value it represent. + */ + + ts->tv_sec = UINT32_MAX; + ts->tv_nsec = NSEC_PER_SEC - 1; + + tmrinfo("max sec=%" PRIu32 "\n", ts->tv_sec); + tmrinfo("max nsec=%ld\n", ts->tv_nsec); + + return OK; +} + +/**************************************************************************** + * Name: oneshot_lh_start + * + * Description: + * Start the oneshot timer. + * + * 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(). + * callback The function to call when when the oneshot timer expires. + * Inside the handler scope. + * arg A pointer to the argument that will accompany the callback. + * ts Provides the duration of the one shot timer. + * + * Returned Value: + * Zero (OK) is returned on success; a negated errno value is returned + * on failure. + * + ****************************************************************************/ + +static int oneshot_lh_start(struct oneshot_lowerhalf_s *lower, + oneshot_callback_t callback, + void *arg, + const struct timespec *ts) +{ + struct esp32s3_oneshot_lowerhalf_s *priv = + (struct esp32s3_oneshot_lowerhalf_s *)lower; + int ret; + irqstate_t flags; + + DEBUGASSERT(priv != NULL); + DEBUGASSERT(callback != NULL); + DEBUGASSERT(ts != NULL); + + /* Save the callback information and start the timer */ + + flags = enter_critical_section(); + priv->callback = callback; + priv->arg = arg; + ret = esp32s3_oneshot_start(&priv->oneshot, oneshot_lh_handler, + priv, ts); + leave_critical_section(flags); + + if (ret < 0) + { + tmrerr("Failed to start oneshot timer: %d\n", ret); + } + + return ret; +} + +/**************************************************************************** + * Name: oneshot_lh_cancel + * + * Description: + * Cancel the oneshot timer and return the time remaining on the timer. + * + * NOTE: This function may execute at a high rate with no timer running (as + * when pre-emption is enabled and disabled). + * + * Input Parameters: + * 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. + * + ****************************************************************************/ + +static int oneshot_lh_cancel(struct oneshot_lowerhalf_s *lower, + struct timespec *ts) +{ + struct esp32s3_oneshot_lowerhalf_s *priv = + (struct esp32s3_oneshot_lowerhalf_s *)lower; + irqstate_t flags; + int ret; + + DEBUGASSERT(priv != NULL); + + /* Cancel the timer */ + + flags = enter_critical_section(); + ret = esp32s3_oneshot_cancel(&priv->oneshot, ts); + priv->callback = NULL; + priv->arg = NULL; + leave_critical_section(flags); + + if (ret < 0) + { + tmrerr("Failed to cancel oneshot timer: %d\n", ret); + } + + return ret; +} + +/**************************************************************************** + * Name: oneshot_lh_current + * + * Description: + * Get the current time. + * + * Input Parameters: + * 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. + * + ****************************************************************************/ + +static int oneshot_lh_current(struct oneshot_lowerhalf_s *lower, + struct timespec *ts) +{ + struct esp32s3_oneshot_lowerhalf_s *priv = + (struct esp32s3_oneshot_lowerhalf_s *)lower; + uint64_t current_us; + + DEBUGASSERT(priv != NULL); + DEBUGASSERT(ts != NULL); + + esp32s3_oneshot_current(&priv->oneshot, ¤t_us); + ts->tv_sec = current_us / USEC_PER_SEC; + current_us = current_us - ts->tv_sec * USEC_PER_SEC; + ts->tv_nsec = current_us * NSEC_PER_USEC; + + return OK; +} + +/**************************************************************************** + * Public Functions + ****************************************************************************/ + +/**************************************************************************** + * Name: oneshot_initialize + * + * Description: + * Initialize the oneshot timer and return a oneshot lower-half driver + * instance. + * + * Input Parameters: + * chan Timer counter channel to be used. + * resolution The required resolution of the timer in units of + * microseconds. NOTE that the range is restricted to the + * range of uint16_t (excluding zero). + * + * Returned Value: + * On success, a non-NULL instance of the oneshot lower-half driver is + * returned. NULL is return on any failure. + * + ****************************************************************************/ + +struct oneshot_lowerhalf_s *oneshot_initialize(int chan, uint16_t resolution) +{ + struct esp32s3_oneshot_lowerhalf_s *priv; + int ret; + + /* Allocate an instance of the lower-half driver */ + + priv = (struct esp32s3_oneshot_lowerhalf_s *)kmm_zalloc( + sizeof(struct esp32s3_oneshot_lowerhalf_s)); + + if (priv == NULL) + { + tmrerr("Failed to allocate oneshot state structure\n"); + return NULL; + } + + priv->lh.ops = &g_esp32s3_timer_ops; /* Pointer to the LH operations */ + priv->callback = NULL; /* No callback yet */ + priv->arg = NULL; /* No arg yet */ + priv->resolution = resolution; /* Configured resolution */ + + /* Initialize esp32s3_oneshot_s structure */ + + ret = esp32s3_oneshot_initialize(&priv->oneshot, chan, resolution); + if (ret < 0) + { + tmrerr("Failed to initialize oneshot timer driver: %d\n", ret); + kmm_free(priv); + return NULL; + } + + return &priv->lh; +}