xref: /openbsd-src/sys/arch/i386/isa/clock.c (revision b2ea75c1b17e1a9a339660e7ed45cd24946b230e)
1 /*	$OpenBSD: clock.c,v 1.23 2001/02/19 04:57:02 ho Exp $	*/
2 /*	$NetBSD: clock.c,v 1.39 1996/05/12 23:11:54 mycroft Exp $	*/
3 
4 /*-
5  * Copyright (c) 1993, 1994 Charles Hannum.
6  * Copyright (c) 1990 The Regents of the University of California.
7  * All rights reserved.
8  *
9  * This code is derived from software contributed to Berkeley by
10  * William Jolitz and Don Ahn.
11  *
12  * Redistribution and use in source and binary forms, with or without
13  * modification, are permitted provided that the following conditions
14  * are met:
15  * 1. Redistributions of source code must retain the above copyright
16  *    notice, this list of conditions and the following disclaimer.
17  * 2. Redistributions in binary form must reproduce the above copyright
18  *    notice, this list of conditions and the following disclaimer in the
19  *    documentation and/or other materials provided with the distribution.
20  * 3. All advertising materials mentioning features or use of this software
21  *    must display the following acknowledgement:
22  *	This product includes software developed by the University of
23  *	California, Berkeley and its contributors.
24  * 4. Neither the name of the University nor the names of its contributors
25  *    may be used to endorse or promote products derived from this software
26  *    without specific prior written permission.
27  *
28  * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
29  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
30  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
31  * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
32  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
33  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
34  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
35  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
36  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
37  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
38  * SUCH DAMAGE.
39  *
40  *	@(#)clock.c	7.2 (Berkeley) 5/12/91
41  */
42 /*
43  * Mach Operating System
44  * Copyright (c) 1991,1990,1989 Carnegie Mellon University
45  * All Rights Reserved.
46  *
47  * Permission to use, copy, modify and distribute this software and its
48  * documentation is hereby granted, provided that both the copyright
49  * notice and this permission notice appear in all copies of the
50  * software, derivative works or modified versions, and any portions
51  * thereof, and that both notices appear in supporting documentation.
52  *
53  * CARNEGIE MELLON ALLOWS FREE USE OF THIS SOFTWARE IN ITS "AS IS"
54  * CONDITION.  CARNEGIE MELLON DISCLAIMS ANY LIABILITY OF ANY KIND FOR
55  * ANY DAMAGES WHATSOEVER RESULTING FROM THE USE OF THIS SOFTWARE.
56  *
57  * Carnegie Mellon requests users of this software to return to
58  *
59  *  Software Distribution Coordinator  or  Software.Distribution@CS.CMU.EDU
60  *  School of Computer Science
61  *  Carnegie Mellon University
62  *  Pittsburgh PA 15213-3890
63  *
64  * any improvements or extensions that they make and grant Carnegie Mellon
65  * the rights to redistribute these changes.
66  */
67 /*
68   Copyright 1988, 1989 by Intel Corporation, Santa Clara, California.
69 
70 		All Rights Reserved
71 
72 Permission to use, copy, modify, and distribute this software and
73 its documentation for any purpose and without fee is hereby
74 granted, provided that the above copyright notice appears in all
75 copies and that both the copyright notice and this permission notice
76 appear in supporting documentation, and that the name of Intel
77 not be used in advertising or publicity pertaining to distribution
78 of the software without specific, written prior permission.
79 
80 INTEL DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE
81 INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS,
82 IN NO EVENT SHALL INTEL BE LIABLE FOR ANY SPECIAL, INDIRECT, OR
83 CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
84 LOSS OF USE, DATA OR PROFITS, WHETHER IN ACTION OF CONTRACT,
85 NEGLIGENCE, OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION
86 WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
87 */
88 
89 /*
90  * Primitive clock interrupt routines.
91  */
92 #include <sys/types.h>
93 #include <sys/param.h>
94 #include <sys/systm.h>
95 #include <sys/time.h>
96 #include <sys/kernel.h>
97 #include <sys/device.h>
98 #include <sys/timeout.h>
99 
100 #include <machine/cpu.h>
101 #include <machine/intr.h>
102 #include <machine/pio.h>
103 #include <machine/cpufunc.h>
104 
105 #include <dev/clock_subr.h>
106 #include <dev/isa/isareg.h>
107 #include <dev/isa/isavar.h>
108 #include <dev/ic/mc146818reg.h>
109 #include <i386/isa/nvram.h>
110 #include <i386/isa/timerreg.h>
111 
112 #include "pcppi.h"
113 #if (NPCPPI > 0)
114 #include <dev/isa/pcppivar.h>
115 
116 #define __BROKEN_INDIRECT_CONFIG /* XXX */
117 #ifdef __BROKEN_INDIRECT_CONFIG
118 int sysbeepmatch __P((struct device *, void *, void *));
119 #else
120 int sysbeepmatch __P((struct device *, struct cfdata *, void *));
121 #endif
122 void sysbeepattach __P((struct device *, struct device *, void *));
123 
124 struct cfattach sysbeep_ca = {
125 	sizeof(struct device), sysbeepmatch, sysbeepattach
126 };
127 
128 struct cfdriver sysbeep_cd = {
129 	NULL, "sysbeep", DV_DULL
130 };
131 
132 static int ppi_attached;
133 static pcppi_tag_t ppicookie;
134 #endif /* PCPPI */
135 
136 void	spinwait __P((int));
137 void	findcpuspeed __P((void));
138 int	clockintr __P((void *));
139 int	gettick __P((void));
140 void	sysbeep __P((int, int));
141 int	rtcget __P((mc_todregs *));
142 void	rtcput __P((mc_todregs *));
143 int 	hexdectodec __P((int));
144 int	dectohexdec __P((int));
145 int	rtcintr __P((void *));
146 void	rtcdrain __P((void *));
147 
148 __inline u_int mc146818_read __P((void *, u_int));
149 __inline void mc146818_write __P((void *, u_int, u_int));
150 
151 #if defined(I586_CPU) || defined(I686_CPU)
152 int pentium_mhz;
153 #endif
154 
155 #define	SECMIN	((unsigned)60)			/* seconds per minute */
156 #define	SECHOUR	((unsigned)(60*SECMIN))		/* seconds per hour */
157 
158 __inline u_int
159 mc146818_read(sc, reg)
160 	void *sc;					/* XXX use it? */
161 	u_int reg;
162 {
163 	int s;
164 	u_char v;
165 
166 	s = splhigh();
167 	outb(IO_RTC, reg);
168 	DELAY(1);
169 	v = inb(IO_RTC+1);
170 	DELAY(1);
171 	splx(s);
172 	return (v);
173 }
174 
175 __inline void
176 mc146818_write(sc, reg, datum)
177 	void *sc;					/* XXX use it? */
178 	u_int reg, datum;
179 {
180 	int s;
181 
182 	s = splhigh();
183 	outb(IO_RTC, reg);
184 	DELAY(1);
185 	outb(IO_RTC+1, datum);
186 	DELAY(1);
187 	splx(s);
188 }
189 
190 void
191 startrtclock()
192 {
193 	int s;
194 
195 	findcpuspeed();		/* use the clock (while it's free)
196 					to find the cpu speed */
197 	initrtclock();
198 
199 	/* Check diagnostic status */
200 	if ((s = mc146818_read(NULL, NVRAM_DIAG)) != 0)	/* XXX softc */
201 		printf("RTC BIOS diagnostic error %b\n", (unsigned int) s,
202 		    NVRAM_DIAG_BITS);
203 }
204 
205 void
206 rtcdrain(void *v)
207 {
208 	struct timeout *to = (struct timeout *)v;
209 
210 	if (to != NULL)
211 		timeout_del(to);
212 
213 	/*
214 	 * Drain any un-acknowledged RTC interrupts.
215 	 * See comment in cpu_initclocks().
216 	 */
217   	while (mc146818_read(NULL, MC_REGC) & MC_REGC_PF)
218 		; /* Nothing. */
219 }
220 
221 void
222 initrtclock()
223 {
224 	/* initialize 8253 clock */
225 	outb(TIMER_MODE, TIMER_SEL0|TIMER_RATEGEN|TIMER_16BIT);
226 
227 	/* Correct rounding will buy us a better precision in timekeeping */
228 	outb(IO_TIMER1, TIMER_DIV(hz) % 256);
229 	outb(IO_TIMER1, TIMER_DIV(hz) / 256);
230 }
231 
232 int
233 clockintr(arg)
234 	void *arg;
235 {
236 	struct clockframe *frame = arg;		/* not strictly necessary */
237 
238 	hardclock(frame);
239 	return (1);
240 }
241 
242 int
243 rtcintr(arg)
244 	void *arg;
245 {
246 	struct clockframe *frame = arg;		/* not strictly necessary */
247 	u_int stat = 0;
248 
249 	/*
250 	 * If rtcintr is 'late', next intr may happen immediately.
251 	 * Get them all. (Also, see comment in cpu_initclocks().)
252 	 */
253 	while (mc146818_read(NULL, MC_REGC) & MC_REGC_PF) {
254 		statclock(frame);
255 		stat = 1;
256 	}
257 	return (stat);
258 }
259 
260 int
261 gettick()
262 {
263 	u_char lo, hi;
264 
265 	/* Don't want someone screwing with the counter while we're here. */
266 	disable_intr();
267 	/* Select counter 0 and latch it. */
268 	outb(TIMER_MODE, TIMER_SEL0 | TIMER_LATCH);
269 	lo = inb(TIMER_CNTR0);
270 	hi = inb(TIMER_CNTR0);
271 	enable_intr();
272 	return ((hi << 8) | lo);
273 }
274 
275 /*
276  * Wait "n" microseconds.
277  * Relies on timer 1 counting down from (TIMER_FREQ / hz) at TIMER_FREQ Hz.
278  * Note: timer had better have been programmed before this is first used!
279  * (Note that we use `rate generator' mode, which counts at 1:1; `square
280  * wave' mode counts at 2:1).
281  */
282 void
283 delay(n)
284 	int n;
285 {
286 	int limit, tick, otick;
287 
288 	/*
289 	 * Read the counter first, so that the rest of the setup overhead is
290 	 * counted.
291 	 */
292 	otick = gettick();
293 
294 #ifdef __GNUC__
295 	/*
296 	 * Calculate ((n * TIMER_FREQ) / 1e6) using explicit assembler code so
297 	 * we can take advantage of the intermediate 64-bit quantity to prevent
298 	 * loss of significance.
299 	 */
300 	n -= 5;
301 	if (n < 0)
302 		return;
303 	__asm __volatile("mul %2\n\tdiv %3"
304 			 : "=a" (n)
305 			 : "0" (n), "r" (TIMER_FREQ), "r" (1000000)
306 			 : "%edx", "cc");
307 #else
308 	/*
309 	 * Calculate ((n * TIMER_FREQ) / 1e6) without using floating point and
310 	 * without any avoidable overflows.
311 	 */
312 	n -= 20;
313 	{
314 		int sec = n / 1000000,
315 		    usec = n % 1000000;
316 		n = sec * TIMER_FREQ +
317 		    usec * (TIMER_FREQ / 1000000) +
318 		    usec * ((TIMER_FREQ % 1000000) / 1000) / 1000 +
319 		    usec * (TIMER_FREQ % 1000) / 1000000;
320 	}
321 #endif
322 
323 	limit = TIMER_FREQ / hz;
324 
325 	while (n > 0) {
326 		tick = gettick();
327 		if (tick > otick)
328 			n -= limit - (tick - otick);
329 		else
330 			n -= otick - tick;
331 		otick = tick;
332 	}
333 }
334 
335 #if (NPCPPI > 0)
336 int
337 sysbeepmatch(parent, match, aux)
338 	struct device *parent;
339 #ifdef __BROKEN_INDIRECT_CONFIG
340 	void *match;
341 #else
342 	struct cfdata *match;
343 #endif
344 	void *aux;
345 {
346 	return (!ppi_attached);
347 }
348 
349 void
350 sysbeepattach(parent, self, aux)
351 	struct device *parent, *self;
352 	void *aux;
353 {
354 	printf("\n");
355 
356 	ppicookie = ((struct pcppi_attach_args *)aux)->pa_cookie;
357 	ppi_attached = 1;
358 }
359 #endif
360 
361 void
362 sysbeep(pitch, period)
363 	int pitch, period;
364 {
365 #if (NPCPPI > 0)
366 	if (ppi_attached)
367 		pcppi_bell(ppicookie, pitch, period, 0);
368 #endif
369 }
370 
371 unsigned int delaycount;	/* calibrated loop variable (1 millisecond) */
372 
373 #define FIRST_GUESS   0x2000
374 
375 void
376 findcpuspeed()
377 {
378 	int i;
379 	int remainder;
380 
381 	/* Put counter in count down mode */
382 	outb(TIMER_MODE, TIMER_SEL0 | TIMER_16BIT | TIMER_RATEGEN);
383 	outb(TIMER_CNTR0, 0xff);
384 	outb(TIMER_CNTR0, 0xff);
385 	for (i = FIRST_GUESS; i; i--)
386 		;
387 	/* Read the value left in the counter */
388 	remainder = gettick();
389 	/*
390 	 * Formula for delaycount is:
391 	 *  (loopcount * timer clock speed) / (counter ticks * 1000)
392 	 */
393 	delaycount = (FIRST_GUESS * TIMER_DIV(1000)) / (0xffff-remainder);
394 }
395 
396 #if defined(I586_CPU) || defined(I686_CPU)
397 void
398 calibrate_cyclecounter()
399 {
400 	unsigned long long count, last_count;
401 #ifdef NTP
402 	extern long time_precision;
403 #endif
404 
405 	__asm __volatile(".byte 0xf, 0x31" : "=A" (last_count));
406 	delay(1000000);
407 	__asm __volatile(".byte 0xf, 0x31" : "=A" (count));
408 	pentium_mhz = ((count - last_count) + 500000) / 1000000;
409 #ifdef NTP
410 	time_precision = 1;	/* XXX */
411 #endif
412 }
413 #endif
414 
415 void
416 cpu_initclocks()
417 {
418 	static struct timeout rtcdrain_timeout;
419 	stathz = 128;
420 	profhz = 1024;
421 
422 	/*
423 	 * XXX If you're doing strange things with multiple clocks, you might
424 	 * want to keep track of clock handlers.
425 	 */
426 	(void)isa_intr_establish(NULL, 0, IST_PULSE, IPL_CLOCK, clockintr,
427 	    0, "clock");
428 	(void)isa_intr_establish(NULL, 8, IST_PULSE, IPL_CLOCK, rtcintr,
429 	    0, "rtc");
430 
431 	mc146818_write(NULL, MC_REGA, MC_BASE_32_KHz | MC_RATE_128_Hz);
432 	mc146818_write(NULL, MC_REGB, MC_REGB_24HR | MC_REGB_PIE);
433 
434 	/*
435 	 * On a number of i386 systems, the rtc will fail to start when booting
436 	 * the system. This is due to us missing to acknowledge an interrupt
437 	 * during early stages of the boot process. If we do not acknowledge
438 	 * the interrupt, the rtc clock will not generate further interrupts.
439 	 * To solve this, once interrupts are enabled, use a timeout (once)
440 	 * to drain any un-acknowledged rtc interrupt(s).
441 	 */
442 
443 	timeout_set(&rtcdrain_timeout, rtcdrain, (void *)&rtcdrain_timeout);
444 	timeout_add(&rtcdrain_timeout, 1);
445 }
446 
447 int
448 rtcget(regs)
449 	mc_todregs *regs;
450 {
451 	if ((mc146818_read(NULL, MC_REGD) & MC_REGD_VRT) == 0) /* XXX softc */
452 		return (-1);
453 	MC146818_GETTOD(NULL, regs);			/* XXX softc */
454 	return (0);
455 }
456 
457 void
458 rtcput(regs)
459 	mc_todregs *regs;
460 {
461 	MC146818_PUTTOD(NULL, regs);			/* XXX softc */
462 }
463 
464 int
465 hexdectodec(n)
466 	int n;
467 {
468 
469 	return (((n >> 4) & 0x0f) * 10 + (n & 0x0f));
470 }
471 
472 int
473 dectohexdec(n)
474 	int n;
475 {
476 
477 	return ((u_char)(((n / 10) << 4) & 0xf0) | ((n % 10) & 0x0f));
478 }
479 
480 static int timeset;
481 
482 /*
483  * check whether the CMOS layout is "standard"-like (ie, not PS/2-like),
484  * to be called at splclock()
485  */
486 int cmoscheck __P((void));
487 int
488 cmoscheck()
489 {
490 	int i;
491 	unsigned short cksum = 0;
492 
493 	for (i = 0x10; i <= 0x2d; i++)
494 		cksum += mc146818_read(NULL, i); /* XXX softc */
495 
496 	return (cksum == (mc146818_read(NULL, 0x2e) << 8)
497 			  + mc146818_read(NULL, 0x2f));
498 }
499 
500 /*
501  * patchable to control century byte handling:
502  * 1: always update
503  * -1: never touch
504  * 0: try to figure out itself
505  */
506 int rtc_update_century = 0;
507 
508 /*
509  * Expand a two-digit year as read from the clock chip
510  * into full width.
511  * Being here, deal with the CMOS century byte.
512  */
513 int clock_expandyear __P((int));
514 int
515 clock_expandyear(clockyear)
516 	int clockyear;
517 {
518 	int s, clockcentury, cmoscentury;
519 
520 	clockcentury = (clockyear < 70) ? 20 : 19;
521 	clockyear += 100 * clockcentury;
522 
523 	if (rtc_update_century < 0)
524 		return (clockyear);
525 
526 	s = splclock();
527 	if (cmoscheck())
528 		cmoscentury = mc146818_read(NULL, NVRAM_CENTURY);
529 	else
530 		cmoscentury = 0;
531 	splx(s);
532 	if (!cmoscentury) {
533 #ifdef DIAGNOSTIC
534 		printf("clock: unknown CMOS layout\n");
535 #endif
536 		return (clockyear);
537 	}
538 	cmoscentury = hexdectodec(cmoscentury);
539 
540 	if (cmoscentury != clockcentury) {
541 		/* XXX note: saying "century is 20" might confuse the naive. */
542 		printf("WARNING: NVRAM century is %d but RTC year is %d\n",
543 		       cmoscentury, clockyear);
544 
545 		/* Kludge to roll over century. */
546 		if ((rtc_update_century > 0) ||
547 		    ((cmoscentury == 19) && (clockcentury == 20) &&
548 		     (clockyear == 2000))) {
549 			printf("WARNING: Setting NVRAM century to %d\n",
550 			       clockcentury);
551 			s = splclock();
552 			mc146818_write(NULL, NVRAM_CENTURY,
553 				       dectohexdec(clockcentury));
554 			splx(s);
555 		}
556 	} else if (cmoscentury == 19 && rtc_update_century == 0)
557 		rtc_update_century = 1; /* will update later in resettodr() */
558 
559 	return (clockyear);
560 }
561 
562 /*
563  * Initialize the time of day register, based on the time base which is, e.g.
564  * from a filesystem.
565  */
566 void
567 inittodr(base)
568 	time_t base;
569 {
570 	mc_todregs rtclk;
571 	struct clock_ymdhms dt;
572 	int s;
573 
574 	/*
575 	 * We mostly ignore the suggested time and go for the RTC clock time
576 	 * stored in the CMOS RAM.  If the time can't be obtained from the
577 	 * CMOS, or if the time obtained from the CMOS is 5 or more years
578 	 * less than the suggested time, we used the suggested time.  (In
579 	 * the latter case, it's likely that the CMOS battery has died.)
580 	 */
581 
582 	if (base < 15*SECYR) {	/* if before 1985, something's odd... */
583 		printf("WARNING: preposterous time in file system\n");
584 		/* read the system clock anyway */
585 		base = 17*SECYR + 186*SECDAY + SECDAY/2;
586 	}
587 
588 	time.tv_usec = 0;
589 
590 	s = splclock();
591 	if (rtcget(&rtclk)) {
592 		splx(s);
593 		printf("WARNING: invalid time in clock chip\n");
594 		goto fstime;
595 	}
596 	splx(s);
597 
598 	dt.dt_sec = hexdectodec(rtclk[MC_SEC]);
599 	dt.dt_min = hexdectodec(rtclk[MC_MIN]);
600 	dt.dt_hour = hexdectodec(rtclk[MC_HOUR]);
601 	dt.dt_day = hexdectodec(rtclk[MC_DOM]);
602 	dt.dt_mon = hexdectodec(rtclk[MC_MONTH]);
603 	dt.dt_year = clock_expandyear(hexdectodec(rtclk[MC_YEAR]));
604 
605 
606 	/*
607 	 * If time_t is 32 bits, then the "End of Time" is
608 	 * Mon Jan 18 22:14:07 2038 (US/Eastern)
609 	 * This code copes with RTC's past the end of time if time_t
610 	 * is an int32 or less. Needed because sometimes RTCs screw
611 	 * up or are badly set, and that would cause the time to go
612 	 * negative in the calculation below, which causes Very Bad
613 	 * Mojo. This at least lets the user boot and fix the problem.
614 	 * Note the code is self eliminating once time_t goes to 64 bits.
615 	 */
616 	if (sizeof(time_t) <= sizeof(int32_t)) {
617 		if (dt.dt_year >= 2038) {
618 			printf("WARNING: RTC time at or beyond 2038.\n");
619 			dt.dt_year = 2037;
620 			printf("WARNING: year set back to 2037.\n");
621 			printf("WARNING: CHECK AND RESET THE DATE!\n");
622 		}
623 	}
624 
625 	time.tv_sec = clock_ymdhms_to_secs(&dt) + tz.tz_minuteswest * 60;
626 	if (tz.tz_dsttime)
627 		time.tv_sec -= 3600;
628 
629 	if (base < time.tv_sec - 5*SECYR)
630 		printf("WARNING: file system time much less than clock time\n");
631 	else if (base > time.tv_sec + 5*SECYR) {
632 		printf("WARNING: clock time much less than file system time\n");
633 		printf("WARNING: using file system time\n");
634 		goto fstime;
635 	}
636 
637 	timeset = 1;
638 	return;
639 
640 fstime:
641 	timeset = 1;
642 	time.tv_sec = base;
643 	printf("WARNING: CHECK AND RESET THE DATE!\n");
644 }
645 
646 /*
647  * Reset the clock.
648  */
649 void
650 resettodr()
651 {
652 	mc_todregs rtclk;
653 	struct clock_ymdhms dt;
654 	int diff;
655 	int century;
656 	int s;
657 
658 	/*
659 	 * We might have been called by boot() due to a crash early
660 	 * on.  Don't reset the clock chip in this case.
661 	 */
662 	if (!timeset)
663 		return;
664 
665 	s = splclock();
666 	if (rtcget(&rtclk))
667 		bzero(&rtclk, sizeof(rtclk));
668 	splx(s);
669 
670 	diff = tz.tz_minuteswest * 60;
671 	if (tz.tz_dsttime)
672 		diff -= 3600;
673 	clock_secs_to_ymdhms(time.tv_sec - diff, &dt);
674 
675 	rtclk[MC_SEC] = dectohexdec(dt.dt_sec);
676 	rtclk[MC_MIN] = dectohexdec(dt.dt_min);
677 	rtclk[MC_HOUR] = dectohexdec(dt.dt_hour);
678 	rtclk[MC_DOW] = dt.dt_wday;
679 	rtclk[MC_YEAR] = dectohexdec(dt.dt_year % 100);
680 	rtclk[MC_MONTH] = dectohexdec(dt.dt_mon);
681 	rtclk[MC_DOM] = dectohexdec(dt.dt_day);
682 	s = splclock();
683 	rtcput(&rtclk);
684 	if (rtc_update_century > 0) {
685 		century = dectohexdec(dt.dt_year / 100);
686 		mc146818_write(NULL, NVRAM_CENTURY, century); /* XXX softc */
687 	}
688 	splx(s);
689 }
690 
691 void
692 setstatclockrate(arg)
693 	int arg;
694 {
695 	if (arg == stathz)
696 		mc146818_write(NULL, MC_REGA, MC_BASE_32_KHz | MC_RATE_128_Hz);
697 	else
698 		mc146818_write(NULL, MC_REGA, MC_BASE_32_KHz | MC_RATE_1024_Hz);
699 }
700