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

View File

@ -181,7 +181,8 @@ void up_initialize(void)
/* 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();
#endif

View File

@ -105,6 +105,7 @@ struct sam34_lowerhalf_s
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 periphid; /* peripheral id */
uint16_t debug;
};
@ -155,7 +156,11 @@ static const struct timer_ops_s g_tcops =
/* "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
@ -245,7 +250,6 @@ static void sam34_putreg(uint32_t val, uint32_t addr)
}
#endif
/****************************************************************************
* 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)
{
FAR struct sam34_lowerhalf_s *priv = &g_tcdev;
FAR struct sam34_lowerhalf_s *priv = &g_tcdevs[irq-SAM_IRQ_TC0];
uint16_t regval;
tcvdbg("Entry\n");
DEBUGASSERT((irq >= SAM_IRQ_TC0) && (irq <= SAM_IRQ_TC5));
/* 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)
{
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! */
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 */
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);
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;
@ -328,9 +333,8 @@ static int sam34_start(FAR struct timer_lowerhalf_s *lower)
return -EINVAL;
}
sam_tc0_enableclk();
sam34_putreg(TC_CCR_CLKDIS, SAM_TC0_CCR); /* Disable counter */
sam_enableperiph0(priv->periphid); /* Enable peripheral clock */
sam34_putreg(TC_CCR_CLKDIS, priv->base + SAM_TC_CCR_OFFSET); /* Disable counter */
/* TC_CMR_WAVE - waveform mode
* 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);
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)
{
/* Clear status and enable interrupt */
sam34_getreg(SAM_TC0_SR);
sam34_putreg(TC_INT_CPCS, SAM_TC0_IER);
sam34_getreg(priv->base + SAM_TC_SR_OFFSET); /* Clear status */
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;
return OK;
@ -382,9 +386,10 @@ static int sam34_stop(FAR struct timer_lowerhalf_s *lower)
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();
sam34_putreg(TC_CCR_CLKDIS, priv->base + SAM_TC_CCR_OFFSET); /* Disable counter */
sam34_putreg(TC_INT_ALL, priv->base + SAM_TC_IDR_OFFSET); /* Disable all ints */
sam_disableperiph0(priv->periphid); /* Disable peripheral clock */
priv->started = false;
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) */
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 */
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)
{
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);
/* NOTE we assume that clocking to the IWDG has already been provided by
* the RCC initialization logic.
*/
DEBUGASSERT((irq >= SAM_IRQ_TC0) && (irq <= SAM_IRQ_TC5));
/* Initialize the driver state structure. Here we assume: (1) the state
* 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.
*/
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
* base address...
*/
#if defined(CONFIG_SAM34_TC1)
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);
/* enable interrupt.
*
* TODO: May want to enable/disable in start/stop...
*/
/* Enable NVIC interrupt. */
up_enable_irq(irq);

View File

@ -116,12 +116,27 @@
* 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)
{
sched_process_cpuload();
return TRUE; /* Reload, no change to interval */
}
#endif /* CONFIG_SCHED_CPULOAD && CONFIG_SCHED_CPULOAD_EXTCLK */
/****************************************************************************
* Public Functions
****************************************************************************/
@ -171,22 +186,74 @@ int sam_timerinitialize(void)
sam_tcinitialize(CONFIG_TIMER5_DEVPATH, SAM_IRQ_TC5);
#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.\n");
tcvdbg("Opening %s\n", CONFIG_SAM4S_XPLAINED_PRO_SCHED_TIMER_DEVPATH);
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)
{
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;
}
/* Set the timeout */
tcvdbg("Timeout = %d.\n", TINTERVAL);
ret = ioctl(fd, TCIOC_SETTIMEOUT, (unsigned long)1000000 / CONFIG_SCHED_CPULOAD_TICKSPERSEC);
tcvdbg("Interval = %d us.\n", (unsigned long)MSEC_PER_TICK * 1000);
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)
{
tcdbg("ioctl(TCIOC_SETTIMEOUT) failed: %d\n", errno);

View File

@ -61,6 +61,16 @@ config MSEC_PER_TICK
may be defined to inform NuttX that the processor hardware is providing
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
bool "64-bit system clock"
default n