xref: /openbsd/sys/arch/amd64/isa/clock.c (revision d415bd75)
1 /*	$OpenBSD: clock.c,v 1.42 2023/09/17 14:50:50 cheloha 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 CLOCK_DEBUG */
90 
91 #include <sys/param.h>
92 #include <sys/systm.h>
93 #include <sys/clockintr.h>
94 #include <sys/time.h>
95 #include <sys/kernel.h>
96 #include <sys/timeout.h>
97 #include <sys/timetc.h>
98 
99 #include <machine/cpu.h>
100 #include <machine/intr.h>
101 #include <machine/pio.h>
102 #include <machine/cpufunc.h>
103 
104 #include <dev/clock_subr.h>
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 
111 /* Timecounter on the i8254 */
112 u_int32_t i8254_lastcount;
113 u_int32_t i8254_offset;
114 int i8254_ticked;
115 u_int i8254_get_timecount(struct timecounter *tc);
116 
117 u_int i8254_simple_get_timecount(struct timecounter *tc);
118 
119 static struct timecounter i8254_timecounter = {
120 	.tc_get_timecount = i8254_get_timecount,
121 	.tc_counter_mask = ~0u,
122 	.tc_frequency = TIMER_FREQ,
123 	.tc_name = "i8254",
124 	.tc_quality = 0,
125 	.tc_priv = NULL,
126 	.tc_user = 0,
127 };
128 
129 int	clockintr(void *);
130 int	rtcintr(void *);
131 int	gettick(void);
132 void	rtcdrain(void *v);
133 int	rtcget(mc_todregs *);
134 void	rtcput(mc_todregs *);
135 int	bcdtobin(int);
136 int	bintobcd(int);
137 
138 u_int mc146818_read(void *, u_int);
139 void mc146818_write(void *, u_int, u_int);
140 
141 u_int
142 mc146818_read(void *sc, u_int reg)
143 {
144 	outb(IO_RTC, reg);
145 	DELAY(1);
146 	return (inb(IO_RTC+1));
147 }
148 
149 void
150 mc146818_write(void *sc, u_int reg, u_int datum)
151 {
152 	outb(IO_RTC, reg);
153 	DELAY(1);
154 	outb(IO_RTC+1, datum);
155 	DELAY(1);
156 }
157 
158 struct mutex timer_mutex = MUTEX_INITIALIZER(IPL_HIGH);
159 
160 u_long rtclock_tval;
161 
162 void
163 startclocks(void)
164 {
165 	mtx_enter(&timer_mutex);
166 	rtclock_tval = TIMER_DIV(hz);
167 	i8254_startclock();
168 	mtx_leave(&timer_mutex);
169 }
170 
171 int
172 clockintr(void *frame)
173 {
174 	if (timecounter->tc_get_timecount == i8254_get_timecount) {
175 		if (i8254_ticked) {
176 			i8254_ticked = 0;
177 		} else {
178 			i8254_offset += rtclock_tval;
179 			i8254_lastcount = 0;
180 		}
181 	}
182 
183 	clockintr_dispatch(frame);
184 
185 	return 1;
186 }
187 
188 int
189 rtcintr(void *frame)
190 {
191 	u_int stat = 0;
192 
193 	/*
194 	 * If rtcintr is 'late', next intr may happen immediately.
195 	 * Get them all. (Also, see comment in cpu_initclocks().)
196 	 */
197 	while (mc146818_read(NULL, MC_REGC) & MC_REGC_PF)
198 		stat = 1;
199 
200 	if (stat)
201 		clockintr_dispatch(frame);
202 
203 	return (stat);
204 }
205 
206 int
207 gettick(void)
208 {
209 	u_long s;
210 	u_char lo, hi;
211 
212 	/* Don't want someone screwing with the counter while we're here. */
213 	mtx_enter(&timer_mutex);
214 	s = intr_disable();
215 	/* Select counter 0 and latch it. */
216 	outb(IO_TIMER1+TIMER_MODE, TIMER_SEL0 | TIMER_LATCH);
217 	lo = inb(IO_TIMER1+TIMER_CNTR0);
218 	hi = inb(IO_TIMER1+TIMER_CNTR0);
219 	intr_restore(s);
220 	mtx_leave(&timer_mutex);
221 	return ((hi << 8) | lo);
222 }
223 
224 /*
225  * Wait "n" microseconds.
226  * Relies on timer 1 counting down from (TIMER_FREQ / hz) at TIMER_FREQ Hz.
227  * Note: timer had better have been programmed before this is first used!
228  * (Note that we use `rate generator' mode, which counts at 1:1; `square
229  * wave' mode counts at 2:1).
230  */
231 void
232 i8254_delay(int n)
233 {
234 	int limit, tick, otick;
235 	static const int delaytab[26] = {
236 		 0,  2,  3,  4,  5,  6,  7,  9, 10, 11,
237 		12, 13, 15, 16, 17, 18, 19, 21, 22, 23,
238 		24, 25, 27, 28, 29, 30,
239 	};
240 
241 	/*
242 	 * Read the counter first, so that the rest of the setup overhead is
243 	 * counted.
244 	 */
245 	otick = gettick();
246 
247 	if (n <= 25)
248 		n = delaytab[n];
249 	else {
250 		/* Force 64-bit math to avoid 32-bit overflow if possible. */
251 		n = (int64_t)n * TIMER_FREQ / 1000000;
252 	}
253 
254 	limit = TIMER_FREQ / hz;
255 
256 	while (n > 0) {
257 		tick = gettick();
258 		if (tick > otick)
259 			n -= limit - (tick - otick);
260 		else
261 			n -= otick - tick;
262 		otick = tick;
263 	}
264 }
265 
266 void
267 rtcdrain(void *v)
268 {
269 	struct timeout *to = (struct timeout *)v;
270 
271 	if (to != NULL)
272 		timeout_del(to);
273 
274 	/* Drain any un-acknowledged RTC interrupts. */
275 	while (mc146818_read(NULL, MC_REGC) & MC_REGC_PF)
276 		; /* Nothing. */
277 }
278 
279 void
280 i8254_initclocks(void)
281 {
282 	i8254_inittimecounter();	/* hook the interrupt-based i8254 tc */
283 
284 	stathz = 128;
285 	profhz = 1024;		/* XXX does not divide into 1 billion */
286 }
287 
288 void
289 i8254_start_both_clocks(void)
290 {
291 	clockintr_cpu_init(NULL);
292 
293 	/*
294 	 * While the clock interrupt handler isn't really MPSAFE, the
295 	 * i8254 can't really be used as a clock on a true MP system.
296 	 */
297 	isa_intr_establish(NULL, 0, IST_PULSE, IPL_CLOCK | IPL_MPSAFE,
298 	    clockintr, 0, "clock");
299 	isa_intr_establish(NULL, 8, IST_PULSE, IPL_STATCLOCK | IPL_MPSAFE,
300 	    rtcintr, 0, "rtc");
301 
302 	rtcstart();			/* start the mc146818 clock */
303 }
304 
305 void
306 rtcstart(void)
307 {
308 	static struct timeout rtcdrain_timeout;
309 
310 	mc146818_write(NULL, MC_REGA, MC_BASE_32_KHz | MC_RATE_128_Hz);
311 	mc146818_write(NULL, MC_REGB, MC_REGB_24HR | MC_REGB_PIE);
312 
313 	/*
314 	 * On a number of i386 systems, the rtc will fail to start when booting
315 	 * the system. This is due to us missing to acknowledge an interrupt
316 	 * during early stages of the boot process. If we do not acknowledge
317 	 * the interrupt, the rtc clock will not generate further interrupts.
318 	 * To solve this, once interrupts are enabled, use a timeout (once)
319 	 * to drain any un-acknowledged rtc interrupt(s).
320 	 */
321 	timeout_set(&rtcdrain_timeout, rtcdrain, (void *)&rtcdrain_timeout);
322 	timeout_add(&rtcdrain_timeout, 1);
323 }
324 
325 void
326 rtcstop(void)
327 {
328 	mc146818_write(NULL, MC_REGB, MC_REGB_24HR);
329 }
330 
331 int
332 rtcget(mc_todregs *regs)
333 {
334 	if ((mc146818_read(NULL, MC_REGD) & MC_REGD_VRT) == 0) /* XXX softc */
335 		return (-1);
336 	MC146818_GETTOD(NULL, regs);			/* XXX softc */
337 	return (0);
338 }
339 
340 void
341 rtcput(mc_todregs *regs)
342 {
343 	MC146818_PUTTOD(NULL, regs);			/* XXX softc */
344 }
345 
346 int
347 bcdtobin(int n)
348 {
349 	return (((n >> 4) & 0x0f) * 10 + (n & 0x0f));
350 }
351 
352 int
353 bintobcd(int n)
354 {
355 	return ((u_char)(((n / 10) << 4) & 0xf0) | ((n % 10) & 0x0f));
356 }
357 
358 /*
359  * check whether the CMOS layout is "standard"-like (ie, not PS/2-like),
360  * to be called at splclock()
361  */
362 static int cmoscheck(void);
363 static int
364 cmoscheck(void)
365 {
366 	int i;
367 	unsigned short cksum = 0;
368 
369 	for (i = 0x10; i <= 0x2d; i++)
370 		cksum += mc146818_read(NULL, i); /* XXX softc */
371 
372 	return (cksum == (mc146818_read(NULL, 0x2e) << 8)
373 			  + mc146818_read(NULL, 0x2f));
374 }
375 
376 /*
377  * patchable to control century byte handling:
378  * 1: always update
379  * -1: never touch
380  * 0: try to figure out itself
381  */
382 int rtc_update_century = 0;
383 
384 /*
385  * Expand a two-digit year as read from the clock chip
386  * into full width.
387  * Being here, deal with the CMOS century byte.
388  */
389 static int centb = NVRAM_CENTURY;
390 static int clock_expandyear(int);
391 static int
392 clock_expandyear(int clockyear)
393 {
394 	int s, clockcentury, cmoscentury;
395 
396 	clockcentury = (clockyear < 70) ? 20 : 19;
397 	clockyear += 100 * clockcentury;
398 
399 	if (rtc_update_century < 0)
400 		return (clockyear);
401 
402 	s = splclock();
403 	if (cmoscheck())
404 		cmoscentury = mc146818_read(NULL, NVRAM_CENTURY);
405 	else
406 		cmoscentury = 0;
407 	splx(s);
408 	if (!cmoscentury)
409 		return (clockyear);
410 
411 	cmoscentury = bcdtobin(cmoscentury);
412 
413 	if (cmoscentury != clockcentury) {
414 		/* XXX note: saying "century is 20" might confuse the naive. */
415 		printf("WARNING: NVRAM century is %d but RTC year is %d\n",
416 		       cmoscentury, clockyear);
417 
418 		/* Kludge to roll over century. */
419 		if ((rtc_update_century > 0) ||
420 		    ((cmoscentury == 19) && (clockcentury == 20) &&
421 		     (clockyear == 2000))) {
422 			printf("WARNING: Setting NVRAM century to %d\n",
423 			       clockcentury);
424 			s = splclock();
425 			mc146818_write(NULL, centb, bintobcd(clockcentury));
426 			splx(s);
427 		}
428 	} else if (cmoscentury == 19 && rtc_update_century == 0)
429 		rtc_update_century = 1; /* will update later in resettodr() */
430 
431 	return (clockyear);
432 }
433 
434 int
435 rtcgettime(struct todr_chip_handle *handle, struct timeval *tv)
436 {
437 	mc_todregs rtclk;
438 	struct clock_ymdhms dt;
439 	int s;
440 
441 	s = splclock();
442 	if (rtcget(&rtclk)) {
443 		splx(s);
444 		return EINVAL;
445 	}
446 	splx(s);
447 
448 #ifdef CLOCK_DEBUG
449 	printf("readclock: %x/%x/%x %x:%x:%x\n", rtclk[MC_YEAR],
450 	    rtclk[MC_MONTH], rtclk[MC_DOM], rtclk[MC_HOUR], rtclk[MC_MIN],
451 	    rtclk[MC_SEC]);
452 #endif
453 
454 	dt.dt_sec = bcdtobin(rtclk[MC_SEC]);
455 	dt.dt_min = bcdtobin(rtclk[MC_MIN]);
456 	dt.dt_hour = bcdtobin(rtclk[MC_HOUR]);
457 	dt.dt_day = bcdtobin(rtclk[MC_DOM]);
458 	dt.dt_mon = bcdtobin(rtclk[MC_MONTH]);
459 	dt.dt_year = clock_expandyear(bcdtobin(rtclk[MC_YEAR]));
460 
461 	tv->tv_sec = clock_ymdhms_to_secs(&dt) - utc_offset;
462 	tv->tv_usec = 0;
463 	return 0;
464 }
465 
466 int
467 rtcsettime(struct todr_chip_handle *handle, struct timeval *tv)
468 {
469 	mc_todregs rtclk;
470 	struct clock_ymdhms dt;
471 	int century, s;
472 
473 	s = splclock();
474 	if (rtcget(&rtclk))
475 		memset(&rtclk, 0, sizeof(rtclk));
476 	splx(s);
477 
478 	clock_secs_to_ymdhms(tv->tv_sec + utc_offset, &dt);
479 
480 	rtclk[MC_SEC] = bintobcd(dt.dt_sec);
481 	rtclk[MC_MIN] = bintobcd(dt.dt_min);
482 	rtclk[MC_HOUR] = bintobcd(dt.dt_hour);
483 	rtclk[MC_DOW] = dt.dt_wday + 1;
484 	rtclk[MC_YEAR] = bintobcd(dt.dt_year % 100);
485 	rtclk[MC_MONTH] = bintobcd(dt.dt_mon);
486 	rtclk[MC_DOM] = bintobcd(dt.dt_day);
487 
488 #ifdef CLOCK_DEBUG
489 	printf("setclock: %x/%x/%x %x:%x:%x\n", rtclk[MC_YEAR], rtclk[MC_MONTH],
490 	   rtclk[MC_DOM], rtclk[MC_HOUR], rtclk[MC_MIN], rtclk[MC_SEC]);
491 #endif
492 
493 	s = splclock();
494 	rtcput(&rtclk);
495 	if (rtc_update_century > 0) {
496 		century = bintobcd(dt.dt_year / 100);
497 		mc146818_write(NULL, centb, century); /* XXX softc */
498 	}
499 	splx(s);
500 	return 0;
501 }
502 
503 struct todr_chip_handle rtc_todr;
504 
505 void
506 rtcinit(void)
507 {
508 	rtc_todr.todr_gettime = rtcgettime;
509 	rtc_todr.todr_settime = rtcsettime;
510 	rtc_todr.todr_quality = 0;
511 	todr_attach(&rtc_todr);
512 }
513 
514 void
515 setstatclockrate(int arg)
516 {
517 	if (initclock_func == i8254_initclocks) {
518 		if (arg == stathz)
519 			mc146818_write(NULL, MC_REGA,
520 			    MC_BASE_32_KHz | MC_RATE_128_Hz);
521 		else
522 			mc146818_write(NULL, MC_REGA,
523 			    MC_BASE_32_KHz | MC_RATE_1024_Hz);
524 	}
525 }
526 
527 void
528 i8254_inittimecounter(void)
529 {
530 	tc_init(&i8254_timecounter);
531 }
532 
533 /*
534  * If we're using lapic to drive hardclock, we can use a simpler
535  * algorithm for the i8254 timecounters.
536  */
537 void
538 i8254_inittimecounter_simple(void)
539 {
540 	i8254_timecounter.tc_get_timecount = i8254_simple_get_timecount;
541 	i8254_timecounter.tc_counter_mask = 0x7fff;
542         i8254_timecounter.tc_frequency = TIMER_FREQ;
543 
544 	mtx_enter(&timer_mutex);
545 	rtclock_tval = 0x8000;
546 	i8254_startclock();
547 	mtx_leave(&timer_mutex);
548 
549 	tc_init(&i8254_timecounter);
550 }
551 
552 void
553 i8254_startclock(void)
554 {
555 	u_long tval = rtclock_tval;
556 
557 	outb(IO_TIMER1 + TIMER_MODE, TIMER_SEL0 | TIMER_RATEGEN | TIMER_16BIT);
558 	outb(IO_TIMER1 + TIMER_CNTR0, tval & 0xff);
559 	outb(IO_TIMER1 + TIMER_CNTR0, tval >> 8);
560 }
561 
562 u_int
563 i8254_simple_get_timecount(struct timecounter *tc)
564 {
565 	return (rtclock_tval - gettick());
566 }
567 
568 u_int
569 i8254_get_timecount(struct timecounter *tc)
570 {
571 	u_char hi, lo;
572 	u_int count;
573 	u_long s;
574 
575 	s = intr_disable();
576 
577 	outb(IO_TIMER1+TIMER_MODE, TIMER_SEL0 | TIMER_LATCH);
578 	lo = inb(IO_TIMER1+TIMER_CNTR0);
579 	hi = inb(IO_TIMER1+TIMER_CNTR0);
580 
581 	count = rtclock_tval - ((hi << 8) | lo);
582 
583 	if (count < i8254_lastcount) {
584 		i8254_ticked = 1;
585 		i8254_offset += rtclock_tval;
586 	}
587 	i8254_lastcount = count;
588 	count += i8254_offset;
589 
590 	intr_restore(s);
591 
592 	return (count);
593 }
594