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