Annotation of sys/arch/amd64/isa/clock.c, Revision 1.1.1.1
1.1 nbrk 1: /* $OpenBSD: clock.c,v 1.13 2007/08/02 16:40:27 deraadt 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 <dev/clock_subr.h>
111: #include <machine/specialreg.h>
112:
113: /* Timecounter on the i8254 */
114: u_int32_t i8254_lastcount;
115: u_int32_t i8254_offset;
116: int i8254_ticked;
117: u_int i8254_get_timecount(struct timecounter *tc);
118:
119: u_int i8254_simple_get_timecount(struct timecounter *tc);
120:
121: static struct timecounter i8254_timecounter = {
122: i8254_get_timecount, NULL, ~0u, TIMER_FREQ, "i8254", 0, NULL
123: };
124:
125: int clockintr(void *);
126: int rtcintr(void *);
127: int gettick(void);
128: void rtcdrain(void *v);
129: int rtcget(mc_todregs *);
130: void rtcput(mc_todregs *);
131: int bcdtobin(int);
132: int bintobcd(int);
133:
134: __inline u_int mc146818_read(void *, u_int);
135: __inline void mc146818_write(void *, u_int, u_int);
136:
137: __inline u_int
138: mc146818_read(void *sc, u_int reg)
139: {
140: outb(IO_RTC, reg);
141: DELAY(1);
142: return (inb(IO_RTC+1));
143: }
144:
145: __inline void
146: mc146818_write(void *sc, u_int reg, u_int datum)
147: {
148: outb(IO_RTC, reg);
149: DELAY(1);
150: outb(IO_RTC+1, datum);
151: DELAY(1);
152: }
153:
154: struct mutex timer_mutex = MUTEX_INITIALIZER(IPL_HIGH);
155:
156: u_long rtclock_tval;
157: int rtclock_init;
158:
159: /* minimal initialization, enough for delay() */
160: void
161: initrtclock(void)
162: {
163: u_long tval;
164:
165: /*
166: * Compute timer_count, the count-down count the timer will be
167: * set to. Also, correctly round
168: * this by carrying an extra bit through the division.
169: */
170: tval = (TIMER_FREQ * 2) / (u_long) hz;
171: tval = (tval / 2) + (tval & 0x1);
172:
173: mtx_enter(&timer_mutex);
174: /* initialize 8253 clock */
175: outb(IO_TIMER1+TIMER_MODE, TIMER_SEL0|TIMER_RATEGEN|TIMER_16BIT);
176:
177: /* Correct rounding will buy us a better precision in timekeeping */
178: outb(IO_TIMER1+TIMER_CNTR0, tval % 256);
179: outb(IO_TIMER1+TIMER_CNTR0, tval / 256);
180:
181: rtclock_tval = tval;
182: rtclock_init = 1;
183: mtx_leave(&timer_mutex);
184: }
185:
186: void
187: startrtclock(void)
188: {
189: int s;
190:
191: if (!rtclock_init)
192: initrtclock();
193:
194: /* Check diagnostic status */
195: if ((s = mc146818_read(NULL, NVRAM_DIAG)) != 0) /* XXX softc */
196: printf("RTC BIOS diagnostic error %b\n", s, NVRAM_DIAG_BITS);
197: }
198:
199: int
200: clockintr(void *arg)
201: {
202: struct clockframe *frame = arg;
203:
204: if (timecounter->tc_get_timecount == i8254_get_timecount) {
205: if (i8254_ticked) {
206: i8254_ticked = 0;
207: } else {
208: i8254_offset += rtclock_tval;
209: i8254_lastcount = 0;
210: }
211: }
212:
213: hardclock(frame);
214:
215: return 1;
216: }
217:
218: int
219: rtcintr(void *arg)
220: {
221: struct clockframe *frame = arg;
222: u_int stat = 0;
223:
224: /*
225: * If rtcintr is 'late', next intr may happen immediately.
226: * Get them all. (Also, see comment in cpu_initclocks().)
227: */
228: while (mc146818_read(NULL, MC_REGC) & MC_REGC_PF) {
229: statclock(frame);
230: stat = 1;
231: }
232:
233: return (stat);
234: }
235:
236: int
237: gettick(void)
238: {
239: u_long ef;
240: u_char lo, hi;
241:
242: /* Don't want someone screwing with the counter while we're here. */
243: mtx_enter(&timer_mutex);
244: ef = read_rflags();
245: disable_intr();
246: /* Select counter 0 and latch it. */
247: outb(IO_TIMER1+TIMER_MODE, TIMER_SEL0 | TIMER_LATCH);
248: lo = inb(IO_TIMER1+TIMER_CNTR0);
249: hi = inb(IO_TIMER1+TIMER_CNTR0);
250: write_rflags(ef);
251: mtx_leave(&timer_mutex);
252: return ((hi << 8) | lo);
253: }
254:
255: /*
256: * Wait "n" microseconds.
257: * Relies on timer 1 counting down from (TIMER_FREQ / hz) at TIMER_FREQ Hz.
258: * Note: timer had better have been programmed before this is first used!
259: * (Note that we use `rate generator' mode, which counts at 1:1; `square
260: * wave' mode counts at 2:1).
261: */
262: void
263: i8254_delay(int n)
264: {
265: int limit, tick, otick;
266: static const int delaytab[26] = {
267: 0, 2, 3, 4, 5, 6, 7, 9, 10, 11,
268: 12, 13, 15, 16, 17, 18, 19, 21, 22, 23,
269: 24, 25, 27, 28, 29, 30,
270: };
271:
272: /* allow DELAY() to be used before startrtclock() */
273: if (!rtclock_init)
274: initrtclock();
275:
276: /*
277: * Read the counter first, so that the rest of the setup overhead is
278: * counted.
279: */
280: otick = gettick();
281:
282: if (n <= 25)
283: n = delaytab[n];
284: else {
285: #ifdef __GNUC__
286: /*
287: * Calculate ((n * TIMER_FREQ) / 1e6) using explicit assembler
288: * code so we can take advantage of the intermediate 64-bit
289: * quantity to prevent loss of significance.
290: */
291: int m;
292: __asm __volatile("mul %3"
293: : "=a" (n), "=d" (m)
294: : "0" (n), "r" (TIMER_FREQ));
295: __asm __volatile("div %4"
296: : "=a" (n), "=d" (m)
297: : "0" (n), "1" (m), "r" (1000000));
298: #else
299: /*
300: * Calculate ((n * TIMER_FREQ) / 1e6) without using floating
301: * point and without any avoidable overflows.
302: */
303: int sec = n / 1000000,
304: usec = n % 1000000;
305: n = sec * TIMER_FREQ +
306: usec * (TIMER_FREQ / 1000000) +
307: usec * ((TIMER_FREQ % 1000000) / 1000) / 1000 +
308: usec * (TIMER_FREQ % 1000) / 1000000;
309: #endif
310: }
311:
312: limit = TIMER_FREQ / hz;
313:
314: while (n > 0) {
315: tick = gettick();
316: if (tick > otick)
317: n -= limit - (tick - otick);
318: else
319: n -= otick - tick;
320: otick = tick;
321: }
322: }
323:
324: void
325: rtcdrain(void *v)
326: {
327: struct timeout *to = (struct timeout *)v;
328:
329: if (to != NULL)
330: timeout_del(to);
331:
332: /*
333: * Drain any un-acknowledged RTC interrupts.
334: * See comment in cpu_initclocks().
335: */
336: while (mc146818_read(NULL, MC_REGC) & MC_REGC_PF)
337: ; /* Nothing. */
338: }
339:
340: void
341: i8254_initclocks(void)
342: {
343: static struct timeout rtcdrain_timeout;
344:
345: stathz = 128;
346: profhz = 1024;
347:
348: isa_intr_establish(NULL, 0, IST_PULSE, IPL_CLOCK, clockintr,
349: 0, "clock");
350: isa_intr_establish(NULL, 8, IST_PULSE, IPL_CLOCK, rtcintr, 0, "rtc");
351:
352: mc146818_write(NULL, MC_REGA, MC_BASE_32_KHz | MC_RATE_128_Hz);
353: mc146818_write(NULL, MC_REGB, MC_REGB_24HR | MC_REGB_PIE);
354:
355: /*
356: * On a number of i386 systems, the rtc will fail to start when booting
357: * the system. This is due to us missing to acknowledge an interrupt
358: * during early stages of the boot process. If we do not acknowledge
359: * the interrupt, the rtc clock will not generate further interrupts.
360: * To solve this, once interrupts are enabled, use a timeout (once)
361: * to drain any un-acknowledged rtc interrupt(s).
362: */
363: timeout_set(&rtcdrain_timeout, rtcdrain, (void *)&rtcdrain_timeout);
364: timeout_add(&rtcdrain_timeout, 1);
365: }
366:
367: int
368: rtcget(mc_todregs *regs)
369: {
370: if ((mc146818_read(NULL, MC_REGD) & MC_REGD_VRT) == 0) /* XXX softc */
371: return (-1);
372: MC146818_GETTOD(NULL, regs); /* XXX softc */
373: return (0);
374: }
375:
376: void
377: rtcput(mc_todregs *regs)
378: {
379: MC146818_PUTTOD(NULL, regs); /* XXX softc */
380: }
381:
382: int
383: bcdtobin(int n)
384: {
385: return (((n >> 4) & 0x0f) * 10 + (n & 0x0f));
386: }
387:
388: int
389: bintobcd(int n)
390: {
391: return ((u_char)(((n / 10) << 4) & 0xf0) | ((n % 10) & 0x0f));
392: }
393:
394: static int timeset;
395:
396: /*
397: * check whether the CMOS layout is "standard"-like (ie, not PS/2-like),
398: * to be called at splclock()
399: */
400: static int cmoscheck(void);
401: static int
402: cmoscheck(void)
403: {
404: int i;
405: unsigned short cksum = 0;
406:
407: for (i = 0x10; i <= 0x2d; i++)
408: cksum += mc146818_read(NULL, i); /* XXX softc */
409:
410: return (cksum == (mc146818_read(NULL, 0x2e) << 8)
411: + mc146818_read(NULL, 0x2f));
412: }
413:
414: /*
415: * patchable to control century byte handling:
416: * 1: always update
417: * -1: never touch
418: * 0: try to figure out itself
419: */
420: int rtc_update_century = 0;
421:
422: /*
423: * Expand a two-digit year as read from the clock chip
424: * into full width.
425: * Being here, deal with the CMOS century byte.
426: */
427: static int centb = NVRAM_CENTURY;
428: static int clock_expandyear(int);
429: static int
430: clock_expandyear(int clockyear)
431: {
432: int s, clockcentury, cmoscentury;
433:
434: clockcentury = (clockyear < 70) ? 20 : 19;
435: clockyear += 100 * clockcentury;
436:
437: if (rtc_update_century < 0)
438: return (clockyear);
439:
440: s = splclock();
441: if (cmoscheck())
442: cmoscentury = mc146818_read(NULL, NVRAM_CENTURY);
443: else
444: cmoscentury = 0;
445: splx(s);
446: if (!cmoscentury) {
447: #ifdef DIAGNOSTIC
448: printf("clock: unknown CMOS layout\n");
449: #endif
450: return (clockyear);
451: }
452: cmoscentury = bcdtobin(cmoscentury);
453:
454: if (cmoscentury != clockcentury) {
455: /* XXX note: saying "century is 20" might confuse the naive. */
456: printf("WARNING: NVRAM century is %d but RTC year is %d\n",
457: cmoscentury, clockyear);
458:
459: /* Kludge to roll over century. */
460: if ((rtc_update_century > 0) ||
461: ((cmoscentury == 19) && (clockcentury == 20) &&
462: (clockyear == 2000))) {
463: printf("WARNING: Setting NVRAM century to %d\n",
464: clockcentury);
465: s = splclock();
466: mc146818_write(NULL, centb, bintobcd(clockcentury));
467: splx(s);
468: }
469: } else if (cmoscentury == 19 && rtc_update_century == 0)
470: rtc_update_century = 1; /* will update later in resettodr() */
471:
472: return (clockyear);
473: }
474:
475: /*
476: * Initialize the time of day register, based on the time base which is, e.g.
477: * from a filesystem.
478: */
479: void
480: inittodr(time_t base)
481: {
482: struct timespec ts;
483: mc_todregs rtclk;
484: struct clock_ymdhms dt;
485: int s;
486:
487: ts.tv_nsec = 0;
488:
489: /*
490: * We mostly ignore the suggested time (which comes from the
491: * file system) and go for the RTC clock time stored in the
492: * CMOS RAM. If the time can't be obtained from the CMOS, or
493: * if the time obtained from the CMOS is 5 or more years less
494: * than the suggested time, we used the suggested time. (In
495: * the latter case, it's likely that the CMOS battery has
496: * died.)
497: */
498:
499: /*
500: * if the file system time is more than a year older than the
501: * kernel, warn and then set the base time to the CONFIG_TIME.
502: */
503: if (base < 30*SECYR) { /* if before 2000, something's odd... */
504: printf("WARNING: preposterous time in file system\n");
505: base = 30*SECYR;
506: }
507:
508: s = splclock();
509: if (rtcget(&rtclk)) {
510: splx(s);
511: printf("WARNING: invalid time in clock chip\n");
512: goto fstime;
513: }
514: splx(s);
515: #ifdef DEBUG_CLOCK
516: printf("readclock: %x/%x/%x %x:%x:%x\n", rtclk[MC_YEAR],
517: rtclk[MC_MONTH], rtclk[MC_DOM], rtclk[MC_HOUR], rtclk[MC_MIN],
518: rtclk[MC_SEC]);
519: #endif
520:
521: dt.dt_sec = bcdtobin(rtclk[MC_SEC]);
522: dt.dt_min = bcdtobin(rtclk[MC_MIN]);
523: dt.dt_hour = bcdtobin(rtclk[MC_HOUR]);
524: dt.dt_day = bcdtobin(rtclk[MC_DOM]);
525: dt.dt_mon = bcdtobin(rtclk[MC_MONTH]);
526: dt.dt_year = clock_expandyear(bcdtobin(rtclk[MC_YEAR]));
527:
528: /*
529: * If time_t is 32 bits, then the "End of Time" is
530: * Mon Jan 18 22:14:07 2038 (US/Eastern)
531: * This code copes with RTC's past the end of time if time_t
532: * is an int32 or less. Needed because sometimes RTCs screw
533: * up or are badly set, and that would cause the time to go
534: * negative in the calculation below, which causes Very Bad
535: * Mojo. This at least lets the user boot and fix the problem.
536: * Note the code is self eliminating once time_t goes to 64 bits.
537: */
538: if (sizeof(time_t) <= sizeof(int32_t)) {
539: if (dt.dt_year >= 2038) {
540: printf("WARNING: RTC time at or beyond 2038.\n");
541: dt.dt_year = 2037;
542: printf("WARNING: year set back to 2037.\n");
543: printf("WARNING: CHECK AND RESET THE DATE!\n");
544: }
545: }
546:
547: ts.tv_sec = clock_ymdhms_to_secs(&dt) + tz.tz_minuteswest * 60;
548: if (tz.tz_dsttime)
549: ts.tv_sec -= 3600;
550:
551: if (base != 0 && base < ts.tv_sec - 5*SECYR)
552: printf("WARNING: file system time much less than clock time\n");
553: else if (base > ts.tv_sec + 5*SECYR) {
554: printf("WARNING: clock time much less than file system time\n");
555: printf("WARNING: using file system time\n");
556: goto fstime;
557: }
558:
559: tc_setclock(&ts);
560: timeset = 1;
561: return;
562:
563: fstime:
564: ts.tv_sec = base;
565: tc_setclock(&ts);
566: timeset = 1;
567: printf("WARNING: CHECK AND RESET THE DATE!\n");
568: }
569:
570: /*
571: * Reset the clock.
572: */
573: void
574: resettodr(void)
575: {
576: mc_todregs rtclk;
577: struct clock_ymdhms dt;
578: int century, diff, s;
579:
580: /*
581: * We might have been called by boot() due to a crash early
582: * on. Don't reset the clock chip in this case.
583: */
584: if (!timeset)
585: return;
586:
587: s = splclock();
588: if (rtcget(&rtclk))
589: memset(&rtclk, 0, sizeof(rtclk));
590: splx(s);
591:
592: diff = tz.tz_minuteswest * 60;
593: if (tz.tz_dsttime)
594: diff -= 3600;
595: clock_secs_to_ymdhms(time_second - diff, &dt);
596:
597: rtclk[MC_SEC] = bintobcd(dt.dt_sec);
598: rtclk[MC_MIN] = bintobcd(dt.dt_min);
599: rtclk[MC_HOUR] = bintobcd(dt.dt_hour);
600: rtclk[MC_DOW] = dt.dt_wday + 1;
601: rtclk[MC_YEAR] = bintobcd(dt.dt_year % 100);
602: rtclk[MC_MONTH] = bintobcd(dt.dt_mon);
603: rtclk[MC_DOM] = bintobcd(dt.dt_day);
604:
605: #ifdef DEBUG_CLOCK
606: printf("setclock: %x/%x/%x %x:%x:%x\n", rtclk[MC_YEAR], rtclk[MC_MONTH],
607: rtclk[MC_DOM], rtclk[MC_HOUR], rtclk[MC_MIN], rtclk[MC_SEC]);
608: #endif
609: s = splclock();
610: rtcput(&rtclk);
611: if (rtc_update_century > 0) {
612: century = bintobcd(dt.dt_year / 100);
613: mc146818_write(NULL, centb, century); /* XXX softc */
614: }
615: splx(s);
616: }
617:
618: void
619: setstatclockrate(int arg)
620: {
621: if (arg == stathz)
622: mc146818_write(NULL, MC_REGA, MC_BASE_32_KHz | MC_RATE_128_Hz);
623: else
624: mc146818_write(NULL, MC_REGA, MC_BASE_32_KHz | MC_RATE_1024_Hz);
625: }
626:
627: void
628: i8254_inittimecounter(void)
629: {
630: tc_init(&i8254_timecounter);
631: }
632:
633: /*
634: * If we're using lapic to drive hardclock, we can use a simpler
635: * algorithm for the i8254 timecounters.
636: */
637: void
638: i8254_inittimecounter_simple(void)
639: {
640: u_long tval = 0x8000;
641:
642: i8254_timecounter.tc_get_timecount = i8254_simple_get_timecount;
643: i8254_timecounter.tc_counter_mask = 0x7fff;
644:
645: i8254_timecounter.tc_frequency = TIMER_FREQ;
646:
647: mtx_enter(&timer_mutex);
648: outb(IO_TIMER1 + TIMER_MODE, TIMER_SEL0 | TIMER_RATEGEN | TIMER_16BIT);
649: outb(IO_TIMER1 + TIMER_CNTR0, tval & 0xff);
650: outb(IO_TIMER1 + TIMER_CNTR0, tval >> 8);
651:
652: rtclock_tval = tval;
653: rtclock_init = 1;
654: mtx_leave(&timer_mutex);
655:
656: tc_init(&i8254_timecounter);
657: }
658:
659: u_int
660: i8254_simple_get_timecount(struct timecounter *tc)
661: {
662: return (rtclock_tval - gettick());
663: }
664:
665: u_int
666: i8254_get_timecount(struct timecounter *tc)
667: {
668: u_char hi, lo;
669: u_int count;
670: u_long ef;
671:
672: ef = read_rflags();
673: disable_intr();
674:
675: outb(IO_TIMER1+TIMER_MODE, TIMER_SEL0 | TIMER_LATCH);
676: lo = inb(IO_TIMER1+TIMER_CNTR0);
677: hi = inb(IO_TIMER1+TIMER_CNTR0);
678:
679: count = rtclock_tval - ((hi << 8) | lo);
680:
681: if (count < i8254_lastcount) {
682: i8254_ticked = 1;
683: i8254_offset += rtclock_tval;
684: }
685: i8254_lastcount = count;
686: count += i8254_offset;
687: write_rflags(ef);
688:
689: return (count);
690: }
CVSweb