Updated system timer logic from Bob Doiron

This commit is contained in:
Gregory Nutt
2014-04-30 14:46:26 -06:00
parent 3240383133
commit f7485ea962
4 changed files with 168 additions and 47 deletions
+2 -1
View File
@@ -181,7 +181,8 @@ void up_initialize(void)
/* Initialize the system timer interrupt */ /* Initialize the system timer interrupt */
#if !defined(CONFIG_SUPPRESS_INTERRUPTS) && !defined(CONFIG_SUPPRESS_TIMER_INTS) #if !defined(CONFIG_SUPPRESS_INTERRUPTS) && !defined(CONFIG_SUPPRESS_TIMER_INTS) && \
!defined(CONFIG_SYSTEMTICK_EXTCLK)
up_timerinit(); up_timerinit();
#endif #endif
+76 -33
View File
@@ -105,6 +105,7 @@ struct sam34_lowerhalf_s
uint32_t adjustment; /* time lost due to clock resolution truncation (us) */ uint32_t adjustment; /* time lost due to clock resolution truncation (us) */
uint32_t clkticks; /* actual clock ticks for current interval */ uint32_t clkticks; /* actual clock ticks for current interval */
bool started; /* The timer has been started */ bool started; /* The timer has been started */
uint16_t periphid; /* peripheral id */
uint16_t debug; uint16_t debug;
}; };
@@ -155,7 +156,11 @@ static const struct timer_ops_s g_tcops =
/* "Lower half" driver state */ /* "Lower half" driver state */
static struct sam34_lowerhalf_s g_tcdev; /* TODO - allocating all 6 now, even though we might not need them.
* May want to allocate the right number to not be wasteful.
*/
static struct sam34_lowerhalf_s g_tcdevs[6];
/**************************************************************************** /****************************************************************************
* Private Functions * Private Functions
@@ -245,7 +250,6 @@ static void sam34_putreg(uint32_t val, uint32_t addr)
} }
#endif #endif
/**************************************************************************** /****************************************************************************
* Name: sam34_interrupt * Name: sam34_interrupt
* *
@@ -262,14 +266,15 @@ static void sam34_putreg(uint32_t val, uint32_t addr)
static int sam34_interrupt(int irq, FAR void *context) static int sam34_interrupt(int irq, FAR void *context)
{ {
FAR struct sam34_lowerhalf_s *priv = &g_tcdev; FAR struct sam34_lowerhalf_s *priv = &g_tcdevs[irq-SAM_IRQ_TC0];
uint16_t regval; uint16_t regval;
tcvdbg("Entry\n"); tcvdbg("Entry\n");
DEBUGASSERT((irq >= SAM_IRQ_TC0) && (irq <= SAM_IRQ_TC5));
/* Check if the interrupt is really pending */ /* Check if the interrupt is really pending */
regval = sam34_getreg(SAM_TC0_SR); regval = sam34_getreg(priv->base + SAM_TC_SR_OFFSET);
if ((regval & TC_INT_CPCS) != 0) if ((regval & TC_INT_CPCS) != 0)
{ {
uint32_t timeout; uint32_t timeout;
@@ -284,17 +289,17 @@ static int sam34_interrupt(int irq, FAR void *context)
/* Set next interval interval. TODO: make sure the interval is not so soon it will be missed! */ /* Set next interval interval. TODO: make sure the interval is not so soon it will be missed! */
sam34_putreg(priv->clkticks, SAM_TC0_RC); sam34_putreg(priv->clkticks, priv->base + SAM_TC_RC_OFFSET);
timeout = (1000000ULL * priv->clkticks) / TC_FCLK; /* trucated timeout */ timeout = (1000000ULL * priv->clkticks) / TC_FCLK; /* trucated timeout */
priv->adjustment = (priv->adjustment + priv->timeout) - timeout; /* truncated time to be added to next interval (dither) */ priv->adjustment = (priv->adjustment + priv->timeout) - timeout; /* truncated time to be added to next interval (dither) */
} }
else /* stop */ else /* stop */
{ {
sam34_stop((FAR struct timer_lowerhalf_s *)&g_tcdev); sam34_stop((FAR struct timer_lowerhalf_s *)priv);
} }
/* TC_INT_CPCS is cleared by reading SAM_TC0_SR */ /* TC_INT_CPCS is cleared by reading SAM_TCx_SR */
} }
return OK; return OK;
@@ -323,14 +328,13 @@ static int sam34_start(FAR struct timer_lowerhalf_s *lower)
tcvdbg("Entry\n"); tcvdbg("Entry\n");
DEBUGASSERT(priv); DEBUGASSERT(priv);
if(priv->started) if (priv->started)
{ {
return -EINVAL; return -EINVAL;
} }
sam_tc0_enableclk(); sam_enableperiph0(priv->periphid); /* Enable peripheral clock */
sam34_putreg(TC_CCR_CLKDIS, priv->base + SAM_TC_CCR_OFFSET); /* 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)
@@ -338,19 +342,19 @@ 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, priv->base + SAM_TC_CMR_OFFSET);
sam34_putreg(priv->clkticks, SAM_TC0_RC); /* Set interval */ sam34_putreg(priv->clkticks, priv->base + SAM_TC_RC_OFFSET); /* Set interval */
if (priv->handler) if (priv->handler)
{ {
/* Clear status and enable interrupt */ /* Clear status and enable interrupt */
sam34_getreg(SAM_TC0_SR); sam34_getreg(priv->base + SAM_TC_SR_OFFSET); /* Clear status */
sam34_putreg(TC_INT_CPCS, SAM_TC0_IER); sam34_putreg(TC_INT_CPCS, priv->base + SAM_TC_IER_OFFSET); /* Enable interrupt */
} }
sam34_putreg(TC_CCR_SWTRG + TC_CCR_CLKEN, SAM_TC0_CCR); /* Start counter */ sam34_putreg(TC_CCR_SWTRG + TC_CCR_CLKEN, priv->base + SAM_TC_CCR_OFFSET); /* Start counter */
priv->started = true; priv->started = true;
return OK; return OK;
@@ -382,9 +386,10 @@ static int sam34_stop(FAR struct timer_lowerhalf_s *lower)
return -EINVAL; return -EINVAL;
} }
sam34_putreg(TC_CCR_CLKDIS, SAM_TC0_CCR); /* Disable counter */ sam34_putreg(TC_CCR_CLKDIS, priv->base + SAM_TC_CCR_OFFSET); /* Disable counter */
sam34_putreg(TC_INT_ALL, SAM_TC0_IDR); /* Disable all ints */ sam34_putreg(TC_INT_ALL, priv->base + SAM_TC_IDR_OFFSET); /* Disable all ints */
sam_tc0_disableclk(); sam_disableperiph0(priv->periphid); /* Disable peripheral clock */
priv->started = false; priv->started = false;
return OK; return OK;
@@ -434,7 +439,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(priv->base + SAM_TC_CV_OFFSET);
status->timeleft = (priv->timeout * elapsed) / (priv->clkticks + 1); /* TODO - check on this +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);
@@ -589,31 +594,69 @@ static int sam34_ioctl(FAR struct timer_lowerhalf_s *lower, int cmd,
void sam_tcinitialize(FAR const char *devpath, int irq) void sam_tcinitialize(FAR const char *devpath, int irq)
{ {
FAR struct sam34_lowerhalf_s *priv = &g_tcdev; FAR struct sam34_lowerhalf_s *priv = &g_tcdevs[irq-SAM_IRQ_TC0];
tcvdbg("Entry: devpath=%s\n", devpath); tcvdbg("Entry: devpath=%s\n", devpath);
DEBUGASSERT((irq >= SAM_IRQ_TC0) && (irq <= SAM_IRQ_TC5));
/* NOTE we assume that clocking to the IWDG has already been provided by
* the RCC initialization logic.
*/
/* Initialize the driver state structure. Here we assume: (1) the state /* Initialize the driver state structure. Here we assume: (1) the state
* structure lies in .bss and was zeroed at reset time. (2) This function * structure lies in .bss and was zeroed at reset time. (2) This function
* is only called once so it is never necessary to re-zero the structure. * is only called once so it is never necessary to re-zero the structure.
*/ */
priv->ops = &g_tcops; switch(irq)
{
#if defined(CONFIG_SAM34_TC0)
case SAM_IRQ_TC0:
priv->base = SAM_TC0_BASE;
priv->periphid = SAM_PID_TC0;
break;
#endif
/* TODO: Add irq + switch in sam34_interrupt or something (also need register #if defined(CONFIG_SAM34_TC1)
* base address... case SAM_IRQ_TC1:
*/ priv->base = SAM_TC1_BASE;
priv->periphid = SAM_PID_TC1;
break;
#endif
#if defined(CONFIG_SAM34_TC2)
case SAM_IRQ_TC2:
priv->base = SAM_TC2_BASE;
priv->periphid = SAM_PID_TC2;
break;
#endif
#if defined(CONFIG_SAM34_TC3)
case SAM_IRQ_TC3:
priv->base = SAM_TC3_BASE;
priv->periphid = SAM_PID_TC3;
break;
#endif
#if defined(CONFIG_SAM34_TC4)
case SAM_IRQ_TC4:
priv->base = SAM_TC4_BASE;
priv->periphid = SAM_PID_TC4;
break;
#endif
#if defined(CONFIG_SAM34_TC5)
case SAM_IRQ_TC5:
priv->base = SAM_TC5_BASE;
priv->periphid = SAM_PID_TC5;
break;
#endif
default:
ASSERT(0);
}
priv->ops = &g_tcops;
(void)irq_attach(irq, sam34_interrupt); (void)irq_attach(irq, sam34_interrupt);
/* enable interrupt. /* Enable NVIC interrupt. */
*
* TODO: May want to enable/disable in start/stop...
*/
up_enable_irq(irq); up_enable_irq(irq);
+80 -13
View File
@@ -116,12 +116,27 @@
* Private Functions * Private Functions
****************************************************************************/ ****************************************************************************/
#if defined(CONFIG_SYSTEMTICK_EXTCLK) && !defined(CONFIG_SUPPRESS_INTERRUPTS) && \
!defined(CONFIG_SUPPRESS_TIMER_INTS)
static bool systemtick(FAR uint32_t *next_interval_us)
{
sched_process_timer();
return true; // reload, no change to interval
}
#endif /* CONFIG_SYSTEMTICK_EXTCLK && !CONFIG_SUPPRESS_INTERRUPTS && !CONFIG_SUPPRESS_TIMER_INTS */
#if defined(CONFIG_SCHED_CPULOAD) && defined(CONFIG_SCHED_CPULOAD_EXTCLK)
static bool calc_cpuload(FAR uint32_t *next_interval_us) static bool calc_cpuload(FAR uint32_t *next_interval_us)
{ {
sched_process_cpuload(); sched_process_cpuload();
return TRUE; /* Reload, no change to interval */ return TRUE; /* Reload, no change to interval */
} }
#endif /* CONFIG_SCHED_CPULOAD && CONFIG_SCHED_CPULOAD_EXTCLK */
/**************************************************************************** /****************************************************************************
* Public Functions * Public Functions
****************************************************************************/ ****************************************************************************/
@@ -141,52 +156,104 @@ int sam_timerinitialize(void)
/* Initialize and register the timer devices */ /* Initialize and register the timer devices */
#if defined(CONFIG_SAM34_TC0) #if defined(CONFIG_SAM34_TC0)
tcvdbg("Initializing %s...\n", CONFIG_TIMER0_DEVPATH); tcvdbg("Initializing %s...\n", CONFIG_TIMER0_DEVPATH);
sam_tcinitialize(CONFIG_TIMER0_DEVPATH, SAM_IRQ_TC0); sam_tcinitialize(CONFIG_TIMER0_DEVPATH, SAM_IRQ_TC0);
#endif #endif
#if defined(CONFIG_SAM34_TC1) #if defined(CONFIG_SAM34_TC1)
tcvdbg("Initializing %s...\n", CONFIG_TIMER1_DEVPATH); tcvdbg("Initializing %s...\n", CONFIG_TIMER1_DEVPATH);
sam_tcinitialize(CONFIG_TIMER1_DEVPATH, SAM_IRQ_TC1); sam_tcinitialize(CONFIG_TIMER1_DEVPATH, SAM_IRQ_TC1);
#endif #endif
#if defined(CONFIG_SAM34_TC2) #if defined(CONFIG_SAM34_TC2)
tcvdbg("Initializing %s...\n", CONFIG_TIMER2_DEVPATH); tcvdbg("Initializing %s...\n", CONFIG_TIMER2_DEVPATH);
sam_tcinitialize(CONFIG_TIMER2_DEVPATH, SAM_IRQ_TC2); sam_tcinitialize(CONFIG_TIMER2_DEVPATH, SAM_IRQ_TC2);
#endif #endif
#if defined(CONFIG_SAM34_TC3) #if defined(CONFIG_SAM34_TC3)
tcvdbg("Initializing %s...\n", CONFIG_TIMER3_DEVPATH); tcvdbg("Initializing %s...\n", CONFIG_TIMER3_DEVPATH);
sam_tcinitialize(CONFIG_TIMER3_DEVPATH, SAM_IRQ_TC3); sam_tcinitialize(CONFIG_TIMER3_DEVPATH, SAM_IRQ_TC3);
#endif #endif
#if defined(CONFIG_SAM34_TC4) #if defined(CONFIG_SAM34_TC4)
tcvdbg("Initializing %s...\n", CONFIG_TIMER4_DEVPATH); tcvdbg("Initializing %s...\n", CONFIG_TIMER4_DEVPATH);
sam_tcinitialize(CONFIG_TIMER4_DEVPATH, SAM_IRQ_TC4); sam_tcinitialize(CONFIG_TIMER4_DEVPATH, SAM_IRQ_TC4);
#endif #endif
#if defined(CONFIG_SAM34_TC5) #if defined(CONFIG_SAM34_TC5)
tcvdbg("Initializing %s...\n", CONFIG_TIMER5_DEVPATH); tcvdbg("Initializing %s...\n", CONFIG_TIMER5_DEVPATH);
sam_tcinitialize(CONFIG_TIMER5_DEVPATH, SAM_IRQ_TC5); sam_tcinitialize(CONFIG_TIMER5_DEVPATH, SAM_IRQ_TC5);
#endif #endif
/* Open the timer device */ #if defined(CONFIG_SYSTEMTICK_EXTCLK) && !defined(CONFIG_SUPPRESS_INTERRUPTS) && \
!defined(CONFIG_SUPPRESS_TIMER_INTS)
/* System Timer Initialization */
#if defined(CONFIG_SCHED_CPULOAD) && defined(CONFIG_SCHED_CPULOAD_EXTCLK) tcvdbg("Opening %s\n", CONFIG_SAM4S_XPLAINED_PRO_SCHED_TIMER_DEVPATH);
tcvdbg("Opening.\n");
fd = open(CONFIG_SAM4S_XPLAINED_PRO_CPULOAD_TIMER_DEVPATH, O_RDONLY); fd = open(CONFIG_SAM4S_XPLAINED_PRO_SCHED_TIMER_DEVPATH, O_RDONLY);
if (fd < 0) if (fd < 0)
{ {
tcdbg("open %s failed: %d\n", CONFIG_SAM4S_XPLAINED_PRO_CPULOAD_TIMER_DEVPATH, errno); tcdbg("open %s failed: %d\n",
CONFIG_SAM4S_XPLAINED_PRO_SCHED_TIMER_DEVPATH, errno);
goto errout; goto errout;
} }
/* Set the timeout */ /* Set the timeout */
tcvdbg("Timeout = %d.\n", TINTERVAL); tcvdbg("Interval = %d us.\n", (unsigned long)MSEC_PER_TICK * 1000);
ret = ioctl(fd, TCIOC_SETTIMEOUT, (unsigned long)1000000 / CONFIG_SCHED_CPULOAD_TICKSPERSEC); ret = ioctl(fd, TCIOC_SETTIMEOUT, (unsigned long)MSEC_PER_TICK * 1000);
if (ret < 0)
{
tcdbg("ioctl(TCIOC_SETTIMEOUT) failed: %d\n", errno);
goto errout_with_dev;
}
/* install user callback */
{
struct timer_capture_s tccb;
tccb.newhandler = systemtick;
tccb.oldhandler = NULL;
ret = ioctl(fd, TCIOC_SETHANDLER, (unsigned long)&tccb);
if (ret < 0)
{
tcdbg("ioctl(TCIOC_SETHANDLER) failed: %d\n", errno);
goto errout_with_dev;
}
}
/* Start the timer */
tcvdbg("Starting.\n");
ret = ioctl(fd, TCIOC_START, 0);
if (ret < 0)
{
tcdbg("ioctl(TCIOC_START) failed: %d\n", errno);
goto errout_with_dev;
}
#endif
#if defined(CONFIG_SCHED_CPULOAD) && defined(CONFIG_SCHED_CPULOAD_EXTCLK)
/* CPU Load initialization */
tcvdbg("Opening %s\n", CONFIG_SAM4S_XPLAINED_PRO_CPULOAD_TIMER_DEVPATH);
fd = open(CONFIG_SAM4S_XPLAINED_PRO_CPULOAD_TIMER_DEVPATH, O_RDONLY);
if (fd < 0)
{
tcdbg("open %s failed: %d\n",
CONFIG_SAM4S_XPLAINED_PRO_CPULOAD_TIMER_DEVPATH, errno);
goto errout;
}
/* Set the timeout */
tcvdbg("Interval = %d us.\n", (unsigned long)1000000 / CONFIG_SCHED_CPULOAD_TICKSPERSEC);
ret = ioctl(fd, TCIOC_SETTIMEOUT,
(unsigned long)1000000 / CONFIG_SCHED_CPULOAD_TICKSPERSEC);
if (ret < 0) if (ret < 0)
{ {
tcdbg("ioctl(TCIOC_SETTIMEOUT) failed: %d\n", errno); tcdbg("ioctl(TCIOC_SETTIMEOUT) failed: %d\n", errno);
+10
View File
@@ -61,6 +61,16 @@ config MSEC_PER_TICK
may be defined to inform NuttX that the processor hardware is providing may be defined to inform NuttX that the processor hardware is providing
system timer interrupts at some interrupt interval other than 10 msec. system timer interrupts at some interrupt interval other than 10 msec.
config SYSTEMTICK_EXTCLK
bool "Use external clock"
default n
---help---
Use external clock for system tick. When enabled, the platform-specific
logic must start it's own timer interrupt to make periodic calls to the
sched_process_timer() or the functions called within. The purpose is
to move the scheduling off the processor to enter low power states that
would disable that clock. Platform-specific logic must also provide
config SYSTEM_TIME64 config SYSTEM_TIME64
bool "64-bit system clock" bool "64-bit system clock"
default n default n