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