xref: /openbsd/sys/arch/i386/isa/clock.c (revision ad29123e)
1 /*	$OpenBSD: clock.c,v 1.70 2024/06/08 00:24:00 jsg 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/clockintr.h>
93 #include <sys/timeout.h>
94 #include <sys/timetc.h>
95 #include <sys/mutex.h>
96 
97 #include <machine/intr.h>
98 #include <machine/pio.h>
99 #include <machine/cpufunc.h>
100 
101 #include <dev/clock_subr.h>
102 #include <dev/isa/isareg.h>
103 #include <dev/isa/isavar.h>
104 #include <dev/ic/mc146818reg.h>
105 #include <dev/ic/i8253reg.h>
106 #include <i386/isa/nvram.h>
107 
108 int	clockintr(void *);
109 int	gettick(void);
110 int	rtcget(mc_todregs *);
111 void	rtcput(mc_todregs *);
112 int	rtcintr(void *);
113 void	rtcdrain(void *);
114 int	calibrate_cyclecounter_ctr(void);
115 
116 u_int mc146818_read(void *, u_int);
117 void mc146818_write(void *, u_int, u_int);
118 
119 int cpuspeed;
120 int clock_broken_latch;
121 
122 /* Timecounter on the i8254 */
123 uint32_t i8254_lastcount;
124 uint32_t i8254_offset;
125 int i8254_ticked;
126 u_int i8254_get_timecount(struct timecounter *tc);
127 u_int i8254_simple_get_timecount(struct timecounter *tc);
128 
129 static struct timecounter i8254_timecounter = {
130 	.tc_get_timecount = i8254_get_timecount,
131 	.tc_counter_mask = ~0u,
132 	.tc_frequency = TIMER_FREQ,
133 	.tc_name = "i8254",
134 	.tc_quality = 0,
135 	.tc_priv = NULL,
136 	.tc_user = 0,
137 };
138 struct mutex timer_mutex = MUTEX_INITIALIZER(IPL_HIGH);
139 u_long rtclock_tval;
140 
141 u_int
mc146818_read(void * sc,u_int reg)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
mc146818_write(void * sc,u_int reg,u_int datum)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
startclocks(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
rtcdrain(void * v)186 rtcdrain(void *v)
187 {
188 	struct timeout *to = (struct timeout *)v;
189 
190 	if (to != NULL)
191 		timeout_del(to);
192 
193 	/* Drain any un-acknowledged RTC interrupts. */
194 	while (mc146818_read(NULL, MC_REGC) & MC_REGC_PF)
195 		; /* Nothing. */
196 }
197 
198 int
clockintr(void * frame)199 clockintr(void *frame)
200 {
201 	if (timecounter->tc_get_timecount == i8254_get_timecount) {
202 		if (i8254_ticked) {
203 			i8254_ticked = 0;
204 		} else {
205 			i8254_offset += rtclock_tval;
206 			i8254_lastcount = 0;
207 		}
208 	}
209 	clockintr_dispatch(frame);
210 	return (1);
211 }
212 
213 int
rtcintr(void * frame)214 rtcintr(void *frame)
215 {
216 	u_int stat = 0;
217 
218 	/*
219 	 * If rtcintr is 'late', next intr may happen immediately.
220 	 * Get them all. (Also, see comment in cpu_initclocks().)
221 	 */
222 	while (mc146818_read(NULL, MC_REGC) & MC_REGC_PF)
223 		stat = 1;
224 
225 	/* XXX Can rtcintr() run before i8254_initclocks() is complete? */
226 	if (stathz != 0 && stat)
227 		clockintr_dispatch(frame);
228 
229 	return (stat);
230 }
231 
232 int
gettick(void)233 gettick(void)
234 {
235 	u_long s;
236 
237 	if (clock_broken_latch) {
238 		int v1, v2, v3;
239 		int w1, w2, w3;
240 
241 		/*
242 		 * Don't lock the mutex in this case, clock_broken_latch
243 		 * CPUs don't do MP anyway.
244 		 */
245 
246 		s = intr_disable();
247 
248 		v1 = inb(IO_TIMER1 + TIMER_CNTR0);
249 		v1 |= inb(IO_TIMER1 + TIMER_CNTR0) << 8;
250 		v2 = inb(IO_TIMER1 + TIMER_CNTR0);
251 		v2 |= inb(IO_TIMER1 + TIMER_CNTR0) << 8;
252 		v3 = inb(IO_TIMER1 + TIMER_CNTR0);
253 		v3 |= inb(IO_TIMER1 + TIMER_CNTR0) << 8;
254 
255 		intr_restore(s);
256 
257 		if (v1 >= v2 && v2 >= v3 && v1 - v3 < 0x200)
258 			return (v2);
259 
260 #define _swap_val(a, b) do { \
261 	int c = a; \
262 	a = b; \
263 	b = c; \
264 } while (0)
265 
266 		/* sort v1 v2 v3 */
267 		if (v1 < v2)
268 			_swap_val(v1, v2);
269 		if (v2 < v3)
270 			_swap_val(v2, v3);
271 		if (v1 < v2)
272 			_swap_val(v1, v2);
273 
274 		/* compute the middle value */
275 		if (v1 - v3 < 0x200)
276 			return (v2);
277 		w1 = v2 - v3;
278 		w2 = v3 - v1 + TIMER_DIV(hz);
279 		w3 = v1 - v2;
280 		if (w1 >= w2) {
281 			if (w1 >= w3)
282 				return (v1);
283 		} else {
284 			if (w2 >= w3)
285 				return (v2);
286 		}
287 		return (v3);
288 	} else {
289 		u_char lo, hi;
290 
291 		mtx_enter(&timer_mutex);
292 		s = intr_disable();
293 		/* Select counter 0 and latch it. */
294 		outb(IO_TIMER1 + TIMER_MODE, TIMER_SEL0 | TIMER_LATCH);
295 		lo = inb(IO_TIMER1 + TIMER_CNTR0);
296 		hi = inb(IO_TIMER1 + TIMER_CNTR0);
297 
298 		intr_restore(s);
299 		mtx_leave(&timer_mutex);
300 		return ((hi << 8) | lo);
301 	}
302 }
303 
304 /*
305  * Wait "n" microseconds.
306  * Relies on timer 1 counting down from (TIMER_FREQ / hz) at TIMER_FREQ Hz.
307  * Note: timer had better have been programmed before this is first used!
308  * (Note that we use `rate generator' mode, which counts at 1:1; `square
309  * wave' mode counts at 2:1).
310  */
311 void
i8254_delay(int n)312 i8254_delay(int n)
313 {
314 	int limit, tick, otick;
315 
316 	/*
317 	 * Read the counter first, so that the rest of the setup overhead is
318 	 * counted.
319 	 */
320 	otick = gettick();
321 
322 #ifdef __GNUC__
323 	/*
324 	 * Calculate ((n * TIMER_FREQ) / 1e6) using explicit assembler code so
325 	 * we can take advantage of the intermediate 64-bit quantity to prevent
326 	 * loss of significance.
327 	 */
328 	n -= 5;
329 	if (n < 0)
330 		return;
331 	__asm volatile("mul %2\n\tdiv %3"
332 			 : "=a" (n)
333 			 : "0" (n), "r" (TIMER_FREQ), "r" (1000000)
334 			 : "%edx", "cc");
335 #else
336 	/*
337 	 * Calculate ((n * TIMER_FREQ) / 1e6) without using floating point and
338 	 * without any avoidable overflows.
339 	 */
340 	n -= 20;
341 	{
342 		int sec = n / 1000000,
343 		    usec = n % 1000000;
344 		n = sec * TIMER_FREQ +
345 		    usec * (TIMER_FREQ / 1000000) +
346 		    usec * ((TIMER_FREQ % 1000000) / 1000) / 1000 +
347 		    usec * (TIMER_FREQ % 1000) / 1000000;
348 	}
349 #endif
350 
351 	limit = TIMER_FREQ / hz;
352 
353 	while (n > 0) {
354 		tick = gettick();
355 		if (tick > otick)
356 			n -= limit - (tick - otick);
357 		else
358 			n -= otick - tick;
359 		otick = tick;
360 	}
361 }
362 
363 int
calibrate_cyclecounter_ctr(void)364 calibrate_cyclecounter_ctr(void)
365 {
366 	struct cpu_info *ci = curcpu();
367 	unsigned long long count, last_count, msr;
368 
369 	if ((ci->ci_flags & CPUF_CONST_TSC) == 0 ||
370 	    (cpu_perf_eax & CPUIDEAX_VERID) <= 1 ||
371 	    CPUIDEDX_NUM_FC(cpu_perf_edx) <= 1)
372 		return (-1);
373 
374 	msr = rdmsr(MSR_PERF_FIXED_CTR_CTRL);
375 	if (msr & MSR_PERF_FIXED_CTR_FC(1, MSR_PERF_FIXED_CTR_FC_MASK)) {
376 		/* some hypervisor is dicking us around */
377 		return (-1);
378 	}
379 
380 	msr |= MSR_PERF_FIXED_CTR_FC(1, MSR_PERF_FIXED_CTR_FC_1);
381 	wrmsr(MSR_PERF_FIXED_CTR_CTRL, msr);
382 
383 	msr = rdmsr(MSR_PERF_GLOBAL_CTRL) | MSR_PERF_GLOBAL_CTR1_EN;
384 	wrmsr(MSR_PERF_GLOBAL_CTRL, msr);
385 
386 	last_count = rdmsr(MSR_PERF_FIXED_CTR1);
387 	delay(1000000);
388 	count = rdmsr(MSR_PERF_FIXED_CTR1);
389 
390 	msr = rdmsr(MSR_PERF_FIXED_CTR_CTRL);
391 	msr &= MSR_PERF_FIXED_CTR_FC(1, MSR_PERF_FIXED_CTR_FC_MASK);
392 	wrmsr(MSR_PERF_FIXED_CTR_CTRL, msr);
393 
394 	msr = rdmsr(MSR_PERF_GLOBAL_CTRL);
395 	msr &= ~MSR_PERF_GLOBAL_CTR1_EN;
396 	wrmsr(MSR_PERF_GLOBAL_CTRL, msr);
397 
398 	cpuspeed = ((count - last_count) + 999999) / 1000000;
399 
400 	return (cpuspeed == 0 ? -1 : 0);
401 }
402 
403 void
calibrate_cyclecounter(void)404 calibrate_cyclecounter(void)
405 {
406 	unsigned long long count, last_count;
407 
408 	if (calibrate_cyclecounter_ctr() == 0)
409 		return;
410 
411 	__asm volatile("rdtsc" : "=A" (last_count));
412 	delay(1000000);
413 	__asm volatile("rdtsc" : "=A" (count));
414 
415 	cpuspeed = ((count - last_count) + 999999) / 1000000;
416 }
417 
418 void
i8254_initclocks(void)419 i8254_initclocks(void)
420 {
421 	i8254_inittimecounter();	/* hook the interrupt-based i8254 tc */
422 
423 	stathz = 128;
424 	profhz = 1024;		/* XXX does not divide into 1 billion */
425 }
426 
427 void
i8254_start_both_clocks(void)428 i8254_start_both_clocks(void)
429 {
430 	clockintr_cpu_init(NULL);
431 
432 	/*
433 	 * When using i8254 for clock, we also use the rtc for profclock.
434 	 *
435 	 * These IRQs are not MP-safe, but it is harmless to lie about it
436 	 * because we cannot reach this point unless we are only booting
437 	 * a single CPU.
438 	 */
439 	(void)isa_intr_establish(NULL, 0, IST_PULSE, IPL_CLOCK | IPL_MPSAFE,
440 	    clockintr, 0, "clock");
441 	(void)isa_intr_establish(NULL, 8, IST_PULSE, IPL_STATCLOCK | IPL_MPSAFE,
442 	    rtcintr, 0, "rtc");
443 
444 	rtcstart();			/* start the mc146818 clock */
445 }
446 
447 void
rtcstart(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
rtcstop(void)469 rtcstop(void)
470 {
471 	mc146818_write(NULL, MC_REGB, MC_REGB_24HR);
472 }
473 
474 int
rtcget(mc_todregs * regs)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
rtcput(mc_todregs * regs)484 rtcput(mc_todregs *regs)
485 {
486 	MC146818_PUTTOD(NULL, regs);			/* XXX softc */
487 }
488 
489 int
bcdtobin(int n)490 bcdtobin(int n)
491 {
492 	return (((n >> 4) & 0x0f) * 10 + (n & 0x0f));
493 }
494 
495 int
bintobcd(int n)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
cmoscheck(void)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
clock_expandyear(int clockyear)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
rtcgettime(struct todr_chip_handle * handle,struct timeval * tv)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
rtcsettime(struct todr_chip_handle * handle,struct timeval * tv)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 struct todr_chip_handle rtc_todr;
650 
651 void
rtcinit(void)652 rtcinit(void)
653 {
654 	rtc_todr.todr_gettime = rtcgettime;
655 	rtc_todr.todr_settime = rtcsettime;
656 	rtc_todr.todr_quality = 0;
657 	todr_attach(&rtc_todr);
658 }
659 
660 void
setstatclockrate(int arg)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
i8254_inittimecounter(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
i8254_inittimecounter_simple(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
i8254_startclock(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
i8254_simple_get_timecount(struct timecounter * tc)709 i8254_simple_get_timecount(struct timecounter *tc)
710 {
711 	return (rtclock_tval - gettick());
712 }
713 
714 u_int
i8254_get_timecount(struct timecounter * tc)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