Home | History | Annotate | Line # | Download | only in isa
clock.c revision 1.5
      1 /*	$NetBSD: clock.c,v 1.5 2006/10/12 01:30:44 christos 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 (at) 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.5 2006/10/12 01:30:44 christos 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, int period)
    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