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