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