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
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

View File

@ -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.