summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorGregory Nutt <gnutt@nuttx.org>2013-10-23 14:53:37 -0600
committerGregory Nutt <gnutt@nuttx.org>2013-10-23 14:53:37 -0600
commit4e79817ffccfc1e9ffb699506acdb070e329c895 (patch)
treead5692d062dd720fdb569e66bb864a67bcf3f9a5
parentaa8b6730904bec450b5fd923a3e43ec80d032ce0 (diff)
downloadnuttx-4e79817ffccfc1e9ffb699506acdb070e329c895.tar.gz
nuttx-4e79817ffccfc1e9ffb699506acdb070e329c895.tar.bz2
nuttx-4e79817ffccfc1e9ffb699506acdb070e329c895.zip
SAMA5 Timer/counter library
-rw-r--r--nuttx/ChangeLog2
-rw-r--r--nuttx/arch/arm/src/sama5/Make.defs8
-rw-r--r--nuttx/arch/arm/src/sama5/chip/sam_tc.h25
-rw-r--r--nuttx/arch/arm/src/sama5/chip/sama5d3x_memorymap.h4
-rw-r--r--nuttx/arch/arm/src/sama5/sam_tc.c664
-rw-r--r--nuttx/arch/arm/src/sama5/sam_tc.h191
6 files changed, 880 insertions, 14 deletions
diff --git a/nuttx/ChangeLog b/nuttx/ChangeLog
index 46dc95a2e..841d31ec5 100644
--- a/nuttx/ChangeLog
+++ b/nuttx/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/nuttx/arch/arm/src/sama5/Make.defs b/nuttx/arch/arm/src/sama5/Make.defs
index de978e2c3..727661ffd 100644
--- a/nuttx/arch/arm/src/sama5/Make.defs
+++ b/nuttx/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/nuttx/arch/arm/src/sama5/chip/sam_tc.h b/nuttx/arch/arm/src/sama5/chip/sam_tc.h
index 1415e5302..bf8fc1817 100644
--- a/nuttx/arch/arm/src/sama5/chip/sam_tc.h
+++ b/nuttx/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/nuttx/arch/arm/src/sama5/chip/sama5d3x_memorymap.h b/nuttx/arch/arm/src/sama5/chip/sama5d3x_memorymap.h
index b83539054..80c353791 100644
--- a/nuttx/arch/arm/src/sama5/chip/sama5d3x_memorymap.h
+++ b/nuttx/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/nuttx/arch/arm/src/sama5/sam_tc.c b/nuttx/arch/arm/src/sama5/sam_tc.c
new file mode 100644
index 000000000..0c15e9072
--- /dev/null
+++ b/nuttx/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 <gnutt@nuttx.org>
+ *
+ * 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 <nuttx/config.h>
+
+#include <sys/types.h>
+#include <stdint.h>
+#include <stdbool.h>
+#include <string.h>
+#include <semaphore.h>
+#include <assert.h>
+#include <errno.h>
+
+#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/nuttx/arch/arm/src/sama5/sam_tc.h b/nuttx/arch/arm/src/sama5/sam_tc.h
new file mode 100644
index 000000000..f12f650be
--- /dev/null
+++ b/nuttx/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 <gnutt@nuttx.org>
+ *
+ * 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 <nuttx/config.h>
+
+#include <stdint.h>
+
+#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 */
+