From e4981b09d98f7cfcdcb3f3ebdceb27c4daf1b7ff Mon Sep 17 00:00:00 2001 From: Gregory Nutt Date: Sat, 9 Aug 2014 15:27:55 -0600 Subject: [PATCH] SAMA5 timer/counter: Add support for a one-shot timer wrapper around the low-level timer/counter logic. This also involved several changes that rippled into the ADC driver (untested). --- arch/arm/src/sama5/Kconfig | 297 +++++++++++++------------ arch/arm/src/sama5/Make.defs | 7 +- arch/arm/src/sama5/chip/sam_tc.h | 2 +- arch/arm/src/sama5/sam_adc.c | 14 +- arch/arm/src/sama5/sam_oneshot.c | 368 +++++++++++++++++++++++++++++++ arch/arm/src/sama5/sam_oneshot.h | 176 +++++++++++++++ arch/arm/src/sama5/sam_tc.c | 99 +++++++-- arch/arm/src/sama5/sam_tc.h | 72 +++++- 8 files changed, 860 insertions(+), 175 deletions(-) create mode 100644 arch/arm/src/sama5/sam_oneshot.c create mode 100644 arch/arm/src/sama5/sam_oneshot.h diff --git a/arch/arm/src/sama5/Kconfig b/arch/arm/src/sama5/Kconfig index f8747160c1..aa400b48b9 100644 --- a/arch/arm/src/sama5/Kconfig +++ b/arch/arm/src/sama5/Kconfig @@ -89,6 +89,10 @@ config SAMA5_HAVE_SPI2 bool default n +config SAMA5_HAVE_TC + bool + default n + config SAMA5_HAVE_TC1 bool default n @@ -369,16 +373,19 @@ config SAMA5_SPI2 config SAMA5_TC0 bool "Timer Counter 0 (ch. 0, 1, 2) (TC0)" default n + select SAMA5_HAVE_TC config SAMA5_TC1 bool "Timer Counter 1 (ch. 3, 4, 5) (TC1)" - depends on SAMA5_HAVE_TC1 default n + depends on SAMA5_HAVE_TC1 + select SAMA5_HAVE_TC config SAMA5_TC2 bool "Timer Counter 2 (ch. 6, 7, 8) (TC2)" - depends on SAMA5_HAVE_TC2 default n + depends on SAMA5_HAVE_TC2 + select SAMA5_HAVE_TC config SAMA5_PWM bool "Pulse Width Modulation Controller (PWM)" @@ -3237,7 +3244,7 @@ config SAMA5_ADC_ADTRG A-to-D Conversion is initiated an event on the ADTRG pin. config SAMA5_ADC_TIOATRIG - bool "TC0 ouput A trigger" + bool "TC0 output A trigger" depends on SAMA5_TC0 ---help--- A-to-D Conversion is initiated the A output from one of @@ -3340,140 +3347,6 @@ config SAMA5_ADC_REGDEBUG endmenu # ADC Configuration -if SAMA5_TC0 || SAMA5_TC1 || SAMA5_TC2 -menu "Timer/counter Configuration" - -if SAMA5_TC0 - -config SAMA5_TC0_CLK0 - bool "Enable TC0 channel 0 clock input pin" - default n - -config SAMA5_TC0_TIOA0 - bool "Enable TC0 channel 0 ouput A" - default n - -config SAMA5_TC0_TIOB0 - bool "Enable TC0 channel 0 ouput B" - default n - -config SAMA5_TC0_CLK1 - bool "Enable TC0 channel 1 clock input pin" - default n - -config SAMA5_TC0_TIOA1 - bool "Enable TC0 channel 1 ouput A" - default n - -config SAMA5_TC0_TIOB1 - bool "Enable TC0 channel 1 ouput B" - default n - -config SAMA5_TC0_CLK2 - bool "Enable TC0 channel 2 clock input pin" - default n - -config SAMA5_TC0_TIOA2 - bool "Enable TC0 channel 2 ouput A" - default n - -config SAMA5_TC0_TIOB2 - bool "Enable TC0 channel 2 ouput B" - default n - -endif # SAMA5_TC0 - -if SAMA5_TC1 - -config SAMA5_TC1_CLK3 - bool "Enable TC1 channel 3 clock input pin" - default n - -config SAMA5_TC1_TIOA3 - bool "Enable TC1 channel 3 ouput A" - default n - -config SAMA5_TC1_TIOB3 - bool "Enable TC1 channel 3 ouput B" - default n - -config SAMA5_TC1_CLK4 - bool "Enable TC1 channel 4 clock input pin" - default n - -config SAMA5_TC1_TIOA4 - bool "Enable TC1 channel 4 ouput A" - default n - -config SAMA5_TC1_TIOB4 - bool "Enable TC1 channel 4 ouput B" - default n - -config SAMA5_TC1_CLK5 - bool "Enable TC1 channel 5 clock input pin" - default n - -config SAMA5_TC1_TIOA5 - bool "Enable TC1 channel 5 ouput A" - default n - -config SAMA5_TC1_TIOB5 - bool "Enable TC1 channel 5 ouput B" - default n - -endif # SAMA5_TC1 - -if SAMA5_TC2 - -config SAMA5_TC2_CLK6 - bool "Enable TC2 channel 6 clock input pin" - default n - -config SAMA5_TC2_TIOA6 - bool "Enable TC2 channel 6 ouput A" - default n - -config SAMA5_TC2_TIOB6 - bool "Enable TC2 channel 6 ouput B" - default n - -config SAMA5_TC2_CLK7 - bool "Enable TC2 channel 7 clock input pin" - default n - -config SAMA5_TC2_TIOA7 - bool "Enable TC2 channel 7 ouput A" - default n - -config SAMA5_TC2_TIOB7 - bool "Enable TC2 channel 7 ouput B" - default n - -config SAMA5_TC2_CLK8 - bool "Enable TC2 channel 8 clock input pin" - default n - -config SAMA5_TC2_TIOA8 - bool "Enable TC2 channel 8 ouput A" - default n - -config SAMA5_TC2_TIOB8 - bool "Enable TC2 channel 8 ouput B" - default n - -endif # SAMA5_TC2 - -config SAMA5_TC_REGDEBUG - bool "TC Register level debug" - depends on DEBUG - default n - ---help--- - Output detailed register-level Timer/Counter device debug information. - Very invasive! Requires also DEBUG. - -endmenu # Timer/counter Configuration -endif # SAMA5_TC0 || SAMA5_TC1 || SAM_TC2 - menu "Touchscreen configuration" config SAMA5_TSD @@ -3548,6 +3421,156 @@ endif # SAMA5_TSD endmenu # Touchscreen Configuration endif # SAMA5_ADC +if SAMA5_HAVE_TC +menu "Timer/counter Configuration" + +if SAMA5_TC0 + +config SAMA5_TC0_CLK0 + bool "Enable TC0 channel 0 clock input pin" + default n + +config SAMA5_TC0_TIOA0 + bool "Enable TC0 channel 0 output A" + default n + +config SAMA5_TC0_TIOB0 + bool "Enable TC0 channel 0 output B" + default n + +config SAMA5_TC0_CLK1 + bool "Enable TC0 channel 1 clock input pin" + default n + +config SAMA5_TC0_TIOA1 + bool "Enable TC0 channel 1 output A" + default n + +config SAMA5_TC0_TIOB1 + bool "Enable TC0 channel 1 output B" + default n + +config SAMA5_TC0_CLK2 + bool "Enable TC0 channel 2 clock input pin" + default n + +config SAMA5_TC0_TIOA2 + bool "Enable TC0 channel 2 output A" + default n + +config SAMA5_TC0_TIOB2 + bool "Enable TC0 channel 2 output B" + default n + +endif # SAMA5_TC0 + +if SAMA5_TC1 + +config SAMA5_TC1_CLK3 + bool "Enable TC1 channel 3 clock input pin" + default n + +config SAMA5_TC1_TIOA3 + bool "Enable TC1 channel 3 output A" + default n + +config SAMA5_TC1_TIOB3 + bool "Enable TC1 channel 3 output B" + default n + +config SAMA5_TC1_CLK4 + bool "Enable TC1 channel 4 clock input pin" + default n + +config SAMA5_TC1_TIOA4 + bool "Enable TC1 channel 4 output A" + default n + +config SAMA5_TC1_TIOB4 + bool "Enable TC1 channel 4 output B" + default n + +config SAMA5_TC1_CLK5 + bool "Enable TC1 channel 5 clock input pin" + default n + +config SAMA5_TC1_TIOA5 + bool "Enable TC1 channel 5 output A" + default n + +config SAMA5_TC1_TIOB5 + bool "Enable TC1 channel 5 output B" + default n + +endif # SAMA5_TC1 + +if SAMA5_TC2 + +config SAMA5_TC2_CLK6 + bool "Enable TC2 channel 6 clock input pin" + default n + +config SAMA5_TC2_TIOA6 + bool "Enable TC2 channel 6 output A" + default n + +config SAMA5_TC2_TIOB6 + bool "Enable TC2 channel 6 output B" + default n + +config SAMA5_TC2_CLK7 + bool "Enable TC2 channel 7 clock input pin" + default n + +config SAMA5_TC2_TIOA7 + bool "Enable TC2 channel 7 output A" + default n + +config SAMA5_TC2_TIOB7 + bool "Enable TC2 channel 7 output B" + default n + +config SAMA5_TC2_CLK8 + bool "Enable TC2 channel 8 clock input pin" + default n + +config SAMA5_TC2_TIOA8 + bool "Enable TC2 channel 8 output A" + default n + +config SAMA5_TC2_TIOB8 + bool "Enable TC2 channel 8 output B" + default n + +endif # SAMA5_TC2 + +config SAMA5_ONESHOT + bool "TC one-shot wrapper" + default n if !SCHED_TICKLESS + default y if SCHED_TICKLESS + ---help--- + Enable a wrapper around the low level timer/counter functions to + support one-shot timer. + +config SAMA5_TC_DEBUG + bool "TC debug" + depends on DEBUG + default n + ---help--- + Output high level Timer/Counter device debug information. + Requires also DEBUG. + +config SAMA5_TC_REGDEBUG + bool "TC register level debug" + depends on DEBUG + default n + ---help--- + Output detailed register-level Timer/Counter device debug + information. Very invasive! Requires also DEBUG. + +endmenu # Timer/counter Configuration +endif # SAMA5_HAVE_TC + if SAMA5_PWM menu "PWM configuration" diff --git a/arch/arm/src/sama5/Make.defs b/arch/arm/src/sama5/Make.defs index 363327871a..4bffa4e481 100644 --- a/arch/arm/src/sama5/Make.defs +++ b/arch/arm/src/sama5/Make.defs @@ -250,11 +250,10 @@ ifeq ($(CONFIG_SAMA5_PWM),y) CHIP_CSRCS += sam_pwm.c endif -ifeq ($(CONFIG_SAMA5_TC0),y) -CHIP_CSRCS += sam_tc.c -else -ifeq ($(CONFIG_SAMA5_TC1),y) +ifeq ($(CONFIG_SAMA5_HAVE_TC),y) CHIP_CSRCS += sam_tc.c +ifeq ($(CONFIG_SAMA5_ONESHOT),y) +CHIP_CSRCS += sam_oneshot.c endif endif diff --git a/arch/arm/src/sama5/chip/sam_tc.h b/arch/arm/src/sama5/chip/sam_tc.h index d53665f266..46943023e3 100644 --- a/arch/arm/src/sama5/chip/sam_tc.h +++ b/arch/arm/src/sama5/chip/sam_tc.h @@ -428,7 +428,7 @@ # define TC_CMR_BURST_NONE (0 << TC_CMR_BURST_SHIFT) /* Clock not gated by external signal */ # define TC_CMR_BURST_XC0 (1 << TC_CMR_BURST_SHIFT) /* XXC0 ANDed with clock */ # define TC_CMR_BURST_XC1 (2 << TC_CMR_BURST_SHIFT) /* XC1 ANDed with clock */ -# define TC_CMR_BURST_XC2 (3 << TC_CMR_BURST_SHIFT) /* XC2 ANDed with clock */ +# define TC_CMR_BURST_XC2 (3 << TC_CMR_BURST_SHIFT) /* XC2 ANDed with clock */ /* Channel Mode Register -- Capture mode */ diff --git a/arch/arm/src/sama5/sam_adc.c b/arch/arm/src/sama5/sam_adc.c index a2b2b460e3..90e8e8ddb0 100644 --- a/arch/arm/src/sama5/sam_adc.c +++ b/arch/arm/src/sama5/sam_adc.c @@ -1229,10 +1229,12 @@ static int sam_adc_ioctl(struct adc_dev_s *dev, int cmd, unsigned long arg) static int sam_adc_settimer(struct sam_adc_s *priv, uint32_t frequency, int channel) { + uint32_t ftc; uint32_t div; uint32_t tcclks; uint32_t mode; uint32_t fdiv; + uint32_t regval; int ret; avdbg("frequency=%ld channel=%d\n", (long)frequency, channel); @@ -1267,7 +1269,7 @@ static int sam_adc_settimer(struct sam_adc_s *priv, uint32_t frequency, } /* The divider returned by sam_tc_divisor() is the reload value that will - * achieve a 1HZ rate. We need to multiply this to get the desired + * achieve a 1Hz rate. We need to multiply this to get the desired * frequency. sam_tc_divisor() should have already assure that we can * do this without overflowing a 32-bit unsigned integer. */ @@ -1275,12 +1277,18 @@ static int sam_adc_settimer(struct sam_adc_s *priv, uint32_t frequency, fdiv = div * frequency; DEBUGASSERT(div > 0 && div <= fdiv); /* Will check for integer overflow */ + /* Calculate the actual counter value from this divider and the tc input + * frequency. + */ + + regval = sam_tc_frequency() / fdiv; + /* Set up TC_RA and TC_RC. The frequency is determined by RA and RC: TIOA is * cleared on RA match; TIOA is set on RC match. */ - sam_tc_setregister(priv->tc, TC_REGA, fdiv << 1); - sam_tc_setregister(priv->tc, TC_REGC, fdiv); + sam_tc_setregister(priv->tc, TC_REGA, regval >> 1); + sam_tc_setregister(priv->tc, TC_REGC, regval); /* And start the timer */ diff --git a/arch/arm/src/sama5/sam_oneshot.c b/arch/arm/src/sama5/sam_oneshot.c new file mode 100644 index 0000000000..509a312656 --- /dev/null +++ b/arch/arm/src/sama5/sam_oneshot.c @@ -0,0 +1,368 @@ +/**************************************************************************** + * arch/arm/src/sama5/sam_oneshot.c + * + * Copyright (C) 2014 Gregory Nutt. All rights reserved. + * Author: Gregory Nutt + * + * References: + * + * SAMA5D3 Series Data Sheet + * Atmel NoOS sample code. + * + * The Atmel sample code has a BSD compatible license that requires this + * copyright notice: + * + * Copyright (c) 2011, Atmel Corporation + * + * 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 names NuttX nor Atmel 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. + * + ****************************************************************************/ + +/**************************************************************************** + * Included Files + ****************************************************************************/ + +#include + +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +#include "sam_oneshot.h" + +#ifdef CONFIG_SAMA5_ONESHOT + +/**************************************************************************** + * Pre-processor Definitions + ****************************************************************************/ + +#ifdef CONFIG_SAMA5_TC_DEBUG +# define tcdbg dbg +# define tcvdbg vdbg +#else +# define tcdbg(x...) +# define tcvdbg(x...) +#endif + +/**************************************************************************** + * Private Types + ****************************************************************************/ + +/**************************************************************************** + * Private Function Prototypes + ****************************************************************************/ + +/**************************************************************************** + * Private Data + ****************************************************************************/ + +/**************************************************************************** + * Private Functions + ****************************************************************************/ +/**************************************************************************** + * Name: sam_oneshot_handler + * + * Description: + * Timer interrupt callback. When the oneshot timer interrupt expires, + * this function will be called. It will forward the call to the next + * level up. + * + * Input Parameters: + * handle - The handle that represents the timer state + * arg - An opaque argument provided when the interrupt was registered + * sr - The value of the timer interrupt status register at the time + * that the interrupt occurred. + * + * Returned Value: + * None + * + ****************************************************************************/ + +static void sam_oneshot_handler(TC_HANDLE handle, void *arg, uint32_t sr) +{ + struct sam_oneshot_s *oneshot = (struct sam_oneshot_s *)arg; + DEBUGASSERT(oneshot && oneshot->handler); + + /* The clock was stopped, but not disabled when the RC match occurred. + * Disable the TC now and disable any further interrupts. + */ + + sam_tc_attach(oneshot->handler, NULL, NULL, 0); + sam_tc_stop(oneshot->handle); + + /* Forward the event */ + + oneshot->running = false; + oneshot->handler(oneshot->arg); +} + +/**************************************************************************** + * Public Functions + ****************************************************************************/ + +/**************************************************************************** + * Name: sam_oneshot_initialize + * + * Description: + * Initialize the oneshot timer wrapper + * + * Input Parameters: + * oneshot Caller allocated instance of the oneshot state structure + * chan Timer counter channel to be used. See the TC_CHAN* + * definitions in arch/arm/src/sama5/sam_tc.h. + * resolution The required resolution of the timer in units of + * microseconds. NOTE that the range is restricted to the + * range of uint16_t (excluding zero). + * + * Returned Value: + * Zero (OK) is returned on success; a negated errno value is returned + * on failure. + * + ****************************************************************************/ + +int sam_oneshot_initialize(struct sam_oneshot_s *oneshot, int chan, + uint16_t resolution) +{ + uint32_t frequency; + uint32_t cmr; + int ret; + + tcvdbg("chan=%d resolution=%d usec\n", chan, resolution); + DEBUGASSERT(oneshot && resolution > 0); + + /* Get the TC frequency the corresponds to the requested resolution */ + + frequency = 1000000 / (uint32_t)resolution; + + /* The pre-calculate values to use when we start the timer */ + + ret = sam_tc_divisor(frequency, &oneshot->divisor, &cmr); + if (ret < 0) + { + tcdbg("ERROR: sam_tc_divisor failed: %d\n", ret); + return ret; + } + + tcvdbg("frequency=%lu, divisor=%u, cmr=%08lx\n", + (unsigned long)frequency, oneshot->divisor, (unsigned long)cmr); + + /* Allocate the timer/counter and select its mode of operation + * + * CMR_TCCLKS - Returned by sam_tc_divisor + * TC_CMR_CLKI=0 - Not inverted + * TC_CMR_BURST_NONE - Not gated by an external signal + * TC_CMR_CPCSTOP=1 - Stop the clock on an RC compare event + * TC_CMR_CPCDIS=0 - Don't disable the clock on an RC compare event + * TC_CMR_EEVTEDG_NONE - No external events (and, hence, no edges + * TC_CMR_EEVT_TIOB - ???? REVISIT + * TC_CMR_ENET=0 - External event trigger disabled + * TC_CMR_WAVSEL_UPRC - TC_CV is incremented from 0 to the value of RC, + * then automatically reset on a RC Compare + * TC_CMR_WAVE - Waveform mode + * TC_CMR_ACPA_NONE - RA compare has no effect on TIOA + * TC_CMR_ACPC_NONE - RC compare has no effect on TIOA + * TC_CMR_AEEVT_NONE - No external event effect on TIOA + * TC_CMR_ASWTRG_NONE - No software trigger effect on TIOA + * TC_CMR_BCPB_NONE - RB compare has no effect on TIOB + * TC_CMR_BCPC_NONE - RC compare has no effect on TIOB + * TC_CMR_BEEVT_NONE - No external event effect on TIOB + * TC_CMR_BSWTRG_NONE - No software trigger effect on TIOB + */ + + cmr |= (TC_CMR_BURST_NONE | TC_CMR_CPCSTOP | TC_CMR_EEVTEDG_NONE | + TC_CMR_EEVT_TIOB | TC_CMR_WAVSEL_UPRC | TC_CMR_WAVE | + TC_CMR_ACPA_NONE | TC_CMR_ACPC_NONE | TC_CMR_AEEVT_NONE | + TC_CMR_ASWTRG_NONE | TC_CMR_BCPB_NONE | TC_CMR_BCPC_NONE | + TC_CMR_BEEVT_NONE | TC_CMR_BSWTRG_NONE); + + oneshot->handle = sam_tc_allocate(chan, cmr); + if (!oneshot->handle) + { + tcdbg("ERROR: Failed to allocate timer channel %d\n", chan); + return -EBUSY; + } + + /* Initialize the remaining fields in the state structure and return + * success. + */ + + oneshot->chan = chan; + oneshot->running = false; + oneshot->resolution = resolution; + oneshot->handler = NULL; + oneshot->arg = NULL; + return OK; +} + +/**************************************************************************** + * Name: sam_oneshot_start + * + * Description: + * Start the oneshot timer + * + * Input Parameters: + * oneshot Caller allocated instance of the oneshot state structure. This + * structure must have been previously initialized via a call to + * sam_oneshot_initialize(); + * handler The function to call when when the oneshot timer expires. + * arg An opaque argument that will accompany the callback. + * ts Provides the duration of the one shot timer. + * + * Returned Value: + * Zero (OK) is returned on success; a negated errno value is returned + * on failure. + * + ****************************************************************************/ + +int sam_oneshot_start(struct sam_oneshot_s *oneshot, oneshot_handler_t handler, + void *arg, const struct timespec *ts) +{ + uint64_t usec; + uint64_t regval; + irqstate_t flags; + + tcvdbg("handler=%p arg=%p, ts=(%lu, %lu)\n", + handler, arg, (unsigned long)ts->tv_sec, (unsigned long)ts->tv_nsec); + DEBUGASSERT(oneshot && handler && ts); + + /* Was the oneshot already running? */ + + flags = irqsave(); + if (oneshot->running) + { + /* Yes.. then cancel it */ + + (void)sam_oneshot_cancel(oneshot, NULL); + } + + /* We configured the counter to run with an LSB of the specified + * resolution. We now must need need to set RC to the number + * of resolution units corresponding to the requested delay. + */ + + usec = (uint64_t)ts->tv_sec * 1000000 + (uint64_t)(ts->tv_nsec / 1000); + regval = usec / oneshot->resolution; + + tcdbg("usec=%llu regval=%08llx\n", usec, regval); + DEBUGASSERT(regval <= UINT32_MAX); + + /* Set up to receive the callback when the interrupt occurs */ + + (void)sam_tc_attach(oneshot->handle, sam_oneshot_handler, oneshot, + TC_INT_CPCS); + + /* Set RC so that an event will be triggered when TC_CV register counts + * up to RC. + */ + + sam_tc_setregister(oneshot->handle, TC_REGC, regval); + + /* Enable interrupts. We should get the callback when the interrupt + * occurs. + */ + + oneshot->running = true; + irqrestore(flags); + return OK; +} + +/**************************************************************************** + * Name: sam_oneshot_cancel + * + * Description: + * Cancel the oneshot timer and return the time remaining on the timer. + * + * Input Parameters: + * oneshot Caller allocated instance of the oneshot state structure. This + * structure must have been previously initialized via a call to + * sam_oneshot_initialize(); + * ts The location in which to return the time remaining on the + * oneshot timer. + * + * Returned Value: + * Zero (OK) is returned on success; a negated errno value is returned + * on failure. + * + ****************************************************************************/ + +int sam_oneshot_cancel(struct sam_oneshot_s *oneshot, struct timespec *ts) +{ + irqstate_t flags; + uint32_t count; + uint32_t rc; + uint32_t usec; + uint32_t sec; + + /* Get the timer counter and rc registers and stop the counter. If the + * counter expires while we are doing this, the counter clock will be + * stopped, but the clock will not be disabled. + * + * REVISIT: Will the counter value be reset to zero? + */ + + flags = irqsave(); + count = sam_tc_getcounter(oneshot->handle); + rc = sam_tc_getregister(oneshot->handle, TC_REGC); + + /* Now we can disable the interrupt and stop the clock */ + + sam_tc_attach(oneshot->handle, NULL, NULL, 0); + sam_tc_stop(oneshot->handle); + + oneshot->running = false; + irqrestore(flags); + + /* The total time remaining is the difference */ + + DEBUGASSERT(rc >= count); + usec = (rc - count) * oneshot->resolution; + + tcdbg("rc=%lu count=%lu resolution=%u usec=%lu\n", + (unsigned long)rc, (unsigned long)count, oneshot->resolution, + (unsigned long)usec); + + /* Return the time remaining in the correct form */ + + sec = usec / 1000000; + ts->tv_sec = sec; + ts->tv_nsec = ((usec) - (sec * 1000000)) / 1000; + + tcvdbg("remaining (%lu, %lu)\n", + (unsigned long)ts->tv_sec, (unsigned long)ts->tv_nsec); + return OK; +} + +#endif /* CONFIG_SAMA5_ONESHOT */ diff --git a/arch/arm/src/sama5/sam_oneshot.h b/arch/arm/src/sama5/sam_oneshot.h new file mode 100644 index 0000000000..954ea48551 --- /dev/null +++ b/arch/arm/src/sama5/sam_oneshot.h @@ -0,0 +1,176 @@ +/**************************************************************************** + * arch/arm/src/sama5/sam_oneshot.h + * + * Copyright (C) 2014 Gregory Nutt. All rights reserved. + * Author: Gregory Nutt + * + * 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. + * + ****************************************************************************/ + +#ifndef __ARCH_ARM_SRC_SAMA5_SAM_ONESHOT_H +#define __ARCH_ARM_SRC_SAMA5_SAM_ONESHOT_H + +/**************************************************************************** + * Included Files + ****************************************************************************/ + +#include + +#include +#include + +#include "sam_tc.h" + +#ifdef CONFIG_SAMA5_ONESHOT + +/**************************************************************************** + * Pre-processor Definitions + ****************************************************************************/ + +/**************************************************************************** + * Public Types + ****************************************************************************/ + +/* This describes the callback function that will be invoked when the oneshot + * timer expires. The oneshot fires, the client will receive: + * + * arg - The opaque argument provided when the interrupt was registered + */ + +typedef void (*oneshot_handler_t)(void *arg); + +/* The oneshot client must allocate an instance of this structure and called + * sam_oneshot_initialize() before using the oneshot facilities. The client + * should not access the contents of this structure directly since the + * contents are subject to change. + */ + +struct sam_oneshot_s +{ + uint8_t chan; /* The timer/counter in use */ + bool running; /* True: the timer is running */ + uint16_t resolution; /* Timer resolution in microseconds */ + uint32_t divisor; /* TC divisor derived from resolution */ + TC_HANDLE handle; /* Handle returned by sam_tc_initialize() */ + oneshot_handler_t handler; /* Oneshot expiration callback */ + void *arg; /* The argument that will accompany the callback */ +}; + +/**************************************************************************** + * Public Data + ****************************************************************************/ + +#undef EXTERN +#if defined(__cplusplus) +#define EXTERN extern "C" +extern "C" +{ +#else +#define EXTERN extern +#endif + +/**************************************************************************** + * Public Function Prototypes + ****************************************************************************/ + +/**************************************************************************** + * Name: sam_oneshot_initialize + * + * Description: + * Initialize the oneshot timer wrapper + * + * Input Parameters: + * oneshot Caller allocated instance of the oneshot state structure + * chan Timer counter channel to be used. See the TC_CHAN* + * definitions in arch/arm/src/sama5/sam_tc.h. + * resolution The required resolution of the timer in units of + * microseconds. NOTE that the range is restricted to the + * range of uint16_t (excluding zero). + * + * Returned Value: + * Zero (OK) is returned on success; a negated errno value is returned + * on failure. + * + ****************************************************************************/ + +int sam_oneshot_initialize(struct sam_oneshot_s *oneshot, int tc, + uint16_t resolution); + +/**************************************************************************** + * Name: sam_oneshot_start + * + * Description: + * Start the oneshot timer + * + * Input Parameters: + * oneshot Caller allocated instance of the oneshot state structure. This + * structure must have been previously initialized via a call to + * sam_oneshot_initialize(); + * handler The function to call when when the oneshot timer expires. + * arg An opaque argument that will accompany the callback. + * ts Provides the duration of the one shot timer. + * + * Returned Value: + * Zero (OK) is returned on success; a negated errno value is returned + * on failure. + * + ****************************************************************************/ + +int sam_oneshot_start(struct sam_oneshot_s *oneshot, oneshot_handler_t handler, + void *arg, const struct timespec *ts); + +/**************************************************************************** + * Name: sam_oneshot_cancel + * + * Description: + * Cancel the oneshot timer and return the time remaining on the timer. + * + * Input Parameters: + * oneshot Caller allocated instance of the oneshot state structure. This + * structure must have been previously initialized via a call to + * sam_oneshot_initialize(); + * ts The location in which to return the time remaining on the + * oneshot timer. + * + * Returned Value: + * Zero (OK) is returned on success; a negated errno value is returned + * on failure. + * + ****************************************************************************/ + +int sam_oneshot_cancel(struct sam_oneshot_s *oneshot, struct timespec *ts); + +#undef EXTERN +#ifdef __cplusplus +} +#endif + +#endif /* CONFIG_SAMA5_ONESHOT */ +#endif /* __ARCH_ARM_SRC_SAMA5_SAM_ONESHOT_H */ + diff --git a/arch/arm/src/sama5/sam_tc.c b/arch/arm/src/sama5/sam_tc.c index c4a89aef7b..778b667940 100644 --- a/arch/arm/src/sama5/sam_tc.c +++ b/arch/arm/src/sama5/sam_tc.c @@ -1,7 +1,7 @@ /**************************************************************************** * arch/arm/src/sama5/sam_tc.c * - * Copyright (C) 2013 Gregory Nutt. All rights reserved. + * Copyright (C) 2013-2014 Gregory Nutt. All rights reserved. * Author: Gregory Nutt * * References: @@ -82,12 +82,12 @@ # undef CONFIG_SAMA5_TC_REGDEBUG #endif -#undef DEBUG_TC +#undef CONFIG_SAMA5_TC_DEBUG #if defined(CONFIG_SAMA5_ADC) && defined(CONFIG_DEBUG_ANALOG) -# define DEBUG_TC 1 +# define CONFIG_SAMA5_TC_DEBUG 1 #endif -#ifdef DEBUG_TC +#ifdef CONFIG_SAMA5_TC_DEBUG # define tcdbg dbg # define tcvdbg vdbg #else @@ -1167,8 +1167,11 @@ void sam_tc_free(TC_HANDLE handle) tcvdbg("Freeing %p channel=%d inuse=%d\n", chan, chan->chan, chan->inuse); DEBUGASSERT(chan && chan->inuse); - /* Make sure that the channel is stopped */ + /* Make sure that interrupts are detached and disabled and that the channel + * is stopped and disabled. + */ + sam_tc_attach(handle, NULL, NULL, 0); sam_tc_stop(handle); /* Mark the channel as available */ @@ -1255,6 +1258,28 @@ tc_handler_t sam_tc_attach(TC_HANDLE handle, tc_handler_t handler, return oldhandler; } +/**************************************************************************** + * Name: sam_tc_pending + * + * Description: + * Return the current contents of the interrutp status register, clearing + * all pending interrupts. + * + * Input Parameters: + * handle The handle that represents the timer state + * + * Returned Value: + * The value of the channel interrupt status register. + * + ****************************************************************************/ + +uint32_t sam_tc_pending(TC_HANDLE handle) +{ + struct sam_chan_s *chan = (struct sam_chan_s *)handle; + DEBUGASSERT(chan); + return sam_chan_getreg(chan, SAM_TC_SR_OFFSET); +} + /**************************************************************************** * Name: sam_tc_stop * @@ -1283,36 +1308,74 @@ void sam_tc_stop(TC_HANDLE handle) * Name: sam_tc_setregister * * Description: - * Set TC_RA, TC_RB, or TC_RB using the provided divisor. The actual - * setting in the register will be the TC input frequency (Ftc) divided by - * the provided divider (which should derive from the divider returned - * by sam_tc_divisor). + * Set TC_REGA, TC_REGB, or TC_REGC register. * * Input Parameters: * handle Channel handle previously allocated by sam_tc_allocate() + * regid One of {TC_REGA, TC_REGB, or TC_REGC} + * regval Then value to set in the register * * Returned Value: * None * ****************************************************************************/ -void sam_tc_setregister(TC_HANDLE handle, int reg, unsigned int div) +void sam_tc_setregister(TC_HANDLE handle, int regid, uint32_t regval) { struct sam_chan_s *chan = (struct sam_chan_s *)handle; - uint32_t frequency; - uint32_t regval; - DEBUGASSERT(reg < TC_NREGISTERS); + DEBUGASSERT(chan && regid < TC_NREGISTERS); - frequency = sam_tc_frequency(); - regval = frequency / div; - tcvdbg("Channel %d: Set register %d to %d / %d = %d\n", - chan->chan, reg, frequency, div, (unsigned int)regval); + tcvdbg("Channel %d: Set register RC%d to 0x08lx\n", + chan->chan, regid, (unsigned long)regval); - sam_chan_putreg(chan, g_regoffset[reg], regval); + sam_chan_putreg(chan, g_regoffset[regid], regval); sam_regdump(chan, "Set register"); } +/**************************************************************************** + * Name: sam_tc_getregister + * + * Description: + * Get the current value of the TC_REGA, TC_REGB, or TC_REGC register. + * + * Input Parameters: + * handle Channel handle previously allocated by sam_tc_allocate() + * regid One of {TC_REGA, TC_REGB, or TC_REGC} + * + * Returned Value: + * The value of the specified register. + * + ****************************************************************************/ + +uint32_t sam_tc_getregister(TC_HANDLE handle, int regid) +{ + struct sam_chan_s *chan = (struct sam_chan_s *)handle; + DEBUGASSERT(chan); + return sam_chan_getreg(chan, g_regoffset[regid]); +} + +/**************************************************************************** + * Name: sam_tc_getcounter + * + * Description: + * Return the current value of the timer counter register + * + * Input Parameters: + * handle Channel handle previously allocated by sam_tc_allocate() + * + * Returned Value: + * The current value of the timer counter register for this channel. + * + ****************************************************************************/ + +uint32_t sam_tc_getcounter(TC_HANDLE handle) +{ + struct sam_chan_s *chan = (struct sam_chan_s *)handle; + DEBUGASSERT(chan); + return sam_chan_getreg(chan, SAM_TC_CV_OFFSET); +} + /**************************************************************************** * Name: sam_tc_frequency * diff --git a/arch/arm/src/sama5/sam_tc.h b/arch/arm/src/sama5/sam_tc.h index b767158ef2..65f7bbeaed 100644 --- a/arch/arm/src/sama5/sam_tc.h +++ b/arch/arm/src/sama5/sam_tc.h @@ -1,7 +1,7 @@ /**************************************************************************** - * arch/arm/src/sama5/sam_adc.h + * arch/arm/src/sama5/sam_tc.h * - * Copyright (C) 2013 Gregory Nutt. All rights reserved. + * Copyright (C) 2013-2014 Gregory Nutt. All rights reserved. * Author: Gregory Nutt * * Redistribution and use in source and binary forms, with or without @@ -47,7 +47,7 @@ #include "chip.h" #include "chip/sam_tc.h" -#if defined(CONFIG_SAMA5_TC0) || defined(CONFIG_SAMA5_TC1) +#if defined(CONFIG_SAMA5_TC0) || defined(CONFIG_SAMA5_TC1) || defined(CONFIG_SAMA5_TC2) /**************************************************************************** * Pre-processor Definitions @@ -78,7 +78,7 @@ typedef void *TC_HANDLE; -/* Timer interrupt callback. When a timer interrup expires, the client will +/* Timer interrupt callback. When a timer interrupt expires, the client will * receive: * * handle - The handle that represents the timer state @@ -186,6 +186,23 @@ tc_handler_t sam_tc_attach(TC_HANDLE handle, tc_handler_t handler, #define sam_tc_detach(h) sam_tc_attach(h, NULL, NULL, 0) +/**************************************************************************** + * Name: sam_tc_pending + * + * Description: + * Return the current contents of the interrutp status register, clearing + * all pending interrupts. + * + * Input Parameters: + * handle The handle that represents the timer state + * + * Returned Value: + * The value of the channel interrupt status register. + * + ****************************************************************************/ + +uint32_t sam_tc_pending(TC_HANDLE handle); + /**************************************************************************** * Name: sam_tc_stop * @@ -205,21 +222,52 @@ void sam_tc_stop(TC_HANDLE handle); * Name: sam_tc_setregister * * Description: - * Set TC_RA, TC_RB, or TC_RB using the provided divisor. The actual - * setting in the register will be the TC input frequency divided by - * the provided divider (which should derive from the divider returned - * by sam_tc_divisor). - * + * Set TC_REGA, TC_REGB, or TC_REGC register. * * Input Parameters: * handle Channel handle previously allocated by sam_tc_allocate() + * regid One of {TC_REGA, TC_REGB, or TC_REGC} + * regval Then value to set in the register * * Returned Value: * None * ****************************************************************************/ -void sam_tc_setregister(TC_HANDLE handle, int reg, unsigned int div); +void sam_tc_setregister(TC_HANDLE handle, int regid, uint32_t regval); + +/**************************************************************************** + * Name: sam_tc_getregister + * + * Description: + * Get the current value of the TC_REGA, TC_REGB, or TC_REGC register. + * + * Input Parameters: + * handle Channel handle previously allocated by sam_tc_allocate() + * regid One of {TC_REGA, TC_REGB, or TC_REGC} + * + * Returned Value: + * The value of the specified register. + * + ****************************************************************************/ + +uint32_t sam_tc_getregister(TC_HANDLE handle, int regid); + +/**************************************************************************** + * Name: sam_tc_getcounter + * + * Description: + * Return the current value of the timer counter register + * + * Input Parameters: + * handle Channel handle previously allocated by sam_tc_allocate() + * + * Returned Value: + * The current value of the timer counter register for this channel. + * + ****************************************************************************/ + +uint32_t sam_tc_getcounter(TC_HANDLE handle); /**************************************************************************** * Name: sam_tc_frequency @@ -245,7 +293,7 @@ uint32_t sam_tc_frequency(void); * Finds the best MCK divisor given the timer frequency and MCK. The * result is guaranteed to satisfy the following equation: * - * (Ftc / (div * 65536)) <= freq <= (Ftc / dev) + * (Ftc / (div * 65536)) <= freq <= (Ftc / div) * * where: * freq - the desired frequency @@ -270,6 +318,6 @@ int sam_tc_divisor(uint32_t frequency, uint32_t *div, uint32_t *tcclks); } #endif -#endif /* CONFIG_SAMA5_TC0 || CONFIG_SAMA5_TC1 */ +#endif /* CONFIG_SAMA5_TC0 || CONFIG_SAMA5_TC1 || CONFIG_SAMA5_TC2 */ #endif /* __ARCH_ARM_SRC_SAMA5_SAM_TC_H */