This is an automated email from the ASF dual-hosted git repository. xiaoxiang781216 pushed a commit to branch master in repository https://gitbox.apache.org/repos/asf/nuttx.git
commit 6f55cb9b859b2f7105af7e7ac4657fc4c762fe62 Author: raiden00pl <[email protected]> AuthorDate: Sat Oct 10 20:41:48 2026 +0200 arch/arm/src/common/stm32: unify tickless driver Use one common tickless driver for all STM32 families with the single-timer tickless implementation and remove the STM32F7, STM32H7 and STM32WB copies. The common driver is based on the STM32F7 version, which already had CONFIG_SCHED_TICKLESS_ALARM support: - The timer counter width is taken from STM32_HAVE_TIMn_32BITS. - The timer is stopped on debug halt where the family has a DBGMCU freeze bit for it. - The CPU load SysTick clock (STM32H7) is generalized to all families. - Kconfig limits the tickless channel to 1-4 and basic timers are refused at build time. STM32H5 selects the single-timer tickless implementation but had no driver, so tickless now builds there too. Signed-off-by: raiden00pl <[email protected]> Assisted-by: Claude Code --- .github/CODEOWNERS | 5 +- LICENSE | 35 +- arch/arm/src/common/stm32/CMakeLists.txt | 8 +- arch/arm/src/common/stm32/Make.defs | 8 +- .../src/{stm32f7 => common/stm32}/stm32_tickless.c | 138 +-- arch/arm/src/common/stm32/stm32_tickless_m3m4_v1.c | 1017 ------------------ arch/arm/src/stm32f7/CMakeLists.txt | 4 - arch/arm/src/stm32f7/Make.defs | 4 - arch/arm/src/stm32h7/CMakeLists.txt | 4 - arch/arm/src/stm32h7/Make.defs | 4 - arch/arm/src/stm32h7/stm32_tickless.c | 1081 -------------------- arch/arm/src/stm32wb/CMakeLists.txt | 4 - arch/arm/src/stm32wb/Make.defs | 4 - arch/arm/src/stm32wb/stm32wb_tickless.c | 886 ---------------- 14 files changed, 91 insertions(+), 3111 deletions(-) diff --git a/.github/CODEOWNERS b/.github/CODEOWNERS index 6c8c05ae783..5c1f4578231 100644 --- a/.github/CODEOWNERS +++ b/.github/CODEOWNERS @@ -1851,7 +1851,7 @@ arch/arm/src/common/stm32/stm32_start_m0_v1.c [email protected] acassis@gma arch/arm/src/common/stm32/stm32_start_m3m4_v1.c [email protected] [email protected] [email protected] [email protected] [email protected] arch/arm/src/common/stm32/stm32_syscfg.h [email protected] [email protected] [email protected] [email protected] [email protected] arch/arm/src/common/stm32/stm32_systick.h [email protected] -arch/arm/src/common/stm32/stm32_tickless_m3m4_v1.c [email protected] [email protected] [email protected] [email protected] [email protected] +arch/arm/src/common/stm32/stm32_tickless.c [email protected] [email protected] [email protected] [email protected] [email protected] arch/arm/src/common/stm32/stm32_tim.c [email protected] [email protected] [email protected] [email protected] [email protected] arch/arm/src/common/stm32/stm32_tim.h [email protected] [email protected] arch/arm/src/common/stm32/stm32_tim_clk.h [email protected] @@ -4006,7 +4006,6 @@ arch/arm/src/stm32f7/stm32_spi.c [email protected] [email protected] arch/arm/src/stm32f7/stm32_spi.h [email protected] [email protected] [email protected] [email protected] arch/arm/src/stm32f7/stm32_start.c [email protected] [email protected] [email protected] [email protected] arch/arm/src/stm32f7/stm32_start.h [email protected] [email protected] [email protected] -arch/arm/src/stm32f7/stm32_tickless.c [email protected] [email protected] [email protected] [email protected] [email protected] arch/arm/src/stm32f7/stm32_uart.h [email protected] [email protected] [email protected] [email protected] arch/arm/src/stm32f7/stm32_uid.c [email protected] [email protected] [email protected] [email protected] arch/arm/src/stm32f7/stm32_uid.h [email protected] [email protected] [email protected] [email protected] @@ -4254,7 +4253,6 @@ arch/arm/src/stm32h7/stm32_spi.h [email protected] [email protected] a arch/arm/src/stm32h7/stm32_spi_slave.c [email protected] [email protected] [email protected] [email protected] [email protected] arch/arm/src/stm32h7/stm32_start.c [email protected] [email protected] [email protected] [email protected] [email protected] arch/arm/src/stm32h7/stm32_start.h [email protected] [email protected] [email protected] [email protected] -arch/arm/src/stm32h7/stm32_tickless.c [email protected] [email protected] [email protected] [email protected] [email protected] arch/arm/src/stm32h7/stm32_uart.h [email protected] [email protected] [email protected] [email protected] [email protected] arch/arm/src/stm32h7/stm32_uid.c [email protected] [email protected] [email protected] [email protected] arch/arm/src/stm32h7/stm32_uid.h [email protected] [email protected] [email protected] @@ -4611,7 +4609,6 @@ arch/arm/src/stm32wb/stm32wb_spi.c [email protected] [email protected] gustav arch/arm/src/stm32wb/stm32wb_spi.h [email protected] [email protected] arch/arm/src/stm32wb/stm32wb_start.c [email protected] [email protected] [email protected] [email protected] [email protected] arch/arm/src/stm32wb/stm32wb_start.h [email protected] [email protected] -arch/arm/src/stm32wb/stm32wb_tickless.c [email protected] [email protected] [email protected] [email protected] arch/arm/src/stm32wb/stm32wb_uart.h [email protected] [email protected] arch/arm/src/stm32wb/stm32wb_uid.c [email protected] [email protected] arch/arm/src/stm32wb/stm32wb_uid.h [email protected] [email protected] diff --git a/LICENSE b/LICENSE index a1014a4c7e7..f6ddcee1a00 100644 --- a/LICENSE +++ b/LICENSE @@ -4144,7 +4144,7 @@ LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -arch/arm/src/common/stm32/stm32_tickless_m3m4_v1.c +arch/arm/src/common/stm32/stm32_tickless.c ====================================== Copyright (C) 2016-2017 Gregory Nutt. All rights reserved. @@ -4507,39 +4507,6 @@ LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -arch/arm/src/stm32f7/stm32_tickless.c -========================================= - - Copyright (C) 2016-2017 Gregory Nutt. All rights reserved. - Copyright (C) 2017 Ansync Labs. All rights reserved. - -Redistribution and use in source and binary forms, with or without -modification, are permitted provided that the following conditions -are met: - -1. Redistributions of source code must retain the above copyright - notice, this list of conditions and the following disclaimer. -2. Redistributions in binary form must reproduce the above copyright - notice, this list of conditions and the following disclaimer in - the documentation and/or other materials provided with the - distribution. -3. Neither the name NuttX nor the names of its contributors may be - used to endorse or promote products derived from this software - without specific prior written permission. - -THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS -"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT -LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS -FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE -COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, -INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, -BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS -OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED -AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT -LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN -ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE -POSSIBILITY OF SUCH DAMAGE. - arch/arm/src/stm32f7/stm32_uid.c arch/arm/src/stm32f7/stm32_uid.h ====================================== diff --git a/arch/arm/src/common/stm32/CMakeLists.txt b/arch/arm/src/common/stm32/CMakeLists.txt index 76c07308640..161cc0ac3da 100644 --- a/arch/arm/src/common/stm32/CMakeLists.txt +++ b/arch/arm/src/common/stm32/CMakeLists.txt @@ -43,6 +43,10 @@ if(NOT CONFIG_SCHED_TICKLESS) endif() endif() +if(CONFIG_STM32_TICKLESS_TIMER) + list(APPEND SRCS stm32_tickless.c) +endif() + if(CONFIG_STM32_IWDG) list(APPEND SRCS stm32_iwdg.c) endif() @@ -170,10 +174,6 @@ if(CONFIG_STM32_COMMON_LEGACY) list(APPEND SRCS stm32_flash_m3m4_g4.c) endif() - if(CONFIG_STM32_TICKLESS_TIMER) - list(APPEND SRCS stm32_tickless_m3m4_v1.c) - endif() - if(CONFIG_BUILD_PROTECTED) list(APPEND SRCS stm32_userspace_m3m4_v1.c stm32_mpuinit_m3m4_v1.c) endif() diff --git a/arch/arm/src/common/stm32/Make.defs b/arch/arm/src/common/stm32/Make.defs index e04f277f0e8..cda5d61fbae 100644 --- a/arch/arm/src/common/stm32/Make.defs +++ b/arch/arm/src/common/stm32/Make.defs @@ -51,6 +51,10 @@ CHIP_CSRCS += stm32_timerisr.c endif endif +ifdef CONFIG_STM32_TICKLESS_TIMER +CHIP_CSRCS += stm32_tickless.c +endif + ifeq ($(CONFIG_STM32_IWDG),y) CHIP_CSRCS += stm32_iwdg.c endif @@ -170,10 +174,6 @@ ifeq ($(CONFIG_STM32_HAVE_IP_DFUMODE_M3M4_V1),y) CHIP_CSRCS += stm32_dfumode_m3m4_v1.c endif -ifdef CONFIG_STM32_TICKLESS_TIMER -CHIP_CSRCS += stm32_tickless_m3m4_v1.c -endif - ifeq ($(CONFIG_BUILD_PROTECTED),y) CHIP_CSRCS += stm32_userspace_m3m4_v1.c CHIP_CSRCS += stm32_mpuinit_m3m4_v1.c diff --git a/arch/arm/src/stm32f7/stm32_tickless.c b/arch/arm/src/common/stm32/stm32_tickless.c similarity index 89% rename from arch/arm/src/stm32f7/stm32_tickless.c rename to arch/arm/src/common/stm32/stm32_tickless.c index 6951ea4db9a..ae8772a8898 100644 --- a/arch/arm/src/stm32f7/stm32_tickless.c +++ b/arch/arm/src/common/stm32/stm32_tickless.c @@ -1,5 +1,5 @@ /**************************************************************************** - * arch/arm/src/stm32f7/stm32_tickless.c + * arch/arm/src/common/stm32/stm32_tickless.c * * SPDX-License-Identifier: BSD-3-Clause * SPDX-FileCopyrightText: 2016-2017 Gregory Nutt. All rights reserved. @@ -89,10 +89,17 @@ #include <nuttx/arch.h> #include <nuttx/debug.h> +#include <arch/board/board.h> #include "arm_internal.h" #include "stm32_tim.h" -#include "stm32_dbgmcu.h" + +#if defined(CONFIG_ARMV7M_SYSTICK) || defined(CONFIG_ARMV8M_SYSTICK) +# include "systick.h" +# include "stm32_systick.h" +#endif + +#include "hardware/stm32_dbgmcu.h" #ifdef CONFIG_SCHED_TICKLESS @@ -100,16 +107,67 @@ * Pre-processor Definitions ****************************************************************************/ -/* Only TIM2 and TIM5 timers may be 32-bits in width - * - * Reference Table 2 of en.DM00042534.pdf - */ - #undef HAVE_32BIT_TICKLESS -#if (CONFIG_STM32_TICKLESS_TIMER == 2) || \ - (CONFIG_STM32_TICKLESS_TIMER == 5) - #define HAVE_32BIT_TICKLESS 1 +#if (CONFIG_STM32_TICKLESS_TIMER == 2 && \ + defined(CONFIG_STM32_HAVE_TIM2_32BITS)) || \ + (CONFIG_STM32_TICKLESS_TIMER == 5 && \ + defined(CONFIG_STM32_HAVE_TIM5_32BITS)) +# define HAVE_32BIT_TICKLESS 1 +#endif + +#if (CONFIG_STM32_TICKLESS_TIMER == 6) || \ + (CONFIG_STM32_TICKLESS_TIMER == 7) +# error Basic timers not supported by the tickless driver +#endif + +/* Debug MCU freeze bit of the tickless timer */ + +#if CONFIG_STM32_TICKLESS_TIMER == 1 && defined(DBGMCU_TIM1_STOP) +# define TICKLESS_DBGMCU_FZ STM32_DBGMCU_TIM1_FZ +# define TICKLESS_DBGMCU_STOP DBGMCU_TIM1_STOP +#elif CONFIG_STM32_TICKLESS_TIMER == 2 && defined(DBGMCU_TIM2_STOP) +# define TICKLESS_DBGMCU_FZ STM32_DBGMCU_TIM2_FZ +# define TICKLESS_DBGMCU_STOP DBGMCU_TIM2_STOP +#elif CONFIG_STM32_TICKLESS_TIMER == 3 && defined(DBGMCU_TIM3_STOP) +# define TICKLESS_DBGMCU_FZ STM32_DBGMCU_TIM3_FZ +# define TICKLESS_DBGMCU_STOP DBGMCU_TIM3_STOP +#elif CONFIG_STM32_TICKLESS_TIMER == 4 && defined(DBGMCU_TIM4_STOP) +# define TICKLESS_DBGMCU_FZ STM32_DBGMCU_TIM4_FZ +# define TICKLESS_DBGMCU_STOP DBGMCU_TIM4_STOP +#elif CONFIG_STM32_TICKLESS_TIMER == 5 && defined(DBGMCU_TIM5_STOP) +# define TICKLESS_DBGMCU_FZ STM32_DBGMCU_TIM5_FZ +# define TICKLESS_DBGMCU_STOP DBGMCU_TIM5_STOP +#elif CONFIG_STM32_TICKLESS_TIMER == 8 && defined(DBGMCU_TIM8_STOP) +# define TICKLESS_DBGMCU_FZ STM32_DBGMCU_TIM8_FZ +# define TICKLESS_DBGMCU_STOP DBGMCU_TIM8_STOP +#elif CONFIG_STM32_TICKLESS_TIMER == 9 && defined(DBGMCU_TIM9_STOP) +# define TICKLESS_DBGMCU_FZ STM32_DBGMCU_TIM9_FZ +# define TICKLESS_DBGMCU_STOP DBGMCU_TIM9_STOP +#elif CONFIG_STM32_TICKLESS_TIMER == 10 && defined(DBGMCU_TIM10_STOP) +# define TICKLESS_DBGMCU_FZ STM32_DBGMCU_TIM10_FZ +# define TICKLESS_DBGMCU_STOP DBGMCU_TIM10_STOP +#elif CONFIG_STM32_TICKLESS_TIMER == 11 && defined(DBGMCU_TIM11_STOP) +# define TICKLESS_DBGMCU_FZ STM32_DBGMCU_TIM11_FZ +# define TICKLESS_DBGMCU_STOP DBGMCU_TIM11_STOP +#elif CONFIG_STM32_TICKLESS_TIMER == 12 && defined(DBGMCU_TIM12_STOP) +# define TICKLESS_DBGMCU_FZ STM32_DBGMCU_TIM12_FZ +# define TICKLESS_DBGMCU_STOP DBGMCU_TIM12_STOP +#elif CONFIG_STM32_TICKLESS_TIMER == 13 && defined(DBGMCU_TIM13_STOP) +# define TICKLESS_DBGMCU_FZ STM32_DBGMCU_TIM13_FZ +# define TICKLESS_DBGMCU_STOP DBGMCU_TIM13_STOP +#elif CONFIG_STM32_TICKLESS_TIMER == 14 && defined(DBGMCU_TIM14_STOP) +# define TICKLESS_DBGMCU_FZ STM32_DBGMCU_TIM14_FZ +# define TICKLESS_DBGMCU_STOP DBGMCU_TIM14_STOP +#elif CONFIG_STM32_TICKLESS_TIMER == 15 && defined(DBGMCU_TIM15_STOP) +# define TICKLESS_DBGMCU_FZ STM32_DBGMCU_TIM15_FZ +# define TICKLESS_DBGMCU_STOP DBGMCU_TIM15_STOP +#elif CONFIG_STM32_TICKLESS_TIMER == 16 && defined(DBGMCU_TIM16_STOP) +# define TICKLESS_DBGMCU_FZ STM32_DBGMCU_TIM16_FZ +# define TICKLESS_DBGMCU_STOP DBGMCU_TIM16_STOP +#elif CONFIG_STM32_TICKLESS_TIMER == 17 && defined(DBGMCU_TIM17_STOP) +# define TICKLESS_DBGMCU_FZ STM32_DBGMCU_TIM17_FZ +# define TICKLESS_DBGMCU_STOP DBGMCU_TIM17_STOP #endif #if CONFIG_STM32_TICKLESS_CHANNEL == 1 @@ -250,7 +308,7 @@ static int stm32_tickless_setchannel(uint8_t channel) /* Further we use range as 0..3; if channel=0 it will also overflow here */ - if (--channel > 4) + if (--channel > 3) { return -EINVAL; } @@ -260,16 +318,6 @@ static int stm32_tickless_setchannel(uint8_t channel) ccer_val &= ~((GTIM_CCER_CC1P | GTIM_CCER_CC1E) << GTIM_CCER_CCXBASE(channel)); - /* This function is not supported on basic timers. To enable or - * disable it, simply set its clock to valid frequency or zero. - */ - - if (g_tickless.base == STM32_TIM6_BASE || - g_tickless.base == STM32_TIM7_BASE) - { - return -EINVAL; - } - /* Frozen mode because we don't want to change the GPIO, preload register * disabled. */ @@ -441,106 +489,74 @@ void up_timer_initialize(void) #ifdef CONFIG_STM32_TIM1 case 1: g_tickless.base = STM32_TIM1_BASE; - modifyreg32(STM32_DBGMCU_TIM1_FZ, 0, DBGMCU_TIM1_STOP); break; #endif #ifdef CONFIG_STM32_TIM2 case 2: g_tickless.base = STM32_TIM2_BASE; - modifyreg32(STM32_DBGMCU_TIM2_FZ, 0, DBGMCU_TIM2_STOP); break; #endif #ifdef CONFIG_STM32_TIM3 case 3: g_tickless.base = STM32_TIM3_BASE; - modifyreg32(STM32_DBGMCU_TIM3_FZ, 0, DBGMCU_TIM3_STOP); break; #endif #ifdef CONFIG_STM32_TIM4 case 4: g_tickless.base = STM32_TIM4_BASE; - modifyreg32(STM32_DBGMCU_TIM4_FZ, 0, DBGMCU_TIM4_STOP); break; #endif #ifdef CONFIG_STM32_TIM5 case 5: g_tickless.base = STM32_TIM5_BASE; - modifyreg32(STM32_DBGMCU_TIM5_FZ, 0, DBGMCU_TIM5_STOP); - break; -#endif - -#ifdef CONFIG_STM32_TIM6 - case 6: - - /* Basic timers not supported by this implementation */ - - DEBUGPANIC(); - break; -#endif - -#ifdef CONFIG_STM32_TIM7 - case 7: - - /* Basic timers not supported by this implementation */ - - DEBUGPANIC(); break; #endif #ifdef CONFIG_STM32_TIM8 case 8: g_tickless.base = STM32_TIM8_BASE; - modifyreg32(STM32_DBGMCU_TIM8_FZ, 0, DBGMCU_TIM8_STOP); break; #endif #ifdef CONFIG_STM32_TIM9 case 9: g_tickless.base = STM32_TIM9_BASE; - modifyreg32(STM32_DBGMCU_TIM9_FZ, 0, DBGMCU_TIM9_STOP); break; #endif #ifdef CONFIG_STM32_TIM10 case 10: g_tickless.base = STM32_TIM10_BASE; - modifyreg32(STM32_DBGMCU_TIM10_FZ, 0, DBGMCU_TIM10_STOP); break; #endif #ifdef CONFIG_STM32_TIM11 case 11: g_tickless.base = STM32_TIM11_BASE; - modifyreg32(STM32_DBGMCU_TIM11_FZ, 0, DBGMCU_TIM11_STOP); break; #endif #ifdef CONFIG_STM32_TIM12 case 12: g_tickless.base = STM32_TIM12_BASE; - modifyreg32(STM32_DBGMCU_TIM12_FZ, 0, DBGMCU_TIM12_STOP); break; #endif #ifdef CONFIG_STM32_TIM13 case 13: g_tickless.base = STM32_TIM13_BASE; - modifyreg32(STM32_DBGMCU_TIM13_FZ, 0, DBGMCU_TIM13_STOP); break; #endif #ifdef CONFIG_STM32_TIM14 case 14: g_tickless.base = STM32_TIM14_BASE; - modifyreg32(STM32_DBGMCU_TIM14_FZ, 0, DBGMCU_TIM14_STOP); break; #endif #ifdef CONFIG_STM32_TIM15 case 15: g_tickless.base = STM32_TIM15_BASE; - /* A freeze bit for TIM15 doesn't seem to exist */ - break; #endif @@ -548,8 +564,6 @@ void up_timer_initialize(void) case 16: g_tickless.base = STM32_TIM16_BASE; - /* A freeze bit for TIM16 doesn't seem to exist */ - break; #endif @@ -557,8 +571,6 @@ void up_timer_initialize(void) case 17: g_tickless.base = STM32_TIM17_BASE; - /* A freeze bit for TIM17 doesn't seem to exist */ - break; #endif @@ -566,6 +578,12 @@ void up_timer_initialize(void) DEBUGPANIC(); } +#ifdef TICKLESS_DBGMCU_FZ + /* Stop the timer when the core is halted by a debugger */ + + modifyreg32(TICKLESS_DBGMCU_FZ, 0, TICKLESS_DBGMCU_STOP); +#endif + /* Get the TC frequency that corresponds to the requested resolution */ g_tickless.frequency = USEC_PER_SEC / (uint32_t)CONFIG_USEC_PER_TICK; @@ -621,6 +639,12 @@ void up_timer_initialize(void) STM32_TIM_ACKINT(g_tickless.tch, ~0); STM32_TIM_ENABLEINT(g_tickless.tch, GTIM_DIER_UIE); + +#if (defined(CONFIG_ARMV7M_SYSTICK) || defined(CONFIG_ARMV8M_SYSTICK)) && \ + defined(CONFIG_CPULOAD_PERIOD) + nxsched_period_extclk(systick_initialize(STM32_SYSTICK_CORECLK, + STM32_SYSTICK_CLOCK, -1)); +#endif } /**************************************************************************** diff --git a/arch/arm/src/common/stm32/stm32_tickless_m3m4_v1.c b/arch/arm/src/common/stm32/stm32_tickless_m3m4_v1.c deleted file mode 100644 index fffce70928c..00000000000 --- a/arch/arm/src/common/stm32/stm32_tickless_m3m4_v1.c +++ /dev/null @@ -1,1017 +0,0 @@ -/**************************************************************************** - * arch/arm/src/common/stm32/stm32_tickless_m3m4_v1.c - * - * SPDX-License-Identifier: BSD-3-Clause - * SPDX-FileCopyrightText: 2016-2017 Gregory Nutt. All rights reserved. - * SPDX-FileCopyrightText: 2017 Ansync Labs. All rights reserved. - * SPDX-FileContributor: Gregory Nutt <[email protected]> - * SPDX-FileContributor: Konstantin Berezenko <[email protected]> - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * - * 1. Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * 2. Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in - * the documentation and/or other materials provided with the - * distribution. - * 3. Neither the name NuttX nor the names of its contributors may be - * used to endorse or promote products derived from this software - * without specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS - * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT - * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS - * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE - * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, - * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, - * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS - * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED - * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT - * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN - * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE - * POSSIBILITY OF SUCH DAMAGE. - * - ****************************************************************************/ - -/**************************************************************************** - * Tickless OS Support. - * - * When CONFIG_SCHED_TICKLESS is enabled, all support for timer interrupts - * is suppressed and the platform specific code is expected to provide the - * following custom functions. - * - * void up_timer_initialize(void): Initializes the timer facilities. - * Called early in the initialization sequence (by up_initialize()). - * int up_timer_gettime(struct timespec *ts): Returns the current - * time from the platform specific time source. - * int up_timer_cancel(void): Cancels the interval timer. - * int up_timer_start(const struct timespec *ts): Start (or re-starts) - * the interval timer. - * - * The RTOS will provide the following interfaces for use by the platform- - * specific interval timer implementation: - * - * void nxsched_process_timer(void): Called by the platform-specific - * logic when the interval timer expires. - * - ****************************************************************************/ - -/**************************************************************************** - * STM32 Timer Usage - * - * This implementation uses one timer: A free running timer to provide - * the current time and a capture/compare channel for timed-events. - * - * BASIC timers that are found on some STM32 chips (timers 6 and 7) are - * incompatible with this implementation because they don't have capture/ - * compare channels. There are two interrupts generated from our timer, - * the overflow interrupt which drives the timing handler and the capture/ - * compare interrupt which drives the interval handler. There are some low - * level timer control functions implemented here because the API of - * stm32_tim.c does not provide adequate control over capture/compare - * interrupts. - * - ****************************************************************************/ - -/**************************************************************************** - * Included Files - ****************************************************************************/ - -#include <nuttx/config.h> - -#include <stdint.h> -#include <stdbool.h> -#include <errno.h> -#include <assert.h> - -#include <nuttx/arch.h> -#include <nuttx/debug.h> - -#include "arm_internal.h" -#include "stm32_tim.h" - -#ifdef CONFIG_SCHED_TICKLESS - -/**************************************************************************** - * Pre-processor Definitions - ****************************************************************************/ - -/* Only TIM2 and TIM5 timers may be 32-bits in width - * - * Reference Table 2 of en.DM00042534.pdf - */ - -#undef HAVE_32BIT_TICKLESS - -#if (CONFIG_STM32_TICKLESS_TIMER == 2 && \ - !defined(STM32_STM32F10XX) && \ - !defined(STM32_STM32L15XX)) \ - || (CONFIG_STM32_TICKLESS_TIMER == 5 && \ - !defined(STM32_STM32F10XX)) - #define HAVE_32BIT_TICKLESS 1 -#endif - -/**************************************************************************** - * Private Types - ****************************************************************************/ - -struct stm32_tickless_s -{ - uint8_t timer; /* The timer/counter in use */ - uint8_t channel; /* The timer channel to use for intervals */ - struct stm32_tim_dev_s *tch; /* Handle returned by stm32_tim_init() */ - uint32_t frequency; - uint32_t overflow; /* Timer counter overflow */ - volatile bool pending; /* True: pending task */ - uint32_t period; /* Interval period */ - uint32_t base; -}; - -/**************************************************************************** - * Private Data - ****************************************************************************/ - -static struct stm32_tickless_s g_tickless; - -/**************************************************************************** - * Private Functions - ****************************************************************************/ - -/**************************************************************************** - * Name: stm32_getreg16 - * - * Description: - * Get a 16-bit register value by offset - * - ****************************************************************************/ - -static inline uint16_t stm32_getreg16(uint8_t offset) -{ - return getreg16(g_tickless.base + offset); -} - -/**************************************************************************** - * Name: stm32_putreg16 - * - * Description: - * Put a 16-bit register value by offset - * - ****************************************************************************/ - -static inline void stm32_putreg16(uint8_t offset, uint16_t value) -{ - putreg16(value, g_tickless.base + offset); -} - -/**************************************************************************** - * Name: stm32_modifyreg16 - * - * Description: - * Modify a 16-bit register value by offset - * - ****************************************************************************/ - -static inline void stm32_modifyreg16(uint8_t offset, uint16_t clearbits, - uint16_t setbits) -{ - modifyreg16(g_tickless.base + offset, clearbits, setbits); -} - -/**************************************************************************** - * Name: stm32_tickless_enableint - ****************************************************************************/ - -static inline void stm32_tickless_enableint(int channel) -{ - stm32_modifyreg16(STM32_BTIM_DIER_OFFSET, 0, 1 << channel); -} - -/**************************************************************************** - * Name: stm32_tickless_disableint - ****************************************************************************/ - -static inline void stm32_tickless_disableint(int channel) -{ - stm32_modifyreg16(STM32_BTIM_DIER_OFFSET, 1 << channel, 0); -} - -/**************************************************************************** - * Name: stm32_tickless_ackint - ****************************************************************************/ - -static inline void stm32_tickless_ackint(int channel) -{ - stm32_putreg16(STM32_BTIM_SR_OFFSET, ~(1 << channel)); -} - -/**************************************************************************** - * Name: stm32_tickless_trigint - ****************************************************************************/ - -static inline void stm32_tickless_trigint(int channel) -{ - stm32_putreg16(STM32_ATIM_EGR_OFFSET, 1 << channel); -} - -/**************************************************************************** - * Name: stm32_tickless_getint - ****************************************************************************/ - -static inline uint16_t stm32_tickless_getint(void) -{ - return stm32_getreg16(STM32_BTIM_SR_OFFSET); -} - -/**************************************************************************** - * Name: stm32_tickless_setchannel - ****************************************************************************/ - -static int stm32_tickless_setchannel(uint8_t channel) -{ - uint16_t ccmr_orig = 0; - uint16_t ccmr_val = 0; - uint16_t ccmr_mask = 0xff; - uint16_t ccer_val = stm32_getreg16(STM32_GTIM_CCER_OFFSET); - uint8_t ccmr_offset = STM32_GTIM_CCMR1_OFFSET; - - /* Further we use range as 0..3; if channel=0 it will also overflow here */ - - if (--channel > 4) - { - return -EINVAL; - } - - /* Assume that channel is disabled and polarity is active high */ - - ccer_val &= ~((GTIM_CCER_CC1P | GTIM_CCER_CC1E) << - GTIM_CCER_CCXBASE(channel)); - - /* This function is not supported on basic timers. To enable or - * disable it, simply set its clock to valid frequency or zero. - */ - -#if STM32_NBTIM > 0 - if (g_tickless.base == STM32_TIM6_BASE -#endif -#if STM32_NBTIM > 1 - || g_tickless.base == STM32_TIM7_BASE -#endif -#if STM32_NBTIM > 0 - ) - { - return -EINVAL; - } -#endif - - /* Frozen mode because we don't want to change the GPIO, preload register - * disabled. - */ - - ccmr_val = (ATIM_CCMR_MODE_FRZN << ATIM_CCMR1_OC1M_SHIFT); - - /* Set polarity */ - - ccer_val |= ATIM_CCER_CC1P << GTIM_CCER_CCXBASE(channel); - - /* Define its position (shift) and get register offset */ - - if ((channel & 1) != 0) - { - ccmr_val <<= 8; - ccmr_mask <<= 8; - } - - if (channel > 1) - { - ccmr_offset = STM32_GTIM_CCMR2_OFFSET; - } - - ccmr_orig = stm32_getreg16(ccmr_offset); - ccmr_orig &= ~ccmr_mask; - ccmr_orig |= ccmr_val; - stm32_putreg16(ccmr_offset, ccmr_orig); - stm32_putreg16(STM32_GTIM_CCER_OFFSET, ccer_val); - - return OK; -} - -/**************************************************************************** - * Name: stm32_interval_handler - * - * Description: - * Called when the timer counter matches the compare register - * - * Input Parameters: - * None - * - * Returned Value: - * None - * - * Assumptions: - * Called early in the initialization sequence before any special - * concurrency protections are required. - * - ****************************************************************************/ - -static void stm32_interval_handler(void) -{ - tmrinfo("Expired...\n"); - - /* Disable the compare interrupt now. */ - - stm32_tickless_disableint(g_tickless.channel); - stm32_tickless_ackint(g_tickless.channel); - - g_tickless.pending = false; - - nxsched_process_timer(); -} - -/**************************************************************************** - * Name: stm32_timing_handler - * - * Description: - * Timer interrupt callback. When the freerun timer counter overflows, - * this interrupt will occur. We will just increment an overflow count. - * - * Input Parameters: - * None - * - * Returned Value: - * None - * - ****************************************************************************/ - -static void stm32_timing_handler(void) -{ - g_tickless.overflow++; - - STM32_TIM_ACKINT(g_tickless.tch, GTIM_SR_UIF); -} - -/**************************************************************************** - * Name: stm32_tickless_handler - * - * Description: - * Generic interrupt handler for this timer. It checks the source of the - * interrupt and fires the appropriate handler. - * - * Input Parameters: - * None - * - * Returned Value: - * None - * - ****************************************************************************/ - -static int stm32_tickless_handler(int irq, void *context, void *arg) -{ - int interrupt_flags = stm32_tickless_getint(); - - if (interrupt_flags & ATIM_SR_UIF) - { - stm32_timing_handler(); - } - - if (interrupt_flags & (1 << g_tickless.channel)) - { - stm32_interval_handler(); - } - - return OK; -} - -/**************************************************************************** - * Public Functions - ****************************************************************************/ - -/**************************************************************************** - * Name: up_timer_initialize - * - * Description: - * Initializes all platform-specific timer facilities. This function is - * called early in the initialization sequence by up_initialize(). - * On return, the current up-time should be available from - * up_timer_gettime() and the interval timer is ready for use (but not - * actively timing. - * - * Provided by platform-specific code and called from the architecture- - * specific logic. - * - * Input Parameters: - * None - * - * Returned Value: - * None - * - * Assumptions: - * Called early in the initialization sequence before any special - * concurrency protections are required. - * - ****************************************************************************/ - -void up_timer_initialize(void) -{ - switch (CONFIG_STM32_TICKLESS_TIMER) - { -#ifdef CONFIG_STM32_TIM1 - case 1: - g_tickless.base = STM32_TIM1_BASE; - break; -#endif - -#ifdef CONFIG_STM32_TIM2 - case 2: - g_tickless.base = STM32_TIM2_BASE; - break; -#endif - -#ifdef CONFIG_STM32_TIM3 - case 3: - g_tickless.base = STM32_TIM3_BASE; - break; -#endif - -#ifdef CONFIG_STM32_TIM4 - case 4: - g_tickless.base = STM32_TIM4_BASE; - break; -#endif -#ifdef CONFIG_STM32_TIM5 - case 5: - g_tickless.base = STM32_TIM5_BASE; - break; -#endif - -#ifdef CONFIG_STM32_TIM6 - case 6: - - /* Basic timers not supported by this implementation */ - - DEBUGPANIC(); - break; -#endif - -#ifdef CONFIG_STM32_TIM7 - case 7: - - /* Basic timers not supported by this implementation */ - - DEBUGPANIC(); - break; -#endif - -#ifdef CONFIG_STM32_TIM8 - case 8: - g_tickless.base = STM32_TIM8_BASE; - break; -#endif - -#ifdef CONFIG_STM32_TIM9 - case 9: - g_tickless.base = STM32_TIM9_BASE; - break; -#endif -#ifdef CONFIG_STM32_TIM10 - case 10: - g_tickless.base = STM32_TIM10_BASE; - break; -#endif - -#ifdef CONFIG_STM32_TIM11 - case 11: - g_tickless.base = STM32_TIM11_BASE; - break; -#endif -#ifdef CONFIG_STM32_TIM12 - case 12: - g_tickless.base = STM32_TIM12_BASE; - break; -#endif -#ifdef CONFIG_STM32_TIM13 - case 13: - g_tickless.base = STM32_TIM13_BASE; - break; -#endif - -#ifdef CONFIG_STM32_TIM14 - case 14: - g_tickless.base = STM32_TIM14_BASE; - break; -#endif -#ifdef CONFIG_STM32_TIM15 - case 15: - g_tickless.base = STM32_TIM15_BASE; - break; -#endif - -#ifdef CONFIG_STM32_TIM16 - case 16: - g_tickless.base = STM32_TIM16_BASE; - break; -#endif - -#ifdef CONFIG_STM32_TIM17 - case 17: - g_tickless.base = STM32_TIM17_BASE; - break; -#endif - - default: - DEBUGPANIC(); - } - - /* Get the TC frequency that corresponds to the requested resolution */ - - g_tickless.frequency = USEC_PER_SEC / (uint32_t)CONFIG_USEC_PER_TICK; - g_tickless.timer = CONFIG_STM32_TICKLESS_TIMER; - g_tickless.channel = CONFIG_STM32_TICKLESS_CHANNEL; - g_tickless.pending = false; - g_tickless.period = 0; - g_tickless.overflow = 0; - - tmrinfo("timer=%d channel=%d frequency=%lu Hz\n", - g_tickless.timer, g_tickless.channel, g_tickless.frequency); - - g_tickless.tch = stm32_tim_init(g_tickless.timer); - if (!g_tickless.tch) - { - tmrerr("ERROR: Failed to allocate TIM%d\n", g_tickless.timer); - DEBUGPANIC(); - } - - STM32_TIM_SETCLOCK(g_tickless.tch, g_tickless.frequency); - - /* Set up to receive the callback when the counter overflow occurs */ - - STM32_TIM_SETISR(g_tickless.tch, stm32_tickless_handler, NULL, 0); - - /* Initialize interval to zero */ - - STM32_TIM_SETCOMPARE(g_tickless.tch, g_tickless.channel, 0); - - /* Setup compare channel for the interval timing */ - - stm32_tickless_setchannel(g_tickless.channel); - - /* Set timer period */ - -#ifdef HAVE_32BIT_TICKLESS - STM32_TIM_SETPERIOD(g_tickless.tch, UINT32_MAX); -#ifdef CONFIG_SCHED_TICKLESS_LIMIT_MAX_SLEEP - g_oneshot_maxticks = UINT32_MAX; -#endif -#else - STM32_TIM_SETPERIOD(g_tickless.tch, UINT16_MAX); -#ifdef CONFIG_SCHED_TICKLESS_LIMIT_MAX_SLEEP - g_oneshot_maxticks = UINT16_MAX; -#endif -#endif - - /* Initialize the counter */ - - STM32_TIM_SETMODE(g_tickless.tch, STM32_TIM_MODE_UP); - - /* Start the timer */ - - STM32_TIM_ACKINT(g_tickless.tch, ~0); - STM32_TIM_ENABLEINT(g_tickless.tch, GTIM_DIER_UIE); -} - -/**************************************************************************** - * Name: up_timer_gettime - * - * Description: - * Return the elapsed time since power-up (or, more correctly, since - * up_timer_initialize() was called). This function is functionally - * equivalent to: - * - * int clock_gettime(clockid_t clockid, struct timespec *ts); - * - * when clockid is CLOCK_MONOTONIC. - * - * This function provides the basis for reporting the current time and - * also is used to eliminate error build-up from small errors in interval - * time calculations. - * - * Provided by platform-specific code and called from the RTOS base code. - * - * Input Parameters: - * ts - Provides the location in which to return the up-time. - * - * Returned Value: - * Zero (OK) is returned on success; a negated errno value is returned on - * any failure. - * - * Assumptions: - * Called from the normal tasking context. The implementation must - * provide whatever mutual exclusion is necessary for correct operation. - * This can include disabling interrupts in order to assure atomic register - * operations. - * - ****************************************************************************/ - -int up_timer_gettime(struct timespec *ts) -{ - uint64_t usec; - uint32_t counter; - uint32_t verify; - uint32_t overflow; - uint32_t sec; - int pending; - irqstate_t flags; - - DEBUGASSERT(ts); - - /* Timer not initialized yet, return zero */ - - if (g_tickless.tch == 0) - { - ts->tv_nsec = 0; - ts->tv_sec = 0; - return OK; - } - - /* Temporarily disable the overflow counter. NOTE that we have to be - * careful here because stm32_tc_getpending() will reset the pending - * interrupt status. If we do not handle the overflow here then, it will - * be lost. - */ - - flags = enter_critical_section(); - - overflow = g_tickless.overflow; - counter = STM32_TIM_GETCOUNTER(g_tickless.tch); - pending = STM32_TIM_CHECKINT(g_tickless.tch, GTIM_SR_UIF); - verify = STM32_TIM_GETCOUNTER(g_tickless.tch); - - /* If an interrupt was pending before we re-enabled interrupts, - * then the overflow needs to be incremented. - */ - - if (pending) - { - STM32_TIM_ACKINT(g_tickless.tch, GTIM_SR_UIF); - - /* Increment the overflow count and use the value of the - * guaranteed to be AFTER the overflow occurred. - */ - - overflow++; - counter = verify; - - /* Update tickless overflow counter. */ - - g_tickless.overflow = overflow; - } - - leave_critical_section(flags); - - tmrinfo("counter=%lu (%lu) overflow=%lu, pending=%i\n", - (unsigned long)counter, (unsigned long)verify, - (unsigned long)overflow, pending); - tmrinfo("frequency=%lu\n", g_tickless.frequency); - - /* Convert the whole thing to units of microseconds. - * - * frequency = ticks / second - * seconds = ticks * frequency - * usecs = (ticks * USEC_PER_SEC) / frequency; - */ -#ifdef HAVE_32BIT_TICKLESS - usec = ((((uint64_t)overflow << 32) + (uint64_t)counter) * USEC_PER_SEC) / - g_tickless.frequency; -#else - usec = ((((uint64_t)overflow << 16) + (uint64_t)counter) * USEC_PER_SEC) / - g_tickless.frequency; -#endif - - /* And return the value of the timer */ - - sec = (uint32_t)(usec / USEC_PER_SEC); - ts->tv_sec = sec; - ts->tv_nsec = (usec - (sec * USEC_PER_SEC)) * NSEC_PER_USEC; - - tmrinfo("usec=%llu ts=(%jd, %ld)\n", - usec, (intmax_t)ts->tv_sec, ts->tv_nsec); - - return OK; -} - -#ifdef CONFIG_CLOCK_TIMEKEEPING - -/**************************************************************************** - * Name: up_timer_gettick - * - * Description: - * To be provided - * - * Input Parameters: - * cycles - 64-bit return value - * - * Returned Value: - * None - * - ****************************************************************************/ - -int up_timer_gettick(clock_t *ticks) -{ - *ticks = STM32_TIM_GETCOUNTER(g_tickless.tch); - return OK; -} - -/**************************************************************************** - * Name: up_timer_getmask - * - * Description: - * To be provided - * - * Input Parameters: - * mask - Location to return the 64-bit mask - * - * Returned Value: - * None - * - ****************************************************************************/ - -void up_timer_getmask(clock_t *mask) -{ - DEBUGASSERT(mask != NULL); -#ifdef HAVE_32BIT_TICKLESS - *mask = UINT32_MAX; -#else - *mask = UINT16_MAX; -#endif -} - -#endif /* CONFIG_CLOCK_TIMEKEEPING */ - -/**************************************************************************** - * Name: up_timer_cancel - * - * Description: - * Cancel the interval timer and return the time remaining on the timer. - * These two steps need to be as nearly atomic as possible. - * nxsched_process_timer() will not be called unless the timer is - * restarted with up_timer_start(). - * - * If, as a race condition, the timer has already expired when this - * function is called, then that pending interrupt must be cleared so - * that up_timer_start() and the remaining time of zero should be - * returned. - * - * NOTE: This function may execute at a high rate with no timer running (as - * when pre-emption is enabled and disabled). - * - * Provided by platform-specific code and called from the RTOS base code. - * - * Input Parameters: - * ts - Location to return the remaining time. Zero should be returned - * if the timer is not active. 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. - * - * Assumptions: - * May be called from interrupt level handling or from the normal tasking - * level. Interrupts may need to be disabled internally to assure - * non-reentrancy. - * - ****************************************************************************/ - -int up_timer_cancel(struct timespec *ts) -{ - irqstate_t flags; - uint64_t usec; - uint64_t sec; - uint64_t nsec; - uint32_t count; - uint32_t period; - - /* Was the timer running? */ - - flags = enter_critical_section(); - if (!g_tickless.pending) - { - /* No.. Just return zero timer remaining and successful cancellation. - * This function may execute at a high rate with no timer running - * (as when pre-emption is enabled and disabled). - */ - - if (ts) - { - ts->tv_sec = 0; - ts->tv_nsec = 0; - } - - leave_critical_section(flags); - return OK; - } - - /* Yes.. Get the timer counter and period registers and disable the compare - * interrupt. - */ - - tmrinfo("Cancelling...\n"); - - /* Disable the interrupt. */ - - stm32_tickless_disableint(g_tickless.channel); - - count = STM32_TIM_GETCOUNTER(g_tickless.tch); - period = g_tickless.period; - - g_tickless.pending = false; - leave_critical_section(flags); - - /* Did the caller provide us with a location to return the time - * remaining? - */ - - if (ts != NULL) - { - /* Yes.. then calculate and return the time remaining on the - * oneshot timer. - */ - - tmrinfo("period=%lu count=%lu\n", - (unsigned long)period, (unsigned long)count); - -#ifndef HAVE_32BIT_TICKLESS - if ((int16_t)(period - count) <= 0) -#else - if ((int32_t)(period - count) <= 0) -#endif - { - /* No time remaining */ - - ts->tv_sec = 0; - ts->tv_nsec = 0; - return OK; - } - - /* The total time remaining is the difference. Convert that - * to units of microseconds. - * - * frequency = ticks / second - * seconds = ticks * frequency - * usecs = (ticks * USEC_PER_SEC) / frequency; - */ - -#ifndef HAVE_32BIT_TICKLESS - usec = (((uint64_t)(uint16_t)(period - count)) * USEC_PER_SEC) / - g_tickless.frequency; -#else - usec = (((uint64_t)(uint32_t)(period - count)) * USEC_PER_SEC) / - g_tickless.frequency; -#endif - - /* Return the time remaining in the correct form */ - - sec = usec / USEC_PER_SEC; - nsec = ((usec) - (sec * USEC_PER_SEC)) * NSEC_PER_USEC; - - ts->tv_sec = sec; - ts->tv_nsec = nsec; - - tmrinfo("remaining (%jd, %ld)\n", - (intmax_t)ts->tv_sec, ts->tv_nsec); - } - - return OK; -} - -/**************************************************************************** - * Name: up_timer_start - * - * Description: - * Start the interval timer. nxsched_process_timer() will be - * called at the completion of the timeout (unless up_timer_cancel - * is called to stop the timing. - * - * Provided by platform-specific code and called from the RTOS base code. - * - * Input Parameters: - * ts - Provides the time interval until nxsched_process_timer() is - * called. - * - * Returned Value: - * Zero (OK) is returned on success; a negated errno value is returned on - * any failure. - * - * Assumptions: - * May be called from interrupt level handling or from the normal tasking - * level. Interrupts may need to be disabled internally to assure - * non-reentrancy. - * - ****************************************************************************/ - -int up_timer_start(const struct timespec *ts) -{ - uint64_t usec; - uint64_t period; - uint32_t count; - irqstate_t flags; - - tmrinfo("ts=(%jd, %ld)\n", - (intmax_t)ts->tv_sec, ts->tv_nsec); - DEBUGASSERT(ts); - DEBUGASSERT(g_tickless.tch); - - /* Was an interval already running? */ - - flags = enter_critical_section(); - if (g_tickless.pending) - { - /* Yes.. then cancel it */ - - tmrinfo("Already running... cancelling\n"); - up_timer_cancel(NULL); - } - - /* Express the delay in microseconds */ - - if (ts->tv_sec < 0 || (ts->tv_sec == 0 && ts->tv_nsec <= 0)) - { - period = 0; - } - else - { - usec = ts->tv_sec * USEC_PER_SEC + - (ts->tv_nsec / NSEC_PER_USEC); - - /* Get the timer counter frequency and determine the number of counts - * need to achieve the requested delay. - * - * frequency = ticks / second - * ticks = seconds * frequency - * = (usecs * frequency) / USEC_PER_SEC; - */ - - period = (usec * (uint64_t)g_tickless.frequency) / USEC_PER_SEC; - } - - if (period == 0) - { - stm32_tickless_enableint(g_tickless.channel); - stm32_tickless_trigint(g_tickless.channel); - g_tickless.pending = true; - leave_critical_section(flags); - return OK; - } - - count = STM32_TIM_GETCOUNTER(g_tickless.tch); - - tmrinfo("usec=%llu period=%08llx\n", usec, period); - - /* Set interval compare value. Rollover is fine, - * channel will trigger on the next period. - */ - -#ifdef HAVE_32BIT_TICKLESS - DEBUGASSERT(period <= UINT32_MAX); - g_tickless.period = (uint32_t)(period + count); -#else - DEBUGASSERT(period <= UINT16_MAX); - g_tickless.period = (uint16_t)(period + count); -#endif - - STM32_TIM_SETCOMPARE(g_tickless.tch, g_tickless.channel, - g_tickless.period); - - /* Enable interrupts. We should get the callback when the interrupt - * occurs. - */ - - stm32_tickless_ackint(g_tickless.channel); - stm32_tickless_enableint(g_tickless.channel); - - g_tickless.pending = true; - - /* Check if the counter already reached or passed the compare target - * while we were configuring the registers. - */ - -#ifdef HAVE_32BIT_TICKLESS - if ((uint32_t)(STM32_TIM_GETCOUNTER(g_tickless.tch) - count) >= - (uint32_t)period) -#else - if ((uint16_t)(STM32_TIM_GETCOUNTER(g_tickless.tch) - (uint16_t)count) >= - (uint16_t)period) -#endif - { - /* Target time already elapsed; force the interrupt immediately */ - - stm32_tickless_trigint(g_tickless.channel); - } - - leave_critical_section(flags); - return OK; -} -#endif /* CONFIG_SCHED_TICKLESS */ diff --git a/arch/arm/src/stm32f7/CMakeLists.txt b/arch/arm/src/stm32f7/CMakeLists.txt index 2d0798008fc..83fc0ff178b 100644 --- a/arch/arm/src/stm32f7/CMakeLists.txt +++ b/arch/arm/src/stm32f7/CMakeLists.txt @@ -35,10 +35,6 @@ list( stm32_start.c stm32_capture.c) -if(CONFIG_SCHED_TICKLESS) - list(APPEND SRCS stm32_tickless.c) -endif() - if(CONFIG_STM32_PROGMEM) list(APPEND SRCS stm32_flash.c) endif() diff --git a/arch/arm/src/stm32f7/Make.defs b/arch/arm/src/stm32f7/Make.defs index bbfc6526cd3..fa4e427836f 100644 --- a/arch/arm/src/stm32f7/Make.defs +++ b/arch/arm/src/stm32f7/Make.defs @@ -34,10 +34,6 @@ CHIP_CSRCS += stm32_allocateheap.c stm32_exti_gpio.c stm32_gpio.c CHIP_CSRCS += stm32_irq.c stm32_lowputc.c stm32_rcc.c stm32_serial.c CHIP_CSRCS += stm32_start.c stm32_capture.c -ifeq ($(CONFIG_SCHED_TICKLESS),y) -CHIP_CSRCS += stm32_tickless.c -endif - ifeq ($(CONFIG_STM32_PROGMEM),y) CHIP_CSRCS += stm32_flash.c endif diff --git a/arch/arm/src/stm32h7/CMakeLists.txt b/arch/arm/src/stm32h7/CMakeLists.txt index 3bd84e3bbc8..0243ccaa07e 100644 --- a/arch/arm/src/stm32h7/CMakeLists.txt +++ b/arch/arm/src/stm32h7/CMakeLists.txt @@ -50,10 +50,6 @@ if(CONFIG_RPTUN) list(APPEND SRCS stm32_rptun.c) endif() -if(CONFIG_SCHED_TICKLESS) - list(APPEND SRCS stm32_tickless.c) -endif() - if(CONFIG_ARM_MPU) list(APPEND SRCS stm32_mpuinit.c) endif() diff --git a/arch/arm/src/stm32h7/Make.defs b/arch/arm/src/stm32h7/Make.defs index 37a65e6d9a3..5fbbdb83880 100644 --- a/arch/arm/src/stm32h7/Make.defs +++ b/arch/arm/src/stm32h7/Make.defs @@ -49,10 +49,6 @@ endif CHIP_CSRCS += stm32_allocateheap.c stm32_exti_gpio.c stm32_gpio.c stm32_irq.c CHIP_CSRCS += stm32_start.c stm32_rcc.c stm32_lowputc.c stm32_serial.c -ifeq ($(CONFIG_SCHED_TICKLESS),y) -CHIP_CSRCS += stm32_tickless.c -endif - ifeq ($(CONFIG_ARM_MPU),y) CHIP_CSRCS += stm32_mpuinit.c endif diff --git a/arch/arm/src/stm32h7/stm32_tickless.c b/arch/arm/src/stm32h7/stm32_tickless.c deleted file mode 100644 index 2fe5e472e5f..00000000000 --- a/arch/arm/src/stm32h7/stm32_tickless.c +++ /dev/null @@ -1,1081 +0,0 @@ -/**************************************************************************** - * arch/arm/src/stm32h7/stm32_tickless.c - * - * SPDX-License-Identifier: Apache-2.0 - * - * Licensed to the Apache Software Foundation (ASF) under one or more - * contributor license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright ownership. The - * ASF licenses this file to you under the Apache License, Version 2.0 (the - * "License"); you may not use this file except in compliance with the - * License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT - * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the - * License for the specific language governing permissions and limitations - * under the License. - * - ****************************************************************************/ - -/**************************************************************************** - * Tickless OS Support. - * - * When CONFIG_SCHED_TICKLESS is enabled, all support for timer interrupts - * is suppressed and the platform specific code is expected to provide the - * following custom functions. - * - * void up_timer_initialize(void): Initializes the timer facilities. - * Called early in the initialization sequence (by up_initialize()). - * int up_timer_gettime(struct timespec *ts): Returns the current - * time from the platform specific time source. - * int up_timer_cancel(void): Cancels the interval timer. - * int up_timer_start(const struct timespec *ts): Start (or re-starts) - * the interval timer. - * - * The RTOS will provide the following interfaces for use by the platform- - * specific interval timer implementation: - * - * void nxsched_process_timer(void): Called by the platform-specific - * logic when the interval timer expires. - * - ****************************************************************************/ - -/**************************************************************************** - * STM32 Timer Usage - * - * This implementation uses one timer: A free running timer to provide - * the current time and a capture/compare channel for timed-events. - * - * BASIC timers that are found on some STM32 chips (timers 6 and 7) are - * incompatible with this implementation because they don't have capture/ - * compare channels. There are two interrupts generated from our timer, - * the overflow interrupt which drives the timing handler and the capture/ - * compare interrupt which drives the interval handler. There are some low - * level timer control functions implemented here because the API of - * stm32_tim.c does not provide adequate control over capture/compare - * interrupts. - * - ****************************************************************************/ - -/**************************************************************************** - * Included Files - ****************************************************************************/ - -#include <nuttx/config.h> - -#include <stdint.h> -#include <stdbool.h> -#include <errno.h> -#include <assert.h> - -#include <nuttx/arch.h> -#include <nuttx/timers/arch_timer.h> -#include <nuttx/debug.h> -#include <arch/board/board.h> - -#include "arm_internal.h" - -#include "stm32_tim.h" -#include "stm32_dbgmcu.h" - -#include "systick.h" - -#ifdef CONFIG_SCHED_TICKLESS - -/* Only TIM2 and TIM5 timers may be 32-bits in width */ - -#undef HAVE_32BIT_TICKLESS - -#if (CONFIG_STM32_TICKLESS_TIMER == 2) || \ - (CONFIG_STM32_TICKLESS_TIMER == 5) - #define HAVE_32BIT_TICKLESS 1 -#endif - -#if (CONFIG_STM32_TICKLESS_TIMER == 6) || \ - (CONFIG_STM32_TICKLESS_TIMER == 7) -# error Basic timers not supported by the tickless driver -#endif - -#if CONFIG_STM32_TICKLESS_CHANNEL == 1 -#define DIER_CAPT_IE GTIM_DIER_CC1IE -#elif CONFIG_STM32_TICKLESS_CHANNEL == 2 -#define DIER_CAPT_IE GTIM_DIER_CC2IE -#elif CONFIG_STM32_TICKLESS_CHANNEL == 3 -#define DIER_CAPT_IE GTIM_DIER_CC3IE -#elif CONFIG_STM32_TICKLESS_CHANNEL == 4 -#define DIER_CAPT_IE GTIM_DIER_CC4IE -#endif - -/**************************************************************************** - * Private Types - ****************************************************************************/ - -struct stm32_tickless_s -{ - uint8_t timer; /* The timer/counter in use */ - uint8_t channel; /* The timer channel to use for intervals */ - struct stm32_tim_dev_s *tch; /* Handle returned by stm32_tim_init() */ - uint32_t frequency; - uint32_t overflow; /* Timer counter overflow */ - volatile bool pending; /* True: pending task */ - uint32_t period; /* Interval period */ - uint32_t base; -#ifdef CONFIG_SCHED_TICKLESS_ALARM - uint64_t last_alrm; -#endif -}; - -/**************************************************************************** - * Private Data - ****************************************************************************/ - -static struct stm32_tickless_s g_tickless; - -/**************************************************************************** - * Private Functions - ****************************************************************************/ - -/**************************************************************************** - * Name: stm32_getreg16 - * - * Description: - * Get a 16-bit register value by offset - * - ****************************************************************************/ - -static inline uint16_t stm32_getreg16(uint8_t offset) -{ - return getreg16(g_tickless.base + offset); -} - -/**************************************************************************** - * Name: stm32_putreg16 - * - * Description: - * Put a 16-bit register value by offset - * - ****************************************************************************/ - -static inline void stm32_putreg16(uint8_t offset, uint16_t value) -{ - putreg16(value, g_tickless.base + offset); -} - -/**************************************************************************** - * Name: stm32_modifyreg16 - * - * Description: - * Modify a 16-bit register value by offset - * - ****************************************************************************/ - -static inline void stm32_modifyreg16(uint8_t offset, uint16_t clearbits, - uint16_t setbits) -{ - modifyreg16(g_tickless.base + offset, clearbits, setbits); -} - -/**************************************************************************** - * Name: stm32_tickless_enableint - ****************************************************************************/ - -static inline void stm32_tickless_enableint(int channel) -{ - stm32_modifyreg16(STM32_BTIM_DIER_OFFSET, 0, 1 << channel); -} - -/**************************************************************************** - * Name: stm32_tickless_disableint - ****************************************************************************/ - -static inline void stm32_tickless_disableint(int channel) -{ - stm32_modifyreg16(STM32_BTIM_DIER_OFFSET, 1 << channel, 0); -} - -/**************************************************************************** - * Name: stm32_tickless_ackint - ****************************************************************************/ - -static inline void stm32_tickless_ackint(int channel) -{ - stm32_putreg16(STM32_BTIM_SR_OFFSET, ~(1 << channel)); -} - -/**************************************************************************** - * Name: stm32_tickless_trigint - ****************************************************************************/ - -static inline void stm32_tickless_trigint(int channel) -{ - stm32_putreg16(STM32_GTIM_EGR_OFFSET, 1 << channel); -} - -/**************************************************************************** - * Name: stm32_tickless_getint - ****************************************************************************/ - -static inline uint16_t stm32_tickless_getint(void) -{ - return stm32_getreg16(STM32_BTIM_SR_OFFSET); -} - -/**************************************************************************** - * Name: stm32_tickless_setchannel - ****************************************************************************/ - -static int stm32_tickless_setchannel(uint8_t channel) -{ - uint16_t ccmr_orig = 0; - uint16_t ccmr_val = 0; - uint16_t ccmr_mask = 0xff; - uint16_t ccer_val = stm32_getreg16(STM32_GTIM_CCER_OFFSET); - uint8_t ccmr_offset = STM32_GTIM_CCMR1_OFFSET; - - /* Further we use range as 0..3; if channel=0 it will also overflow here */ - - if (--channel > 4) - { - return -EINVAL; - } - - /* Assume that channel is disabled and polarity is active high */ - - ccer_val &= ~((GTIM_CCER_CC1P | GTIM_CCER_CC1E) << - GTIM_CCER_CCXBASE(channel)); - - /* This function is not supported on basic timers. To enable or - * disable it, simply set its clock to valid frequency or zero. - */ - - if (g_tickless.base == STM32_TIM6_BASE || - g_tickless.base == STM32_TIM7_BASE) - { - return -EINVAL; - } - - /* Frozen mode because we don't want to change the GPIO, preload register - * disabled. - */ - - ccmr_val = (GTIM_CCMR_MODE_FRZN << GTIM_CCMR1_OC1M_SHIFT); - - /* Set polarity */ - - ccer_val |= GTIM_CCER_CC1P << GTIM_CCER_CCXBASE(channel); - - /* Define its position (shift) and get register offset */ - - if ((channel & 1) != 0) - { - ccmr_val <<= 8; - ccmr_mask <<= 8; - } - - if (channel > 1) - { - ccmr_offset = STM32_GTIM_CCMR2_OFFSET; - } - - ccmr_orig = stm32_getreg16(ccmr_offset); - ccmr_orig &= ~ccmr_mask; - ccmr_orig |= ccmr_val; - stm32_putreg16(ccmr_offset, ccmr_orig); - stm32_putreg16(STM32_GTIM_CCER_OFFSET, ccer_val); - - return OK; -} - -/**************************************************************************** - * Name: stm32_interval_handler - * - * Description: - * Called when the timer counter matches the compare register - * - * Input Parameters: - * None - * - * Returned Value: - * None - * - * Assumptions: - * Called early in the initialization sequence before any special - * concurrency protections are required. - * - ****************************************************************************/ - -static void stm32_interval_handler(void) -{ - tmrinfo("Expired...\n"); - - /* Disable the compare interrupt now. */ - - stm32_tickless_disableint(g_tickless.channel); - stm32_tickless_ackint(g_tickless.channel); - - g_tickless.pending = false; - - nxsched_process_timer(); -} - -/**************************************************************************** - * Name: stm32_timing_handler - * - * Description: - * Timer interrupt callback. When the freerun timer counter overflows, - * this interrupt will occur. We will just increment an overflow count. - * - * Input Parameters: - * None - * - * Returned Value: - * None - * - ****************************************************************************/ - -static void stm32_timing_handler(void) -{ - g_tickless.overflow++; - - STM32_TIM_ACKINT(g_tickless.tch, GTIM_SR_UIF); -} - -/**************************************************************************** - * Name: stm32_tickless_handler - * - * Description: - * Generic interrupt handler for this timer. It checks the source of the - * interrupt and fires the appropriate handler. - * - * Input Parameters: - * None - * - * Returned Value: - * None - * - ****************************************************************************/ - -static int stm32_tickless_handler(int irq, void *context, void *arg) -{ - int interrupt_flags = stm32_tickless_getint(); - - if (interrupt_flags & GTIM_SR_UIF) - { - stm32_timing_handler(); - } - - if (interrupt_flags & (1 << g_tickless.channel)) - { - stm32_interval_handler(); - } - - return OK; -} - -#ifdef CONFIG_SCHED_TICKLESS_ALARM -/**************************************************************************** - * Name: stm32_get_counter - ****************************************************************************/ - -static uint64_t stm32_get_counter(void) -{ -#ifdef HAVE_32BIT_TICKLESS - return ((uint64_t)g_tickless.overflow << 32) | - STM32_TIM_GETCOUNTER(g_tickless.tch); -#else - return ((uint64_t)g_tickless.overflow << 16) | - STM32_TIM_GETCOUNTER(g_tickless.tch); -#endif -} -#endif - -/**************************************************************************** - * Public Functions - ****************************************************************************/ - -/**************************************************************************** - * Name: up_timer_initialize - * - * Description: - * Initializes all platform-specific timer facilities. This function is - * called early in the initialization sequence by up_initialize(). - * On return, the current up-time should be available from - * up_timer_gettime() and the interval timer is ready for use (but not - * actively timing. - * - * Provided by platform-specific code and called from the architecture- - * specific logic. - * - * Input Parameters: - * None - * - * Returned Value: - * None - * - * Assumptions: - * Called early in the initialization sequence before any special - * concurrency protections are required. - * - ****************************************************************************/ - -void up_timer_initialize(void) -{ - switch (CONFIG_STM32_TICKLESS_TIMER) - { -#ifdef CONFIG_STM32_TIM1 - case 1: - g_tickless.base = STM32_TIM1_BASE; - modifyreg32(STM32_DBGMCU_TIM1_FZ, 0, DBGMCU_TIM1_STOP); - break; -#endif - -#ifdef CONFIG_STM32_TIM2 - case 2: - g_tickless.base = STM32_TIM2_BASE; - modifyreg32(STM32_DBGMCU_TIM2_FZ, 0, DBGMCU_TIM2_STOP); - break; -#endif - -#ifdef CONFIG_STM32_TIM3 - case 3: - g_tickless.base = STM32_TIM3_BASE; - modifyreg32(STM32_DBGMCU_TIM3_FZ, 0, DBGMCU_TIM3_STOP); - break; -#endif - -#ifdef CONFIG_STM32_TIM4 - case 4: - g_tickless.base = STM32_TIM4_BASE; - modifyreg32(STM32_DBGMCU_TIM4_FZ, 0, DBGMCU_TIM4_STOP); - break; -#endif -#ifdef CONFIG_STM32_TIM5 - case 5: - g_tickless.base = STM32_TIM5_BASE; - modifyreg32(STM32_DBGMCU_TIM5_FZ, 0, DBGMCU_TIM5_STOP); - break; -#endif - -#ifdef CONFIG_STM32_TIM8 - case 8: - g_tickless.base = STM32_TIM8_BASE; - modifyreg32(STM32_DBGMCU_TIM8_FZ, 0, DBGMCU_TIM8_STOP); - break; -#endif - -#ifdef CONFIG_STM32_TIM9 - case 9: - g_tickless.base = STM32_TIM9_BASE; - - /* A freeze bit for TIM9 doesn't seem to exist */ - - break; -#endif - - /* Note: TIM10 and TIM11 do not exist on STM32H7 */ - -#ifdef CONFIG_STM32_TIM12 - case 12: - g_tickless.base = STM32_TIM12_BASE; - modifyreg32(STM32_DBGMCU_TIM12_FZ, 0, DBGMCU_TIM12_STOP); - break; -#endif -#ifdef CONFIG_STM32_TIM13 - case 13: - g_tickless.base = STM32_TIM13_BASE; - modifyreg32(STM32_DBGMCU_TIM13_FZ, 0, DBGMCU_TIM13_STOP); - break; -#endif - -#ifdef CONFIG_STM32_TIM14 - case 14: - g_tickless.base = STM32_TIM14_BASE; - modifyreg32(STM32_DBGMCU_TIM14_FZ, 0, DBGMCU_TIM14_STOP); - break; -#endif -#ifdef CONFIG_STM32_TIM15 - case 15: - g_tickless.base = STM32_TIM15_BASE; - modifyreg32(STM32_DBGMCU_TIM15_FZ, 0, DBGMCU_TIM15_STOP); - break; -#endif - -#ifdef CONFIG_STM32_TIM16 - case 16: - g_tickless.base = STM32_TIM16_BASE; - modifyreg32(STM32_DBGMCU_TIM16_FZ, 0, DBGMCU_TIM16_STOP); - break; -#endif - -#ifdef CONFIG_STM32_TIM17 - case 17: - g_tickless.base = STM32_TIM17_BASE; - modifyreg32(STM32_DBGMCU_TIM17_FZ, 0, DBGMCU_TIM17_STOP); - break; -#endif - - default: - DEBUGPANIC(); - } - - /* Get the TC frequency that corresponds to the requested resolution */ - - g_tickless.frequency = USEC_PER_SEC / (uint32_t)CONFIG_USEC_PER_TICK; - g_tickless.timer = CONFIG_STM32_TICKLESS_TIMER; - g_tickless.channel = CONFIG_STM32_TICKLESS_CHANNEL; - g_tickless.pending = false; - g_tickless.period = 0; - g_tickless.overflow = 0; - - tmrinfo("timer=%d channel=%d frequency=%lu Hz\n", - g_tickless.timer, g_tickless.channel, g_tickless.frequency); - - g_tickless.tch = stm32_tim_init(g_tickless.timer); - if (g_tickless.tch == NULL) - { - tmrerr("ERROR: Failed to allocate TIM%d\n", g_tickless.timer); - DEBUGPANIC(); - } - - STM32_TIM_SETCLOCK(g_tickless.tch, g_tickless.frequency); - - /* Set up to receive the callback when the counter overflow occurs */ - - STM32_TIM_SETISR(g_tickless.tch, stm32_tickless_handler, NULL, 0); - - /* Initialize interval to zero */ - - STM32_TIM_SETCOMPARE(g_tickless.tch, g_tickless.channel, 0); - - /* Setup compare channel for the interval timing */ - - stm32_tickless_setchannel(g_tickless.channel); - - /* Set timer period */ - -#ifdef HAVE_32BIT_TICKLESS - STM32_TIM_SETPERIOD(g_tickless.tch, UINT32_MAX); -#ifdef CONFIG_SCHED_TICKLESS_LIMIT_MAX_SLEEP - g_oneshot_maxticks = UINT32_MAX; -#endif -#else - STM32_TIM_SETPERIOD(g_tickless.tch, UINT16_MAX); -#ifdef CONFIG_SCHED_TICKLESS_LIMIT_MAX_SLEEP - g_oneshot_maxticks = UINT16_MAX; -#endif -#endif - - /* Initialize the counter */ - - STM32_TIM_SETMODE(g_tickless.tch, STM32_TIM_MODE_UP); - - /* Start the timer */ - - STM32_TIM_ACKINT(g_tickless.tch, ~0); - STM32_TIM_ENABLEINT(g_tickless.tch, GTIM_DIER_UIE); - -#if defined(CONFIG_ARMV7M_SYSTICK) && defined(CONFIG_CPULOAD_PERIOD) - nxsched_period_extclk(systick_initialize(true, - STM32_CPUCLK_FREQUENCY, -1)); -#endif -} - -/**************************************************************************** - * Name: up_timer_gettime - * - * Description: - * Return the elapsed time since power-up (or, more correctly, since - * up_timer_initialize() was called). This function is functionally - * equivalent to: - * - * int clock_gettime(clockid_t clockid, struct timespec *ts); - * - * when clockid is CLOCK_MONOTONIC. - * - * This function provides the basis for reporting the current time and - * also is used to eliminate error build-up from small errors in interval - * time calculations. - * - * Provided by platform-specific code and called from the RTOS base code. - * - * Input Parameters: - * ts - Provides the location in which to return the up-time. - * - * Returned Value: - * Zero (OK) is returned on success; a negated errno value is returned on - * any failure. - * - * Assumptions: - * Called from the normal tasking context. The implementation must - * provide whatever mutual exclusion is necessary for correct operation. - * This can include disabling interrupts in order to assure atomic register - * operations. - * - ****************************************************************************/ - -int up_timer_gettime(struct timespec *ts) -{ - uint64_t usec; - uint32_t counter; - uint32_t verify; - uint32_t overflow; - uint32_t sec; - int pending; - irqstate_t flags; - - DEBUGASSERT(ts); - - /* Timer not initialized yet, return zero */ - - if (g_tickless.tch == NULL) - { - ts->tv_nsec = 0; - ts->tv_sec = 0; - return OK; - } - - /* Temporarily disable the overflow counter. NOTE that we have to be - * careful here because stm32_tc_getpending() will reset the pending - * interrupt status. If we do not handle the overflow here then, it will - * be lost. - */ - - flags = enter_critical_section(); - - overflow = g_tickless.overflow; - counter = STM32_TIM_GETCOUNTER(g_tickless.tch); - pending = STM32_TIM_CHECKINT(g_tickless.tch, GTIM_SR_UIF); - verify = STM32_TIM_GETCOUNTER(g_tickless.tch); - - /* If an interrupt was pending before we re-enabled interrupts, - * then the overflow needs to be incremented. - */ - - if (pending) - { - STM32_TIM_ACKINT(g_tickless.tch, GTIM_SR_UIF); - - /* Increment the overflow count and use the value of the - * guaranteed to be AFTER the overflow occurred. - */ - - overflow++; - counter = verify; - - /* Update tickless overflow counter. */ - - g_tickless.overflow = overflow; - } - - leave_critical_section(flags); - - tmrinfo("counter=%lu (%lu) overflow=%lu, pending=%i\n", - (unsigned long)counter, (unsigned long)verify, - (unsigned long)overflow, pending); - tmrinfo("frequency=%lu\n", g_tickless.frequency); - - /* Convert the whole thing to units of microseconds. - * - * frequency = ticks / second - * seconds = ticks * frequency - * usecs = (ticks * USEC_PER_SEC) / frequency; - */ -#ifdef HAVE_32BIT_TICKLESS - usec = ((((uint64_t)overflow << 32) + (uint64_t)counter) * USEC_PER_SEC) / - g_tickless.frequency; -#else - usec = ((((uint64_t)overflow << 16) + (uint64_t)counter) * USEC_PER_SEC) / - g_tickless.frequency; -#endif - - /* And return the value of the timer */ - - sec = (uint32_t)(usec / USEC_PER_SEC); - ts->tv_sec = sec; - ts->tv_nsec = (usec - (sec * USEC_PER_SEC)) * NSEC_PER_USEC; - - tmrinfo("usec=%llu ts=(%jd, %ld)\n", - usec, (intmax_t)ts->tv_sec, ts->tv_nsec); - - return OK; -} - -#ifdef CONFIG_CLOCK_TIMEKEEPING - -/**************************************************************************** - * Name: up_timer_gettick - * - * Description: - * To be provided - * - * Input Parameters: - * cycles - 64-bit return value - * - * Returned Value: - * None - * - ****************************************************************************/ - -int up_timer_gettick(clock_t *ticks) -{ - *ticks = STM32_TIM_GETCOUNTER(g_tickless.tch); - return OK; -} - -/**************************************************************************** - * Name: up_timer_getmask - * - * Description: - * To be provided - * - * Input Parameters: - * mask - Location to return the 64-bit mask - * - * Returned Value: - * None - * - ****************************************************************************/ - -void up_timer_getmask(clock_t *mask) -{ - DEBUGASSERT(mask != NULL); -#ifdef HAVE_32BIT_TICKLESS - *mask = UINT32_MAX; -#else - *mask = UINT16_MAX; -#endif -} - -#endif /* CONFIG_CLOCK_TIMEKEEPING */ - -/**************************************************************************** - * Name: up_timer_cancel - * - * Description: - * Cancel the interval timer and return the time remaining on the timer. - * These two steps need to be as nearly atomic as possible. - * nxsched_process_timer() will not be called unless the timer is - * restarted with up_timer_start(). - * - * If, as a race condition, the timer has already expired when this - * function is called, then that pending interrupt must be cleared so - * that up_timer_start() and the remaining time of zero should be - * returned. - * - * NOTE: This function may execute at a high rate with no timer running (as - * when pre-emption is enabled and disabled). - * - * Provided by platform-specific code and called from the RTOS base code. - * - * Input Parameters: - * ts - Location to return the remaining time. Zero should be returned - * if the timer is not active. 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. - * - * Assumptions: - * May be called from interrupt level handling or from the normal tasking - * level. Interrupts may need to be disabled internally to assure - * non-reentrancy. - * - ****************************************************************************/ - -#ifndef CONFIG_SCHED_TICKLESS_ALARM -int up_timer_cancel(struct timespec *ts) -{ - irqstate_t flags; - uint64_t usec; - uint64_t sec; - uint64_t nsec; - uint32_t count; - uint32_t period; - - /* Was the timer running? */ - - flags = enter_critical_section(); - if (!g_tickless.pending) - { - /* No.. Just return zero timer remaining and successful cancellation. - * This function may execute at a high rate with no timer running - * (as when pre-emption is enabled and disabled). - */ - - if (ts != NULL) - { - ts->tv_sec = 0; - ts->tv_nsec = 0; - } - - leave_critical_section(flags); - return OK; - } - - /* Yes.. Get the timer counter and period registers and disable the compare - * interrupt. - */ - - tmrinfo("Cancelling...\n"); - - /* Disable the interrupt. */ - - stm32_tickless_disableint(g_tickless.channel); - - count = STM32_TIM_GETCOUNTER(g_tickless.tch); - period = g_tickless.period; - - g_tickless.pending = false; - leave_critical_section(flags); - - /* Did the caller provide us with a location to return the time - * remaining? - */ - - if (ts != NULL) - { - /* Yes.. then calculate and return the time remaining on the - * oneshot timer. - */ - - tmrinfo("period=%lu count=%lu\n", - (unsigned long)period, (unsigned long)count); - -#ifndef HAVE_32BIT_TICKLESS - if ((int16_t)(period - count) <= 0) -#else - if ((int32_t)(period - count) <= 0) -#endif - { - /* No time remaining */ - - ts->tv_sec = 0; - ts->tv_nsec = 0; - return OK; - } - - /* The total time remaining is the difference. Convert that - * to units of microseconds. - * - * frequency = ticks / second - * seconds = ticks * frequency - * usecs = (ticks * USEC_PER_SEC) / frequency; - */ - -#ifndef HAVE_32BIT_TICKLESS - usec = (((uint64_t)(uint16_t)(period - count)) * USEC_PER_SEC) / - g_tickless.frequency; -#else - usec = (((uint64_t)(uint32_t)(period - count)) * USEC_PER_SEC) / - g_tickless.frequency; -#endif - - /* Return the time remaining in the correct form */ - - sec = usec / USEC_PER_SEC; - nsec = ((usec) - (sec * USEC_PER_SEC)) * NSEC_PER_USEC; - - ts->tv_sec = sec; - ts->tv_nsec = nsec; - - tmrinfo("remaining (%jd, %ld)\n", - (intmax_t)ts->tv_sec, ts->tv_nsec); - } - - return OK; -} -#endif - -/**************************************************************************** - * Name: up_timer_start - * - * Description: - * Start the interval timer. nxsched_process_timer() will be - * called at the completion of the timeout (unless up_timer_cancel - * is called to stop the timing. - * - * Provided by platform-specific code and called from the RTOS base code. - * - * Input Parameters: - * ts - Provides the time interval until nxsched_process_timer() is - * called. - * - * Returned Value: - * Zero (OK) is returned on success; a negated errno value is returned on - * any failure. - * - * Assumptions: - * May be called from interrupt level handling or from the normal tasking - * level. Interrupts may need to be disabled internally to assure - * non-reentrancy. - * - ****************************************************************************/ - -#ifndef CONFIG_SCHED_TICKLESS_ALARM -int up_timer_start(const struct timespec *ts) -{ - uint64_t usec; - uint64_t period; - uint32_t count; - irqstate_t flags; - - tmrinfo("ts=(%jd, %ld)\n", - (intmax_t)ts->tv_sec, ts->tv_nsec); - DEBUGASSERT(ts); - DEBUGASSERT(g_tickless.tch); - - /* Was an interval already running? */ - - flags = enter_critical_section(); - if (g_tickless.pending) - { - /* Yes.. then cancel it */ - - tmrinfo("Already running... cancelling\n"); - up_timer_cancel(NULL); - } - - /* Express the delay in microseconds */ - - if (ts->tv_sec < 0 || (ts->tv_sec == 0 && ts->tv_nsec <= 0)) - { - period = 0; - } - else - { - usec = ts->tv_sec * USEC_PER_SEC + - (ts->tv_nsec / NSEC_PER_USEC); - - /* Get the timer counter frequency and determine the number of counts - * need to achieve the requested delay. - * - * frequency = ticks / second - * ticks = seconds * frequency - * = (usecs * frequency) / USEC_PER_SEC; - */ - - period = (usec * (uint64_t)g_tickless.frequency) / USEC_PER_SEC; - } - - if (period == 0) - { - stm32_tickless_enableint(g_tickless.channel); - stm32_tickless_trigint(g_tickless.channel); - g_tickless.pending = true; - leave_critical_section(flags); - return OK; - } - - count = STM32_TIM_GETCOUNTER(g_tickless.tch); - - tmrinfo("usec=%llu period=%08llx\n", usec, period); - - /* Set interval compare value. Rollover is fine, - * channel will trigger on the next period. - */ - -#ifdef HAVE_32BIT_TICKLESS - DEBUGASSERT(period <= UINT32_MAX); - g_tickless.period = (uint32_t)(period + count); -#else - DEBUGASSERT(period <= UINT16_MAX); - g_tickless.period = (uint16_t)(period + count); -#endif - - STM32_TIM_SETCOMPARE(g_tickless.tch, g_tickless.channel, - g_tickless.period); - - /* Enable interrupts. We should get the callback when the interrupt - * occurs. - */ - - stm32_tickless_ackint(g_tickless.channel); - stm32_tickless_enableint(g_tickless.channel); - - g_tickless.pending = true; - - /* Check if the counter already reached or passed the compare target - * while we were configuring the registers. - */ - -#ifdef HAVE_32BIT_TICKLESS - if ((uint32_t)(STM32_TIM_GETCOUNTER(g_tickless.tch) - count) >= - (uint32_t)period) -#else - if ((uint16_t)(STM32_TIM_GETCOUNTER(g_tickless.tch) - (uint16_t)count) >= - (uint16_t)period) -#endif - { - /* Target time already elapsed; force the interrupt immediately */ - - stm32_tickless_trigint(g_tickless.channel); - } - - leave_critical_section(flags); - return OK; -} -#endif - -#ifdef CONFIG_SCHED_TICKLESS_ALARM -int up_alarm_start(const struct timespec *ts) -{ - size_t offset = 1; - uint64_t tm = (ts->tv_sec * NSEC_PER_SEC + ts->tv_nsec) / - NSEC_PER_TICK; - irqstate_t flags; - - flags = enter_critical_section(); - - STM32_TIM_SETCOMPARE(g_tickless.tch, CONFIG_STM32_TICKLESS_CHANNEL, tm); - - stm32_tickless_ackint(g_tickless.channel); - stm32_tickless_enableint(CONFIG_STM32_TICKLESS_CHANNEL); - - g_tickless.pending = true; - - /* If we have already passed this time, there is a chance we didn't set the - * compare register in time and we've missed the interrupt. If we don't - * catch this case, we won't interrupt until a full loop of the clock. - * - * Since we can't make assumptions about the clock speed and tick rate, - * we simply keep adding an offset to the current time, until we can leave - * certain that the interrupt is going to fire as soon as we leave the - * critical section. - */ - - while (tm <= stm32_get_counter()) - { - tm = stm32_get_counter() + offset++; - STM32_TIM_SETCOMPARE(g_tickless.tch, CONFIG_STM32_TICKLESS_CHANNEL, - tm); - } - - leave_critical_section(flags); - return OK; -} - -int up_alarm_cancel(struct timespec *ts) -{ -#ifdef HAVE_32BIT_TICKLESS - uint64_t nsecs = (((uint64_t)g_tickless.overflow << 32) | - STM32_TIM_GETCOUNTER(g_tickless.tch)) * NSEC_PER_TICK; -#else - uint64_t nsecs = (((uint64_t)g_tickless.overflow << 16) | - STM32_TIM_GETCOUNTER(g_tickless.tch)) * NSEC_PER_TICK; -#endif - - ts->tv_sec = nsecs / NSEC_PER_SEC; - ts->tv_nsec = nsecs - ts->tv_sec * NSEC_PER_SEC; - - stm32_tickless_disableint(CONFIG_STM32_TICKLESS_CHANNEL); - - return 0; -} -#endif - -#endif /* CONFIG_SCHED_TICKLESS */ diff --git a/arch/arm/src/stm32wb/CMakeLists.txt b/arch/arm/src/stm32wb/CMakeLists.txt index 41b85160b7a..ce7798e48d2 100644 --- a/arch/arm/src/stm32wb/CMakeLists.txt +++ b/arch/arm/src/stm32wb/CMakeLists.txt @@ -41,10 +41,6 @@ if(NOT CONFIG_ARCH_IDLE_CUSTOM) list(APPEND SRCS stm32wb_idle.c) endif() -if(CONFIG_SCHED_TICKLESS) - list(APPEND SRCS stm32wb_tickless.c) -endif() - if(CONFIG_BUILD_PROTECTED) list(APPEND SRCS stm32wb_userspace.c stm32wb_mpuinit.c) endif() diff --git a/arch/arm/src/stm32wb/Make.defs b/arch/arm/src/stm32wb/Make.defs index 2cbdb9d7fe6..58dc01b7777 100644 --- a/arch/arm/src/stm32wb/Make.defs +++ b/arch/arm/src/stm32wb/Make.defs @@ -41,10 +41,6 @@ ifneq ($(CONFIG_ARCH_IDLE_CUSTOM),y) CHIP_CSRCS += stm32wb_idle.c endif -ifeq ($(CONFIG_SCHED_TICKLESS),y) -CHIP_CSRCS += stm32wb_tickless.c -endif - ifeq ($(CONFIG_BUILD_PROTECTED),y) CHIP_CSRCS += stm32wb_userspace.c stm32wb_mpuinit.c endif diff --git a/arch/arm/src/stm32wb/stm32wb_tickless.c b/arch/arm/src/stm32wb/stm32wb_tickless.c deleted file mode 100644 index 708cbde22da..00000000000 --- a/arch/arm/src/stm32wb/stm32wb_tickless.c +++ /dev/null @@ -1,886 +0,0 @@ -/**************************************************************************** - * arch/arm/src/stm32wb/stm32wb_tickless.c - * - * SPDX-License-Identifier: Apache-2.0 - * - * Licensed to the Apache Software Foundation (ASF) under one or more - * contributor license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright ownership. The - * ASF licenses this file to you under the Apache License, Version 2.0 (the - * "License"); you may not use this file except in compliance with the - * License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT - * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the - * License for the specific language governing permissions and limitations - * under the License. - * - ****************************************************************************/ - -/**************************************************************************** - * Tickless OS Support. - * - * When CONFIG_SCHED_TICKLESS is enabled, all support for timer interrupts - * is suppressed and the platform specific code is expected to provide the - * following custom functions. - * - * void up_timer_initialize(void): Initializes the timer facilities. - * Called early in the initialization sequence (by up_initialize()). - * int up_timer_gettime(struct timespec *ts): Returns the current - * time from the platform specific time source. - * int up_timer_cancel(void): Cancels the interval timer. - * int up_timer_start(const struct timespec *ts): Start (or re-starts) - * the interval timer. - * - * The RTOS will provide the following interfaces for use by the platform- - * specific interval timer implementation: - * - * void nxsched_process_timer(void): Called by the platform-specific - * logic when the interval timer expires. - * - ****************************************************************************/ - -/**************************************************************************** - * STM32WB Timer Usage - * - * This implementation uses one timer: A free running timer to provide - * the current time and a capture/compare channel for timed-events. - * - * There are two interrupts generated from our timer, the overflow interrupt - * which drives the timing handler and the capture/compare interrupt which - * drives the interval handler. There are some low level timer control - * functions implemented here because the API of stm32_tim.c does not - * provide adequate control over capture/compare interrupts. - * - ****************************************************************************/ - -/**************************************************************************** - * Included Files - ****************************************************************************/ - -#include <nuttx/config.h> - -#include <stdint.h> -#include <stdbool.h> -#include <errno.h> -#include <assert.h> - -#include <nuttx/arch.h> -#include <nuttx/debug.h> - -#include "arm_internal.h" -#include "stm32_tim.h" - -#ifdef CONFIG_SCHED_TICKLESS - -/**************************************************************************** - * Pre-processor Definitions - ****************************************************************************/ - -/* Only TIM2 is a 32-bit timer */ - -#undef HAVE_32BIT_TICKLESS - -#ifdef CONFIG_STM32_TICKLESS_TIMER -# if CONFIG_STM32_TICKLESS_TIMER == 2 -# define HAVE_32BIT_TICKLESS 1 -# endif -#else -# error "STM32_TICKLESS_TIMER must be defined for tickless configuration" -#endif - -/**************************************************************************** - * Private Types - ****************************************************************************/ - -struct stm32_tickless_s -{ - uint8_t timer; /* The timer/counter in use */ - uint8_t channel; /* The timer channel to use - * for intervals */ - struct stm32_tim_dev_s *tch; /* Pointer returned by - * stm32_tim_init() */ - uint32_t frequency; - uint32_t overflow; /* Timer counter overflow */ - volatile bool pending; /* True: pending task */ - uint32_t period; /* Interval period */ - uint32_t base; -}; - -/**************************************************************************** - * Private Data - ****************************************************************************/ - -static struct stm32_tickless_s g_tickless; - -/**************************************************************************** - * Private Functions - ****************************************************************************/ - -/**************************************************************************** - * Name: stm32_getreg16 - * - * Description: - * Get a 16-bit register value by offset - * - ****************************************************************************/ - -static inline uint16_t stm32_getreg16(uint8_t offset) -{ - return getreg16(g_tickless.base + offset); -} - -/**************************************************************************** - * Name: stm32_putreg16 - * - * Description: - * Put a 16-bit register value by offset - * - ****************************************************************************/ - -static inline void stm32_putreg16(uint8_t offset, uint16_t value) -{ - putreg16(value, g_tickless.base + offset); -} - -/**************************************************************************** - * Name: stm32_modifyreg16 - * - * Description: - * Modify a 16-bit register value by offset - * - ****************************************************************************/ - -static inline void stm32_modifyreg16(uint8_t offset, uint16_t clearbits, - uint16_t setbits) -{ - modifyreg16(g_tickless.base + offset, clearbits, setbits); -} - -/**************************************************************************** - * Name: stm32_tickless_enableint - ****************************************************************************/ - -static inline void stm32_tickless_enableint(int channel) -{ - stm32_modifyreg16(STM32_GTIM_DIER_OFFSET, 0, 1 << channel); -} - -/**************************************************************************** - * Name: stm32_tickless_disableint - ****************************************************************************/ - -static inline void stm32_tickless_disableint(int channel) -{ - stm32_modifyreg16(STM32_GTIM_DIER_OFFSET, 1 << channel, 0); -} - -/**************************************************************************** - * Name: stm32_tickless_ackint - ****************************************************************************/ - -static inline void stm32_tickless_ackint(int channel) -{ - stm32_putreg16(STM32_GTIM_SR_OFFSET, ~(1 << channel)); -} - -/**************************************************************************** - * Name: stm32_tickless_trigint - ****************************************************************************/ - -static inline void stm32_tickless_trigint(int channel) -{ - stm32_putreg16(STM32_GTIM_EGR_OFFSET, 1 << channel); -} - -/**************************************************************************** - * Name: stm32_tickless_getint - ****************************************************************************/ - -static inline uint16_t stm32_tickless_getint(void) -{ - return stm32_getreg16(STM32_GTIM_SR_OFFSET); -} - -/**************************************************************************** - * Name: stm32_tickless_setchannel - ****************************************************************************/ - -static int stm32_tickless_setchannel(uint8_t channel) -{ - uint16_t ccmr_orig = 0; - uint16_t ccmr_val = 0; - uint16_t ccer_val; - uint8_t ccmr_offset = STM32_GTIM_CCMR1_OFFSET; - - /* Further we use range as 0..3; if channel=0 it will also overflow here */ - - if (--channel > 3) - { - return -EINVAL; - } - - /* Assume that channel is disabled and polarity is active high */ - - ccer_val = stm32_getreg16(STM32_GTIM_CCER_OFFSET); - ccer_val &= ~((GTIM_CCER_CC1E | GTIM_CCER_CC1P) << - GTIM_CCER_CCXBASE(channel)); - - /* Frozen mode because we don't want to change the GPIO, preload register - * disabled. - */ - - ccmr_val = (GTIM_CCMR_MODE_FRZN << GTIM_CCMR1_OC1M_SHIFT) << - ((channel & 1) << 3); - - /* Set polarity */ - - ccer_val |= GTIM_CCER_CC1P << GTIM_CCER_CCXBASE(channel); - - if (channel > 1) - { - ccmr_offset = STM32_GTIM_CCMR2_OFFSET; - } - - ccmr_orig = stm32_getreg16(ccmr_offset); - ccmr_orig &= ~((GTIM_CCMR1_OC1M_MASK | GTIM_CCMR1_OC1PE) << - ((channel & 1) << 3)); - ccmr_orig |= ccmr_val; - stm32_putreg16(ccmr_offset, ccmr_orig); - stm32_putreg16(STM32_GTIM_CCER_OFFSET, ccer_val); - - return OK; -} - -/**************************************************************************** - * Name: stm32_interval_handler - * - * Description: - * Called when the timer counter matches the compare register - * - * Input Parameters: - * None - * - * Returned Value: - * None - * - * Assumptions: - * Called early in the initialization sequence before any special - * concurrency protections are required. - * - ****************************************************************************/ - -static void stm32_interval_handler(void) -{ - tmrinfo("Expired...\n"); - - /* Disable the compare interrupt now. */ - - stm32_tickless_disableint(g_tickless.channel); - stm32_tickless_ackint(g_tickless.channel); - - g_tickless.pending = false; - - nxsched_process_timer(); -} - -/**************************************************************************** - * Name: stm32_timing_handler - * - * Description: - * Timer interrupt callback. When the freerun timer counter overflows, - * this interrupt will occur. We will just increment an overflow count. - * - * Input Parameters: - * None - * - * Returned Value: - * None - * - ****************************************************************************/ - -static void stm32_timing_handler(void) -{ - g_tickless.overflow++; - - STM32_TIM_ACKINT(g_tickless.tch, GTIM_SR_UIF); -} - -/**************************************************************************** - * Name: stm32_tickless_handler - * - * Description: - * Generic interrupt handler for this timer. It checks the source of the - * interrupt and fires the appropriate handler. - * - * Input Parameters: - * None - * - * Returned Value: - * None - * - ****************************************************************************/ - -static int stm32_tickless_handler(int irq, void *context, void *arg) -{ - int interrupt_flags = stm32_tickless_getint(); - - if (interrupt_flags & GTIM_SR_UIF) - { - stm32_timing_handler(); - } - - if (interrupt_flags & (1 << g_tickless.channel)) - { - stm32_interval_handler(); - } - - return OK; -} - -/**************************************************************************** - * Public Functions - ****************************************************************************/ - -/**************************************************************************** - * Name: up_timer_initialize - * - * Description: - * Initializes all platform-specific timer facilities. This function is - * called early in the initialization sequence by up_initialize(). - * On return, the current up-time should be available from - * up_timer_gettime() and the interval timer is ready for use (but not - * actively timing. - * - * Provided by platform-specific code and called from the architecture- - * specific logic. - * - * Input Parameters: - * None - * - * Returned Value: - * None - * - * Assumptions: - * Called early in the initialization sequence before any special - * concurrency protections are required. - * - ****************************************************************************/ - -void up_timer_initialize(void) -{ - switch (CONFIG_STM32_TICKLESS_TIMER) - { -#ifdef CONFIG_STM32_TIM1 - case 1: - g_tickless.base = STM32_TIM1_BASE; - break; -#endif - -#ifdef CONFIG_STM32_TIM2 - case 2: - g_tickless.base = STM32_TIM2_BASE; - break; -#endif - -#ifdef CONFIG_STM32_TIM16 - case 16: - g_tickless.base = STM32_TIM16_BASE; - break; -#endif - -#ifdef CONFIG_STM32_TIM17 - case 17: - g_tickless.base = STM32_TIM17_BASE; - break; -#endif - - default: - DEBUGPANIC(); - } - - /* Get the TC frequency that corresponds to the requested resolution */ - - g_tickless.frequency = USEC_PER_SEC / (uint32_t)CONFIG_USEC_PER_TICK; - g_tickless.timer = CONFIG_STM32_TICKLESS_TIMER; - g_tickless.channel = CONFIG_STM32_TICKLESS_CHANNEL; - g_tickless.pending = false; - g_tickless.period = 0; - g_tickless.overflow = 0; - - tmrinfo("timer=%d channel=%d frequency=%lu Hz\n", - g_tickless.timer, g_tickless.channel, g_tickless.frequency); - - g_tickless.tch = stm32_tim_init(g_tickless.timer); - if (!g_tickless.tch) - { - tmrerr("ERROR: Failed to allocate TIM%d\n", g_tickless.timer); - DEBUGPANIC(); - } - - STM32_TIM_SETCLOCK(g_tickless.tch, g_tickless.frequency); - - /* Set up to receive the callback when the counter overflow occurs */ - - STM32_TIM_SETISR(g_tickless.tch, stm32_tickless_handler, NULL, 0); - - /* Initialize interval to zero */ - - STM32_TIM_SETCOMPARE(g_tickless.tch, g_tickless.channel, 0); - - /* Setup compare channel for the interval timing */ - - stm32_tickless_setchannel(g_tickless.channel); - - /* Set timer period */ - -#ifdef HAVE_32BIT_TICKLESS - STM32_TIM_SETPERIOD(g_tickless.tch, UINT32_MAX); -#ifdef CONFIG_SCHED_TICKLESS_LIMIT_MAX_SLEEP - g_oneshot_maxticks = UINT32_MAX; -#endif -#else - STM32_TIM_SETPERIOD(g_tickless.tch, UINT16_MAX); -#ifdef CONFIG_SCHED_TICKLESS_LIMIT_MAX_SLEEP - g_oneshot_maxticks = UINT16_MAX; -#endif -#endif - - /* Initialize the counter */ - - STM32_TIM_SETMODE(g_tickless.tch, STM32_TIM_MODE_UP); - - /* Start the timer */ - - STM32_TIM_ACKINT(g_tickless.tch, ~0); - STM32_TIM_ENABLEINT(g_tickless.tch, GTIM_DIER_UIE); -} - -/**************************************************************************** - * Name: up_timer_gettime - * - * Description: - * Return the elapsed time since power-up (or, more correctly, since - * up_timer_initialize() was called). This function is functionally - * equivalent to: - * - * int clock_gettime(clockid_t clockid, struct timespec *ts); - * - * when clockid is CLOCK_MONOTONIC. - * - * This function provides the basis for reporting the current time and - * also is used to eliminate error build-up from small errors in interval - * time calculations. - * - * Provided by platform-specific code and called from the RTOS base code. - * - * Input Parameters: - * ts - Provides the location in which to return the up-time. - * - * Returned Value: - * Zero (OK) is returned on success; a negated errno value is returned on - * any failure. - * - * Assumptions: - * Called from the normal tasking context. The implementation must - * provide whatever mutual exclusion is necessary for correct operation. - * This can include disabling interrupts in order to assure atomic register - * operations. - * - ****************************************************************************/ - -int up_timer_gettime(struct timespec *ts) -{ - uint64_t usec; - uint32_t counter; - uint32_t verify; - uint32_t overflow; - uint32_t sec; - int pending; - irqstate_t flags; - - DEBUGASSERT(g_tickless.tch && ts); - - /* Temporarily disable the overflow counter. NOTE that we have to be - * careful here because stm32_tc_getpending() will reset the pending - * interrupt status. If we do not handle the overflow here then, it will - * be lost. - */ - - flags = enter_critical_section(); - - overflow = g_tickless.overflow; - counter = STM32_TIM_GETCOUNTER(g_tickless.tch); - pending = STM32_TIM_CHECKINT(g_tickless.tch, GTIM_SR_UIF); - verify = STM32_TIM_GETCOUNTER(g_tickless.tch); - - /* If an interrupt was pending before we re-enabled interrupts, - * then the overflow needs to be incremented. - */ - - if (pending) - { - STM32_TIM_ACKINT(g_tickless.tch, GTIM_SR_UIF); - - /* Increment the overflow count and use the value of the - * guaranteed to be AFTER the overflow occurred. - */ - - overflow++; - counter = verify; - - /* Update tickless overflow counter. */ - - g_tickless.overflow = overflow; - } - - leave_critical_section(flags); - - tmrinfo("counter=%lu (%lu) overflow=%lu, pending=%i\n", - (unsigned long)counter, (unsigned long)verify, - (unsigned long)overflow, pending); - tmrinfo("frequency=%lu\n", g_tickless.frequency); - - /* Convert the whole thing to units of microseconds. - * - * frequency = ticks / second - * seconds = ticks * frequency - * usecs = (ticks * USEC_PER_SEC) / frequency; - */ -#ifdef HAVE_32BIT_TICKLESS - usec = ((((uint64_t)overflow << 32) + (uint64_t)counter) * USEC_PER_SEC) / - g_tickless.frequency; -#else - usec = ((((uint64_t)overflow << 16) + (uint64_t)counter) * USEC_PER_SEC) / - g_tickless.frequency; -#endif - - /* And return the value of the timer */ - - sec = (uint32_t)(usec / USEC_PER_SEC); - ts->tv_sec = sec; - ts->tv_nsec = (usec - (sec * USEC_PER_SEC)) * NSEC_PER_USEC; - - tmrinfo("usec=%llu ts=(%jd, %ld)\n", - usec, (intmax_t)ts->tv_sec, ts->tv_nsec); - - return OK; -} - -#ifdef CONFIG_CLOCK_TIMEKEEPING - -/**************************************************************************** - * Name: up_timer_gettick - * - * Description: - * To be provided - * - * Input Parameters: - * cycles - 64-bit return value - * - * Returned Value: - * None - * - ****************************************************************************/ - -int up_timer_gettick(clock_t *ticks) -{ - *ticks = STM32_TIM_GETCOUNTER(g_tickless.tch); - return OK; -} - -/**************************************************************************** - * Name: up_timer_getmask - * - * Description: - * To be provided - * - * Input Parameters: - * mask - Location to return the 64-bit mask - * - * Returned Value: - * None - * - ****************************************************************************/ - -void up_timer_getmask(clock_t *mask) -{ - DEBUGASSERT(mask != NULL); -#ifdef HAVE_32BIT_TICKLESS - *mask = UINT32_MAX; -#else - *mask = UINT16_MAX; -#endif -} - -#endif /* CONFIG_CLOCK_TIMEKEEPING */ - -/**************************************************************************** - * Name: up_timer_cancel - * - * Description: - * Cancel the interval timer and return the time remaining on the timer. - * These two steps need to be as nearly atomic as possible. - * nxsched_process_timer() will not be called unless the timer is - * restarted with up_timer_start(). - * - * If, as a race condition, the timer has already expired when this - * function is called, then that pending interrupt must be cleared so - * that up_timer_start() and the remaining time of zero should be - * returned. - * - * NOTE: This function may execute at a high rate with no timer running (as - * when pre-emption is enabled and disabled). - * - * Provided by platform-specific code and called from the RTOS base code. - * - * Input Parameters: - * ts - Location to return the remaining time. Zero should be returned - * if the timer is not active. 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. - * - * Assumptions: - * May be called from interrupt level handling or from the normal tasking - * level. Interrupts may need to be disabled internally to assure - * non-reentrancy. - * - ****************************************************************************/ - -int up_timer_cancel(struct timespec *ts) -{ - irqstate_t flags; - uint64_t usec; - uint64_t sec; - uint64_t nsec; - uint32_t count; - uint32_t period; - - /* Was the timer running? */ - - flags = enter_critical_section(); - if (!g_tickless.pending) - { - /* No.. Just return zero timer remaining and successful cancellation. - * This function may execute at a high rate with no timer running - * (as when pre-emption is enabled and disabled). - */ - - if (ts) - { - ts->tv_sec = 0; - ts->tv_nsec = 0; - } - - leave_critical_section(flags); - return OK; - } - - /* Yes.. Get the timer counter and period registers and disable the compare - * interrupt. - */ - - tmrinfo("Cancelling...\n"); - - /* Disable the interrupt. */ - - stm32_tickless_disableint(g_tickless.channel); - - count = STM32_TIM_GETCOUNTER(g_tickless.tch); - period = g_tickless.period; - - g_tickless.pending = false; - leave_critical_section(flags); - - /* Did the caller provide us with a location to return the time - * remaining? - */ - - if (ts != NULL) - { - /* Yes.. then calculate and return the time remaining on the - * oneshot timer. - */ - - tmrinfo("period=%lu count=%lu\n", - (unsigned long)period, (unsigned long)count); - -#ifndef HAVE_32BIT_TICKLESS - if ((int16_t)(period - count) <= 0) -#else - if ((int32_t)(period - count) <= 0) -#endif - { - /* No time remaining */ - - ts->tv_sec = 0; - ts->tv_nsec = 0; - return OK; - } - - /* The total time remaining is the difference. Convert that - * to units of microseconds. - * - * frequency = ticks / second - * seconds = ticks * frequency - * usecs = (ticks * USEC_PER_SEC) / frequency; - */ - -#ifndef HAVE_32BIT_TICKLESS - usec = (((uint64_t)(uint16_t)(period - count)) * USEC_PER_SEC) / - g_tickless.frequency; -#else - usec = (((uint64_t)(uint32_t)(period - count)) * USEC_PER_SEC) / - g_tickless.frequency; -#endif - - /* Return the time remaining in the correct form */ - - sec = usec / USEC_PER_SEC; - nsec = (usec - (sec * USEC_PER_SEC)) * NSEC_PER_USEC; - - ts->tv_sec = sec; - ts->tv_nsec = nsec; - - tmrinfo("remaining (%jd, %ld)\n", - (intmax_t)ts->tv_sec, ts->tv_nsec); - } - - return OK; -} - -/**************************************************************************** - * Name: up_timer_start - * - * Description: - * Start the interval timer. nxsched_process_timer() will be - * called at the completion of the timeout (unless up_timer_cancel - * is called to stop the timing. - * - * Provided by platform-specific code and called from the RTOS base code. - * - * Input Parameters: - * ts - Provides the time interval until nxsched_process_timer() is - * called. - * - * Returned Value: - * Zero (OK) is returned on success; a negated errno value is returned on - * any failure. - * - * Assumptions: - * May be called from interrupt level handling or from the normal tasking - * level. Interrupts may need to be disabled internally to assure - * non-reentrancy. - * - ****************************************************************************/ - -int up_timer_start(const struct timespec *ts) -{ - uint64_t usec; - uint64_t period; - uint32_t count; - irqstate_t flags; - - tmrinfo("ts=(%jd, %ld)\n", - (intmax_t)ts->tv_sec, ts->tv_nsec); - DEBUGASSERT(ts); - DEBUGASSERT(g_tickless.tch); - - /* Was an interval already running? */ - - flags = enter_critical_section(); - if (g_tickless.pending) - { - /* Yes.. then cancel it */ - - tmrinfo("Already running... cancelling\n"); - up_timer_cancel(NULL); - } - - /* Express the delay in microseconds */ - - if (ts->tv_sec < 0 || (ts->tv_sec == 0 && ts->tv_nsec <= 0)) - { - period = 0; - } - else - { - usec = ts->tv_sec * USEC_PER_SEC + - (ts->tv_nsec / NSEC_PER_USEC); - - /* Get the timer counter frequency and determine the number of counts - * need to achieve the requested delay. - * - * frequency = ticks / second - * ticks = seconds * frequency - * = (usecs * frequency) / USEC_PER_SEC; - */ - - period = (usec * (uint64_t)g_tickless.frequency) / USEC_PER_SEC; - } - - if (period == 0) - { - stm32_tickless_enableint(g_tickless.channel); - stm32_tickless_trigint(g_tickless.channel); - g_tickless.pending = true; - leave_critical_section(flags); - return OK; - } - - count = STM32_TIM_GETCOUNTER(g_tickless.tch); - - tmrinfo("usec=%llu period=%08llx\n", usec, period); - - /* Set interval compare value. Rollover is fine, - * channel will trigger on the next period. - */ - -#ifdef HAVE_32BIT_TICKLESS - DEBUGASSERT(period <= UINT32_MAX); - g_tickless.period = (uint32_t)(period + count); -#else - DEBUGASSERT(period <= UINT16_MAX); - g_tickless.period = (uint16_t)(period + count); -#endif - - STM32_TIM_SETCOMPARE(g_tickless.tch, g_tickless.channel, - g_tickless.period); - - /* Enable interrupts. We should get the callback when the interrupt - * occurs. - */ - - stm32_tickless_ackint(g_tickless.channel); - stm32_tickless_enableint(g_tickless.channel); - - g_tickless.pending = true; - - /* Check if the counter already reached or passed the compare target - * while we were configuring the registers. - */ - -#ifdef HAVE_32BIT_TICKLESS - if ((uint32_t)(STM32_TIM_GETCOUNTER(g_tickless.tch) - count) >= - (uint32_t)period) -#else - if ((uint16_t)(STM32_TIM_GETCOUNTER(g_tickless.tch) - (uint16_t)count) >= - (uint16_t)period) -#endif - { - /* Target time already elapsed; force the interrupt immediately */ - - stm32_tickless_trigint(g_tickless.channel); - } - - leave_critical_section(flags); - return OK; -} -#endif /* CONFIG_SCHED_TICKLESS */
