xref: /openbsd/sys/arch/amd64/isa/clock.c (revision 6f40fd34)
1 /*	$OpenBSD: clock.c,v 1.24 2017/01/25 08:23:50 tom Exp $	*/
2 /*	$NetBSD: clock.c,v 1.1 2003/04/26 18:39:50 fvdl Exp $	*/
3 
4 /*-
5  * Copyright (c) 1993, 1994 Charles M. 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. Neither the name of the University nor the names of its contributors
21  *    may be used to endorse or promote products derived from this software
22  *    without specific prior written permission.
23  *
24  * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
25  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
26  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
27  * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
28  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
29  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
30  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
31  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
32  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
33  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
34  * SUCH DAMAGE.
35  *
36  *	@(#)clock.c	7.2 (Berkeley) 5/12/91
37  */
38 /*
39  * Mach Operating System
40  * Copyright (c) 1991,1990,1989 Carnegie Mellon University
41  * All Rights Reserved.
42  *
43  * Permission to use, copy, modify and distribute this software and its
44  * documentation is hereby granted, provided that both the copyright
45  * notice and this permission notice appear in all copies of the
46  * software, derivative works or modified versions, and any portions
47  * thereof, and that both notices appear in supporting documentation.
48  *
49  * CARNEGIE MELLON ALLOWS FREE USE OF THIS SOFTWARE IN ITS "AS IS"
50  * CONDITION.  CARNEGIE MELLON DISCLAIMS ANY LIABILITY OF ANY KIND FOR
51  * ANY DAMAGES WHATSOEVER RESULTING FROM THE USE OF THIS SOFTWARE.
52  *
53  * Carnegie Mellon requests users of this software to return to
54  *
55  *  Software Distribution Coordinator  or  Software.Distribution@CS.CMU.EDU
56  *  School of Computer Science
57  *  Carnegie Mellon University
58  *  Pittsburgh PA 15213-3890
59  *
60  * any improvements or extensions that they make and grant Carnegie Mellon
61  * the rights to redistribute these changes.
62  */
63 /*
64   Copyright 1988, 1989 by Intel Corporation, Santa Clara, California.
65 
66 		All Rights Reserved
67 
68 Permission to use, copy, modify, and distribute this software and
69 its documentation for any purpose and without fee is hereby
70 granted, provided that the above copyright notice appears in all
71 copies and that both the copyright notice and this permission notice
72 appear in supporting documentation, and that the name of Intel
73 not be used in advertising or publicity pertaining to distribution
74 of the software without specific, written prior permission.
75 
76 INTEL DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE
77 INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS,
78 IN NO EVENT SHALL INTEL BE LIABLE FOR ANY SPECIAL, INDIRECT, OR
79 CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
80 LOSS OF USE, DATA OR PROFITS, WHETHER IN ACTION OF CONTRACT,
81 NEGLIGENCE, OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION
82 WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
83 */
84 
85 /*
86  * Primitive clock interrupt routines.
87  */
88 
89 /* #define CLOCKDEBUG */
90 /* #define CLOCK_PARANOIA */
91 
92 #include <sys/param.h>
93 #include <sys/systm.h>
94 #include <sys/time.h>
95 #include <sys/kernel.h>
96 #include <sys/device.h>
97 #include <sys/timeout.h>
98 #include <sys/timetc.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 <dev/ic/i8253reg.h>
109 #include <amd64/isa/nvram.h>
110 #include <machine/specialreg.h>
111 
112 /* Timecounter on the i8254 */
113 u_int32_t i8254_lastcount;
114 u_int32_t i8254_offset;
115 int i8254_ticked;
116 u_int i8254_get_timecount(struct timecounter *tc);
117 
118 u_int i8254_simple_get_timecount(struct timecounter *tc);
119 
120 static struct timecounter i8254_timecounter = {
121 	i8254_get_timecount, NULL, ~0u, TIMER_FREQ, "i8254", 0, NULL
122 };
123 
124 int	clockintr(void *);
125 int	rtcintr(void *);
126 int	gettick(void);
127 void	rtcdrain(void *v);
128 int	rtcget(mc_todregs *);
129 void	rtcput(mc_todregs *);
130 int	bcdtobin(int);
131 int	bintobcd(int);
132 
133 u_int mc146818_read(void *, u_int);
134 void mc146818_write(void *, u_int, u_int);
135 
136 u_int
137 mc146818_read(void *sc, u_int reg)
138 {
139 	outb(IO_RTC, reg);
140 	DELAY(1);
141 	return (inb(IO_RTC+1));
142 }
143 
144 void
145 mc146818_write(void *sc, u_int reg, u_int datum)
146 {
147 	outb(IO_RTC, reg);
148 	DELAY(1);
149 	outb(IO_RTC+1, datum);
150 	DELAY(1);
151 }
152 
153 struct mutex timer_mutex = MUTEX_INITIALIZER(IPL_HIGH);
154 
155 u_long rtclock_tval;
156 
157 void
158 startclocks(void)
159 {
160 	int s;
161 
162 	mtx_enter(&timer_mutex);
163 	rtclock_tval = TIMER_DIV(hz);
164 	i8254_startclock();
165 	mtx_leave(&timer_mutex);
166 
167 	/* Check diagnostic status */
168 	if ((s = mc146818_read(NULL, NVRAM_DIAG)) != 0)	/* XXX softc */
169 		printf("RTC BIOS diagnostic error %b\n", s, NVRAM_DIAG_BITS);
170 }
171 
172 int
173 clockintr(void *arg)
174 {
175 	struct clockframe *frame = arg;
176 
177 	if (timecounter->tc_get_timecount == i8254_get_timecount) {
178 		if (i8254_ticked) {
179 			i8254_ticked = 0;
180 		} else {
181 			i8254_offset += rtclock_tval;
182 			i8254_lastcount = 0;
183 		}
184 	}
185 
186 	hardclock(frame);
187 
188 	return 1;
189 }
190 
191 int
192 rtcintr(void *arg)
193 {
194 	struct clockframe *frame = arg;
195 	u_int stat = 0;
196 
197 	/*
198 	* If rtcintr is 'late', next intr may happen immediately.
199 	* Get them all. (Also, see comment in cpu_initclocks().)
200 	*/
201 	while (mc146818_read(NULL, MC_REGC) & MC_REGC_PF) {
202 		statclock(frame);
203 		stat = 1;
204 	}
205 
206 	return (stat);
207 }
208 
209 int
210 gettick(void)
211 {
212 	u_long ef;
213 	u_char lo, hi;
214 
215 	/* Don't want someone screwing with the counter while we're here. */
216 	mtx_enter(&timer_mutex);
217 	ef = read_rflags();
218 	disable_intr();
219 	/* Select counter 0 and latch it. */
220 	outb(IO_TIMER1+TIMER_MODE, TIMER_SEL0 | TIMER_LATCH);
221 	lo = inb(IO_TIMER1+TIMER_CNTR0);
222 	hi = inb(IO_TIMER1+TIMER_CNTR0);
223 	write_rflags(ef);
224 	mtx_leave(&timer_mutex);
225 	return ((hi << 8) | lo);
226 }
227 
228 /*
229  * Wait "n" microseconds.
230  * Relies on timer 1 counting down from (TIMER_FREQ / hz) at TIMER_FREQ Hz.
231  * Note: timer had better have been programmed before this is first used!
232  * (Note that we use `rate generator' mode, which counts at 1:1; `square
233  * wave' mode counts at 2:1).
234  */
235 void
236 i8254_delay(int n)
237 {
238 	int limit, tick, otick;
239 	static const int delaytab[26] = {
240 		 0,  2,  3,  4,  5,  6,  7,  9, 10, 11,
241 		12, 13, 15, 16, 17, 18, 19, 21, 22, 23,
242 		24, 25, 27, 28, 29, 30,
243 	};
244 
245 	/*
246 	 * Read the counter first, so that the rest of the setup overhead is
247 	 * counted.
248 	 */
249 	otick = gettick();
250 
251 	if (n <= 25)
252 		n = delaytab[n];
253 	else {
254 #ifdef __GNUC__
255 		/*
256 		 * Calculate ((n * TIMER_FREQ) / 1e6) using explicit assembler
257 		 * code so we can take advantage of the intermediate 64-bit
258 		 * quantity to prevent loss of significance.
259 		 */
260 		int m;
261 		__asm volatile("mul %3"
262 				 : "=a" (n), "=d" (m)
263 				 : "0" (n), "r" (TIMER_FREQ));
264 		__asm volatile("div %4"
265 				 : "=a" (n), "=d" (m)
266 				 : "0" (n), "1" (m), "r" (1000000));
267 #else
268 		/*
269 		 * Calculate ((n * TIMER_FREQ) / 1e6) without using floating
270 		 * point and without any avoidable overflows.
271 		 */
272 		int sec = n / 1000000,
273 		    usec = n % 1000000;
274 		n = sec * TIMER_FREQ +
275 		    usec * (TIMER_FREQ / 1000000) +
276 		    usec * ((TIMER_FREQ % 1000000) / 1000) / 1000 +
277 		    usec * (TIMER_FREQ % 1000) / 1000000;
278 #endif
279 	}
280 
281 	limit = TIMER_FREQ / hz;
282 
283 	while (n > 0) {
284 		tick = gettick();
285 		if (tick > otick)
286 			n -= limit - (tick - otick);
287 		else
288 			n -= otick - tick;
289 		otick = tick;
290 	}
291 }
292 
293 void
294 rtcdrain(void *v)
295 {
296 	struct timeout *to = (struct timeout *)v;
297 
298 	if (to != NULL)
299 		timeout_del(to);
300 
301 	/*
302 	* Drain any un-acknowledged RTC interrupts.
303 	* See comment in cpu_initclocks().
304 	*/
305 	while (mc146818_read(NULL, MC_REGC) & MC_REGC_PF)
306 		; /* Nothing. */
307 }
308 
309 void
310 i8254_initclocks(void)
311 {
312 	stathz = 128;
313 	profhz = 1024;
314 
315 	isa_intr_establish(NULL, 0, IST_PULSE, IPL_CLOCK, clockintr,
316 	    0, "clock");
317 	isa_intr_establish(NULL, 8, IST_PULSE, IPL_STATCLOCK, rtcintr,
318 	    0, "rtc");
319 
320 	rtcstart();			/* start the mc146818 clock */
321 
322 	i8254_inittimecounter();	/* hook the interrupt-based i8254 tc */
323 }
324 
325 void
326 rtcstart(void)
327 {
328 	static struct timeout rtcdrain_timeout;
329 
330 	mc146818_write(NULL, MC_REGA, MC_BASE_32_KHz | MC_RATE_128_Hz);
331 	mc146818_write(NULL, MC_REGB, MC_REGB_24HR | MC_REGB_PIE);
332 
333 	/*
334 	 * On a number of i386 systems, the rtc will fail to start when booting
335 	 * the system. This is due to us missing to acknowledge an interrupt
336 	 * during early stages of the boot process. If we do not acknowledge
337 	 * the interrupt, the rtc clock will not generate further interrupts.
338 	 * To solve this, once interrupts are enabled, use a timeout (once)
339 	 * to drain any un-acknowledged rtc interrupt(s).
340 	 */
341 	timeout_set(&rtcdrain_timeout, rtcdrain, (void *)&rtcdrain_timeout);
342 	timeout_add(&rtcdrain_timeout, 1);
343 }
344 
345 void
346 rtcstop(void)
347 {
348 	mc146818_write(NULL, MC_REGB, MC_REGB_24HR);
349 }
350 
351 int
352 rtcget(mc_todregs *regs)
353 {
354 	if ((mc146818_read(NULL, MC_REGD) & MC_REGD_VRT) == 0) /* XXX softc */
355 		return (-1);
356 	MC146818_GETTOD(NULL, regs);			/* XXX softc */
357 	return (0);
358 }
359 
360 void
361 rtcput(mc_todregs *regs)
362 {
363 	MC146818_PUTTOD(NULL, regs);			/* XXX softc */
364 }
365 
366 int
367 bcdtobin(int n)
368 {
369 	return (((n >> 4) & 0x0f) * 10 + (n & 0x0f));
370 }
371 
372 int
373 bintobcd(int n)
374 {
375 	return ((u_char)(((n / 10) << 4) & 0xf0) | ((n % 10) & 0x0f));
376 }
377 
378 static int timeset;
379 
380 /*
381  * check whether the CMOS layout is "standard"-like (ie, not PS/2-like),
382  * to be called at splclock()
383  */
384 static int cmoscheck(void);
385 static int
386 cmoscheck(void)
387 {
388 	int i;
389 	unsigned short cksum = 0;
390 
391 	for (i = 0x10; i <= 0x2d; i++)
392 		cksum += mc146818_read(NULL, i); /* XXX softc */
393 
394 	return (cksum == (mc146818_read(NULL, 0x2e) << 8)
395 			  + mc146818_read(NULL, 0x2f));
396 }
397 
398 /*
399  * patchable to control century byte handling:
400  * 1: always update
401  * -1: never touch
402  * 0: try to figure out itself
403  */
404 int rtc_update_century = 0;
405 
406 /*
407  * Expand a two-digit year as read from the clock chip
408  * into full width.
409  * Being here, deal with the CMOS century byte.
410  */
411 static int centb = NVRAM_CENTURY;
412 static int clock_expandyear(int);
413 static int
414 clock_expandyear(int clockyear)
415 {
416 	int s, clockcentury, cmoscentury;
417 
418 	clockcentury = (clockyear < 70) ? 20 : 19;
419 	clockyear += 100 * clockcentury;
420 
421 	if (rtc_update_century < 0)
422 		return (clockyear);
423 
424 	s = splclock();
425 	if (cmoscheck())
426 		cmoscentury = mc146818_read(NULL, NVRAM_CENTURY);
427 	else
428 		cmoscentury = 0;
429 	splx(s);
430 	if (!cmoscentury)
431 		return (clockyear);
432 
433 	cmoscentury = bcdtobin(cmoscentury);
434 
435 	if (cmoscentury != clockcentury) {
436 		/* XXX note: saying "century is 20" might confuse the naive. */
437 		printf("WARNING: NVRAM century is %d but RTC year is %d\n",
438 		       cmoscentury, clockyear);
439 
440 		/* Kludge to roll over century. */
441 		if ((rtc_update_century > 0) ||
442 		    ((cmoscentury == 19) && (clockcentury == 20) &&
443 		     (clockyear == 2000))) {
444 			printf("WARNING: Setting NVRAM century to %d\n",
445 			       clockcentury);
446 			s = splclock();
447 			mc146818_write(NULL, centb, bintobcd(clockcentury));
448 			splx(s);
449 		}
450 	} else if (cmoscentury == 19 && rtc_update_century == 0)
451 		rtc_update_century = 1; /* will update later in resettodr() */
452 
453 	return (clockyear);
454 }
455 
456 /*
457  * Initialize the time of day register, based on the time base which is, e.g.
458  * from a filesystem.
459  */
460 void
461 inittodr(time_t base)
462 {
463 	struct timespec ts;
464 	mc_todregs rtclk;
465 	struct clock_ymdhms dt;
466 	int s;
467 
468 	ts.tv_nsec = 0;
469 
470 	/*
471 	 * We mostly ignore the suggested time (which comes from the
472 	 * file system) and go for the RTC clock time stored in the
473 	 * CMOS RAM.  If the time can't be obtained from the CMOS, or
474 	 * if the time obtained from the CMOS is 5 or more years less
475 	 * than the suggested time, we used the suggested time.  (In
476 	 * the latter case, it's likely that the CMOS battery has
477 	 * died.)
478 	 */
479 
480 	/*
481 	 * if the file system time is more than a year older than the
482 	 * kernel, warn and then set the base time to the CONFIG_TIME.
483 	 */
484 	if (base < 30*SECYR) {	/* if before 2000, something's odd... */
485 		printf("WARNING: preposterous time in file system\n");
486 		base = 30*SECYR;
487 	}
488 
489 	s = splclock();
490 	if (rtcget(&rtclk)) {
491 		splx(s);
492 		printf("WARNING: invalid time in clock chip\n");
493 		goto fstime;
494 	}
495 	splx(s);
496 #ifdef DEBUG_CLOCK
497 	printf("readclock: %x/%x/%x %x:%x:%x\n", rtclk[MC_YEAR],
498 	    rtclk[MC_MONTH], rtclk[MC_DOM], rtclk[MC_HOUR], rtclk[MC_MIN],
499 	    rtclk[MC_SEC]);
500 #endif
501 
502 	dt.dt_sec = bcdtobin(rtclk[MC_SEC]);
503 	dt.dt_min = bcdtobin(rtclk[MC_MIN]);
504 	dt.dt_hour = bcdtobin(rtclk[MC_HOUR]);
505 	dt.dt_day = bcdtobin(rtclk[MC_DOM]);
506 	dt.dt_mon = bcdtobin(rtclk[MC_MONTH]);
507 	dt.dt_year = clock_expandyear(bcdtobin(rtclk[MC_YEAR]));
508 
509 	ts.tv_sec = clock_ymdhms_to_secs(&dt) + tz.tz_minuteswest * 60;
510 	if (tz.tz_dsttime)
511 		ts.tv_sec -= 3600;
512 
513 	if (base != 0 && base < ts.tv_sec - 5*SECYR)
514 		printf("WARNING: file system time much less than clock time\n");
515 	else if (base > ts.tv_sec + 5*SECYR) {
516 		printf("WARNING: clock time much less than file system time\n");
517 		printf("WARNING: using file system time\n");
518 		goto fstime;
519 	}
520 
521 	tc_setclock(&ts);
522 	timeset = 1;
523 	return;
524 
525 fstime:
526 	ts.tv_sec = base;
527 	tc_setclock(&ts);
528 	timeset = 1;
529 	printf("WARNING: CHECK AND RESET THE DATE!\n");
530 }
531 
532 /*
533  * Reset the clock.
534  */
535 void
536 resettodr(void)
537 {
538 	mc_todregs rtclk;
539 	struct clock_ymdhms dt;
540 	int century, diff, s;
541 
542 	/*
543 	 * We might have been called by boot() due to a crash early
544 	 * on.  Don't reset the clock chip in this case.
545 	 */
546 	if (!timeset)
547 		return;
548 
549 	s = splclock();
550 	if (rtcget(&rtclk))
551 		memset(&rtclk, 0, sizeof(rtclk));
552 	splx(s);
553 
554 	diff = tz.tz_minuteswest * 60;
555 	if (tz.tz_dsttime)
556 		diff -= 3600;
557 	clock_secs_to_ymdhms(time_second - diff, &dt);
558 
559 	rtclk[MC_SEC] = bintobcd(dt.dt_sec);
560 	rtclk[MC_MIN] = bintobcd(dt.dt_min);
561 	rtclk[MC_HOUR] = bintobcd(dt.dt_hour);
562 	rtclk[MC_DOW] = dt.dt_wday + 1;
563 	rtclk[MC_YEAR] = bintobcd(dt.dt_year % 100);
564 	rtclk[MC_MONTH] = bintobcd(dt.dt_mon);
565 	rtclk[MC_DOM] = bintobcd(dt.dt_day);
566 
567 #ifdef DEBUG_CLOCK
568 	printf("setclock: %x/%x/%x %x:%x:%x\n", rtclk[MC_YEAR], rtclk[MC_MONTH],
569 	   rtclk[MC_DOM], rtclk[MC_HOUR], rtclk[MC_MIN], rtclk[MC_SEC]);
570 #endif
571 	s = splclock();
572 	rtcput(&rtclk);
573 	if (rtc_update_century > 0) {
574 		century = bintobcd(dt.dt_year / 100);
575 		mc146818_write(NULL, centb, century); /* XXX softc */
576 	}
577 	splx(s);
578 }
579 
580 void
581 setstatclockrate(int arg)
582 {
583 	if (initclock_func == i8254_initclocks) {
584 		if (arg == stathz)
585 			mc146818_write(NULL, MC_REGA,
586 			    MC_BASE_32_KHz | MC_RATE_128_Hz);
587 		else
588 			mc146818_write(NULL, MC_REGA,
589 			    MC_BASE_32_KHz | MC_RATE_1024_Hz);
590 	}
591 }
592 
593 void
594 i8254_inittimecounter(void)
595 {
596 	tc_init(&i8254_timecounter);
597 }
598 
599 /*
600  * If we're using lapic to drive hardclock, we can use a simpler
601  * algorithm for the i8254 timecounters.
602  */
603 void
604 i8254_inittimecounter_simple(void)
605 {
606 	i8254_timecounter.tc_get_timecount = i8254_simple_get_timecount;
607 	i8254_timecounter.tc_counter_mask = 0x7fff;
608         i8254_timecounter.tc_frequency = TIMER_FREQ;
609 
610 	mtx_enter(&timer_mutex);
611 	rtclock_tval = 0x8000;
612 	i8254_startclock();
613 	mtx_leave(&timer_mutex);
614 
615 	tc_init(&i8254_timecounter);
616 }
617 
618 void
619 i8254_startclock(void)
620 {
621 	u_long tval = rtclock_tval;
622 
623 	outb(IO_TIMER1 + TIMER_MODE, TIMER_SEL0 | TIMER_RATEGEN | TIMER_16BIT);
624 	outb(IO_TIMER1 + TIMER_CNTR0, tval & 0xff);
625 	outb(IO_TIMER1 + TIMER_CNTR0, tval >> 8);
626 }
627 
628 u_int
629 i8254_simple_get_timecount(struct timecounter *tc)
630 {
631 	return (rtclock_tval - gettick());
632 }
633 
634 u_int
635 i8254_get_timecount(struct timecounter *tc)
636 {
637 	u_char hi, lo;
638 	u_int count;
639 	u_long ef;
640 
641 	ef = read_rflags();
642 	disable_intr();
643 
644 	outb(IO_TIMER1+TIMER_MODE, TIMER_SEL0 | TIMER_LATCH);
645 	lo = inb(IO_TIMER1+TIMER_CNTR0);
646 	hi = inb(IO_TIMER1+TIMER_CNTR0);
647 
648 	count = rtclock_tval - ((hi << 8) | lo);
649 
650 	if (count < i8254_lastcount) {
651 		i8254_ticked = 1;
652 		i8254_offset += rtclock_tval;
653 	}
654 	i8254_lastcount = count;
655 	count += i8254_offset;
656 	write_rflags(ef);
657 
658 	return (count);
659 }
660