xref: /openbsd/sys/arch/i386/isa/clock.c (revision 17df1aa7)
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 
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 #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 
109 void	spinwait(int);
110 int	clockintr(void *);
111 int	gettick(void);
112 int	rtcget(mc_todregs *);
113 void	rtcput(mc_todregs *);
114 int 	hexdectodec(int);
115 int	dectohexdec(int);
116 int	rtcintr(void *);
117 void	rtcdrain(void *);
118 
119 u_int mc146818_read(void *, u_int);
120 void mc146818_write(void *, u_int, u_int);
121 
122 int cpuspeed;
123 int clock_broken_latch;
124 
125 /* Timecounter on the i8254 */
126 uint32_t i8254_lastcount;
127 uint32_t i8254_offset;
128 int i8254_ticked;
129 u_int i8254_get_timecount(struct timecounter *tc);
130 u_int i8254_simple_get_timecount(struct timecounter *tc);
131 
132 static struct timecounter i8254_timecounter = {
133 	i8254_get_timecount, NULL, ~0u, TIMER_FREQ, "i8254", 0, NULL
134 };
135 struct mutex timer_mutex = MUTEX_INITIALIZER(IPL_HIGH);
136 u_long rtclock_tval;
137 
138 #define	SECMIN	((unsigned)60)			/* seconds per minute */
139 #define	SECHOUR	((unsigned)(60*SECMIN))		/* seconds per hour */
140 
141 u_int
142 mc146818_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 
156 void
157 mc146818_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 
169 void
170 startrtclock(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 
182 void
183 rtcdrain(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 
198 void
199 initrtclock(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 
214 int
215 clockintr(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 
232 int
233 rtcintr(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 
257 int
258 gettick(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  */
337 void
338 i8254_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 
389 void
390 calibrate_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 
400 void
401 i8254_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 
430 int
431 rtcget(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 
439 void
440 rtcput(mc_todregs *regs)
441 {
442 	MC146818_PUTTOD(NULL, regs);			/* XXX softc */
443 }
444 
445 int
446 hexdectodec(int n)
447 {
448 
449 	return (((n >> 4) & 0x0f) * 10 + (n & 0x0f));
450 }
451 
452 int
453 dectohexdec(int n)
454 {
455 
456 	return ((u_char)(((n / 10) << 4) & 0xf0) | ((n % 10) & 0x0f));
457 }
458 
459 static 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  */
465 int cmoscheck(void);
466 int
467 cmoscheck(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  */
485 int 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  */
492 int clock_expandyear(int);
493 int
494 clock_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  */
544 void
545 inittodr(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 
620 fstime:
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  */
630 void
631 resettodr(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 
672 void
673 setstatclockrate(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 
681 void
682 i8254_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  */
691 void
692 i8254_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 
712 u_int
713 i8254_simple_get_timecount(struct timecounter *tc)
714 {
715 	return (rtclock_tval - gettick());
716 }
717 
718 u_int
719 i8254_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