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