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