xref: /openbsd/sys/arch/i386/isa/clock.c (revision 6f40fd34)
1 /*	$OpenBSD: clock.c,v 1.51 2017/01/25 08:23:50 tom 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 	ts.tv_sec = clock_ymdhms_to_secs(&dt) + tz.tz_minuteswest * 60;
628 	if (tz.tz_dsttime)
629 		ts.tv_sec -= 3600;
630 
631 	if (base < ts.tv_sec - 5*SECYR)
632 		printf("WARNING: file system time much less than clock time\n");
633 	else if (base > ts.tv_sec + 5*SECYR) {
634 		printf("WARNING: clock time much less than file system time\n");
635 		printf("WARNING: using file system time\n");
636 		goto fstime;
637 	}
638 
639 	tc_setclock(&ts);
640 	timeset = 1;
641 	return;
642 
643 fstime:
644 	ts.tv_sec = base;
645 	tc_setclock(&ts);
646 	timeset = 1;
647 	printf("WARNING: CHECK AND RESET THE DATE!\n");
648 }
649 
650 /*
651  * Reset the clock.
652  */
653 void
654 resettodr(void)
655 {
656 	mc_todregs rtclk;
657 	struct clock_ymdhms dt;
658 	int diff;
659 	int century;
660 	int s;
661 
662 	/*
663 	 * We might have been called by boot() due to a crash early
664 	 * on.  Don't reset the clock chip in this case.
665 	 */
666 	if (!timeset)
667 		return;
668 
669 	s = splclock();
670 	if (rtcget(&rtclk))
671 		bzero(&rtclk, sizeof(rtclk));
672 	splx(s);
673 
674 	diff = tz.tz_minuteswest * 60;
675 	if (tz.tz_dsttime)
676 		diff -= 3600;
677 	clock_secs_to_ymdhms(time_second - diff, &dt);
678 
679 	rtclk[MC_SEC] = dectohexdec(dt.dt_sec);
680 	rtclk[MC_MIN] = dectohexdec(dt.dt_min);
681 	rtclk[MC_HOUR] = dectohexdec(dt.dt_hour);
682 	rtclk[MC_DOW] = dt.dt_wday;
683 	rtclk[MC_YEAR] = dectohexdec(dt.dt_year % 100);
684 	rtclk[MC_MONTH] = dectohexdec(dt.dt_mon);
685 	rtclk[MC_DOM] = dectohexdec(dt.dt_day);
686 	s = splclock();
687 	rtcput(&rtclk);
688 	if (rtc_update_century > 0) {
689 		century = dectohexdec(dt.dt_year / 100);
690 		mc146818_write(NULL, NVRAM_CENTURY, century); /* XXX softc */
691 	}
692 	splx(s);
693 }
694 
695 void
696 setstatclockrate(int arg)
697 {
698 	if (initclock_func == i8254_initclocks) {
699 		if (arg == stathz)
700 			mc146818_write(NULL, MC_REGA,
701 			    MC_BASE_32_KHz | MC_RATE_128_Hz);
702 		else
703 			mc146818_write(NULL, MC_REGA,
704 			    MC_BASE_32_KHz | MC_RATE_1024_Hz);
705 	}
706 }
707 
708 void
709 i8254_inittimecounter(void)
710 {
711 	tc_init(&i8254_timecounter);
712 }
713 
714 /*
715  * If we're using lapic to drive hardclock, we can use a simpler
716  * algorithm for the i8254 timecounters.
717  */
718 void
719 i8254_inittimecounter_simple(void)
720 {
721 	i8254_timecounter.tc_get_timecount = i8254_simple_get_timecount;
722 	i8254_timecounter.tc_counter_mask = 0x7fff;
723 	i8254_timecounter.tc_frequency = TIMER_FREQ;
724 
725 	mtx_enter(&timer_mutex);
726 	rtclock_tval = 0x8000;
727 	i8254_startclock();
728 	mtx_leave(&timer_mutex);
729 
730 	tc_init(&i8254_timecounter);
731 }
732 
733 void
734 i8254_startclock(void)
735 {
736 	u_long tval = rtclock_tval;
737 
738 	outb(IO_TIMER1 + TIMER_MODE, TIMER_SEL0 | TIMER_RATEGEN | TIMER_16BIT);
739 	outb(IO_TIMER1 + TIMER_CNTR0, tval & 0xff);
740 	outb(IO_TIMER1 + TIMER_CNTR0, tval >> 8);
741 }
742 
743 u_int
744 i8254_simple_get_timecount(struct timecounter *tc)
745 {
746 	return (rtclock_tval - gettick());
747 }
748 
749 u_int
750 i8254_get_timecount(struct timecounter *tc)
751 {
752 	u_char hi, lo;
753 	u_int count;
754 	u_long ef;
755 
756 	ef = read_eflags();
757 	disable_intr();
758 
759 	outb(IO_TIMER1 + TIMER_MODE, TIMER_SEL0 | TIMER_LATCH);
760 	lo = inb(IO_TIMER1 + TIMER_CNTR0);
761 	hi = inb(IO_TIMER1 + TIMER_CNTR0);
762 
763 	count = rtclock_tval - ((hi << 8) | lo);
764 
765 	if (count < i8254_lastcount) {
766 		i8254_ticked = 1;
767 		i8254_offset += rtclock_tval;
768 	}
769 	i8254_lastcount = count;
770 	count += i8254_offset;
771 	write_eflags(ef);
772 
773 	return (count);
774 }
775