summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorGregory Nutt <gnutt@nuttx.org>2014-04-30 14:10:02 -0600
committerGregory Nutt <gnutt@nuttx.org>2014-04-30 14:10:02 -0600
commitee5dcbda94fa89b146e94d3c163a30f7ce49fe5b (patch)
tree10774d774374f6e2a1aafad8267d0b486cd042fd
parentdc9c2633f86894a4be35596994464c1e616fedf8 (diff)
downloadpx4-nuttx-ee5dcbda94fa89b146e94d3c163a30f7ce49fe5b.tar.gz
px4-nuttx-ee5dcbda94fa89b146e94d3c163a30f7ce49fe5b.tar.bz2
px4-nuttx-ee5dcbda94fa89b146e94d3c163a30f7ce49fe5b.zip
SAM3/4: Enhanced timer/counter driver from Bob Doiron
-rw-r--r--nuttx/arch/arm/src/sam34/Kconfig30
-rw-r--r--nuttx/arch/arm/src/sam34/sam_tc.c155
2 files changed, 96 insertions, 89 deletions
diff --git a/nuttx/arch/arm/src/sam34/Kconfig b/nuttx/arch/arm/src/sam34/Kconfig
index ad076de88..62ddf0743 100644
--- a/nuttx/arch/arm/src/sam34/Kconfig
+++ b/nuttx/arch/arm/src/sam34/Kconfig
@@ -475,45 +475,54 @@ config SAM34_SSC
config SAM34_TC0
bool "Timer/Counter 0 (TC0)"
default n
+ select SAM34_TC
config SAM34_TC1
bool "Timer/Counter 1 (TC1)"
default n
+ select SAM34_TC
config SAM34_TC2
bool "Timer/Counter 2 (TC2)"
default n
depends on ARCH_CHIP_SAM3U || ARCH_CHIP_SAM3X || ARCH_CHIP_SAM3A || ARCH_CHIP_SAM4S || ARCH_CHIP_SAM4E
+ select SAM34_TC
config SAM34_TC3
bool "Timer/Counter 3 (TC3)"
default n
depends on ARCH_CHIP_SAM3X || ARCH_CHIP_SAM3A || ARCH_CHIP_SAM4S || ARCH_CHIP_SAM4E
+ select SAM34_TC
config SAM34_TC4
bool "Timer/Counter 4 (TC4)"
default n
depends on ARCH_CHIP_SAM3X || ARCH_CHIP_SAM3A || ARCH_CHIP_SAM4S || ARCH_CHIP_SAM4E
+ select SAM34_TC
config SAM34_TC5
bool "Timer/Counter 5 (TC5)"
default n
depends on ARCH_CHIP_SAM3X || ARCH_CHIP_SAM3A || ARCH_CHIP_SAM4S || ARCH_CHIP_SAM4E
+ select SAM34_TC
config SAM34_TC6
bool "Timer/Counter 6 (TC6)"
default n
depends on ARCH_CHIP_SAM3X || ARCH_CHIP_SAM3A || ARCH_CHIP_SAM4E
+ select SAM34_TC
config SAM34_TC7
bool "Timer/Counter 7 (TC7)"
default n
depends on ARCH_CHIP_SAM3X || ARCH_CHIP_SAM3A || ARCH_CHIP_SAM4E
+ select SAM34_TC
config SAM34_TC8
bool "Timer/Counter 6 (TC8)"
default n
depends on ARCH_CHIP_SAM3X || ARCH_CHIP_SAM3A || ARCH_CHIP_SAM4E
+ select SAM34_TC
config SAM34_TRNG
bool "True Random Number Generator (TRNG)"
@@ -1177,8 +1186,8 @@ config SAM34_HSMCI_CMDDEBUG
endmenu # HSMCI device driver options
endif # SAM34_HSMCI
-if SAM34_UDP
menu "AT91SAM3/4 USB Full Speed Device Controller driver (DCD) options"
+ depends on SAM34_UDP
config SAM34_UDP_REGDEBUG
bool "Enable low-level UDP register debug"
@@ -1186,10 +1195,23 @@ config SAM34_UDP_REGDEBUG
depends on DEBUG
endmenu # USB Full Speed Device Controller driver (DCD) options
-endif # SAM34_UDP
-if SAM34_WDT
+config SAM34_TC
+ bool
+ default n
+
+menu "AT91SAM3/4 Timer/Counter options"
+ depends on SAM34_TC
+
+config SAM34_TC_REGDEBUG
+ bool "Enable low-level timer/counter register debug"
+ default n
+ depends on DEBUG
+
+endmenu # USB Full Speed Device Controller driver (DCD) options
+
menu "AT91SAM3/4 Watchdog Configuration"
+ depends on SAM34_WDT
config WDT_ENABLED_ON_RESET
bool "Watchdog Enabled on reset"
@@ -1248,6 +1270,6 @@ config WDT_THREAD_PRIORITY
config WDT_THREAD_STACKSIZE
int "Watchdog Thread Stacksize"
default 1024
+
endif # WDT_THREAD
endmenu #"AT91SAM3/4 Watchdog device driver options"
-endif # SAM34_WDT
diff --git a/nuttx/arch/arm/src/sam34/sam_tc.c b/nuttx/arch/arm/src/sam34/sam_tc.c
index 99fbce646..cb48f84b6 100644
--- a/nuttx/arch/arm/src/sam34/sam_tc.c
+++ b/nuttx/arch/arm/src/sam34/sam_tc.c
@@ -54,8 +54,6 @@
#include "sam_tc.h"
#include "sam_periphclks.h"
-//#define CONFIG_SAM34_TC_REGDEBUG
-
#if defined(CONFIG_TIMER) && (defined(CONFIG_SAM34_TC0) || \
defined(CONFIG_SAM34_TC1) || defined(CONFIG_SAM34_TC2) || \
defined(CONFIG_SAM34_TC3) || defined(CONFIG_SAM34_TC4) || \
@@ -98,10 +96,15 @@
struct sam34_lowerhalf_s
{
FAR const struct timer_ops_s *ops; /* Lower half operations */
- xcpt_t handler; /* Current user interrupt handler */
- uint32_t timeout; /* The actual timeout value (us) */
- bool started; /* The timer has been started */
- uint16_t reload; /* The 12-bit reload field reset value (WDV) */
+
+ /* Private data */
+
+ uint32_t base; /* Base address of the timer */
+ tccb_t handler; /* Current user interrupt handler */
+ uint32_t timeout; /* The current timeout value (us) */
+ uint32_t adjustment; /* time lost due to clock resolution truncation (us) */
+ uint32_t clkticks; /* actual clock ticks for current interval */
+ bool started; /* The timer has been started */
uint16_t debug;
};
@@ -118,7 +121,7 @@ static void sam34_putreg(uint32_t val, uint32_t addr);
# define sam34_putreg(val,addr) putreg32(val,addr)
#endif
-/* Interrupt hanlding *******************************************************/
+/* Interrupt handling *******************************************************/
static int sam34_interrupt(int irq, FAR void *context);
@@ -130,8 +133,8 @@ static int sam34_getstatus(FAR struct timer_lowerhalf_s *lower,
FAR struct timer_status_s *status);
static int sam34_settimeout(FAR struct timer_lowerhalf_s *lower,
uint32_t timeout);
-static xcpt_t sam34_capture(FAR struct timer_lowerhalf_s *lower,
- xcpt_t handler);
+static tccb_t sam34_capture(FAR struct timer_lowerhalf_s *lower,
+ tccb_t handler);
static int sam34_ioctl(FAR struct timer_lowerhalf_s *lower, int cmd,
unsigned long arg);
@@ -269,12 +272,27 @@ static int sam34_interrupt(int irq, FAR void *context)
regval = sam34_getreg(SAM_TC0_SR);
if ((regval & TC_INT_CPCS) != 0)
{
+ uint32_t timeout;
+
/* Is there a registered handler? */
- if (priv->handler)
- {
- priv->handler(irq, context);
- }
+ if (priv->handler && priv->handler(&priv->timeout))
+ {
+ /* Calculate new ticks */
+
+ priv->clkticks = ((uint64_t)(priv->adjustment + priv->timeout))*TC_FCLK / 1000000;
+
+ /* Set next interval interval. TODO: make sure the interval is not so soon it will be missed! */
+
+ sam34_putreg(priv->clkticks, SAM_TC0_RC);
+
+ timeout = (1000000ULL * priv->clkticks) / TC_FCLK; /* trucated timeout */
+ priv->adjustment = (priv->adjustment + priv->timeout) - timeout; /* truncated time to be added to next interval (dither) */
+ }
+ else /* stop */
+ {
+ sam34_stop((FAR struct timer_lowerhalf_s *)&g_tcdev);
+ }
/* TC_INT_CPCS is cleared by reading SAM_TC0_SR */
}
@@ -305,9 +323,14 @@ static int sam34_start(FAR struct timer_lowerhalf_s *lower)
tcvdbg("Entry\n");
DEBUGASSERT(priv);
+ if(priv->started)
+ {
+ return -EINVAL;
+ }
+
sam_tc0_enableclk();
- sam34_putreg(TC_CCR_CLKDIS, SAM_TC0_CCR); // disable counter
+ sam34_putreg(TC_CCR_CLKDIS, SAM_TC0_CCR); /* Disable counter */
/* TC_CMR_WAVE - waveform mode
* TC_CMR_WAVSEL_UPAUTO - reset on RC compare (interval timer)
@@ -317,19 +340,17 @@ static int sam34_start(FAR struct timer_lowerhalf_s *lower)
mr_val |= (TC_CMR_WAVE + TC_CMR_WAVSEL_UPAUTO + TC_CMR_TCCLKS_TIMERCLOCK5);
sam34_putreg(mr_val, SAM_TC0_CMR);
- sam34_putreg(priv->reload, SAM_TC0_RC); // set interval
+ sam34_putreg(priv->clkticks, SAM_TC0_RC); /* Set interval */
- /* TODO: isr active without user handle for now... */
-// if (priv->handler)
+ if (priv->handler)
{
- /* Clear status */
+ /* Clear status and enable interrupt */
sam34_getreg(SAM_TC0_SR);
- sam34_putreg(TC_INT_CPCS, SAM_TC0_IMR);
sam34_putreg(TC_INT_CPCS, SAM_TC0_IER);
}
- sam34_putreg(TC_CCR_SWTRG + TC_CCR_CLKEN, SAM_TC0_CCR); // start counter
+ sam34_putreg(TC_CCR_SWTRG + TC_CCR_CLKEN, SAM_TC0_CCR); /* Start counter */
priv->started = true;
return OK;
@@ -352,10 +373,19 @@ static int sam34_start(FAR struct timer_lowerhalf_s *lower)
static int sam34_stop(FAR struct timer_lowerhalf_s *lower)
{
+ FAR struct sam34_lowerhalf_s *priv = (FAR struct sam34_lowerhalf_s *)lower;
tcvdbg("Entry\n");
- sam34_putreg(TC_CCR_CLKDIS, SAM_TC0_CCR); // disable counter
- sam34_putreg(TC_INT_ALL, SAM_TC0_IDR); // disable all ints
+ DEBUGASSERT(priv);
+
+ if(!priv->started)
+ {
+ return -EINVAL;
+ }
+
+ sam34_putreg(TC_CCR_CLKDIS, SAM_TC0_CCR); /* Disable counter */
+ sam34_putreg(TC_INT_ALL, SAM_TC0_IDR); /* Disable all ints */
sam_tc0_disableclk();
+ priv->started = false;
return OK;
}
@@ -367,9 +397,9 @@ static int sam34_stop(FAR struct timer_lowerhalf_s *lower)
* Get the current timer status
*
* Input Parameters:
- * lower - A pointer the publicly visible representation of the "lower-half"
- * driver state structure.
- * stawtus - The location to return the status information.
+ * lower - A pointer the publicly visible representation of the "lower-half"
+ * driver state structure.
+ * status - The location to return the status information.
*
* Returned Values:
* Zero on success; a negated errno value on failure.
@@ -405,7 +435,7 @@ static int sam34_getstatus(FAR struct timer_lowerhalf_s *lower,
/* Get the time remaining until the timer expires (in microseconds) */
elapsed = sam34_getreg(SAM_TC0_CV);
- status->timeleft = (priv->timeout * elapsed) / (priv->reload + 1);
+ status->timeleft = (priv->timeout * elapsed) / (priv->clkticks + 1); /* TODO - check on this +1 */
tcvdbg(" flags : %08x\n", status->flags);
tcvdbg(" timeout : %d\n", status->timeout);
@@ -422,7 +452,7 @@ static int sam34_getstatus(FAR struct timer_lowerhalf_s *lower,
* Input Parameters:
* lower - A pointer the publicly visible representation of the "lower-half"
* driver state structure.
- * timeout - The new timeout value in millisecnds.
+ * timeout - The new timeout value in milliseconds.
*
* Returned Values:
* Zero on success; a negated errno value on failure.
@@ -433,7 +463,6 @@ static int sam34_settimeout(FAR struct timer_lowerhalf_s *lower,
uint32_t timeout)
{
FAR struct sam34_lowerhalf_s *priv = (FAR struct sam34_lowerhalf_s *)lower;
- uint32_t reload;
DEBUGASSERT(priv);
tcvdbg("Entry: timeout=%d\n", timeout);
@@ -447,32 +476,13 @@ static int sam34_settimeout(FAR struct timer_lowerhalf_s *lower,
return -ERANGE;
}
+ priv->timeout = timeout; /* Intended timeout */
+ priv->clkticks = (((uint64_t)timeout * TC_FCLK) / 1000000); /* Actual clock ticks */
+ timeout = (1000000ULL * priv->clkticks) / TC_FCLK; /* Truncated timeout */
+ priv->adjustment = priv->timeout - timeout; /* Truncated time to be added to next interval (dither) */
- /* TODOR: -1 or no? */
-
- reload = (((uint64_t)timeout * TC_FCLK) / 1000000) - 1;
-
- /* Make sure that the final reload value is within range */
- /* TODOR: +1 or no? */
-
- if (reload > TC_CV_MASK)
- {
- reload = TC_CV_MASK;
- }
-
- /* Calculate and save the actual timeout value in milliseconds:
- *
- * timeout = 1000 * (reload + 1) / Fwdt
- */
-
- priv->timeout = 1000 * (reload + 1) / TC_FCLK;
-
- /* Remember the selected values */
-
- priv->reload = reload;
-
- tcvdbg("fwdt=%d reload=%d timout=%d\n",
- TC_FCLK, reload, priv->timeout);
+ tcvdbg("fwdt=%d reload=%d timout=%d, adjustment=%d\n",
+ TC_FCLK, reload, priv->timeout, priv->adjustment);
/* Don't commit to MR register until started! */
@@ -501,53 +511,28 @@ static int sam34_settimeout(FAR struct timer_lowerhalf_s *lower,
*
****************************************************************************/
-static xcpt_t sam34_capture(FAR struct timer_lowerhalf_s *lower,
- xcpt_t handler)
+static tccb_t sam34_capture(FAR struct timer_lowerhalf_s *lower,
+ tccb_t handler)
{
-#if 0 // TODO
FAR struct sam34_lowerhalf_s *priv = (FAR struct sam34_lowerhalf_s *)lower;
irqstate_t flags;
- xcpt_t oldhandler;
- uint16_t regval;
+ tccb_t oldhandler;
+
+ flags = irqsave();
DEBUGASSERT(priv);
tcvdbg("Entry: handler=%p\n", handler);
/* Get the old handler return value */
- flags = irqsave();
+
oldhandler = priv->handler;
/* Save the new handler */
priv->handler = handler;
- /* Are we attaching or detaching the handler? */
-
- regval = sam34_getreg(SAM_TC_CFR);
- if (handler)
- {
- /* Attaching... Enable the EWI interrupt */
-
- regval |= WWDG_CFR_EWI;
- sam34_putreg(regval, SAM_TC_CFR);
-
- up_enable_irq(STM32_IRQ_WWDG);
- }
- else
- {
- /* Detaching... Disable the EWI interrupt */
-
- regval &= ~WWDG_CFR_EWI;
- sam34_putreg(regval, SAM_TC_CFR);
-
- up_disable_irq(STM32_IRQ_WWDG);
- }
-
irqrestore(flags);
return oldhandler;
-#endif
- ASSERT(0);
- return NULL;
}
/****************************************************************************
@@ -560,7 +545,7 @@ static xcpt_t sam34_capture(FAR struct timer_lowerhalf_s *lower,
* Input Parameters:
* lower - A pointer the publicly visible representation of the "lower-half"
* driver state structure.
- * cmd - The ioctol command value
+ * cmd - The ioctl command value
* arg - The optional argument that accompanies the 'cmd'. The
* interpretation of this argument depends on the particular
* command.