clock.c revision 1.39
1/*	$OpenBSD: clock.c,v 1.39 2007/03/19 09:29:33 art 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 <i386/isa/nvram.h>
107#include <i386/isa/timerreg.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
122#if defined(I586_CPU) || defined(I686_CPU)
123int cpuspeed;
124#endif
125#if defined(I486_CPU) || defined(I586_CPU) || defined(I686_CPU)
126int clock_broken_latch;
127#endif
128
129/* Timecounter on the i8254 */
130uint32_t i8254_lastcount;
131uint32_t i8254_offset;
132int i8254_ticked;
133u_int i8254_get_timecount(struct timecounter *tc);
134u_int i8254_simple_get_timecount(struct timecounter *tc);
135
136static struct timecounter i8254_timecounter = {
137	i8254_get_timecount, NULL, ~0u, TIMER_FREQ, "i8254", 0, NULL
138};
139struct mutex timer_mutex = MUTEX_INITIALIZER(IPL_HIGH);
140u_long rtclock_tval;
141
142#define	SECMIN	((unsigned)60)			/* seconds per minute */
143#define	SECHOUR	((unsigned)(60*SECMIN))		/* seconds per hour */
144
145u_int
146mc146818_read(void *sc, u_int reg)
147{
148	int s;
149	u_char v;
150
151	s = splhigh();
152	outb(IO_RTC, reg);
153	DELAY(1);
154	v = inb(IO_RTC+1);
155	DELAY(1);
156	splx(s);
157	return (v);
158}
159
160void
161mc146818_write(void *sc, u_int reg, u_int datum)
162{
163	int s;
164
165	s = splhigh();
166	outb(IO_RTC, reg);
167	DELAY(1);
168	outb(IO_RTC+1, datum);
169	DELAY(1);
170	splx(s);
171}
172
173void
174startrtclock(void)
175{
176	int s;
177
178	initrtclock();
179
180	/* Check diagnostic status */
181	if ((s = mc146818_read(NULL, NVRAM_DIAG)) != 0)	/* XXX softc */
182		printf("RTC BIOS diagnostic error %b\n", (unsigned int) s,
183		    NVRAM_DIAG_BITS);
184}
185
186void
187rtcdrain(void *v)
188{
189	struct timeout *to = (struct timeout *)v;
190
191	if (to != NULL)
192		timeout_del(to);
193
194	/*
195	 * Drain any un-acknowledged RTC interrupts.
196	 * See comment in cpu_initclocks().
197	 */
198  	while (mc146818_read(NULL, MC_REGC) & MC_REGC_PF)
199		; /* Nothing. */
200}
201
202void
203initrtclock(void)
204{
205	mtx_enter(&timer_mutex);
206
207	/* initialize 8253 clock */
208	outb(TIMER_MODE, TIMER_SEL0|TIMER_RATEGEN|TIMER_16BIT);
209
210	/* Correct rounding will buy us a better precision in timekeeping */
211	outb(IO_TIMER1, TIMER_DIV(hz) % 256);
212	outb(IO_TIMER1, TIMER_DIV(hz) / 256);
213
214	rtclock_tval = TIMER_DIV(hz);
215	mtx_leave(&timer_mutex);
216}
217
218int
219clockintr(void *arg)
220{
221	struct clockframe *frame = arg;		/* not strictly necessary */
222
223	if (timecounter->tc_get_timecount == i8254_get_timecount) {
224		if (i8254_ticked) {
225			i8254_ticked = 0;
226		} else {
227			i8254_offset += rtclock_tval;
228			i8254_lastcount = 0;
229		}
230	}
231
232	hardclock(frame);
233	return (1);
234}
235
236int
237rtcintr(void *arg)
238{
239	struct clockframe *frame = arg;		/* not strictly necessary */
240	u_int stat = 0;
241
242	/*
243	 * If rtcintr is 'late', next intr may happen immediately.
244	 * Get them all. (Also, see comment in cpu_initclocks().)
245	 */
246	while (mc146818_read(NULL, MC_REGC) & MC_REGC_PF) {
247		statclock(frame);
248		stat = 1;
249	}
250	return (stat);
251}
252
253int
254gettick(void)
255{
256
257#if defined(I586_CPU) || defined(I686_CPU)
258	if (clock_broken_latch) {
259		int v1, v2, v3;
260		int w1, w2, w3;
261
262		/*
263		 * Don't lock the mutex in this case, clock_broken_latch
264		 * CPUs don't do MP anyway.
265		 */
266
267		disable_intr();
268
269		v1 = inb(TIMER_CNTR0);
270		v1 |= inb(TIMER_CNTR0) << 8;
271		v2 = inb(TIMER_CNTR0);
272		v2 |= inb(TIMER_CNTR0) << 8;
273		v3 = inb(TIMER_CNTR0);
274		v3 |= inb(TIMER_CNTR0) << 8;
275
276		enable_intr();
277
278		if (v1 >= v2 && v2 >= v3 && v1 - v3 < 0x200)
279			return (v2);
280
281#define _swap_val(a, b) do { \
282	int c = a; \
283	a = b; \
284	b = c; \
285} while (0)
286
287		/* sort v1 v2 v3 */
288		if (v1 < v2)
289			_swap_val(v1, v2);
290		if (v2 < v3)
291			_swap_val(v2, v3);
292		if (v1 < v2)
293			_swap_val(v1, v2);
294
295		/* compute the middle value */
296		if (v1 - v3 < 0x200)
297			return (v2);
298		w1 = v2 - v3;
299		w2 = v3 - v1 + TIMER_DIV(hz);
300		w3 = v1 - v2;
301		if (w1 >= w2) {
302			if (w1 >= w3)
303				return (v1);
304		} else {
305			if (w2 >= w3)
306				return (v2);
307		}
308		return (v3);
309	} else
310#endif
311	{
312		u_char lo, hi;
313		u_long ef;
314
315		mtx_enter(&timer_mutex);
316		ef = read_eflags();
317		disable_intr();
318		/* Select counter 0 and latch it. */
319		outb(TIMER_MODE, TIMER_SEL0 | TIMER_LATCH);
320		lo = inb(TIMER_CNTR0);
321		hi = inb(TIMER_CNTR0);
322
323		write_eflags(ef);
324		mtx_leave(&timer_mutex);
325		return ((hi << 8) | lo);
326	}
327}
328
329/*
330 * Wait "n" microseconds.
331 * Relies on timer 1 counting down from (TIMER_FREQ / hz) at TIMER_FREQ Hz.
332 * Note: timer had better have been programmed before this is first used!
333 * (Note that we use `rate generator' mode, which counts at 1:1; `square
334 * wave' mode counts at 2:1).
335 */
336void
337i8254_delay(int n)
338{
339	int limit, tick, otick;
340
341	/*
342	 * Read the counter first, so that the rest of the setup overhead is
343	 * counted.
344	 */
345	otick = gettick();
346
347#ifdef __GNUC__
348	/*
349	 * Calculate ((n * TIMER_FREQ) / 1e6) using explicit assembler code so
350	 * we can take advantage of the intermediate 64-bit quantity to prevent
351	 * loss of significance.
352	 */
353	n -= 5;
354	if (n < 0)
355		return;
356	__asm __volatile("mul %2\n\tdiv %3"
357			 : "=a" (n)
358			 : "0" (n), "r" (TIMER_FREQ), "r" (1000000)
359			 : "%edx", "cc");
360#else
361	/*
362	 * Calculate ((n * TIMER_FREQ) / 1e6) without using floating point and
363	 * without any avoidable overflows.
364	 */
365	n -= 20;
366	{
367		int sec = n / 1000000,
368		    usec = n % 1000000;
369		n = sec * TIMER_FREQ +
370		    usec * (TIMER_FREQ / 1000000) +
371		    usec * ((TIMER_FREQ % 1000000) / 1000) / 1000 +
372		    usec * (TIMER_FREQ % 1000) / 1000000;
373	}
374#endif
375
376	limit = TIMER_FREQ / hz;
377
378	while (n > 0) {
379		tick = gettick();
380		if (tick > otick)
381			n -= limit - (tick - otick);
382		else
383			n -= otick - tick;
384		otick = tick;
385	}
386}
387
388#if defined(I586_CPU) || defined(I686_CPU)
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#endif
400
401void
402i8254_initclocks(void)
403{
404	static struct timeout rtcdrain_timeout;
405	stathz = 128;
406	profhz = 1024;
407
408	/*
409	 * XXX If you're doing strange things with multiple clocks, you might
410	 * want to keep track of clock handlers.
411	 */
412	(void)isa_intr_establish(NULL, 0, IST_PULSE, IPL_CLOCK, clockintr,
413	    0, "clock");
414	(void)isa_intr_establish(NULL, 8, IST_PULSE, IPL_CLOCK, rtcintr,
415	    0, "rtc");
416
417	mc146818_write(NULL, MC_REGA, MC_BASE_32_KHz | MC_RATE_128_Hz);
418	mc146818_write(NULL, MC_REGB, MC_REGB_24HR | MC_REGB_PIE);
419
420	/*
421	 * On a number of i386 systems, the rtc will fail to start when booting
422	 * the system. This is due to us missing to acknowledge an interrupt
423	 * during early stages of the boot process. If we do not acknowledge
424	 * the interrupt, the rtc clock will not generate further interrupts.
425	 * To solve this, once interrupts are enabled, use a timeout (once)
426	 * to drain any un-acknowledged rtc interrupt(s).
427	 */
428
429	timeout_set(&rtcdrain_timeout, rtcdrain, (void *)&rtcdrain_timeout);
430	timeout_add(&rtcdrain_timeout, 1);
431}
432
433int
434rtcget(mc_todregs *regs)
435{
436	if ((mc146818_read(NULL, MC_REGD) & MC_REGD_VRT) == 0) /* XXX softc */
437		return (-1);
438	MC146818_GETTOD(NULL, regs);			/* XXX softc */
439	return (0);
440}
441
442void
443rtcput(mc_todregs *regs)
444{
445	MC146818_PUTTOD(NULL, regs);			/* XXX softc */
446}
447
448int
449hexdectodec(int n)
450{
451
452	return (((n >> 4) & 0x0f) * 10 + (n & 0x0f));
453}
454
455int
456dectohexdec(int n)
457{
458
459	return ((u_char)(((n / 10) << 4) & 0xf0) | ((n % 10) & 0x0f));
460}
461
462static int timeset;
463
464/*
465 * check whether the CMOS layout is "standard"-like (ie, not PS/2-like),
466 * to be called at splclock()
467 */
468int cmoscheck(void);
469int
470cmoscheck(void)
471{
472	int i;
473	unsigned short cksum = 0;
474
475	for (i = 0x10; i <= 0x2d; i++)
476		cksum += mc146818_read(NULL, i); /* XXX softc */
477
478	return (cksum == (mc146818_read(NULL, 0x2e) << 8)
479			  + mc146818_read(NULL, 0x2f));
480}
481
482/*
483 * patchable to control century byte handling:
484 * 1: always update
485 * -1: never touch
486 * 0: try to figure out itself
487 */
488int rtc_update_century = 0;
489
490/*
491 * Expand a two-digit year as read from the clock chip
492 * into full width.
493 * Being here, deal with the CMOS century byte.
494 */
495int clock_expandyear(int);
496int
497clock_expandyear(int clockyear)
498{
499	int s, clockcentury, cmoscentury;
500
501	clockcentury = (clockyear < 70) ? 20 : 19;
502	clockyear += 100 * clockcentury;
503
504	if (rtc_update_century < 0)
505		return (clockyear);
506
507	s = splclock();
508	if (cmoscheck())
509		cmoscentury = mc146818_read(NULL, NVRAM_CENTURY);
510	else
511		cmoscentury = 0;
512	splx(s);
513	if (!cmoscentury) {
514#ifdef DIAGNOSTIC
515		printf("clock: unknown CMOS layout\n");
516#endif
517		return (clockyear);
518	}
519	cmoscentury = hexdectodec(cmoscentury);
520
521	if (cmoscentury != clockcentury) {
522		/* XXX note: saying "century is 20" might confuse the naive. */
523		printf("WARNING: NVRAM century is %d but RTC year is %d\n",
524		       cmoscentury, clockyear);
525
526		/* Kludge to roll over century. */
527		if ((rtc_update_century > 0) ||
528		    ((cmoscentury == 19) && (clockcentury == 20) &&
529		     (clockyear == 2000))) {
530			printf("WARNING: Setting NVRAM century to %d\n",
531			       clockcentury);
532			s = splclock();
533			mc146818_write(NULL, NVRAM_CENTURY,
534				       dectohexdec(clockcentury));
535			splx(s);
536		}
537	} else if (cmoscentury == 19 && rtc_update_century == 0)
538		rtc_update_century = 1; /* will update later in resettodr() */
539
540	return (clockyear);
541}
542
543/*
544 * Initialize the time of day register, based on the time base which is, e.g.
545 * from a filesystem.
546 */
547void
548inittodr(time_t base)
549{
550	struct timespec ts;
551	mc_todregs rtclk;
552	struct clock_ymdhms dt;
553	int s;
554
555
556	ts.tv_nsec = 0;
557
558	/*
559	 * We mostly ignore the suggested time and go for the RTC clock time
560	 * stored in the CMOS RAM.  If the time can't be obtained from the
561	 * CMOS, or if the time obtained from the CMOS is 5 or more years
562	 * less than the suggested time, we used the suggested time.  (In
563	 * the latter case, it's likely that the CMOS battery has died.)
564	 */
565
566	if (base < 15*SECYR) {	/* if before 1985, something's odd... */
567		printf("WARNING: preposterous time in file system\n");
568		/* read the system clock anyway */
569		base = 17*SECYR + 186*SECDAY + SECDAY/2;
570	}
571
572	s = splclock();
573	if (rtcget(&rtclk)) {
574		splx(s);
575		printf("WARNING: invalid time in clock chip\n");
576		goto fstime;
577	}
578	splx(s);
579
580	dt.dt_sec = hexdectodec(rtclk[MC_SEC]);
581	dt.dt_min = hexdectodec(rtclk[MC_MIN]);
582	dt.dt_hour = hexdectodec(rtclk[MC_HOUR]);
583	dt.dt_day = hexdectodec(rtclk[MC_DOM]);
584	dt.dt_mon = hexdectodec(rtclk[MC_MONTH]);
585	dt.dt_year = clock_expandyear(hexdectodec(rtclk[MC_YEAR]));
586
587
588	/*
589	 * If time_t is 32 bits, then the "End of Time" is
590	 * Mon Jan 18 22:14:07 2038 (US/Eastern)
591	 * This code copes with RTC's past the end of time if time_t
592	 * is an int32 or less. Needed because sometimes RTCs screw
593	 * up or are badly set, and that would cause the time to go
594	 * negative in the calculation below, which causes Very Bad
595	 * Mojo. This at least lets the user boot and fix the problem.
596	 * Note the code is self eliminating once time_t goes to 64 bits.
597	 */
598	if (sizeof(time_t) <= sizeof(int32_t)) {
599		if (dt.dt_year >= 2038) {
600			printf("WARNING: RTC time at or beyond 2038.\n");
601			dt.dt_year = 2037;
602			printf("WARNING: year set back to 2037.\n");
603			printf("WARNING: CHECK AND RESET THE DATE!\n");
604		}
605	}
606
607	ts.tv_sec = clock_ymdhms_to_secs(&dt) + tz.tz_minuteswest * 60;
608	if (tz.tz_dsttime)
609		ts.tv_sec -= 3600;
610
611	if (base < ts.tv_sec - 5*SECYR)
612		printf("WARNING: file system time much less than clock time\n");
613	else if (base > ts.tv_sec + 5*SECYR) {
614		printf("WARNING: clock time much less than file system time\n");
615		printf("WARNING: using file system time\n");
616		goto fstime;
617	}
618
619	tc_setclock(&ts);
620	timeset = 1;
621	return;
622
623fstime:
624	ts.tv_sec = base;
625	tc_setclock(&ts);
626	timeset = 1;
627	printf("WARNING: CHECK AND RESET THE DATE!\n");
628}
629
630/*
631 * Reset the clock.
632 */
633void
634resettodr(void)
635{
636	mc_todregs rtclk;
637	struct clock_ymdhms dt;
638	int diff;
639	int century;
640	int s;
641
642	/*
643	 * We might have been called by boot() due to a crash early
644	 * on.  Don't reset the clock chip in this case.
645	 */
646	if (!timeset)
647		return;
648
649	s = splclock();
650	if (rtcget(&rtclk))
651		bzero(&rtclk, sizeof(rtclk));
652	splx(s);
653
654	diff = tz.tz_minuteswest * 60;
655	if (tz.tz_dsttime)
656		diff -= 3600;
657	clock_secs_to_ymdhms(time_second - diff, &dt);
658
659	rtclk[MC_SEC] = dectohexdec(dt.dt_sec);
660	rtclk[MC_MIN] = dectohexdec(dt.dt_min);
661	rtclk[MC_HOUR] = dectohexdec(dt.dt_hour);
662	rtclk[MC_DOW] = dt.dt_wday;
663	rtclk[MC_YEAR] = dectohexdec(dt.dt_year % 100);
664	rtclk[MC_MONTH] = dectohexdec(dt.dt_mon);
665	rtclk[MC_DOM] = dectohexdec(dt.dt_day);
666	s = splclock();
667	rtcput(&rtclk);
668	if (rtc_update_century > 0) {
669		century = dectohexdec(dt.dt_year / 100);
670		mc146818_write(NULL, NVRAM_CENTURY, century); /* XXX softc */
671	}
672	splx(s);
673}
674
675void
676setstatclockrate(int arg)
677{
678	if (arg == stathz)
679		mc146818_write(NULL, MC_REGA, MC_BASE_32_KHz | MC_RATE_128_Hz);
680	else
681		mc146818_write(NULL, MC_REGA, MC_BASE_32_KHz | MC_RATE_1024_Hz);
682}
683
684void
685i8254_inittimecounter(void)
686{
687	tc_init(&i8254_timecounter);
688}
689
690/*
691 * If we're using lapic to drive hardclock, we can use a simpler
692 * algorithm for the i8254 timecounters.
693 */
694void
695i8254_inittimecounter_simple(void)
696{
697	u_long tval = 0x8000;
698
699	i8254_timecounter.tc_get_timecount = i8254_simple_get_timecount;
700	i8254_timecounter.tc_counter_mask = 0x7fff;
701
702	i8254_timecounter.tc_frequency = TIMER_FREQ;
703
704	mtx_enter(&timer_mutex);
705	outb(TIMER_MODE, TIMER_SEL0 | TIMER_RATEGEN | TIMER_16BIT);
706	outb(IO_TIMER1, tval & 0xff);
707	outb(IO_TIMER1, tval >> 8);
708
709	rtclock_tval = tval;
710	mtx_leave(&timer_mutex);
711
712	tc_init(&i8254_timecounter);
713}
714
715u_int
716i8254_simple_get_timecount(struct timecounter *tc)
717{
718	return (rtclock_tval - gettick());
719}
720
721u_int
722i8254_get_timecount(struct timecounter *tc)
723{
724	u_char hi, lo;
725	u_int count;
726	u_long ef;
727
728	ef = read_eflags();
729	disable_intr();
730
731	outb(TIMER_MODE, TIMER_SEL0 | TIMER_LATCH);
732	lo = inb(TIMER_CNTR0);
733	hi = inb(TIMER_CNTR0);
734
735	count = rtclock_tval - ((hi << 8) | lo);
736
737	if (count < i8254_lastcount) {
738		i8254_ticked = 1;
739		i8254_offset += rtclock_tval;
740	}
741	i8254_lastcount = count;
742	count += i8254_offset;
743	write_eflags(ef);
744
745	return (count);
746}
747