Source-Changes-HG archive

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index][Old Index]

[src/trunk]: src/sys Extend kernel PPS api with pps_ref_event().



details:   https://anonhg.NetBSD.org/src/rev/bc59d9d35809
branches:  trunk
changeset: 786975:bc59d9d35809
user:      kardel <kardel%NetBSD.org@localhost>
date:      Sun May 26 18:07:42 2013 +0000

description:
Extend kernel PPS api with pps_ref_event().
pps_ref_event() allows capturing PPS time stamps
that are not generated at precisely 1Hz (e. g.
by reading a precision clock via callout()).

This extension allows clock drivers to supply PPS
time-stamps and drive the kernel NTP PLL
without the overhead of interrupt-handling and
-processing.

diffstat:

 sys/conf/files     |    4 +-
 sys/kern/kern_tc.c |  295 +++++++++++++++++++++++++++++++++++++++++++++++-----
 sys/sys/timepps.h  |   13 ++-
 3 files changed, 280 insertions(+), 32 deletions(-)

diffs (truncated from 464 to 300 lines):

diff -r 01aab8d68b63 -r bc59d9d35809 sys/conf/files
--- a/sys/conf/files    Sun May 26 17:25:53 2013 +0000
+++ b/sys/conf/files    Sun May 26 18:07:42 2013 +0000
@@ -1,4 +1,4 @@
-#      $NetBSD: files,v 1.1070 2013/04/28 03:11:32 christos Exp $
+#      $NetBSD: files,v 1.1071 2013/05/26 18:07:42 kardel Exp $
 #      @(#)files.newconf       7.5 (Berkeley) 5/10/93
 
 version        20100430
@@ -64,7 +64,7 @@
 defflag        opt_sysv.h              SYSVMSG SYSVSEM SYSVSHM
 defparam opt_sysvparam.h       SHMMAXPGS SEMMNI SEMMNS SEMUME SEMMNU
 
-defflag        opt_ntp.h               PPS_SYNC NTP
+defflag        opt_ntp.h               PPS_SYNC PPS_DEBUG NTP
 
 defflag        opt_ptm.h               NO_DEV_PTM COMPAT_BSDPTY
 
diff -r 01aab8d68b63 -r bc59d9d35809 sys/kern/kern_tc.c
--- a/sys/kern/kern_tc.c        Sun May 26 17:25:53 2013 +0000
+++ b/sys/kern/kern_tc.c        Sun May 26 18:07:42 2013 +0000
@@ -1,4 +1,4 @@
-/* $NetBSD: kern_tc.c,v 1.44 2012/11/13 20:10:02 pooka Exp $ */
+/* $NetBSD: kern_tc.c,v 1.45 2013/05/26 18:07:42 kardel Exp $ */
 
 /*-
  * Copyright (c) 2008, 2009 The NetBSD Foundation, Inc.
@@ -40,7 +40,7 @@
 
 #include <sys/cdefs.h>
 /* __FBSDID("$FreeBSD: src/sys/kern/kern_tc.c,v 1.166 2005/09/19 22:16:31 andre Exp $"); */
-__KERNEL_RCSID(0, "$NetBSD: kern_tc.c,v 1.44 2012/11/13 20:10:02 pooka Exp $");
+__KERNEL_RCSID(0, "$NetBSD: kern_tc.c,v 1.45 2013/05/26 18:07:42 kardel Exp $");
 
 #ifdef _KERNEL_OPT
 #include "opt_ntp.h"
