int sam_freerun_counter(struct sam_freerun_s *freerun, struct timespec *ts) { uint64_t usec; uint32_t counter; uint32_t verify; uint32_t sr; uint32_t overflow; uint32_t sec; irqstate_t flags; DEBUGASSERT(freerun && freerun->tch && ts); /* 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 = enter_critical_section(); overflow = freerun->overflow; counter = sam_tc_getcounter(freerun->tch); sr = sam_tc_getpending(freerun->tch); verify = sam_tc_getcounter(freerun->tch); /* If an interrupt was pending before we re-enabled interrupts, * then the overflow needs to be incremented. */ if ((sr & TC_INT_COVFS) != 0) { /* Increment the overflow count and use the value of the * guaranteed to be AFTER the overflow occurred. */ overflow++; counter = verify; /* Update freerun overflow counter. */ freerun->overflow = overflow; } leave_critical_section(flags); tmrinfo("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 * seconds = ticks * frequency * usecs = (ticks * USEC_PER_SEC) / frequency; */ usec = ((((uint64_t)overflow << 16) + (uint64_t)counter) * USEC_PER_SEC) / sam_tc_divfreq(freerun->tch); /* And return the value of the timer */ sec = (uint32_t)(usec / USEC_PER_SEC); ts->tv_sec = sec; ts->tv_nsec = (usec - (sec * USEC_PER_SEC)) * NSEC_PER_USEC; tmrinfo("usec=%llu ts=(%lu, %lu)\n", usec, (unsigned long)ts->tv_sec, (unsigned long)ts->tv_nsec); return OK; }
int sam_freerun_counter(struct sam_freerun_s *freerun, struct timespec *ts) { uint64_t usec; uint32_t counter; uint32_t verify; uint32_t sr; uint32_t overflow; uint32_t sec; irqstate_t flags; DEBUGASSERT(freerun && freerun->tch && ts); /* Temporarily disable the overflow counter */ 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. */ if ((sr & TC_INT_COVFS) != 0) { /* Increment the overflow count and use the value of the * guaranteed to be AFTER the overflow occurred. */ overflow++; counter = verify; tcvdbg("counter=%lu overflow=%lu\n", (unsigned long)counter, (unsigned long)overflow); } /* Convert the whole thing to units of microseconds. * * frequency = ticks / second * seconds = ticks * frequency * usecs = (ticks * USEC_PER_SEC) / frequency; */ usec = ((((uint64_t)overflow << 32) + (uint64_t)counter) * USEC_PER_SEC) / sam_tc_divfreq(freerun->tch); /* And return the value of the timer */ sec = (uint32_t)(usec / USEC_PER_SEC); ts->tv_sec = sec; ts->tv_nsec = (usec - (sec * USEC_PER_SEC)) * NSEC_PER_USEC; tcvdbg("usec=%llu ts=(%lu, %lu)\n", usec, (unsigned long)ts->tv_sec, (unsigned long)ts->tv_nsec); return OK; }