1 /* $NetBSD: clock.c,v 1.6 2006/10/13 10:09:36 hannken Exp $ */ 2 3 /*- 4 * Copyright (c) 1990 The Regents of the University of California. 5 * All rights reserved. 6 * 7 * This code is derived from software contributed to Berkeley by 8 * William Jolitz and Don Ahn. 9 * 10 * Redistribution and use in source and binary forms, with or without 11 * modification, are permitted provided that the following conditions 12 * are met: 13 * 1. Redistributions of source code must retain the above copyright 14 * notice, this list of conditions and the following disclaimer. 15 * 2. Redistributions in binary form must reproduce the above copyright 16 * notice, this list of conditions and the following disclaimer in the 17 * documentation and/or other materials provided with the distribution. 18 * 3. Neither the name of the University nor the names of its contributors 19 * may be used to endorse or promote products derived from this software 20 * without specific prior written permission. 21 * 22 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND 23 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 24 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 25 * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE 26 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 27 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 28 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 29 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 30 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 31 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 32 * SUCH DAMAGE. 33 * 34 * @(#)clock.c 7.2 (Berkeley) 5/12/91 35 */ 36 /*- 37 * Copyright (c) 1993, 1994 Charles M. Hannum. 38 * 39 * This code is derived from software contributed to Berkeley by 40 * William Jolitz and Don Ahn. 41 * 42 * Redistribution and use in source and binary forms, with or without 43 * modification, are permitted provided that the following conditions 44 * are met: 45 * 1. Redistributions of source code must retain the above copyright 46 * notice, this list of conditions and the following disclaimer. 47 * 2. Redistributions in binary form must reproduce the above copyright 48 * notice, this list of conditions and the following disclaimer in the 49 * documentation and/or other materials provided with the distribution. 50 * 3. All advertising materials mentioning features or use of this software 51 * must display the following acknowledgement: 52 * This product includes software developed by the University of 53 * California, Berkeley and its contributors. 54 * 4. Neither the name of the University nor the names of its contributors 55 * may be used to endorse or promote products derived from this software 56 * without specific prior written permission. 57 * 58 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND 59 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 60 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 61 * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE 62 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 63 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 64 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 65 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 66 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 67 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 68 * SUCH DAMAGE. 69 * 70 * @(#)clock.c 7.2 (Berkeley) 5/12/91 71 */ 72 /* 73 * Mach Operating System 74 * Copyright (c) 1991,1990,1989 Carnegie Mellon University 75 * All Rights Reserved. 76 * 77 * Permission to use, copy, modify and distribute this software and its 78 * documentation is hereby granted, provided that both the copyright 79 * notice and this permission notice appear in all copies of the 80 * software, derivative works or modified versions, and any portions 81 * thereof, and that both notices appear in supporting documentation. 82 * 83 * CARNEGIE MELLON ALLOWS FREE USE OF THIS SOFTWARE IN ITS "AS IS" 84 * CONDITION. CARNEGIE MELLON DISCLAIMS ANY LIABILITY OF ANY KIND FOR 85 * ANY DAMAGES WHATSOEVER RESULTING FROM THE USE OF THIS SOFTWARE. 86 * 87 * Carnegie Mellon requests users of this software to return to 88 * 89 * Software Distribution Coordinator or Software.Distribution@CS.CMU.EDU 90 * School of Computer Science 91 * Carnegie Mellon University 92 * Pittsburgh PA 15213-3890 93 * 94 * any improvements or extensions that they make and grant Carnegie Mellon 95 * the rights to redistribute these changes. 96 */ 97 /* 98 Copyright 1988, 1989 by Intel Corporation, Santa Clara, California. 99 100 All Rights Reserved 101 102 Permission to use, copy, modify, and distribute this software and 103 its documentation for any purpose and without fee is hereby 104 granted, provided that the above copyright notice appears in all 105 copies and that both the copyright notice and this permission notice 106 appear in supporting documentation, and that the name of Intel 107 not be used in advertising or publicity pertaining to distribution 108 of the software without specific, written prior permission. 109 110 INTEL DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE 111 INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, 112 IN NO EVENT SHALL INTEL BE LIABLE FOR ANY SPECIAL, INDIRECT, OR 113 CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM 114 LOSS OF USE, DATA OR PROFITS, WHETHER IN ACTION OF CONTRACT, 115 NEGLIGENCE, OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION 116 WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 117 */ 118 119 /* 120 * Primitive clock interrupt routines. 121 */ 122 123 #include <sys/cdefs.h> 124 __KERNEL_RCSID(0, "$NetBSD: clock.c,v 1.6 2006/10/13 10:09:36 hannken Exp $"); 125 126 /* #define CLOCKDEBUG */ 127 /* #define CLOCK_PARANOIA */ 128 129 #include "opt_multiprocessor.h" 130 #include "opt_ntp.h" 131 132 #include <sys/param.h> 133 #include <sys/systm.h> 134 #include <sys/time.h> 135 #include <sys/timetc.h> 136 #include <sys/kernel.h> 137 #include <sys/device.h> 138 139 #include <machine/cpu.h> 140 #include <machine/intr.h> 141 #include <machine/pio.h> 142 #include <machine/cpufunc.h> 143 144 #include <dev/isa/isareg.h> 145 #include <dev/isa/isavar.h> 146 #include <dev/ic/mc146818reg.h> 147 #include <dev/ic/i8253reg.h> 148 #include <i386/isa/nvram.h> 149 #include <x86/x86/tsc.h> 150 #include <dev/clock_subr.h> 151 #include <machine/specialreg.h> 152 153 #include "config_time.h" /* for CONFIG_TIME */ 154 155 #ifndef __x86_64__ 156 #include "mca.h" 157 #endif 158 #if NMCA > 0 159 #include <machine/mca_machdep.h> /* for MCA_system */ 160 #endif 161 162 #include "pcppi.h" 163 #if (NPCPPI > 0) 164 #include <dev/isa/pcppivar.h> 165 166 int sysbeepmatch(struct device *, struct cfdata *, void *); 167 void sysbeepattach(struct device *, struct device *, void *); 168 169 CFATTACH_DECL(sysbeep, sizeof(struct device), 170 sysbeepmatch, sysbeepattach, NULL, NULL); 171 172 static int ppi_attached; 173 static pcppi_tag_t ppicookie; 174 #endif /* PCPPI */ 175 176 #ifdef __x86_64__ 177 #define READ_FLAGS() read_rflags() 178 #define WRITE_FLAGS(x) write_rflags(x) 179 #else /* i386 architecture processor */ 180 #define READ_FLAGS() read_eflags() 181 #define WRITE_FLAGS(x) write_eflags(x) 182 #endif 183 184 #ifdef CLOCKDEBUG 185 int clock_debug = 0; 186 #define DPRINTF(arg) if (clock_debug) printf arg 187 #else 188 #define DPRINTF(arg) 189 #endif 190 191 int gettick(void); 192 void sysbeep(int, int); 193 static void tickle_tc(void); 194 195 static int clockintr(void *, struct intrframe); 196 static void rtcinit(void); 197 static int rtcget(mc_todregs *); 198 static void rtcput(mc_todregs *); 199 200 static int cmoscheck(void); 201 202 static int clock_expandyear(int); 203 204 static inline int gettick_broken_latch(void); 205 206 static volatile uint32_t i8254_lastcount; 207 static volatile uint32_t i8254_offset; 208 static volatile int i8254_ticked; 209 210 static struct simplelock tmr_lock = SIMPLELOCK_INITIALIZER; /* protect TC timer variables */ 211 212 inline u_int mc146818_read(void *, u_int); 213 inline void mc146818_write(void *, u_int, u_int); 214 215 u_int i8254_get_timecount(struct timecounter *); 216 static void rtc_register(void); 217 218 static struct timecounter i8254_timecounter = { 219 i8254_get_timecount, /* get_timecount */ 220 0, /* no poll_pps */ 221 ~0u, /* counter_mask */ 222 TIMER_FREQ, /* frequency */ 223 "i8254", /* name */ 224 100, /* quality */ 225 NULL, /* prev */ 226 NULL, /* next */ 227 }; 228 229 /* XXX use sc? */ 230 inline u_int 231 mc146818_read(void *sc __unused, u_int reg) 232 { 233 234 outb(IO_RTC, reg); 235 return (inb(IO_RTC+1)); 236 } 237 238 /* XXX use sc? */ 239 inline void 240 mc146818_write(void *sc __unused, u_int reg, u_int datum) 241 { 242 243 outb(IO_RTC, reg); 244 outb(IO_RTC+1, datum); 245 } 246 247 u_long rtclock_tval; /* i8254 reload value for countdown */ 248 int rtclock_init = 0; 249 250 int clock_broken_latch = 0; 251 252 #ifdef CLOCK_PARANOIA 253 static int ticks[6]; 254 #endif 255 /* 256 * i8254 latch check routine: 257 * National Geode (formerly Cyrix MediaGX) has a serious bug in 258 * its built-in i8254-compatible clock module. 259 * machdep sets the variable 'clock_broken_latch' to indicate it. 260 */ 261 262 int 263 gettick_broken_latch(void) 264 { 265 u_long flags; 266 int v1, v2, v3; 267 int w1, w2, w3; 268 269 /* Don't want someone screwing with the counter while we're here. */ 270 flags = READ_FLAGS(); 271 disable_intr(); 272 273 v1 = inb(IO_TIMER1+TIMER_CNTR0); 274 v1 |= inb(IO_TIMER1+TIMER_CNTR0) << 8; 275 v2 = inb(IO_TIMER1+TIMER_CNTR0); 276 v2 |= inb(IO_TIMER1+TIMER_CNTR0) << 8; 277 v3 = inb(IO_TIMER1+TIMER_CNTR0); 278 v3 |= inb(IO_TIMER1+TIMER_CNTR0) << 8; 279 280 WRITE_FLAGS(flags); 281 282 #ifdef CLOCK_PARANOIA 283 if (clock_debug) { 284 ticks[0] = ticks[3]; 285 ticks[1] = ticks[4]; 286 ticks[2] = ticks[5]; 287 ticks[3] = v1; 288 ticks[4] = v2; 289 ticks[5] = v3; 290 } 291 #endif 292 293 if (v1 >= v2 && v2 >= v3 && v1 - v3 < 0x200) 294 return (v2); 295 296 #define _swap_val(a, b) do { \ 297 int c = a; \ 298 a = b; \ 299 b = c; \ 300 } while (0) 301 302 /* 303 * sort v1 v2 v3 304 */ 305 if (v1 < v2) 306 _swap_val(v1, v2); 307 if (v2 < v3) 308 _swap_val(v2, v3); 309 if (v1 < v2) 310 _swap_val(v1, v2); 311 312 /* 313 * compute the middle value 314 */ 315 316 if (v1 - v3 < 0x200) 317 return (v2); 318 319 w1 = v2 - v3; 320 w2 = v3 - v1 + rtclock_tval; 321 w3 = v1 - v2; 322 if (w1 >= w2) { 323 if (w1 >= w3) 324 return (v1); 325 } else { 326 if (w2 >= w3) 327 return (v2); 328 } 329 return (v3); 330 } 331 332 /* minimal initialization, enough for delay() */ 333 void 334 initrtclock(u_long freq) 335 { 336 u_long tval; 337 /* 338 * Compute timer_count, the count-down count the timer will be 339 * set to. Also, correctly round 340 * this by carrying an extra bit through the division. 341 */ 342 tval = (freq * 2) / (u_long) hz; 343 tval = (tval / 2) + (tval & 0x1); 344 345 /* initialize 8254 clock */ 346 outb(IO_TIMER1+TIMER_MODE, TIMER_SEL0|TIMER_RATEGEN|TIMER_16BIT); 347 348 /* Correct rounding will buy us a better precision in timekeeping */ 349 outb(IO_TIMER1+TIMER_CNTR0, tval % 256); 350 outb(IO_TIMER1+TIMER_CNTR0, tval / 256); 351 352 rtclock_tval = tval ? tval : 0xFFFF; 353 rtclock_init = 1; 354 } 355 356 void 357 startrtclock(void) 358 { 359 int s; 360 361 if (!rtclock_init) 362 initrtclock(TIMER_FREQ); 363 364 /* Check diagnostic status */ 365 if ((s = mc146818_read(NULL, NVRAM_DIAG)) != 0) { /* XXX softc */ 366 char bits[128]; 367 printf("RTC BIOS diagnostic error %s\n", 368 bitmask_snprintf(s, NVRAM_DIAG_BITS, bits, sizeof(bits))); 369 } 370 371 tc_init(&i8254_timecounter); 372 373 #if defined(I586_CPU) || defined(I686_CPU) || defined(__x86_64__) 374 init_TSC(); 375 #endif 376 377 rtc_register(); 378 } 379 380 381 static void 382 tickle_tc(void) 383 { 384 #if defined(MULTIPROCESSOR) 385 struct cpu_info *ci = curcpu(); 386 /* 387 * If we are not the primary CPU, we're not allowed to do 388 * any more work. 389 */ 390 if (CPU_IS_PRIMARY(ci) == 0) 391 return; 392 #endif 393 if (rtclock_tval && timecounter->tc_get_timecount == i8254_get_timecount) { 394 simple_lock(&tmr_lock); 395 if (i8254_ticked) 396 i8254_ticked = 0; 397 else { 398 i8254_offset += rtclock_tval; 399 i8254_lastcount = 0; 400 } 401 simple_unlock(&tmr_lock); 402 } 403 404 } 405 406 static int 407 clockintr(void *arg __unused, struct intrframe frame) 408 { 409 tickle_tc(); 410 411 hardclock((struct clockframe *)&frame); 412 413 #if NMCA > 0 414 if (MCA_system) { 415 /* Reset PS/2 clock interrupt by asserting bit 7 of port 0x61 */ 416 outb(0x61, inb(0x61) | 0x80); 417 } 418 #endif 419 return -1; 420 } 421 422 u_int 423 i8254_get_timecount(struct timecounter *tc __unused) 424 { 425 u_int count; 426 u_char high, low; 427 u_long flags; 428 429 /* Don't want someone screwing with the counter while we're here. */ 430 flags = READ_FLAGS(); 431 disable_intr(); 432 433 simple_lock(&tmr_lock); 434 435 /* Select timer0 and latch counter value. */ 436 outb(IO_TIMER1 + TIMER_MODE, TIMER_SEL0 | TIMER_LATCH); 437 438 low = inb(IO_TIMER1 + TIMER_CNTR0); 439 high = inb(IO_TIMER1 + TIMER_CNTR0); 440 count = rtclock_tval - ((high << 8) | low); 441 442 if (rtclock_tval && (count < i8254_lastcount || !i8254_ticked)) { 443 i8254_ticked = 1; 444 i8254_offset += rtclock_tval; 445 } 446 447 i8254_lastcount = count; 448 count += i8254_offset; 449 450 simple_unlock(&tmr_lock); 451 452 WRITE_FLAGS(flags); 453 return (count); 454 } 455 456 int 457 gettick(void) 458 { 459 u_long flags; 460 u_char lo, hi; 461 462 if (clock_broken_latch) 463 return (gettick_broken_latch()); 464 465 /* Don't want someone screwing with the counter while we're here. */ 466 flags = READ_FLAGS(); 467 disable_intr(); 468 /* Select counter 0 and latch it. */ 469 outb(IO_TIMER1+TIMER_MODE, TIMER_SEL0 | TIMER_LATCH); 470 lo = inb(IO_TIMER1+TIMER_CNTR0); 471 hi = inb(IO_TIMER1+TIMER_CNTR0); 472 WRITE_FLAGS(flags); 473 return ((hi << 8) | lo); 474 } 475 476 /* 477 * Wait approximately `n' microseconds. 478 * Relies on timer 1 counting down from (TIMER_FREQ / hz) at TIMER_FREQ Hz. 479 * Note: timer had better have been programmed before this is first used! 480 * (Note that we use `rate generator' mode, which counts at 1:1; `square 481 * wave' mode counts at 2:1). 482 * Don't rely on this being particularly accurate. 483 */ 484 void 485 i8254_delay(int n) 486 { 487 int delay_tick, odelay_tick; 488 static const int delaytab[26] = { 489 0, 2, 3, 4, 5, 6, 7, 9, 10, 11, 490 12, 13, 15, 16, 17, 18, 19, 21, 22, 23, 491 24, 25, 27, 28, 29, 30, 492 }; 493 494 /* allow DELAY() to be used before startrtclock() */ 495 if (!rtclock_init) 496 initrtclock(TIMER_FREQ); 497 498 /* 499 * Read the counter first, so that the rest of the setup overhead is 500 * counted. 501 */ 502 odelay_tick = gettick(); 503 504 if (n <= 25) 505 n = delaytab[n]; 506 else { 507 #ifdef __GNUC__ 508 /* 509 * Calculate ((n * TIMER_FREQ) / 1e6) using explicit assembler 510 * code so we can take advantage of the intermediate 64-bit 511 * quantity to prevent loss of significance. 512 */ 513 int m; 514 __asm volatile("mul %3" 515 : "=a" (n), "=d" (m) 516 : "0" (n), "r" (TIMER_FREQ)); 517 __asm volatile("div %4" 518 : "=a" (n), "=d" (m) 519 : "0" (n), "1" (m), "r" (1000000)); 520 #else 521 /* 522 * Calculate ((n * TIMER_FREQ) / 1e6) without using floating 523 * point and without any avoidable overflows. 524 */ 525 int sec = n / 1000000, 526 usec = n % 1000000; 527 n = sec * TIMER_FREQ + 528 usec * (TIMER_FREQ / 1000000) + 529 usec * ((TIMER_FREQ % 1000000) / 1000) / 1000 + 530 usec * (TIMER_FREQ % 1000) / 1000000; 531 #endif 532 } 533 534 while (n > 0) { 535 #ifdef CLOCK_PARANOIA 536 int delta; 537 delay_tick = gettick(); 538 if (delay_tick > odelay_tick) 539 delta = rtclock_tval - (delay_tick - odelay_tick); 540 else 541 delta = odelay_tick - delay_tick; 542 if (delta < 0 || delta >= rtclock_tval / 2) { 543 DPRINTF(("delay: ignore ticks %.4x-%.4x", 544 odelay_tick, delay_tick)); 545 if (clock_broken_latch) { 546 DPRINTF((" (%.4x %.4x %.4x %.4x %.4x %.4x)\n", 547 ticks[0], ticks[1], ticks[2], 548 ticks[3], ticks[4], ticks[5])); 549 } else { 550 DPRINTF(("\n")); 551 } 552 } else 553 n -= delta; 554 #else 555 delay_tick = gettick(); 556 if (delay_tick > odelay_tick) 557 n -= rtclock_tval - (delay_tick - odelay_tick); 558 else 559 n -= odelay_tick - delay_tick; 560 #endif 561 odelay_tick = delay_tick; 562 } 563 } 564 565 #if (NPCPPI > 0) 566 int 567 sysbeepmatch(struct device *parent __unused, struct cfdata *match __unused, 568 void *aux __unused) 569 { 570 return (!ppi_attached); 571 } 572 573 void 574 sysbeepattach(struct device *parent __unused, struct device *self __unused, 575 void *aux) 576 { 577 aprint_naive("\n"); 578 aprint_normal("\n"); 579 580 ppicookie = ((struct pcppi_attach_args *)aux)->pa_cookie; 581 ppi_attached = 1; 582 } 583 #endif 584 585 void 586 sysbeep(int pitch __unused, int period __unused) 587 { 588 #if (NPCPPI > 0) 589 if (ppi_attached) 590 pcppi_bell(ppicookie, pitch, period, 0); 591 #endif 592 } 593 594 void 595 i8254_initclocks(void) 596 { 597 598 /* 599 * XXX If you're doing strange things with multiple clocks, you might 600 * want to keep track of clock handlers. 601 */ 602 (void)isa_intr_establish(NULL, 0, IST_PULSE, IPL_CLOCK, 603 (int (*)(void *))clockintr, 0); 604 } 605 606 static void 607 rtcinit(void) 608 { 609 static int first_rtcopen_ever = 1; 610 611 if (!first_rtcopen_ever) 612 return; 613 first_rtcopen_ever = 0; 614 615 mc146818_write(NULL, MC_REGA, /* XXX softc */ 616 MC_BASE_32_KHz | MC_RATE_1024_Hz); 617 mc146818_write(NULL, MC_REGB, MC_REGB_24HR); /* XXX softc */ 618 } 619 620 static int 621 rtcget(mc_todregs *regs) 622 { 623 624 rtcinit(); 625 if ((mc146818_read(NULL, MC_REGD) & MC_REGD_VRT) == 0) /* XXX softc */ 626 return (-1); 627 MC146818_GETTOD(NULL, regs); /* XXX softc */ 628 return (0); 629 } 630 631 static void 632 rtcput(mc_todregs *regs) 633 { 634 635 rtcinit(); 636 MC146818_PUTTOD(NULL, regs); /* XXX softc */ 637 } 638 639 /* 640 * check whether the CMOS layout is "standard"-like (ie, not PS/2-like), 641 * to be called at splclock() 642 */ 643 static int 644 cmoscheck(void) 645 { 646 int i; 647 unsigned short cksum = 0; 648 649 for (i = 0x10; i <= 0x2d; i++) 650 cksum += mc146818_read(NULL, i); /* XXX softc */ 651 652 return (cksum == (mc146818_read(NULL, 0x2e) << 8) 653 + mc146818_read(NULL, 0x2f)); 654 } 655 656 #if NMCA > 0 657 /* 658 * Check whether the CMOS layout is PS/2 like, to be called at splclock(). 659 */ 660 static int cmoscheckps2(void); 661 static int 662 cmoscheckps2(void) 663 { 664 #if 0 665 /* Disabled until I find out the CRC checksum algorithm IBM uses */ 666 int i; 667 unsigned short cksum = 0; 668 669 for (i = 0x10; i <= 0x31; i++) 670 cksum += mc146818_read(NULL, i); /* XXX softc */ 671 672 return (cksum == (mc146818_read(NULL, 0x32) << 8) 673 + mc146818_read(NULL, 0x33)); 674 #else 675 /* Check 'incorrect checksum' bit of IBM PS/2 Diagnostic Status Byte */ 676 return ((mc146818_read(NULL, NVRAM_DIAG) & (1<<6)) == 0); 677 #endif 678 } 679 #endif /* NMCA > 0 */ 680 681 /* 682 * patchable to control century byte handling: 683 * 1: always update 684 * -1: never touch 685 * 0: try to figure out itself 686 */ 687 int rtc_update_century = 0; 688 689 /* 690 * Expand a two-digit year as read from the clock chip 691 * into full width. 692 * Being here, deal with the CMOS century byte. 693 */ 694 static int centb = NVRAM_CENTURY; 695 static int 696 clock_expandyear(int clockyear) 697 { 698 int s, clockcentury, cmoscentury; 699 700 clockcentury = (clockyear < 70) ? 20 : 19; 701 clockyear += 100 * clockcentury; 702 703 if (rtc_update_century < 0) 704 return (clockyear); 705 706 s = splclock(); 707 if (cmoscheck()) 708 cmoscentury = mc146818_read(NULL, NVRAM_CENTURY); 709 #if NMCA > 0 710 else if (MCA_system && cmoscheckps2()) 711 cmoscentury = mc146818_read(NULL, (centb = 0x37)); 712 #endif 713 else 714 cmoscentury = 0; 715 splx(s); 716 if (!cmoscentury) { 717 #ifdef DIAGNOSTIC 718 printf("clock: unknown CMOS layout\n"); 719 #endif 720 return (clockyear); 721 } 722 cmoscentury = bcdtobin(cmoscentury); 723 724 if (cmoscentury != clockcentury) { 725 /* XXX note: saying "century is 20" might confuse the naive. */ 726 printf("WARNING: NVRAM century is %d but RTC year is %d\n", 727 cmoscentury, clockyear); 728 729 /* Kludge to roll over century. */ 730 if ((rtc_update_century > 0) || 731 ((cmoscentury == 19) && (clockcentury == 20) && 732 (clockyear == 2000))) { 733 printf("WARNING: Setting NVRAM century to %d\n", 734 clockcentury); 735 s = splclock(); 736 mc146818_write(NULL, centb, bintobcd(clockcentury)); 737 splx(s); 738 } 739 } else if (cmoscentury == 19 && rtc_update_century == 0) 740 rtc_update_century = 1; /* will update later in resettodr() */ 741 742 return (clockyear); 743 } 744 745 static int 746 rtc_get_ymdhms(todr_chip_handle_t tch __unused, struct clock_ymdhms *dt) 747 { 748 int s; 749 mc_todregs rtclk; 750 751 s = splclock(); 752 if (rtcget(&rtclk)) { 753 splx(s); 754 return -1; 755 } 756 splx(s); 757 758 dt->dt_sec = bcdtobin(rtclk[MC_SEC]); 759 dt->dt_min = bcdtobin(rtclk[MC_MIN]); 760 dt->dt_hour = bcdtobin(rtclk[MC_HOUR]); 761 dt->dt_day = bcdtobin(rtclk[MC_DOM]); 762 dt->dt_mon = bcdtobin(rtclk[MC_MONTH]); 763 dt->dt_year = clock_expandyear(bcdtobin(rtclk[MC_YEAR])); 764 765 return 0; 766 } 767 768 static int 769 rtc_set_ymdhms(todr_chip_handle_t tch __unused, struct clock_ymdhms *dt) 770 { 771 mc_todregs rtclk; 772 int century; 773 int s; 774 775 s = splclock(); 776 if (rtcget(&rtclk)) 777 memset(&rtclk, 0, sizeof(rtclk)); 778 splx(s); 779 780 rtclk[MC_SEC] = bintobcd(dt->dt_sec); 781 rtclk[MC_MIN] = bintobcd(dt->dt_min); 782 rtclk[MC_HOUR] = bintobcd(dt->dt_hour); 783 rtclk[MC_DOW] = dt->dt_wday + 1; 784 rtclk[MC_YEAR] = bintobcd(dt->dt_year % 100); 785 rtclk[MC_MONTH] = bintobcd(dt->dt_mon); 786 rtclk[MC_DOM] = bintobcd(dt->dt_day); 787 788 #ifdef DEBUG_CLOCK 789 printf("setclock: %x/%x/%x %x:%x:%x\n", rtclk[MC_YEAR], rtclk[MC_MONTH], 790 rtclk[MC_DOM], rtclk[MC_HOUR], rtclk[MC_MIN], rtclk[MC_SEC]); 791 #endif 792 s = splclock(); 793 rtcput(&rtclk); 794 if (rtc_update_century > 0) { 795 century = bintobcd(dt->dt_year / 100); 796 mc146818_write(NULL, centb, century); /* XXX softc */ 797 } 798 splx(s); 799 return 0; 800 801 } 802 803 static void 804 rtc_register(void) 805 { 806 static struct todr_chip_handle tch; 807 tch.todr_gettime_ymdhms = rtc_get_ymdhms; 808 tch.todr_settime_ymdhms = rtc_set_ymdhms; 809 tch.todr_setwen = NULL; 810 811 todr_attach(&tch); 812 } 813 814 void 815 setstatclockrate(int arg __unused) 816 { 817 } 818