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