diff --git a/arch/xtensa/src/esp32s2/Make.defs b/arch/xtensa/src/esp32s2/Make.defs index 79878aeaae..b375737e3b 100644 --- a/arch/xtensa/src/esp32s2/Make.defs +++ b/arch/xtensa/src/esp32s2/Make.defs @@ -89,3 +89,10 @@ endif ifeq ($(CONFIG_ESP32S2_RT_TIMER),y) CHIP_CSRCS += esp32s2_rt_timer.c endif + +ifeq ($(CONFIG_ESP32S2_ONESHOT),y) +CHIP_CSRCS += esp32s2_oneshot.c +ifeq ($(CONFIG_ONESHOT),y) +CHIP_CSRCS += esp32s2_oneshot_lowerhalf.c +endif +endif diff --git a/arch/xtensa/src/esp32s2/esp32s2_oneshot.c b/arch/xtensa/src/esp32s2/esp32s2_oneshot.c new file mode 100644 index 0000000000..ced51d210f --- /dev/null +++ b/arch/xtensa/src/esp32s2/esp32s2_oneshot.c @@ -0,0 +1,465 @@ +/**************************************************************************** + * arch/xtensa/src/esp32s2/esp32s2_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 "hardware/esp32s2_soc.h" + +#include "esp32s2_tim.h" +#include "esp32s2_clockconfig.h" +#include "esp32s2_oneshot.h" + +#ifdef CONFIG_ESP32S2_ONESHOT + +/**************************************************************************** + * Pre-processor Definitions + ****************************************************************************/ + +#define MAX_TIMER_COUNTER UINT64_MAX + +/**************************************************************************** + * Private Function Prototypes + ****************************************************************************/ + +static int esp32s2_oneshot_handler(int irq, void *context, void *arg); + +/**************************************************************************** + * Private Data + ****************************************************************************/ + +/**************************************************************************** + * Private Functions + ****************************************************************************/ + +/**************************************************************************** + * Name: esp32s2_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 + * 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 esp32s2_oneshot_handler(int irq, void *context, void *arg) +{ + int ret = OK; + struct esp32s2_oneshot_s *oneshot = + (struct esp32s2_oneshot_s *)arg; + + DEBUGASSERT(oneshot != NULL && oneshot->handler != NULL); + + tmrinfo("Oneshot handler triggered\n"); + + /* Stop timer + * Note: It's not necessary to disable the alarm because + * it automatically disables each time it expires. + */ + + ESP32S2_TIM_STOP(oneshot->tim); + + /* Disable interrupts */ + + ESP32S2_TIM_DISABLEINT(oneshot->tim); + + /* Detach handler */ + + ret = ESP32S2_TIM_SETISR(oneshot->tim, NULL, NULL); + + /* Call the callback */ + + oneshot->handler((void *)oneshot->arg); + + /* Restore state */ + + oneshot->running = false; + oneshot->handler = NULL; + oneshot->arg = NULL; + + /* Clear the Interrupt */ + + ESP32S2_TIM_ACKINT(oneshot->tim); + + return ret; +} + +/**************************************************************************** + * Public Functions + ****************************************************************************/ + +/**************************************************************************** + * Name: esp32s2_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 esp32s2_oneshot_initialize(struct esp32s2_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 = esp32s2_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. + */ + + ESP32S2_TIM_STOP(oneshot->tim); + + ESP32S2_TIM_CLK_SRC(oneshot->tim, ESP32S2_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 */ + + ESP32S2_TIM_SETPRE(oneshot->tim, pre); + } + + return ret; +} + +/**************************************************************************** + * Name: esp32s2_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 + * esp32s2_oneshot_initialize(); + * + * Output Parameters: + * usec The location in which to return the maximum delay in us. + * + * Returned Value: + * Zero (OK). + * + ****************************************************************************/ + +int esp32s2_oneshot_max_delay(struct esp32s2_oneshot_s *oneshot, + uint64_t *usec) +{ + DEBUGASSERT(oneshot != NULL && 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: esp32s2_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 + * esp32s2_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 esp32s2_oneshot_start(struct esp32s2_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("One shot timer already in use. Cancelling it ...\n"); + + /* If the oneshot timer was already started, cancel it and then + * restart. + */ + + esp32s2_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 */ + + ESP32S2_TIM_STOP(oneshot->tim); + + /* Configure TIMER mode */ + + ESP32S2_TIM_SETMODE(oneshot->tim, ESP32S2_TIM_MODE_UP); + + /* Clear TIMER counter value */ + + ESP32S2_TIM_CLEAR(oneshot->tim); + + /* Disable autoreload */ + + ESP32S2_TIM_SETARLD(oneshot->tim, false); + + /* Set the timeout */ + + ESP32S2_TIM_SETALRVL(oneshot->tim, timeout_us / oneshot->resolution); + + /* Enable TIMER alarm */ + + ESP32S2_TIM_SETALRM(oneshot->tim, true); + + /* Clear Interrupt Bits Status */ + + ESP32S2_TIM_ACKINT(oneshot->tim); + + /* Set the interrupt */ + + /* Register the handler that calls the callback */ + + ret = ESP32S2_TIM_SETISR(oneshot->tim, esp32s2_oneshot_handler, oneshot); + if (ret == OK) + { + ESP32S2_TIM_ENABLEINT(oneshot->tim); + + /* Finally, start the TIMER */ + + ESP32S2_TIM_START(oneshot->tim); + + oneshot->running = true; + } + + return ret; +} + +/**************************************************************************** + * Name: esp32s2_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 + * esp32s2_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 esp32s2_oneshot_cancel(struct esp32s2_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); + + if (oneshot->running == false) + { + tmrinfo("Trying to cancel a non started oneshot timer.\n"); + ts->tv_sec = 0; + ts->tv_nsec = 0; + } + else + { + /* Stop timer */ + + ESP32S2_TIM_STOP(oneshot->tim); + + /* Disable int */ + + ESP32S2_TIM_DISABLEINT(oneshot->tim); + + /* Detach handler */ + + ret = ESP32S2_TIM_SETISR(oneshot->tim, NULL, NULL); + + if (ts != NULL) + { + /* Get the current counter value */ + + ESP32S2_TIM_GETCTR(oneshot->tim, &counter_value); + + /* Get the current configured timeout */ + + ESP32S2_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: esp32s2_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 + * esp32s2_oneshot_initialize(); + * + * Output Parameters: + * usec The location in which to return the current time in us. + * + * Returned Value: + * Zero (OK). + * + ****************************************************************************/ + +int esp32s2_oneshot_current(struct esp32s2_oneshot_s *oneshot, + uint64_t *usec) +{ + /* Get the current counter value */ + + ESP32S2_TIM_GETCTR(oneshot->tim, usec); + + *usec = *usec * (uint64_t)oneshot->resolution; + + return OK; +} + +#endif /* CONFIG_ESP32S2_ONESHOT */ diff --git a/arch/xtensa/src/esp32s2/esp32s2_oneshot.h b/arch/xtensa/src/esp32s2/esp32s2_oneshot.h new file mode 100644 index 0000000000..97344aff1d --- /dev/null +++ b/arch/xtensa/src/esp32s2/esp32s2_oneshot.h @@ -0,0 +1,208 @@ +/**************************************************************************** + * arch/xtensa/src/esp32s2/esp32s2_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_ESP32S2_ONESHOT_H +#define __ARCH_XTENSA_SRC_ESP32S2_ONESHOT_H + +/**************************************************************************** + * Included Files + ****************************************************************************/ + +#include + +#include +#include + +#include "esp32s2_tim.h" + +#ifdef CONFIG_ESP32S2_ONESHOT + +/**************************************************************************** + * Pre-processor Definitions + ****************************************************************************/ + +/**************************************************************************** + * 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 + * esp32s2_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 esp32s2_oneshot_s +{ + uint8_t chan; /* The timer/counter in use */ + volatile bool running; /* True: the timer is running */ + struct esp32s2_tim_dev_s *tim; /* Pointer returned by + * esp32s2_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: esp32s2_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 esp32s2_oneshot_initialize(struct esp32s2_oneshot_s *oneshot, + int chan, uint16_t resolution); + +/**************************************************************************** + * Name: esp32s2_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 esp32s2_oneshot_max_delay(struct esp32s2_oneshot_s *oneshot, + uint64_t *usec); + +/**************************************************************************** + * Name: esp32s2_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 + * esp32s2_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 esp32s2_oneshot_start(struct esp32s2_oneshot_s *oneshot, + oneshot_handler_t handler, void *arg, + const struct timespec *ts); + +/**************************************************************************** + * Name: esp32s2_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 + * esp32s2_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 esp32s2_oneshot_cancel(struct esp32s2_oneshot_s *oneshot, + struct timespec *ts); + +/**************************************************************************** + * Name: esp32s2_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 + * esp32s2_oneshot_initialize(); + * + * Output Parameters: + * usec The location in which to return the current time in us. + * + * Returned Value: + * Zero (OK). + * + ****************************************************************************/ + +int esp32s2_oneshot_current(struct esp32s2_oneshot_s *oneshot, + uint64_t *usec); + +#undef EXTERN +#ifdef __cplusplus +} +#endif + +#endif /* CONFIG_ESP32S2_ONESHOT */ +#endif /* __ARCH_XTENSA_SRC_ESP32S2_ONESHOT_H */ diff --git a/arch/xtensa/src/esp32s2/esp32s2_oneshot_lowerhalf.c b/arch/xtensa/src/esp32s2/esp32s2_oneshot_lowerhalf.c new file mode 100644 index 0000000000..935779d1f8 --- /dev/null +++ b/arch/xtensa/src/esp32s2/esp32s2_oneshot_lowerhalf.c @@ -0,0 +1,370 @@ +/**************************************************************************** + * arch/xtensa/src/esp32s2/esp32s2_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 "esp32s2_oneshot.h" + +/**************************************************************************** + * Pre-processor Definitions + ****************************************************************************/ + +/**************************************************************************** + * Private Types + ****************************************************************************/ + +struct esp32s2_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 esp32s2_oneshot_lowerhalf_s and vice versa. + */ + + struct oneshot_lowerhalf_s lh; /* Lower half instance */ + struct esp32s2_oneshot_s oneshot; /* ESP32-S2-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 esp32s2_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_esp32s2_timer_ops = +{ + .max_delay = oneshot_lh_max_delay, + .start = oneshot_lh_start, + .cancel = oneshot_lh_cancel, + .current = oneshot_lh_current +}; + +/**************************************************************************** + * Private Functions + ****************************************************************************/ + +/**************************************************************************** + * Name: esp32s2_oneshot_lh_handler + * + * Description: + * Timer expiration handler. + * + * Input Parameters: + * arg - Should be the same argument provided when esp32s2_oneshot_start() + * was called. + * + ****************************************************************************/ + +static void esp32s2_oneshot_lh_handler(void *arg) +{ + struct esp32s2_oneshot_lowerhalf_s *priv = + (struct esp32s2_oneshot_lowerhalf_s *)arg; + + DEBUGASSERT(priv != NULL); + DEBUGASSERT(priv->callback != NULL); + + tmrinfo("Oneshot LH handler triggered\n"); + + /* Call the callback */ + + priv->callback(&priv->lh, priv->arg); + + /* Restore state */ + + priv->callback = NULL; + priv->arg = NULL; +} + +/**************************************************************************** + * Name: oneshot_lh_max_delay + * + * Description: + * Determine the maximum delay of the one-shot 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 can 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 esp32s2_oneshot_lowerhalf_s *priv = + (struct esp32s2_oneshot_lowerhalf_s *)lower; + int ret; + irqstate_t flags; + + DEBUGASSERT(priv != NULL); + DEBUGASSERT(callback != NULL); + DEBUGASSERT(arg != NULL); + DEBUGASSERT(ts != NULL); + + /* Save the callback information and start the timer */ + + flags = enter_critical_section(); + priv->callback = callback; + priv->arg = arg; + ret = esp32s2_oneshot_start(&priv->oneshot, + esp32s2_oneshot_lh_handler, + priv, + ts); + leave_critical_section(flags); + + if (ret < 0) + { + tmrerr("ERROR: esp32s2_oneshot_start failed: %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 esp32s2_oneshot_lowerhalf_s *priv = + (struct esp32s2_oneshot_lowerhalf_s *)lower; + irqstate_t flags; + int ret; + + DEBUGASSERT(priv != NULL); + + /* Cancel the timer */ + + flags = enter_critical_section(); + ret = esp32s2_oneshot_cancel(&priv->oneshot, ts); + priv->callback = NULL; + priv->arg = NULL; + leave_critical_section(flags); + + if (ret < 0) + { + tmrerr("ERROR: esp32s2_oneshot_cancel failed: %d\n", flags); + } + + 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 esp32s2_oneshot_lowerhalf_s *priv = + (struct esp32s2_oneshot_lowerhalf_s *)lower; + uint64_t current_us; + + DEBUGASSERT(priv != NULL); + DEBUGASSERT(ts != NULL); + + esp32s2_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 esp32s2_oneshot_lowerhalf_s *priv; + int ret; + + /* Allocate an instance of the lower half driver */ + + priv = (struct esp32s2_oneshot_lowerhalf_s *)kmm_zalloc( + sizeof(struct esp32s2_oneshot_lowerhalf_s)); + + if (priv == NULL) + { + tmrerr("ERROR: Failed to initialize oneshot state structure\n"); + return NULL; + } + + priv->lh.ops = &g_esp32s2_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 esp32s2_oneshot_s structure */ + + ret = esp32s2_oneshot_initialize(&priv->oneshot, chan, resolution); + if (ret < 0) + { + tmrerr("ERROR: esp32s2_oneshot_initialize failed: %d\n", ret); + kmm_free(priv); + return NULL; + } + + return &priv->lh; +} diff --git a/boards/xtensa/esp32s2/esp32s2-saola-1/configs/oneshot/defconfig b/boards/xtensa/esp32s2/esp32s2-saola-1/configs/oneshot/defconfig new file mode 100644 index 0000000000..a65ac977bd --- /dev/null +++ b/boards/xtensa/esp32s2/esp32s2-saola-1/configs/oneshot/defconfig @@ -0,0 +1,51 @@ +# +# This file is autogenerated: PLEASE DO NOT EDIT IT. +# +# You can use "make menuconfig" to make any modifications to the installed .config file. +# You can then do "make savedefconfig" to generate a new defconfig file that includes your +# modifications. +# +# CONFIG_ARCH_LEDS is not set +# CONFIG_NSH_ARGCAT is not set +# CONFIG_NSH_CMDOPT_HEXDUMP is not set +# CONFIG_NSH_CMDPARMS is not set +CONFIG_ARCH="xtensa" +CONFIG_ARCH_BOARD="esp32s2-saola-1" +CONFIG_ARCH_BOARD_ESP32S2_SAOLA_1=y +CONFIG_ARCH_CHIP="esp32s2" +CONFIG_ARCH_CHIP_ESP32S2=y +CONFIG_ARCH_CHIP_ESP32S2WROVER=y +CONFIG_ARCH_STACKDUMP=y +CONFIG_ARCH_XTENSA=y +CONFIG_BOARD_LOOPSPERMSEC=16717 +CONFIG_BUILTIN=y +CONFIG_ESP32S2_DATA_CACHE_0KB=y +CONFIG_ESP32S2_ONESHOT=y +CONFIG_ESP32S2_TIMER0=y +CONFIG_ESP32S2_UART0=y +CONFIG_EXAMPLES_ONESHOT=y +CONFIG_EXAMPLES_ONESHOT_DELAY=2000000 +CONFIG_FS_PROCFS=y +CONFIG_HAVE_CXX=y +CONFIG_HAVE_CXXINITIALIZE=y +CONFIG_IDLETHREAD_STACKSIZE=3072 +CONFIG_INTELHEX_BINARY=y +CONFIG_NSH_ARCHINIT=y +CONFIG_NSH_BUILTIN_APPS=y +CONFIG_NSH_FILEIOSIZE=512 +CONFIG_NSH_LINELEN=64 +CONFIG_NSH_READLINE=y +CONFIG_ONESHOT=y +CONFIG_PREALLOC_TIMERS=4 +CONFIG_RAM_SIZE=114688 +CONFIG_RAM_START=0x20000000 +CONFIG_RAW_BINARY=y +CONFIG_RR_INTERVAL=200 +CONFIG_SCHED_WAITPID=y +CONFIG_SDCLONE_DISABLE=y +CONFIG_START_DAY=6 +CONFIG_START_MONTH=12 +CONFIG_START_YEAR=2011 +CONFIG_SYSTEM_NSH=y +CONFIG_UART0_SERIAL_CONSOLE=y +CONFIG_USER_ENTRYPOINT="nsh_main" diff --git a/boards/xtensa/esp32s2/esp32s2-saola-1/src/Make.defs b/boards/xtensa/esp32s2/esp32s2-saola-1/src/Make.defs index ba1f755649..23737b3157 100644 --- a/boards/xtensa/esp32s2/esp32s2-saola-1/src/Make.defs +++ b/boards/xtensa/esp32s2/esp32s2-saola-1/src/Make.defs @@ -37,6 +37,10 @@ ifeq ($(CONFIG_DEV_GPIO),y) CSRCS += esp32s2_gpio.c endif +ifeq ($(CONFIG_ONESHOT),y) +CSRCS += esp32s2_oneshot.c +endif + SCRIPTIN = $(SCRIPTDIR)$(DELIM)esp32s2.template.ld SCRIPTOUT = $(SCRIPTDIR)$(DELIM)esp32s2_out.ld diff --git a/boards/xtensa/esp32s2/esp32s2-saola-1/src/esp32s2-saola-1.h b/boards/xtensa/esp32s2/esp32s2-saola-1/src/esp32s2-saola-1.h index b014312c42..35bb4e25bc 100644 --- a/boards/xtensa/esp32s2/esp32s2-saola-1/src/esp32s2-saola-1.h +++ b/boards/xtensa/esp32s2/esp32s2-saola-1/src/esp32s2-saola-1.h @@ -46,6 +46,11 @@ #define TIMER2 2 #define TIMER3 3 +/* ONESHOT */ + +#define ONESHOT_TIMER TIMER0 +#define ONESHOT_RESOLUTION_US 1 + /**************************************************************************** * Public Types ****************************************************************************/ @@ -92,5 +97,25 @@ int esp32s2_bringup(void); int esp32s2_gpio_init(void); #endif +/**************************************************************************** + * Name: board_oneshot_init + * + * Description: + * Configure the oneshot timer driver. + * + * Input Parameters: + * timer - Timer instance to be used as oneshot timer. + * resolution - Oneshot timer resolution. + * + * Returned Value: + * Zero (OK) is returned on success; A negated errno value is returned + * to indicate the nature of any failure. + * + ****************************************************************************/ + +#ifdef CONFIG_ONESHOT +int board_oneshot_init(int timer, uint16_t resolution); +#endif + #endif /* __ASSEMBLY__ */ #endif /* __BOARDS_XTENSA_ESP32S2_ESP32S2_SAOLA_1_SRC_ESP32S2_SAOLA_1_H */ diff --git a/boards/xtensa/esp32s2/esp32s2-saola-1/src/esp32s2_bringup.c b/boards/xtensa/esp32s2/esp32s2-saola-1/src/esp32s2_bringup.c index 77297ac21d..f6dd5edd6a 100644 --- a/boards/xtensa/esp32s2/esp32s2-saola-1/src/esp32s2_bringup.c +++ b/boards/xtensa/esp32s2/esp32s2-saola-1/src/esp32s2_bringup.c @@ -112,7 +112,7 @@ int esp32s2_bringup(void) #ifdef CONFIG_TIMER -#ifdef CONFIG_ESP32S2_TIMER0 +#if defined(CONFIG_ESP32S2_TIMER0) && !defined(CONFIG_ONESHOT) ret = esp32s2_timer_initialize("/dev/timer0", TIMER0); if (ret < 0) { @@ -164,7 +164,19 @@ int esp32s2_bringup(void) { syslog(LOG_ERR, "Failed to initialize RT timer: %d\n", ret); } + #endif + /* Now register one oneshot driver */ + +#if defined(CONFIG_ONESHOT) && defined(CONFIG_ESP32S2_TIMER0) + + ret = board_oneshot_init(ONESHOT_TIMER, ONESHOT_RESOLUTION_US); + if (ret < 0) + { + syslog(LOG_ERR, "ERROR: board_oneshot_init() failed: %d\n", ret); + } + +#endif /* CONFIG_ONESHOT */ /* If we got here then perhaps not all initialization was successful, but * at least enough succeeded to bring-up NSH with perhaps reduced diff --git a/boards/xtensa/esp32s2/esp32s2-saola-1/src/esp32s2_oneshot.c b/boards/xtensa/esp32s2/esp32s2-saola-1/src/esp32s2_oneshot.c new file mode 100644 index 0000000000..b701da36dc --- /dev/null +++ b/boards/xtensa/esp32s2/esp32s2-saola-1/src/esp32s2_oneshot.c @@ -0,0 +1,83 @@ +/**************************************************************************** + * boards/xtensa/esp32s2/esp32s2-saola-1/src/esp32s2_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 "esp32s2-saola-1.h" + +/**************************************************************************** + * Pre-processor Definitions + ****************************************************************************/ + +/**************************************************************************** + * Public Functions + ****************************************************************************/ + +/**************************************************************************** + * Name: board_oneshot_init + * + * Description: + * Configure the oneshot timer driver. + * + * Returned Value: + * Zero (OK) is returned on success; A negated errno value is returned + * to indicate the nature of any failure. + * + ****************************************************************************/ + +int board_oneshot_init(int timer, uint16_t resolution) +{ + int ret = OK; + struct oneshot_lowerhalf_s *os_lower = NULL; + + os_lower = oneshot_initialize(timer, resolution); + if (os_lower != NULL) + { +#if defined(CONFIG_CPULOAD_ONESHOT) + /* Configure the oneshot timer to support CPU load measurement */ + + nxsched_oneshot_extclk(os_lower); + +#else + ret = oneshot_register("/dev/oneshot", os_lower); + if (ret < 0) + { + syslog(LOG_ERR, + "ERROR: Failed to register oneshot at /dev/oneshot: %d\n", ret); + } +#endif /* CONFIG_CPULOAD_ONESHOT */ + } + else + { + syslog(LOG_ERR, "ERROR: oneshot_initialize failed\n"); + ret = -EBUSY; + } + + return ret; +}