clock.c revision 1.39
1/* $OpenBSD: clock.c,v 1.39 2007/03/19 09:29:33 art 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 68Permission to use, copy, modify, and distribute this software and 69its documentation for any purpose and without fee is hereby 70granted, provided that the above copyright notice appears in all 71copies and that both the copyright notice and this permission notice 72appear in supporting documentation, and that the name of Intel 73not be used in advertising or publicity pertaining to distribution 74of the software without specific, written prior permission. 75 76INTEL DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE 77INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, 78IN NO EVENT SHALL INTEL BE LIABLE FOR ANY SPECIAL, INDIRECT, OR 79CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM 80LOSS OF USE, DATA OR PROFITS, WHETHER IN ACTION OF CONTRACT, 81NEGLIGENCE, OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION 82WITH 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 <i386/isa/nvram.h> 107#include <i386/isa/timerreg.h> 108 109void spinwait(int); 110int clockintr(void *); 111int gettick(void); 112int rtcget(mc_todregs *); 113void rtcput(mc_todregs *); 114int hexdectodec(int); 115int dectohexdec(int); 116int rtcintr(void *); 117void rtcdrain(void *); 118 119u_int mc146818_read(void *, u_int); 120void mc146818_write(void *, u_int, u_int); 121 122#if defined(I586_CPU) || defined(I686_CPU) 123int cpuspeed; 124#endif 125#if defined(I486_CPU) || defined(I586_CPU) || defined(I686_CPU) 126int clock_broken_latch; 127#endif 128 129/* Timecounter on the i8254 */ 130uint32_t i8254_lastcount; 131uint32_t i8254_offset; 132int i8254_ticked; 133u_int i8254_get_timecount(struct timecounter *tc); 134u_int i8254_simple_get_timecount(struct timecounter *tc); 135 136static struct timecounter i8254_timecounter = { 137 i8254_get_timecount, NULL, ~0u, TIMER_FREQ, "i8254", 0, NULL 138}; 139struct mutex timer_mutex = MUTEX_INITIALIZER(IPL_HIGH); 140u_long rtclock_tval; 141 142#define SECMIN ((unsigned)60) /* seconds per minute */ 143#define SECHOUR ((unsigned)(60*SECMIN)) /* seconds per hour */ 144 145u_int 146mc146818_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 160void 161mc146818_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 173void 174startrtclock(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 186void 187rtcdrain(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 202void 203initrtclock(void) 204{ 205 mtx_enter(&timer_mutex); 206 207 /* initialize 8253 clock */ 208 outb(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 218int 219clockintr(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 236int 237rtcintr(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 253int 254gettick(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(TIMER_CNTR0); 270 v1 |= inb(TIMER_CNTR0) << 8; 271 v2 = inb(TIMER_CNTR0); 272 v2 |= inb(TIMER_CNTR0) << 8; 273 v3 = inb(TIMER_CNTR0); 274 v3 |= inb(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(TIMER_MODE, TIMER_SEL0 | TIMER_LATCH); 320 lo = inb(TIMER_CNTR0); 321 hi = inb(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 */ 336void 337i8254_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) 389void 390calibrate_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 401void 402i8254_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 433int 434rtcget(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 442void 443rtcput(mc_todregs *regs) 444{ 445 MC146818_PUTTOD(NULL, regs); /* XXX softc */ 446} 447 448int 449hexdectodec(int n) 450{ 451 452 return (((n >> 4) & 0x0f) * 10 + (n & 0x0f)); 453} 454 455int 456dectohexdec(int n) 457{ 458 459 return ((u_char)(((n / 10) << 4) & 0xf0) | ((n % 10) & 0x0f)); 460} 461 462static 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 */ 468int cmoscheck(void); 469int 470cmoscheck(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 */ 488int 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 */ 495int clock_expandyear(int); 496int 497clock_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 */ 547void 548inittodr(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 623fstime: 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 */ 633void 634resettodr(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 675void 676setstatclockrate(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 684void 685i8254_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 */ 694void 695i8254_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(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 715u_int 716i8254_simple_get_timecount(struct timecounter *tc) 717{ 718 return (rtclock_tval - gettick()); 719} 720 721u_int 722i8254_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(TIMER_MODE, TIMER_SEL0 | TIMER_LATCH); 732 lo = inb(TIMER_CNTR0); 733 hi = inb(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} 747