kern_tc.c revision 58377
1117610Sdes/*
2117610Sdes * ----------------------------------------------------------------------------
3141098Sdes * "THE BEER-WARE LICENSE" (Revision 42):
4255376Sdes * <phk@FreeBSD.ORG> wrote this file.  As long as you retain this notice you
5141098Sdes * can do whatever you want with this stuff. If we meet some day, and you think
6141098Sdes * this stuff is worth it, you can buy me a beer in return.   Poul-Henning Kamp
7141098Sdes * ----------------------------------------------------------------------------
8141098Sdes *
9117610Sdes * $FreeBSD: head/sys/kern/kern_tc.c 58377 2000-03-20 14:09:06Z phk $
10141098Sdes */
11117610Sdes
12141098Sdes#include "opt_ntp.h"
13141098Sdes
14141098Sdes#include <sys/param.h>
15141098Sdes#include <sys/timetc.h>
16141098Sdes#include <sys/malloc.h>
17141098Sdes#include <sys/kernel.h>
18117610Sdes#include <sys/sysctl.h>
19141098Sdes#include <sys/systm.h>
20141098Sdes#include <sys/timex.h>
21141098Sdes#include <sys/timepps.h>
22141098Sdes
23141098Sdes/*
24141098Sdes * Number of timecounters used to implement stable storage
25141098Sdes */
26141098Sdes#ifndef NTIMECOUNTER
27141098Sdes#define NTIMECOUNTER	5
28141098Sdes#endif
29141098Sdes
30141098Sdesstatic MALLOC_DEFINE(M_TIMECOUNTER, "timecounter",
31141098Sdes	"Timecounter stable storage");
32141098Sdes
33141098Sdesstatic void tco_setscales __P((struct timecounter *tc));
34141098Sdesstatic __inline unsigned tco_delta __P((struct timecounter *tc));
35141098Sdes
36141098Sdestime_t time_second;
37117610Sdes
38255376Sdesstruct	timeval boottime;
39117610SdesSYSCTL_STRUCT(_kern, KERN_BOOTTIME, boottime, CTLFLAG_RD,
40117610Sdes    &boottime, timeval, "System boottime");
41117610Sdes
42228692SdesSYSCTL_NODE(_kern, OID_AUTO, timecounter, CTLFLAG_RW, 0, "");
43117610Sdes
44228692Sdesstatic unsigned nmicrotime;
45228692Sdesstatic unsigned nnanotime;
46228692Sdesstatic unsigned ngetmicrotime;
47228692Sdesstatic unsigned ngetnanotime;
48117610Sdesstatic unsigned nmicrouptime;
49117610Sdesstatic unsigned nnanouptime;
50117610Sdesstatic unsigned ngetmicrouptime;
51228692Sdesstatic unsigned ngetnanouptime;
52228692SdesSYSCTL_INT(_kern_timecounter, OID_AUTO, nmicrotime, CTLFLAG_RD, &nmicrotime, 0, "");
53228692SdesSYSCTL_INT(_kern_timecounter, OID_AUTO, nnanotime, CTLFLAG_RD, &nnanotime, 0, "");
54228692SdesSYSCTL_INT(_kern_timecounter, OID_AUTO, nmicrouptime, CTLFLAG_RD, &nmicrouptime, 0, "");
55228692SdesSYSCTL_INT(_kern_timecounter, OID_AUTO, nnanouptime, CTLFLAG_RD, &nnanouptime, 0, "");
56228692SdesSYSCTL_INT(_kern_timecounter, OID_AUTO, ngetmicrotime, CTLFLAG_RD, &ngetmicrotime, 0, "");
57117610SdesSYSCTL_INT(_kern_timecounter, OID_AUTO, ngetnanotime, CTLFLAG_RD, &ngetnanotime, 0, "");
58228692SdesSYSCTL_INT(_kern_timecounter, OID_AUTO, ngetmicrouptime, CTLFLAG_RD, &ngetmicrouptime, 0, "");
59228692SdesSYSCTL_INT(_kern_timecounter, OID_AUTO, ngetnanouptime, CTLFLAG_RD, &ngetnanouptime, 0, "");
60117610Sdes
61228692Sdes/*
62228692Sdes * Implement a dummy timecounter which we can use until we get a real one
63228692Sdes * in the air.  This allows the console and other early stuff to use
64228692Sdes * timeservices.
65228692Sdes */
66228692Sdes
67228692Sdesstatic unsigned
68228692Sdesdummy_get_timecount(struct timecounter *tc)
69228692Sdes{
70117610Sdes	static unsigned now;
71228692Sdes
72228692Sdes	return (++now);
73228692Sdes}
74228692Sdes
75228692Sdesstatic struct timecounter dummy_timecounter = {
76228692Sdes	dummy_get_timecount,
77228692Sdes	0,
78228692Sdes	~0u,
79228692Sdes	1000000,
80228692Sdes	"dummy"
81228692Sdes};
82228692Sdes
83228692Sdesstruct timecounter *timecounter = &dummy_timecounter;
84228692Sdes
85228692Sdesstatic __inline unsigned
86228692Sdestco_delta(struct timecounter *tc)
87228692Sdes{
88228692Sdes
89141098Sdes	return ((tc->tc_get_timecount(tc) - tc->tc_offset_count) &
90228692Sdes	    tc->tc_counter_mask);
91228692Sdes}
92141098Sdes
93228692Sdes/*
94141098Sdes * We have eight functions for looking at the clock, four for
95141098Sdes * microseconds and four for nanoseconds.  For each there is fast
96141098Sdes * but less precise version "get{nano|micro}[up]time" which will
97228692Sdes * return a time which is up to 1/HZ previous to the call, whereas
98228692Sdes * the raw version "{nano|micro}[up]time" will return a timestamp
99228692Sdes * which is as precise as possible.  The "up" variants return the
100174832Sdes * time relative to system boot, these are well suited for time
101117610Sdes * interval measurements.
102228692Sdes */
103228692Sdes
104141098Sdesvoid
105174832Sdesgetmicrotime(struct timeval *tvp)
106174832Sdes{
107117610Sdes	struct timecounter *tc;
108174832Sdes
109174832Sdes	ngetmicrotime++;
110174832Sdes	tc = timecounter;
111117610Sdes	*tvp = tc->tc_microtime;
112141098Sdes}
113228692Sdes
114228692Sdesvoid
115117610Sdesgetnanotime(struct timespec *tsp)
116228692Sdes{
117228692Sdes	struct timecounter *tc;
118228692Sdes
119228692Sdes	ngetnanotime++;
120228692Sdes	tc = timecounter;
121228692Sdes	*tsp = tc->tc_nanotime;
122228692Sdes}
123228692Sdes
124228692Sdesvoid
125228692Sdesmicrotime(struct timeval *tv)
126141098Sdes{
127228692Sdes	struct timecounter *tc;
128228692Sdes
129141098Sdes	nmicrotime++;
130117610Sdes	tc = timecounter;
131228692Sdes	tv->tv_sec = tc->tc_offset_sec;
132141098Sdes	tv->tv_usec = tc->tc_offset_micro;
133228692Sdes	tv->tv_usec += ((u_int64_t)tco_delta(tc) * tc->tc_scale_micro) >> 32;
134117610Sdes	tv->tv_usec += boottime.tv_usec;
135228692Sdes	tv->tv_sec += boottime.tv_sec;
136117610Sdes	while (tv->tv_usec >= 1000000) {
137228692Sdes		tv->tv_usec -= 1000000;
138228692Sdes		tv->tv_sec++;
139141098Sdes	}
140228692Sdes}
141117610Sdes
142174832Sdesvoid
143117610Sdesnanotime(struct timespec *ts)
144228692Sdes{
145228692Sdes	unsigned count;
146228692Sdes	u_int64_t delta;
147228692Sdes	struct timecounter *tc;
148228692Sdes
149228692Sdes	nnanotime++;
150228692Sdes	tc = timecounter;
151228692Sdes	ts->tv_sec = tc->tc_offset_sec;
152117610Sdes	count = tco_delta(tc);
153141098Sdes	delta = tc->tc_offset_nano;
154228692Sdes	delta += ((u_int64_t)count * tc->tc_scale_nano_f);
155117610Sdes	delta >>= 32;
156228692Sdes	delta += ((u_int64_t)count * tc->tc_scale_nano_i);
157117610Sdes	delta += boottime.tv_usec * 1000;
158228692Sdes	ts->tv_sec += boottime.tv_sec;
159255376Sdes	while (delta >= 1000000000) {
160255376Sdes		delta -= 1000000000;
161255376Sdes		ts->tv_sec++;
162255376Sdes	}
163228692Sdes	ts->tv_nsec = delta;
164117610Sdes}
165228692Sdes
166117610Sdesvoid
167174832Sdesgetmicrouptime(struct timeval *tvp)
168174832Sdes{
169228692Sdes	struct timecounter *tc;
170141098Sdes
171228692Sdes	ngetmicrouptime++;
172228692Sdes	tc = timecounter;
173228692Sdes	tvp->tv_sec = tc->tc_offset_sec;
174228692Sdes	tvp->tv_usec = tc->tc_offset_micro;
175228692Sdes}
176141098Sdes
177228692Sdesvoid
178141098Sdesgetnanouptime(struct timespec *tsp)
179141098Sdes{
180228692Sdes	struct timecounter *tc;
181228692Sdes
182228692Sdes	ngetnanouptime++;
183228692Sdes	tc = timecounter;
184228692Sdes	tsp->tv_sec = tc->tc_offset_sec;
185228692Sdes	tsp->tv_nsec = tc->tc_offset_nano >> 32;
186228692Sdes}
187228692Sdes
188228692Sdesvoid
189228692Sdesmicrouptime(struct timeval *tv)
190228692Sdes{
191228692Sdes	struct timecounter *tc;
192228692Sdes
193255376Sdes	nmicrouptime++;
194255376Sdes	tc = timecounter;
195255376Sdes	tv->tv_sec = tc->tc_offset_sec;
196255376Sdes	tv->tv_usec = tc->tc_offset_micro;
197228692Sdes	tv->tv_usec += ((u_int64_t)tco_delta(tc) * tc->tc_scale_micro) >> 32;
198228692Sdes	if (tv->tv_usec >= 1000000) {
199228692Sdes		tv->tv_usec -= 1000000;
200228692Sdes		tv->tv_sec++;
201141098Sdes	}
202141098Sdes}
203141098Sdes
204141098Sdesvoid
205255376Sdesnanouptime(struct timespec *ts)
206141098Sdes{
207141098Sdes	unsigned count;
208117610Sdes	u_int64_t delta;
209117610Sdes	struct timecounter *tc;
210228692Sdes
211255376Sdes	nnanouptime++;
212255376Sdes	tc = timecounter;
213255376Sdes	ts->tv_sec = tc->tc_offset_sec;
214255376Sdes	count = tco_delta(tc);
215255376Sdes	delta = tc->tc_offset_nano;
216228692Sdes	delta += ((u_int64_t)count * tc->tc_scale_nano_f);
217228692Sdes	delta >>= 32;
218228692Sdes	delta += ((u_int64_t)count * tc->tc_scale_nano_i);
219228692Sdes	if (delta >= 1000000000) {
220228692Sdes		delta -= 1000000000;
221228692Sdes		ts->tv_sec++;
222228692Sdes	}
223228692Sdes	ts->tv_nsec = delta;
224228692Sdes}
225228692Sdes
226228692Sdesstatic void
227228692Sdestco_setscales(struct timecounter *tc)
228228692Sdes{
229228692Sdes	u_int64_t scale;
230228692Sdes
231228692Sdes	scale = 1000000000LL << 32;
232228692Sdes	scale += tc->tc_adjustment;
233228692Sdes	scale /= tc->tc_tweak->tc_frequency;
234228692Sdes	tc->tc_scale_micro = scale / 1000;
235228692Sdes	tc->tc_scale_nano_f = scale & 0xffffffff;
236228692Sdes	tc->tc_scale_nano_i = scale >> 32;
237228692Sdes}
238228692Sdes
239228692Sdesvoid
240228692Sdestc_update(struct timecounter *tc)
241141098Sdes{
242141098Sdes	tco_setscales(tc);
243255376Sdes}
244141098Sdes
245255376Sdesvoid
246141098Sdestc_init(struct timecounter *tc)
247117610Sdes{
248141098Sdes	struct timespec ts1;
249141098Sdes	struct timecounter *t1, *t2, *t3;
250228692Sdes	int i;
251228692Sdes
252228692Sdes	tc->tc_adjustment = 0;
253228692Sdes	tc->tc_tweak = tc;
254117610Sdes	tco_setscales(tc);
255174832Sdes	tc->tc_offset_count = tc->tc_get_timecount(tc);
256141098Sdes	if (timecounter == &dummy_timecounter)
257141098Sdes		tc->tc_avail = tc;
258141098Sdes	else {
259141098Sdes		tc->tc_avail = timecounter->tc_tweak->tc_avail;
260141098Sdes		timecounter->tc_tweak->tc_avail = tc;
261141098Sdes	}
262117610Sdes	MALLOC(t1, struct timecounter *, sizeof *t1, M_TIMECOUNTER, M_WAITOK);
263228692Sdes	tc->tc_other = t1;
264141098Sdes	*t1 = *tc;
265141098Sdes	t2 = t1;
266141098Sdes	for (i = 1; i < NTIMECOUNTER; i++) {
267228692Sdes		MALLOC(t3, struct timecounter *, sizeof *t3,
268117610Sdes		    M_TIMECOUNTER, M_WAITOK);
269141098Sdes		*t3 = *tc;
270141098Sdes		t3->tc_other = t2;
271141098Sdes		t2 = t3;
272174832Sdes	}
273228692Sdes	t1->tc_other = t3;
274174832Sdes	tc = t1;
275174832Sdes
276228692Sdes	printf("Timecounter \"%s\"  frequency %lu Hz\n",
277228692Sdes	    tc->tc_name, (u_long)tc->tc_frequency);
278228692Sdes
279228692Sdes	/* XXX: For now always start using the counter. */
280228692Sdes	tc->tc_offset_count = tc->tc_get_timecount(tc);
281228692Sdes	nanouptime(&ts1);
282228692Sdes	tc->tc_offset_nano = (u_int64_t)ts1.tv_nsec << 32;
283228692Sdes	tc->tc_offset_micro = ts1.tv_nsec / 1000;
284228692Sdes	tc->tc_offset_sec = ts1.tv_sec;
285228692Sdes	timecounter = tc;
286228692Sdes}
287228692Sdes
288228692Sdesvoid
289228692Sdestc_setclock(struct timespec *ts)
290228692Sdes{
291228692Sdes	struct timespec ts2;
292228692Sdes
293228692Sdes	nanouptime(&ts2);
294228692Sdes	boottime.tv_sec = ts->tv_sec - ts2.tv_sec;
295228692Sdes	boottime.tv_usec = (ts->tv_nsec - ts2.tv_nsec) / 1000;
296228692Sdes	if (boottime.tv_usec < 0) {
297228692Sdes		boottime.tv_usec += 1000000;
298228692Sdes		boottime.tv_sec--;
299228692Sdes	}
300228692Sdes	/* fiddle all the little crinkly bits around the fiords... */
301228692Sdes	tc_windup();
302228692Sdes}
303228692Sdes
304228692Sdesstatic void
305228692Sdesswitch_timecounter(struct timecounter *newtc)
306228692Sdes{
307228692Sdes	int s;
308228692Sdes	struct timecounter *tc;
309141098Sdes	struct timespec ts;
310141098Sdes
311117610Sdes	s = splclock();
312228692Sdes	tc = timecounter;
313117610Sdes	if (newtc->tc_tweak == tc->tc_tweak) {
314228692Sdes		splx(s);
315228692Sdes		return;
316228692Sdes	}
317228692Sdes	newtc = newtc->tc_tweak->tc_other;
318228692Sdes	nanouptime(&ts);
319228692Sdes	newtc->tc_offset_sec = ts.tv_sec;
320228692Sdes	newtc->tc_offset_nano = (u_int64_t)ts.tv_nsec << 32;
321228692Sdes	newtc->tc_offset_micro = ts.tv_nsec / 1000;
322228692Sdes	newtc->tc_offset_count = newtc->tc_get_timecount(newtc);
323228692Sdes	tco_setscales(newtc);
324117610Sdes	timecounter = newtc;
325228692Sdes	splx(s);
326228692Sdes}
327228692Sdes
328228692Sdesstatic struct timecounter *
329228692Sdessync_other_counter(void)
330228692Sdes{
331228692Sdes	struct timecounter *tc, *tcn, *tco;
332117610Sdes	unsigned delta;
333228692Sdes
334228692Sdes	tco = timecounter;
335228692Sdes	tc = tco->tc_other;
336228692Sdes	tcn = tc->tc_other;
337228692Sdes	*tc = *tco;
338228692Sdes	tc->tc_other = tcn;
339228692Sdes	delta = tco_delta(tc);
340117610Sdes	tc->tc_offset_count += delta;
341228692Sdes	tc->tc_offset_count &= tc->tc_counter_mask;
342228692Sdes	tc->tc_offset_nano += (u_int64_t)delta * tc->tc_scale_nano_f;
343228692Sdes	tc->tc_offset_nano += (u_int64_t)delta * tc->tc_scale_nano_i << 32;
344228692Sdes	return (tc);
345228692Sdes}
346228692Sdes
347228692Sdesvoid
348228692Sdestc_windup(void)
349228692Sdes{
350117610Sdes	struct timecounter *tc, *tco;
351228692Sdes	struct timeval tvt;
352228692Sdes
353228692Sdes	tco = timecounter;
354228692Sdes	tc = sync_other_counter();
355228692Sdes	/*
356228692Sdes	 * We may be inducing a tiny error here, the tc_poll_pps() may
357255376Sdes	 * process a latched count which happens after the tco_delta()
358228692Sdes	 * in sync_other_counter(), which would extend the previous
359228692Sdes	 * counters parameters into the domain of this new one.
360228692Sdes	 * Since the timewindow is very small for this, the error is
361228692Sdes	 * going to be only a few weenieseconds (as Dave Mills would
362228692Sdes	 * say), so lets just not talk more about it, OK ?
363228692Sdes	 */
364228692Sdes	if (tco->tc_poll_pps)
365228692Sdes		tco->tc_poll_pps(tco);
366228692Sdes	if (timedelta != 0) {
367228692Sdes		tvt = boottime;
368228692Sdes		tvt.tv_usec += tickdelta;
369228692Sdes		if (tvt.tv_usec >= 1000000) {
370228692Sdes			tvt.tv_sec++;
371228692Sdes			tvt.tv_usec -= 1000000;
372228692Sdes		} else if (tvt.tv_usec < 0) {
373228692Sdes			tvt.tv_sec--;
374228692Sdes			tvt.tv_usec += 1000000;
375228692Sdes		}
376228692Sdes		boottime = tvt;
377228692Sdes		timedelta -= tickdelta;
378228692Sdes	}
379228692Sdes
380228692Sdes	while (tc->tc_offset_nano >= 1000000000ULL << 32) {
381228692Sdes		tc->tc_offset_nano -= 1000000000ULL << 32;
382228692Sdes		tc->tc_offset_sec++;
383228692Sdes		ntp_update_second(tc);	/* XXX only needed if xntpd runs */
384228692Sdes		tco_setscales(tc);
385228692Sdes	}
386228692Sdes
387228692Sdes	tc->tc_offset_micro = (tc->tc_offset_nano / 1000) >> 32;
388228692Sdes
389228692Sdes	/* Figure out the wall-clock time */
390228692Sdes	tc->tc_nanotime.tv_sec = tc->tc_offset_sec + boottime.tv_sec;
391228692Sdes	tc->tc_nanotime.tv_nsec =
392228692Sdes	    (tc->tc_offset_nano >> 32) + boottime.tv_usec * 1000;
393228692Sdes	tc->tc_microtime.tv_usec = tc->tc_offset_micro + boottime.tv_usec;
394228692Sdes	if (tc->tc_nanotime.tv_nsec >= 1000000000) {
395255376Sdes		tc->tc_nanotime.tv_nsec -= 1000000000;
396228692Sdes		tc->tc_microtime.tv_usec -= 1000000;
397228692Sdes		tc->tc_nanotime.tv_sec++;
398228692Sdes	}
399228692Sdes	time_second = tc->tc_microtime.tv_sec = tc->tc_nanotime.tv_sec;
400228692Sdes
401228692Sdes	timecounter = tc;
402228692Sdes}
403228692Sdes
404228692Sdesstatic int
405141098Sdessysctl_kern_timecounter_hardware SYSCTL_HANDLER_ARGS
406228692Sdes{
407228692Sdes	char newname[32];
408228692Sdes	struct timecounter *newtc, *tc;
409228692Sdes	int error;
410228692Sdes
411228692Sdes	tc = timecounter->tc_tweak;
412228692Sdes	strncpy(newname, tc->tc_name, sizeof(newname));
413255376Sdes	error = sysctl_handle_string(oidp, &newname[0], sizeof(newname), req);
414228692Sdes	if (error == 0 && req->newptr != NULL &&
415228692Sdes	    strcmp(newname, tc->tc_name) != 0) {
416228692Sdes		for (newtc = tc->tc_avail; newtc != tc;
417228692Sdes		    newtc = newtc->tc_avail) {
418228692Sdes			if (strcmp(newname, newtc->tc_name) == 0) {
419228692Sdes				/* Warm up new timecounter. */
420228692Sdes				(void)newtc->tc_get_timecount(newtc);
421228692Sdes
422228692Sdes				switch_timecounter(newtc);
423228692Sdes				return (0);
424228692Sdes			}
425228692Sdes		}
426228692Sdes		return (EINVAL);
427228692Sdes	}
428228692Sdes	return (error);
429228692Sdes}
430228692Sdes
431228692SdesSYSCTL_PROC(_kern_timecounter, OID_AUTO, hardware, CTLTYPE_STRING | CTLFLAG_RW,
432228692Sdes    0, 0, sysctl_kern_timecounter_hardware, "A", "");
433228692Sdes
434228692Sdes
435228692Sdesint
436228692Sdespps_ioctl(u_long cmd, caddr_t data, struct pps_state *pps)
437228692Sdes{
438228692Sdes	pps_params_t *app;
439228692Sdes	struct pps_fetch_args *fapi;
440228692Sdes#ifdef PPS_SYNC
441141098Sdes	struct pps_kcbind_args *kapi;
442228692Sdes#endif
443141098Sdes
444117610Sdes	switch (cmd) {
445141098Sdes	case PPS_IOC_CREATE:
446228692Sdes		return (0);
447228692Sdes	case PPS_IOC_DESTROY:
448228692Sdes		return (0);
449228692Sdes	case PPS_IOC_SETPARAMS:
450141098Sdes		app = (pps_params_t *)data;
451117610Sdes		if (app->mode & ~pps->ppscap)
452141098Sdes			return (EINVAL);
453141098Sdes		pps->ppsparam = *app;
454141098Sdes		return (0);
455117610Sdes	case PPS_IOC_GETPARAMS:
456141098Sdes		app = (pps_params_t *)data;
457174832Sdes		*app = pps->ppsparam;
458117610Sdes		app->api_version = PPS_API_VERS_1;
459174832Sdes		return (0);
460228692Sdes	case PPS_IOC_GETCAP:
461117610Sdes		*(int*)data = pps->ppscap;
462141098Sdes		return (0);
463141098Sdes	case PPS_IOC_FETCH:
464141098Sdes		fapi = (struct pps_fetch_args *)data;
465141098Sdes		if (fapi->tsformat && fapi->tsformat != PPS_TSFMT_TSPEC)
466174832Sdes			return (EINVAL);
467141098Sdes		if (fapi->timeout.tv_sec || fapi->timeout.tv_nsec)
468228692Sdes			return (EOPNOTSUPP);
469228692Sdes		pps->ppsinfo.current_mode = pps->ppsparam.mode;
470228692Sdes		fapi->pps_info_buf = pps->ppsinfo;
471228692Sdes		return (0);
472117610Sdes	case PPS_IOC_KCBIND:
473228692Sdes#ifdef PPS_SYNC
474228692Sdes		kapi = (struct pps_kcbind_args *)data;
475228692Sdes		/* XXX Only root should be able to do this */
476228692Sdes		if (kapi->tsformat && kapi->tsformat != PPS_TSFMT_TSPEC)
477117610Sdes			return (EINVAL);
478228692Sdes		if (kapi->kernel_consumer != PPS_KC_HARDPPS)
479228692Sdes			return (EINVAL);
480228692Sdes		if (kapi->edge & ~pps->ppscap)
481228692Sdes			return (EINVAL);
482228692Sdes		pps->kcmode = kapi->edge;
483117610Sdes		return (0);
484228692Sdes#else
485228692Sdes		return (EOPNOTSUPP);
486228692Sdes#endif
487228692Sdes	default:
488228692Sdes		return (ENOTTY);
489228692Sdes	}
490228692Sdes}
491228692Sdes
492228692Sdesvoid
493228692Sdespps_init(struct pps_state *pps)
494228692Sdes{
495228692Sdes	pps->ppscap |= PPS_TSFMT_TSPEC;
496228692Sdes	if (pps->ppscap & PPS_CAPTUREASSERT)
497228692Sdes		pps->ppscap |= PPS_OFFSETASSERT;
498228692Sdes	if (pps->ppscap & PPS_CAPTURECLEAR)
499228692Sdes		pps->ppscap |= PPS_OFFSETCLEAR;
500228692Sdes}
501228692Sdes
502228692Sdesvoid
503228692Sdespps_event(struct pps_state *pps, struct timecounter *tc, unsigned count, int event)
504228692Sdes{
505228692Sdes	struct timespec ts, *tsp, *osp;
506228692Sdes	u_int64_t delta;
507228692Sdes	unsigned tcount, *pcount;
508228692Sdes	int foff, fhard;
509228692Sdes	pps_seq_t	*pseq;
510228692Sdes
511228692Sdes	/* Things would be easier with arrays... */
512228692Sdes	if (event == PPS_CAPTUREASSERT) {
513228692Sdes		tsp = &pps->ppsinfo.assert_timestamp;
514228692Sdes		osp = &pps->ppsparam.assert_offset;
515228692Sdes		foff = pps->ppsparam.mode & PPS_OFFSETASSERT;
516228692Sdes		fhard = pps->kcmode & PPS_CAPTUREASSERT;
517228692Sdes		pcount = &pps->ppscount[0];
518228692Sdes		pseq = &pps->ppsinfo.assert_sequence;
519141098Sdes	} else {
520117610Sdes		tsp = &pps->ppsinfo.clear_timestamp;
521141098Sdes		osp = &pps->ppsparam.clear_offset;
522141098Sdes		foff = pps->ppsparam.mode & PPS_OFFSETCLEAR;
523141098Sdes		fhard = pps->kcmode & PPS_CAPTURECLEAR;
524141098Sdes		pcount = &pps->ppscount[1];
525228692Sdes		pseq = &pps->ppsinfo.clear_sequence;
526228692Sdes	}
527141098Sdes
528	/* The timecounter changed: bail */
529	if (!pps->ppstc ||
530	    pps->ppstc->tc_name != tc->tc_name ||
531	    tc->tc_name != timecounter->tc_name) {
532		pps->ppstc = tc;
533		*pcount = count;
534		return;
535	}
536
537	/* Nothing really happened */
538	if (*pcount == count)
539		return;
540
541	*pcount = count;
542
543	/* Convert the count to timespec */
544	ts.tv_sec = tc->tc_offset_sec;
545	tcount = count - tc->tc_offset_count;
546	tcount &= tc->tc_counter_mask;
547	delta = tc->tc_offset_nano;
548	delta += ((u_int64_t)tcount * tc->tc_scale_nano_f);
549	delta >>= 32;
550	delta += ((u_int64_t)tcount * tc->tc_scale_nano_i);
551	delta += boottime.tv_usec * 1000;
552	ts.tv_sec += boottime.tv_sec;
553	while (delta >= 1000000000) {
554		delta -= 1000000000;
555		ts.tv_sec++;
556	}
557	ts.tv_nsec = delta;
558
559	(*pseq)++;
560	*tsp = ts;
561
562	if (foff) {
563		timespecadd(tsp, osp);
564		if (tsp->tv_nsec < 0) {
565			tsp->tv_nsec += 1000000000;
566			tsp->tv_sec -= 1;
567		}
568	}
569#ifdef PPS_SYNC
570	if (fhard) {
571		/* magic, at its best... */
572		tcount = count - pps->ppscount[2];
573		pps->ppscount[2] = count;
574		tcount &= tc->tc_counter_mask;
575		delta = ((u_int64_t)tcount * tc->tc_tweak->tc_scale_nano_f);
576		delta >>= 32;
577		delta += ((u_int64_t)tcount * tc->tc_tweak->tc_scale_nano_i);
578		hardpps(tsp, delta);
579	}
580#endif
581}
582