aboutsummaryrefslogtreecommitdiffstats
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/sam3x8e/timer.c20
1 files changed, 16 insertions, 4 deletions
diff --git a/src/sam3x8e/timer.c b/src/sam3x8e/timer.c
index 0d3728ed..667e9b65 100644
--- a/src/sam3x8e/timer.c
+++ b/src/sam3x8e/timer.c
@@ -30,9 +30,10 @@ timer_from_us(uint32_t us)
void __visible
TC0_Handler(void)
{
- TC0->TC_CHANNEL[0].TC_SR; // clear irq pending
irq_disable();
- sched_timer_kick();
+ uint32_t status = TC0->TC_CHANNEL[0].TC_SR; // read to clear irq pending
+ if (likely(status & TC_SR_CPAS))
+ sched_timer_kick();
irq_enable();
}
@@ -43,6 +44,13 @@ timer_set(uint32_t value)
}
static void
+timer_set_clear(uint32_t value)
+{
+ TC0->TC_CHANNEL[0].TC_RA = value;
+ TC0->TC_CHANNEL[0].TC_SR; // read to clear irq pending
+}
+
+static void
timer_init(void)
{
TcChannel *tc = &TC0->TC_CHANNEL[0];
@@ -83,12 +91,16 @@ uint8_t
timer_set_next(uint32_t next)
{
uint32_t cur = timer_read_time();
+ if (sched_is_before(TC0->TC_CHANNEL[0].TC_RA, cur)
+ && !(TC0->TC_CHANNEL[0].TC_SR & TC_SR_CPAS))
+ // Already processing timer irqs
+ try_shutdown("timer_set_next called during timer dispatch");
uint32_t mintime = cur + TIMER_MIN_TICKS;
if (sched_is_before(mintime, next)) {
- timer_set(next);
+ timer_set_clear(next);
return 0;
}
- timer_set(mintime);
+ timer_set_clear(mintime);
return 1;
}