clock.c revision 1.66
1/* $OpenBSD: clock.c,v 1.66 2023/08/22 17:13:22 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 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/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 108void spinwait(int); 109int clockintr(void *); 110int gettick(void); 111int rtcget(mc_todregs *); 112void rtcput(mc_todregs *); 113int rtcintr(void *); 114void rtcdrain(void *); 115int calibrate_cyclecounter_ctr(void); 116 117u_int mc146818_read(void *, u_int); 118void mc146818_write(void *, u_int, u_int); 119 120int cpuspeed; 121int clock_broken_latch; 122 123/* Timecounter on the i8254 */ 124uint32_t i8254_lastcount; 125uint32_t i8254_offset; 126int i8254_ticked; 127u_int i8254_get_timecount(struct timecounter *tc); 128u_int i8254_simple_get_timecount(struct timecounter *tc); 129 130static 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}; 139struct mutex timer_mutex = MUTEX_INITIALIZER(IPL_HIGH); 140u_long rtclock_tval; 141 142#define SECMIN ((unsigned)60) /* seconds per minute */ 143#define SECHOUR ((unsigned)(60*SECMIN)) /* seconds per hour */ 144 145u_int 146mc146818_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 160void 161mc146818_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 173void 174startclocks(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 189void 190rtcdrain(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 202int 203clockintr(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 217int 218rtcintr(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 236int 237gettick(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 */ 315void 316i8254_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 367int 368calibrate_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 407void 408calibrate_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 422void 423i8254_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 clockintr_init(0); 430 431 clockintr_cpu_init(NULL); 432 433 /* 434 * When using i8254 for clock, we also use the rtc for profclock. 435 * 436 * These IRQs are not MP-safe, but it is harmless to lie about it 437 * because we cannot reach this point unless we are only booting 438 * a single CPU. 439 */ 440 (void)isa_intr_establish(NULL, 0, IST_PULSE, IPL_CLOCK | IPL_MPSAFE, 441 clockintr, 0, "clock"); 442 (void)isa_intr_establish(NULL, 8, IST_PULSE, IPL_STATCLOCK | IPL_MPSAFE, 443 rtcintr, 0, "rtc"); 444 445 rtcstart(); /* start the mc146818 clock */ 446} 447 448void 449rtcstart(void) 450{ 451 static struct timeout rtcdrain_timeout; 452 453 mc146818_write(NULL, MC_REGA, MC_BASE_32_KHz | MC_RATE_128_Hz); 454 mc146818_write(NULL, MC_REGB, MC_REGB_24HR | MC_REGB_PIE); 455 456 /* 457 * On a number of i386 systems, the rtc will fail to start when booting 458 * the system. This is due to us missing to acknowledge an interrupt 459 * during early stages of the boot process. If we do not acknowledge 460 * the interrupt, the rtc clock will not generate further interrupts. 461 * To solve this, once interrupts are enabled, use a timeout (once) 462 * to drain any un-acknowledged rtc interrupt(s). 463 */ 464 465 timeout_set(&rtcdrain_timeout, rtcdrain, (void *)&rtcdrain_timeout); 466 timeout_add(&rtcdrain_timeout, 1); 467} 468 469void 470rtcstop(void) 471{ 472 mc146818_write(NULL, MC_REGB, MC_REGB_24HR); 473} 474 475int 476rtcget(mc_todregs *regs) 477{ 478 if ((mc146818_read(NULL, MC_REGD) & MC_REGD_VRT) == 0) /* XXX softc */ 479 return (-1); 480 MC146818_GETTOD(NULL, regs); /* XXX softc */ 481 return (0); 482} 483 484void 485rtcput(mc_todregs *regs) 486{ 487 MC146818_PUTTOD(NULL, regs); /* XXX softc */ 488} 489 490int 491bcdtobin(int n) 492{ 493 return (((n >> 4) & 0x0f) * 10 + (n & 0x0f)); 494} 495 496int 497bintobcd(int n) 498{ 499 return ((u_char)(((n / 10) << 4) & 0xf0) | ((n % 10) & 0x0f)); 500} 501 502/* 503 * check whether the CMOS layout is "standard"-like (ie, not PS/2-like), 504 * to be called at splclock() 505 */ 506int cmoscheck(void); 507int 508cmoscheck(void) 509{ 510 int i; 511 unsigned short cksum = 0; 512 513 for (i = 0x10; i <= 0x2d; i++) 514 cksum += mc146818_read(NULL, i); /* XXX softc */ 515 516 return (cksum == (mc146818_read(NULL, 0x2e) << 8) 517 + mc146818_read(NULL, 0x2f)); 518} 519 520/* 521 * patchable to control century byte handling: 522 * 1: always update 523 * -1: never touch 524 * 0: try to figure out itself 525 */ 526int rtc_update_century = 0; 527 528/* 529 * Expand a two-digit year as read from the clock chip 530 * into full width. 531 * Being here, deal with the CMOS century byte. 532 */ 533int clock_expandyear(int); 534int 535clock_expandyear(int clockyear) 536{ 537 int s, clockcentury, cmoscentury; 538 539 clockcentury = (clockyear < 70) ? 20 : 19; 540 clockyear += 100 * clockcentury; 541 542 if (rtc_update_century < 0) 543 return (clockyear); 544 545 s = splclock(); 546 if (cmoscheck()) 547 cmoscentury = mc146818_read(NULL, NVRAM_CENTURY); 548 else 549 cmoscentury = 0; 550 splx(s); 551 if (!cmoscentury) { 552#ifdef DIAGNOSTIC 553 printf("clock: unknown CMOS layout\n"); 554#endif 555 return (clockyear); 556 } 557 cmoscentury = bcdtobin(cmoscentury); 558 559 if (cmoscentury != clockcentury) { 560 /* XXX note: saying "century is 20" might confuse the naive. */ 561 printf("WARNING: NVRAM century is %d but RTC year is %d\n", 562 cmoscentury, clockyear); 563 564 /* Kludge to roll over century. */ 565 if ((rtc_update_century > 0) || 566 ((cmoscentury == 19) && (clockcentury == 20) && 567 (clockyear == 2000))) { 568 printf("WARNING: Setting NVRAM century to %d\n", 569 clockcentury); 570 s = splclock(); 571 mc146818_write(NULL, NVRAM_CENTURY, 572 bintobcd(clockcentury)); 573 splx(s); 574 } 575 } else if (cmoscentury == 19 && rtc_update_century == 0) 576 rtc_update_century = 1; /* will update later in resettodr() */ 577 578 return (clockyear); 579} 580 581int 582rtcgettime(struct todr_chip_handle *handle, struct timeval *tv) 583{ 584 mc_todregs rtclk; 585 struct clock_ymdhms dt; 586 int s; 587 588 s = splclock(); 589 if (rtcget(&rtclk)) { 590 splx(s); 591 return EINVAL; 592 } 593 splx(s); 594 595#ifdef CLOCK_DEBUG 596 printf("readclock: %x/%x/%x %x:%x:%x\n", rtclk[MC_YEAR], 597 rtclk[MC_MONTH], rtclk[MC_DOM], rtclk[MC_HOUR], rtclk[MC_MIN], 598 rtclk[MC_SEC]); 599#endif 600 601 dt.dt_sec = bcdtobin(rtclk[MC_SEC]); 602 dt.dt_min = bcdtobin(rtclk[MC_MIN]); 603 dt.dt_hour = bcdtobin(rtclk[MC_HOUR]); 604 dt.dt_day = bcdtobin(rtclk[MC_DOM]); 605 dt.dt_mon = bcdtobin(rtclk[MC_MONTH]); 606 dt.dt_year = clock_expandyear(bcdtobin(rtclk[MC_YEAR])); 607 608 tv->tv_sec = clock_ymdhms_to_secs(&dt) - utc_offset; 609 tv->tv_usec = 0; 610 return 0; 611} 612 613int 614rtcsettime(struct todr_chip_handle *handle, struct timeval *tv) 615{ 616 mc_todregs rtclk; 617 struct clock_ymdhms dt; 618 int century, s; 619 620 s = splclock(); 621 if (rtcget(&rtclk)) 622 memset(&rtclk, 0, sizeof(rtclk)); 623 splx(s); 624 625 clock_secs_to_ymdhms(tv->tv_sec + utc_offset, &dt); 626 627 rtclk[MC_SEC] = bintobcd(dt.dt_sec); 628 rtclk[MC_MIN] = bintobcd(dt.dt_min); 629 rtclk[MC_HOUR] = bintobcd(dt.dt_hour); 630 rtclk[MC_DOW] = dt.dt_wday + 1; 631 rtclk[MC_YEAR] = bintobcd(dt.dt_year % 100); 632 rtclk[MC_MONTH] = bintobcd(dt.dt_mon); 633 rtclk[MC_DOM] = bintobcd(dt.dt_day); 634 635#ifdef CLOCK_DEBUG 636 printf("setclock: %x/%x/%x %x:%x:%x\n", rtclk[MC_YEAR], rtclk[MC_MONTH], 637 rtclk[MC_DOM], rtclk[MC_HOUR], rtclk[MC_MIN], rtclk[MC_SEC]); 638#endif 639 640 s = splclock(); 641 rtcput(&rtclk); 642 if (rtc_update_century > 0) { 643 century = bintobcd(dt.dt_year / 100); 644 mc146818_write(NULL, NVRAM_CENTURY, century); /* XXX softc */ 645 } 646 splx(s); 647 return 0; 648} 649 650struct todr_chip_handle rtc_todr; 651 652void 653rtcinit(void) 654{ 655 rtc_todr.todr_gettime = rtcgettime; 656 rtc_todr.todr_settime = rtcsettime; 657 rtc_todr.todr_quality = 0; 658 todr_attach(&rtc_todr); 659} 660 661void 662setstatclockrate(int arg) 663{ 664 if (initclock_func == i8254_initclocks) { 665 if (arg == stathz) 666 mc146818_write(NULL, MC_REGA, 667 MC_BASE_32_KHz | MC_RATE_128_Hz); 668 else 669 mc146818_write(NULL, MC_REGA, 670 MC_BASE_32_KHz | MC_RATE_1024_Hz); 671 } 672} 673 674void 675i8254_inittimecounter(void) 676{ 677 tc_init(&i8254_timecounter); 678} 679 680/* 681 * If we're using lapic to drive hardclock, we can use a simpler 682 * algorithm for the i8254 timecounters. 683 */ 684void 685i8254_inittimecounter_simple(void) 686{ 687 i8254_timecounter.tc_get_timecount = i8254_simple_get_timecount; 688 i8254_timecounter.tc_counter_mask = 0x7fff; 689 i8254_timecounter.tc_frequency = TIMER_FREQ; 690 691 mtx_enter(&timer_mutex); 692 rtclock_tval = 0x8000; 693 i8254_startclock(); 694 mtx_leave(&timer_mutex); 695 696 tc_init(&i8254_timecounter); 697} 698 699void 700i8254_startclock(void) 701{ 702 u_long tval = rtclock_tval; 703 704 outb(IO_TIMER1 + TIMER_MODE, TIMER_SEL0 | TIMER_RATEGEN | TIMER_16BIT); 705 outb(IO_TIMER1 + TIMER_CNTR0, tval & 0xff); 706 outb(IO_TIMER1 + TIMER_CNTR0, tval >> 8); 707} 708 709u_int 710i8254_simple_get_timecount(struct timecounter *tc) 711{ 712 return (rtclock_tval - gettick()); 713} 714 715u_int 716i8254_get_timecount(struct timecounter *tc) 717{ 718 u_char hi, lo; 719 u_int count; 720 u_long s; 721 722 s = intr_disable(); 723 724 outb(IO_TIMER1 + TIMER_MODE, TIMER_SEL0 | TIMER_LATCH); 725 lo = inb(IO_TIMER1 + TIMER_CNTR0); 726 hi = inb(IO_TIMER1 + TIMER_CNTR0); 727 728 count = rtclock_tval - ((hi << 8) | lo); 729 730 if (count < i8254_lastcount) { 731 i8254_ticked = 1; 732 i8254_offset += rtclock_tval; 733 } 734 i8254_lastcount = count; 735 count += i8254_offset; 736 737 intr_restore(s); 738 739 return (count); 740} 741