xref: /openbsd/sys/arch/i386/isa/clock.c (revision 905646f0)
1 /*	$OpenBSD: clock.c,v 1.59 2020/07/06 13:33:07 pirofti 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/param.h>
89 #include <sys/systm.h>
90 #include <sys/time.h>
91 #include <sys/kernel.h>
92 #include <sys/device.h>
93 #include <sys/timeout.h>
94 #include <sys/timetc.h>
95 #include <sys/mutex.h>
96 
97 #include <machine/cpu.h>
98 #include <machine/intr.h>
99 #include <machine/pio.h>
100 #include <machine/cpufunc.h>
101 
102 #include <dev/clock_subr.h>
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	rtcintr(void *);
115 void	rtcdrain(void *);
116 int	calibrate_cyclecounter_ctr(void);
117 
118 u_int mc146818_read(void *, u_int);
119 void mc146818_write(void *, u_int, u_int);
120 
121 int cpuspeed;
122 int clock_broken_latch;
123 
124 /* Timecounter on the i8254 */
125 uint32_t i8254_lastcount;
126 uint32_t i8254_offset;
127 int i8254_ticked;
128 u_int i8254_get_timecount(struct timecounter *tc);
129 u_int i8254_simple_get_timecount(struct timecounter *tc);
130 
131 static struct timecounter i8254_timecounter = {
132 	i8254_get_timecount, NULL, ~0u, TIMER_FREQ, "i8254", 0, NULL, 0
133 };
134 struct mutex timer_mutex = MUTEX_INITIALIZER(IPL_HIGH);
135 u_long rtclock_tval;
136 
137 #define	SECMIN	((unsigned)60)			/* seconds per minute */
138 #define	SECHOUR	((unsigned)(60*SECMIN))		/* seconds per hour */
139 
140 u_int
141 mc146818_read(void *sc, u_int reg)
142 {
143 	int s;
144 	u_char v;
145 
146 	s = splhigh();
147 	outb(IO_RTC, reg);
148 	DELAY(1);
149 	v = inb(IO_RTC+1);
150 	DELAY(1);
151 	splx(s);
152 	return (v);
153 }
154 
155 void
156 mc146818_write(void *sc, u_int reg, u_int datum)
157 {
158 	int s;
159 
160 	s = splhigh();
161 	outb(IO_RTC, reg);
162 	DELAY(1);
163 	outb(IO_RTC+1, datum);
164 	DELAY(1);
165 	splx(s);
166 }
167 
168 void
169 startclocks(void)
170 {
171 	int s;
172 
173 	mtx_enter(&timer_mutex);
174 	rtclock_tval = TIMER_DIV(hz);
175 	i8254_startclock();
176 	mtx_leave(&timer_mutex);
177 
178 	/* Check diagnostic status */
179 	if ((s = mc146818_read(NULL, NVRAM_DIAG)) != 0)	/* XXX softc */
180 		printf("RTC BIOS diagnostic error %b\n", (unsigned int) s,
181 		    NVRAM_DIAG_BITS);
182 }
183 
184 void
185 rtcdrain(void *v)
186 {
187 	struct timeout *to = (struct timeout *)v;
188 
189 	if (to != NULL)
190 		timeout_del(to);
191 
192 	/* Drain any un-acknowledged RTC interrupts. */
193 	while (mc146818_read(NULL, MC_REGC) & MC_REGC_PF)
194 		; /* Nothing. */
195 }
196 
197 int
198 clockintr(void *arg)
199 {
200 	struct clockframe *frame = arg;		/* not strictly necessary */
201 
202 	if (timecounter->tc_get_timecount == i8254_get_timecount) {
203 		if (i8254_ticked) {
204 			i8254_ticked = 0;
205 		} else {
206 			i8254_offset += rtclock_tval;
207 			i8254_lastcount = 0;
208 		}
209 	}
210 
211 	hardclock(frame);
212 	return (1);
213 }
214 
215 int
216 rtcintr(void *arg)
217 {
218 	struct clockframe *frame = arg;		/* not strictly necessary */
219 	u_int stat = 0;
220 
221 	if (stathz == 0) {
222 		extern int psratio;
223 
224 		stathz = 128;
225 		profhz = 1024;
226 		psratio = profhz / stathz;
227 	}
228 
229 	/*
230 	 * If rtcintr is 'late', next intr may happen immediately.
231 	 * Get them all. (Also, see comment in cpu_initclocks().)
232 	 */
233 	while (mc146818_read(NULL, MC_REGC) & MC_REGC_PF) {
234 		statclock(frame);
235 		stat = 1;
236 	}
237 	return (stat);
238 }
239 
240 int
241 gettick(void)
242 {
243 	u_long s;
244 
245 	if (clock_broken_latch) {
246 		int v1, v2, v3;
247 		int w1, w2, w3;
248 
249 		/*
250 		 * Don't lock the mutex in this case, clock_broken_latch
251 		 * CPUs don't do MP anyway.
252 		 */
253 
254 		s = intr_disable();
255 
256 		v1 = inb(IO_TIMER1 + TIMER_CNTR0);
257 		v1 |= inb(IO_TIMER1 + TIMER_CNTR0) << 8;
258 		v2 = inb(IO_TIMER1 + TIMER_CNTR0);
259 		v2 |= inb(IO_TIMER1 + TIMER_CNTR0) << 8;
260 		v3 = inb(IO_TIMER1 + TIMER_CNTR0);
261 		v3 |= inb(IO_TIMER1 + TIMER_CNTR0) << 8;
262 
263 		intr_restore(s);
264 
265 		if (v1 >= v2 && v2 >= v3 && v1 - v3 < 0x200)
266 			return (v2);
267 
268 #define _swap_val(a, b) do { \
269 	int c = a; \
270 	a = b; \
271 	b = c; \
272 } while (0)
273 
274 		/* sort v1 v2 v3 */
275 		if (v1 < v2)
276 			_swap_val(v1, v2);
277 		if (v2 < v3)
278 			_swap_val(v2, v3);
279 		if (v1 < v2)
280 			_swap_val(v1, v2);
281 
282 		/* compute the middle value */
283 		if (v1 - v3 < 0x200)
284 			return (v2);
285 		w1 = v2 - v3;
286 		w2 = v3 - v1 + TIMER_DIV(hz);
287 		w3 = v1 - v2;
288 		if (w1 >= w2) {
289 			if (w1 >= w3)
290 				return (v1);
291 		} else {
292 			if (w2 >= w3)
293 				return (v2);
294 		}
295 		return (v3);
296 	} else {
297 		u_char lo, hi;
298 
299 		mtx_enter(&timer_mutex);
300 		s = intr_disable();
301 		/* Select counter 0 and latch it. */
302 		outb(IO_TIMER1 + TIMER_MODE, TIMER_SEL0 | TIMER_LATCH);
303 		lo = inb(IO_TIMER1 + TIMER_CNTR0);
304 		hi = inb(IO_TIMER1 + TIMER_CNTR0);
305 
306 		intr_restore(s);
307 		mtx_leave(&timer_mutex);
308 		return ((hi << 8) | lo);
309 	}
310 }
311 
312 /*
313  * Wait "n" microseconds.
314  * Relies on timer 1 counting down from (TIMER_FREQ / hz) at TIMER_FREQ Hz.
315  * Note: timer had better have been programmed before this is first used!
316  * (Note that we use `rate generator' mode, which counts at 1:1; `square
317  * wave' mode counts at 2:1).
318  */
319 void
320 i8254_delay(int n)
321 {
322 	int limit, tick, otick;
323 
324 	/*
325 	 * Read the counter first, so that the rest of the setup overhead is
326 	 * counted.
327 	 */
328 	otick = gettick();
329 
330 #ifdef __GNUC__
331 	/*
332 	 * Calculate ((n * TIMER_FREQ) / 1e6) using explicit assembler code so
333 	 * we can take advantage of the intermediate 64-bit quantity to prevent
334 	 * loss of significance.
335 	 */
336 	n -= 5;
337 	if (n < 0)
338 		return;
339 	__asm volatile("mul %2\n\tdiv %3"
340 			 : "=a" (n)
341 			 : "0" (n), "r" (TIMER_FREQ), "r" (1000000)
342 			 : "%edx", "cc");
343 #else
344 	/*
345 	 * Calculate ((n * TIMER_FREQ) / 1e6) without using floating point and
346 	 * without any avoidable overflows.
347 	 */
348 	n -= 20;
349 	{
350 		int sec = n / 1000000,
351 		    usec = n % 1000000;
352 		n = sec * TIMER_FREQ +
353 		    usec * (TIMER_FREQ / 1000000) +
354 		    usec * ((TIMER_FREQ % 1000000) / 1000) / 1000 +
355 		    usec * (TIMER_FREQ % 1000) / 1000000;
356 	}
357 #endif
358 
359 	limit = TIMER_FREQ / hz;
360 
361 	while (n > 0) {
362 		tick = gettick();
363 		if (tick > otick)
364 			n -= limit - (tick - otick);
365 		else
366 			n -= otick - tick;
367 		otick = tick;
368 	}
369 }
370 
371 int
372 calibrate_cyclecounter_ctr(void)
373 {
374 	struct cpu_info *ci = curcpu();
375 	unsigned long long count, last_count, msr;
376 
377 	if ((ci->ci_flags & CPUF_CONST_TSC) == 0 ||
378 	    (cpu_perf_eax & CPUIDEAX_VERID) <= 1 ||
379 	    CPUIDEDX_NUM_FC(cpu_perf_edx) <= 1)
380 		return (-1);
381 
382 	msr = rdmsr(MSR_PERF_FIXED_CTR_CTRL);
383 	if (msr & MSR_PERF_FIXED_CTR_FC(1, MSR_PERF_FIXED_CTR_FC_MASK)) {
384 		/* some hypervisor is dicking us around */
385 		return (-1);
386 	}
387 
388 	msr |= MSR_PERF_FIXED_CTR_FC(1, MSR_PERF_FIXED_CTR_FC_1);
389 	wrmsr(MSR_PERF_FIXED_CTR_CTRL, msr);
390 
391 	msr = rdmsr(MSR_PERF_GLOBAL_CTRL) | MSR_PERF_GLOBAL_CTR1_EN;
392 	wrmsr(MSR_PERF_GLOBAL_CTRL, msr);
393 
394 	last_count = rdmsr(MSR_PERF_FIXED_CTR1);
395 	delay(1000000);
396 	count = rdmsr(MSR_PERF_FIXED_CTR1);
397 
398 	msr = rdmsr(MSR_PERF_FIXED_CTR_CTRL);
399 	msr &= MSR_PERF_FIXED_CTR_FC(1, MSR_PERF_FIXED_CTR_FC_MASK);
400 	wrmsr(MSR_PERF_FIXED_CTR_CTRL, msr);
401 
402 	msr = rdmsr(MSR_PERF_GLOBAL_CTRL);
403 	msr &= ~MSR_PERF_GLOBAL_CTR1_EN;
404 	wrmsr(MSR_PERF_GLOBAL_CTRL, msr);
405 
406 	cpuspeed = ((count - last_count) + 999999) / 1000000;
407 
408 	return (cpuspeed == 0 ? -1 : 0);
409 }
410 
411 void
412 calibrate_cyclecounter(void)
413 {
414 	unsigned long long count, last_count;
415 
416 	if (calibrate_cyclecounter_ctr() == 0)
417 		return;
418 
419 	__asm volatile("rdtsc" : "=A" (last_count));
420 	delay(1000000);
421 	__asm volatile("rdtsc" : "=A" (count));
422 
423 	cpuspeed = ((count - last_count) + 999999) / 1000000;
424 }
425 
426 void
427 i8254_initclocks(void)
428 {
429 	/* When using i8254 for clock, we also use the rtc for profclock */
430 	(void)isa_intr_establish(NULL, 0, IST_PULSE, IPL_CLOCK,
431 	    clockintr, 0, "clock");
432 	(void)isa_intr_establish(NULL, 8, IST_PULSE, IPL_STATCLOCK,
433 	    rtcintr, 0, "rtc");
434 
435 	rtcstart();			/* start the mc146818 clock */
436 
437 	i8254_inittimecounter();	/* hook the interrupt-based i8254 tc */
438 }
439 
440 void
441 rtcstart(void)
442 {
443 	static struct timeout rtcdrain_timeout;
444 
445 	mc146818_write(NULL, MC_REGA, MC_BASE_32_KHz | MC_RATE_128_Hz);
446 	mc146818_write(NULL, MC_REGB, MC_REGB_24HR | MC_REGB_PIE);
447 
448 	/*
449 	 * On a number of i386 systems, the rtc will fail to start when booting
450 	 * the system. This is due to us missing to acknowledge an interrupt
451 	 * during early stages of the boot process. If we do not acknowledge
452 	 * the interrupt, the rtc clock will not generate further interrupts.
453 	 * To solve this, once interrupts are enabled, use a timeout (once)
454 	 * to drain any un-acknowledged rtc interrupt(s).
455 	 */
456 
457 	timeout_set(&rtcdrain_timeout, rtcdrain, (void *)&rtcdrain_timeout);
458 	timeout_add(&rtcdrain_timeout, 1);
459 }
460 
461 void
462 rtcstop(void)
463 {
464 	mc146818_write(NULL, MC_REGB, MC_REGB_24HR);
465 }
466 
467 int
468 rtcget(mc_todregs *regs)
469 {
470 	if ((mc146818_read(NULL, MC_REGD) & MC_REGD_VRT) == 0) /* XXX softc */
471 		return (-1);
472 	MC146818_GETTOD(NULL, regs);			/* XXX softc */
473 	return (0);
474 }
475 
476 void
477 rtcput(mc_todregs *regs)
478 {
479 	MC146818_PUTTOD(NULL, regs);			/* XXX softc */
480 }
481 
482 int
483 bcdtobin(int n)
484 {
485 	return (((n >> 4) & 0x0f) * 10 + (n & 0x0f));
486 }
487 
488 int
489 bintobcd(int n)
490 {
491 	return ((u_char)(((n / 10) << 4) & 0xf0) | ((n % 10) & 0x0f));
492 }
493 
494 /*
495  * check whether the CMOS layout is "standard"-like (ie, not PS/2-like),
496  * to be called at splclock()
497  */
498 int cmoscheck(void);
499 int
500 cmoscheck(void)
501 {
502 	int i;
503 	unsigned short cksum = 0;
504 
505 	for (i = 0x10; i <= 0x2d; i++)
506 		cksum += mc146818_read(NULL, i); /* XXX softc */
507 
508 	return (cksum == (mc146818_read(NULL, 0x2e) << 8)
509 			  + mc146818_read(NULL, 0x2f));
510 }
511 
512 /*
513  * patchable to control century byte handling:
514  * 1: always update
515  * -1: never touch
516  * 0: try to figure out itself
517  */
518 int rtc_update_century = 0;
519 
520 /*
521  * Expand a two-digit year as read from the clock chip
522  * into full width.
523  * Being here, deal with the CMOS century byte.
524  */
525 int clock_expandyear(int);
526 int
527 clock_expandyear(int clockyear)
528 {
529 	int s, clockcentury, cmoscentury;
530 
531 	clockcentury = (clockyear < 70) ? 20 : 19;
532 	clockyear += 100 * clockcentury;
533 
534 	if (rtc_update_century < 0)
535 		return (clockyear);
536 
537 	s = splclock();
538 	if (cmoscheck())
539 		cmoscentury = mc146818_read(NULL, NVRAM_CENTURY);
540 	else
541 		cmoscentury = 0;
542 	splx(s);
543 	if (!cmoscentury) {
544 #ifdef DIAGNOSTIC
545 		printf("clock: unknown CMOS layout\n");
546 #endif
547 		return (clockyear);
548 	}
549 	cmoscentury = bcdtobin(cmoscentury);
550 
551 	if (cmoscentury != clockcentury) {
552 		/* XXX note: saying "century is 20" might confuse the naive. */
553 		printf("WARNING: NVRAM century is %d but RTC year is %d\n",
554 		       cmoscentury, clockyear);
555 
556 		/* Kludge to roll over century. */
557 		if ((rtc_update_century > 0) ||
558 		    ((cmoscentury == 19) && (clockcentury == 20) &&
559 		     (clockyear == 2000))) {
560 			printf("WARNING: Setting NVRAM century to %d\n",
561 			       clockcentury);
562 			s = splclock();
563 			mc146818_write(NULL, NVRAM_CENTURY,
564 				       bintobcd(clockcentury));
565 			splx(s);
566 		}
567 	} else if (cmoscentury == 19 && rtc_update_century == 0)
568 		rtc_update_century = 1; /* will update later in resettodr() */
569 
570 	return (clockyear);
571 }
572 
573 int
574 rtcgettime(struct todr_chip_handle *handle, struct timeval *tv)
575 {
576 	mc_todregs rtclk;
577 	struct clock_ymdhms dt;
578 	int s;
579 
580 	s = splclock();
581 	if (rtcget(&rtclk)) {
582 		splx(s);
583 		return EINVAL;
584 	}
585 	splx(s);
586 
587 #ifdef CLOCK_DEBUG
588 	printf("readclock: %x/%x/%x %x:%x:%x\n", rtclk[MC_YEAR],
589 	    rtclk[MC_MONTH], rtclk[MC_DOM], rtclk[MC_HOUR], rtclk[MC_MIN],
590 	    rtclk[MC_SEC]);
591 #endif
592 
593 	dt.dt_sec = bcdtobin(rtclk[MC_SEC]);
594 	dt.dt_min = bcdtobin(rtclk[MC_MIN]);
595 	dt.dt_hour = bcdtobin(rtclk[MC_HOUR]);
596 	dt.dt_day = bcdtobin(rtclk[MC_DOM]);
597 	dt.dt_mon = bcdtobin(rtclk[MC_MONTH]);
598 	dt.dt_year = clock_expandyear(bcdtobin(rtclk[MC_YEAR]));
599 
600 	tv->tv_sec = clock_ymdhms_to_secs(&dt) - utc_offset;
601 	tv->tv_usec = 0;
602 	return 0;
603 }
604 
605 int
606 rtcsettime(struct todr_chip_handle *handle, struct timeval *tv)
607 {
608 	mc_todregs rtclk;
609 	struct clock_ymdhms dt;
610 	int century, s;
611 
612 	s = splclock();
613 	if (rtcget(&rtclk))
614 		memset(&rtclk, 0, sizeof(rtclk));
615 	splx(s);
616 
617 	clock_secs_to_ymdhms(tv->tv_sec + utc_offset, &dt);
618 
619 	rtclk[MC_SEC] = bintobcd(dt.dt_sec);
620 	rtclk[MC_MIN] = bintobcd(dt.dt_min);
621 	rtclk[MC_HOUR] = bintobcd(dt.dt_hour);
622 	rtclk[MC_DOW] = dt.dt_wday + 1;
623 	rtclk[MC_YEAR] = bintobcd(dt.dt_year % 100);
624 	rtclk[MC_MONTH] = bintobcd(dt.dt_mon);
625 	rtclk[MC_DOM] = bintobcd(dt.dt_day);
626 
627 #ifdef CLOCK_DEBUG
628 	printf("setclock: %x/%x/%x %x:%x:%x\n", rtclk[MC_YEAR], rtclk[MC_MONTH],
629 	   rtclk[MC_DOM], rtclk[MC_HOUR], rtclk[MC_MIN], rtclk[MC_SEC]);
630 #endif
631 
632 	s = splclock();
633 	rtcput(&rtclk);
634 	if (rtc_update_century > 0) {
635 		century = bintobcd(dt.dt_year / 100);
636 		mc146818_write(NULL, NVRAM_CENTURY, century); /* XXX softc */
637 	}
638 	splx(s);
639 	return 0;
640 }
641 
642 extern todr_chip_handle_t todr_handle;
643 struct todr_chip_handle rtc_todr;
644 
645 void
646 rtcinit(void)
647 {
648 	rtc_todr.todr_gettime = rtcgettime;
649 	rtc_todr.todr_settime = rtcsettime;
650 	todr_handle = &rtc_todr;
651 }
652 
653 void
654 setstatclockrate(int arg)
655 {
656 	if (initclock_func == i8254_initclocks) {
657 		if (arg == stathz)
658 			mc146818_write(NULL, MC_REGA,
659 			    MC_BASE_32_KHz | MC_RATE_128_Hz);
660 		else
661 			mc146818_write(NULL, MC_REGA,
662 			    MC_BASE_32_KHz | MC_RATE_1024_Hz);
663 	}
664 }
665 
666 void
667 i8254_inittimecounter(void)
668 {
669 	tc_init(&i8254_timecounter);
670 }
671 
672 /*
673  * If we're using lapic to drive hardclock, we can use a simpler
674  * algorithm for the i8254 timecounters.
675  */
676 void
677 i8254_inittimecounter_simple(void)
678 {
679 	i8254_timecounter.tc_get_timecount = i8254_simple_get_timecount;
680 	i8254_timecounter.tc_counter_mask = 0x7fff;
681 	i8254_timecounter.tc_frequency = TIMER_FREQ;
682 
683 	mtx_enter(&timer_mutex);
684 	rtclock_tval = 0x8000;
685 	i8254_startclock();
686 	mtx_leave(&timer_mutex);
687 
688 	tc_init(&i8254_timecounter);
689 }
690 
691 void
692 i8254_startclock(void)
693 {
694 	u_long tval = rtclock_tval;
695 
696 	outb(IO_TIMER1 + TIMER_MODE, TIMER_SEL0 | TIMER_RATEGEN | TIMER_16BIT);
697 	outb(IO_TIMER1 + TIMER_CNTR0, tval & 0xff);
698 	outb(IO_TIMER1 + TIMER_CNTR0, tval >> 8);
699 }
700 
701 u_int
702 i8254_simple_get_timecount(struct timecounter *tc)
703 {
704 	return (rtclock_tval - gettick());
705 }
706 
707 u_int
708 i8254_get_timecount(struct timecounter *tc)
709 {
710 	u_char hi, lo;
711 	u_int count;
712 	u_long s;
713 
714 	s = intr_disable();
715 
716 	outb(IO_TIMER1 + TIMER_MODE, TIMER_SEL0 | TIMER_LATCH);
717 	lo = inb(IO_TIMER1 + TIMER_CNTR0);
718 	hi = inb(IO_TIMER1 + TIMER_CNTR0);
719 
720 	count = rtclock_tval - ((hi << 8) | lo);
721 
722 	if (count < i8254_lastcount) {
723 		i8254_ticked = 1;
724 		i8254_offset += rtclock_tval;
725 	}
726 	i8254_lastcount = count;
727 	count += i8254_offset;
728 
729 	intr_restore(s);
730 
731 	return (count);
732 }
733