Home | History | Annotate | Line # | Download | only in isa
clock.c revision 1.4.6.2
      1 /*	$NetBSD: clock.c,v 1.4.6.2 2006/09/14 12:31:22 yamt 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.4.6.2 2006/09/14 12:31:22 yamt 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, 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, 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, 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)
    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, struct cfdata *match, void *aux)
    568 {
    569 	return (!ppi_attached);
    570 }
    571 
    572 void
    573 sysbeepattach(struct device *parent, struct device *self, void *aux)
    574 {
    575 	aprint_naive("\n");
    576 	aprint_normal("\n");
    577 
    578 	ppicookie = ((struct pcppi_attach_args *)aux)->pa_cookie;
    579 	ppi_attached = 1;
    580 }
    581 #endif
    582 
    583 void
    584 sysbeep(int pitch, int period)
    585 {
    586 #if (NPCPPI > 0)
    587 	if (ppi_attached)
    588 		pcppi_bell(ppicookie, pitch, period, 0);
    589 #endif
    590 }
    591 
    592 void
    593 i8254_initclocks(void)
    594 {
    595 
    596 	/*
    597 	 * XXX If you're doing strange things with multiple clocks, you might
    598 	 * want to keep track of clock handlers.
    599 	 */
    600 	(void)isa_intr_establish(NULL, 0, IST_PULSE, IPL_CLOCK,
    601 	    (int (*)(void *))clockintr, 0);
    602 }
    603 
    604 static void
    605 rtcinit(void)
    606 {
    607 	static int first_rtcopen_ever = 1;
    608 
    609 	if (!first_rtcopen_ever)
    610 		return;
    611 	first_rtcopen_ever = 0;
    612 
    613 	mc146818_write(NULL, MC_REGA,			/* XXX softc */
    614 	    MC_BASE_32_KHz | MC_RATE_1024_Hz);
    615 	mc146818_write(NULL, MC_REGB, MC_REGB_24HR);	/* XXX softc */
    616 }
    617 
    618 static int
    619 rtcget(mc_todregs *regs)
    620 {
    621 
    622 	rtcinit();
    623 	if ((mc146818_read(NULL, MC_REGD) & MC_REGD_VRT) == 0) /* XXX softc */
    624 		return (-1);
    625 	MC146818_GETTOD(NULL, regs);			/* XXX softc */
    626 	return (0);
    627 }
    628 
    629 static void
    630 rtcput(mc_todregs *regs)
    631 {
    632 
    633 	rtcinit();
    634 	MC146818_PUTTOD(NULL, regs);			/* XXX softc */
    635 }
    636 
    637 /*
    638  * check whether the CMOS layout is "standard"-like (ie, not PS/2-like),
    639  * to be called at splclock()
    640  */
    641 static int
    642 cmoscheck(void)
    643 {
    644 	int i;
    645 	unsigned short cksum = 0;
    646 
    647 	for (i = 0x10; i <= 0x2d; i++)
    648 		cksum += mc146818_read(NULL, i); /* XXX softc */
    649 
    650 	return (cksum == (mc146818_read(NULL, 0x2e) << 8)
    651 			  + mc146818_read(NULL, 0x2f));
    652 }
    653 
    654 #if NMCA > 0
    655 /*
    656  * Check whether the CMOS layout is PS/2 like, to be called at splclock().
    657  */
    658 static int cmoscheckps2(void);
    659 static int
    660 cmoscheckps2(void)
    661 {
    662 #if 0
    663 	/* Disabled until I find out the CRC checksum algorithm IBM uses */
    664 	int i;
    665 	unsigned short cksum = 0;
    666 
    667 	for (i = 0x10; i <= 0x31; i++)
    668 		cksum += mc146818_read(NULL, i); /* XXX softc */
    669 
    670 	return (cksum == (mc146818_read(NULL, 0x32) << 8)
    671 			  + mc146818_read(NULL, 0x33));
    672 #else
    673 	/* Check 'incorrect checksum' bit of IBM PS/2 Diagnostic Status Byte */
    674 	return ((mc146818_read(NULL, NVRAM_DIAG) & (1<<6)) == 0);
    675 #endif
    676 }
    677 #endif /* NMCA > 0 */
    678 
    679 /*
    680  * patchable to control century byte handling:
    681  * 1: always update
    682  * -1: never touch
    683  * 0: try to figure out itself
    684  */
    685 int rtc_update_century = 0;
    686 
    687 /*
    688  * Expand a two-digit year as read from the clock chip
    689  * into full width.
    690  * Being here, deal with the CMOS century byte.
    691  */
    692 static int centb = NVRAM_CENTURY;
    693 static int
    694 clock_expandyear(int clockyear)
    695 {
    696 	int s, clockcentury, cmoscentury;
    697 
    698 	clockcentury = (clockyear < 70) ? 20 : 19;
    699 	clockyear += 100 * clockcentury;
    700 
    701 	if (rtc_update_century < 0)
    702 		return (clockyear);
    703 
    704 	s = splclock();
    705 	if (cmoscheck())
    706 		cmoscentury = mc146818_read(NULL, NVRAM_CENTURY);
    707 #if NMCA > 0
    708 	else if (MCA_system && cmoscheckps2())
    709 		cmoscentury = mc146818_read(NULL, (centb = 0x37));
    710 #endif
    711 	else
    712 		cmoscentury = 0;
    713 	splx(s);
    714 	if (!cmoscentury) {
    715 #ifdef DIAGNOSTIC
    716 		printf("clock: unknown CMOS layout\n");
    717 #endif
    718 		return (clockyear);
    719 	}
    720 	cmoscentury = bcdtobin(cmoscentury);
    721 
    722 	if (cmoscentury != clockcentury) {
    723 		/* XXX note: saying "century is 20" might confuse the naive. */
    724 		printf("WARNING: NVRAM century is %d but RTC year is %d\n",
    725 		       cmoscentury, clockyear);
    726 
    727 		/* Kludge to roll over century. */
    728 		if ((rtc_update_century > 0) ||
    729 		    ((cmoscentury == 19) && (clockcentury == 20) &&
    730 		     (clockyear == 2000))) {
    731 			printf("WARNING: Setting NVRAM century to %d\n",
    732 			       clockcentury);
    733 			s = splclock();
    734 			mc146818_write(NULL, centb, bintobcd(clockcentury));
    735 			splx(s);
    736 		}
    737 	} else if (cmoscentury == 19 && rtc_update_century == 0)
    738 		rtc_update_century = 1; /* will update later in resettodr() */
    739 
    740 	return (clockyear);
    741 }
    742 
    743 static int
    744 rtc_get_ymdhms(todr_chip_handle_t tch, struct clock_ymdhms *dt)
    745 {
    746 	int s;
    747 	mc_todregs rtclk;
    748 
    749 	s = splclock();
    750 	if (rtcget(&rtclk)) {
    751 		splx(s);
    752 		return -1;
    753 	}
    754 	splx(s);
    755 
    756 	dt->dt_sec = bcdtobin(rtclk[MC_SEC]);
    757 	dt->dt_min = bcdtobin(rtclk[MC_MIN]);
    758 	dt->dt_hour = bcdtobin(rtclk[MC_HOUR]);
    759 	dt->dt_day = bcdtobin(rtclk[MC_DOM]);
    760 	dt->dt_mon = bcdtobin(rtclk[MC_MONTH]);
    761 	dt->dt_year = clock_expandyear(bcdtobin(rtclk[MC_YEAR]));
    762 
    763 	return 0;
    764 }
    765 
    766 static int
    767 rtc_set_ymdhms(todr_chip_handle_t tch, struct clock_ymdhms *dt)
    768 {
    769 	mc_todregs rtclk;
    770 	int century;
    771 	int s;
    772 
    773 	s = splclock();
    774 	if (rtcget(&rtclk))
    775 		memset(&rtclk, 0, sizeof(rtclk));
    776 	splx(s);
    777 
    778 	rtclk[MC_SEC] = bintobcd(dt->dt_sec);
    779 	rtclk[MC_MIN] = bintobcd(dt->dt_min);
    780 	rtclk[MC_HOUR] = bintobcd(dt->dt_hour);
    781 	rtclk[MC_DOW] = dt->dt_wday + 1;
    782 	rtclk[MC_YEAR] = bintobcd(dt->dt_year % 100);
    783 	rtclk[MC_MONTH] = bintobcd(dt->dt_mon);
    784 	rtclk[MC_DOM] = bintobcd(dt->dt_day);
    785 
    786 #ifdef DEBUG_CLOCK
    787 	printf("setclock: %x/%x/%x %x:%x:%x\n", rtclk[MC_YEAR], rtclk[MC_MONTH],
    788 	   rtclk[MC_DOM], rtclk[MC_HOUR], rtclk[MC_MIN], rtclk[MC_SEC]);
    789 #endif
    790 	s = splclock();
    791 	rtcput(&rtclk);
    792 	if (rtc_update_century > 0) {
    793 		century = bintobcd(dt->dt_year / 100);
    794 		mc146818_write(NULL, centb, century); /* XXX softc */
    795 	}
    796 	splx(s);
    797 	return 0;
    798 
    799 }
    800 
    801 static void
    802 rtc_register(void)
    803 {
    804 	static struct todr_chip_handle	tch;
    805 	tch.todr_gettime_ymdhms = rtc_get_ymdhms;
    806 	tch.todr_settime_ymdhms = rtc_set_ymdhms;
    807 	tch.todr_setwen = NULL;
    808 
    809 	todr_attach(&tch);
    810 }
    811 
    812 void
    813 setstatclockrate(int arg)
    814 {
    815 }
    816