SAM3/4: Enhanced timer/counter driver from Bob Doiron
This commit is contained in:
parent
d897b4de76
commit
aa44b8b588
@ -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
|
||||
|
@ -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.
|
||||
|
Loading…
Reference in New Issue
Block a user