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