diff --git a/arch/arm/src/kinetis/CMakeLists.txt b/arch/arm/src/kinetis/CMakeLists.txt index cd8795e987535..ed8723af56fca 100644 --- a/arch/arm/src/kinetis/CMakeLists.txt +++ b/arch/arm/src/kinetis/CMakeLists.txt @@ -131,4 +131,8 @@ if(CONFIG_ANALOG) endif() endif() +if(CONFIG_KINETIS_PIT) + list(APPEND SRCS kinetis_oneshot_pit.c) +endif() + target_sources(arch PRIVATE ${SRCS}) diff --git a/arch/arm/src/kinetis/Kconfig b/arch/arm/src/kinetis/Kconfig index 6dbf32c615e53..5af694bcb6f32 100644 --- a/arch/arm/src/kinetis/Kconfig +++ b/arch/arm/src/kinetis/Kconfig @@ -687,6 +687,51 @@ config KINETIS_TPM2 ---help--- Support TPM module 2 + +config KINETIS_PIT + bool "Periodic Interrupt Timer (PIT)" + default n + select ONESHOT + select ONESHOT_COUNT + ---help--- + Support for Periodic Interrupt Timer (PIT). + + From the Reference Manual: + + The PIT module is an array of timers that can be + used to raise interrupts and trigger DMA channels. + + Currently, PIT is used for oneshot lower half driver. + +config KINETIS_PIT_CH0 + bool "Channel 0" + default n + depends on KINETIS_PIT + ---help--- + Enable Channel 0 of the Periodic Interrupt Timer (PIT). + +config KINETIS_PIT_CH1 + bool "Channel 1" + default n + depends on KINETIS_PIT + ---help--- + Enable Channel 1 of the Periodic Interrupt Timer (PIT). + +config KINETIS_PIT_CH2 + bool "Channel 2" + default n + depends on KINETIS_PIT + ---help--- + Enable Channel 2 of the Periodic Interrupt Timer (PIT). + +config KINETIS_PIT_CH3 + bool "Channel 3" + default n + depends on KINETIS_PIT + ---help--- + Enable Channel 3 of the Periodic Interrupt Timer (PIT). + + config KINETIS_LPTMR0 bool "Low power timer 0 (LPTMR0)" default n @@ -783,12 +828,6 @@ config KINETIS_PDB ---help--- Support the Programmable Delay Block -config KINETIS_PIT - bool "Programmable interval timer (PIT)" - default n - ---help--- - Support Programmable Interval Timers - endmenu menu "Kinetis FTM PWM Configuration" diff --git a/arch/arm/src/kinetis/Make.defs b/arch/arm/src/kinetis/Make.defs index 66461d889fa5e..22344ae10ade9 100644 --- a/arch/arm/src/kinetis/Make.defs +++ b/arch/arm/src/kinetis/Make.defs @@ -39,6 +39,10 @@ ifneq ($(CONFIG_SCHED_TICKLESS),y) CHIP_CSRCS += kinetis_timerisr.c endif +ifeq ($(CONFIG_KINETIS_PIT),y) +CHIP_CSRCS += kinetis_oneshot_pit.c +endif + ifeq ($(CONFIG_BUILD_PROTECTED),y) CHIP_CSRCS += kinetis_userspace.c endif diff --git a/arch/arm/src/kinetis/kinetis_oneshot_pit.c b/arch/arm/src/kinetis/kinetis_oneshot_pit.c new file mode 100644 index 0000000000000..c55c8db3e67ed --- /dev/null +++ b/arch/arm/src/kinetis/kinetis_oneshot_pit.c @@ -0,0 +1,403 @@ +/***************************************************************************** + * arch/arm/src/kinetis/kinetis_oneshot_pit.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. + * + ****************************************************************************/ + +/***************************************************************************** + * Periodic Interrupt Timer (PIT) + * + * From the Reference manual: + * + * The PIT module is an array of timers that can be used to raise interrupts + * and trigger DMA channels. + * + * This file provides oneshot timer driver that uses Kinetis' PIT. + * + ****************************************************************************/ + +/***************************************************************************** + * Included Files + ****************************************************************************/ + +#include + +#include +#include + +#include /* Enter and leave critical sections. */ +#include /* oneshot_lowerhalf_s, oneshot_... */ + +#include "hardware/kinetis_sim.h" /* KINETIS_SIM_... */ +#include "hardware/kinetis_pit.h" /* KINETIS_PIT_... */ + +#include "kinetis.h" + +#if defined(CONFIG_KINETIS_PIT) + +#if !defined(CONFIG_ONESHOT) || !defined(CONFIG_ONESHOT_COUNT) +# error "CONFIG_ONESHOT and CONFIG_ONESHOT_COUNT must be defined" +#endif + +/***************************************************************************** + * Private Types + * + * kinetis_oneshot_lowerhalf_s + * Private data structure to keep oneshot_lowerhalf_s structure and private + * information of the Kinetis implementation. + * + ****************************************************************************/ + +struct kinetis_oneshot_lowerhalf_s +{ + struct oneshot_lowerhalf_s lh; + + int channel; +}; + +/***************************************************************************** + * Private Function Prototypes + * + * See doc of the oneshot_operations_s in include/nuttx/timers/oneshot.h + * + * kinetis_oneshot_isr + * Interrupt service routine that calls oneshot_process_callback when timer + * finishes. + * + ****************************************************************************/ + +static clkcnt_t kinetis_oneshot_current(struct oneshot_lowerhalf_s *lower); +static void kinetis_oneshot_start(struct oneshot_lowerhalf_s *lower, + clkcnt_t delay_count); +static void kinetis_oneshot_start_absolute( + struct oneshot_lowerhalf_s *lower, clkcnt_t at_count); +static void kinetis_oneshot_cancel(struct oneshot_lowerhalf_s *lower); +static clkcnt_t kinetis_oneshot_maxdelay(struct oneshot_lowerhalf_s *lower); + +static int kinetis_oneshot_isr(int irq, void *context, void *arg); + +/***************************************************************************** + * Private Data + ****************************************************************************/ + +static struct oneshot_operations_s const g_oneshot_ops = +{ + .current = kinetis_oneshot_current, + .start = kinetis_oneshot_start, + .start_absolute = kinetis_oneshot_start_absolute, + .cancel = kinetis_oneshot_cancel, + .max_delay = kinetis_oneshot_maxdelay, +}; + +#if defined(CONFIG_KINETIS_PIT_CH0) +static struct kinetis_oneshot_lowerhalf_s g_pit_ch0 = +{ + .lh = + { + .ops = &g_oneshot_ops, + }, + .channel = 0, +}; +#endif /* CONFIG_KINETIS_PIT_CH0 */ + +#if defined(CONFIG_KINETIS_PIT_CH1) +static struct kinetis_oneshot_lowerhalf_s g_pit_ch1 = +{ + .lh = + { + .ops = &g_oneshot_ops, + }, + .channel = 1, +}; +#endif /* CONFIG_KINETIS_PIT_CH1 */ + +#if defined(CONFIG_KINETIS_PIT_CH2) +static struct kinetis_oneshot_lowerhalf_s g_pit_ch2 = +{ + .lh = + { + .ops = &g_oneshot_ops, + }, + .channel = 2, +}; +#endif /* CONFIG_KINETIS_PIT_CH2 */ + +#if defined(CONFIG_KINETIS_PIT_CH3) +static struct kinetis_oneshot_lowerhalf_s g_pit_ch3 = +{ + .lh = + { + .ops = &g_oneshot_ops, + }, + .channel = 3, +}; +#endif /* CONFIG_KINETIS_PIT_CH3 */ + +/***************************************************************************** + * Private Functions + ****************************************************************************/ + +static clkcnt_t +kinetis_oneshot_current(struct oneshot_lowerhalf_s *lower) +{ + struct kinetis_oneshot_lowerhalf_s *priv = + (struct kinetis_oneshot_lowerhalf_s *)lower; + + DEBUGASSERT(NULL != priv); + + switch (priv->channel) + { + case 0: + return getreg32(KINETIS_PIT_CVAL0); + break; + case 1: + return getreg32(KINETIS_PIT_CVAL1); + break; + case 2: + return getreg32(KINETIS_PIT_CVAL2); + break; + case 3: + return getreg32(KINETIS_PIT_CVAL3); + break; + default: + tmrerr("ERROR: Bad channel %d", priv->channel); + return 0; + } +} + +static void +kinetis_oneshot_start(struct oneshot_lowerhalf_s *lower, clkcnt_t delay_count) +{ + struct kinetis_oneshot_lowerhalf_s *priv = + (struct kinetis_oneshot_lowerhalf_s *)lower; + irqstate_t flags; + + DEBUGASSERT(NULL != priv); + + flags = enter_critical_section(); + switch (priv->channel) + { + case 0: + putreg32(0x00000000, KINETIS_PIT_TCTRL0); + putreg32(PIT_TFLG_TIF, KINETIS_PIT_TFLG0); + putreg32((uint32_t)delay_count, KINETIS_PIT_LDVAL0); + putreg32(PIT_TCTRL_TEN | PIT_TCTRL_TIE, KINETIS_PIT_TCTRL0); + irq_attach(KINETIS_IRQ_PITCH0, kinetis_oneshot_isr, NULL); + up_enable_irq(KINETIS_IRQ_PITCH0); + break; + case 1: + putreg32(0x00000000, KINETIS_PIT_TCTRL1); + putreg32(PIT_TFLG_TIF, KINETIS_PIT_TFLG1); + putreg32((uint32_t)delay_count, KINETIS_PIT_LDVAL1); + putreg32(PIT_TCTRL_TEN | PIT_TCTRL_TIE, KINETIS_PIT_TCTRL1); + irq_attach(KINETIS_IRQ_PITCH1, kinetis_oneshot_isr, NULL); + up_enable_irq(KINETIS_IRQ_PITCH1); + break; + case 2: + putreg32(0x00000000, KINETIS_PIT_TCTRL2); + putreg32(PIT_TFLG_TIF, KINETIS_PIT_TFLG2); + putreg32((uint32_t)delay_count, KINETIS_PIT_LDVAL2); + putreg32(PIT_TCTRL_TEN | PIT_TCTRL_TIE, KINETIS_PIT_TCTRL2); + irq_attach(KINETIS_IRQ_PITCH2, kinetis_oneshot_isr, NULL); + up_enable_irq(KINETIS_IRQ_PITCH2); + break; + case 3: + putreg32(0x00000000, KINETIS_PIT_TCTRL3); + putreg32(PIT_TFLG_TIF, KINETIS_PIT_TFLG3); + putreg32((uint32_t)delay_count, KINETIS_PIT_LDVAL3); + putreg32(PIT_TCTRL_TEN | PIT_TCTRL_TIE, KINETIS_PIT_TCTRL3); + irq_attach(KINETIS_IRQ_PITCH3, kinetis_oneshot_isr, NULL); + up_enable_irq(KINETIS_IRQ_PITCH3); + break; + default: + tmrerr("ERROR: Bad channel 0 <= %d <= 3", priv->channel); + } + + leave_critical_section(flags); +} + +static void +kinetis_oneshot_start_absolute(struct oneshot_lowerhalf_s *lower, + clkcnt_t at_count) +{ + /* Copied from the esp_oneshot.c file. */ + + uint32_t alarm = (uint32_t)at_count; + uint32_t counter = (uint32_t)kinetis_oneshot_current(lower); + + counter = alarm - counter >= alarm ? 0 : alarm - counter; + kinetis_oneshot_start(lower, counter); +} + +static void +kinetis_oneshot_cancel(struct oneshot_lowerhalf_s *lower) +{ + struct kinetis_oneshot_lowerhalf_s *priv = + (struct kinetis_oneshot_lowerhalf_s *)lower; + irqstate_t flags; + + DEBUGASSERT(NULL != priv); + + /* Reset PIT_TCTRLn and KINETIS_PIT_TFLGn. */ + + flags = enter_critical_section(); + switch (priv->channel) + { + case 0: + putreg32(0x00000000, KINETIS_PIT_TCTRL0); + putreg32(PIT_TFLG_TIF, KINETIS_PIT_TFLG0); + up_disable_irq(KINETIS_IRQ_PITCH0); + irq_detach(KINETIS_IRQ_PITCH0); + break; + case 1: + putreg32(0x00000000, KINETIS_PIT_TCTRL1); + putreg32(PIT_TFLG_TIF, KINETIS_PIT_TFLG1); + up_disable_irq(KINETIS_IRQ_PITCH1); + irq_detach(KINETIS_IRQ_PITCH1); + break; + case 2: + putreg32(0x00000000, KINETIS_PIT_TCTRL2); + putreg32(PIT_TFLG_TIF, KINETIS_PIT_TFLG2); + up_disable_irq(KINETIS_IRQ_PITCH2); + irq_detach(KINETIS_IRQ_PITCH2); + break; + case 3: + putreg32(0x00000000, KINETIS_PIT_TCTRL3); + putreg32(PIT_TFLG_TIF, KINETIS_PIT_TFLG3); + up_disable_irq(KINETIS_IRQ_PITCH3); + irq_detach(KINETIS_IRQ_PITCH3); + break; + default: + tmrerr("ERROR: Bad channel 0 <= %d <= 3", priv->channel); + } + + leave_critical_section(flags); +} + +static clkcnt_t +kinetis_oneshot_maxdelay(struct oneshot_lowerhalf_s *lower) +{ + /* PIT is 32-bit wide register, we cannot put in more. */ + + return 0xffffffff; +} + +static int +kinetis_oneshot_isr(int irq, void *context, void *arg) +{ + UNUSED(context); + UNUSED(arg); + + switch (irq) + { +#if defined(CONFIG_KINETIS_PIT_CH0) + case KINETIS_IRQ_PITCH0: + kinetis_oneshot_cancel(&g_pit_ch0.lh); + oneshot_process_callback(&g_pit_ch0.lh); + break; +#endif +#if defined(CONFIG_KINETIS_PIT_CH1) + case KINETIS_IRQ_PITCH1: + kinetis_oneshot_cancel(&g_pit_ch1.lh); + oneshot_process_callback(&g_pit_ch1.lh); + break; +#endif +#if defined(CONFIG_KINETIS_PIT_CH2) + case KINETIS_IRQ_PITCH2: + kinetis_oneshot_cancel(&g_pit_ch2.lh); + oneshot_process_callback(&g_pit_ch2.lh); + break; +#endif +#if defined(CONFIG_KINETIS_PIT_CH3) + case KINETIS_IRQ_PITCH3: + kinetis_oneshot_cancel(&g_pit_ch3.lh); + oneshot_process_callback(&g_pit_ch3.lh); + break; +#endif + } + + return 0; +} + +/***************************************************************************** + * Public Functions + ****************************************************************************/ + +struct oneshot_lowerhalf_s *oneshot_initialize(int chan, uint16_t resolution) +{ + DEBUGASSERT(0 <= chan); + DEBUGASSERT(chan <= 3); + + /* We do not use resolution. Frequency of PIT for Kinetis is given by the + * Peripheral bus clock. + */ + + UNUSED(resolution); + + uint32_t reg; + + /* Enable PIT in SIM. */ + + reg = getreg32(KINETIS_SIM_SCGC6); + reg |= SIM_SCGC6_PIT; + putreg32(reg, KINETIS_SIM_SCGC6); + + /* Enable PIT in PIT_MCR. */ + + reg = getreg32(KINETIS_PIT_MCR); + reg &= ~PIT_MCR_MDIS; + putreg32(reg, KINETIS_PIT_MCR); + + switch (chan) + { +#if defined(CONFIG_KINETIS_PIT_CH0) + case 0: + oneshot_count_init(&g_pit_ch0.lh, (uint32_t)BOARD_BUS_FREQ); + return &g_pit_ch0.lh; +#endif +#if defined(CONFIG_KINETIS_PIT_CH1) + case 1: + oneshot_count_init(&g_pit_ch1.lh, (uint32_t)BOARD_BUS_FREQ); + return &g_pit_ch1.lh; +#endif +#if defined(CONFIG_KINETIS_PIT_CH2) + case 2: + oneshot_count_init(&g_pit_ch2.lh, (uint32_t)BOARD_BUS_FREQ); + return &g_pit_ch2.lh; +#endif +#if defined(CONFIG_KINETIS_PIT_CH3) + case 3: + oneshot_count_init(&g_pit_ch3.lh, (uint32_t)BOARD_BUS_FREQ); + return &g_pit_ch3.lh; +#endif + default: + if (0 <= chan && chan <= 3) + { + tmrwarn("CONFIG_KINETIS_PIT_CH%d must be defined", chan); + } + else + { + tmrwarn("Bad channel specified, must be 0 <= %d <= 3", chan); + } + } + + return NULL; +} + +#endif /* CONFIG_KINETIS_PIT */ diff --git a/include/nuttx/timers/oneshot.h b/include/nuttx/timers/oneshot.h index 74ed607c96f68..99d2090d6380f 100644 --- a/include/nuttx/timers/oneshot.h +++ b/include/nuttx/timers/oneshot.h @@ -203,12 +203,26 @@ struct oneshot_operations_s * conversion, and the theoretical optimal timing accuracy. */ + /* Get the current timer count. */ + CODE clkcnt_t (*current)(FAR struct oneshot_lowerhalf_s *lower); + + /* Start a relative timer. */ + CODE void (*start)(FAR struct oneshot_lowerhalf_s *lower, clkcnt_t delay); + + /* Start an absolute timer. */ + CODE void (*start_absolute)(FAR struct oneshot_lowerhalf_s *lower, clkcnt_t cnt); + + /* Cancel the timer event. */ + CODE void (*cancel)(FAR struct oneshot_lowerhalf_s *lower); + + /* Get the timer's maximum delay. */ + CODE clkcnt_t (*max_delay)(FAR struct oneshot_lowerhalf_s *lower); #else /* Deprecated interfaces, just for compatiable-usage. */