diff options
Diffstat (limited to 'nuttx/arch/arm/src/sama5/sam_freerun.c')
-rw-r--r-- | nuttx/arch/arm/src/sama5/sam_freerun.c | 333 |
1 files changed, 333 insertions, 0 deletions
diff --git a/nuttx/arch/arm/src/sama5/sam_freerun.c b/nuttx/arch/arm/src/sama5/sam_freerun.c new file mode 100644 index 000000000..514e22778 --- /dev/null +++ b/nuttx/arch/arm/src/sama5/sam_freerun.c @@ -0,0 +1,333 @@ +/**************************************************************************** + * arch/arm/src/sama5/sam_freerun.c + * + * Copyright (C) 2014 Gregory Nutt. All rights reserved. + * Author: Gregory Nutt <gnutt@nuttx.org> + * + * 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 <nuttx/config.h> + +#include <sys/types.h> +#include <stdint.h> +#include <stdbool.h> +#include <assert.h> +#include <errno.h> +#include <debug.h> + +#include <arch/irq.h> + +#include "sam_freerun.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_freerun_handler + * + * Description: + * Timer interrupt callback. When the freerun timer counter overflows, + * this interrupt will occur. We will just increment an overflow count. + * + * 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_freerun_handler(TC_HANDLE handle, void *arg, uint32_t sr) +{ + struct sam_freerun_s *freerun = (struct sam_freerun_s *)arg; + DEBUGASSERT(freerun && freerun->overflow < UINT16_MAX); + freerun->overflow++; +} + +/**************************************************************************** + * Public Functions + ****************************************************************************/ + +/**************************************************************************** + * Name: sam_freerun_initialize + * + * Description: + * Initialize the freerun timer wrapper + * + * Input Parameters: + * freerun Caller allocated instance of the freerun 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_freerun_initialize(struct sam_freerun_s *freerun, int chan, + uint16_t resolution) +{ + uint32_t frequency; + uint32_t divisor; + uint32_t cmr; + int ret; + + tcvdbg("chan=%d resolution=%d usec\n", chan, resolution); + DEBUGASSERT(freerun && 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, &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, (unsigned long)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=0 - Don't 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_UP - TC_CV is incremented from 0 to 0xffffffff + * 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_EEVTEDG_NONE | TC_CMR_EEVT_TIOB | + TC_CMR_WAVSEL_UP | 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); + + freerun->handle = sam_tc_allocate(chan, cmr); + if (!freerun->handle) + { + tcdbg("ERROR: Failed to allocate timer channel %d\n", chan); + return -EBUSY; + } + + /* Initialize the remaining fields in the state structure and return + * success. + */ + + freerun->chan = chan; + freerun->running = false; + freerun->resolution = resolution; + + /* Set up to receive the callback when the counter overflow occurs */ + + (void)sam_tc_attach(freerun->handle, sam_freerun_handler, freerun, + TC_INT_COVFS); + + /* Start the counter */ + + sam_tc_start(freerun->handle); + + return OK; +} + +/**************************************************************************** + * Name: sam_freerun_counter + * + * Description: + * Read the counter register of the free-running timer. + * + * Input Parameters: + * freerun Caller allocated instance of the freerun state structure. This + * structure must have been previously initialized via a call to + * sam_freerun_initialize(); + * ts The location in which to return the time from the free-running + * timer. + * + * Returned Value: + * Zero (OK) is returned on success; a negated errno value is returned + * on failure. + * + ****************************************************************************/ + +int sam_freerun_counter(struct sam_freerun_s *freerun, struct timespec *ts) +{ + uint64_t usec; + uint32_t counter; + uint32_t verify; + uint32_t sr; + uint32_t overflow; + uint32_t sec; + irqstate_t flags; + + /* Temporarily disable the overflow counter */ + + flags = irqsave(); + overflow = freerun->overflow; + counter = sam_tc_getcounter(freerun->handle); + sr = sam_tc_getpending(freerun->handle); + verify = sam_tc_getcounter(freerun->handle); + irqrestore(flags); + + tcvdbg("counter=%lu (%lu) overflow=%lu, sr=%08lx\n", + (unsigned long)counter, (unsigned long)verify, + (unsigned long)overflow, (unsigned long)sr); + + /* If an interrupt was pending before we re-enabled interrupts, + * then our value of overflow needs to be incremented. + */ + + if ((sr & TC_INT_COVFS) != 0) + { + /* Increment the overflow count and use the value of the + * guaranteed to be AFTER the overflow occurred. + */ + + overflow++; + counter = verify; + + tcvdbg("counter=%lu overflow=%lu\n", + (unsigned long)counter, (unsigned long)overflow); + } + + /* Convert the whole thing to units of microseconds */ + + usec = (((uint64_t)overflow << 32) + (uint64_t)counter) * + freerun->resolution; + + /* And return the value of the timer */ + + sec = (uint32_t)(usec / 1000000); + ts->tv_sec = sec; + ts->tv_nsec = (usec - (sec * 1000000)) * 1000; + + tcvdbg("usec=%016llx ts=(%lu, %lu)\n", + usec, (unsigned long)ts->tv_sec, (unsigned long)ts->tv_nsec); + + return OK; +} + +/**************************************************************************** + * Name: sam_freerun_uninitialize + * + * Description: + * Stop the free-running timer and release all resources that it uses. + * + * Input Parameters: + * freerun Caller allocated instance of the freerun state structure. This + * structure must have been previously initialized via a call to + * sam_freerun_initialize(); + * + * Returned Value: + * Zero (OK) is returned on success; a negated errno value is returned + * on failure. + * + ****************************************************************************/ + +int sam_freerun_uninitialize(struct sam_freerun_s *freerun) +{ + irqstate_t flags; + + /* Now we can disable the timer interrupt and disable the timer. */ + + sam_tc_attach(freerun->handle, NULL, NULL, 0); + sam_tc_stop(freerun->handle); + + /* Free the timer */ + + sam_tc_free(freerun->handle); + irqrestore(flags); + return OK; +} + +#endif /* CONFIG_SAMA5_ONESHOT */ |