From f7485ea962c86c655f723f7bb925ae8468d189a6 Mon Sep 17 00:00:00 2001 From: Gregory Nutt Date: Wed, 30 Apr 2014 14:46:26 -0600 Subject: [PATCH] Updated system timer logic from Bob Doiron --- arch/arm/src/common/up_initialize.c | 3 +- arch/arm/src/sam34/sam_tc.c | 109 +++++++++++++++++------- configs/sam4s-xplained-pro/src/sam_tc.c | 93 +++++++++++++++++--- sched/Kconfig | 10 +++ 4 files changed, 168 insertions(+), 47 deletions(-) diff --git a/arch/arm/src/common/up_initialize.c b/arch/arm/src/common/up_initialize.c index e5158e91e5..acdd99bdb2 100644 --- a/arch/arm/src/common/up_initialize.c +++ b/arch/arm/src/common/up_initialize.c @@ -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 diff --git a/arch/arm/src/sam34/sam_tc.c b/arch/arm/src/sam34/sam_tc.c index cb48f84b6a..27dbd16420 100644 --- a/arch/arm/src/sam34/sam_tc.c +++ b/arch/arm/src/sam34/sam_tc.c @@ -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); diff --git a/configs/sam4s-xplained-pro/src/sam_tc.c b/configs/sam4s-xplained-pro/src/sam_tc.c index 5408689a41..851e0d2de3 100755 --- a/configs/sam4s-xplained-pro/src/sam_tc.c +++ b/configs/sam4s-xplained-pro/src/sam_tc.c @@ -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); diff --git a/sched/Kconfig b/sched/Kconfig index aebedace1d..fdef8ac199 100644 --- a/sched/Kconfig +++ b/sched/Kconfig @@ -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