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