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