diff --git a/sys/kern/kern_tc.c b/sys/kern/kern_tc.c index 23c85cf0a97f..e4411594cc35 100644 --- a/sys/kern/kern_tc.c +++ b/sys/kern/kern_tc.c @@ -1,614 +1,580 @@ /* * ---------------------------------------------------------------------------- * "THE BEER-WARE LICENSE" (Revision 42): * wrote this file. As long as you retain this notice you * can do whatever you want with this stuff. If we meet some day, and you think * this stuff is worth it, you can buy me a beer in return. Poul-Henning Kamp * ---------------------------------------------------------------------------- * * $FreeBSD$ */ #include "opt_ntp.h" #include #include -#include #include #include #include #include #include /* - * Number of timecounters used to implement stable storage + * Implement a dummy timecounter which we can use until we get a real one + * in the air. This allows the console and other early stuff to use + * timeservices. */ -#ifndef NTIMECOUNTER -#define NTIMECOUNTER hz -#endif -static MALLOC_DEFINE(M_TIMECOUNTER, "timecounter", - "Timecounter stable storage"); +static unsigned +dummy_get_timecount(struct timecounter *tc) +{ + static unsigned now; + + return (++now); +} + +static struct timecounter dummy_timecounter = { + dummy_get_timecount, + 0, + ~0u, + 1000000, + "dummy" +}; + +struct timehands { + /* These fields must be initialized by the driver. */ + struct timecounter *tc_counter; + int64_t tc_adjustment; + u_int64_t tc_scale; + unsigned tc_offset_count; + struct bintime tc_offset; + struct timeval tc_microtime; + struct timespec tc_nanotime; + /* Fields not to be copied in tc_windup start with tc_generation */ + volatile unsigned tc_generation; + struct timehands *tc_next; +}; + + +extern struct timehands th0; +static struct timehands th9 = { NULL, 0, 0, 0, {0, 0}, {0, 0}, {0, 0}, 1, &th0}; +static struct timehands th8 = { NULL, 0, 0, 0, {0, 0}, {0, 0}, {0, 0}, 1, &th9}; +static struct timehands th7 = { NULL, 0, 0, 0, {0, 0}, {0, 0}, {0, 0}, 1, &th8}; +static struct timehands th6 = { NULL, 0, 0, 0, {0, 0}, {0, 0}, {0, 0}, 1, &th7}; +static struct timehands th5 = { NULL, 0, 0, 0, {0, 0}, {0, 0}, {0, 0}, 1, &th6}; +static struct timehands th4 = { NULL, 0, 0, 0, {0, 0}, {0, 0}, {0, 0}, 1, &th5}; +static struct timehands th3 = { NULL, 0, 0, 0, {0, 0}, {0, 0}, {0, 0}, 1, &th4}; +static struct timehands th2 = { NULL, 0, 0, 0, {0, 0}, {0, 0}, {0, 0}, 1, &th3}; +static struct timehands th1 = { NULL, 0, 0, 0, {0, 0}, {0, 0}, {0, 0}, 1, &th2}; +static struct timehands th0 = + { &dummy_timecounter, 0, 0, 0, {0, 0}, {0, 0}, {0, 0}, 1, &th1}; + +static struct timehands *volatile timehands = &th0; +struct timecounter *timecounter = &dummy_timecounter; +static struct timecounter *timecounters = &dummy_timecounter; time_t time_second; struct bintime boottimebin; struct timeval boottime; SYSCTL_STRUCT(_kern, KERN_BOOTTIME, boottime, CTLFLAG_RD, &boottime, timeval, "System boottime"); SYSCTL_NODE(_kern, OID_AUTO, timecounter, CTLFLAG_RW, 0, ""); #define TC_STATS(foo) \ static unsigned foo; \ SYSCTL_INT(_kern_timecounter, OID_AUTO, foo, CTLFLAG_RD, & foo, 0, "") TC_STATS(nbinuptime); TC_STATS(nnanouptime); TC_STATS(nmicrouptime); TC_STATS(nbintime); TC_STATS(nnanotime); TC_STATS(nmicrotime); TC_STATS(ngetbinuptime); TC_STATS(ngetnanouptime); TC_STATS(ngetmicrouptime); TC_STATS(ngetbintime); TC_STATS(ngetnanotime); TC_STATS(ngetmicrotime); #undef TC_STATS static void tc_windup(void); -/* - * Implement a dummy timecounter which we can use until we get a real one - * in the air. This allows the console and other early stuff to use - * timeservices. - */ - -static unsigned -dummy_get_timecount(struct timecounter *tc) -{ - static unsigned now; - - if (tc->tc_generation == 0) - tc->tc_generation = 1; - return (++now); -} - -static struct timecounter dummy_timecounter = { - dummy_get_timecount, - 0, - ~0u, - 1000000, - "dummy" -}; - -struct timecounter *volatile timecounter = &dummy_timecounter; static __inline unsigned -tc_delta(struct timecounter *tc) +tc_delta(struct timehands *tc) { - return ((tc->tc_get_timecount(tc) - tc->tc_offset_count) & - tc->tc_counter_mask); + return ((tc->tc_counter->tc_get_timecount(tc->tc_counter) - + tc->tc_offset_count) & tc->tc_counter->tc_counter_mask); } void binuptime(struct bintime *bt) { - struct timecounter *tc; + struct timehands *tc; unsigned gen; nbinuptime++; do { - tc = timecounter; + tc = timehands; gen = tc->tc_generation; *bt = tc->tc_offset; bintime_addx(bt, tc->tc_scale * tc_delta(tc)); } while (gen == 0 || gen != tc->tc_generation); } void nanouptime(struct timespec *ts) { struct bintime bt; nnanouptime++; binuptime(&bt); bintime2timespec(&bt, ts); } void microuptime(struct timeval *tv) { struct bintime bt; nmicrouptime++; binuptime(&bt); bintime2timeval(&bt, tv); } void bintime(struct bintime *bt) { nbintime++; binuptime(bt); bintime_add(bt, &boottimebin); } void nanotime(struct timespec *ts) { struct bintime bt; nnanotime++; bintime(&bt); bintime2timespec(&bt, ts); } void microtime(struct timeval *tv) { struct bintime bt; nmicrotime++; bintime(&bt); bintime2timeval(&bt, tv); } void getbinuptime(struct bintime *bt) { - struct timecounter *tc; + struct timehands *tc; unsigned gen; ngetbinuptime++; do { - tc = timecounter; + tc = timehands; gen = tc->tc_generation; *bt = tc->tc_offset; } while (gen == 0 || gen != tc->tc_generation); } void getnanouptime(struct timespec *tsp) { - struct timecounter *tc; + struct timehands *tc; unsigned gen; ngetnanouptime++; do { - tc = timecounter; + tc = timehands; gen = tc->tc_generation; bintime2timespec(&tc->tc_offset, tsp); } while (gen == 0 || gen != tc->tc_generation); } void getmicrouptime(struct timeval *tvp) { - struct timecounter *tc; + struct timehands *tc; unsigned gen; ngetmicrouptime++; do { - tc = timecounter; + tc = timehands; gen = tc->tc_generation; bintime2timeval(&tc->tc_offset, tvp); } while (gen == 0 || gen != tc->tc_generation); } void getbintime(struct bintime *bt) { - struct timecounter *tc; + struct timehands *tc; unsigned gen; ngetbintime++; do { - tc = timecounter; + tc = timehands; gen = tc->tc_generation; *bt = tc->tc_offset; } while (gen == 0 || gen != tc->tc_generation); bintime_add(bt, &boottimebin); } void getnanotime(struct timespec *tsp) { - struct timecounter *tc; + struct timehands *tc; unsigned gen; ngetnanotime++; do { - tc = timecounter; + tc = timehands; gen = tc->tc_generation; *tsp = tc->tc_nanotime; } while (gen == 0 || gen != tc->tc_generation); } void getmicrotime(struct timeval *tvp) { - struct timecounter *tc; + struct timehands *tc; unsigned gen; ngetmicrotime++; do { - tc = timecounter; + tc = timehands; gen = tc->tc_generation; *tvp = tc->tc_microtime; } while (gen == 0 || gen != tc->tc_generation); } static void -tc_setscales(struct timecounter *tc) +tc_setscales(struct timehands *tc) { u_int64_t scale; /* Sacrifice the lower bit to the deity for code clarity */ scale = 1ULL << 63; /* * We get nanoseconds with 32 bit binary fraction and want * 64 bit binary fraction: x = a * 2^32 / 10^9 = a * 4.294967296 * The range is +/- 5000PPM so we can only multiply by about 850 * without overflowing. The best suitable fraction is 2199/512. * Divide by 2 times 512 to match the temporary lower precision. */ scale += (tc->tc_adjustment / 1024) * 2199; - scale /= tc->tc_tweak->tc_frequency; + scale /= tc->tc_counter->tc_frequency; tc->tc_scale = scale * 2; } void tc_init(struct timecounter *tc) { - struct timecounter *t1, *t2, *t3; - int i; - - tc->tc_adjustment = 0; - tc->tc_tweak = tc; - tc_setscales(tc); - tc->tc_offset_count = tc->tc_get_timecount(tc); - if (timecounter == &dummy_timecounter) - tc->tc_avail = tc; - else { - tc->tc_avail = timecounter->tc_tweak->tc_avail; - timecounter->tc_tweak->tc_avail = tc; - } - MALLOC(t1, struct timecounter *, sizeof *t1, M_TIMECOUNTER, M_WAITOK | M_ZERO); - tc->tc_next = t1; - *t1 = *tc; - t2 = t1; - t3 = NULL; - for (i = 1; i < NTIMECOUNTER; i++) { - MALLOC(t3, struct timecounter *, sizeof *t3, - M_TIMECOUNTER, M_WAITOK | M_ZERO); - *t3 = *tc; - t3->tc_next = t2; - t2 = t3; - } - t1->tc_next = t3; - tc = t1; + tc->tc_next = timecounters; + timecounters = tc; printf("Timecounter \"%s\" frequency %lu Hz\n", tc->tc_name, (u_long)tc->tc_frequency); - - /* XXX: For now always start using the counter. */ - tc->tc_offset_count = tc->tc_get_timecount(tc); - binuptime(&tc->tc_offset); timecounter = tc; - tc_windup(); +} + +u_int32_t +tc_getfrequency(void) +{ + + return (timehands->tc_counter->tc_frequency); } void tc_setclock(struct timespec *ts) { struct timespec ts2; nanouptime(&ts2); boottime.tv_sec = ts->tv_sec - ts2.tv_sec; boottime.tv_usec = (ts->tv_nsec - ts2.tv_nsec) / 1000; if (boottime.tv_usec < 0) { boottime.tv_usec += 1000000; boottime.tv_sec--; } timeval2bintime(&boottime, &boottimebin); /* fiddle all the little crinkly bits around the fiords... */ tc_windup(); } -u_int32_t -tc_getfrequency(void) -{ - - return (timecounter->tc_frequency); -} - -static void -switch_timecounter(struct timecounter *newtc) -{ - int s; - struct timecounter *tc; - - s = splclock(); - tc = timecounter; - if (newtc->tc_tweak == tc->tc_tweak) { - splx(s); - return; - } - newtc = newtc->tc_tweak->tc_next; - binuptime(&newtc->tc_offset); - newtc->tc_offset_count = newtc->tc_get_timecount(newtc); - tc_setscales(newtc); - newtc->tc_generation = 0; - timecounter = newtc; - tc_windup(); - splx(s); -} - static void tc_windup(void) { - struct timecounter *tc, *tco; + struct timehands *tc, *tco; struct bintime bt; - unsigned ogen, delta; + unsigned ogen, delta, ncount; int i; - tco = timecounter; + ncount = 0; /* GCC is lame */ + tco = timehands; tc = tco->tc_next; ogen = tc->tc_generation; tc->tc_generation = 0; - bcopy(tco, tc, __offsetof(struct timecounter, tc_generation)); + bcopy(tco, tc, __offsetof(struct timehands, tc_generation)); delta = tc_delta(tc); + if (tc->tc_counter != timecounter) + ncount = timecounter->tc_get_timecount(timecounter); tc->tc_offset_count += delta; - tc->tc_offset_count &= tc->tc_counter_mask; + tc->tc_offset_count &= tc->tc_counter->tc_counter_mask; bintime_addx(&tc->tc_offset, tc->tc_scale * delta); /* * We may be inducing a tiny error here, the tc_poll_pps() may * process a latched count which happens after the tc_delta() * in sync_other_counter(), which would extend the previous * counters parameters into the domain of this new one. * Since the timewindow is very small for this, the error is * going to be only a few weenieseconds (as Dave Mills would * say), so lets just not talk more about it, OK ? */ - if (tco->tc_poll_pps) - tco->tc_poll_pps(tco); + if (tco->tc_counter->tc_poll_pps) + tco->tc_counter->tc_poll_pps(tco->tc_counter); for (i = tc->tc_offset.sec - tco->tc_offset.sec; i > 0; i--) ntp_update_second(&tc->tc_adjustment, &tc->tc_offset.sec); tc_setscales(tc); bt = tc->tc_offset; bintime_add(&bt, &boottimebin); bintime2timeval(&bt, &tc->tc_microtime); bintime2timespec(&bt, &tc->tc_nanotime); ogen++; if (ogen == 0) ogen++; tc->tc_generation = ogen; time_second = tc->tc_microtime.tv_sec; - timecounter = tc; + timehands = tc; } static int sysctl_kern_timecounter_hardware(SYSCTL_HANDLER_ARGS) { char newname[32]; struct timecounter *newtc, *tc; int error; - tc = timecounter->tc_tweak; + tc = timecounter; strncpy(newname, tc->tc_name, sizeof(newname)); error = sysctl_handle_string(oidp, &newname[0], sizeof(newname), req); - if (error == 0 && req->newptr != NULL && - strcmp(newname, tc->tc_name) != 0) { - for (newtc = tc->tc_avail; newtc != tc; - newtc = newtc->tc_avail) { - if (strcmp(newname, newtc->tc_name) == 0) { - /* Warm up new timecounter. */ - (void)newtc->tc_get_timecount(newtc); - - switch_timecounter(newtc); - return (0); - } - } - return (EINVAL); + if (error != 0 && req->newptr == NULL && !strcmp(newname, tc->tc_name)) + return(error); + for (newtc = timecounters; newtc != NULL; newtc = newtc->tc_next) { + if (strcmp(newname, newtc->tc_name)) + continue; + /* Warm up new timecounter. */ + (void)newtc->tc_get_timecount(newtc); + (void)newtc->tc_get_timecount(newtc); + timecounter = newtc; + return (0); } - return (error); + return (EINVAL); } SYSCTL_PROC(_kern_timecounter, OID_AUTO, hardware, CTLTYPE_STRING | CTLFLAG_RW, 0, 0, sysctl_kern_timecounter_hardware, "A", ""); int pps_ioctl(u_long cmd, caddr_t data, struct pps_state *pps) { pps_params_t *app; struct pps_fetch_args *fapi; #ifdef PPS_SYNC struct pps_kcbind_args *kapi; #endif switch (cmd) { case PPS_IOC_CREATE: return (0); case PPS_IOC_DESTROY: return (0); case PPS_IOC_SETPARAMS: app = (pps_params_t *)data; if (app->mode & ~pps->ppscap) return (EINVAL); pps->ppsparam = *app; return (0); case PPS_IOC_GETPARAMS: app = (pps_params_t *)data; *app = pps->ppsparam; app->api_version = PPS_API_VERS_1; return (0); case PPS_IOC_GETCAP: *(int*)data = pps->ppscap; return (0); case PPS_IOC_FETCH: fapi = (struct pps_fetch_args *)data; if (fapi->tsformat && fapi->tsformat != PPS_TSFMT_TSPEC) return (EINVAL); if (fapi->timeout.tv_sec || fapi->timeout.tv_nsec) return (EOPNOTSUPP); pps->ppsinfo.current_mode = pps->ppsparam.mode; fapi->pps_info_buf = pps->ppsinfo; return (0); case PPS_IOC_KCBIND: #ifdef PPS_SYNC kapi = (struct pps_kcbind_args *)data; /* XXX Only root should be able to do this */ if (kapi->tsformat && kapi->tsformat != PPS_TSFMT_TSPEC) return (EINVAL); if (kapi->kernel_consumer != PPS_KC_HARDPPS) return (EINVAL); if (kapi->edge & ~pps->ppscap) return (EINVAL); pps->kcmode = kapi->edge; return (0); #else return (EOPNOTSUPP); #endif default: return (ENOTTY); } } void pps_init(struct pps_state *pps) { pps->ppscap |= PPS_TSFMT_TSPEC; if (pps->ppscap & PPS_CAPTUREASSERT) pps->ppscap |= PPS_OFFSETASSERT; if (pps->ppscap & PPS_CAPTURECLEAR) pps->ppscap |= PPS_OFFSETCLEAR; } void pps_capture(struct pps_state *pps) { - struct timecounter *tc; + struct timehands *tc; - tc = timecounter; + tc = timehands; pps->captc = tc; pps->capgen = tc->tc_generation; - pps->capcount = tc->tc_get_timecount(tc); + pps->capcount = tc->tc_counter->tc_get_timecount(tc->tc_counter); } void pps_event(struct pps_state *pps, int event) { struct timespec ts, *tsp, *osp; unsigned tcount, *pcount; struct bintime bt; int foff, fhard; pps_seq_t *pseq; /* If the timecounter were wound up, bail. */ if (pps->capgen != pps->capgen) return; /* Things would be easier with arrays... */ if (event == PPS_CAPTUREASSERT) { tsp = &pps->ppsinfo.assert_timestamp; osp = &pps->ppsparam.assert_offset; foff = pps->ppsparam.mode & PPS_OFFSETASSERT; fhard = pps->kcmode & PPS_CAPTUREASSERT; 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; fhard = pps->kcmode & PPS_CAPTURECLEAR; pcount = &pps->ppscount[1]; pseq = &pps->ppsinfo.clear_sequence; } /* The timecounter changed: bail */ if (!pps->ppstc || - pps->ppstc->tc_name != pps->captc->tc_name || - pps->captc->tc_name != timecounter->tc_name) { - pps->ppstc = pps->captc; + pps->ppstc != pps->captc->tc_counter || + pps->captc->tc_counter != timehands->tc_counter) { + pps->ppstc = pps->captc->tc_counter; *pcount = pps->capcount; #ifdef PPS_SYNC pps->ppscount[2] = pps->capcount; #endif return; } /* Nothing really happened */ if (*pcount == pps->capcount) return; /* Convert the count to timespec */ tcount = pps->capcount - pps->captc->tc_offset_count; - tcount &= pps->captc->tc_counter_mask; + tcount &= pps->captc->tc_counter->tc_counter_mask; bt = pps->captc->tc_offset; bintime_addx(&bt, pps->captc->tc_scale * tcount); bintime2timespec(&bt, &ts); /* If the timecounter were wound up, bail. */ if (pps->capgen != pps->capgen) return; *pcount = pps->capcount; (*pseq)++; *tsp = ts; if (foff) { timespecadd(tsp, osp); if (tsp->tv_nsec < 0) { tsp->tv_nsec += 1000000000; tsp->tv_sec -= 1; } } #ifdef PPS_SYNC if (fhard) { /* magic, at its best... */ tcount = pps->capcount - pps->ppscount[2]; pps->ppscount[2] = pps->capcount; - tcount &= pps->captc->tc_counter_mask; + tcount &= pps->captc->tc_counter->tc_counter_mask; bt.sec = 0; bt.frac = 0; bintime_addx(&bt, pps->captc->tc_scale * tcount); bintime2timespec(&bt, &ts); hardpps(tsp, ts.tv_nsec + 1000000000 * ts.tv_sec); } #endif } /*- * Timecounters need to be updated every so often to prevent the hardware * counter from overflowing. Updating also recalculates the cached values * used by the get*() family of functions, so their precision depends on * the update frequency. * Don't update faster than approx once per millisecond, if people want * better timestamps they should use the non-"get" functions. */ static int tc_tick; SYSCTL_INT(_kern_timecounter, OID_AUTO, tick, CTLFLAG_RD, &tick, 0, ""); static void tc_ticktock(void *dummy) { tc_windup(); timeout(tc_ticktock, NULL, tc_tick); } static void inittimecounter(void *dummy) { u_int p; if (hz > 1000) tc_tick = (hz + 500) / 1000; else tc_tick = 1; p = (tc_tick * 1000000) / hz; printf("Timecounters tick every %d.%03u msec\n", p / 1000, p % 1000); tc_ticktock(NULL); } SYSINIT(timecounter, SI_SUB_CLOCKS, SI_ORDER_FIRST, inittimecounter, NULL) diff --git a/sys/sys/timepps.h b/sys/sys/timepps.h index 24c9a1d0a497..429e5cf2d61b 100644 --- a/sys/sys/timepps.h +++ b/sys/sys/timepps.h @@ -1,196 +1,198 @@ /* * ---------------------------------------------------------------------------- * "THE BEER-WARE LICENSE" (Revision 42): * wrote this file. As long as you retain this notice you * can do whatever you want with this stuff. If we meet some day, and you think * this stuff is worth it, you can buy me a beer in return. Poul-Henning Kamp * ---------------------------------------------------------------------------- * * $FreeBSD$ * * The is a FreeBSD protype version of the "draft-mogul-pps-api-05.txt" * specification for Pulse Per Second timing interfaces. */ #ifndef _SYS_TIMEPPS_H_ #define _SYS_TIMEPPS_H_ #include #define PPS_API_VERS_1 1 typedef int pps_handle_t; typedef unsigned pps_seq_t; typedef struct ntp_fp { unsigned int integral; unsigned int fractional; } ntp_fp_t; typedef union pps_timeu { struct timespec tspec; ntp_fp_t ntpfp; unsigned long longpad[3]; } pps_timeu_t; typedef struct { pps_seq_t assert_sequence; /* assert event seq # */ pps_seq_t clear_sequence; /* clear event seq # */ pps_timeu_t assert_tu; pps_timeu_t clear_tu; int current_mode; /* current mode bits */ } pps_info_t; #define assert_timestamp assert_tu.tspec #define clear_timestamp clear_tu.tspec #define assert_timestamp_ntpfp assert_tu.ntpfp #define clear_timestamp_ntpfp clear_tu.ntpfp typedef struct { int api_version; /* API version # */ int mode; /* mode bits */ pps_timeu_t assert_off_tu; pps_timeu_t clear_off_tu; } pps_params_t; #define assert_offset assert_off_tu.tspec #define clear_offset clear_off_tu.tspec #define assert_offset_ntpfp assert_off_tu.ntpfp #define clear_offset_ntpfp clear_off_tu.ntpfp #define PPS_CAPTUREASSERT 0x01 #define PPS_CAPTURECLEAR 0x02 #define PPS_CAPTUREBOTH 0x03 #define PPS_OFFSETASSERT 0x10 #define PPS_OFFSETCLEAR 0x20 #define PPS_ECHOASSERT 0x40 #define PPS_ECHOCLEAR 0x80 #define PPS_CANWAIT 0x100 #define PPS_CANPOLL 0x200 #define PPS_TSFMT_TSPEC 0x1000 #define PPS_TSFMT_NTPFP 0x2000 #define PPS_KC_HARDPPS 0 #define PPS_KC_HARDPPS_PLL 1 #define PPS_KC_HARDPPS_FLL 2 struct pps_fetch_args { int tsformat; pps_info_t pps_info_buf; struct timespec timeout; }; struct pps_kcbind_args { int kernel_consumer; int edge; int tsformat; }; #define PPS_IOC_CREATE _IO('1', 1) #define PPS_IOC_DESTROY _IO('1', 2) #define PPS_IOC_SETPARAMS _IOW('1', 3, pps_params_t) #define PPS_IOC_GETPARAMS _IOR('1', 4, pps_params_t) #define PPS_IOC_GETCAP _IOR('1', 5, int) #define PPS_IOC_FETCH _IOWR('1', 6, struct pps_fetch_args) #define PPS_IOC_KCBIND _IOW('1', 7, struct pps_kcbind_args) #ifdef _KERNEL +struct timehands; +struct timecounter; struct pps_state { /* capture information */ - struct timecounter *captc; + struct timehands *captc; u_int capgen; u_int capcount; /* state information */ pps_params_t ppsparam; pps_info_t ppsinfo; int kcmode; int ppscap; struct timecounter *ppstc; unsigned ppscount[3]; }; void pps_capture(struct pps_state *pps); void pps_event(struct pps_state *pps, int event); void pps_init(struct pps_state *pps); int pps_ioctl(u_long cmd, caddr_t data, struct pps_state *pps); void hardpps(struct timespec *tsp, long nsec); #else /* !_KERNEL */ static __inline int time_pps_create(int filedes, pps_handle_t *handle) { int error; *handle = -1; error = ioctl(filedes, PPS_IOC_CREATE, 0); if (error < 0) return (-1); *handle = filedes; return (0); } static __inline int time_pps_destroy(pps_handle_t handle) { return (ioctl(handle, PPS_IOC_DESTROY, 0)); } static __inline int time_pps_setparams(pps_handle_t handle, const pps_params_t *ppsparams) { return (ioctl(handle, PPS_IOC_SETPARAMS, ppsparams)); } static __inline int time_pps_getparams(pps_handle_t handle, pps_params_t *ppsparams) { return (ioctl(handle, PPS_IOC_GETPARAMS, ppsparams)); } static __inline int time_pps_getcap(pps_handle_t handle, int *mode) { return (ioctl(handle, PPS_IOC_GETCAP, mode)); } static __inline int time_pps_fetch(pps_handle_t handle, const int tsformat, pps_info_t *ppsinfobuf, const struct timespec *timeout) { int error; struct pps_fetch_args arg; arg.tsformat = tsformat; if (timeout == NULL) { arg.timeout.tv_sec = -1; arg.timeout.tv_nsec = -1; } else arg.timeout = *timeout; error = ioctl(handle, PPS_IOC_FETCH, &arg); *ppsinfobuf = arg.pps_info_buf; return (error); } static __inline int time_pps_kcbind(pps_handle_t handle, const int kernel_consumer, const int edge, const int tsformat) { struct pps_kcbind_args arg; arg.kernel_consumer = kernel_consumer; arg.edge = edge; arg.tsformat = tsformat; return (ioctl(handle, PPS_IOC_KCBIND, &arg)); } #endif /* !_KERNEL */ #endif /* _SYS_TIMEPPS_H_ */ diff --git a/sys/sys/timetc.h b/sys/sys/timetc.h index a35f6fb32cd9..bdf92a0a10d3 100644 --- a/sys/sys/timetc.h +++ b/sys/sys/timetc.h @@ -1,101 +1,90 @@ /* * ---------------------------------------------------------------------------- * "THE BEER-WARE LICENSE" (Revision 42): * wrote this file. As long as you retain this notice you * can do whatever you want with this stuff. If we meet some day, and you think * this stuff is worth it, you can buy me a beer in return. Poul-Henning Kamp * ---------------------------------------------------------------------------- * * $FreeBSD$ */ #ifndef _SYS_TIMETC_H_ #define _SYS_TIMETC_H_ /* * Structure used to interface to the machine dependent hardware support * for timekeeping. * * A timecounter is a (hard or soft) binary counter which has two properties: * * it runs at a fixed, known frequency. * * it must not roll over in less than (1 + delta)/HZ seconds. "delta" * is expected to be less than 20 msec, but no hard data has been * collected on this. 16 bit at 5 MHz (31 msec) is known to work. * * get_timecount() reads the counter. * * counter_mask removes unimplemented bits from the count value. * * frequency is the counter frequency in hz. * * name is a short mnemonic name for this counter. * * cost is a measure of how long time it takes to read the counter. * * adjustment [PPM << 16] which means that the smallest unit of correction * you can apply amounts to 481.5 usec/year. * * scale_micro [2^32 * usec/tick]. * scale_nano_i [ns/tick]. * scale_nano_f [(ns/2^32)/tick]. * * offset_count is the contents of the counter which corresponds to the * rest of the offset_* values. * * offset_sec [s]. * offset_micro [usec]. * offset_nano [ns/2^32] is misnamed, the real unit is .23283064365... * attoseconds (10E-18) and before you ask: yes, they are in fact * called attoseconds, it comes from "atten" for 18 in Danish/Swedish. * * Each timecounter must supply an array of three timecounters, this is needed * to guarantee atomicity in the code. Index zero is used to transport * modifications, for instance done with sysctl, into the timecounter being * used in a safe way. Such changes may be adopted with a delay of up to 1/HZ, * index one & two are used alternately for the actual timekeeping. * * 'tc_avail' points to the next available (external) timecounter in a * circular queue. This is only valid for index 0. * * `tc_other' points to the next "work" timecounter in a circular queue, * i.e., for index i > 0 it points to index 1 + (i - 1) % NTIMECOUNTER. * We also use it to point from index 0 to index 1. * * `tc_tweak' points to index 0. */ struct timecounter; typedef unsigned timecounter_get_t(struct timecounter *); typedef void timecounter_pps_t(struct timecounter *); struct timecounter { /* These fields must be initialized by the driver. */ timecounter_get_t *tc_get_timecount; timecounter_pps_t *tc_poll_pps; unsigned tc_counter_mask; u_int32_t tc_frequency; char *tc_name; void *tc_priv; - /* These fields will be managed by the generic code. */ - int64_t tc_adjustment; - u_int64_t tc_scale; - unsigned tc_offset_count; - struct bintime tc_offset; - struct timeval tc_microtime; - struct timespec tc_nanotime; - struct timecounter *tc_avail; - struct timecounter *tc_tweak; - /* Fields not to be copied in tc_windup start with tc_generation */ - volatile unsigned tc_generation; struct timecounter *tc_next; }; #ifdef _KERNEL -extern struct timecounter *volatile timecounter; +extern struct timecounter *timecounter; u_int32_t tc_getfrequency(void); void tc_init(struct timecounter *tc); void tc_setclock(struct timespec *ts); #endif /* !_KERNEL */ #endif /* !_SYS_TIMETC_H_ */