diff --git a/arch/arm/src/sam34/Kconfig b/arch/arm/src/sam34/Kconfig index ad076de882..62ddf0743d 100644 --- a/arch/arm/src/sam34/Kconfig +++ b/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/arch/arm/src/sam34/sam_tc.c b/arch/arm/src/sam34/sam_tc.c index 99fbce6467..cb48f84b6a 100644 --- a/arch/arm/src/sam34/sam_tc.c +++ b/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.