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

This commit is contained in:
Gregory Nutt 2015-05-26 08:09:10 -06:00
parent fe175fbc16
commit f055d4cac4
2 changed files with 28 additions and 18 deletions

View File

@ -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

View File

@ -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