Home | History | Annotate | Line # | Download | only in isa
clock.c revision 1.9
      1 /*	$NetBSD: clock.c,v 1.9 2007/07/09 20:52:37 ad 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.9 2007/07/09 20:52:37 ad 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 #include <sys/mutex.h>
    139 
    140 #include <machine/cpu.h>
    141 #include <machine/intr.h>
    142 #include <machine/pio.h>
    143 #include <machine/cpufunc.h>
    144 
    145 #include <dev/isa/isareg.h>
    146 #include <dev/isa/isavar.h>
    147 #include <dev/ic/mc146818reg.h>
    148 #include <dev/ic/i8253reg.h>
    149 #include <i386/isa/nvram.h>
    150 #include <x86/x86/tsc.h>
    151 #include <dev/clock_subr.h>
    152 #include <machine/specialreg.h>
    153 
    154 #include "config_time.h"		/* for CONFIG_TIME */
    155 
    156 #ifndef __x86_64__
    157 #include "mca.h"
    158 #endif
    159 #if NMCA > 0
    160 #include <machine/mca_machdep.h>	/* for MCA_system */
    161 #endif
    162 
    163 #include "pcppi.h"
    164 #if (NPCPPI > 0)
    165 #include <dev/isa/pcppivar.h>
    166 
    167 int sysbeepmatch(struct device *, struct cfdata *, void *);
    168 void sysbeepattach(struct device *, struct device *, void *);
    169 
    170 CFATTACH_DECL(sysbeep, sizeof(struct device),
    171     sysbeepmatch, sysbeepattach, NULL, NULL);
    172 
    173 static int ppi_attached;
    174 static pcppi_tag_t ppicookie;
    175 #endif /* PCPPI */
    176 
    177 #ifdef __x86_64__
    178 #define READ_FLAGS()	read_rflags()
    179 #define WRITE_FLAGS(x)	write_rflags(x)
    180 #else /* i386 architecture processor */
    181 #define READ_FLAGS()	read_eflags()
    182 #define WRITE_FLAGS(x)	write_eflags(x)
    183 #endif
    184 
    185 #ifdef CLOCKDEBUG
    186 int clock_debug = 0;
    187 #define DPRINTF(arg) if (clock_debug) printf arg
    188 #else
    189 #define DPRINTF(arg)
    190 #endif
    191 
    192 int		gettick(void);
    193 void		sysbeep(int, int);
    194 static void     tickle_tc(void);
    195 
    196 static int	clockintr(void *, struct intrframe *);
    197 static void	rtcinit(void);
    198 static int	rtcget(mc_todregs *);
    199 static void	rtcput(mc_todregs *);
    200 
    201 static int	cmoscheck(void);
    202 
    203 static int	clock_expandyear(int);
    204 
    205 static inline int gettick_broken_latch(void);
    206 
    207 static volatile uint32_t i8254_lastcount;
    208 static volatile uint32_t i8254_offset;
    209 static volatile int i8254_ticked;
    210 
    211 /* to protect TC timer variables */
    212 static __cpu_simple_lock_t tmr_lock = __SIMPLELOCK_UNLOCKED;
    213 
    214 inline u_int mc146818_read(void *, u_int);
    215 inline void mc146818_write(void *, u_int, u_int);
    216 
    217 u_int i8254_get_timecount(struct timecounter *);
    218 static void rtc_register(void);
    219 
    220 static struct timecounter i8254_timecounter = {
    221 	i8254_get_timecount,	/* get_timecount */
    222 	0,			/* no poll_pps */
    223 	~0u,			/* counter_mask */
    224 	TIMER_FREQ,		/* frequency */
    225 	"i8254",		/* name */
    226 	100,			/* quality */
    227 	NULL,			/* prev */
    228 	NULL,			/* next */
    229 };
    230 
    231 /* XXX use sc? */
    232 inline u_int
    233 mc146818_read(void *sc, u_int reg)
    234 {
    235 
    236 	outb(IO_RTC, reg);
    237 	return (inb(IO_RTC+1));
    238 }
    239 
    240 /* XXX use sc? */
    241 inline void
    242 mc146818_write(void *sc, u_int reg, u_int datum)
    243 {
    244 
    245 	outb(IO_RTC, reg);
    246 	outb(IO_RTC+1, datum);
    247 }
    248 
    249 u_long rtclock_tval;		/* i8254 reload value for countdown */
    250 int    rtclock_init = 0;
    251 
    252 int clock_broken_latch = 0;
    253 
    254 #ifdef CLOCK_PARANOIA
    255 static int ticks[6];
    256 #endif
    257 /*
    258  * i8254 latch check routine:
    259  *     National Geode (formerly Cyrix MediaGX) has a serious bug in
    260  *     its built-in i8254-compatible clock module.
    261  *     machdep sets the variable 'clock_broken_latch' to indicate it.
    262  */
    263 
    264 int
    265 gettick_broken_latch(void)
    266 {
    267 	u_long flags;
    268 	int v1, v2, v3;
    269 	int w1, w2, w3;
    270 
    271 	/* Don't want someone screwing with the counter while we're here. */
    272 	flags = READ_FLAGS();
    273 	disable_intr();
    274 
    275 	v1 = inb(IO_TIMER1+TIMER_CNTR0);
    276 	v1 |= inb(IO_TIMER1+TIMER_CNTR0) << 8;
    277 	v2 = inb(IO_TIMER1+TIMER_CNTR0);
    278 	v2 |= inb(IO_TIMER1+TIMER_CNTR0) << 8;
    279 	v3 = inb(IO_TIMER1+TIMER_CNTR0);
    280 	v3 |= inb(IO_TIMER1+TIMER_CNTR0) << 8;
    281 
    282 	WRITE_FLAGS(flags);
    283 
    284 #ifdef CLOCK_PARANOIA
    285 	if (clock_debug) {
    286 		ticks[0] = ticks[3];
    287 		ticks[1] = ticks[4];
    288 		ticks[2] = ticks[5];
    289 		ticks[3] = v1;
    290 		ticks[4] = v2;
    291 		ticks[5] = v3;
    292 	}
    293 #endif
    294 
    295 	if (v1 >= v2 && v2 >= v3 && v1 - v3 < 0x200)
    296 		return (v2);
    297 
    298 #define _swap_val(a, b) do { \
    299 	int c = a; \
    300 	a = b; \
    301 	b = c; \
    302 } while (0)
    303 
    304 	/*
    305 	 * sort v1 v2 v3
    306 	 */
    307 	if (v1 < v2)
    308 		_swap_val(v1, v2);
    309 	if (v2 < v3)
    310 		_swap_val(v2, v3);
    311 	if (v1 < v2)
    312 		_swap_val(v1, v2);
    313 
    314 	/*
    315 	 * compute the middle value
    316 	 */
    317 
    318 	if (v1 - v3 < 0x200)
    319 		return (v2);
    320 
    321 	w1 = v2 - v3;
    322 	w2 = v3 - v1 + rtclock_tval;
    323 	w3 = v1 - v2;
    324 	if (w1 >= w2) {
    325 		if (w1 >= w3)
    326 		        return (v1);
    327 	} else {
    328 		if (w2 >= w3)
    329 			return (v2);
    330 	}
    331 	return (v3);
    332 }
    333 
    334 /* minimal initialization, enough for delay() */
    335 void
    336 initrtclock(u_long freq)
    337 {
    338 	u_long tval;
    339 
    340 	/*
    341 	 * Compute timer_count, the count-down count the timer will be
    342 	 * set to.  Also, correctly round
    343 	 * this by carrying an extra bit through the division.
    344 	 */
    345 	tval = (freq * 2) / (u_long) hz;
    346 	tval = (tval / 2) + (tval & 0x1);
    347 
    348 	/* initialize 8254 clock */
    349 	outb(IO_TIMER1+TIMER_MODE, TIMER_SEL0|TIMER_RATEGEN|TIMER_16BIT);
    350 
    351 	/* Correct rounding will buy us a better precision in timekeeping */
    352 	outb(IO_TIMER1+TIMER_CNTR0, tval % 256);
    353 	outb(IO_TIMER1+TIMER_CNTR0, tval / 256);
    354 
    355 	rtclock_tval = tval ? tval : 0xFFFF;
    356 	rtclock_init = 1;
    357 }
    358 
    359 void
    360 startrtclock(void)
    361 {
    362 	int s;
    363 
    364 	if (!rtclock_init)
    365 		initrtclock(TIMER_FREQ);
    366 
    367 	/* Check diagnostic status */
    368 	if ((s = mc146818_read(NULL, NVRAM_DIAG)) != 0) { /* XXX softc */
    369 		char bits[128];
    370 		printf("RTC BIOS diagnostic error %s\n",
    371 		    bitmask_snprintf(s, NVRAM_DIAG_BITS, bits, sizeof(bits)));
    372 	}
    373 
    374 	tc_init(&i8254_timecounter);
    375 
    376 #if defined(I586_CPU) || defined(I686_CPU) || defined(__x86_64__)
    377 	init_TSC();
    378 #endif
    379 
    380 	rtc_register();
    381 }
    382 
    383 /*
    384  * Must be called at splclock().
    385  */
    386 static void
    387 tickle_tc(void)
    388 {
    389 #if defined(MULTIPROCESSOR)
    390 	struct cpu_info *ci = curcpu();
    391 	/*
    392 	 * If we are not the primary CPU, we're not allowed to do
    393 	 * any more work.
    394 	 */
    395 	if (CPU_IS_PRIMARY(ci) == 0)
    396 		return;
    397 #endif
    398 	if (rtclock_tval && timecounter->tc_get_timecount == i8254_get_timecount) {
    399 		__cpu_simple_lock(&tmr_lock);
    400 		if (i8254_ticked)
    401 			i8254_ticked    = 0;
    402 		else {
    403 			i8254_offset   += rtclock_tval;
    404 			i8254_lastcount = 0;
    405 		}
    406 		__cpu_simple_unlock(&tmr_lock);
    407 	}
    408 
    409 }
    410 
    411 static int
    412 clockintr(void *arg, struct intrframe *frame)
    413 {
    414 	tickle_tc();
    415 
    416 	hardclock((struct clockframe *)frame);
    417 
    418 #if NMCA > 0
    419 	if (MCA_system) {
    420 		/* Reset PS/2 clock interrupt by asserting bit 7 of port 0x61 */
    421 		outb(0x61, inb(0x61) | 0x80);
    422 	}
    423 #endif
    424 	return -1;
    425 }
    426 
    427 u_int
    428 i8254_get_timecount(struct timecounter *tc)
    429 {
    430 	u_int count;
    431 	u_char high, low;
    432 	u_long flags;
    433 
    434 	/* Don't want someone screwing with the counter while we're here. */
    435 	flags = READ_FLAGS();
    436 	disable_intr();
    437 	__cpu_simple_lock(&tmr_lock);
    438 
    439 	/* Select timer0 and latch counter value. */
    440 	outb(IO_TIMER1 + TIMER_MODE, TIMER_SEL0 | TIMER_LATCH);
    441 
    442 	low = inb(IO_TIMER1 + TIMER_CNTR0);
    443 	high = inb(IO_TIMER1 + TIMER_CNTR0);
    444 	count = rtclock_tval - ((high << 8) | low);
    445 
    446 	if (rtclock_tval && (count < i8254_lastcount || !i8254_ticked)) {
    447 		i8254_ticked = 1;
    448 		i8254_offset += rtclock_tval;
    449 	}
    450 
    451 	i8254_lastcount = count;
    452 	count += i8254_offset;
    453 
    454 	__cpu_simple_unlock(&tmr_lock);
    455 	WRITE_FLAGS(flags);
    456 
    457 	return (count);
    458 }
    459 
    460 int
    461 gettick(void)
    462 {
    463 	u_long flags;
    464 	u_char lo, hi;
    465 
    466 	if (clock_broken_latch)
    467 		return (gettick_broken_latch());
    468 
    469 	/* Don't want someone screwing with the counter while we're here. */
    470 	flags = READ_FLAGS();
    471 	disable_intr();
    472 	/* Select counter 0 and latch it. */
    473 	outb(IO_TIMER1+TIMER_MODE, TIMER_SEL0 | TIMER_LATCH);
    474 	lo = inb(IO_TIMER1+TIMER_CNTR0);
    475 	hi = inb(IO_TIMER1+TIMER_CNTR0);
    476 	WRITE_FLAGS(flags);
    477 	return ((hi << 8) | lo);
    478 }
    479 
    480 /*
    481  * Wait approximately `n' microseconds.
    482  * Relies on timer 1 counting down from (TIMER_FREQ / hz) at TIMER_FREQ Hz.
    483  * Note: timer had better have been programmed before this is first used!
    484  * (Note that we use `rate generator' mode, which counts at 1:1; `square
    485  * wave' mode counts at 2:1).
    486  * Don't rely on this being particularly accurate.
    487  */
    488 void
    489 i8254_delay(int n)
    490 {
    491 	int delay_tick, odelay_tick;
    492 	static const int delaytab[26] = {
    493 		 0,  2,  3,  4,  5,  6,  7,  9, 10, 11,
    494 		12, 13, 15, 16, 17, 18, 19, 21, 22, 23,
    495 		24, 25, 27, 28, 29, 30,
    496 	};
    497 
    498 	/* allow DELAY() to be used before startrtclock() */
    499 	if (!rtclock_init)
    500 		initrtclock(TIMER_FREQ);
    501 
    502 	/*
    503 	 * Read the counter first, so that the rest of the setup overhead is
    504 	 * counted.
    505 	 */
    506 	odelay_tick = gettick();
    507 
    508 	if (n <= 25)
    509 		n = delaytab[n];
    510 	else {
    511 #ifdef __GNUC__
    512 		/*
    513 		 * Calculate ((n * TIMER_FREQ) / 1e6) using explicit assembler
    514 		 * code so we can take advantage of the intermediate 64-bit
    515 		 * quantity to prevent loss of significance.
    516 		 */
    517 		int m;
    518 		__asm volatile("mul %3"
    519 				 : "=a" (n), "=d" (m)
    520 				 : "0" (n), "r" (TIMER_FREQ));
    521 		__asm volatile("div %4"
    522 				 : "=a" (n), "=d" (m)
    523 				 : "0" (n), "1" (m), "r" (1000000));
    524 #else
    525 		/*
    526 		 * Calculate ((n * TIMER_FREQ) / 1e6) without using floating
    527 		 * point and without any avoidable overflows.
    528 		 */
    529 		int sec = n / 1000000,
    530 		    usec = n % 1000000;
    531 		n = sec * TIMER_FREQ +
    532 		    usec * (TIMER_FREQ / 1000000) +
    533 		    usec * ((TIMER_FREQ % 1000000) / 1000) / 1000 +
    534 		    usec * (TIMER_FREQ % 1000) / 1000000;
    535 #endif
    536 	}
    537 
    538 	while (n > 0) {
    539 #ifdef CLOCK_PARANOIA
    540 		int delta;
    541 		delay_tick = gettick();
    542 		if (delay_tick > odelay_tick)
    543 			delta = rtclock_tval - (delay_tick - odelay_tick);
    544 		else
    545 			delta = odelay_tick - delay_tick;
    546 		if (delta < 0 || delta >= rtclock_tval / 2) {
    547 			DPRINTF(("delay: ignore ticks %.4x-%.4x",
    548 				 odelay_tick, delay_tick));
    549 			if (clock_broken_latch) {
    550 				DPRINTF(("  (%.4x %.4x %.4x %.4x %.4x %.4x)\n",
    551 				         ticks[0], ticks[1], ticks[2],
    552 				         ticks[3], ticks[4], ticks[5]));
    553 			} else {
    554 				DPRINTF(("\n"));
    555 			}
    556 		} else
    557 			n -= delta;
    558 #else
    559 		delay_tick = gettick();
    560 		if (delay_tick > odelay_tick)
    561 			n -= rtclock_tval - (delay_tick - odelay_tick);
    562 		else
    563 			n -= odelay_tick - delay_tick;
    564 #endif
    565 		odelay_tick = delay_tick;
    566 	}
    567 }
    568 
    569 #if (NPCPPI > 0)
    570 int
    571 sysbeepmatch(struct device *parent, struct cfdata *match,
    572     void *aux)
    573 {
    574 	return (!ppi_attached);
    575 }
    576 
    577 void
    578 sysbeepattach(struct device *parent, struct device *self,
    579     void *aux)
    580 {
    581 	aprint_naive("\n");
    582 	aprint_normal("\n");
    583 
    584 	ppicookie = ((struct pcppi_attach_args *)aux)->pa_cookie;
    585 	ppi_attached = 1;
    586 }
    587 #endif
    588 
    589 void
    590 sysbeep(int pitch, int period)
    591 {
    592 #if (NPCPPI > 0)
    593 	if (ppi_attached)
    594 		pcppi_bell(ppicookie, pitch, period, 0);
    595 #endif
    596 }
    597 
    598 void
    599 i8254_initclocks(void)
    600 {
    601 
    602 	/*
    603 	 * XXX If you're doing strange things with multiple clocks, you might
    604 	 * want to keep track of clock handlers.
    605 	 */
    606 	(void)isa_intr_establish(NULL, 0, IST_PULSE, IPL_CLOCK,
    607 	    (int (*)(void *))clockintr, 0);
    608 }
    609 
    610 static void
    611 rtcinit(void)
    612 {
    613 	static int first_rtcopen_ever = 1;
    614 
    615 	if (!first_rtcopen_ever)
    616 		return;
    617 	first_rtcopen_ever = 0;
    618 
    619 	mc146818_write(NULL, MC_REGA,			/* XXX softc */
    620 	    MC_BASE_32_KHz | MC_RATE_1024_Hz);
    621 	mc146818_write(NULL, MC_REGB, MC_REGB_24HR);	/* XXX softc */
    622 }
    623 
    624 static int
    625 rtcget(mc_todregs *regs)
    626 {
    627 
    628 	rtcinit();
    629 	if ((mc146818_read(NULL, MC_REGD) & MC_REGD_VRT) == 0) /* XXX softc */
    630 		return (-1);
    631 	MC146818_GETTOD(NULL, regs);			/* XXX softc */
    632 	return (0);
    633 }
    634 
    635 static void
    636 rtcput(mc_todregs *regs)
    637 {
    638 
    639 	rtcinit();
    640 	MC146818_PUTTOD(NULL, regs);			/* XXX softc */
    641 }
    642 
    643 /*
    644  * check whether the CMOS layout is "standard"-like (ie, not PS/2-like),
    645  * to be called at splclock()
    646  */
    647 static int
    648 cmoscheck(void)
    649 {
    650 	int i;
    651 	unsigned short cksum = 0;
    652 
    653 	for (i = 0x10; i <= 0x2d; i++)
    654 		cksum += mc146818_read(NULL, i); /* XXX softc */
    655 
    656 	return (cksum == (mc146818_read(NULL, 0x2e) << 8)
    657 			  + mc146818_read(NULL, 0x2f));
    658 }
    659 
    660 #if NMCA > 0
    661 /*
    662  * Check whether the CMOS layout is PS/2 like, to be called at splclock().
    663  */
    664 static int cmoscheckps2(void);
    665 static int
    666 cmoscheckps2(void)
    667 {
    668 #if 0
    669 	/* Disabled until I find out the CRC checksum algorithm IBM uses */
    670 	int i;
    671 	unsigned short cksum = 0;
    672 
    673 	for (i = 0x10; i <= 0x31; i++)
    674 		cksum += mc146818_read(NULL, i); /* XXX softc */
    675 
    676 	return (cksum == (mc146818_read(NULL, 0x32) << 8)
    677 			  + mc146818_read(NULL, 0x33));
    678 #else
    679 	/* Check 'incorrect checksum' bit of IBM PS/2 Diagnostic Status Byte */
    680 	return ((mc146818_read(NULL, NVRAM_DIAG) & (1<<6)) == 0);
    681 #endif
    682 }
    683 #endif /* NMCA > 0 */
    684 
    685 /*
    686  * patchable to control century byte handling:
    687  * 1: always update
    688  * -1: never touch
    689  * 0: try to figure out itself
    690  */
    691 int rtc_update_century = 0;
    692 
    693 /*
    694  * Expand a two-digit year as read from the clock chip
    695  * into full width.
    696  * Being here, deal with the CMOS century byte.
    697  */
    698 static int centb = NVRAM_CENTURY;
    699 static int
    700 clock_expandyear(int clockyear)
    701 {
    702 	int s, clockcentury, cmoscentury;
    703 
    704 	clockcentury = (clockyear < 70) ? 20 : 19;
    705 	clockyear += 100 * clockcentury;
    706 
    707 	if (rtc_update_century < 0)
    708 		return (clockyear);
    709 
    710 	s = splclock();
    711 	if (cmoscheck())
    712 		cmoscentury = mc146818_read(NULL, NVRAM_CENTURY);
    713 #if NMCA > 0
    714 	else if (MCA_system && cmoscheckps2())
    715 		cmoscentury = mc146818_read(NULL, (centb = 0x37));
    716 #endif
    717 	else
    718 		cmoscentury = 0;
    719 	splx(s);
    720 	if (!cmoscentury) {
    721 #ifdef DIAGNOSTIC
    722 		printf("clock: unknown CMOS layout\n");
    723 #endif
    724 		return (clockyear);
    725 	}
    726 	cmoscentury = bcdtobin(cmoscentury);
    727 
    728 	if (cmoscentury != clockcentury) {
    729 		/* XXX note: saying "century is 20" might confuse the naive. */
    730 		printf("WARNING: NVRAM century is %d but RTC year is %d\n",
    731 		       cmoscentury, clockyear);
    732 
    733 		/* Kludge to roll over century. */
    734 		if ((rtc_update_century > 0) ||
    735 		    ((cmoscentury == 19) && (clockcentury == 20) &&
    736 		     (clockyear == 2000))) {
    737 			printf("WARNING: Setting NVRAM century to %d\n",
    738 			       clockcentury);
    739 			s = splclock();
    740 			mc146818_write(NULL, centb, bintobcd(clockcentury));
    741 			splx(s);
    742 		}
    743 	} else if (cmoscentury == 19 && rtc_update_century == 0)
    744 		rtc_update_century = 1; /* will update later in resettodr() */
    745 
    746 	return (clockyear);
    747 }
    748 
    749 static int
    750 rtc_get_ymdhms(todr_chip_handle_t tch, struct clock_ymdhms *dt)
    751 {
    752 	int s;
    753 	mc_todregs rtclk;
    754 
    755 	s = splclock();
    756 	if (rtcget(&rtclk)) {
    757 		splx(s);
    758 		return -1;
    759 	}
    760 	splx(s);
    761 
    762 	dt->dt_sec = bcdtobin(rtclk[MC_SEC]);
    763 	dt->dt_min = bcdtobin(rtclk[MC_MIN]);
    764 	dt->dt_hour = bcdtobin(rtclk[MC_HOUR]);
    765 	dt->dt_day = bcdtobin(rtclk[MC_DOM]);
    766 	dt->dt_mon = bcdtobin(rtclk[MC_MONTH]);
    767 	dt->dt_year = clock_expandyear(bcdtobin(rtclk[MC_YEAR]));
    768 
    769 	return 0;
    770 }
    771 
    772 static int
    773 rtc_set_ymdhms(todr_chip_handle_t tch, struct clock_ymdhms *dt)
    774 {
    775 	mc_todregs rtclk;
    776 	int century;
    777 	int s;
    778 
    779 	s = splclock();
    780 	if (rtcget(&rtclk))
    781 		memset(&rtclk, 0, sizeof(rtclk));
    782 	splx(s);
    783 
    784 	rtclk[MC_SEC] = bintobcd(dt->dt_sec);
    785 	rtclk[MC_MIN] = bintobcd(dt->dt_min);
    786 	rtclk[MC_HOUR] = bintobcd(dt->dt_hour);
    787 	rtclk[MC_DOW] = dt->dt_wday + 1;
    788 	rtclk[MC_YEAR] = bintobcd(dt->dt_year % 100);
    789 	rtclk[MC_MONTH] = bintobcd(dt->dt_mon);
    790 	rtclk[MC_DOM] = bintobcd(dt->dt_day);
    791 
    792 #ifdef DEBUG_CLOCK
    793 	printf("setclock: %x/%x/%x %x:%x:%x\n", rtclk[MC_YEAR], rtclk[MC_MONTH],
    794 	   rtclk[MC_DOM], rtclk[MC_HOUR], rtclk[MC_MIN], rtclk[MC_SEC]);
    795 #endif
    796 	s = splclock();
    797 	rtcput(&rtclk);
    798 	if (rtc_update_century > 0) {
    799 		century = bintobcd(dt->dt_year / 100);
    800 		mc146818_write(NULL, centb, century); /* XXX softc */
    801 	}
    802 	splx(s);
    803 	return 0;
    804 
    805 }
    806 
    807 static void
    808 rtc_register(void)
    809 {
    810 	static struct todr_chip_handle	tch;
    811 	tch.todr_gettime_ymdhms = rtc_get_ymdhms;
    812 	tch.todr_settime_ymdhms = rtc_set_ymdhms;
    813 	tch.todr_setwen = NULL;
    814 
    815 	todr_attach(&tch);
    816 }
    817 
    818 void
    819 setstatclockrate(int arg)
    820 {
    821 }
    822