From 6e3fdea1a64a7edaa65a8fd6351c18f73e99ec7c Mon Sep 17 00:00:00 2001 From: Gregory Nutt Date: Tue, 26 May 2015 08:09:10 -0600 Subject: [PATCH] Logic that samples the free running counter reads the pending interrupt status regsiter and can cause interrupts to be lost. So, if when the status regsiter is read, the logic must also handle the timer overflow event. Found and fixed by Max Neklyudov --- arch/arm/src/sam34/sam4cm_freerun.c | 23 ++++++++++++++--------- arch/arm/src/sama5/sam_freerun.c | 23 ++++++++++++++--------- 2 files changed, 28 insertions(+), 18 deletions(-) diff --git a/arch/arm/src/sam34/sam4cm_freerun.c b/arch/arm/src/sam34/sam4cm_freerun.c index 1deb4c37105..0183367282e 100644 --- a/arch/arm/src/sam34/sam4cm_freerun.c +++ b/arch/arm/src/sam34/sam4cm_freerun.c @@ -226,21 +226,19 @@ int sam_freerun_counter(struct sam_freerun_s *freerun, struct timespec *ts) DEBUGASSERT(freerun && freerun->tch && ts); - /* Temporarily disable the overflow counter */ + /* Temporarily disable the overflow counter. NOTE that we have to be careful + * here because sam_tc_getpending() will reset the pending interrupt status. + * If we do not handle the overflow here then, it will be lost. + */ flags = irqsave(); overflow = freerun->overflow; counter = sam_tc_getcounter(freerun->tch); sr = sam_tc_getpending(freerun->tch); verify = sam_tc_getcounter(freerun->tch); - irqrestore(flags); - - tcvdbg("counter=%lu (%lu) overflow=%lu, sr=%08lx\n", - (unsigned long)counter, (unsigned long)verify, - (unsigned long)overflow, (unsigned long)sr); /* If an interrupt was pending before we re-enabled interrupts, - * then our value of overflow needs to be incremented. + * then the overflow needs to be incremented. */ if ((sr & TC_INT_COVFS) != 0) @@ -252,10 +250,17 @@ int sam_freerun_counter(struct sam_freerun_s *freerun, struct timespec *ts) overflow++; counter = verify; - tcvdbg("counter=%lu overflow=%lu\n", - (unsigned long)counter, (unsigned long)overflow); + /* Update freerun overflow counter. */ + + freerun->overflow = overflow; } + irqrestore(flags); + + tcvdbg("counter=%lu (%lu) overflow=%lu, sr=%08lx\n", + (unsigned long)counter, (unsigned long)verify, + (unsigned long)overflow, (unsigned long)sr); + /* Convert the whole thing to units of microseconds. * * frequency = ticks / second diff --git a/arch/arm/src/sama5/sam_freerun.c b/arch/arm/src/sama5/sam_freerun.c index b3c4c323c03..90ad9676f17 100644 --- a/arch/arm/src/sama5/sam_freerun.c +++ b/arch/arm/src/sama5/sam_freerun.c @@ -243,21 +243,19 @@ int sam_freerun_counter(struct sam_freerun_s *freerun, struct timespec *ts) DEBUGASSERT(freerun && freerun->tch && ts); - /* Temporarily disable the overflow counter */ + /* Temporarily disable the overflow counter. NOTE that we have to be careful + * here because sam_tc_getpending() will reset the pending interrupt status. + * If we do not handle the overflow here then, it will be lost. + */ flags = irqsave(); overflow = freerun->overflow; counter = sam_tc_getcounter(freerun->tch); sr = sam_tc_getpending(freerun->tch); verify = sam_tc_getcounter(freerun->tch); - irqrestore(flags); - - tcvdbg("counter=%lu (%lu) overflow=%lu, sr=%08lx\n", - (unsigned long)counter, (unsigned long)verify, - (unsigned long)overflow, (unsigned long)sr); /* If an interrupt was pending before we re-enabled interrupts, - * then our value of overflow needs to be incremented. + * then the overflow needs to be incremented. */ if ((sr & TC_INT_COVFS) != 0) @@ -269,10 +267,17 @@ int sam_freerun_counter(struct sam_freerun_s *freerun, struct timespec *ts) overflow++; counter = verify; - tcvdbg("counter=%lu overflow=%lu\n", - (unsigned long)counter, (unsigned long)overflow); + /* Update freerun overflow counter. */ + + freerun->overflow = overflow; } + irqrestore(flags); + + tcvdbg("counter=%lu (%lu) overflow=%lu, sr=%08lx\n", + (unsigned long)counter, (unsigned long)verify, + (unsigned long)overflow, (unsigned long)sr); + /* Convert the whole thing to units of microseconds. * * frequency = ticks / second