xref: /openbsd-src/sys/arch/amd64/isa/clock.c (revision 0ed1bf01ac7e45759b65902d0ab711deb359316d)
1*0ed1bf01Scheloha /*	$OpenBSD: clock.c,v 1.42 2023/09/17 14:50:50 cheloha Exp $	*/
2f5df1827Smickey /*	$NetBSD: clock.c,v 1.1 2003/04/26 18:39:50 fvdl Exp $	*/
3f5df1827Smickey 
4f5df1827Smickey /*-
5f5df1827Smickey  * Copyright (c) 1993, 1994 Charles M. Hannum.
6f5df1827Smickey  * Copyright (c) 1990 The Regents of the University of California.
7f5df1827Smickey  * All rights reserved.
8f5df1827Smickey  *
9f5df1827Smickey  * This code is derived from software contributed to Berkeley by
10f5df1827Smickey  * William Jolitz and Don Ahn.
11f5df1827Smickey  *
12f5df1827Smickey  * Redistribution and use in source and binary forms, with or without
13f5df1827Smickey  * modification, are permitted provided that the following conditions
14f5df1827Smickey  * are met:
15f5df1827Smickey  * 1. Redistributions of source code must retain the above copyright
16f5df1827Smickey  *    notice, this list of conditions and the following disclaimer.
17f5df1827Smickey  * 2. Redistributions in binary form must reproduce the above copyright
18f5df1827Smickey  *    notice, this list of conditions and the following disclaimer in the
19f5df1827Smickey  *    documentation and/or other materials provided with the distribution.
20c5217b0aSjsg  * 3. Neither the name of the University nor the names of its contributors
21f5df1827Smickey  *    may be used to endorse or promote products derived from this software
22f5df1827Smickey  *    without specific prior written permission.
23f5df1827Smickey  *
24f5df1827Smickey  * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
25f5df1827Smickey  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
26f5df1827Smickey  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
27f5df1827Smickey  * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
28f5df1827Smickey  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
29f5df1827Smickey  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
30f5df1827Smickey  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
31f5df1827Smickey  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
32f5df1827Smickey  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
33f5df1827Smickey  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
34f5df1827Smickey  * SUCH DAMAGE.
35f5df1827Smickey  *
36f5df1827Smickey  *	@(#)clock.c	7.2 (Berkeley) 5/12/91
37f5df1827Smickey  */
38f5df1827Smickey /*
39f5df1827Smickey  * Mach Operating System
40f5df1827Smickey  * Copyright (c) 1991,1990,1989 Carnegie Mellon University
41f5df1827Smickey  * All Rights Reserved.
42f5df1827Smickey  *
43f5df1827Smickey  * Permission to use, copy, modify and distribute this software and its
44f5df1827Smickey  * documentation is hereby granted, provided that both the copyright
45f5df1827Smickey  * notice and this permission notice appear in all copies of the
46f5df1827Smickey  * software, derivative works or modified versions, and any portions
47f5df1827Smickey  * thereof, and that both notices appear in supporting documentation.
48f5df1827Smickey  *
49f5df1827Smickey  * CARNEGIE MELLON ALLOWS FREE USE OF THIS SOFTWARE IN ITS "AS IS"
50f5df1827Smickey  * CONDITION.  CARNEGIE MELLON DISCLAIMS ANY LIABILITY OF ANY KIND FOR
51f5df1827Smickey  * ANY DAMAGES WHATSOEVER RESULTING FROM THE USE OF THIS SOFTWARE.
52f5df1827Smickey  *
53f5df1827Smickey  * Carnegie Mellon requests users of this software to return to
54f5df1827Smickey  *
55f5df1827Smickey  *  Software Distribution Coordinator  or  Software.Distribution@CS.CMU.EDU
56f5df1827Smickey  *  School of Computer Science
57f5df1827Smickey  *  Carnegie Mellon University
58f5df1827Smickey  *  Pittsburgh PA 15213-3890
59f5df1827Smickey  *
60f5df1827Smickey  * any improvements or extensions that they make and grant Carnegie Mellon
61f5df1827Smickey  * the rights to redistribute these changes.
62f5df1827Smickey  */
63f5df1827Smickey /*
64f5df1827Smickey   Copyright 1988, 1989 by Intel Corporation, Santa Clara, California.
65f5df1827Smickey 
66f5df1827Smickey 		All Rights Reserved
67f5df1827Smickey 
68f5df1827Smickey Permission to use, copy, modify, and distribute this software and
69f5df1827Smickey its documentation for any purpose and without fee is hereby
70f5df1827Smickey granted, provided that the above copyright notice appears in all
71f5df1827Smickey copies and that both the copyright notice and this permission notice
72f5df1827Smickey appear in supporting documentation, and that the name of Intel
73f5df1827Smickey not be used in advertising or publicity pertaining to distribution
74f5df1827Smickey of the software without specific, written prior permission.
75f5df1827Smickey 
76f5df1827Smickey INTEL DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE
77f5df1827Smickey INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS,
78f5df1827Smickey IN NO EVENT SHALL INTEL BE LIABLE FOR ANY SPECIAL, INDIRECT, OR
79f5df1827Smickey CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
80f5df1827Smickey LOSS OF USE, DATA OR PROFITS, WHETHER IN ACTION OF CONTRACT,
81f5df1827Smickey NEGLIGENCE, OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION
82f5df1827Smickey WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
83f5df1827Smickey */
84f5df1827Smickey 
85f5df1827Smickey /*
86f5df1827Smickey  * Primitive clock interrupt routines.
87f5df1827Smickey  */
88f5df1827Smickey 
896905cb4aSkettenis /* #define CLOCK_DEBUG */
90f5df1827Smickey 
91f5df1827Smickey #include <sys/param.h>
92f5df1827Smickey #include <sys/systm.h>
939e761879Scheloha #include <sys/clockintr.h>
94f5df1827Smickey #include <sys/time.h>
95f5df1827Smickey #include <sys/kernel.h>
96f5df1827Smickey #include <sys/timeout.h>
9707e9c1faSotto #include <sys/timetc.h>
98f5df1827Smickey 
99f5df1827Smickey #include <machine/cpu.h>
100f5df1827Smickey #include <machine/intr.h>
101f5df1827Smickey #include <machine/pio.h>
102f5df1827Smickey #include <machine/cpufunc.h>
103f5df1827Smickey 
1046905cb4aSkettenis #include <dev/clock_subr.h>
105f5df1827Smickey #include <dev/isa/isareg.h>
106f5df1827Smickey #include <dev/isa/isavar.h>
107f5df1827Smickey #include <dev/ic/mc146818reg.h>
108f5df1827Smickey #include <dev/ic/i8253reg.h>
1095be2bff2Sderaadt #include <amd64/isa/nvram.h>
110f5df1827Smickey 
11107e9c1faSotto /* Timecounter on the i8254 */
11207e9c1faSotto u_int32_t i8254_lastcount;
11307e9c1faSotto u_int32_t i8254_offset;
11407e9c1faSotto int i8254_ticked;
11507e9c1faSotto u_int i8254_get_timecount(struct timecounter *tc);
11607e9c1faSotto 
11707e9c1faSotto u_int i8254_simple_get_timecount(struct timecounter *tc);
11807e9c1faSotto 
11907e9c1faSotto static struct timecounter i8254_timecounter = {
1208611d3cdScheloha 	.tc_get_timecount = i8254_get_timecount,
1218611d3cdScheloha 	.tc_counter_mask = ~0u,
1228611d3cdScheloha 	.tc_frequency = TIMER_FREQ,
1238611d3cdScheloha 	.tc_name = "i8254",
1248611d3cdScheloha 	.tc_quality = 0,
1258611d3cdScheloha 	.tc_priv = NULL,
1268611d3cdScheloha 	.tc_user = 0,
12707e9c1faSotto };
12807e9c1faSotto 
129f5df1827Smickey int	clockintr(void *);
130f5df1827Smickey int	rtcintr(void *);
131f5df1827Smickey int	gettick(void);
132f5df1827Smickey void	rtcdrain(void *v);
133f5df1827Smickey int	rtcget(mc_todregs *);
134f5df1827Smickey void	rtcput(mc_todregs *);
135f5df1827Smickey int	bcdtobin(int);
136f5df1827Smickey int	bintobcd(int);
137f5df1827Smickey 
13823f8a50cSjsg u_int mc146818_read(void *, u_int);
13923f8a50cSjsg void mc146818_write(void *, u_int, u_int);
140f5df1827Smickey 
14123f8a50cSjsg u_int
mc146818_read(void * sc,u_int reg)1422bb6026aSjsg mc146818_read(void *sc, u_int reg)
143f5df1827Smickey {
144f5df1827Smickey 	outb(IO_RTC, reg);
145f5df1827Smickey 	DELAY(1);
146f5df1827Smickey 	return (inb(IO_RTC+1));
147f5df1827Smickey }
148f5df1827Smickey 
14923f8a50cSjsg void
mc146818_write(void * sc,u_int reg,u_int datum)1502bb6026aSjsg mc146818_write(void *sc, u_int reg, u_int datum)
151f5df1827Smickey {
152f5df1827Smickey 	outb(IO_RTC, reg);
153f5df1827Smickey 	DELAY(1);
154f5df1827Smickey 	outb(IO_RTC+1, datum);
155f5df1827Smickey 	DELAY(1);
156f5df1827Smickey }
157f5df1827Smickey 
15807e9c1faSotto struct mutex timer_mutex = MUTEX_INITIALIZER(IPL_HIGH);
15907e9c1faSotto 
160b5b9857bSart u_long rtclock_tval;
161f5df1827Smickey 
162f5df1827Smickey void
startclocks(void)16394ce6677Sderaadt startclocks(void)
164f5df1827Smickey {
16594ce6677Sderaadt 	mtx_enter(&timer_mutex);
16694ce6677Sderaadt 	rtclock_tval = TIMER_DIV(hz);
16794ce6677Sderaadt 	i8254_startclock();
16894ce6677Sderaadt 	mtx_leave(&timer_mutex);
169f5df1827Smickey }
170f5df1827Smickey 
171f5df1827Smickey int
clockintr(void * frame)1729e761879Scheloha clockintr(void *frame)
173f5df1827Smickey {
17407e9c1faSotto 	if (timecounter->tc_get_timecount == i8254_get_timecount) {
17507e9c1faSotto 		if (i8254_ticked) {
17607e9c1faSotto 			i8254_ticked = 0;
17707e9c1faSotto 		} else {
17807e9c1faSotto 			i8254_offset += rtclock_tval;
17907e9c1faSotto 			i8254_lastcount = 0;
18007e9c1faSotto 		}
18107e9c1faSotto 	}
18207e9c1faSotto 
1839e761879Scheloha 	clockintr_dispatch(frame);
184f5df1827Smickey 
185f5df1827Smickey 	return 1;
186f5df1827Smickey }
187f5df1827Smickey 
188f5df1827Smickey int
rtcintr(void * frame)1899e761879Scheloha rtcintr(void *frame)
190f5df1827Smickey {
191f5df1827Smickey 	u_int stat = 0;
192f5df1827Smickey 
193f5df1827Smickey 	/*
194f5df1827Smickey 	 * If rtcintr is 'late', next intr may happen immediately.
195f5df1827Smickey 	 * Get them all. (Also, see comment in cpu_initclocks().)
196f5df1827Smickey 	 */
1979e761879Scheloha 	while (mc146818_read(NULL, MC_REGC) & MC_REGC_PF)
198f5df1827Smickey 		stat = 1;
1999e761879Scheloha 
2009e761879Scheloha 	if (stat)
2019e761879Scheloha 		clockintr_dispatch(frame);
202f5df1827Smickey 
203f5df1827Smickey 	return (stat);
204f5df1827Smickey }
205f5df1827Smickey 
206f5df1827Smickey int
gettick(void)2072bb6026aSjsg gettick(void)
208f5df1827Smickey {
2090db9c031Skettenis 	u_long s;
210f5df1827Smickey 	u_char lo, hi;
211f5df1827Smickey 
212f5df1827Smickey 	/* Don't want someone screwing with the counter while we're here. */
21307e9c1faSotto 	mtx_enter(&timer_mutex);
2140db9c031Skettenis 	s = intr_disable();
215f5df1827Smickey 	/* Select counter 0 and latch it. */
216f5df1827Smickey 	outb(IO_TIMER1+TIMER_MODE, TIMER_SEL0 | TIMER_LATCH);
217f5df1827Smickey 	lo = inb(IO_TIMER1+TIMER_CNTR0);
218f5df1827Smickey 	hi = inb(IO_TIMER1+TIMER_CNTR0);
2190db9c031Skettenis 	intr_restore(s);
22007e9c1faSotto 	mtx_leave(&timer_mutex);
221f5df1827Smickey 	return ((hi << 8) | lo);
222f5df1827Smickey }
223f5df1827Smickey 
224f5df1827Smickey /*
225f5df1827Smickey  * Wait "n" microseconds.
226f5df1827Smickey  * Relies on timer 1 counting down from (TIMER_FREQ / hz) at TIMER_FREQ Hz.
227f5df1827Smickey  * Note: timer had better have been programmed before this is first used!
228f5df1827Smickey  * (Note that we use `rate generator' mode, which counts at 1:1; `square
229f5df1827Smickey  * wave' mode counts at 2:1).
230f5df1827Smickey  */
231f5df1827Smickey void
i8254_delay(int n)232b5b9857bSart i8254_delay(int n)
233f5df1827Smickey {
234f5df1827Smickey 	int limit, tick, otick;
235f807562cSderaadt 	static const int delaytab[26] = {
236f807562cSderaadt 		 0,  2,  3,  4,  5,  6,  7,  9, 10, 11,
237f807562cSderaadt 		12, 13, 15, 16, 17, 18, 19, 21, 22, 23,
238f807562cSderaadt 		24, 25, 27, 28, 29, 30,
239f807562cSderaadt 	};
240f807562cSderaadt 
241f5df1827Smickey 	/*
242f5df1827Smickey 	 * Read the counter first, so that the rest of the setup overhead is
243f5df1827Smickey 	 * counted.
244f5df1827Smickey 	 */
245f5df1827Smickey 	otick = gettick();
246f5df1827Smickey 
247f807562cSderaadt 	if (n <= 25)
248f807562cSderaadt 		n = delaytab[n];
249f807562cSderaadt 	else {
2502fbcb6d2Scheloha 		/* Force 64-bit math to avoid 32-bit overflow if possible. */
2512fbcb6d2Scheloha 		n = (int64_t)n * TIMER_FREQ / 1000000;
252f807562cSderaadt 	}
253f5df1827Smickey 
254f5df1827Smickey 	limit = TIMER_FREQ / hz;
255f5df1827Smickey 
256f5df1827Smickey 	while (n > 0) {
257f5df1827Smickey 		tick = gettick();
258f5df1827Smickey 		if (tick > otick)
259f5df1827Smickey 			n -= limit - (tick - otick);
260f5df1827Smickey 		else
261f5df1827Smickey 			n -= otick - tick;
262f5df1827Smickey 		otick = tick;
263f5df1827Smickey 	}
264f5df1827Smickey }
265f5df1827Smickey 
266f5df1827Smickey void
rtcdrain(void * v)267f5df1827Smickey rtcdrain(void *v)
268f5df1827Smickey {
269f5df1827Smickey 	struct timeout *to = (struct timeout *)v;
270f5df1827Smickey 
271f5df1827Smickey 	if (to != NULL)
272f5df1827Smickey 		timeout_del(to);
273f5df1827Smickey 
2742b2a6d92Sjasper 	/* Drain any un-acknowledged RTC interrupts. */
275f5df1827Smickey 	while (mc146818_read(NULL, MC_REGC) & MC_REGC_PF)
276f5df1827Smickey 		; /* Nothing. */
277f5df1827Smickey }
278f5df1827Smickey 
279f5df1827Smickey void
i8254_initclocks(void)2802bb6026aSjsg i8254_initclocks(void)
281f5df1827Smickey {
2829e761879Scheloha 	i8254_inittimecounter();	/* hook the interrupt-based i8254 tc */
2839e761879Scheloha 
284f5df1827Smickey 	stathz = 128;
2859e761879Scheloha 	profhz = 1024;		/* XXX does not divide into 1 billion */
28611d1f9b2Scheloha }
2879e761879Scheloha 
28811d1f9b2Scheloha void
i8254_start_both_clocks(void)28911d1f9b2Scheloha i8254_start_both_clocks(void)
29011d1f9b2Scheloha {
2919e761879Scheloha 	clockintr_cpu_init(NULL);
292f5df1827Smickey 
293587afcd5Skettenis 	/*
294587afcd5Skettenis 	 * While the clock interrupt handler isn't really MPSAFE, the
295587afcd5Skettenis 	 * i8254 can't really be used as a clock on a true MP system.
296587afcd5Skettenis 	 */
297587afcd5Skettenis 	isa_intr_establish(NULL, 0, IST_PULSE, IPL_CLOCK | IPL_MPSAFE,
298587afcd5Skettenis 	    clockintr, 0, "clock");
299587afcd5Skettenis 	isa_intr_establish(NULL, 8, IST_PULSE, IPL_STATCLOCK | IPL_MPSAFE,
300587afcd5Skettenis 	    rtcintr, 0, "rtc");
30194ce6677Sderaadt 
30294ce6677Sderaadt 	rtcstart();			/* start the mc146818 clock */
30394ce6677Sderaadt }
30494ce6677Sderaadt 
30594ce6677Sderaadt void
rtcstart(void)30694ce6677Sderaadt rtcstart(void)
30794ce6677Sderaadt {
30894ce6677Sderaadt 	static struct timeout rtcdrain_timeout;
309f5df1827Smickey 
310f5df1827Smickey 	mc146818_write(NULL, MC_REGA, MC_BASE_32_KHz | MC_RATE_128_Hz);
311f5df1827Smickey 	mc146818_write(NULL, MC_REGB, MC_REGB_24HR | MC_REGB_PIE);
312f5df1827Smickey 
313f5df1827Smickey 	/*
314f5df1827Smickey 	 * On a number of i386 systems, the rtc will fail to start when booting
315f5df1827Smickey 	 * the system. This is due to us missing to acknowledge an interrupt
316f5df1827Smickey 	 * during early stages of the boot process. If we do not acknowledge
317f5df1827Smickey 	 * the interrupt, the rtc clock will not generate further interrupts.
318f5df1827Smickey 	 * To solve this, once interrupts are enabled, use a timeout (once)
319f5df1827Smickey 	 * to drain any un-acknowledged rtc interrupt(s).
320f5df1827Smickey 	 */
321f5df1827Smickey 	timeout_set(&rtcdrain_timeout, rtcdrain, (void *)&rtcdrain_timeout);
322f5df1827Smickey 	timeout_add(&rtcdrain_timeout, 1);
323f5df1827Smickey }
324f5df1827Smickey 
3253191b86aSkettenis void
rtcstop(void)3263191b86aSkettenis rtcstop(void)
3273191b86aSkettenis {
3283191b86aSkettenis 	mc146818_write(NULL, MC_REGB, MC_REGB_24HR);
3293191b86aSkettenis }
3303191b86aSkettenis 
331f5df1827Smickey int
rtcget(mc_todregs * regs)3322bb6026aSjsg rtcget(mc_todregs *regs)
333f5df1827Smickey {
334f5df1827Smickey 	if ((mc146818_read(NULL, MC_REGD) & MC_REGD_VRT) == 0) /* XXX softc */
335f5df1827Smickey 		return (-1);
336f5df1827Smickey 	MC146818_GETTOD(NULL, regs);			/* XXX softc */
337f5df1827Smickey 	return (0);
338f5df1827Smickey }
339f5df1827Smickey 
340f5df1827Smickey void
rtcput(mc_todregs * regs)3412bb6026aSjsg rtcput(mc_todregs *regs)
342f5df1827Smickey {
343f5df1827Smickey 	MC146818_PUTTOD(NULL, regs);			/* XXX softc */
344f5df1827Smickey }
345f5df1827Smickey 
346f5df1827Smickey int
bcdtobin(int n)3472bb6026aSjsg bcdtobin(int n)
348f5df1827Smickey {
349f5df1827Smickey 	return (((n >> 4) & 0x0f) * 10 + (n & 0x0f));
350f5df1827Smickey }
351f5df1827Smickey 
352f5df1827Smickey int
bintobcd(int n)3532bb6026aSjsg bintobcd(int n)
354f5df1827Smickey {
355f5df1827Smickey 	return ((u_char)(((n / 10) << 4) & 0xf0) | ((n % 10) & 0x0f));
356f5df1827Smickey }
357f5df1827Smickey 
358f5df1827Smickey /*
359f5df1827Smickey  * check whether the CMOS layout is "standard"-like (ie, not PS/2-like),
360f5df1827Smickey  * to be called at splclock()
361f5df1827Smickey  */
362f5df1827Smickey static int cmoscheck(void);
363f5df1827Smickey static int
cmoscheck(void)3642bb6026aSjsg cmoscheck(void)
365f5df1827Smickey {
366f5df1827Smickey 	int i;
367f5df1827Smickey 	unsigned short cksum = 0;
368f5df1827Smickey 
369f5df1827Smickey 	for (i = 0x10; i <= 0x2d; i++)
370f5df1827Smickey 		cksum += mc146818_read(NULL, i); /* XXX softc */
371f5df1827Smickey 
372f5df1827Smickey 	return (cksum == (mc146818_read(NULL, 0x2e) << 8)
373f5df1827Smickey 			  + mc146818_read(NULL, 0x2f));
374f5df1827Smickey }
375f5df1827Smickey 
376f5df1827Smickey /*
377f5df1827Smickey  * patchable to control century byte handling:
378f5df1827Smickey  * 1: always update
379f5df1827Smickey  * -1: never touch
380f5df1827Smickey  * 0: try to figure out itself
381f5df1827Smickey  */
382f5df1827Smickey int rtc_update_century = 0;
383f5df1827Smickey 
384f5df1827Smickey /*
385f5df1827Smickey  * Expand a two-digit year as read from the clock chip
386f5df1827Smickey  * into full width.
387f5df1827Smickey  * Being here, deal with the CMOS century byte.
388f5df1827Smickey  */
389f5df1827Smickey static int centb = NVRAM_CENTURY;
390f5df1827Smickey static int clock_expandyear(int);
391f5df1827Smickey static int
clock_expandyear(int clockyear)3922bb6026aSjsg clock_expandyear(int clockyear)
393f5df1827Smickey {
394f5df1827Smickey 	int s, clockcentury, cmoscentury;
395f5df1827Smickey 
396f5df1827Smickey 	clockcentury = (clockyear < 70) ? 20 : 19;
397f5df1827Smickey 	clockyear += 100 * clockcentury;
398f5df1827Smickey 
399f5df1827Smickey 	if (rtc_update_century < 0)
400f5df1827Smickey 		return (clockyear);
401f5df1827Smickey 
402f5df1827Smickey 	s = splclock();
403f5df1827Smickey 	if (cmoscheck())
404f5df1827Smickey 		cmoscentury = mc146818_read(NULL, NVRAM_CENTURY);
405f5df1827Smickey 	else
406f5df1827Smickey 		cmoscentury = 0;
407f5df1827Smickey 	splx(s);
4086b03d5deSjcs 	if (!cmoscentury)
409f5df1827Smickey 		return (clockyear);
4106b03d5deSjcs 
411f5df1827Smickey 	cmoscentury = bcdtobin(cmoscentury);
412f5df1827Smickey 
413f5df1827Smickey 	if (cmoscentury != clockcentury) {
414f5df1827Smickey 		/* XXX note: saying "century is 20" might confuse the naive. */
415f5df1827Smickey 		printf("WARNING: NVRAM century is %d but RTC year is %d\n",
416f5df1827Smickey 		       cmoscentury, clockyear);
417f5df1827Smickey 
418f5df1827Smickey 		/* Kludge to roll over century. */
419f5df1827Smickey 		if ((rtc_update_century > 0) ||
420f5df1827Smickey 		    ((cmoscentury == 19) && (clockcentury == 20) &&
421f5df1827Smickey 		     (clockyear == 2000))) {
422f5df1827Smickey 			printf("WARNING: Setting NVRAM century to %d\n",
423f5df1827Smickey 			       clockcentury);
424f5df1827Smickey 			s = splclock();
425f5df1827Smickey 			mc146818_write(NULL, centb, bintobcd(clockcentury));
426f5df1827Smickey 			splx(s);
427f5df1827Smickey 		}
428f5df1827Smickey 	} else if (cmoscentury == 19 && rtc_update_century == 0)
429f5df1827Smickey 		rtc_update_century = 1; /* will update later in resettodr() */
430f5df1827Smickey 
431f5df1827Smickey 	return (clockyear);
432f5df1827Smickey }
433f5df1827Smickey 
4346905cb4aSkettenis int
rtcgettime(struct todr_chip_handle * handle,struct timeval * tv)4356905cb4aSkettenis rtcgettime(struct todr_chip_handle *handle, struct timeval *tv)
436f5df1827Smickey {
437f5df1827Smickey 	mc_todregs rtclk;
438f5df1827Smickey 	struct clock_ymdhms dt;
439f5df1827Smickey 	int s;
44007e9c1faSotto 
441f5df1827Smickey 	s = splclock();
442f5df1827Smickey 	if (rtcget(&rtclk)) {
443f5df1827Smickey 		splx(s);
4446905cb4aSkettenis 		return EINVAL;
445f5df1827Smickey 	}
446f5df1827Smickey 	splx(s);
4476905cb4aSkettenis 
4486905cb4aSkettenis #ifdef CLOCK_DEBUG
449f5df1827Smickey 	printf("readclock: %x/%x/%x %x:%x:%x\n", rtclk[MC_YEAR],
450f5df1827Smickey 	    rtclk[MC_MONTH], rtclk[MC_DOM], rtclk[MC_HOUR], rtclk[MC_MIN],
451f5df1827Smickey 	    rtclk[MC_SEC]);
452f5df1827Smickey #endif
453f5df1827Smickey 
454f5df1827Smickey 	dt.dt_sec = bcdtobin(rtclk[MC_SEC]);
455f5df1827Smickey 	dt.dt_min = bcdtobin(rtclk[MC_MIN]);
456f5df1827Smickey 	dt.dt_hour = bcdtobin(rtclk[MC_HOUR]);
457f5df1827Smickey 	dt.dt_day = bcdtobin(rtclk[MC_DOM]);
458f5df1827Smickey 	dt.dt_mon = bcdtobin(rtclk[MC_MONTH]);
459f5df1827Smickey 	dt.dt_year = clock_expandyear(bcdtobin(rtclk[MC_YEAR]));
460f5df1827Smickey 
4616905cb4aSkettenis 	tv->tv_sec = clock_ymdhms_to_secs(&dt) - utc_offset;
4626905cb4aSkettenis 	tv->tv_usec = 0;
4636905cb4aSkettenis 	return 0;
464f5df1827Smickey }
465f5df1827Smickey 
4666905cb4aSkettenis int
rtcsettime(struct todr_chip_handle * handle,struct timeval * tv)4676905cb4aSkettenis rtcsettime(struct todr_chip_handle *handle, struct timeval *tv)
468f5df1827Smickey {
469f5df1827Smickey 	mc_todregs rtclk;
470f5df1827Smickey 	struct clock_ymdhms dt;
471db1dd8d3Scheloha 	int century, s;
472f5df1827Smickey 
473f5df1827Smickey 	s = splclock();
474f5df1827Smickey 	if (rtcget(&rtclk))
475f5df1827Smickey 		memset(&rtclk, 0, sizeof(rtclk));
476f5df1827Smickey 	splx(s);
477f5df1827Smickey 
478a5dde40eSkettenis 	clock_secs_to_ymdhms(tv->tv_sec + utc_offset, &dt);
479f5df1827Smickey 
480f5df1827Smickey 	rtclk[MC_SEC] = bintobcd(dt.dt_sec);
481f5df1827Smickey 	rtclk[MC_MIN] = bintobcd(dt.dt_min);
482f5df1827Smickey 	rtclk[MC_HOUR] = bintobcd(dt.dt_hour);
483f5df1827Smickey 	rtclk[MC_DOW] = dt.dt_wday + 1;
484f5df1827Smickey 	rtclk[MC_YEAR] = bintobcd(dt.dt_year % 100);
485f5df1827Smickey 	rtclk[MC_MONTH] = bintobcd(dt.dt_mon);
486f5df1827Smickey 	rtclk[MC_DOM] = bintobcd(dt.dt_day);
487f5df1827Smickey 
4886905cb4aSkettenis #ifdef CLOCK_DEBUG
489f5df1827Smickey 	printf("setclock: %x/%x/%x %x:%x:%x\n", rtclk[MC_YEAR], rtclk[MC_MONTH],
490f5df1827Smickey 	   rtclk[MC_DOM], rtclk[MC_HOUR], rtclk[MC_MIN], rtclk[MC_SEC]);
491f5df1827Smickey #endif
4926905cb4aSkettenis 
493f5df1827Smickey 	s = splclock();
494f5df1827Smickey 	rtcput(&rtclk);
495f5df1827Smickey 	if (rtc_update_century > 0) {
496f5df1827Smickey 		century = bintobcd(dt.dt_year / 100);
497f5df1827Smickey 		mc146818_write(NULL, centb, century); /* XXX softc */
498f5df1827Smickey 	}
499f5df1827Smickey 	splx(s);
5006905cb4aSkettenis 	return 0;
5016905cb4aSkettenis }
5026905cb4aSkettenis 
5036905cb4aSkettenis struct todr_chip_handle rtc_todr;
5046905cb4aSkettenis 
5056905cb4aSkettenis void
rtcinit(void)5066905cb4aSkettenis rtcinit(void)
5076905cb4aSkettenis {
5086905cb4aSkettenis 	rtc_todr.todr_gettime = rtcgettime;
5096905cb4aSkettenis 	rtc_todr.todr_settime = rtcsettime;
5104b3ced23Skettenis 	rtc_todr.todr_quality = 0;
5114b3ced23Skettenis 	todr_attach(&rtc_todr);
512f5df1827Smickey }
513f5df1827Smickey 
514f5df1827Smickey void
setstatclockrate(int arg)5152bb6026aSjsg setstatclockrate(int arg)
516f5df1827Smickey {
5174237b508Sgerhard 	if (initclock_func == i8254_initclocks) {
518f5df1827Smickey 		if (arg == stathz)
5194237b508Sgerhard 			mc146818_write(NULL, MC_REGA,
5204237b508Sgerhard 			    MC_BASE_32_KHz | MC_RATE_128_Hz);
521f5df1827Smickey 		else
5224237b508Sgerhard 			mc146818_write(NULL, MC_REGA,
5234237b508Sgerhard 			    MC_BASE_32_KHz | MC_RATE_1024_Hz);
5244237b508Sgerhard 	}
525f5df1827Smickey }
52607e9c1faSotto 
52707e9c1faSotto void
i8254_inittimecounter(void)52807e9c1faSotto i8254_inittimecounter(void)
52907e9c1faSotto {
53007e9c1faSotto 	tc_init(&i8254_timecounter);
53107e9c1faSotto }
53207e9c1faSotto 
53307e9c1faSotto /*
53407e9c1faSotto  * If we're using lapic to drive hardclock, we can use a simpler
53507e9c1faSotto  * algorithm for the i8254 timecounters.
53607e9c1faSotto  */
53707e9c1faSotto void
i8254_inittimecounter_simple(void)53807e9c1faSotto i8254_inittimecounter_simple(void)
53907e9c1faSotto {
54007e9c1faSotto 	i8254_timecounter.tc_get_timecount = i8254_simple_get_timecount;
54107e9c1faSotto 	i8254_timecounter.tc_counter_mask = 0x7fff;
54207e9c1faSotto         i8254_timecounter.tc_frequency = TIMER_FREQ;
54307e9c1faSotto 
54407e9c1faSotto 	mtx_enter(&timer_mutex);
54594ce6677Sderaadt 	rtclock_tval = 0x8000;
54694ce6677Sderaadt 	i8254_startclock();
54707e9c1faSotto 	mtx_leave(&timer_mutex);
54807e9c1faSotto 
54907e9c1faSotto 	tc_init(&i8254_timecounter);
55007e9c1faSotto }
55107e9c1faSotto 
55294ce6677Sderaadt void
i8254_startclock(void)55394ce6677Sderaadt i8254_startclock(void)
55494ce6677Sderaadt {
55594ce6677Sderaadt 	u_long tval = rtclock_tval;
55694ce6677Sderaadt 
55794ce6677Sderaadt 	outb(IO_TIMER1 + TIMER_MODE, TIMER_SEL0 | TIMER_RATEGEN | TIMER_16BIT);
55894ce6677Sderaadt 	outb(IO_TIMER1 + TIMER_CNTR0, tval & 0xff);
55994ce6677Sderaadt 	outb(IO_TIMER1 + TIMER_CNTR0, tval >> 8);
56094ce6677Sderaadt }
56194ce6677Sderaadt 
56207e9c1faSotto u_int
i8254_simple_get_timecount(struct timecounter * tc)56307e9c1faSotto i8254_simple_get_timecount(struct timecounter *tc)
56407e9c1faSotto {
56507e9c1faSotto 	return (rtclock_tval - gettick());
56607e9c1faSotto }
56707e9c1faSotto 
56807e9c1faSotto u_int
i8254_get_timecount(struct timecounter * tc)56907e9c1faSotto i8254_get_timecount(struct timecounter *tc)
57007e9c1faSotto {
57107e9c1faSotto 	u_char hi, lo;
57207e9c1faSotto 	u_int count;
5730db9c031Skettenis 	u_long s;
57407e9c1faSotto 
5750db9c031Skettenis 	s = intr_disable();
57607e9c1faSotto 
57707e9c1faSotto 	outb(IO_TIMER1+TIMER_MODE, TIMER_SEL0 | TIMER_LATCH);
57807e9c1faSotto 	lo = inb(IO_TIMER1+TIMER_CNTR0);
57907e9c1faSotto 	hi = inb(IO_TIMER1+TIMER_CNTR0);
58007e9c1faSotto 
58107e9c1faSotto 	count = rtclock_tval - ((hi << 8) | lo);
58207e9c1faSotto 
58307e9c1faSotto 	if (count < i8254_lastcount) {
58407e9c1faSotto 		i8254_ticked = 1;
58507e9c1faSotto 		i8254_offset += rtclock_tval;
58607e9c1faSotto 	}
58707e9c1faSotto 	i8254_lastcount = count;
58807e9c1faSotto 	count += i8254_offset;
5890db9c031Skettenis 
5900db9c031Skettenis 	intr_restore(s);
59107e9c1faSotto 
59207e9c1faSotto 	return (count);
59307e9c1faSotto }
594