Home | History | Annotate | Line # | Download | only in isa
clock.c revision 1.1
      1 /*	$NetBSD: clock.c,v 1.1 2006/09/04 02:16:03 perry 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.1 2006/09/04 02:16:03 perry 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 static 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 static 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 static int timeset;
    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_gettime(todr_chip_handle_t tch, volatile struct timeval *tv)
    747 {
    748 	int s;
    749 	mc_todregs rtclk;
    750 	struct clock_ymdhms dt;
    751 
    752 	s = splclock();
    753 	if (rtcget(&rtclk)) {
    754 		splx(s);
    755 		return -1;
    756 	}
    757 	splx(s);
    758 
    759 	dt.dt_sec = bcdtobin(rtclk[MC_SEC]);
    760 	dt.dt_min = bcdtobin(rtclk[MC_MIN]);
    761 	dt.dt_hour = bcdtobin(rtclk[MC_HOUR]);
    762 	dt.dt_day = bcdtobin(rtclk[MC_DOM]);
    763 	dt.dt_mon = bcdtobin(rtclk[MC_MONTH]);
    764 	dt.dt_year = clock_expandyear(bcdtobin(rtclk[MC_YEAR]));
    765 
    766 	/*
    767 	 * If time_t is 32 bits, then the "End of Time" is
    768 	 * Mon Jan 18 22:14:07 2038 (US/Eastern)
    769 	 * This code copes with RTC's past the end of time if time_t
    770 	 * is an int32 or less. Needed because sometimes RTCs screw
    771 	 * up or are badly set, and that would cause the time to go
    772 	 * negative in the calculation below, which causes Very Bad
    773 	 * Mojo. This at least lets the user boot and fix the problem.
    774 	 * Note the code is self eliminating once time_t goes to 64 bits.
    775 	 */
    776 	if (sizeof(time_t) <= sizeof(int32_t)) {
    777 		if (dt.dt_year >= 2038) {
    778 			return -1;
    779 		}
    780 	}
    781 
    782 	tv->tv_sec = clock_ymdhms_to_secs(&dt) + rtc_offset * 60;
    783 	tv->tv_usec = 0;
    784 	return 0;
    785 }
    786 
    787 static int
    788 rtc_settime(todr_chip_handle_t tch, volatile struct timeval *tvp)
    789 {
    790 	mc_todregs rtclk;
    791 	struct clock_ymdhms dt;
    792 	int century;
    793 	int s;
    794 
    795 	/*
    796 	 * We might have been called by boot() due to a crash early
    797 	 * on.  Don't reset the clock chip in this case.
    798 	 */
    799 	if (!timeset)
    800 		return 0;
    801 
    802 	s = splclock();
    803 	if (rtcget(&rtclk))
    804 		memset(&rtclk, 0, sizeof(rtclk));
    805 	splx(s);
    806 
    807 	clock_secs_to_ymdhms(time_second - rtc_offset * 60, &dt);
    808 
    809 	rtclk[MC_SEC] = bintobcd(dt.dt_sec);
    810 	rtclk[MC_MIN] = bintobcd(dt.dt_min);
    811 	rtclk[MC_HOUR] = bintobcd(dt.dt_hour);
    812 	rtclk[MC_DOW] = dt.dt_wday + 1;
    813 	rtclk[MC_YEAR] = bintobcd(dt.dt_year % 100);
    814 	rtclk[MC_MONTH] = bintobcd(dt.dt_mon);
    815 	rtclk[MC_DOM] = bintobcd(dt.dt_day);
    816 
    817 #ifdef DEBUG_CLOCK
    818 	printf("setclock: %x/%x/%x %x:%x:%x\n", rtclk[MC_YEAR], rtclk[MC_MONTH],
    819 	   rtclk[MC_DOM], rtclk[MC_HOUR], rtclk[MC_MIN], rtclk[MC_SEC]);
    820 #endif
    821 	s = splclock();
    822 	rtcput(&rtclk);
    823 	if (rtc_update_century > 0) {
    824 		century = bintobcd(dt.dt_year / 100);
    825 		mc146818_write(NULL, centb, century); /* XXX softc */
    826 	}
    827 	splx(s);
    828 	return 0;
    829 
    830 }
    831 
    832 static int
    833 rtc_getcal(todr_chip_handle_t tch, int *vp)
    834 {
    835 	return EOPNOTSUPP;
    836 }
    837 
    838 static int
    839 rtc_setcal(todr_chip_handle_t tch, int v)
    840 {
    841 	return EOPNOTSUPP;
    842 }
    843 
    844 static void
    845 rtc_register(void)
    846 {
    847 	static struct todr_chip_handle	tch;
    848 	tch.todr_gettime = rtc_gettime;
    849 	tch.todr_settime = rtc_settime;
    850 	tch.todr_getcal = rtc_getcal;
    851 	tch.todr_setcal = rtc_setcal;
    852 	tch.todr_setwen = NULL;
    853 
    854 	todr_attach(&tch);
    855 }
    856 
    857 void
    858 setstatclockrate(int arg)
    859 {
    860 }
    861