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