@@ -688,7 +688,8 @@
 
        if (timestepwarnings) {
                bintime2timespec(&bt2, &ts2);
-               log(LOG_INFO, "Time stepped from %lld.%09ld to %lld.%09ld\n",
+               log(LOG_INFO,
+                   "Time stepped from %lld.%09ld to %lld.%09ld\n",
                    (long long)ts2.tv_sec, ts2.tv_nsec,
                    (long long)ts->tv_sec, ts->tv_nsec);
        }
@@ -859,7 +860,8 @@
 
        KASSERT(mutex_owned(&timecounter_lock));
 
-       KASSERT(pps != NULL); /* XXX ("NULL pps pointer in pps_ioctl") */
+       KASSERT(pps != NULL);
+
        switch (cmd) {
        case PPS_IOC_CREATE:
                return (0);
@@ -913,6 +915,9 @@
                pps->ppscap |= PPS_OFFSETCLEAR;
 }
 
+/*
+ * capture a timetamp in the pps structure
+ */
 void
 pps_capture(struct pps_state *pps)
 {
@@ -929,72 +934,223 @@
                pps->capgen = 0;
 }
 
+#ifdef PPS_DEBUG
+int ppsdebug = 0;
+#endif
+
+/*
+ * process a pps_capture()ed event
+ */
 void
 pps_event(struct pps_state *pps, int event)
 {
-       struct bintime bt;
+       pps_ref_event(pps, event, NULL, PPS_REFEVNT_PPS|PPS_REFEVNT_CAPTURE);
+}
+
+/*
+ * extended pps api /  kernel pll/fll entry point
+ *
+ * feed reference time stamps to PPS engine
+ *
+ * will simulate a PPS event and feed
+ * the NTP PLL/FLL if requested.
+ *
+ * the ref time stamps should be roughly once
+ * a second but do not need to be exactly in phase
+ * with the UTC second but should be close to it.
+ * this relaxation of requirements allows callout
+ * driven timestamping mechanisms to feed to pps 
+ * capture/kernel pll logic.
+ *
+ * calling pattern is:
+ *  pps_capture() (for PPS_REFEVNT_{CAPTURE|CAPCUR})
+ *  read timestamp from reference source
+ *  pps_ref_event()
+ *
+ * supported refmodes:
+ *  PPS_REFEVNT_CAPTURE
+ *    use system timestamp of pps_capture()
+ *  PPS_REFEVNT_CURRENT
+ *    use system timestamp of this call
+ *  PPS_REFEVNT_CAPCUR
+ *    use average of read capture and current system time stamp
+ *  PPS_REFEVNT_PPS
+ *    assume timestamp on second mark - ref_ts is ignored
+ *
+ */
+
+void
+pps_ref_event(struct pps_state *pps,
+             int event,
+             struct bintime *ref_ts,
+             int refmode
+       )
+{
+       struct bintime bt;      /* current time */
+       struct bintime btd;     /* time difference */
+       struct bintime bt_ref;  /* reference time */
        struct timespec ts, *tsp, *osp;
-       u_int64_t tcount, *pcount;
-       int foff;
-#ifdef PPS_SYNC
-       int fhard;
-#endif
+       struct timehands *th;
+       u_int64_t tcount, acount, dcount, *pcount;
+       int foff, fhard, gen;
        pps_seq_t *pseq;
 
        KASSERT(mutex_owned(&timecounter_lock));
 
-       KASSERT(pps != NULL); /* XXX ("NULL pps pointer in pps_event") */
-       /* If the timecounter was wound up underneath us, bail out. */
-       if (pps->capgen == 0 || pps->capgen != pps->capth->th_generation)
-               return;
+       KASSERT(pps != NULL);
+
+        /* pick up current time stamp if needed */
+       if (refmode & (PPS_REFEVNT_CURRENT|PPS_REFEVNT_CAPCUR)) {
+               /* pick up current time stamp */
+               th = timehands;
+               gen = th->th_generation;
+               tcount = (u_int64_t)tc_delta(th) + th->th_offset_count;
+               if (gen != th->th_generation)
+                       gen = 0;
 
-       /* Things would be easier with arrays. */
+               /* If the timecounter was wound up underneath us, bail out. */
+               if (pps->capgen == 0 ||
+                   pps->capgen != pps->capth->th_generation ||
+                   gen == 0 ||
+                   gen != pps->capgen) {
+#ifdef PPS_DEBUG
+                       if (ppsdebug & 0x1) {
+                               log(LOG_DEBUG,
+                                   "pps_ref_event(pps=%p, event=%d, ...): DROP (wind-up)\n",
+                                   pps, event);
+                       }
+#endif
+                       return;
+               }
+       } else {
+               tcount = 0;     /* keep GCC happy */
+       }
+
+#ifdef PPS_DEBUG
+       if (ppsdebug & 0x1) {
+               struct timespec tmsp;
+       
+               if (ref_ts == NULL) {
+                       tmsp.tv_sec = 0;
+                       tmsp.tv_nsec = 0;
+               } else {
+                       bintime2timespec(ref_ts, &tmsp);
+               }
+
+               log(LOG_DEBUG,
+                   "pps_ref_event(pps=%p, event=%d, ref_ts=%"PRIi64
+                   ".%09"PRIi32", refmode=0x%1x)\n",
+                   pps, event, tmsp.tv_sec, (int32_t)tmsp.tv_nsec, refmode);
+       }
+#endif
+
+       /* setup correct event references */
        if (event == PPS_CAPTUREASSERT) {
                tsp = &pps->ppsinfo.assert_timestamp;
                osp = &pps->ppsparam.assert_offset;
                foff = pps->ppsparam.mode & PPS_OFFSETASSERT;
-#ifdef PPS_SYNC
                fhard = pps->kcmode & PPS_CAPTUREASSERT;
-#endif
                pcount = &pps->ppscount[0];
                pseq = &pps->ppsinfo.assert_sequence;
        } else {
                tsp = &pps->ppsinfo.clear_timestamp;
                osp = &pps->ppsparam.clear_offset;
                foff = pps->ppsparam.mode & PPS_OFFSETCLEAR;
-#ifdef PPS_SYNC
                fhard = pps->kcmode & PPS_CAPTURECLEAR;
-#endif
                pcount = &pps->ppscount[1];
                pseq = &pps->ppsinfo.clear_sequence;
        }
 
+       /* determine system time stamp according to refmode */
+       dcount = 0;             /* keep GCC happy */
+       switch (refmode & PPS_REFEVNT_RMASK) {
+       case PPS_REFEVNT_CAPTURE:
+               acount = pps->capcount; /* use capture timestamp */
+               break;
+
+       case PPS_REFEVNT_CURRENT:
+               acount = tcount; /* use current timestamp */
+               break;
+
+       case PPS_REFEVNT_CAPCUR:
+               /*
+                * calculate counter value between pps_capture() and
+                * pps_ref_event()
+                */
+               dcount = tcount - pps->capcount;
+               acount = (dcount / 2) + pps->capcount;
+               break;
+
+       default:                /* ignore call error silently */
+               return;
+       }
+
        /*
         * If the timecounter changed, we cannot compare the count values, so
         * we have to drop the rest of the PPS-stuff until the next event.
         */
        if (pps->ppstc != pps->capth->th_counter) {
                pps->ppstc = pps->capth->th_counter;
-               *pcount = pps->capcount;
-               pps->ppscount[2] = pps->capcount;
+               pps->capcount = acount;
+               *pcount = acount;
+               pps->ppscount[2] = acount;
+#ifdef PPS_DEBUG
+               if (ppsdebug & 0x1) {
+                       log(LOG_DEBUG,
+                           "pps_ref_event(pps=%p, event=%d, ...): DROP (time-counter change)\n",
+                           pps, event);
+               }
+#endif
                return;
        }
 
-       /* Convert the count to a timespec. */
-       tcount = pps->capcount - pps->capth->th_offset_count;
+       pps->capcount = acount;
+
+       /* Convert the count to a bintime. */
        bt = pps->capth->th_offset;
-       bintime_addx(&bt, pps->capth->th_scale * tcount);
+       bintime_addx(&bt, pps->capth->th_scale * (acount - pps->capth->th_offset_count));
        bintime_add(&bt, &timebasebin);
+
+       if ((refmode & PPS_REFEVNT_PPS) == 0) {
+               /* determine difference to reference time stamp */
+               bt_ref = *ref_ts;
+
+               btd = bt;
+               bintime_sub(&btd, &bt_ref);
+
+               /* 
+                * simulate a PPS timestamp by dropping the fraction
+                * and applying the offset
+                */
+               if (bt.frac >= (uint64_t)1<<63) /* skip to nearest second */
+                       bt.sec++;
+               bt.frac = 0;
+               bintime_add(&bt, &btd);
+       } else {
+               /*
+                * create ref_ts from current time - 
+                * we are supposed to be called on
+                * the second mark
+                */
+               bt_ref = bt;
+               if (bt_ref.frac >= (uint64_t)1<<63)     /* skip to nearest second */
+                       bt_ref.sec++;
+               bt_ref.frac = 0;
+       }
+
+       /* convert bintime to timestamp */
        bintime2timespec(&bt, &ts);
 
        /* If the timecounter was wound up underneath us, bail out. */
        if (pps->capgen != pps->capth->th_generation)
                return;



Home | Main Index | Thread Index | Old Index