1 /* $OpenBSD: clock.c,v 1.30 2003/06/02 23:27:47 millert 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 96 #include <machine/cpu.h> 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 <i386/isa/nvram.h> 106 #include <i386/isa/timerreg.h> 107 108 #include "pcppi.h" 109 #if (NPCPPI > 0) 110 #include <dev/isa/pcppivar.h> 111 112 #define __BROKEN_INDIRECT_CONFIG /* XXX */ 113 #ifdef __BROKEN_INDIRECT_CONFIG 114 int sysbeepmatch(struct device *, void *, void *); 115 #else 116 int sysbeepmatch(struct device *, struct cfdata *, void *); 117 #endif 118 void sysbeepattach(struct device *, struct device *, void *); 119 120 struct cfattach sysbeep_ca = { 121 sizeof(struct device), sysbeepmatch, sysbeepattach 122 }; 123 124 struct cfdriver sysbeep_cd = { 125 NULL, "sysbeep", DV_DULL 126 }; 127 128 static int ppi_attached; 129 static pcppi_tag_t ppicookie; 130 #endif /* PCPPI */ 131 132 void spinwait(int); 133 void findcpuspeed(void); 134 int clockintr(void *); 135 int gettick(void); 136 void sysbeep(int, int); 137 int rtcget(mc_todregs *); 138 void rtcput(mc_todregs *); 139 int hexdectodec(int); 140 int dectohexdec(int); 141 int rtcintr(void *); 142 void rtcdrain(void *); 143 144 u_int mc146818_read(void *, u_int); 145 void mc146818_write(void *, u_int, u_int); 146 147 #if defined(I486_CPU) || defined(I586_CPU) || defined(I686_CPU) 148 int pentium_mhz, clock_broken_latch; 149 #endif 150 151 #define SECMIN ((unsigned)60) /* seconds per minute */ 152 #define SECHOUR ((unsigned)(60*SECMIN)) /* seconds per hour */ 153 154 u_int 155 mc146818_read(sc, reg) 156 void *sc; /* XXX use it? */ 157 u_int reg; 158 { 159 int s; 160 u_char v; 161 162 s = splhigh(); 163 outb(IO_RTC, reg); 164 DELAY(1); 165 v = inb(IO_RTC+1); 166 DELAY(1); 167 splx(s); 168 return (v); 169 } 170 171 void 172 mc146818_write(sc, reg, datum) 173 void *sc; /* XXX use it? */ 174 u_int reg, datum; 175 { 176 int s; 177 178 s = splhigh(); 179 outb(IO_RTC, reg); 180 DELAY(1); 181 outb(IO_RTC+1, datum); 182 DELAY(1); 183 splx(s); 184 } 185 186 void 187 startrtclock() 188 { 189 int s; 190 191 findcpuspeed(); /* use the clock (while it's free) 192 to find the cpu speed */ 193 initrtclock(); 194 195 /* Check diagnostic status */ 196 if ((s = mc146818_read(NULL, NVRAM_DIAG)) != 0) /* XXX softc */ 197 printf("RTC BIOS diagnostic error %b\n", (unsigned int) s, 198 NVRAM_DIAG_BITS); 199 } 200 201 void 202 rtcdrain(void *v) 203 { 204 struct timeout *to = (struct timeout *)v; 205 206 if (to != NULL) 207 timeout_del(to); 208 209 /* 210 * Drain any un-acknowledged RTC interrupts. 211 * See comment in cpu_initclocks(). 212 */ 213 while (mc146818_read(NULL, MC_REGC) & MC_REGC_PF) 214 ; /* Nothing. */ 215 } 216 217 void 218 initrtclock() 219 { 220 /* initialize 8253 clock */ 221 outb(TIMER_MODE, TIMER_SEL0|TIMER_RATEGEN|TIMER_16BIT); 222 223 /* Correct rounding will buy us a better precision in timekeeping */ 224 outb(IO_TIMER1, TIMER_DIV(hz) % 256); 225 outb(IO_TIMER1, TIMER_DIV(hz) / 256); 226 } 227 228 int 229 clockintr(arg) 230 void *arg; 231 { 232 struct clockframe *frame = arg; /* not strictly necessary */ 233 234 hardclock(frame); 235 return (1); 236 } 237 238 int 239 rtcintr(arg) 240 void *arg; 241 { 242 struct clockframe *frame = arg; /* not strictly necessary */ 243 u_int stat = 0; 244 245 /* 246 * If rtcintr is 'late', next intr may happen immediately. 247 * Get them all. (Also, see comment in cpu_initclocks().) 248 */ 249 while (mc146818_read(NULL, MC_REGC) & MC_REGC_PF) { 250 statclock(frame); 251 stat = 1; 252 } 253 return (stat); 254 } 255 256 int 257 gettick() 258 { 259 260 #if defined(I586_CPU) || defined(I686_CPU) 261 if (clock_broken_latch) { 262 int v1, v2, v3; 263 int w1, w2, w3; 264 265 disable_intr(); 266 267 v1 = inb(TIMER_CNTR0); 268 v1 |= inb(TIMER_CNTR0) << 8; 269 v2 = inb(TIMER_CNTR0); 270 v2 |= inb(TIMER_CNTR0) << 8; 271 v3 = inb(TIMER_CNTR0); 272 v3 |= inb(TIMER_CNTR0) << 8; 273 274 enable_intr(); 275 276 if (v1 >= v2 && v2 >= v3 && v1 - v3 < 0x200) 277 return (v2); 278 279 #define _swap_val(a, b) do { \ 280 int c = a; \ 281 a = b; \ 282 b = c; \ 283 } while (0) 284 285 /* sort v1 v2 v3 */ 286 if (v1 < v2) 287 _swap_val(v1, v2); 288 if (v2 < v3) 289 _swap_val(v2, v3); 290 if (v1 < v2) 291 _swap_val(v1, v2); 292 293 /* compute the middle value */ 294 if (v1 - v3 < 0x200) 295 return (v2); 296 w1 = v2 - v3; 297 w2 = v3 - v1 + TIMER_DIV(hz); 298 w3 = v1 - v2; 299 if (w1 >= w2) { 300 if (w1 >= w3) 301 return (v1); 302 } else { 303 if (w2 >= w3) 304 return (v2); 305 } 306 return (v3); 307 } else 308 #endif 309 { 310 u_char lo, hi; 311 312 disable_intr(); 313 /* Select counter 0 and latch it. */ 314 outb(TIMER_MODE, TIMER_SEL0 | TIMER_LATCH); 315 lo = inb(TIMER_CNTR0); 316 hi = inb(TIMER_CNTR0); 317 318 enable_intr(); 319 return ((hi << 8) | lo); 320 } 321 } 322 323 /* 324 * Wait "n" microseconds. 325 * Relies on timer 1 counting down from (TIMER_FREQ / hz) at TIMER_FREQ Hz. 326 * Note: timer had better have been programmed before this is first used! 327 * (Note that we use `rate generator' mode, which counts at 1:1; `square 328 * wave' mode counts at 2:1). 329 */ 330 void 331 delay(n) 332 int n; 333 { 334 int limit, tick, otick; 335 336 /* 337 * Read the counter first, so that the rest of the setup overhead is 338 * counted. 339 */ 340 otick = gettick(); 341 342 #ifdef __GNUC__ 343 /* 344 * Calculate ((n * TIMER_FREQ) / 1e6) using explicit assembler code so 345 * we can take advantage of the intermediate 64-bit quantity to prevent 346 * loss of significance. 347 */ 348 n -= 5; 349 if (n < 0) 350 return; 351 __asm __volatile("mul %2\n\tdiv %3" 352 : "=a" (n) 353 : "0" (n), "r" (TIMER_FREQ), "r" (1000000) 354 : "%edx", "cc"); 355 #else 356 /* 357 * Calculate ((n * TIMER_FREQ) / 1e6) without using floating point and 358 * without any avoidable overflows. 359 */ 360 n -= 20; 361 { 362 int sec = n / 1000000, 363 usec = n % 1000000; 364 n = sec * TIMER_FREQ + 365 usec * (TIMER_FREQ / 1000000) + 366 usec * ((TIMER_FREQ % 1000000) / 1000) / 1000 + 367 usec * (TIMER_FREQ % 1000) / 1000000; 368 } 369 #endif 370 371 limit = TIMER_FREQ / hz; 372 373 while (n > 0) { 374 tick = gettick(); 375 if (tick > otick) 376 n -= limit - (tick - otick); 377 else 378 n -= otick - tick; 379 otick = tick; 380 } 381 } 382 383 #if (NPCPPI > 0) 384 int 385 sysbeepmatch(parent, match, aux) 386 struct device *parent; 387 #ifdef __BROKEN_INDIRECT_CONFIG 388 void *match; 389 #else 390 struct cfdata *match; 391 #endif 392 void *aux; 393 { 394 return (!ppi_attached); 395 } 396 397 void 398 sysbeepattach(parent, self, aux) 399 struct device *parent, *self; 400 void *aux; 401 { 402 printf("\n"); 403 404 ppicookie = ((struct pcppi_attach_args *)aux)->pa_cookie; 405 ppi_attached = 1; 406 } 407 #endif 408 409 void 410 sysbeep(pitch, period) 411 int pitch, period; 412 { 413 #if (NPCPPI > 0) 414 if (ppi_attached) 415 pcppi_bell(ppicookie, pitch, period, 0); 416 #endif 417 } 418 419 unsigned int delaycount; /* calibrated loop variable (1 millisecond) */ 420 421 #define FIRST_GUESS 0x2000 422 423 void 424 findcpuspeed() 425 { 426 int i; 427 int remainder; 428 429 /* Put counter in count down mode */ 430 outb(TIMER_MODE, TIMER_SEL0 | TIMER_16BIT | TIMER_RATEGEN); 431 outb(TIMER_CNTR0, 0xff); 432 outb(TIMER_CNTR0, 0xff); 433 for (i = FIRST_GUESS; i; i--) 434 ; 435 /* Read the value left in the counter */ 436 remainder = gettick(); 437 /* 438 * Formula for delaycount is: 439 * (loopcount * timer clock speed) / (counter ticks * 1000) 440 */ 441 delaycount = (FIRST_GUESS * TIMER_DIV(1000)) / (0xffff-remainder); 442 } 443 444 #if defined(I586_CPU) || defined(I686_CPU) 445 void 446 calibrate_cyclecounter() 447 { 448 unsigned long long count, last_count; 449 450 __asm __volatile("rdtsc" : "=A" (last_count)); 451 delay(1000000); 452 __asm __volatile("rdtsc" : "=A" (count)); 453 pentium_mhz = ((count - last_count) + 500000) / 1000000; 454 } 455 #endif 456 457 void 458 cpu_initclocks() 459 { 460 static struct timeout rtcdrain_timeout; 461 stathz = 128; 462 profhz = 1024; 463 464 /* 465 * XXX If you're doing strange things with multiple clocks, you might 466 * want to keep track of clock handlers. 467 */ 468 (void)isa_intr_establish(NULL, 0, IST_PULSE, IPL_CLOCK, clockintr, 469 0, "clock"); 470 (void)isa_intr_establish(NULL, 8, IST_PULSE, IPL_CLOCK, rtcintr, 471 0, "rtc"); 472 473 mc146818_write(NULL, MC_REGA, MC_BASE_32_KHz | MC_RATE_128_Hz); 474 mc146818_write(NULL, MC_REGB, MC_REGB_24HR | MC_REGB_PIE); 475 476 /* 477 * On a number of i386 systems, the rtc will fail to start when booting 478 * the system. This is due to us missing to acknowledge an interrupt 479 * during early stages of the boot process. If we do not acknowledge 480 * the interrupt, the rtc clock will not generate further interrupts. 481 * To solve this, once interrupts are enabled, use a timeout (once) 482 * to drain any un-acknowledged rtc interrupt(s). 483 */ 484 485 timeout_set(&rtcdrain_timeout, rtcdrain, (void *)&rtcdrain_timeout); 486 timeout_add(&rtcdrain_timeout, 1); 487 } 488 489 int 490 rtcget(regs) 491 mc_todregs *regs; 492 { 493 if ((mc146818_read(NULL, MC_REGD) & MC_REGD_VRT) == 0) /* XXX softc */ 494 return (-1); 495 MC146818_GETTOD(NULL, regs); /* XXX softc */ 496 return (0); 497 } 498 499 void 500 rtcput(regs) 501 mc_todregs *regs; 502 { 503 MC146818_PUTTOD(NULL, regs); /* XXX softc */ 504 } 505 506 int 507 hexdectodec(n) 508 int n; 509 { 510 511 return (((n >> 4) & 0x0f) * 10 + (n & 0x0f)); 512 } 513 514 int 515 dectohexdec(n) 516 int n; 517 { 518 519 return ((u_char)(((n / 10) << 4) & 0xf0) | ((n % 10) & 0x0f)); 520 } 521 522 static int timeset; 523 524 /* 525 * check whether the CMOS layout is "standard"-like (ie, not PS/2-like), 526 * to be called at splclock() 527 */ 528 int cmoscheck(void); 529 int 530 cmoscheck() 531 { 532 int i; 533 unsigned short cksum = 0; 534 535 for (i = 0x10; i <= 0x2d; i++) 536 cksum += mc146818_read(NULL, i); /* XXX softc */ 537 538 return (cksum == (mc146818_read(NULL, 0x2e) << 8) 539 + mc146818_read(NULL, 0x2f)); 540 } 541 542 /* 543 * patchable to control century byte handling: 544 * 1: always update 545 * -1: never touch 546 * 0: try to figure out itself 547 */ 548 int rtc_update_century = 0; 549 550 /* 551 * Expand a two-digit year as read from the clock chip 552 * into full width. 553 * Being here, deal with the CMOS century byte. 554 */ 555 int clock_expandyear(int); 556 int 557 clock_expandyear(clockyear) 558 int clockyear; 559 { 560 int s, clockcentury, cmoscentury; 561 562 clockcentury = (clockyear < 70) ? 20 : 19; 563 clockyear += 100 * clockcentury; 564 565 if (rtc_update_century < 0) 566 return (clockyear); 567 568 s = splclock(); 569 if (cmoscheck()) 570 cmoscentury = mc146818_read(NULL, NVRAM_CENTURY); 571 else 572 cmoscentury = 0; 573 splx(s); 574 if (!cmoscentury) { 575 #ifdef DIAGNOSTIC 576 printf("clock: unknown CMOS layout\n"); 577 #endif 578 return (clockyear); 579 } 580 cmoscentury = hexdectodec(cmoscentury); 581 582 if (cmoscentury != clockcentury) { 583 /* XXX note: saying "century is 20" might confuse the naive. */ 584 printf("WARNING: NVRAM century is %d but RTC year is %d\n", 585 cmoscentury, clockyear); 586 587 /* Kludge to roll over century. */ 588 if ((rtc_update_century > 0) || 589 ((cmoscentury == 19) && (clockcentury == 20) && 590 (clockyear == 2000))) { 591 printf("WARNING: Setting NVRAM century to %d\n", 592 clockcentury); 593 s = splclock(); 594 mc146818_write(NULL, NVRAM_CENTURY, 595 dectohexdec(clockcentury)); 596 splx(s); 597 } 598 } else if (cmoscentury == 19 && rtc_update_century == 0) 599 rtc_update_century = 1; /* will update later in resettodr() */ 600 601 return (clockyear); 602 } 603 604 /* 605 * Initialize the time of day register, based on the time base which is, e.g. 606 * from a filesystem. 607 */ 608 void 609 inittodr(base) 610 time_t base; 611 { 612 mc_todregs rtclk; 613 struct clock_ymdhms dt; 614 int s; 615 616 /* 617 * We mostly ignore the suggested time and go for the RTC clock time 618 * stored in the CMOS RAM. If the time can't be obtained from the 619 * CMOS, or if the time obtained from the CMOS is 5 or more years 620 * less than the suggested time, we used the suggested time. (In 621 * the latter case, it's likely that the CMOS battery has died.) 622 */ 623 624 if (base < 15*SECYR) { /* if before 1985, something's odd... */ 625 printf("WARNING: preposterous time in file system\n"); 626 /* read the system clock anyway */ 627 base = 17*SECYR + 186*SECDAY + SECDAY/2; 628 } 629 630 time.tv_usec = 0; 631 632 s = splclock(); 633 if (rtcget(&rtclk)) { 634 splx(s); 635 printf("WARNING: invalid time in clock chip\n"); 636 goto fstime; 637 } 638 splx(s); 639 640 dt.dt_sec = hexdectodec(rtclk[MC_SEC]); 641 dt.dt_min = hexdectodec(rtclk[MC_MIN]); 642 dt.dt_hour = hexdectodec(rtclk[MC_HOUR]); 643 dt.dt_day = hexdectodec(rtclk[MC_DOM]); 644 dt.dt_mon = hexdectodec(rtclk[MC_MONTH]); 645 dt.dt_year = clock_expandyear(hexdectodec(rtclk[MC_YEAR])); 646 647 648 /* 649 * If time_t is 32 bits, then the "End of Time" is 650 * Mon Jan 18 22:14:07 2038 (US/Eastern) 651 * This code copes with RTC's past the end of time if time_t 652 * is an int32 or less. Needed because sometimes RTCs screw 653 * up or are badly set, and that would cause the time to go 654 * negative in the calculation below, which causes Very Bad 655 * Mojo. This at least lets the user boot and fix the problem. 656 * Note the code is self eliminating once time_t goes to 64 bits. 657 */ 658 if (sizeof(time_t) <= sizeof(int32_t)) { 659 if (dt.dt_year >= 2038) { 660 printf("WARNING: RTC time at or beyond 2038.\n"); 661 dt.dt_year = 2037; 662 printf("WARNING: year set back to 2037.\n"); 663 printf("WARNING: CHECK AND RESET THE DATE!\n"); 664 } 665 } 666 667 time.tv_sec = clock_ymdhms_to_secs(&dt) + tz.tz_minuteswest * 60; 668 if (tz.tz_dsttime) 669 time.tv_sec -= 3600; 670 671 if (base < time.tv_sec - 5*SECYR) 672 printf("WARNING: file system time much less than clock time\n"); 673 else if (base > time.tv_sec + 5*SECYR) { 674 printf("WARNING: clock time much less than file system time\n"); 675 printf("WARNING: using file system time\n"); 676 goto fstime; 677 } 678 679 timeset = 1; 680 return; 681 682 fstime: 683 timeset = 1; 684 time.tv_sec = base; 685 printf("WARNING: CHECK AND RESET THE DATE!\n"); 686 } 687 688 /* 689 * Reset the clock. 690 */ 691 void 692 resettodr() 693 { 694 mc_todregs rtclk; 695 struct clock_ymdhms dt; 696 int diff; 697 int century; 698 int s; 699 700 /* 701 * We might have been called by boot() due to a crash early 702 * on. Don't reset the clock chip in this case. 703 */ 704 if (!timeset) 705 return; 706 707 s = splclock(); 708 if (rtcget(&rtclk)) 709 bzero(&rtclk, sizeof(rtclk)); 710 splx(s); 711 712 diff = tz.tz_minuteswest * 60; 713 if (tz.tz_dsttime) 714 diff -= 3600; 715 clock_secs_to_ymdhms(time.tv_sec - diff, &dt); 716 717 rtclk[MC_SEC] = dectohexdec(dt.dt_sec); 718 rtclk[MC_MIN] = dectohexdec(dt.dt_min); 719 rtclk[MC_HOUR] = dectohexdec(dt.dt_hour); 720 rtclk[MC_DOW] = dt.dt_wday; 721 rtclk[MC_YEAR] = dectohexdec(dt.dt_year % 100); 722 rtclk[MC_MONTH] = dectohexdec(dt.dt_mon); 723 rtclk[MC_DOM] = dectohexdec(dt.dt_day); 724 s = splclock(); 725 rtcput(&rtclk); 726 if (rtc_update_century > 0) { 727 century = dectohexdec(dt.dt_year / 100); 728 mc146818_write(NULL, NVRAM_CENTURY, century); /* XXX softc */ 729 } 730 splx(s); 731 } 732 733 void 734 setstatclockrate(arg) 735 int arg; 736 { 737 if (arg == stathz) 738 mc146818_write(NULL, MC_REGA, MC_BASE_32_KHz | MC_RATE_128_Hz); 739 else 740 mc146818_write(NULL, MC_REGA, MC_BASE_32_KHz | MC_RATE_1024_Hz); 741 } 742