1 /* $OpenBSD: clock.c,v 1.40 2007/08/01 13:18:18 martin 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. 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 #include <sys/types.h>
89 #include <sys/param.h>
90 #include <sys/systm.h>
91 #include <sys/time.h>
92 #include <sys/kernel.h>
93 #include <sys/device.h>
94 #include <sys/timeout.h>
95 #include <sys/timetc.h>
96 #include <sys/mutex.h>
97
98 #include <machine/cpu.h>
99 #include <machine/intr.h>
100 #include <machine/pio.h>
101 #include <machine/cpufunc.h>
102
103 #include <dev/isa/isareg.h>
104 #include <dev/isa/isavar.h>
105 #include <dev/ic/mc146818reg.h>
106 #include <dev/ic/i8253reg.h>
107 #include <i386/isa/nvram.h>
108
109 void spinwait(int);
110 int clockintr(void *);
111 int gettick(void);
112 int rtcget(mc_todregs *);
113 void rtcput(mc_todregs *);
114 int hexdectodec(int);
115 int dectohexdec(int);
116 int rtcintr(void *);
117 void rtcdrain(void *);
118
119 u_int mc146818_read(void *, u_int);
120 void mc146818_write(void *, u_int, u_int);
121
122 #if defined(I586_CPU) || defined(I686_CPU)
123 int cpuspeed;
124 #endif
125 #if defined(I486_CPU) || defined(I586_CPU) || defined(I686_CPU)
126 int clock_broken_latch;
127 #endif
128
129 /* Timecounter on the i8254 */
130 uint32_t i8254_lastcount;
131 uint32_t i8254_offset;
132 int i8254_ticked;
133 u_int i8254_get_timecount(struct timecounter *tc);
134 u_int i8254_simple_get_timecount(struct timecounter *tc);
135
136 static struct timecounter i8254_timecounter = {
137 i8254_get_timecount, NULL, ~0u, TIMER_FREQ, "i8254", 0, NULL
138 };
139 struct mutex timer_mutex = MUTEX_INITIALIZER(IPL_HIGH);
140 u_long rtclock_tval;
141
142 #define SECMIN ((unsigned)60) /* seconds per minute */
143 #define SECHOUR ((unsigned)(60*SECMIN)) /* seconds per hour */
144
145 u_int
146 mc146818_read(void *sc, u_int reg)
147 {
148 int s;
149 u_char v;
150
151 s = splhigh();
152 outb(IO_RTC, reg);
153 DELAY(1);
154 v = inb(IO_RTC+1);
155 DELAY(1);
156 splx(s);
157 return (v);
158 }
159
160 void
161 mc146818_write(void *sc, u_int reg, u_int datum)
162 {
163 int s;
164
165 s = splhigh();
166 outb(IO_RTC, reg);
167 DELAY(1);
168 outb(IO_RTC+1, datum);
169 DELAY(1);
170 splx(s);
171 }
172
173 void
174 startrtclock(void)
175 {
176 int s;
177
178 initrtclock();
179
180 /* Check diagnostic status */
181 if ((s = mc146818_read(NULL, NVRAM_DIAG)) != 0) /* XXX softc */
182 printf("RTC BIOS diagnostic error %b\n", (unsigned int) s,
183 NVRAM_DIAG_BITS);
184 }
185
186 void
187 rtcdrain(void *v)
188 {
189 struct timeout *to = (struct timeout *)v;
190
191 if (to != NULL)
192 timeout_del(to);
193
194 /*
195 * Drain any un-acknowledged RTC interrupts.
196 * See comment in cpu_initclocks().
197 */
198 while (mc146818_read(NULL, MC_REGC) & MC_REGC_PF)
199 ; /* Nothing. */
200 }
201
202 void
203 initrtclock(void)
204 {
205 mtx_enter(&timer_mutex);
206
207 /* initialize 8253 clock */
208 outb(IO_TIMER1 + TIMER_MODE, TIMER_SEL0|TIMER_RATEGEN|TIMER_16BIT);
209
210 /* Correct rounding will buy us a better precision in timekeeping */
211 outb(IO_TIMER1, TIMER_DIV(hz) % 256);
212 outb(IO_TIMER1, TIMER_DIV(hz) / 256);
213
214 rtclock_tval = TIMER_DIV(hz);
215 mtx_leave(&timer_mutex);
216 }
217
218 int
219 clockintr(void *arg)
220 {
221 struct clockframe *frame = arg; /* not strictly necessary */
222
223 if (timecounter->tc_get_timecount == i8254_get_timecount) {
224 if (i8254_ticked) {
225 i8254_ticked = 0;
226 } else {
227 i8254_offset += rtclock_tval;
228 i8254_lastcount = 0;
229 }
230 }
231
232 hardclock(frame);
233 return (1);
234 }
235
236 int
237 rtcintr(void *arg)
238 {
239 struct clockframe *frame = arg; /* not strictly necessary */
240 u_int stat = 0;
241
242 /*
243 * If rtcintr is 'late', next intr may happen immediately.
244 * Get them all. (Also, see comment in cpu_initclocks().)
245 */
246 while (mc146818_read(NULL, MC_REGC) & MC_REGC_PF) {
247 statclock(frame);
248 stat = 1;
249 }
250 return (stat);
251 }
252
253 int
254 gettick(void)
255 {
256
257 #if defined(I586_CPU) || defined(I686_CPU)
258 if (clock_broken_latch) {
259 int v1, v2, v3;
260 int w1, w2, w3;
261
262 /*
263 * Don't lock the mutex in this case, clock_broken_latch
264 * CPUs don't do MP anyway.
265 */
266
267 disable_intr();
268
269 v1 = inb(IO_TIMER1 + TIMER_CNTR0);
270 v1 |= inb(IO_TIMER1 + TIMER_CNTR0) << 8;
271 v2 = inb(IO_TIMER1 + TIMER_CNTR0);
272 v2 |= inb(IO_TIMER1 + TIMER_CNTR0) << 8;
273 v3 = inb(IO_TIMER1 + TIMER_CNTR0);
274 v3 |= inb(IO_TIMER1 + TIMER_CNTR0) << 8;
275
276 enable_intr();
277
278 if (v1 >= v2 && v2 >= v3 && v1 - v3 < 0x200)
279 return (v2);
280
281 #define _swap_val(a, b) do { \
282 int c = a; \
283 a = b; \
284 b = c; \
285 } while (0)
286
287 /* sort v1 v2 v3 */
288 if (v1 < v2)
289 _swap_val(v1, v2);
290 if (v2 < v3)
291 _swap_val(v2, v3);
292 if (v1 < v2)
293 _swap_val(v1, v2);
294
295 /* compute the middle value */
296 if (v1 - v3 < 0x200)
297 return (v2);
298 w1 = v2 - v3;
299 w2 = v3 - v1 + TIMER_DIV(hz);
300 w3 = v1 - v2;
301 if (w1 >= w2) {
302 if (w1 >= w3)
303 return (v1);
304 } else {
305 if (w2 >= w3)
306 return (v2);
307 }
308 return (v3);
309 } else
310 #endif
311 {
312 u_char lo, hi;
313 u_long ef;
314
315 mtx_enter(&timer_mutex);
316 ef = read_eflags();
317 disable_intr();
318 /* Select counter 0 and latch it. */
319 outb(IO_TIMER1 + TIMER_MODE, TIMER_SEL0 | TIMER_LATCH);
320 lo = inb(IO_TIMER1 + TIMER_CNTR0);
321 hi = inb(IO_TIMER1 + TIMER_CNTR0);
322
323 write_eflags(ef);
324 mtx_leave(&timer_mutex);
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
337 i8254_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 defined(I586_CPU) || defined(I686_CPU)
389 void
390 calibrate_cyclecounter(void)
391 {
392 unsigned long long count, last_count;
393
394 __asm __volatile("rdtsc" : "=A" (last_count));
395 delay(1000000);
396 __asm __volatile("rdtsc" : "=A" (count));
397 cpuspeed = ((count - last_count) + 999999) / 1000000;
398 }
399 #endif
400
401 void
402 i8254_initclocks(void)
403 {
404 static struct timeout rtcdrain_timeout;
405 stathz = 128;
406 profhz = 1024;
407
408 /*
409 * XXX If you're doing strange things with multiple clocks, you might
410 * want to keep track of clock handlers.
411 */
412 (void)isa_intr_establish(NULL, 0, IST_PULSE, IPL_CLOCK, clockintr,
413 0, "clock");
414 (void)isa_intr_establish(NULL, 8, IST_PULSE, IPL_CLOCK, rtcintr,
415 0, "rtc");
416
417 mc146818_write(NULL, MC_REGA, MC_BASE_32_KHz | MC_RATE_128_Hz);
418 mc146818_write(NULL, MC_REGB, MC_REGB_24HR | MC_REGB_PIE);
419
420 /*
421 * On a number of i386 systems, the rtc will fail to start when booting
422 * the system. This is due to us missing to acknowledge an interrupt
423 * during early stages of the boot process. If we do not acknowledge
424 * the interrupt, the rtc clock will not generate further interrupts.
425 * To solve this, once interrupts are enabled, use a timeout (once)
426 * to drain any un-acknowledged rtc interrupt(s).
427 */
428
429 timeout_set(&rtcdrain_timeout, rtcdrain, (void *)&rtcdrain_timeout);
430 timeout_add(&rtcdrain_timeout, 1);
431 }
432
433 int
434 rtcget(mc_todregs *regs)
435 {
436 if ((mc146818_read(NULL, MC_REGD) & MC_REGD_VRT) == 0) /* XXX softc */
437 return (-1);
438 MC146818_GETTOD(NULL, regs); /* XXX softc */
439 return (0);
440 }
441
442 void
443 rtcput(mc_todregs *regs)
444 {
445 MC146818_PUTTOD(NULL, regs); /* XXX softc */
446 }
447
448 int
449 hexdectodec(int n)
450 {
451
452 return (((n >> 4) & 0x0f) * 10 + (n & 0x0f));
453 }
454
455 int
456 dectohexdec(int n)
457 {
458
459 return ((u_char)(((n / 10) << 4) & 0xf0) | ((n % 10) & 0x0f));
460 }
461
462 static int timeset;
463
464 /*
465 * check whether the CMOS layout is "standard"-like (ie, not PS/2-like),
466 * to be called at splclock()
467 */
468 int cmoscheck(void);
469 int
470 cmoscheck(void)
471 {
472 int i;
473 unsigned short cksum = 0;
474
475 for (i = 0x10; i <= 0x2d; i++)
476 cksum += mc146818_read(NULL, i); /* XXX softc */
477
478 return (cksum == (mc146818_read(NULL, 0x2e) << 8)
479 + mc146818_read(NULL, 0x2f));
480 }
481
482 /*
483 * patchable to control century byte handling:
484 * 1: always update
485 * -1: never touch
486 * 0: try to figure out itself
487 */
488 int rtc_update_century = 0;
489
490 /*
491 * Expand a two-digit year as read from the clock chip
492 * into full width.
493 * Being here, deal with the CMOS century byte.
494 */
495 int clock_expandyear(int);
496 int
497 clock_expandyear(int clockyear)
498 {
499 int s, clockcentury, cmoscentury;
500
501 clockcentury = (clockyear < 70) ? 20 : 19;
502 clockyear += 100 * clockcentury;
503
504 if (rtc_update_century < 0)
505 return (clockyear);
506
507 s = splclock();
508 if (cmoscheck())
509 cmoscentury = mc146818_read(NULL, NVRAM_CENTURY);
510 else
511 cmoscentury = 0;
512 splx(s);
513 if (!cmoscentury) {
514 #ifdef DIAGNOSTIC
515 printf("clock: unknown CMOS layout\n");
516 #endif
517 return (clockyear);
518 }
519 cmoscentury = hexdectodec(cmoscentury);
520
521 if (cmoscentury != clockcentury) {
522 /* XXX note: saying "century is 20" might confuse the naive. */
523 printf("WARNING: NVRAM century is %d but RTC year is %d\n",
524 cmoscentury, clockyear);
525
526 /* Kludge to roll over century. */
527 if ((rtc_update_century > 0) ||
528 ((cmoscentury == 19) && (clockcentury == 20) &&
529 (clockyear == 2000))) {
530 printf("WARNING: Setting NVRAM century to %d\n",
531 clockcentury);
532 s = splclock();
533 mc146818_write(NULL, NVRAM_CENTURY,
534 dectohexdec(clockcentury));
535 splx(s);
536 }
537 } else if (cmoscentury == 19 && rtc_update_century == 0)
538 rtc_update_century = 1; /* will update later in resettodr() */
539
540 return (clockyear);
541 }
542
543 /*
544 * Initialize the time of day register, based on the time base which is, e.g.
545 * from a filesystem.
546 */
547 void
548 inittodr(time_t base)
549 {
550 struct timespec ts;
551 mc_todregs rtclk;
552 struct clock_ymdhms dt;
553 int s;
554
555
556 ts.tv_nsec = 0;
557
558 /*
559 * We mostly ignore the suggested time and go for the RTC clock time
560 * stored in the CMOS RAM. If the time can't be obtained from the
561 * CMOS, or if the time obtained from the CMOS is 5 or more years
562 * less than the suggested time, we used the suggested time. (In
563 * the latter case, it's likely that the CMOS battery has died.)
564 */
565
566 if (base < 15*SECYR) { /* if before 1985, something's odd... */
567 printf("WARNING: preposterous time in file system\n");
568 /* read the system clock anyway */
569 base = 17*SECYR + 186*SECDAY + SECDAY/2;
570 }
571
572 s = splclock();
573 if (rtcget(&rtclk)) {
574 splx(s);
575 printf("WARNING: invalid time in clock chip\n");
576 goto fstime;
577 }
578 splx(s);
579
580 dt.dt_sec = hexdectodec(rtclk[MC_SEC]);
581 dt.dt_min = hexdectodec(rtclk[MC_MIN]);
582 dt.dt_hour = hexdectodec(rtclk[MC_HOUR]);
583 dt.dt_day = hexdectodec(rtclk[MC_DOM]);
584 dt.dt_mon = hexdectodec(rtclk[MC_MONTH]);
585 dt.dt_year = clock_expandyear(hexdectodec(rtclk[MC_YEAR]));
586
587
588 /*
589 * If time_t is 32 bits, then the "End of Time" is
590 * Mon Jan 18 22:14:07 2038 (US/Eastern)
591 * This code copes with RTC's past the end of time if time_t
592 * is an int32 or less. Needed because sometimes RTCs screw
593 * up or are badly set, and that would cause the time to go
594 * negative in the calculation below, which causes Very Bad
595 * Mojo. This at least lets the user boot and fix the problem.
596 * Note the code is self eliminating once time_t goes to 64 bits.
597 */
598 if (sizeof(time_t) <= sizeof(int32_t)) {
599 if (dt.dt_year >= 2038) {
600 printf("WARNING: RTC time at or beyond 2038.\n");
601 dt.dt_year = 2037;
602 printf("WARNING: year set back to 2037.\n");
603 printf("WARNING: CHECK AND RESET THE DATE!\n");
604 }
605 }
606
607 ts.tv_sec = clock_ymdhms_to_secs(&dt) + tz.tz_minuteswest * 60;
608 if (tz.tz_dsttime)
609 ts.tv_sec -= 3600;
610
611 if (base < ts.tv_sec - 5*SECYR)
612 printf("WARNING: file system time much less than clock time\n");
613 else if (base > ts.tv_sec + 5*SECYR) {
614 printf("WARNING: clock time much less than file system time\n");
615 printf("WARNING: using file system time\n");
616 goto fstime;
617 }
618
619 tc_setclock(&ts);
620 timeset = 1;
621 return;
622
623 fstime:
624 ts.tv_sec = base;
625 tc_setclock(&ts);
626 timeset = 1;
627 printf("WARNING: CHECK AND RESET THE DATE!\n");
628 }
629
630 /*
631 * Reset the clock.
632 */
633 void
634 resettodr(void)
635 {
636 mc_todregs rtclk;
637 struct clock_ymdhms dt;
638 int diff;
639 int century;
640 int s;
641
642 /*
643 * We might have been called by boot() due to a crash early
644 * on. Don't reset the clock chip in this case.
645 */
646 if (!timeset)
647 return;
648
649 s = splclock();
650 if (rtcget(&rtclk))
651 bzero(&rtclk, sizeof(rtclk));
652 splx(s);
653
654 diff = tz.tz_minuteswest * 60;
655 if (tz.tz_dsttime)
656 diff -= 3600;
657 clock_secs_to_ymdhms(time_second - diff, &dt);
658
659 rtclk[MC_SEC] = dectohexdec(dt.dt_sec);
660 rtclk[MC_MIN] = dectohexdec(dt.dt_min);
661 rtclk[MC_HOUR] = dectohexdec(dt.dt_hour);
662 rtclk[MC_DOW] = dt.dt_wday;
663 rtclk[MC_YEAR] = dectohexdec(dt.dt_year % 100);
664 rtclk[MC_MONTH] = dectohexdec(dt.dt_mon);
665 rtclk[MC_DOM] = dectohexdec(dt.dt_day);
666 s = splclock();
667 rtcput(&rtclk);
668 if (rtc_update_century > 0) {
669 century = dectohexdec(dt.dt_year / 100);
670 mc146818_write(NULL, NVRAM_CENTURY, century); /* XXX softc */
671 }
672 splx(s);
673 }
674
675 void
676 setstatclockrate(int arg)
677 {
678 if (arg == stathz)
679 mc146818_write(NULL, MC_REGA, MC_BASE_32_KHz | MC_RATE_128_Hz);
680 else
681 mc146818_write(NULL, MC_REGA, MC_BASE_32_KHz | MC_RATE_1024_Hz);
682 }
683
684 void
685 i8254_inittimecounter(void)
686 {
687 tc_init(&i8254_timecounter);
688 }
689
690 /*
691 * If we're using lapic to drive hardclock, we can use a simpler
692 * algorithm for the i8254 timecounters.
693 */
694 void
695 i8254_inittimecounter_simple(void)
696 {
697 u_long tval = 0x8000;
698
699 i8254_timecounter.tc_get_timecount = i8254_simple_get_timecount;
700 i8254_timecounter.tc_counter_mask = 0x7fff;
701
702 i8254_timecounter.tc_frequency = TIMER_FREQ;
703
704 mtx_enter(&timer_mutex);
705 outb(IO_TIMER1 + TIMER_MODE, TIMER_SEL0 | TIMER_RATEGEN | TIMER_16BIT);
706 outb(IO_TIMER1, tval & 0xff);
707 outb(IO_TIMER1, tval >> 8);
708
709 rtclock_tval = tval;
710 mtx_leave(&timer_mutex);
711
712 tc_init(&i8254_timecounter);
713 }
714
715 u_int
716 i8254_simple_get_timecount(struct timecounter *tc)
717 {
718 return (rtclock_tval - gettick());
719 }
720
721 u_int
722 i8254_get_timecount(struct timecounter *tc)
723 {
724 u_char hi, lo;
725 u_int count;
726 u_long ef;
727
728 ef = read_eflags();
729 disable_intr();
730
731 outb(IO_TIMER1 + TIMER_MODE, TIMER_SEL0 | TIMER_LATCH);
732 lo = inb(IO_TIMER1 + TIMER_CNTR0);
733 hi = inb(IO_TIMER1 + TIMER_CNTR0);
734
735 count = rtclock_tval - ((hi << 8) | lo);
736
737 if (count < i8254_lastcount) {
738 i8254_ticked = 1;
739 i8254_offset += rtclock_tval;
740 }
741 i8254_lastcount = count;
742 count += i8254_offset;
743 write_eflags(ef);
744
745 return (count);
746 }