clock.c revision 1.42
1/*	$OpenBSD: clock.c,v 1.42 2009/01/29 13:36:17 kettenis Exp $	*/
2/*	$NetBSD: clock.c,v 1.39 1996/05/12 23:11:54 mycroft Exp $	*/
3
4/*-
5 * Copyright (c) 1993, 1994 Charles Hannum.
6 * Copyright (c) 1990 The Regents of the University of California.
7 * All rights reserved.
8 *
9 * This code is derived from software contributed to Berkeley by
10 * William Jolitz and Don Ahn.
11 *
12 * Redistribution and use in source and binary forms, with or without
13 * modification, are permitted provided that the following conditions
14 * are met:
15 * 1. Redistributions of source code must retain the above copyright
16 *    notice, this list of conditions and the following disclaimer.
17 * 2. Redistributions in binary form must reproduce the above copyright
18 *    notice, this list of conditions and the following disclaimer in the
19 *    documentation and/or other materials provided with the distribution.
20 * 3. Neither the name of the University nor the names of its contributors
21 *    may be used to endorse or promote products derived from this software
22 *    without specific prior written permission.
23 *
24 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
25 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
26 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
27 * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
28 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
29 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
30 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
31 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
32 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
33 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
34 * SUCH DAMAGE.
35 *
36 *	@(#)clock.c	7.2 (Berkeley) 5/12/91
37 */
38/*
39 * Mach Operating System
40 * Copyright (c) 1991,1990,1989 Carnegie Mellon University
41 * All Rights Reserved.
42 *
43 * Permission to use, copy, modify and distribute this software and its
44 * documentation is hereby granted, provided that both the copyright
45 * notice and this permission notice appear in all copies of the
46 * software, derivative works or modified versions, and any portions
47 * thereof, and that both notices appear in supporting documentation.
48 *
49 * CARNEGIE MELLON ALLOWS FREE USE OF THIS SOFTWARE IN ITS "AS IS"
50 * CONDITION.  CARNEGIE MELLON DISCLAIMS ANY LIABILITY OF ANY KIND FOR
51 * ANY DAMAGES WHATSOEVER RESULTING FROM THE USE OF THIS SOFTWARE.
52 *
53 * Carnegie Mellon requests users of this software to return to
54 *
55 *  Software Distribution Coordinator  or  Software.Distribution@CS.CMU.EDU
56 *  School of Computer Science
57 *  Carnegie Mellon University
58 *  Pittsburgh PA 15213-3890
59 *
60 * any improvements or extensions that they make and grant Carnegie Mellon
61 * the rights to redistribute these changes.
62 */
63/*
64  Copyright 1988, 1989 by Intel Corporation, Santa Clara, California.
65
66		All Rights Reserved
67
68Permission to use, copy, modify, and distribute this software and
69its documentation for any purpose and without fee is hereby
70granted, provided that the above copyright notice appears in all
71copies and that both the copyright notice and this permission notice
72appear in supporting documentation, and that the name of Intel
73not be used in advertising or publicity pertaining to distribution
74of the software without specific, written prior permission.
75
76INTEL DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE
77INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS,
78IN NO EVENT SHALL INTEL BE LIABLE FOR ANY SPECIAL, INDIRECT, OR
79CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
80LOSS OF USE, DATA OR PROFITS, WHETHER IN ACTION OF CONTRACT,
81NEGLIGENCE, OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION
82WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
83*/
84
85/*
86 * Primitive clock interrupt routines.
87 */
88#include <sys/types.h>
89#include <sys/param.h>
90#include <sys/systm.h>
91#include <sys/time.h>
92#include <sys/kernel.h>
93#include <sys/device.h>
94#include <sys/timeout.h>
95#include <sys/timetc.h>
96#include <sys/mutex.h>
97
98#include <machine/cpu.h>
99#include <machine/intr.h>
100#include <machine/pio.h>
101#include <machine/cpufunc.h>
102
103#include <dev/isa/isareg.h>
104#include <dev/isa/isavar.h>
105#include <dev/ic/mc146818reg.h>
106#include <dev/ic/i8253reg.h>
107#include <i386/isa/nvram.h>
108
109void	spinwait(int);
110int	clockintr(void *);
111int	gettick(void);
112int	rtcget(mc_todregs *);
113void	rtcput(mc_todregs *);
114int 	hexdectodec(int);
115int	dectohexdec(int);
116int	rtcintr(void *);
117void	rtcdrain(void *);
118
119u_int mc146818_read(void *, u_int);
120void mc146818_write(void *, u_int, u_int);
121
122int cpuspeed;
123int clock_broken_latch;
124
125/* Timecounter on the i8254 */
126uint32_t i8254_lastcount;
127uint32_t i8254_offset;
128int i8254_ticked;
129u_int i8254_get_timecount(struct timecounter *tc);
130u_int i8254_simple_get_timecount(struct timecounter *tc);
131
132static struct timecounter i8254_timecounter = {
133	i8254_get_timecount, NULL, ~0u, TIMER_FREQ, "i8254", 0, NULL
134};
135struct mutex timer_mutex = MUTEX_INITIALIZER(IPL_HIGH);
136u_long rtclock_tval;
137
138#define	SECMIN	((unsigned)60)			/* seconds per minute */
139#define	SECHOUR	((unsigned)(60*SECMIN))		/* seconds per hour */
140
141u_int
142mc146818_read(void *sc, u_int reg)
143{
144	int s;
145	u_char v;
146
147	s = splhigh();
148	outb(IO_RTC, reg);
149	DELAY(1);
150	v = inb(IO_RTC+1);
151	DELAY(1);
152	splx(s);
153	return (v);
154}
155
156void
157mc146818_write(void *sc, u_int reg, u_int datum)
158{
159	int s;
160
161	s = splhigh();
162	outb(IO_RTC, reg);
163	DELAY(1);
164	outb(IO_RTC+1, datum);
165	DELAY(1);
166	splx(s);
167}
168
169void
170startrtclock(void)
171{
172	int s;
173
174	initrtclock();
175
176	/* Check diagnostic status */
177	if ((s = mc146818_read(NULL, NVRAM_DIAG)) != 0)	/* XXX softc */
178		printf("RTC BIOS diagnostic error %b\n", (unsigned int) s,
179		    NVRAM_DIAG_BITS);
180}
181
182void
183rtcdrain(void *v)
184{
185	struct timeout *to = (struct timeout *)v;
186
187	if (to != NULL)
188		timeout_del(to);
189
190	/*
191	 * Drain any un-acknowledged RTC interrupts.
192	 * See comment in cpu_initclocks().
193	 */
194  	while (mc146818_read(NULL, MC_REGC) & MC_REGC_PF)
195		; /* Nothing. */
196}
197
198void
199initrtclock(void)
200{
201	mtx_enter(&timer_mutex);
202
203	/* initialize 8253 clock */
204	outb(IO_TIMER1 + TIMER_MODE, TIMER_SEL0|TIMER_RATEGEN|TIMER_16BIT);
205
206	/* Correct rounding will buy us a better precision in timekeeping */
207	outb(IO_TIMER1, TIMER_DIV(hz) % 256);
208	outb(IO_TIMER1, TIMER_DIV(hz) / 256);
209
210	rtclock_tval = TIMER_DIV(hz);
211	mtx_leave(&timer_mutex);
212}
213
214int
215clockintr(void *arg)
216{
217	struct clockframe *frame = arg;		/* not strictly necessary */
218
219	if (timecounter->tc_get_timecount == i8254_get_timecount) {
220		if (i8254_ticked) {
221			i8254_ticked = 0;
222		} else {
223			i8254_offset += rtclock_tval;
224			i8254_lastcount = 0;
225		}
226	}
227
228	hardclock(frame);
229	return (1);
230}
231
232int
233rtcintr(void *arg)
234{
235	struct clockframe *frame = arg;		/* not strictly necessary */
236	u_int stat = 0;
237
238	if (stathz == 0) {
239		extern int psratio;
240
241		stathz = 128;
242		profhz = 1024;
243		psratio = profhz / stathz;
244	}
245
246	/*
247	 * If rtcintr is 'late', next intr may happen immediately.
248	 * Get them all. (Also, see comment in cpu_initclocks().)
249	 */
250	while (mc146818_read(NULL, MC_REGC) & MC_REGC_PF) {
251		statclock(frame);
252		stat = 1;
253	}
254	return (stat);
255}
256
257int
258gettick(void)
259{
260
261	if (clock_broken_latch) {
262		int v1, v2, v3;
263		int w1, w2, w3;
264
265		/*
266		 * Don't lock the mutex in this case, clock_broken_latch
267		 * CPUs don't do MP anyway.
268		 */
269
270		disable_intr();
271
272		v1 = inb(IO_TIMER1 + TIMER_CNTR0);
273		v1 |= inb(IO_TIMER1 + TIMER_CNTR0) << 8;
274		v2 = inb(IO_TIMER1 + TIMER_CNTR0);
275		v2 |= inb(IO_TIMER1 + TIMER_CNTR0) << 8;
276		v3 = inb(IO_TIMER1 + TIMER_CNTR0);
277		v3 |= inb(IO_TIMER1 + TIMER_CNTR0) << 8;
278
279		enable_intr();
280
281		if (v1 >= v2 && v2 >= v3 && v1 - v3 < 0x200)
282			return (v2);
283
284#define _swap_val(a, b) do { \
285	int c = a; \
286	a = b; \
287	b = c; \
288} while (0)
289
290		/* sort v1 v2 v3 */
291		if (v1 < v2)
292			_swap_val(v1, v2);
293		if (v2 < v3)
294			_swap_val(v2, v3);
295		if (v1 < v2)
296			_swap_val(v1, v2);
297
298		/* compute the middle value */
299		if (v1 - v3 < 0x200)
300			return (v2);
301		w1 = v2 - v3;
302		w2 = v3 - v1 + TIMER_DIV(hz);
303		w3 = v1 - v2;
304		if (w1 >= w2) {
305			if (w1 >= w3)
306				return (v1);
307		} else {
308			if (w2 >= w3)
309				return (v2);
310		}
311		return (v3);
312	} else {
313		u_char lo, hi;
314		u_long ef;
315
316		mtx_enter(&timer_mutex);
317		ef = read_eflags();
318		disable_intr();
319		/* Select counter 0 and latch it. */
320		outb(IO_TIMER1 + TIMER_MODE, TIMER_SEL0 | TIMER_LATCH);
321		lo = inb(IO_TIMER1 + TIMER_CNTR0);
322		hi = inb(IO_TIMER1 + TIMER_CNTR0);
323
324		write_eflags(ef);
325		mtx_leave(&timer_mutex);
326		return ((hi << 8) | lo);
327	}
328}
329
330/*
331 * Wait "n" microseconds.
332 * Relies on timer 1 counting down from (TIMER_FREQ / hz) at TIMER_FREQ Hz.
333 * Note: timer had better have been programmed before this is first used!
334 * (Note that we use `rate generator' mode, which counts at 1:1; `square
335 * wave' mode counts at 2:1).
336 */
337void
338i8254_delay(int n)
339{
340	int limit, tick, otick;
341
342	/*
343	 * Read the counter first, so that the rest of the setup overhead is
344	 * counted.
345	 */
346	otick = gettick();
347
348#ifdef __GNUC__
349	/*
350	 * Calculate ((n * TIMER_FREQ) / 1e6) using explicit assembler code so
351	 * we can take advantage of the intermediate 64-bit quantity to prevent
352	 * loss of significance.
353	 */
354	n -= 5;
355	if (n < 0)
356		return;
357	__asm __volatile("mul %2\n\tdiv %3"
358			 : "=a" (n)
359			 : "0" (n), "r" (TIMER_FREQ), "r" (1000000)
360			 : "%edx", "cc");
361#else
362	/*
363	 * Calculate ((n * TIMER_FREQ) / 1e6) without using floating point and
364	 * without any avoidable overflows.
365	 */
366	n -= 20;
367	{
368		int sec = n / 1000000,
369		    usec = n % 1000000;
370		n = sec * TIMER_FREQ +
371		    usec * (TIMER_FREQ / 1000000) +
372		    usec * ((TIMER_FREQ % 1000000) / 1000) / 1000 +
373		    usec * (TIMER_FREQ % 1000) / 1000000;
374	}
375#endif
376
377	limit = TIMER_FREQ / hz;
378
379	while (n > 0) {
380		tick = gettick();
381		if (tick > otick)
382			n -= limit - (tick - otick);
383		else
384			n -= otick - tick;
385		otick = tick;
386	}
387}
388
389void
390calibrate_cyclecounter(void)
391{
392	unsigned long long count, last_count;
393
394	__asm __volatile("rdtsc" : "=A" (last_count));
395	delay(1000000);
396	__asm __volatile("rdtsc" : "=A" (count));
397	cpuspeed = ((count - last_count) + 999999) / 1000000;
398}
399
400void
401i8254_initclocks(void)
402{
403	static struct timeout rtcdrain_timeout;
404
405	/*
406	 * XXX If you're doing strange things with multiple clocks, you might
407	 * want to keep track of clock handlers.
408	 */
409	(void)isa_intr_establish(NULL, 0, IST_PULSE, IPL_CLOCK, clockintr,
410	    0, "clock");
411	(void)isa_intr_establish(NULL, 8, IST_PULSE, IPL_CLOCK, rtcintr,
412	    0, "rtc");
413
414	mc146818_write(NULL, MC_REGA, MC_BASE_32_KHz | MC_RATE_128_Hz);
415	mc146818_write(NULL, MC_REGB, MC_REGB_24HR | MC_REGB_PIE);
416
417	/*
418	 * On a number of i386 systems, the rtc will fail to start when booting
419	 * the system. This is due to us missing to acknowledge an interrupt
420	 * during early stages of the boot process. If we do not acknowledge
421	 * the interrupt, the rtc clock will not generate further interrupts.
422	 * To solve this, once interrupts are enabled, use a timeout (once)
423	 * to drain any un-acknowledged rtc interrupt(s).
424	 */
425
426	timeout_set(&rtcdrain_timeout, rtcdrain, (void *)&rtcdrain_timeout);
427	timeout_add(&rtcdrain_timeout, 1);
428}
429
430int
431rtcget(mc_todregs *regs)
432{
433	if ((mc146818_read(NULL, MC_REGD) & MC_REGD_VRT) == 0) /* XXX softc */
434		return (-1);
435	MC146818_GETTOD(NULL, regs);			/* XXX softc */
436	return (0);
437}
438
439void
440rtcput(mc_todregs *regs)
441{
442	MC146818_PUTTOD(NULL, regs);			/* XXX softc */
443}
444
445int
446hexdectodec(int n)
447{
448
449	return (((n >> 4) & 0x0f) * 10 + (n & 0x0f));
450}
451
452int
453dectohexdec(int n)
454{
455
456	return ((u_char)(((n / 10) << 4) & 0xf0) | ((n % 10) & 0x0f));
457}
458
459static int timeset;
460
461/*
462 * check whether the CMOS layout is "standard"-like (ie, not PS/2-like),
463 * to be called at splclock()
464 */
465int cmoscheck(void);
466int
467cmoscheck(void)
468{
469	int i;
470	unsigned short cksum = 0;
471
472	for (i = 0x10; i <= 0x2d; i++)
473		cksum += mc146818_read(NULL, i); /* XXX softc */
474
475	return (cksum == (mc146818_read(NULL, 0x2e) << 8)
476			  + mc146818_read(NULL, 0x2f));
477}
478
479/*
480 * patchable to control century byte handling:
481 * 1: always update
482 * -1: never touch
483 * 0: try to figure out itself
484 */
485int rtc_update_century = 0;
486
487/*
488 * Expand a two-digit year as read from the clock chip
489 * into full width.
490 * Being here, deal with the CMOS century byte.
491 */
492int clock_expandyear(int);
493int
494clock_expandyear(int clockyear)
495{
496	int s, clockcentury, cmoscentury;
497
498	clockcentury = (clockyear < 70) ? 20 : 19;
499	clockyear += 100 * clockcentury;
500
501	if (rtc_update_century < 0)
502		return (clockyear);
503
504	s = splclock();
505	if (cmoscheck())
506		cmoscentury = mc146818_read(NULL, NVRAM_CENTURY);
507	else
508		cmoscentury = 0;
509	splx(s);
510	if (!cmoscentury) {
511#ifdef DIAGNOSTIC
512		printf("clock: unknown CMOS layout\n");
513#endif
514		return (clockyear);
515	}
516	cmoscentury = hexdectodec(cmoscentury);
517
518	if (cmoscentury != clockcentury) {
519		/* XXX note: saying "century is 20" might confuse the naive. */
520		printf("WARNING: NVRAM century is %d but RTC year is %d\n",
521		       cmoscentury, clockyear);
522
523		/* Kludge to roll over century. */
524		if ((rtc_update_century > 0) ||
525		    ((cmoscentury == 19) && (clockcentury == 20) &&
526		     (clockyear == 2000))) {
527			printf("WARNING: Setting NVRAM century to %d\n",
528			       clockcentury);
529			s = splclock();
530			mc146818_write(NULL, NVRAM_CENTURY,
531				       dectohexdec(clockcentury));
532			splx(s);
533		}
534	} else if (cmoscentury == 19 && rtc_update_century == 0)
535		rtc_update_century = 1; /* will update later in resettodr() */
536
537	return (clockyear);
538}
539
540/*
541 * Initialize the time of day register, based on the time base which is, e.g.
542 * from a filesystem.
543 */
544void
545inittodr(time_t base)
546{
547	struct timespec ts;
548	mc_todregs rtclk;
549	struct clock_ymdhms dt;
550	int s;
551
552
553	ts.tv_nsec = 0;
554
555	/*
556	 * We mostly ignore the suggested time and go for the RTC clock time
557	 * stored in the CMOS RAM.  If the time can't be obtained from the
558	 * CMOS, or if the time obtained from the CMOS is 5 or more years
559	 * less than the suggested time, we used the suggested time.  (In
560	 * the latter case, it's likely that the CMOS battery has died.)
561	 */
562
563	if (base < 15*SECYR) {	/* if before 1985, something's odd... */
564		printf("WARNING: preposterous time in file system\n");
565		/* read the system clock anyway */
566		base = 17*SECYR + 186*SECDAY + SECDAY/2;
567	}
568
569	s = splclock();
570	if (rtcget(&rtclk)) {
571		splx(s);
572		printf("WARNING: invalid time in clock chip\n");
573		goto fstime;
574	}
575	splx(s);
576
577	dt.dt_sec = hexdectodec(rtclk[MC_SEC]);
578	dt.dt_min = hexdectodec(rtclk[MC_MIN]);
579	dt.dt_hour = hexdectodec(rtclk[MC_HOUR]);
580	dt.dt_day = hexdectodec(rtclk[MC_DOM]);
581	dt.dt_mon = hexdectodec(rtclk[MC_MONTH]);
582	dt.dt_year = clock_expandyear(hexdectodec(rtclk[MC_YEAR]));
583
584
585	/*
586	 * If time_t is 32 bits, then the "End of Time" is
587	 * Mon Jan 18 22:14:07 2038 (US/Eastern)
588	 * This code copes with RTC's past the end of time if time_t
589	 * is an int32 or less. Needed because sometimes RTCs screw
590	 * up or are badly set, and that would cause the time to go
591	 * negative in the calculation below, which causes Very Bad
592	 * Mojo. This at least lets the user boot and fix the problem.
593	 * Note the code is self eliminating once time_t goes to 64 bits.
594	 */
595	if (sizeof(time_t) <= sizeof(int32_t)) {
596		if (dt.dt_year >= 2038) {
597			printf("WARNING: RTC time at or beyond 2038.\n");
598			dt.dt_year = 2037;
599			printf("WARNING: year set back to 2037.\n");
600			printf("WARNING: CHECK AND RESET THE DATE!\n");
601		}
602	}
603
604	ts.tv_sec = clock_ymdhms_to_secs(&dt) + tz.tz_minuteswest * 60;
605	if (tz.tz_dsttime)
606		ts.tv_sec -= 3600;
607
608	if (base < ts.tv_sec - 5*SECYR)
609		printf("WARNING: file system time much less than clock time\n");
610	else if (base > ts.tv_sec + 5*SECYR) {
611		printf("WARNING: clock time much less than file system time\n");
612		printf("WARNING: using file system time\n");
613		goto fstime;
614	}
615
616	tc_setclock(&ts);
617	timeset = 1;
618	return;
619
620fstime:
621	ts.tv_sec = base;
622	tc_setclock(&ts);
623	timeset = 1;
624	printf("WARNING: CHECK AND RESET THE DATE!\n");
625}
626
627/*
628 * Reset the clock.
629 */
630void
631resettodr(void)
632{
633	mc_todregs rtclk;
634	struct clock_ymdhms dt;
635	int diff;
636	int century;
637	int s;
638
639	/*
640	 * We might have been called by boot() due to a crash early
641	 * on.  Don't reset the clock chip in this case.
642	 */
643	if (!timeset)
644		return;
645
646	s = splclock();
647	if (rtcget(&rtclk))
648		bzero(&rtclk, sizeof(rtclk));
649	splx(s);
650
651	diff = tz.tz_minuteswest * 60;
652	if (tz.tz_dsttime)
653		diff -= 3600;
654	clock_secs_to_ymdhms(time_second - diff, &dt);
655
656	rtclk[MC_SEC] = dectohexdec(dt.dt_sec);
657	rtclk[MC_MIN] = dectohexdec(dt.dt_min);
658	rtclk[MC_HOUR] = dectohexdec(dt.dt_hour);
659	rtclk[MC_DOW] = dt.dt_wday;
660	rtclk[MC_YEAR] = dectohexdec(dt.dt_year % 100);
661	rtclk[MC_MONTH] = dectohexdec(dt.dt_mon);
662	rtclk[MC_DOM] = dectohexdec(dt.dt_day);
663	s = splclock();
664	rtcput(&rtclk);
665	if (rtc_update_century > 0) {
666		century = dectohexdec(dt.dt_year / 100);
667		mc146818_write(NULL, NVRAM_CENTURY, century); /* XXX softc */
668	}
669	splx(s);
670}
671
672void
673setstatclockrate(int arg)
674{
675	if (arg == stathz)
676		mc146818_write(NULL, MC_REGA, MC_BASE_32_KHz | MC_RATE_128_Hz);
677	else
678		mc146818_write(NULL, MC_REGA, MC_BASE_32_KHz | MC_RATE_1024_Hz);
679}
680
681void
682i8254_inittimecounter(void)
683{
684	tc_init(&i8254_timecounter);
685}
686
687/*
688 * If we're using lapic to drive hardclock, we can use a simpler
689 * algorithm for the i8254 timecounters.
690 */
691void
692i8254_inittimecounter_simple(void)
693{
694	u_long tval = 0x8000;
695
696	i8254_timecounter.tc_get_timecount = i8254_simple_get_timecount;
697	i8254_timecounter.tc_counter_mask = 0x7fff;
698
699	i8254_timecounter.tc_frequency = TIMER_FREQ;
700
701	mtx_enter(&timer_mutex);
702	outb(IO_TIMER1 + TIMER_MODE, TIMER_SEL0 | TIMER_RATEGEN | TIMER_16BIT);
703	outb(IO_TIMER1, tval & 0xff);
704	outb(IO_TIMER1, tval >> 8);
705
706	rtclock_tval = tval;
707	mtx_leave(&timer_mutex);
708
709	tc_init(&i8254_timecounter);
710}
711
712u_int
713i8254_simple_get_timecount(struct timecounter *tc)
714{
715	return (rtclock_tval - gettick());
716}
717
718u_int
719i8254_get_timecount(struct timecounter *tc)
720{
721	u_char hi, lo;
722	u_int count;
723	u_long ef;
724
725	ef = read_eflags();
726	disable_intr();
727
728	outb(IO_TIMER1 + TIMER_MODE, TIMER_SEL0 | TIMER_LATCH);
729	lo = inb(IO_TIMER1 + TIMER_CNTR0);
730	hi = inb(IO_TIMER1 + TIMER_CNTR0);
731
732	count = rtclock_tval - ((hi << 8) | lo);
733
734	if (count < i8254_lastcount) {
735		i8254_ticked = 1;
736		i8254_offset += rtclock_tval;
737	}
738	i8254_lastcount = count;
739	count += i8254_offset;
740	write_eflags(ef);
741
742	return (count);
743}
744