Annotation of sys/arch/i386/isa/clock.c, Revision 1.1.1.1
1.1 nbrk 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: }
CVSweb