Updated system timer logic from Bob Doiron
This commit is contained in:
parent
3240383133
commit
f7485ea962
@ -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
|
||||
|
||||
|
@ -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;
|
||||
@ -323,14 +328,13 @@ static int sam34_start(FAR struct timer_lowerhalf_s *lower)
|
||||
tcvdbg("Entry\n");
|
||||
DEBUGASSERT(priv);
|
||||
|
||||
if(priv->started)
|
||||
if (priv->started)
|
||||
{
|
||||
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);
|
||||
|
||||
|
@ -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
|
||||
****************************************************************************/
|
||||
@ -141,52 +156,104 @@ int sam_timerinitialize(void)
|
||||
|
||||
/* Initialize and register the timer devices */
|
||||
|
||||
#if defined(CONFIG_SAM34_TC0)
|
||||
#if defined(CONFIG_SAM34_TC0)
|
||||
tcvdbg("Initializing %s...\n", CONFIG_TIMER0_DEVPATH);
|
||||
sam_tcinitialize(CONFIG_TIMER0_DEVPATH, SAM_IRQ_TC0);
|
||||
#endif
|
||||
|
||||
#if defined(CONFIG_SAM34_TC1)
|
||||
#if defined(CONFIG_SAM34_TC1)
|
||||
tcvdbg("Initializing %s...\n", CONFIG_TIMER1_DEVPATH);
|
||||
sam_tcinitialize(CONFIG_TIMER1_DEVPATH, SAM_IRQ_TC1);
|
||||
#endif
|
||||
|
||||
#if defined(CONFIG_SAM34_TC2)
|
||||
#if defined(CONFIG_SAM34_TC2)
|
||||
tcvdbg("Initializing %s...\n", CONFIG_TIMER2_DEVPATH);
|
||||
sam_tcinitialize(CONFIG_TIMER2_DEVPATH, SAM_IRQ_TC2);
|
||||
#endif
|
||||
|
||||
#if defined(CONFIG_SAM34_TC3)
|
||||
#if defined(CONFIG_SAM34_TC3)
|
||||
tcvdbg("Initializing %s...\n", CONFIG_TIMER3_DEVPATH);
|
||||
sam_tcinitialize(CONFIG_TIMER3_DEVPATH, SAM_IRQ_TC3);
|
||||
#endif
|
||||
|
||||
#if defined(CONFIG_SAM34_TC4)
|
||||
#if defined(CONFIG_SAM34_TC4)
|
||||
tcvdbg("Initializing %s...\n", CONFIG_TIMER4_DEVPATH);
|
||||
sam_tcinitialize(CONFIG_TIMER4_DEVPATH, SAM_IRQ_TC4);
|
||||
#endif
|
||||
|
||||
#if defined(CONFIG_SAM34_TC5)
|
||||
#if defined(CONFIG_SAM34_TC5)
|
||||
tcvdbg("Initializing %s...\n", CONFIG_TIMER5_DEVPATH);
|
||||
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);
|
||||
|
@ -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
|
||||
|
Loading…
Reference in New Issue
Block a user