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