SAM3/4: Enhanced timer/counter driver from Bob Doiron

This commit is contained in:
Gregory Nutt 2014-04-30 14:10:02 -06:00
parent d897b4de76
commit aa44b8b588
2 changed files with 96 additions and 89 deletions

View File

@ -475,45 +475,54 @@ config SAM34_SSC
config SAM34_TC0 config SAM34_TC0
bool "Timer/Counter 0 (TC0)" bool "Timer/Counter 0 (TC0)"
default n default n
select SAM34_TC
config SAM34_TC1 config SAM34_TC1
bool "Timer/Counter 1 (TC1)" bool "Timer/Counter 1 (TC1)"
default n default n
select SAM34_TC
config SAM34_TC2 config SAM34_TC2
bool "Timer/Counter 2 (TC2)" bool "Timer/Counter 2 (TC2)"
default n default n
depends on ARCH_CHIP_SAM3U || ARCH_CHIP_SAM3X || ARCH_CHIP_SAM3A || ARCH_CHIP_SAM4S || ARCH_CHIP_SAM4E depends on ARCH_CHIP_SAM3U || ARCH_CHIP_SAM3X || ARCH_CHIP_SAM3A || ARCH_CHIP_SAM4S || ARCH_CHIP_SAM4E
select SAM34_TC
config SAM34_TC3 config SAM34_TC3
bool "Timer/Counter 3 (TC3)" bool "Timer/Counter 3 (TC3)"
default n default n
depends on ARCH_CHIP_SAM3X || ARCH_CHIP_SAM3A || ARCH_CHIP_SAM4S || ARCH_CHIP_SAM4E depends on ARCH_CHIP_SAM3X || ARCH_CHIP_SAM3A || ARCH_CHIP_SAM4S || ARCH_CHIP_SAM4E
select SAM34_TC
config SAM34_TC4 config SAM34_TC4
bool "Timer/Counter 4 (TC4)" bool "Timer/Counter 4 (TC4)"
default n default n
depends on ARCH_CHIP_SAM3X || ARCH_CHIP_SAM3A || ARCH_CHIP_SAM4S || ARCH_CHIP_SAM4E depends on ARCH_CHIP_SAM3X || ARCH_CHIP_SAM3A || ARCH_CHIP_SAM4S || ARCH_CHIP_SAM4E
select SAM34_TC
config SAM34_TC5 config SAM34_TC5
bool "Timer/Counter 5 (TC5)" bool "Timer/Counter 5 (TC5)"
default n default n
depends on ARCH_CHIP_SAM3X || ARCH_CHIP_SAM3A || ARCH_CHIP_SAM4S || ARCH_CHIP_SAM4E depends on ARCH_CHIP_SAM3X || ARCH_CHIP_SAM3A || ARCH_CHIP_SAM4S || ARCH_CHIP_SAM4E
select SAM34_TC
config SAM34_TC6 config SAM34_TC6
bool "Timer/Counter 6 (TC6)" bool "Timer/Counter 6 (TC6)"
default n default n
depends on ARCH_CHIP_SAM3X || ARCH_CHIP_SAM3A || ARCH_CHIP_SAM4E depends on ARCH_CHIP_SAM3X || ARCH_CHIP_SAM3A || ARCH_CHIP_SAM4E
select SAM34_TC
config SAM34_TC7 config SAM34_TC7
bool "Timer/Counter 7 (TC7)" bool "Timer/Counter 7 (TC7)"
default n default n
depends on ARCH_CHIP_SAM3X || ARCH_CHIP_SAM3A || ARCH_CHIP_SAM4E depends on ARCH_CHIP_SAM3X || ARCH_CHIP_SAM3A || ARCH_CHIP_SAM4E
select SAM34_TC
config SAM34_TC8 config SAM34_TC8
bool "Timer/Counter 6 (TC8)" bool "Timer/Counter 6 (TC8)"
default n default n
depends on ARCH_CHIP_SAM3X || ARCH_CHIP_SAM3A || ARCH_CHIP_SAM4E depends on ARCH_CHIP_SAM3X || ARCH_CHIP_SAM3A || ARCH_CHIP_SAM4E
select SAM34_TC
config SAM34_TRNG config SAM34_TRNG
bool "True Random Number Generator (TRNG)" bool "True Random Number Generator (TRNG)"
@ -1177,8 +1186,8 @@ config SAM34_HSMCI_CMDDEBUG
endmenu # HSMCI device driver options endmenu # HSMCI device driver options
endif # SAM34_HSMCI endif # SAM34_HSMCI
if SAM34_UDP
menu "AT91SAM3/4 USB Full Speed Device Controller driver (DCD) options" menu "AT91SAM3/4 USB Full Speed Device Controller driver (DCD) options"
depends on SAM34_UDP
config SAM34_UDP_REGDEBUG config SAM34_UDP_REGDEBUG
bool "Enable low-level UDP register debug" bool "Enable low-level UDP register debug"
@ -1186,10 +1195,23 @@ config SAM34_UDP_REGDEBUG
depends on DEBUG depends on DEBUG
endmenu # USB Full Speed Device Controller driver (DCD) options 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" menu "AT91SAM3/4 Watchdog Configuration"
depends on SAM34_WDT
config WDT_ENABLED_ON_RESET config WDT_ENABLED_ON_RESET
bool "Watchdog Enabled on reset" bool "Watchdog Enabled on reset"
@ -1248,6 +1270,6 @@ config WDT_THREAD_PRIORITY
config WDT_THREAD_STACKSIZE config WDT_THREAD_STACKSIZE
int "Watchdog Thread Stacksize" int "Watchdog Thread Stacksize"
default 1024 default 1024
endif # WDT_THREAD endif # WDT_THREAD
endmenu #"AT91SAM3/4 Watchdog device driver options" endmenu #"AT91SAM3/4 Watchdog device driver options"
endif # SAM34_WDT

