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