clock.c revision 1.26
1/*	$OpenBSD: clock.c,v 1.26 2002/05/16 15:33:10 mickey 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
72Permission to use, copy, modify, and distribute this software and
73its documentation for any purpose and without fee is hereby
74granted, provided that the above copyright notice appears in all
75copies and that both the copyright notice and this permission notice
76appear in supporting documentation, and that the name of Intel
77not be used in advertising or publicity pertaining to distribution
78of the software without specific, written prior permission.
79
80INTEL DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE
81INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS,
82IN NO EVENT SHALL INTEL BE LIABLE FOR ANY SPECIAL, INDIRECT, OR
83CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
84LOSS OF USE, DATA OR PROFITS, WHETHER IN ACTION OF CONTRACT,
85NEGLIGENCE, OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION
86WITH 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
118int sysbeepmatch(struct device *, void *, void *);
119#else
120int sysbeepmatch(struct device *, struct cfdata *, void *);
121#endif
122void sysbeepattach(struct device *, struct device *, void *);
123
124struct cfattach sysbeep_ca = {
125	sizeof(struct device), sysbeepmatch, sysbeepattach
126};
127
128struct cfdriver sysbeep_cd = {
129	NULL, "sysbeep", DV_DULL
130};
131
132static int ppi_attached;
133static pcppi_tag_t ppicookie;
134#endif /* PCPPI */
135
136void	spinwait(int);
137void	findcpuspeed(void);
138int	clockintr(void *);
139int	gettick(void);
140void	sysbeep(int, int);
141int	rtcget(mc_todregs *);
142void	rtcput(mc_todregs *);
143int 	hexdectodec(int);
144int	dectohexdec(int);
145int	rtcintr(void *);
146void	rtcdrain(void *);
147
148u_int mc146818_read(void *, u_int);
149void mc146818_write(void *, u_int, u_int);
150
151#if defined(I586_CPU) || defined(I686_CPU)
152int pentium_mhz, clock_broken_latch;
153#endif
154
155#define	SECMIN	((unsigned)60)			/* seconds per minute */
156#define	SECHOUR	((unsigned)(60*SECMIN))		/* seconds per hour */
157
158u_int
159mc146818_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
175void
176mc146818_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
190void
191startrtclock()
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
205void
206rtcdrain(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
221void
222initrtclock()
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#if defined(CPU_I586) || defined(CPU_I686)
232	{
233		extern int cpu_id;
234		switch (cpu_id) {
235		case 0x440:	/* Cyrix MediaGX */
236		case 0x540:	/* Cyrix GXm */
237			clock_broken_latch = 1;
238			break;
239		default:
240			clock_broken_latch = 0;
241			break;
242		}
243	}
244#endif
245}
246
247int
248clockintr(arg)
249	void *arg;
250{
251	struct clockframe *frame = arg;		/* not strictly necessary */
252
253	hardclock(frame);
254	return (1);
255}
256
257int
258rtcintr(arg)
259	void *arg;
260{
261	struct clockframe *frame = arg;		/* not strictly necessary */
262	u_int stat = 0;
263
264	/*
265	 * If rtcintr is 'late', next intr may happen immediately.
266	 * Get them all. (Also, see comment in cpu_initclocks().)
267	 */
268	while (mc146818_read(NULL, MC_REGC) & MC_REGC_PF) {
269		statclock(frame);
270		stat = 1;
271	}
272	return (stat);
273}
274
275int
276gettick()
277{
278
279#if defined(I586_CPU) || defined(I686_CPU)
280	if (clock_broken_latch) {
281		int v1, v2, v3;
282		int w1, w2, w3;
283
284		disable_intr();
285
286		v1 = inb(TIMER_CNTR0);
287		v1 |= inb(TIMER_CNTR0) << 8;
288		v2 = inb(TIMER_CNTR0);
289		v2 |= inb(TIMER_CNTR0) << 8;
290		v3 = inb(TIMER_CNTR0);
291		v3 |= inb(TIMER_CNTR0) << 8;
292
293		enable_intr();
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		/* sort v1 v2 v3 */
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		/* compute the middle value */
313		if (v1 - v3 < 0x200)
314			return (v2);
315		w1 = v2 - v3;
316		w2 = v3 - v1 + TIMER_DIV(hz);
317		w3 = v1 - v2;
318		if (w1 >= w2) {
319			if (w1 >= w3)
320				return (v1);
321		} else {
322			if (w2 >= w3)
323				return (v2);
324		}
325		return (v3);
326	} else
327#endif
328	{
329		u_char lo, hi;
330
331		disable_intr();
332		/* Select counter 0 and latch it. */
333		outb(TIMER_MODE, TIMER_SEL0 | TIMER_LATCH);
334		lo = inb(TIMER_CNTR0);
335		hi = inb(TIMER_CNTR0);
336
337		enable_intr();
338		return ((hi << 8) | lo);
339	}
340}
341
342/*
343 * Wait "n" microseconds.
344 * Relies on timer 1 counting down from (TIMER_FREQ / hz) at TIMER_FREQ Hz.
345 * Note: timer had better have been programmed before this is first used!
346 * (Note that we use `rate generator' mode, which counts at 1:1; `square
347 * wave' mode counts at 2:1).
348 */
349void
350delay(n)
351	int n;
352{
353	int limit, tick, otick;
354
355	/*
356	 * Read the counter first, so that the rest of the setup overhead is
357	 * counted.
358	 */
359	otick = gettick();
360
361#ifdef __GNUC__
362	/*
363	 * Calculate ((n * TIMER_FREQ) / 1e6) using explicit assembler code so
364	 * we can take advantage of the intermediate 64-bit quantity to prevent
365	 * loss of significance.
366	 */
367	n -= 5;
368	if (n < 0)
369		return;
370	__asm __volatile("mul %2\n\tdiv %3"
371			 : "=a" (n)
372			 : "0" (n), "r" (TIMER_FREQ), "r" (1000000)
373			 : "%edx", "cc");
374#else
375	/*
376	 * Calculate ((n * TIMER_FREQ) / 1e6) without using floating point and
377	 * without any avoidable overflows.
378	 */
379	n -= 20;
380	{
381		int sec = n / 1000000,
382		    usec = n % 1000000;
383		n = sec * TIMER_FREQ +
384		    usec * (TIMER_FREQ / 1000000) +
385		    usec * ((TIMER_FREQ % 1000000) / 1000) / 1000 +
386		    usec * (TIMER_FREQ % 1000) / 1000000;
387	}
388#endif
389
390	limit = TIMER_FREQ / hz;
391
392	while (n > 0) {
393		tick = gettick();
394		if (tick > otick)
395			n -= limit - (tick - otick);
396		else
397			n -= otick - tick;
398		otick = tick;
399	}
400}
401
402#if (NPCPPI > 0)
403int
404sysbeepmatch(parent, match, aux)
405	struct device *parent;
406#ifdef __BROKEN_INDIRECT_CONFIG
407	void *match;
408#else
409	struct cfdata *match;
410#endif
411	void *aux;
412{
413	return (!ppi_attached);
414}
415
416void
417sysbeepattach(parent, self, aux)
418	struct device *parent, *self;
419	void *aux;
420{
421	printf("\n");
422
423	ppicookie = ((struct pcppi_attach_args *)aux)->pa_cookie;
424	ppi_attached = 1;
425}
426#endif
427
428void
429sysbeep(pitch, period)
430	int pitch, period;
431{
432#if (NPCPPI > 0)
433	if (ppi_attached)
434		pcppi_bell(ppicookie, pitch, period, 0);
435#endif
436}
437
438unsigned int delaycount;	/* calibrated loop variable (1 millisecond) */
439
440#define FIRST_GUESS   0x2000
441
442void
443findcpuspeed()
444{
445	int i;
446	int remainder;
447
448	/* Put counter in count down mode */
449	outb(TIMER_MODE, TIMER_SEL0 | TIMER_16BIT | TIMER_RATEGEN);
450	outb(TIMER_CNTR0, 0xff);
451	outb(TIMER_CNTR0, 0xff);
452	for (i = FIRST_GUESS; i; i--)
453		;
454	/* Read the value left in the counter */
455	remainder = gettick();
456	/*
457	 * Formula for delaycount is:
458	 *  (loopcount * timer clock speed) / (counter ticks * 1000)
459	 */
460	delaycount = (FIRST_GUESS * TIMER_DIV(1000)) / (0xffff-remainder);
461}
462
463#if defined(I586_CPU) || defined(I686_CPU)
464void
465calibrate_cyclecounter()
466{
467	unsigned long long count, last_count;
468#ifdef NTP
469	extern long time_precision;
470#endif
471
472	__asm __volatile(".byte 0xf, 0x31" : "=A" (last_count));
473	delay(1000000);
474	__asm __volatile(".byte 0xf, 0x31" : "=A" (count));
475	pentium_mhz = ((count - last_count) + 500000) / 1000000;
476#ifdef NTP
477	time_precision = 1;	/* XXX */
478#endif
479}
480#endif
481
482void
483cpu_initclocks()
484{
485	static struct timeout rtcdrain_timeout;
486	stathz = 128;
487	profhz = 1024;
488
489	/*
490	 * XXX If you're doing strange things with multiple clocks, you might
491	 * want to keep track of clock handlers.
492	 */
493	(void)isa_intr_establish(NULL, 0, IST_PULSE, IPL_CLOCK, clockintr,
494	    0, "clock");
495	(void)isa_intr_establish(NULL, 8, IST_PULSE, IPL_CLOCK, rtcintr,
496	    0, "rtc");
497
498	mc146818_write(NULL, MC_REGA, MC_BASE_32_KHz | MC_RATE_128_Hz);
499	mc146818_write(NULL, MC_REGB, MC_REGB_24HR | MC_REGB_PIE);
500
501	/*
502	 * On a number of i386 systems, the rtc will fail to start when booting
503	 * the system. This is due to us missing to acknowledge an interrupt
504	 * during early stages of the boot process. If we do not acknowledge
505	 * the interrupt, the rtc clock will not generate further interrupts.
506	 * To solve this, once interrupts are enabled, use a timeout (once)
507	 * to drain any un-acknowledged rtc interrupt(s).
508	 */
509
510	timeout_set(&rtcdrain_timeout, rtcdrain, (void *)&rtcdrain_timeout);
511	timeout_add(&rtcdrain_timeout, 1);
512}
513
514int
515rtcget(regs)
516	mc_todregs *regs;
517{
518	if ((mc146818_read(NULL, MC_REGD) & MC_REGD_VRT) == 0) /* XXX softc */
519		return (-1);
520	MC146818_GETTOD(NULL, regs);			/* XXX softc */
521	return (0);
522}
523
524void
525rtcput(regs)
526	mc_todregs *regs;
527{
528	MC146818_PUTTOD(NULL, regs);			/* XXX softc */
529}
530
531int
532hexdectodec(n)
533	int n;
534{
535
536	return (((n >> 4) & 0x0f) * 10 + (n & 0x0f));
537}
538
539int
540dectohexdec(n)
541	int n;
542{
543
544	return ((u_char)(((n / 10) << 4) & 0xf0) | ((n % 10) & 0x0f));
545}
546
547static int timeset;
548
549/*
550 * check whether the CMOS layout is "standard"-like (ie, not PS/2-like),
551 * to be called at splclock()
552 */
553int cmoscheck(void);
554int
555cmoscheck()
556{
557	int i;
558	unsigned short cksum = 0;
559
560	for (i = 0x10; i <= 0x2d; i++)
561		cksum += mc146818_read(NULL, i); /* XXX softc */
562
563	return (cksum == (mc146818_read(NULL, 0x2e) << 8)
564			  + mc146818_read(NULL, 0x2f));
565}
566
567/*
568 * patchable to control century byte handling:
569 * 1: always update
570 * -1: never touch
571 * 0: try to figure out itself
572 */
573int rtc_update_century = 0;
574
575/*
576 * Expand a two-digit year as read from the clock chip
577 * into full width.
578 * Being here, deal with the CMOS century byte.
579 */
580int clock_expandyear(int);
581int
582clock_expandyear(clockyear)
583	int clockyear;
584{
585	int s, clockcentury, cmoscentury;
586
587	clockcentury = (clockyear < 70) ? 20 : 19;
588	clockyear += 100 * clockcentury;
589
590	if (rtc_update_century < 0)
591		return (clockyear);
592
593	s = splclock();
594	if (cmoscheck())
595		cmoscentury = mc146818_read(NULL, NVRAM_CENTURY);
596	else
597		cmoscentury = 0;
598	splx(s);
599	if (!cmoscentury) {
600#ifdef DIAGNOSTIC
601		printf("clock: unknown CMOS layout\n");
602#endif
603		return (clockyear);
604	}
605	cmoscentury = hexdectodec(cmoscentury);
606
607	if (cmoscentury != clockcentury) {
608		/* XXX note: saying "century is 20" might confuse the naive. */
609		printf("WARNING: NVRAM century is %d but RTC year is %d\n",
610		       cmoscentury, clockyear);
611
612		/* Kludge to roll over century. */
613		if ((rtc_update_century > 0) ||
614		    ((cmoscentury == 19) && (clockcentury == 20) &&
615		     (clockyear == 2000))) {
616			printf("WARNING: Setting NVRAM century to %d\n",
617			       clockcentury);
618			s = splclock();
619			mc146818_write(NULL, NVRAM_CENTURY,
620				       dectohexdec(clockcentury));
621			splx(s);
622		}
623	} else if (cmoscentury == 19 && rtc_update_century == 0)
624		rtc_update_century = 1; /* will update later in resettodr() */
625
626	return (clockyear);
627}
628
629/*
630 * Initialize the time of day register, based on the time base which is, e.g.
631 * from a filesystem.
632 */
633void
634inittodr(base)
635	time_t base;
636{
637	mc_todregs rtclk;
638	struct clock_ymdhms dt;
639	int s;
640
641	/*
642	 * We mostly ignore the suggested time and go for the RTC clock time
643	 * stored in the CMOS RAM.  If the time can't be obtained from the
644	 * CMOS, or if the time obtained from the CMOS is 5 or more years
645	 * less than the suggested time, we used the suggested time.  (In
646	 * the latter case, it's likely that the CMOS battery has died.)
647	 */
648
649	if (base < 15*SECYR) {	/* if before 1985, something's odd... */
650		printf("WARNING: preposterous time in file system\n");
651		/* read the system clock anyway */
652		base = 17*SECYR + 186*SECDAY + SECDAY/2;
653	}
654
655	time.tv_usec = 0;
656
657	s = splclock();
658	if (rtcget(&rtclk)) {
659		splx(s);
660		printf("WARNING: invalid time in clock chip\n");
661		goto fstime;
662	}
663	splx(s);
664
665	dt.dt_sec = hexdectodec(rtclk[MC_SEC]);
666	dt.dt_min = hexdectodec(rtclk[MC_MIN]);
667	dt.dt_hour = hexdectodec(rtclk[MC_HOUR]);
668	dt.dt_day = hexdectodec(rtclk[MC_DOM]);
669	dt.dt_mon = hexdectodec(rtclk[MC_MONTH]);
670	dt.dt_year = clock_expandyear(hexdectodec(rtclk[MC_YEAR]));
671
672
673	/*
674	 * If time_t is 32 bits, then the "End of Time" is
675	 * Mon Jan 18 22:14:07 2038 (US/Eastern)
676	 * This code copes with RTC's past the end of time if time_t
677	 * is an int32 or less. Needed because sometimes RTCs screw
678	 * up or are badly set, and that would cause the time to go
679	 * negative in the calculation below, which causes Very Bad
680	 * Mojo. This at least lets the user boot and fix the problem.
681	 * Note the code is self eliminating once time_t goes to 64 bits.
682	 */
683	if (sizeof(time_t) <= sizeof(int32_t)) {
684		if (dt.dt_year >= 2038) {
685			printf("WARNING: RTC time at or beyond 2038.\n");
686			dt.dt_year = 2037;
687			printf("WARNING: year set back to 2037.\n");
688			printf("WARNING: CHECK AND RESET THE DATE!\n");
689		}
690	}
691
692	time.tv_sec = clock_ymdhms_to_secs(&dt) + tz.tz_minuteswest * 60;
693	if (tz.tz_dsttime)
694		time.tv_sec -= 3600;
695
696	if (base < time.tv_sec - 5*SECYR)
697		printf("WARNING: file system time much less than clock time\n");
698	else if (base > time.tv_sec + 5*SECYR) {
699		printf("WARNING: clock time much less than file system time\n");
700		printf("WARNING: using file system time\n");
701		goto fstime;
702	}
703
704	timeset = 1;
705	return;
706
707fstime:
708	timeset = 1;
709	time.tv_sec = base;
710	printf("WARNING: CHECK AND RESET THE DATE!\n");
711}
712
713/*
714 * Reset the clock.
715 */
716void
717resettodr()
718{
719	mc_todregs rtclk;
720	struct clock_ymdhms dt;
721	int diff;
722	int century;
723	int s;
724
725	/*
726	 * We might have been called by boot() due to a crash early
727	 * on.  Don't reset the clock chip in this case.
728	 */
729	if (!timeset)
730		return;
731
732	s = splclock();
733	if (rtcget(&rtclk))
734		bzero(&rtclk, sizeof(rtclk));
735	splx(s);
736
737	diff = tz.tz_minuteswest * 60;
738	if (tz.tz_dsttime)
739		diff -= 3600;
740	clock_secs_to_ymdhms(time.tv_sec - diff, &dt);
741
742	rtclk[MC_SEC] = dectohexdec(dt.dt_sec);
743	rtclk[MC_MIN] = dectohexdec(dt.dt_min);
744	rtclk[MC_HOUR] = dectohexdec(dt.dt_hour);
745	rtclk[MC_DOW] = dt.dt_wday;
746	rtclk[MC_YEAR] = dectohexdec(dt.dt_year % 100);
747	rtclk[MC_MONTH] = dectohexdec(dt.dt_mon);
748	rtclk[MC_DOM] = dectohexdec(dt.dt_day);
749	s = splclock();
750	rtcput(&rtclk);
751	if (rtc_update_century > 0) {
752		century = dectohexdec(dt.dt_year / 100);
753		mc146818_write(NULL, NVRAM_CENTURY, century); /* XXX softc */
754	}
755	splx(s);
756}
757
758void
759setstatclockrate(arg)
760	int arg;
761{
762	if (arg == stathz)
763		mc146818_write(NULL, MC_REGA, MC_BASE_32_KHz | MC_RATE_128_Hz);
764	else
765		mc146818_write(NULL, MC_REGA, MC_BASE_32_KHz | MC_RATE_1024_Hz);
766}
767