View File

@ -54,8 +54,6 @@
#include "sam_tc.h" #include "sam_tc.h"
#include "sam_periphclks.h" #include "sam_periphclks.h"
//#define CONFIG_SAM34_TC_REGDEBUG
#if defined(CONFIG_TIMER) && (defined(CONFIG_SAM34_TC0) || \ #if defined(CONFIG_TIMER) && (defined(CONFIG_SAM34_TC0) || \
defined(CONFIG_SAM34_TC1) || defined(CONFIG_SAM34_TC2) || \ defined(CONFIG_SAM34_TC1) || defined(CONFIG_SAM34_TC2) || \
defined(CONFIG_SAM34_TC3) || defined(CONFIG_SAM34_TC4) || \ defined(CONFIG_SAM34_TC3) || defined(CONFIG_SAM34_TC4) || \
@ -98,10 +96,15 @@
struct sam34_lowerhalf_s struct sam34_lowerhalf_s
{ {
FAR const struct timer_ops_s *ops; /* Lower half operations */ 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) */ /* Private data */
bool started; /* The timer has been started */
uint16_t reload; /* The 12-bit reload field reset value (WDV) */ 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; 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) # define sam34_putreg(val,addr) putreg32(val,addr)
#endif #endif
/* Interrupt hanlding *******************************************************/ /* Interrupt handling *******************************************************/
static int sam34_interrupt(int irq, FAR void *context); 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); FAR struct timer_status_s *status);
static int sam34_settimeout(FAR struct timer_lowerhalf_s *lower, static int sam34_settimeout(FAR struct timer_lowerhalf_s *lower,
uint32_t timeout); uint32_t timeout);
static xcpt_t sam34_capture(FAR struct timer_lowerhalf_s *lower, static tccb_t sam34_capture(FAR struct timer_lowerhalf_s *lower,
xcpt_t handler); tccb_t handler);
static int sam34_ioctl(FAR struct timer_lowerhalf_s *lower, int cmd, static int sam34_ioctl(FAR struct timer_lowerhalf_s *lower, int cmd,
unsigned long arg); unsigned long arg);
@ -269,12 +272,27 @@ static int sam34_interrupt(int irq, FAR void *context)
regval = sam34_getreg(SAM_TC0_SR); regval = sam34_getreg(SAM_TC0_SR);
if ((regval & TC_INT_CPCS) != 0) if ((regval & TC_INT_CPCS) != 0)
{ {
uint32_t timeout;
/* Is there a registered handler? */ /* Is there a registered handler? */
if (priv->handler) if (priv->handler && priv->handler(&priv->timeout))
{ {
priv->handler(irq, context); /* 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 */ /* 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"); tcvdbg("Entry\n");
DEBUGASSERT(priv); DEBUGASSERT(priv);
if(priv->started)
{
return -EINVAL;
}
sam_tc0_enableclk(); 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_WAVE - waveform mode
* TC_CMR_WAVSEL_UPAUTO - reset on RC compare (interval timer) * 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); mr_val |= (TC_CMR_WAVE + TC_CMR_WAVSEL_UPAUTO + TC_CMR_TCCLKS_TIMERCLOCK5);
sam34_putreg(mr_val, SAM_TC0_CMR); 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_getreg(SAM_TC0_SR);
sam34_putreg(TC_INT_CPCS, SAM_TC0_IMR);
sam34_putreg(TC_INT_CPCS, SAM_TC0_IER); 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; priv->started = true;
return OK; 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) 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"); tcvdbg("Entry\n");
sam34_putreg(TC_CCR_CLKDIS, SAM_TC0_CCR); // disable counter DEBUGASSERT(priv);
sam34_putreg(TC_INT_ALL, SAM_TC0_IDR); // disable all ints
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(); sam_tc0_disableclk();
priv->started = false;
return OK; return OK;
} }
@ -367,9 +397,9 @@ static int sam34_stop(FAR struct timer_lowerhalf_s *lower)
* Get the current timer status * Get the current timer status
* *
* Input Parameters: * Input Parameters:
* lower - A pointer the publicly visible representation of the "lower-half" * lower - A pointer the publicly visible representation of the "lower-half"
* driver state structure. * driver state structure.
* stawtus - The location to return the status information. * status - The location to return the status information.
* *
* Returned Values: * Returned Values:
* Zero on success; a negated errno value on failure. * 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) */ /* Get the time remaining until the timer expires (in microseconds) */
elapsed = sam34_getreg(SAM_TC0_CV); 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(" flags : %08x\n", status->flags);
tcvdbg(" timeout : %d\n", status->timeout); tcvdbg(" timeout : %d\n", status->timeout);
@ -422,7 +452,7 @@ static int sam34_getstatus(FAR struct timer_lowerhalf_s *lower,
* Input Parameters: * Input Parameters:
* lower - A pointer the publicly visible representation of the "lower-half" * lower - A pointer the publicly visible representation of the "lower-half"
* driver state structure. * driver state structure.
* timeout - The new timeout value in millisecnds. * timeout - The new timeout value in milliseconds.
* *
* Returned Values: * Returned Values:
* Zero on success; a negated errno value on failure. * 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) uint32_t timeout)
{ {
FAR struct sam34_lowerhalf_s *priv = (FAR struct sam34_lowerhalf_s *)lower; FAR struct sam34_lowerhalf_s *priv = (FAR struct sam34_lowerhalf_s *)lower;
uint32_t reload;
DEBUGASSERT(priv); DEBUGASSERT(priv);
tcvdbg("Entry: timeout=%d\n", timeout); tcvdbg("Entry: timeout=%d\n", timeout);
@ -447,32 +476,13 @@ static int sam34_settimeout(FAR struct timer_lowerhalf_s *lower,
return -ERANGE; 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? */ tcvdbg("fwdt=%d reload=%d timout=%d, adjustment=%d\n",
TC_FCLK, reload, priv->timeout, priv->adjustment);
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);
/* Don't commit to MR register until started! */ /* 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, static tccb_t sam34_capture(FAR struct timer_lowerhalf_s *lower,
xcpt_t handler) tccb_t handler)
{ {
#if 0 // TODO
FAR struct sam34_lowerhalf_s *priv = (FAR struct sam34_lowerhalf_s *)lower; FAR struct sam34_lowerhalf_s *priv = (FAR struct sam34_lowerhalf_s *)lower;
irqstate_t flags; irqstate_t flags;
xcpt_t oldhandler; tccb_t oldhandler;
uint16_t regval;
flags = irqsave();
DEBUGASSERT(priv); DEBUGASSERT(priv);
tcvdbg("Entry: handler=%p\n", handler); tcvdbg("Entry: handler=%p\n", handler);
/* Get the old handler return value */ /* Get the old handler return value */
flags = irqsave();
oldhandler = priv->handler; oldhandler = priv->handler;
/* Save the new handler */ /* Save the new handler */
priv->handler = 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); irqrestore(flags);
return oldhandler; return oldhandler;
#endif
ASSERT(0);
return NULL;
} }
/**************************************************************************** /****************************************************************************
@ -560,7 +545,7 @@ static xcpt_t sam34_capture(FAR struct timer_lowerhalf_s *lower,
* Input Parameters: * Input Parameters:
* lower - A pointer the publicly visible representation of the "lower-half" * lower - A pointer the publicly visible representation of the "lower-half"
* driver state structure. * driver state structure.
* cmd - The ioctol command value * cmd - The ioctl command value
* arg - The optional argument that accompanies the 'cmd'. The * arg - The optional argument that accompanies the 'cmd'. The
* interpretation of this argument depends on the particular * interpretation of this argument depends on the particular
* command. * command.