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