xref: /netbsd-src/sys/arch/x86/isa/clock.c (revision ce2c90c7c172d95d2402a5b3d96d8f8e6d138a21)
1 /*	$NetBSD: clock.c,v 1.6 2006/10/13 10:09:36 hannken Exp $	*/
2 
3 /*-
4  * Copyright (c) 1990 The Regents of the University of California.
5  * All rights reserved.
6  *
7  * This code is derived from software contributed to Berkeley by
8  * William Jolitz and Don Ahn.
9  *
10  * Redistribution and use in source and binary forms, with or without
11  * modification, are permitted provided that the following conditions
12  * are met:
13  * 1. Redistributions of source code must retain the above copyright
14  *    notice, this list of conditions and the following disclaimer.
15  * 2. Redistributions in binary form must reproduce the above copyright
16  *    notice, this list of conditions and the following disclaimer in the
17  *    documentation and/or other materials provided with the distribution.
18  * 3. Neither the name of the University nor the names of its contributors
19  *    may be used to endorse or promote products derived from this software
20  *    without specific prior written permission.
21  *
22  * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
23  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
24  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
25  * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
26  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
27  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
28  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
29  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
30  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
31  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
32  * SUCH DAMAGE.
33  *
34  *	@(#)clock.c	7.2 (Berkeley) 5/12/91
35  */
36 /*-
37  * Copyright (c) 1993, 1994 Charles M. Hannum.
38  *
39  * This code is derived from software contributed to Berkeley by
40  * William Jolitz and Don Ahn.
41  *
42  * Redistribution and use in source and binary forms, with or without
43  * modification, are permitted provided that the following conditions
44  * are met:
45  * 1. Redistributions of source code must retain the above copyright
46  *    notice, this list of conditions and the following disclaimer.
47  * 2. Redistributions in binary form must reproduce the above copyright
48  *    notice, this list of conditions and the following disclaimer in the
49  *    documentation and/or other materials provided with the distribution.
50  * 3. All advertising materials mentioning features or use of this software
51  *    must display the following acknowledgement:
52  *	This product includes software developed by the University of
53  *	California, Berkeley and its contributors.
54  * 4. Neither the name of the University nor the names of its contributors
55  *    may be used to endorse or promote products derived from this software
56  *    without specific prior written permission.
57  *
58  * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
59  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
60  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
61  * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
62  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
63  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
64  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
65  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
66  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
67  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
68  * SUCH DAMAGE.
69  *
70  *	@(#)clock.c	7.2 (Berkeley) 5/12/91
71  */
72 /*
73  * Mach Operating System
74  * Copyright (c) 1991,1990,1989 Carnegie Mellon University
75  * All Rights Reserved.
76  *
77  * Permission to use, copy, modify and distribute this software and its
78  * documentation is hereby granted, provided that both the copyright
79  * notice and this permission notice appear in all copies of the
80  * software, derivative works or modified versions, and any portions
81  * thereof, and that both notices appear in supporting documentation.
82  *
83  * CARNEGIE MELLON ALLOWS FREE USE OF THIS SOFTWARE IN ITS "AS IS"
84  * CONDITION.  CARNEGIE MELLON DISCLAIMS ANY LIABILITY OF ANY KIND FOR
85  * ANY DAMAGES WHATSOEVER RESULTING FROM THE USE OF THIS SOFTWARE.
86  *
87  * Carnegie Mellon requests users of this software to return to
88  *
89  *  Software Distribution Coordinator  or  Software.Distribution@CS.CMU.EDU
90  *  School of Computer Science
91  *  Carnegie Mellon University
92  *  Pittsburgh PA 15213-3890
93  *
94  * any improvements or extensions that they make and grant Carnegie Mellon
95  * the rights to redistribute these changes.
96  */
97 /*
98   Copyright 1988, 1989 by Intel Corporation, Santa Clara, California.
99 
100 		All Rights Reserved
101 
102 Permission to use, copy, modify, and distribute this software and
103 its documentation for any purpose and without fee is hereby
104 granted, provided that the above copyright notice appears in all
105 copies and that both the copyright notice and this permission notice
106 appear in supporting documentation, and that the name of Intel
107 not be used in advertising or publicity pertaining to distribution
108 of the software without specific, written prior permission.
109 
110 INTEL DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE
111 INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS,
112 IN NO EVENT SHALL INTEL BE LIABLE FOR ANY SPECIAL, INDIRECT, OR
113 CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
114 LOSS OF USE, DATA OR PROFITS, WHETHER IN ACTION OF CONTRACT,
115 NEGLIGENCE, OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION
116 WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
117 */
118 
119 /*
120  * Primitive clock interrupt routines.
121  */
122 
123 #include <sys/cdefs.h>
124 __KERNEL_RCSID(0, "$NetBSD: clock.c,v 1.6 2006/10/13 10:09:36 hannken Exp $");
125 
126 /* #define CLOCKDEBUG */
127 /* #define CLOCK_PARANOIA */
128 
129 #include "opt_multiprocessor.h"
130 #include "opt_ntp.h"
131 
132 #include <sys/param.h>
133 #include <sys/systm.h>
134 #include <sys/time.h>
135 #include <sys/timetc.h>
136 #include <sys/kernel.h>
137 #include <sys/device.h>
138 
139 #include <machine/cpu.h>
140 #include <machine/intr.h>
141 #include <machine/pio.h>
142 #include <machine/cpufunc.h>
143 
144 #include <dev/isa/isareg.h>
145 #include <dev/isa/isavar.h>
146 #include <dev/ic/mc146818reg.h>
147 #include <dev/ic/i8253reg.h>
148 #include <i386/isa/nvram.h>
149 #include <x86/x86/tsc.h>
150 #include <dev/clock_subr.h>
151 #include <machine/specialreg.h>
152 
153 #include "config_time.h"		/* for CONFIG_TIME */
154 
155 #ifndef __x86_64__
156 #include "mca.h"
157 #endif
158 #if NMCA > 0
159 #include <machine/mca_machdep.h>	/* for MCA_system */
160 #endif
161 
162 #include "pcppi.h"
163 #if (NPCPPI > 0)
164 #include <dev/isa/pcppivar.h>
165 
166 int sysbeepmatch(struct device *, struct cfdata *, void *);
167 void sysbeepattach(struct device *, struct device *, void *);
168 
169 CFATTACH_DECL(sysbeep, sizeof(struct device),
170     sysbeepmatch, sysbeepattach, NULL, NULL);
171 
172 static int ppi_attached;
173 static pcppi_tag_t ppicookie;
174 #endif /* PCPPI */
175 
176 #ifdef __x86_64__
177 #define READ_FLAGS()	read_rflags()
178 #define WRITE_FLAGS(x)	write_rflags(x)
179 #else /* i386 architecture processor */
180 #define READ_FLAGS()	read_eflags()
181 #define WRITE_FLAGS(x)	write_eflags(x)
182 #endif
183 
184 #ifdef CLOCKDEBUG
185 int clock_debug = 0;
186 #define DPRINTF(arg) if (clock_debug) printf arg
187 #else
188 #define DPRINTF(arg)
189 #endif
190 
191 int		gettick(void);
192 void		sysbeep(int, int);
193 static void     tickle_tc(void);
194 
195 static int	clockintr(void *, struct intrframe);
196 static void	rtcinit(void);
197 static int	rtcget(mc_todregs *);
198 static void	rtcput(mc_todregs *);
199 
200 static int	cmoscheck(void);
201 
202 static int	clock_expandyear(int);
203 
204 static inline int gettick_broken_latch(void);
205 
206 static volatile uint32_t i8254_lastcount;
207 static volatile uint32_t i8254_offset;
208 static volatile int i8254_ticked;
209 
210 static struct simplelock tmr_lock = SIMPLELOCK_INITIALIZER;  /* protect TC timer variables */
211 
212 inline u_int mc146818_read(void *, u_int);
213 inline void mc146818_write(void *, u_int, u_int);
214 
215 u_int i8254_get_timecount(struct timecounter *);
216 static void rtc_register(void);
217 
218 static struct timecounter i8254_timecounter = {
219 	i8254_get_timecount,	/* get_timecount */
220 	0,			/* no poll_pps */
221 	~0u,			/* counter_mask */
222 	TIMER_FREQ,		/* frequency */
223 	"i8254",		/* name */
224 	100,			/* quality */
225 	NULL,			/* prev */
226 	NULL,			/* next */
227 };
228 
229 /* XXX use sc? */
230 inline u_int
231 mc146818_read(void *sc __unused, u_int reg)
232 {
233 
234 	outb(IO_RTC, reg);
235 	return (inb(IO_RTC+1));
236 }
237 
238 /* XXX use sc? */
239 inline void
240 mc146818_write(void *sc __unused, u_int reg, u_int datum)
241 {
242 
243 	outb(IO_RTC, reg);
244 	outb(IO_RTC+1, datum);
245 }
246 
247 u_long rtclock_tval;		/* i8254 reload value for countdown */
248 int    rtclock_init = 0;
249 
250 int clock_broken_latch = 0;
251 
252 #ifdef CLOCK_PARANOIA
253 static int ticks[6];
254 #endif
255 /*
256  * i8254 latch check routine:
257  *     National Geode (formerly Cyrix MediaGX) has a serious bug in
258  *     its built-in i8254-compatible clock module.
259  *     machdep sets the variable 'clock_broken_latch' to indicate it.
260  */
261 
262 int
263 gettick_broken_latch(void)
264 {
265 	u_long flags;
266 	int v1, v2, v3;
267 	int w1, w2, w3;
268 
269 	/* Don't want someone screwing with the counter while we're here. */
270 	flags = READ_FLAGS();
271 	disable_intr();
272 
273 	v1 = inb(IO_TIMER1+TIMER_CNTR0);
274 	v1 |= inb(IO_TIMER1+TIMER_CNTR0) << 8;
275 	v2 = inb(IO_TIMER1+TIMER_CNTR0);
276 	v2 |= inb(IO_TIMER1+TIMER_CNTR0) << 8;
277 	v3 = inb(IO_TIMER1+TIMER_CNTR0);
278 	v3 |= inb(IO_TIMER1+TIMER_CNTR0) << 8;
279 
280 	WRITE_FLAGS(flags);
281 
282 #ifdef CLOCK_PARANOIA
283 	if (clock_debug) {
284 		ticks[0] = ticks[3];
285 		ticks[1] = ticks[4];
286 		ticks[2] = ticks[5];
287 		ticks[3] = v1;
288 		ticks[4] = v2;
289 		ticks[5] = v3;
290 	}
291 #endif
292 
293 	if (v1 >= v2 && v2 >= v3 && v1 - v3 < 0x200)
294 		return (v2);
295 
296 #define _swap_val(a, b) do { \
297 	int c = a; \
298 	a = b; \
299 	b = c; \
300 } while (0)
301 
302 	/*
303 	 * sort v1 v2 v3
304 	 */
305 	if (v1 < v2)
306 		_swap_val(v1, v2);
307 	if (v2 < v3)
308 		_swap_val(v2, v3);
309 	if (v1 < v2)
310 		_swap_val(v1, v2);
311 
312 	/*
313 	 * compute the middle value
314 	 */
315 
316 	if (v1 - v3 < 0x200)
317 		return (v2);
318 
319 	w1 = v2 - v3;
320 	w2 = v3 - v1 + rtclock_tval;
321 	w3 = v1 - v2;
322 	if (w1 >= w2) {
323 		if (w1 >= w3)
324 		        return (v1);
325 	} else {
326 		if (w2 >= w3)
327 			return (v2);
328 	}
329 	return (v3);
330 }
331 
332 /* minimal initialization, enough for delay() */
333 void
334 initrtclock(u_long freq)
335 {
336 	u_long tval;
337 	/*
338 	 * Compute timer_count, the count-down count the timer will be
339 	 * set to.  Also, correctly round
340 	 * this by carrying an extra bit through the division.
341 	 */
342 	tval = (freq * 2) / (u_long) hz;
343 	tval = (tval / 2) + (tval & 0x1);
344 
345 	/* initialize 8254 clock */
346 	outb(IO_TIMER1+TIMER_MODE, TIMER_SEL0|TIMER_RATEGEN|TIMER_16BIT);
347 
348 	/* Correct rounding will buy us a better precision in timekeeping */
349 	outb(IO_TIMER1+TIMER_CNTR0, tval % 256);
350 	outb(IO_TIMER1+TIMER_CNTR0, tval / 256);
351 
352 	rtclock_tval = tval ? tval : 0xFFFF;
353 	rtclock_init = 1;
354 }
355 
356 void
357 startrtclock(void)
358 {
359 	int s;
360 
361 	if (!rtclock_init)
362 		initrtclock(TIMER_FREQ);
363 
364 	/* Check diagnostic status */
365 	if ((s = mc146818_read(NULL, NVRAM_DIAG)) != 0) { /* XXX softc */
366 		char bits[128];
367 		printf("RTC BIOS diagnostic error %s\n",
368 		    bitmask_snprintf(s, NVRAM_DIAG_BITS, bits, sizeof(bits)));
369 	}
370 
371 	tc_init(&i8254_timecounter);
372 
373 #if defined(I586_CPU) || defined(I686_CPU) || defined(__x86_64__)
374 	init_TSC();
375 #endif
376 
377 	rtc_register();
378 }
379 
380 
381 static void
382 tickle_tc(void)
383 {
384 #if defined(MULTIPROCESSOR)
385 	struct cpu_info *ci = curcpu();
386 	/*
387 	 * If we are not the primary CPU, we're not allowed to do
388 	 * any more work.
389 	 */
390 	if (CPU_IS_PRIMARY(ci) == 0)
391 		return;
392 #endif
393 	if (rtclock_tval && timecounter->tc_get_timecount == i8254_get_timecount) {
394 		simple_lock(&tmr_lock);
395 		if (i8254_ticked)
396 			i8254_ticked    = 0;
397 		else {
398 			i8254_offset   += rtclock_tval;
399 			i8254_lastcount = 0;
400 		}
401 		simple_unlock(&tmr_lock);
402 	}
403 
404 }
405 
406 static int
407 clockintr(void *arg __unused, struct intrframe frame)
408 {
409 	tickle_tc();
410 
411 	hardclock((struct clockframe *)&frame);
412 
413 #if NMCA > 0
414 	if (MCA_system) {
415 		/* Reset PS/2 clock interrupt by asserting bit 7 of port 0x61 */
416 		outb(0x61, inb(0x61) | 0x80);
417 	}
418 #endif
419 	return -1;
420 }
421 
422 u_int
423 i8254_get_timecount(struct timecounter *tc __unused)
424 {
425 	u_int count;
426 	u_char high, low;
427 	u_long flags;
428 
429 	/* Don't want someone screwing with the counter while we're here. */
430 	flags = READ_FLAGS();
431 	disable_intr();
432 
433 	simple_lock(&tmr_lock);
434 
435 	/* Select timer0 and latch counter value. */
436 	outb(IO_TIMER1 + TIMER_MODE, TIMER_SEL0 | TIMER_LATCH);
437 
438 	low = inb(IO_TIMER1 + TIMER_CNTR0);
439 	high = inb(IO_TIMER1 + TIMER_CNTR0);
440 	count = rtclock_tval - ((high << 8) | low);
441 
442 	if (rtclock_tval && (count < i8254_lastcount || !i8254_ticked)) {
443 		i8254_ticked = 1;
444 		i8254_offset += rtclock_tval;
445 	}
446 
447 	i8254_lastcount = count;
448 	count += i8254_offset;
449 
450 	simple_unlock(&tmr_lock);
451 
452 	WRITE_FLAGS(flags);
453 	return (count);
454 }
455 
456 int
457 gettick(void)
458 {
459 	u_long flags;
460 	u_char lo, hi;
461 
462 	if (clock_broken_latch)
463 		return (gettick_broken_latch());
464 
465 	/* Don't want someone screwing with the counter while we're here. */
466 	flags = READ_FLAGS();
467 	disable_intr();
468 	/* Select counter 0 and latch it. */
469 	outb(IO_TIMER1+TIMER_MODE, TIMER_SEL0 | TIMER_LATCH);
470 	lo = inb(IO_TIMER1+TIMER_CNTR0);
471 	hi = inb(IO_TIMER1+TIMER_CNTR0);
472 	WRITE_FLAGS(flags);
473 	return ((hi << 8) | lo);
474 }
475 
476 /*
477  * Wait approximately `n' microseconds.
478  * Relies on timer 1 counting down from (TIMER_FREQ / hz) at TIMER_FREQ Hz.
479  * Note: timer had better have been programmed before this is first used!
480  * (Note that we use `rate generator' mode, which counts at 1:1; `square
481  * wave' mode counts at 2:1).
482  * Don't rely on this being particularly accurate.
483  */
484 void
485 i8254_delay(int n)
486 {
487 	int delay_tick, odelay_tick;
488 	static const int delaytab[26] = {
489 		 0,  2,  3,  4,  5,  6,  7,  9, 10, 11,
490 		12, 13, 15, 16, 17, 18, 19, 21, 22, 23,
491 		24, 25, 27, 28, 29, 30,
492 	};
493 
494 	/* allow DELAY() to be used before startrtclock() */
495 	if (!rtclock_init)
496 		initrtclock(TIMER_FREQ);
497 
498 	/*
499 	 * Read the counter first, so that the rest of the setup overhead is
500 	 * counted.
501 	 */
502 	odelay_tick = gettick();
503 
504 	if (n <= 25)
505 		n = delaytab[n];
506 	else {
507 #ifdef __GNUC__
508 		/*
509 		 * Calculate ((n * TIMER_FREQ) / 1e6) using explicit assembler
510 		 * code so we can take advantage of the intermediate 64-bit
511 		 * quantity to prevent loss of significance.
512 		 */
513 		int m;
514 		__asm volatile("mul %3"
515 				 : "=a" (n), "=d" (m)
516 				 : "0" (n), "r" (TIMER_FREQ));
517 		__asm volatile("div %4"
518 				 : "=a" (n), "=d" (m)
519 				 : "0" (n), "1" (m), "r" (1000000));
520 #else
521 		/*
522 		 * Calculate ((n * TIMER_FREQ) / 1e6) without using floating
523 		 * point and without any avoidable overflows.
524 		 */
525 		int sec = n / 1000000,
526 		    usec = n % 1000000;
527 		n = sec * TIMER_FREQ +
528 		    usec * (TIMER_FREQ / 1000000) +
529 		    usec * ((TIMER_FREQ % 1000000) / 1000) / 1000 +
530 		    usec * (TIMER_FREQ % 1000) / 1000000;
531 #endif
532 	}
533 
534 	while (n > 0) {
535 #ifdef CLOCK_PARANOIA
536 		int delta;
537 		delay_tick = gettick();
538 		if (delay_tick > odelay_tick)
539 			delta = rtclock_tval - (delay_tick - odelay_tick);
540 		else
541 			delta = odelay_tick - delay_tick;
542 		if (delta < 0 || delta >= rtclock_tval / 2) {
543 			DPRINTF(("delay: ignore ticks %.4x-%.4x",
544 				 odelay_tick, delay_tick));
545 			if (clock_broken_latch) {
546 				DPRINTF(("  (%.4x %.4x %.4x %.4x %.4x %.4x)\n",
547 				         ticks[0], ticks[1], ticks[2],
548 				         ticks[3], ticks[4], ticks[5]));
549 			} else {
550 				DPRINTF(("\n"));
551 			}
552 		} else
553 			n -= delta;
554 #else
555 		delay_tick = gettick();
556 		if (delay_tick > odelay_tick)
557 			n -= rtclock_tval - (delay_tick - odelay_tick);
558 		else
559 			n -= odelay_tick - delay_tick;
560 #endif
561 		odelay_tick = delay_tick;
562 	}
563 }
564 
565 #if (NPCPPI > 0)
566 int
567 sysbeepmatch(struct device *parent __unused, struct cfdata *match __unused,
568     void *aux __unused)
569 {
570 	return (!ppi_attached);
571 }
572 
573 void
574 sysbeepattach(struct device *parent __unused, struct device *self __unused,
575     void *aux)
576 {
577 	aprint_naive("\n");
578 	aprint_normal("\n");
579 
580 	ppicookie = ((struct pcppi_attach_args *)aux)->pa_cookie;
581 	ppi_attached = 1;
582 }
583 #endif
584 
585 void
586 sysbeep(int pitch __unused, int period __unused)
587 {
588 #if (NPCPPI > 0)
589 	if (ppi_attached)
590 		pcppi_bell(ppicookie, pitch, period, 0);
591 #endif
592 }
593 
594 void
595 i8254_initclocks(void)
596 {
597 
598 	/*
599 	 * XXX If you're doing strange things with multiple clocks, you might
600 	 * want to keep track of clock handlers.
601 	 */
602 	(void)isa_intr_establish(NULL, 0, IST_PULSE, IPL_CLOCK,
603 	    (int (*)(void *))clockintr, 0);
604 }
605 
606 static void
607 rtcinit(void)
608 {
609 	static int first_rtcopen_ever = 1;
610 
611 	if (!first_rtcopen_ever)
612 		return;
613 	first_rtcopen_ever = 0;
614 
615 	mc146818_write(NULL, MC_REGA,			/* XXX softc */
616 	    MC_BASE_32_KHz | MC_RATE_1024_Hz);
617 	mc146818_write(NULL, MC_REGB, MC_REGB_24HR);	/* XXX softc */
618 }
619 
620 static int
621 rtcget(mc_todregs *regs)
622 {
623 
624 	rtcinit();
625 	if ((mc146818_read(NULL, MC_REGD) & MC_REGD_VRT) == 0) /* XXX softc */
626 		return (-1);
627 	MC146818_GETTOD(NULL, regs);			/* XXX softc */
628 	return (0);
629 }
630 
631 static void
632 rtcput(mc_todregs *regs)
633 {
634 
635 	rtcinit();
636 	MC146818_PUTTOD(NULL, regs);			/* XXX softc */
637 }
638 
639 /*
640  * check whether the CMOS layout is "standard"-like (ie, not PS/2-like),
641  * to be called at splclock()
642  */
643 static int
644 cmoscheck(void)
645 {
646 	int i;
647 	unsigned short cksum = 0;
648 
649 	for (i = 0x10; i <= 0x2d; i++)
650 		cksum += mc146818_read(NULL, i); /* XXX softc */
651 
652 	return (cksum == (mc146818_read(NULL, 0x2e) << 8)
653 			  + mc146818_read(NULL, 0x2f));
654 }
655 
656 #if NMCA > 0
657 /*
658  * Check whether the CMOS layout is PS/2 like, to be called at splclock().
659  */
660 static int cmoscheckps2(void);
661 static int
662 cmoscheckps2(void)
663 {
664 #if 0
665 	/* Disabled until I find out the CRC checksum algorithm IBM uses */
666 	int i;
667 	unsigned short cksum = 0;
668 
669 	for (i = 0x10; i <= 0x31; i++)
670 		cksum += mc146818_read(NULL, i); /* XXX softc */
671 
672 	return (cksum == (mc146818_read(NULL, 0x32) << 8)
673 			  + mc146818_read(NULL, 0x33));
674 #else
675 	/* Check 'incorrect checksum' bit of IBM PS/2 Diagnostic Status Byte */
676 	return ((mc146818_read(NULL, NVRAM_DIAG) & (1<<6)) == 0);
677 #endif
678 }
679 #endif /* NMCA > 0 */
680 
681 /*
682  * patchable to control century byte handling:
683  * 1: always update
684  * -1: never touch
685  * 0: try to figure out itself
686  */
687 int rtc_update_century = 0;
688 
689 /*
690  * Expand a two-digit year as read from the clock chip
691  * into full width.
692  * Being here, deal with the CMOS century byte.
693  */
694 static int centb = NVRAM_CENTURY;
695 static int
696 clock_expandyear(int clockyear)
697 {
698 	int s, clockcentury, cmoscentury;
699 
700 	clockcentury = (clockyear < 70) ? 20 : 19;
701 	clockyear += 100 * clockcentury;
702 
703 	if (rtc_update_century < 0)
704 		return (clockyear);
705 
706 	s = splclock();
707 	if (cmoscheck())
708 		cmoscentury = mc146818_read(NULL, NVRAM_CENTURY);
709 #if NMCA > 0
710 	else if (MCA_system && cmoscheckps2())
711 		cmoscentury = mc146818_read(NULL, (centb = 0x37));
712 #endif
713 	else
714 		cmoscentury = 0;
715 	splx(s);
716 	if (!cmoscentury) {
717 #ifdef DIAGNOSTIC
718 		printf("clock: unknown CMOS layout\n");
719 #endif
720 		return (clockyear);
721 	}
722 	cmoscentury = bcdtobin(cmoscentury);
723 
724 	if (cmoscentury != clockcentury) {
725 		/* XXX note: saying "century is 20" might confuse the naive. */
726 		printf("WARNING: NVRAM century is %d but RTC year is %d\n",
727 		       cmoscentury, clockyear);
728 
729 		/* Kludge to roll over century. */
730 		if ((rtc_update_century > 0) ||
731 		    ((cmoscentury == 19) && (clockcentury == 20) &&
732 		     (clockyear == 2000))) {
733 			printf("WARNING: Setting NVRAM century to %d\n",
734 			       clockcentury);
735 			s = splclock();
736 			mc146818_write(NULL, centb, bintobcd(clockcentury));
737 			splx(s);
738 		}
739 	} else if (cmoscentury == 19 && rtc_update_century == 0)
740 		rtc_update_century = 1; /* will update later in resettodr() */
741 
742 	return (clockyear);
743 }
744 
745 static int
746 rtc_get_ymdhms(todr_chip_handle_t tch __unused, struct clock_ymdhms *dt)
747 {
748 	int s;
749 	mc_todregs rtclk;
750 
751 	s = splclock();
752 	if (rtcget(&rtclk)) {
753 		splx(s);
754 		return -1;
755 	}
756 	splx(s);
757 
758 	dt->dt_sec = bcdtobin(rtclk[MC_SEC]);
759 	dt->dt_min = bcdtobin(rtclk[MC_MIN]);
760 	dt->dt_hour = bcdtobin(rtclk[MC_HOUR]);
761 	dt->dt_day = bcdtobin(rtclk[MC_DOM]);
762 	dt->dt_mon = bcdtobin(rtclk[MC_MONTH]);
763 	dt->dt_year = clock_expandyear(bcdtobin(rtclk[MC_YEAR]));
764 
765 	return 0;
766 }
767 
768 static int
769 rtc_set_ymdhms(todr_chip_handle_t tch __unused, struct clock_ymdhms *dt)
770 {
771 	mc_todregs rtclk;
772 	int century;
773 	int s;
774 
775 	s = splclock();
776 	if (rtcget(&rtclk))
777 		memset(&rtclk, 0, sizeof(rtclk));
778 	splx(s);
779 
780 	rtclk[MC_SEC] = bintobcd(dt->dt_sec);
781 	rtclk[MC_MIN] = bintobcd(dt->dt_min);
782 	rtclk[MC_HOUR] = bintobcd(dt->dt_hour);
783 	rtclk[MC_DOW] = dt->dt_wday + 1;
784 	rtclk[MC_YEAR] = bintobcd(dt->dt_year % 100);
785 	rtclk[MC_MONTH] = bintobcd(dt->dt_mon);
786 	rtclk[MC_DOM] = bintobcd(dt->dt_day);
787 
788 #ifdef DEBUG_CLOCK
789 	printf("setclock: %x/%x/%x %x:%x:%x\n", rtclk[MC_YEAR], rtclk[MC_MONTH],
790 	   rtclk[MC_DOM], rtclk[MC_HOUR], rtclk[MC_MIN], rtclk[MC_SEC]);
791 #endif
792 	s = splclock();
793 	rtcput(&rtclk);
794 	if (rtc_update_century > 0) {
795 		century = bintobcd(dt->dt_year / 100);
796 		mc146818_write(NULL, centb, century); /* XXX softc */
797 	}
798 	splx(s);
799 	return 0;
800 
801 }
802 
803 static void
804 rtc_register(void)
805 {
806 	static struct todr_chip_handle	tch;
807 	tch.todr_gettime_ymdhms = rtc_get_ymdhms;
808 	tch.todr_settime_ymdhms = rtc_set_ymdhms;
809 	tch.todr_setwen = NULL;
810 
811 	todr_attach(&tch);
812 }
813 
814 void
815 setstatclockrate(int arg __unused)
816 {
817 }
818