xref: /openbsd-src/sys/arch/amd64/isa/clock.c (revision a28daedfc357b214be5c701aa8ba8adb29a7f1c2)
1 /*	$OpenBSD: clock.c,v 1.13 2007/08/02 16:40:27 deraadt Exp $	*/
2 /*	$NetBSD: clock.c,v 1.1 2003/04/26 18:39:50 fvdl Exp $	*/
3 
4 /*-
5  * Copyright (c) 1993, 1994 Charles M. 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 
68 Permission to use, copy, modify, and distribute this software and
69 its documentation for any purpose and without fee is hereby
70 granted, provided that the above copyright notice appears in all
71 copies and that both the copyright notice and this permission notice
72 appear in supporting documentation, and that the name of Intel
73 not be used in advertising or publicity pertaining to distribution
74 of the software without specific, written prior permission.
75 
76 INTEL DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE
77 INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS,
78 IN NO EVENT SHALL INTEL BE LIABLE FOR ANY SPECIAL, INDIRECT, OR
79 CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
80 LOSS OF USE, DATA OR PROFITS, WHETHER IN ACTION OF CONTRACT,
81 NEGLIGENCE, OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION
82 WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
83 */
84 
85 /*
86  * Primitive clock interrupt routines.
87  */
88 
89 /* #define CLOCKDEBUG */
90 /* #define CLOCK_PARANOIA */
91 
92 #include <sys/param.h>
93 #include <sys/systm.h>
94 #include <sys/time.h>
95 #include <sys/kernel.h>
96 #include <sys/device.h>
97 #include <sys/timeout.h>
98 #include <sys/timetc.h>
99 
100 #include <machine/cpu.h>
101 #include <machine/intr.h>
102 #include <machine/pio.h>
103 #include <machine/cpufunc.h>
104 
105 #include <dev/isa/isareg.h>
106 #include <dev/isa/isavar.h>
107 #include <dev/ic/mc146818reg.h>
108 #include <dev/ic/i8253reg.h>
109 #include <amd64/isa/nvram.h>
110 #include <dev/clock_subr.h>
111 #include <machine/specialreg.h>
112 
113 /* Timecounter on the i8254 */
114 u_int32_t i8254_lastcount;
115 u_int32_t i8254_offset;
116 int i8254_ticked;
117 u_int i8254_get_timecount(struct timecounter *tc);
118 
119 u_int i8254_simple_get_timecount(struct timecounter *tc);
120 
121 static struct timecounter i8254_timecounter = {
122 	i8254_get_timecount, NULL, ~0u, TIMER_FREQ, "i8254", 0, NULL
123 };
124 
125 int	clockintr(void *);
126 int	rtcintr(void *);
127 int	gettick(void);
128 void	rtcdrain(void *v);
129 int	rtcget(mc_todregs *);
130 void	rtcput(mc_todregs *);
131 int 	bcdtobin(int);
132 int	bintobcd(int);
133 
134 __inline u_int mc146818_read(void *, u_int);
135 __inline void mc146818_write(void *, u_int, u_int);
136 
137 __inline u_int
138 mc146818_read(void *sc, u_int reg)
139 {
140 	outb(IO_RTC, reg);
141 	DELAY(1);
142 	return (inb(IO_RTC+1));
143 }
144 
145 __inline void
146 mc146818_write(void *sc, u_int reg, u_int datum)
147 {
148 	outb(IO_RTC, reg);
149 	DELAY(1);
150 	outb(IO_RTC+1, datum);
151 	DELAY(1);
152 }
153 
154 struct mutex timer_mutex = MUTEX_INITIALIZER(IPL_HIGH);
155 
156 u_long rtclock_tval;
157 int rtclock_init;
158 
159 /* minimal initialization, enough for delay() */
160 void
161 initrtclock(void)
162 {
163 	u_long tval;
164 
165 	/*
166 	 * Compute timer_count, the count-down count the timer will be
167 	 * set to.  Also, correctly round
168 	 * this by carrying an extra bit through the division.
169 	 */
170 	tval = (TIMER_FREQ * 2) / (u_long) hz;
171 	tval = (tval / 2) + (tval & 0x1);
172 
173 	mtx_enter(&timer_mutex);
174 	/* initialize 8253 clock */
175 	outb(IO_TIMER1+TIMER_MODE, TIMER_SEL0|TIMER_RATEGEN|TIMER_16BIT);
176 
177 	/* Correct rounding will buy us a better precision in timekeeping */
178 	outb(IO_TIMER1+TIMER_CNTR0, tval % 256);
179 	outb(IO_TIMER1+TIMER_CNTR0, tval / 256);
180 
181 	rtclock_tval = tval;
182 	rtclock_init = 1;
183 	mtx_leave(&timer_mutex);
184 }
185 
186 void
187 startrtclock(void)
188 {
189 	int s;
190 
191 	if (!rtclock_init)
192 		initrtclock();
193 
194 	/* Check diagnostic status */
195 	if ((s = mc146818_read(NULL, NVRAM_DIAG)) != 0)	/* XXX softc */
196 		printf("RTC BIOS diagnostic error %b\n", s, NVRAM_DIAG_BITS);
197 }
198 
199 int
200 clockintr(void *arg)
201 {
202 	struct clockframe *frame = arg;
203 
204 	if (timecounter->tc_get_timecount == i8254_get_timecount) {
205 		if (i8254_ticked) {
206 			i8254_ticked = 0;
207 		} else {
208 			i8254_offset += rtclock_tval;
209 			i8254_lastcount = 0;
210 		}
211 	}
212 
213 	hardclock(frame);
214 
215 	return 1;
216 }
217 
218 int
219 rtcintr(void *arg)
220 {
221 	struct clockframe *frame = arg;
222 	u_int stat = 0;
223 
224 	/*
225 	* If rtcintr is 'late', next intr may happen immediately.
226 	* Get them all. (Also, see comment in cpu_initclocks().)
227 	*/
228 	while (mc146818_read(NULL, MC_REGC) & MC_REGC_PF) {
229 		statclock(frame);
230 		stat = 1;
231 	}
232 
233 	return (stat);
234 }
235 
236 int
237 gettick(void)
238 {
239 	u_long ef;
240 	u_char lo, hi;
241 
242 	/* Don't want someone screwing with the counter while we're here. */
243 	mtx_enter(&timer_mutex);
244 	ef = read_rflags();
245 	disable_intr();
246 	/* Select counter 0 and latch it. */
247 	outb(IO_TIMER1+TIMER_MODE, TIMER_SEL0 | TIMER_LATCH);
248 	lo = inb(IO_TIMER1+TIMER_CNTR0);
249 	hi = inb(IO_TIMER1+TIMER_CNTR0);
250 	write_rflags(ef);
251 	mtx_leave(&timer_mutex);
252 	return ((hi << 8) | lo);
253 }
254 
255 /*
256  * Wait "n" microseconds.
257  * Relies on timer 1 counting down from (TIMER_FREQ / hz) at TIMER_FREQ Hz.
258  * Note: timer had better have been programmed before this is first used!
259  * (Note that we use `rate generator' mode, which counts at 1:1; `square
260  * wave' mode counts at 2:1).
261  */
262 void
263 i8254_delay(int n)
264 {
265 	int limit, tick, otick;
266 	static const int delaytab[26] = {
267 		 0,  2,  3,  4,  5,  6,  7,  9, 10, 11,
268 		12, 13, 15, 16, 17, 18, 19, 21, 22, 23,
269 		24, 25, 27, 28, 29, 30,
270 	};
271 
272 	/* allow DELAY() to be used before startrtclock() */
273 	if (!rtclock_init)
274 		initrtclock();
275 
276 	/*
277 	 * Read the counter first, so that the rest of the setup overhead is
278 	 * counted.
279 	 */
280 	otick = gettick();
281 
282 	if (n <= 25)
283 		n = delaytab[n];
284 	else {
285 #ifdef __GNUC__
286 		/*
287 		 * Calculate ((n * TIMER_FREQ) / 1e6) using explicit assembler
288 		 * code so we can take advantage of the intermediate 64-bit
289 		 * quantity to prevent loss of significance.
290 		 */
291 		int m;
292 		__asm __volatile("mul %3"
293 				 : "=a" (n), "=d" (m)
294 				 : "0" (n), "r" (TIMER_FREQ));
295 		__asm __volatile("div %4"
296 				 : "=a" (n), "=d" (m)
297 				 : "0" (n), "1" (m), "r" (1000000));
298 #else
299 		/*
300 		 * Calculate ((n * TIMER_FREQ) / 1e6) without using floating
301 		 * point and without any avoidable overflows.
302 		 */
303 		int sec = n / 1000000,
304 		    usec = n % 1000000;
305 		n = sec * TIMER_FREQ +
306 		    usec * (TIMER_FREQ / 1000000) +
307 		    usec * ((TIMER_FREQ % 1000000) / 1000) / 1000 +
308 		    usec * (TIMER_FREQ % 1000) / 1000000;
309 #endif
310 	}
311 
312 	limit = TIMER_FREQ / hz;
313 
314 	while (n > 0) {
315 		tick = gettick();
316 		if (tick > otick)
317 			n -= limit - (tick - otick);
318 		else
319 			n -= otick - tick;
320 		otick = tick;
321 	}
322 }
323 
324 void
325 rtcdrain(void *v)
326 {
327 	struct timeout *to = (struct timeout *)v;
328 
329 	if (to != NULL)
330 		timeout_del(to);
331 
332 	/*
333 	* Drain any un-acknowledged RTC interrupts.
334 	* See comment in cpu_initclocks().
335 	*/
336 	while (mc146818_read(NULL, MC_REGC) & MC_REGC_PF)
337 		; /* Nothing. */
338 }
339 
340 void
341 i8254_initclocks(void)
342 {
343 	static struct timeout rtcdrain_timeout;
344 
345 	stathz = 128;
346 	profhz = 1024;
347 
348 	isa_intr_establish(NULL, 0, IST_PULSE, IPL_CLOCK, clockintr,
349 	    0, "clock");
350 	isa_intr_establish(NULL, 8, IST_PULSE, IPL_CLOCK, rtcintr, 0, "rtc");
351 
352 	mc146818_write(NULL, MC_REGA, MC_BASE_32_KHz | MC_RATE_128_Hz);
353 	mc146818_write(NULL, MC_REGB, MC_REGB_24HR | MC_REGB_PIE);
354 
355 	/*
356 	 * On a number of i386 systems, the rtc will fail to start when booting
357 	 * the system. This is due to us missing to acknowledge an interrupt
358 	 * during early stages of the boot process. If we do not acknowledge
359 	 * the interrupt, the rtc clock will not generate further interrupts.
360 	 * To solve this, once interrupts are enabled, use a timeout (once)
361 	 * to drain any un-acknowledged rtc interrupt(s).
362 	 */
363 	timeout_set(&rtcdrain_timeout, rtcdrain, (void *)&rtcdrain_timeout);
364 	timeout_add(&rtcdrain_timeout, 1);
365 }
366 
367 int
368 rtcget(mc_todregs *regs)
369 {
370 	if ((mc146818_read(NULL, MC_REGD) & MC_REGD_VRT) == 0) /* XXX softc */
371 		return (-1);
372 	MC146818_GETTOD(NULL, regs);			/* XXX softc */
373 	return (0);
374 }
375 
376 void
377 rtcput(mc_todregs *regs)
378 {
379 	MC146818_PUTTOD(NULL, regs);			/* XXX softc */
380 }
381 
382 int
383 bcdtobin(int n)
384 {
385 	return (((n >> 4) & 0x0f) * 10 + (n & 0x0f));
386 }
387 
388 int
389 bintobcd(int n)
390 {
391 	return ((u_char)(((n / 10) << 4) & 0xf0) | ((n % 10) & 0x0f));
392 }
393 
394 static int timeset;
395 
396 /*
397  * check whether the CMOS layout is "standard"-like (ie, not PS/2-like),
398  * to be called at splclock()
399  */
400 static int cmoscheck(void);
401 static int
402 cmoscheck(void)
403 {
404 	int i;
405 	unsigned short cksum = 0;
406 
407 	for (i = 0x10; i <= 0x2d; i++)
408 		cksum += mc146818_read(NULL, i); /* XXX softc */
409 
410 	return (cksum == (mc146818_read(NULL, 0x2e) << 8)
411 			  + mc146818_read(NULL, 0x2f));
412 }
413 
414 /*
415  * patchable to control century byte handling:
416  * 1: always update
417  * -1: never touch
418  * 0: try to figure out itself
419  */
420 int rtc_update_century = 0;
421 
422 /*
423  * Expand a two-digit year as read from the clock chip
424  * into full width.
425  * Being here, deal with the CMOS century byte.
426  */
427 static int centb = NVRAM_CENTURY;
428 static int clock_expandyear(int);
429 static int
430 clock_expandyear(int clockyear)
431 {
432 	int s, clockcentury, cmoscentury;
433 
434 	clockcentury = (clockyear < 70) ? 20 : 19;
435 	clockyear += 100 * clockcentury;
436 
437 	if (rtc_update_century < 0)
438 		return (clockyear);
439 
440 	s = splclock();
441 	if (cmoscheck())
442 		cmoscentury = mc146818_read(NULL, NVRAM_CENTURY);
443 	else
444 		cmoscentury = 0;
445 	splx(s);
446 	if (!cmoscentury) {
447 #ifdef DIAGNOSTIC
448 		printf("clock: unknown CMOS layout\n");
449 #endif
450 		return (clockyear);
451 	}
452 	cmoscentury = bcdtobin(cmoscentury);
453 
454 	if (cmoscentury != clockcentury) {
455 		/* XXX note: saying "century is 20" might confuse the naive. */
456 		printf("WARNING: NVRAM century is %d but RTC year is %d\n",
457 		       cmoscentury, clockyear);
458 
459 		/* Kludge to roll over century. */
460 		if ((rtc_update_century > 0) ||
461 		    ((cmoscentury == 19) && (clockcentury == 20) &&
462 		     (clockyear == 2000))) {
463 			printf("WARNING: Setting NVRAM century to %d\n",
464 			       clockcentury);
465 			s = splclock();
466 			mc146818_write(NULL, centb, bintobcd(clockcentury));
467 			splx(s);
468 		}
469 	} else if (cmoscentury == 19 && rtc_update_century == 0)
470 		rtc_update_century = 1; /* will update later in resettodr() */
471 
472 	return (clockyear);
473 }
474 
475 /*
476  * Initialize the time of day register, based on the time base which is, e.g.
477  * from a filesystem.
478  */
479 void
480 inittodr(time_t base)
481 {
482 	struct timespec ts;
483 	mc_todregs rtclk;
484 	struct clock_ymdhms dt;
485 	int s;
486 
487 	ts.tv_nsec = 0;
488 
489 	/*
490 	 * We mostly ignore the suggested time (which comes from the
491 	 * file system) and go for the RTC clock time stored in the
492 	 * CMOS RAM.  If the time can't be obtained from the CMOS, or
493 	 * if the time obtained from the CMOS is 5 or more years less
494 	 * than the suggested time, we used the suggested time.  (In
495 	 * the latter case, it's likely that the CMOS battery has
496 	 * died.)
497 	 */
498 
499 	/*
500 	 * if the file system time is more than a year older than the
501 	 * kernel, warn and then set the base time to the CONFIG_TIME.
502 	 */
503 	if (base < 30*SECYR) {	/* if before 2000, something's odd... */
504 		printf("WARNING: preposterous time in file system\n");
505 		base = 30*SECYR;
506 	}
507 
508 	s = splclock();
509 	if (rtcget(&rtclk)) {
510 		splx(s);
511 		printf("WARNING: invalid time in clock chip\n");
512 		goto fstime;
513 	}
514 	splx(s);
515 #ifdef DEBUG_CLOCK
516 	printf("readclock: %x/%x/%x %x:%x:%x\n", rtclk[MC_YEAR],
517 	    rtclk[MC_MONTH], rtclk[MC_DOM], rtclk[MC_HOUR], rtclk[MC_MIN],
518 	    rtclk[MC_SEC]);
519 #endif
520 
521 	dt.dt_sec = bcdtobin(rtclk[MC_SEC]);
522 	dt.dt_min = bcdtobin(rtclk[MC_MIN]);
523 	dt.dt_hour = bcdtobin(rtclk[MC_HOUR]);
524 	dt.dt_day = bcdtobin(rtclk[MC_DOM]);
525 	dt.dt_mon = bcdtobin(rtclk[MC_MONTH]);
526 	dt.dt_year = clock_expandyear(bcdtobin(rtclk[MC_YEAR]));
527 
528 	/*
529 	 * If time_t is 32 bits, then the "End of Time" is
530 	 * Mon Jan 18 22:14:07 2038 (US/Eastern)
531 	 * This code copes with RTC's past the end of time if time_t
532 	 * is an int32 or less. Needed because sometimes RTCs screw
533 	 * up or are badly set, and that would cause the time to go
534 	 * negative in the calculation below, which causes Very Bad
535 	 * Mojo. This at least lets the user boot and fix the problem.
536 	 * Note the code is self eliminating once time_t goes to 64 bits.
537 	 */
538 	if (sizeof(time_t) <= sizeof(int32_t)) {
539 		if (dt.dt_year >= 2038) {
540 			printf("WARNING: RTC time at or beyond 2038.\n");
541 			dt.dt_year = 2037;
542 			printf("WARNING: year set back to 2037.\n");
543 			printf("WARNING: CHECK AND RESET THE DATE!\n");
544 		}
545 	}
546 
547 	ts.tv_sec = clock_ymdhms_to_secs(&dt) + tz.tz_minuteswest * 60;
548 	if (tz.tz_dsttime)
549 		ts.tv_sec -= 3600;
550 
551 	if (base != 0 && base < ts.tv_sec - 5*SECYR)
552 		printf("WARNING: file system time much less than clock time\n");
553 	else if (base > ts.tv_sec + 5*SECYR) {
554 		printf("WARNING: clock time much less than file system time\n");
555 		printf("WARNING: using file system time\n");
556 		goto fstime;
557 	}
558 
559 	tc_setclock(&ts);
560 	timeset = 1;
561 	return;
562 
563 fstime:
564 	ts.tv_sec = base;
565 	tc_setclock(&ts);
566 	timeset = 1;
567 	printf("WARNING: CHECK AND RESET THE DATE!\n");
568 }
569 
570 /*
571  * Reset the clock.
572  */
573 void
574 resettodr(void)
575 {
576 	mc_todregs rtclk;
577 	struct clock_ymdhms dt;
578 	int century, diff, s;
579 
580 	/*
581 	 * We might have been called by boot() due to a crash early
582 	 * on.  Don't reset the clock chip in this case.
583 	 */
584 	if (!timeset)
585 		return;
586 
587 	s = splclock();
588 	if (rtcget(&rtclk))
589 		memset(&rtclk, 0, sizeof(rtclk));
590 	splx(s);
591 
592 	diff = tz.tz_minuteswest * 60;
593 	if (tz.tz_dsttime)
594 		diff -= 3600;
595 	clock_secs_to_ymdhms(time_second - diff, &dt);
596 
597 	rtclk[MC_SEC] = bintobcd(dt.dt_sec);
598 	rtclk[MC_MIN] = bintobcd(dt.dt_min);
599 	rtclk[MC_HOUR] = bintobcd(dt.dt_hour);
600 	rtclk[MC_DOW] = dt.dt_wday + 1;
601 	rtclk[MC_YEAR] = bintobcd(dt.dt_year % 100);
602 	rtclk[MC_MONTH] = bintobcd(dt.dt_mon);
603 	rtclk[MC_DOM] = bintobcd(dt.dt_day);
604 
605 #ifdef DEBUG_CLOCK
606 	printf("setclock: %x/%x/%x %x:%x:%x\n", rtclk[MC_YEAR], rtclk[MC_MONTH],
607 	   rtclk[MC_DOM], rtclk[MC_HOUR], rtclk[MC_MIN], rtclk[MC_SEC]);
608 #endif
609 	s = splclock();
610 	rtcput(&rtclk);
611 	if (rtc_update_century > 0) {
612 		century = bintobcd(dt.dt_year / 100);
613 		mc146818_write(NULL, centb, century); /* XXX softc */
614 	}
615 	splx(s);
616 }
617 
618 void
619 setstatclockrate(int arg)
620 {
621 	if (arg == stathz)
622 		mc146818_write(NULL, MC_REGA, MC_BASE_32_KHz | MC_RATE_128_Hz);
623 	else
624 		mc146818_write(NULL, MC_REGA, MC_BASE_32_KHz | MC_RATE_1024_Hz);
625 }
626 
627 void
628 i8254_inittimecounter(void)
629 {
630 	tc_init(&i8254_timecounter);
631 }
632 
633 /*
634  * If we're using lapic to drive hardclock, we can use a simpler
635  * algorithm for the i8254 timecounters.
636  */
637 void
638 i8254_inittimecounter_simple(void)
639 {
640 	u_long tval = 0x8000;
641 
642 	i8254_timecounter.tc_get_timecount = i8254_simple_get_timecount;
643 	i8254_timecounter.tc_counter_mask = 0x7fff;
644 
645         i8254_timecounter.tc_frequency = TIMER_FREQ;
646 
647 	mtx_enter(&timer_mutex);
648 	outb(IO_TIMER1 + TIMER_MODE, TIMER_SEL0 | TIMER_RATEGEN | TIMER_16BIT);
649 	outb(IO_TIMER1 + TIMER_CNTR0, tval & 0xff);
650 	outb(IO_TIMER1 + TIMER_CNTR0, tval >> 8);
651 
652 	rtclock_tval = tval;
653 	rtclock_init = 1;
654 	mtx_leave(&timer_mutex);
655 
656 	tc_init(&i8254_timecounter);
657 }
658 
659 u_int
660 i8254_simple_get_timecount(struct timecounter *tc)
661 {
662 	return (rtclock_tval - gettick());
663 }
664 
665 u_int
666 i8254_get_timecount(struct timecounter *tc)
667 {
668 	u_char hi, lo;
669 	u_int count;
670 	u_long ef;
671 
672 	ef = read_rflags();
673 	disable_intr();
674 
675 	outb(IO_TIMER1+TIMER_MODE, TIMER_SEL0 | TIMER_LATCH);
676 	lo = inb(IO_TIMER1+TIMER_CNTR0);
677 	hi = inb(IO_TIMER1+TIMER_CNTR0);
678 
679 	count = rtclock_tval - ((hi << 8) | lo);
680 
681 	if (count < i8254_lastcount) {
682 		i8254_ticked = 1;
683 		i8254_offset += rtclock_tval;
684 	}
685 	i8254_lastcount = count;
686 	count += i8254_offset;
687 	write_rflags(ef);
688 
689 	return (count);
690 }
691