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