From f6f2d2b9610977b8d9b33d415554aa8b18ef5c79 Mon Sep 17 00:00:00 2001 From: Gregory Nutt Date: Wed, 23 Oct 2013 14:53:37 -0600 Subject: [PATCH] SAMA5 Timer/counter library --- ChangeLog | 2 + arch/arm/src/sama5/Make.defs | 8 + arch/arm/src/sama5/chip/sam_tc.h | 25 +- arch/arm/src/sama5/chip/sama5d3x_memorymap.h | 4 +- arch/arm/src/sama5/sam_tc.c | 664 +++++++++++++++++++ arch/arm/src/sama5/sam_tc.h | 191 ++++++ 6 files changed, 880 insertions(+), 14 deletions(-) create mode 100644 arch/arm/src/sama5/sam_tc.c create mode 100644 arch/arm/src/sama5/sam_tc.h diff --git a/ChangeLog b/ChangeLog index 46dc95a2ec..841d31ec52 100644 --- a/ChangeLog +++ b/ChangeLog @@ -5857,4 +5857,6 @@ * arch/arm/src/stm32/stm32f10xxx_dma.c: DMA fix from David Sidrane: The DMA_CNDTRx register cannot be modified if the DMA channel is disabled (2013-10-23). + * arch/arm/src/sama5/sam_tc.c and .h: First but at a timer/counter + library for the SAMA5D3 (2013-10-23). diff --git a/arch/arm/src/sama5/Make.defs b/arch/arm/src/sama5/Make.defs index de978e2c3f..727661ffd2 100644 --- a/arch/arm/src/sama5/Make.defs +++ b/arch/arm/src/sama5/Make.defs @@ -204,3 +204,11 @@ ifeq ($(CONFIG_SAMA5_TSD),y) CHIP_CSRCS += sam_tsd.c endif endif + +ifeq ($(CONFIG_SAMA5_TC0),y) +CHIP_CSRCS += sam_tc.c +else +ifeq ($(CONFIG_SAMA5_TC1),y) +CHIP_CSRCS += sam_tc.c +endif +endif diff --git a/arch/arm/src/sama5/chip/sam_tc.h b/arch/arm/src/sama5/chip/sam_tc.h index 1415e53020..bf8fc18176 100644 --- a/arch/arm/src/sama5/chip/sam_tc.h +++ b/arch/arm/src/sama5/chip/sam_tc.h @@ -60,12 +60,12 @@ #define SAM_TC_SR_OFFSET 0x0020 /* Status Register */ #define SAM_TC_IER_OFFSET 0x0024 /* Interrupt Enable Register */ #define SAM_TC_IDR_OFFSET 0x0028 /* Interrupt Disable Register */ -#define SAM_TC_IDR_OFFSET 0x002c /* Interrupt Mask Register */ +#define SAM_TC_IMR_OFFSET 0x002c /* Interrupt Mask Register */ #define SAM_TCn_CCR_OFFSET(n) (SAM_TC_CHAN_OFFSET(n)+SAM_TC_CCR_OFFSET) #define SAM_TCn_CMR_OFFSET(n) (SAM_TC_CHAN_OFFSET(n)+SAM_TC_CMR_OFFSET) #define SAM_TCn_SMMR_OFFSET(n) (SAM_TC_CHAN_OFFSET(n)+SAM_TC_SMMR_OFFSET) -#define SAM_TCn_SMMR_OFFSET(n) (SAM_TC_CHAN_OFFSET(n)+SAM_TC_RAB_OFFSET) +#define SAM_TCn_RAB_OFFSET(n) (SAM_TC_CHAN_OFFSET(n)+SAM_TC_RAB_OFFSET) #define SAM_TCn_CV_OFFSET(n) (SAM_TC_CHAN_OFFSET(n)+SAM_TC_CV_OFFSET) #define SAM_TCn_RA_OFFSET(n) (SAM_TC_CHAN_OFFSET(n)+SAM_TC_RA_OFFSET) #define SAM_TCn_RB_OFFSET(n) (SAM_TC_CHAN_OFFSET(n)+SAM_TC_RB_OFFSET) @@ -73,12 +73,12 @@ #define SAM_TCn_SR_OFFSET(n) (SAM_TC_CHAN_OFFSET(n)+SAM_TC_SR_OFFSET) #define SAM_TCn_IER_OFFSET(n) (SAM_TC_CHAN_OFFSET(n)+SAM_TC_IER_OFFSET) #define SAM_TCn_IDR_OFFSET(n) (SAM_TC_CHAN_OFFSET(n)+SAM_TC_IDR_OFFSET) -#define SAM_TCn_IMR_OFFSET(n) (SAM_TC_CHAN_OFFSET(n)+SAM_TC_IDR_OFFSET) +#define SAM_TCn_IMR_OFFSET(n) (SAM_TC_CHAN_OFFSET(n)+SAM_TC_IMR_OFFSET) #define SAM_TC0_CCR_OFFSET SAM_TCn_CCR_OFFSET(0) #define SAM_TC0_CMR_OFFSET SAM_TCn_CMR_OFFSET(0) #define SAM_TC0_SMMR_OFFSET SAM_TCn_SMMR_OFFSET(0) -#define SAM_TC0_RAB_OFFSET SAM_TCn_SMMR_OFFSET(0) +#define SAM_TC0_RAB_OFFSET SAM_TCn_RAB_OFFSET(0) #define SAM_TC0_CV_OFFSET SAM_TCn_CV_OFFSET(0) #define SAM_TC0_RA_OFFSET SAM_TCn_RA_OFFSET(0) #define SAM_TC0_RB_OFFSET SAM_TCn_RB_OFFSET(0) @@ -91,7 +91,7 @@ #define SAM_TC1_CCR_OFFSET SAM_TCn_CCR_OFFSET(1) #define SAM_TC1_CMR_OFFSET SAM_TCn_CMR_OFFSET(1) #define SAM_TC1_SMMR_OFFSET SAM_TCn_SMMR_OFFSET(1) -#define SAM_TC1_RAB_OFFSET SAM_TCn_SMMR_OFFSET(1) +#define SAM_TC1_RAB_OFFSET SAM_TCn_RAB_OFFSET(1) #define SAM_TC1_CV_OFFSET SAM_TCn_CV_OFFSET(1) #define SAM_TC1_RA_OFFSET SAM_TCn_RA_OFFSET(1) #define SAM_TC1_RB_OFFSET SAM_TCn_RB_OFFSET(1) @@ -104,7 +104,7 @@ #define SAM_TC2_CCR_OFFSET SAM_TCn_CCR_OFFSET(2) #define SAM_TC2_CMR_OFFSET SAM_TCn_CMR_OFFSET(2) #define SAM_TC2_SMMR_OFFSET SAM_TCn_SMMR_OFFSET(2) -#define SAM_TC2_RAB_OFFSET SAM_TCn_SMMR_OFFSET(2) +#define SAM_TC2_RAB_OFFSET SAM_TCn_RAB_OFFSET(2) #define SAM_TC2_CV_OFFSET SAM_TCn_CV_OFFSET(2) #define SAM_TC2_RA_OFFSET SAM_TCn_RA_OFFSET(2) #define SAM_TC2_RB_OFFSET SAM_TCn_RB_OFFSET(2) @@ -142,7 +142,7 @@ #define SAM_TC0_CCR SAM_TC012_CCR(0) #define SAM_TC0_CMR SAM_TC012_CMR(0) #define SAM_TC0_SMMR SAM_TC012_SMMR(0) -#define SAM_TC0_RAB SAM_TC012_SMMR(0) +#define SAM_TC0_RAB SAM_TC012_RAB(0) #define SAM_TC0_CV SAM_TC012_CV(0) #define SAM_TC0_RA SAM_TC012_RA(0) #define SAM_TC0_RB SAM_TC012_RB(0) @@ -155,7 +155,7 @@ #define SAM_TC1_CCR SAM_TC012_CCR(1) #define SAM_TC1_CMR SAM_TC012_CMR(1) #define SAM_TC1_SMMR SAM_TC012_SMMR(1) -#define SAM_TC1_RAB SAM_TC012_SMMR(1) +#define SAM_TC1_RAB SAM_TC012_RAB(1) #define SAM_TC1_CV SAM_TC012_CV(1) #define SAM_TC1_RA SAM_TC012_RA(1) #define SAM_TC1_RB SAM_TC012_RB(1) @@ -168,7 +168,7 @@ #define SAM_TC2_CCR SAM_TC012_CCR(2) #define SAM_TC2_CMR SAM_TC012_CMR(2) #define SAM_TC2_SMMR SAM_TC012_SMMR(2) -#define SAM_TC2_RAB SAM_TC012_SMMR(2) +#define SAM_TC2_RAB SAM_TC012_RAB(2) #define SAM_TC2_CV SAM_TC012_CV(2) #define SAM_TC2_RA SAM_TC012_RA(2) #define SAM_TC2_RB SAM_TC012_RB(2) @@ -204,7 +204,7 @@ #define SAM_TC3_CCR SAM_TC345_CCR(3) #define SAM_TC3_CMR SAM_TC345_CMR(3) #define SAM_TC3_SMMR SAM_TC345_SMMR(3) -#define SAM_TC3_RAB SAM_TC345_SMMR(3) +#define SAM_TC3_RAB SAM_TC345_RAB(3) #define SAM_TC3_CV SAM_TC345_CV(3) #define SAM_TC3_RA SAM_TC345_RA(3) #define SAM_TC3_RB SAM_TC345_RB(3) @@ -217,7 +217,7 @@ #define SAM_TC4_CCR SAM_TC345_CCR(4) #define SAM_TC4_CMR SAM_TC345_CMR(4) #define SAM_TC4_SMMR SAM_TC345_SMMR(4) -#define SAM_TC4_RAB SAM_TC345_SMMR(4) +#define SAM_TC4_RAB SAM_TC345_RAB(4) #define SAM_TC4_CV SAM_TC345_CV(4) #define SAM_TC4_RA SAM_TC345_RA(4) #define SAM_TC4_RB SAM_TC345_RB(4) @@ -230,7 +230,7 @@ #define SAM_TC5_CCR SAM_TC345_CCR(5) #define SAM_TC5_CMR SAM_TC345_CMR(5) #define SAM_TC5_SMMR SAM_TC345_SMMR(5) -#define SAM_TC5_RAB SAM_TC345_SMMR(5) +#define SAM_TC5_RAB SAM_TC345_RAB(5) #define SAM_TC5_CV SAM_TC345_CV(5) #define SAM_TC5_RA SAM_TC345_RA(5) #define SAM_TC5_RB SAM_TC345_RB(5) @@ -398,6 +398,7 @@ #define TC_INT_LDRAS (1 << 5) /* Bit 5: RA Loading Status */ #define TC_INT_LDRBS (1 << 6) /* Bit 6: RB Loading Status */ #define TC_INT_ETRGS (1 << 7) /* Bit 7: External Trigger Status */ +#define TC_INT_ALL (0xff) #define TC_SR_CLKSTA (1 << 16) /* Bit 16: Clock Enabling Status */ #define TC_SR_MTIOA (1 << 17) /* Bit 17: TIOA Mirror */ diff --git a/arch/arm/src/sama5/chip/sama5d3x_memorymap.h b/arch/arm/src/sama5/chip/sama5d3x_memorymap.h index b835390546..80c353791b 100644 --- a/arch/arm/src/sama5/chip/sama5d3x_memorymap.h +++ b/arch/arm/src/sama5/chip/sama5d3x_memorymap.h @@ -104,7 +104,7 @@ # define SAM_SPI0_OFFSET 0x00004000 /* 0x00004000-0x00007fff: SPI0 */ # define SAM_SSC0_OFFSET 0x00008000 /* 0x00008000-0x0000bfff: SSC0 */ # define SAM_CAN0_OFFSET 0x0000c000 /* 0x0000c000-0x0000ffff: CAN0 */ -# define SAM_TC012_OFFSET 0x00010000 /* 0x00010000-0x00013fff: TC0, TC1, TC2 */ +# define SAM_TC012_OFFSET 0x00010000 /* 0x00010000-0x00013fff: TC channels 0, 1, and 2 */ # define SAM_TWI0_OFFSET 0x00014000 /* 0x00014000-0x00017fff: TWI0 */ # define SAM_TWI1_OFFSET 0x00018000 /* 0x00018000-0x0001bfff: TWI1 */ # define SAM_USART0_OFFSET 0x0001c000 /* 0x0001c000-0x0001ffff: USART0 */ @@ -123,7 +123,7 @@ # define SAM_SPI1_OFFSET 0x00008000 /* 0x00008000-0x0000bfff: SPI1 */ # define SAM_SSC1_OFFSET 0x0000c000 /* 0x0000c000-0x0000ffff: SSC1 */ # define SAM_CAN1_OFFSET 0x00010000 /* 0x00010000-0x00013fff: CAN1 */ -# define SAM_TC345_OFFSET 0x00014000 /* 0x00014000-0x00017fff: TC3, TC4, TC5 */ +# define SAM_TC345_OFFSET 0x00014000 /* 0x00014000-0x00017fff: TC channels 3, 4, and 5 */ # define SAM_TSADC_OFFSET 0x00018000 /* 0x00018000-0x0001bfff: TSADC */ # define SAM_TWI2_OFFSET 0x0001c000 /* 0x0001c000-0x0001ffff: TWI2 */ # define SAM_USART2_OFFSET 0x00020000 /* 0x00020000-0x00023fff: USART2 */ diff --git a/arch/arm/src/sama5/sam_tc.c b/arch/arm/src/sama5/sam_tc.c new file mode 100644 index 0000000000..0c15e9072f --- /dev/null +++ b/arch/arm/src/sama5/sam_tc.c @@ -0,0 +1,664 @@ +/**************************************************************************** + * arch/arm/src/sama5/sam_tc.c + * + * Copyright (C) 2013 Gregory Nutt. All rights reserved. + * Author: Gregory Nutt + * + * References: + * + * SAMA5D3 Series Data Sheet + * Atmel NoOS sample code. + * + * The Atmel sample code has a BSD compatibile 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 "up_arch.h" +#include "sam_periphclks.h" +#include "sam_tc.h" + +#if defined(CONFIG_SAMA5_TC0) || defined(CONFIG_SAMA5_TC1) + +/**************************************************************************** + * Private Types + ****************************************************************************/ + +/* This structure describes one timer counter channel */ + +struct sam_tc_s; +struct sam_chan_s +{ + struct sam_tc_s *tc; /* Parent timer/counter */ + uintptr_t base; /* Channel register base address */ + uint8_t chan; /* Channel number (0, 1, or 2 OR 3, 4, or 5) */ + bool inuse; /* True: channel is in use */ +}; + +/* This structure describes on timer/counter */ + +struct sam_tc_s +{ + sem_t exclsem; /* Assures mutually exclusive access to TC */ + uintptr_t base; /* Register base address */ + uint8_t pid; /* Peripheral ID */ + bool initialized; /* True: Timer data has been initialized */ + + /* Channels */ + + struct sam_chan_s channel[3]; + + /* Debug stuff */ + +#ifdef CONFIG_SAMA5_HSMCI_REGDEBUG + bool wrlast; /* Last was a write */ + uint32_t addrlast; /* Last address */ + uint32_t vallast; /* Last value */ + int ntimes; /* Number of times */ +#endif +}; + +/**************************************************************************** + * Private Function Prototypes + ****************************************************************************/ + +/* Low-level helpers ********************************************************/ + +static void sam_takesem(struct sam_tc_s *tc); +#define sam_givesem(tc) (sem_post(&tc->exclsem)) + +#ifdef CONFIG_SAMA5_HSMCI_REGDEBUG +static bool sam_checkreg(struct sam_tc_s *tc, bool wr, + uint32_t value, uint32_t regaddr, uint32_t regval); +#else +# define sam_checkreg(tc,wr,value,regaddr) (false) +#endif + +static inline uint32_t sam_tc_getreg(struct sam_chan_s *chan, + unsigned int offset); +static inline void sam_tc_putreg(struct sam_chan_s *chan, + unsigned int offset, uint32_t value); + +static inline uint32_t sam_chan_getreg(struct sam_chan_s *chan, + unsigned int offset); +static inline void sam_chan_putreg(struct sam_chan_s *chan, + unsigned int offset, uint32_t value); + +/* Initialization ***********************************************************/ + +static inline struct sam_chan_s *sam_tc_initialize(int channel); + +/**************************************************************************** + * Private Data + ****************************************************************************/ + +#ifdef CONFIG_SAMA5_TC0 +static struct sam_tc_s g_tc012; +#endif +#ifdef CONFIG_SAMA5_TC1 +static struct sam_tc_s g_tc345; +#endif + +/**************************************************************************** + * Private Functions + ****************************************************************************/ +/**************************************************************************** + * Low-level Helpers + ****************************************************************************/ +/**************************************************************************** + * Name: sam_takesem + * + * Description: + * Take the wait semaphore (handling false alarm wakeups due to the receipt + * of signals). + * + * Input Parameters: + * dev - Instance of the SDIO device driver state structure. + * + * Returned Value: + * None + * + ****************************************************************************/ + +static void sam_takesem(struct sam_tc_s *tc) +{ + /* Take the semaphore (perhaps waiting) */ + + while (sem_wait(&tc->exclsem) != 0) + { + /* The only case that an error should occr here is if the wait was + * awakened by a signal. + */ + + ASSERT(errno == EINTR); + } +} + +/**************************************************************************** + * Name: sam_checkreg + * + * Description: + * Check if the current register access is a duplicate of the preceding. + * + * Input Parameters: + * value - The value to be written + * regaddr - The address of the register to write to + * + * Returned Value: + * true: This is the first register access of this type. + * flase: This is the same as the preceding register access. + * + ****************************************************************************/ + +#ifdef CONFIG_SAMA5_HSMCI_REGDEBUG +static bool sam_checkreg(struct sam_tc_s *tc, bool wr, uint32_t regaddr, + uint32_t value) +{ + if (wr == tc->wrlast && /* Same kind of access? */ + value == tc->vallast && /* Same value? */ + regaddr == tc->addrlast) /* Same regaddr? */ + { + /* Yes, then just keep a count of the number of times we did this. */ + + tc->ntimes++; + return false; + } + else + { + /* Did we do the previous operation more than once? */ + + if (tc->ntimes > 0) + { + /* Yes... show how many times we did it */ + + lldbg("...[Repeats %d times]...\n", tc->ntimes); + } + + /* Save information about the new access */ + + tc->wrlast = wr; + tc->vallast = value; + tc->addrlast = regaddr; + tc->ntimes = 0; + } + + /* Return true if this is the first time that we have done this operation */ + + return true; +} +#endif + +/**************************************************************************** + * Name: sam_tc_getreg + * + * Description: + * Read an SPI register + * + ****************************************************************************/ + +static inline uint32_t sam_tc_getreg(struct sam_chan_s *chan, + unsigned int offset) +{ + struct sam_tc_s *tc = chan->tc; + uint32_t regaddr = tc->base + offset; + uint32_t regval = getreg32(regaddr); + +#ifdef CONFIG_SAMA5_HSMCI_REGDEBUG + if (sam_checkreg(tc, false, regval, regaddr)) + { + lldbg("%08x->%08x\n", regaddr, regval); + } +#endif + + return regval; +} + +/**************************************************************************** + * Name: sam_tc_putreg + * + * Description: + * Write a value to an SPI register + * + ****************************************************************************/ + +static inline void sam_tc_putreg(struct sam_chan_s *chan, uint32_t regval, + unsigned int offset) +{ + struct sam_tc_s *tc = chan->tc; + uint32_t regaddr = tc->base + offset; + +#ifdef CONFIG_SAMA5_HSMCI_REGDEBUG + if (sam_checkreg(tc, true, regval, regaddr)) + { + lldbg("%08x<-%08x\n", regaddr, regval); + } +#endif + + putreg32(regval, regaddr); +} + +/**************************************************************************** + * Name: sam_chan_getreg + * + * Description: + * Read an SPI register + * + ****************************************************************************/ + +static inline uint32_t sam_chan_getreg(struct sam_chan_s *chan, + unsigned int offset) +{ + uint32_t regaddr = chan->base + offset; + uint32_t regval = getreg32(regaddr); + +#ifdef CONFIG_SAMA5_HSMCI_REGDEBUG + if (sam_checkreg(chan->tc, false, regval, regaddr)) + { + lldbg("%08x->%08x\n", regaddr, regval); + } +#endif + + return regval; +} + +/**************************************************************************** + * Name: sam_chan_putreg + * + * Description: + * Write a value to an SPI register + * + ****************************************************************************/ + +static inline void sam_chan_putreg(struct sam_chan_s *chan, unsigned int offset, + uint32_t regval) +{ + uint32_t regaddr = chan->base + offset; + +#ifdef CONFIG_SAMA5_HSMCI_REGDEBUG + if (sam_checkreg(chan->tc, true, regval, regaddr)) + { + lldbg("%08x<-%08x\n", regaddr, regval); + } +#endif + + putreg32(regval, regaddr); +} + +/**************************************************************************** + * Initialization + ****************************************************************************/ +/**************************************************************************** + * Name: sam_tc_initialize + * + * Description: + * There is no global, one-time initialization of timer/counter data + * structures. Rather, this function is called each time that a channel + * is allocated and, if the channel has not been initialized, it will be + * initialized then. + * + * Input Parameters: + * channel TC channel number (see TC_CHANx definitions) + * + * Returned Value: + * A pointer to the initialized timer channel structure associated with tc + * and channel. NULL is returned on any failure. + * + * On successful return, the caller holds the tc exclusive access semaphore. + * + ****************************************************************************/ + +static inline struct sam_chan_s *sam_tc_initialize(int channel) +{ + static struct sam_tc_s *tc; + static struct sam_chan_s *chan; + irqstate_t flags; + uintptr_t tcbase; + uintptr_t chbase; + int chfirst; + int chndx; + int pid; + int i; + + /* Select the timer/counter and get the index associated with the + * channel. + */ + +#ifdef CONFIG_SAMA5_TC0 + if (channel >= 0 && channel < 3) + { + tc = &g_tc012; + chndx = channel; + + /* These are only needed in the case where we need to initialize the + * timer/counter. + */ + + chfirst = 0; + tcbase = SAM_TC012_VBASE; + chbase = SAM_TC012_CHAN_BASE(channel); + pid = SAM_PID_TC0; + } + else +#endif +#ifdef CONFIG_SAMA5_TC1 + if (channel >= 3 && channel < 5) + { + tc = &g_tc345; + chndx = channel - 3; + + /* These are only needed in the case where we need to initialize the + * timer/counter. + */ + + chfirst = 3; + tcbase = SAM_TC345_VBASE; + chbase = SAM_TC345_CHAN_BASE(channel) + pid = SAM_PID_TC0; + } + else +#endif + { + /* Timer/counter is not invalid or not enabled */ + + return NULL; + } + + /* Has the timer counter been initialized. We have to be careful here + * because there is no semaphore protection. + */ + + flags = irqsave(); + if (!tc->initialized) + { + /* Initialize the timer counter data structure. */ + + memset(tc, 0, sizeof(struct sam_tc_s)); + sem_init(&tc->exclsem, 0, 1); + tc->base = tcbase; + tc->pid = pid; + + /* Initialize the channels */ + + for (i = 0; i < 3; i++) + { + chan = &tc->channel[i]; + chan->base = chbase; + chan->chan = chfirst++; + } + + /* Enable clocking to the timer counter */ + + sam_enableperiph0(pid); + + /* Now the channel is initialized */ + + tc->initialized = true; + } + + /* Get exclusive access to the timer/count data structure */ + + sam_takesem(tc); + irqrestore(flags); + + /* Get the requested channel structure */ + + chan = &tc->channel[chndx]; + + /* Is it available? */ + + if (chan->inuse) + { + /* No.. return a failure */ + + sam_givesem(tc); + return NULL; + } + + /* OK.. return the channel with the semaphore locked */ + + return chan; +} + +/**************************************************************************** + * Public Functions + ****************************************************************************/ +/**************************************************************************** + * Name: sam_tc_allocate + * + * Description: + * Configures a Timer Counter to operate in the given mode. The timer is + * stopped after configuration and must be restarted with sam_tc_start(). + * All the interrupts of the timer are also disabled. + * + * Input Parameters: + * channel TC channel number (see TC_CHANx definitions) + * mode Operating mode (TC_CMR value). + * + * Returned Value: + * On success, a non-NULL handle value is returned. This handle may be + * used with subsequent timer/counter interfaces to manage the timer. A + * NULL handle value is returned on a failure. + * + ****************************************************************************/ + +TCHANDLE sam_tc_allocate(int channel, int mode) +{ + struct sam_chan_s *chan; + + /* Initialize the timer/counter data (if necessary) and get exclusive + * access to the requested channel. + */ + + chan = sam_tc_initialize(channel); + if (chan) + { + /* Disable TC clock */ + + sam_chan_putreg(chan, SAM_TC_CCR_OFFSET, TC_CCR_CLKDIS); + + /* Disable channel interrupts */ + + sam_chan_putreg(chan, SAM_TC_IDR_OFFSET, TC_INT_ALL); + + /* Clear and pending status */ + + (void)sam_chan_getreg(chan, SAM_TC_SR_OFFSET); + + /* And set the requested mode */ + + sam_chan_putreg(chan, SAM_TC_CMR_OFFSET, mode); + } + + /* Return an opaque reference to the channel */ + + return (TCHANDLE)chan; +} + +/**************************************************************************** + * Name: sam_tc_free + * + * Description: + * Release the handle previously allocated by sam_tc_allocate(). + * + * Input Parameters: + * handle Channel handle previously allocated by sam_tc_allocate() + * + * Returned Value: + * None + * + ****************************************************************************/ + +void sam_tc_free(TCHANDLE handle) +{ + struct sam_chan_s *chan = (struct sam_chan_s *)handle; + DEBUGASSERT(chan && chan->inuse); + + /* Make sure that the channel is stopped */ + + sam_tc_stop(handle); + + /* Mark the channel as available */ + + chan->inuse = false; +} + +/**************************************************************************** + * Name: sam_tc_start + * + * Description: + * Reset and Start the TC Channel. Enables the timer clock and performs a + * software reset to start the counting. + * + * Input Parameters: + * handle Channel handle previously allocated by sam_tc_allocate() + * + * Returned Value: + * + ****************************************************************************/ + +void sam_tc_start(TCHANDLE handle) +{ + struct sam_chan_s *chan = (struct sam_chan_s *)handle; + + DEBUGASSERT(chan && chan->inuse); + sam_chan_putreg(chan, SAM_TC_CCR_OFFSET, TC_CCR_CLKEN | TC_CCR_SWTRG); +} + +/**************************************************************************** + * Name: sam_tc_stop + * + * Description: + * Stop TC Channel. Disables the timer clock, stopping the counting. + * + * Input Parameters: + * handle Channel handle previously allocated by sam_tc_allocate() + * + * Returned Value: + * + ****************************************************************************/ + +void sam_tc_stop(TCHANDLE handle) +{ + struct sam_chan_s *chan = (struct sam_chan_s *)handle; + + DEBUGASSERT(chan && chan->inuse); + sam_chan_putreg(chan, SAM_TC_CCR_OFFSET, TC_CCR_CLKDIS); +} + +/**************************************************************************** + * Name: sam_tc_divisor + * + * Description: + * Finds the best MCK divisor given the timer frequency and MCK. The + * result is guaranteed to satisfy the following equation: + * + * (MCK / (DIV * 65536)) <= freq <= (MCK / DIV) + * + * with DIV being the highest possible value. + * + * Input Parameters: + * + * frequency Desired timer frequency. + * mck Master clock frequency. + * div Divisor value. + * tcclks TCCLKS field value for divisor. + * boardmck Board clock frequency. + * + * Returned Value: + * Zero (OK) if a proper divisor has been found, otherwise a negated errno + * value indicating the nature of the failure. + * + ****************************************************************************/ + +uint32_t sam_tc_divisor(uint32_t frequency, uint32_t mck, uint32_t *div, + uint32_t *tcclks, uint32_t boardmck) +{ + const uint32_t adivisors[5] = { 2, 8, 32, 128, boardmck / 32768 }; + int ndx = 0; + + /* Satisfy lower bound */ + + while (frequency < ((mck / adivisors[ndx]) / 65536)) + { + ndx++; + + /* If no divisor can be found, return -ERANGE */ + + if (ndx == (sizeof(adivisors)/sizeof(adivisors[0]))) + { + return -ERANGE; + } + } + + /* Try to maximize DIV while satisfying upper bound */ + + while (ndx < 4) + { + + if (frequency > (mck / adivisors[ndx + 1])) + { + break; + } + + ndx++; + } + + /* Store results */ + + if (div) + { + *div = adivisors[ndx]; + } + + if (tcclks) + { + *tcclks = ndx; + } + + return OK; +} + +#endif /* CONFIG_SAMA5_TC0 || CONFIG_SAMA5_TC1 */ diff --git a/arch/arm/src/sama5/sam_tc.h b/arch/arm/src/sama5/sam_tc.h new file mode 100644 index 0000000000..f12f650be1 --- /dev/null +++ b/arch/arm/src/sama5/sam_tc.h @@ -0,0 +1,191 @@ +/**************************************************************************** + * arch/arm/src/sama5/sam_adc.h + * + * Copyright (C) 2013 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_TC_H +#define __ARCH_ARM_SRC_SAMA5_SAM_TC_H + +/**************************************************************************** + * Included Files + ****************************************************************************/ + +#include + +#include + +#include "chip.h" +#include "chip/sam_tc.h" + +#if defined(CONFIG_SAMA5_TC0) || defined(CONFIG_SAMA5_TC1) + +/**************************************************************************** + * Pre-processor Definitions + ****************************************************************************/ + +/* The timer/counter and channel arguments to sam_tc_allocate() */ + +#define TC_CHAN0 0 +#define TC_CHAN1 1 +#define TC_CHAN2 2 +#define TC_CHAN3 3 +#define TC_CHAN4 4 +#define TC_CHAN5 5 + +/**************************************************************************** + * Public Types + ****************************************************************************/ + +typedef void *TCHANDLE; + +/**************************************************************************** + * Public Data + ****************************************************************************/ + +#undef EXTERN +#if defined(__cplusplus) +#define EXTERN extern "C" +extern "C" +{ +#else +#define EXTERN extern +#endif + +/**************************************************************************** + * Public Function Prototypes + ****************************************************************************/ + +/**************************************************************************** + * Name: sam_tc_allocate + * + * Description: + * Configures a Timer Counter to operate in the given mode. The timer is + * stopped after configuration and must be restarted with sam_tc_start(). + * All the interrupts of the timer are also disabled. + * + * Input Parameters: + * channel TC channel number (see TC_CHANx definitions) + * mode Operating mode (TC_CMR value). + * + * Returned Value: + * On success, a non-NULL handle value is returned. This handle may be + * used with subsequent timer/counter interfaces to manage the timer. A + * NULL handle value is returned on a failure. + * + ****************************************************************************/ + +TCHANDLE sam_tc_allocate(int channel, int mode); + +/**************************************************************************** + * Name: sam_tc_free + * + * Description: + * Release the handle previously allocated by sam_tc_allocate(). + * + * Input Parameters: + * handle Channel handle previously allocated by sam_tc_allocate() + * + * Returned Value: + * None + * + ****************************************************************************/ + +void sam_tc_free(TCHANDLE handle); + +/**************************************************************************** + * Name: sam_tc_start + * + * Description: + * Reset and Start the TC Channel. Enables the timer clock and performs a + * software reset to start the counting. + * + * Input Parameters: + * handle Channel handle previously allocated by sam_tc_allocate() + * + * Returned Value: + * + ****************************************************************************/ + +void sam_tc_start(TCHANDLE handle); + +/**************************************************************************** + * Name: sam_tc_stop + * + * Description: + * Stop TC Channel. Disables the timer clock, stopping the counting. + * + * Input Parameters: + * handle Channel handle previously allocated by sam_tc_allocate() + * + * Returned Value: + * + ****************************************************************************/ + +void sam_tc_stop(TCHANDLE handle); + +/**************************************************************************** + * Name: sam_tc_divisor + * + * Description: + * Finds the best MCK divisor given the timer frequency and MCK. The + * result is guaranteed to satisfy the following equation: + * + * (MCK / (DIV * 65536)) <= freq <= (MCK / DIV) + * + * with DIV being the highest possible value. + * + * Input Parameters: + * + * frequency Desired timer frequency. + * mck Master clock frequency. + * div Divisor value. + * tcclks TCCLKS field value for divisor. + * boardmck Board clock frequency. + * + * Returned Value: + * Zero (OK) if a proper divisor has been found, otherwise a negated errno + * value indicating the nature of the failure. + * + ****************************************************************************/ + +uint32_t sam_tc_divisor(uint32_t frequency, uint32_t mck, uint32_t *div, + uint32_t *tcclks, uint32_t boardmck); + +#undef EXTERN +#ifdef __cplusplus +} +#endif + +#endif /* CONFIG_SAMA5_TC0 || CONFIG_SAMA5_TC1 */ +#endif /* __ARCH_ARM_SRC_SAMA5_SAM_TC_H */ +