tsc.c revision 3185
1/*- 2 * Copyright (c) 1990 The Regents of the University of California. 3 * All rights reserved. 4 * 5 * This code is derived from software contributed to Berkeley by 6 * William Jolitz and Don Ahn. 7 * 8 * Redistribution and use in source and binary forms, with or without 9 * modification, are permitted provided that the following conditions 10 * are met: 11 * 1. Redistributions of source code must retain the above copyright 12 * notice, this list of conditions and the following disclaimer. 13 * 2. Redistributions in binary form must reproduce the above copyright 14 * notice, this list of conditions and the following disclaimer in the 15 * documentation and/or other materials provided with the distribution. 16 * 3. All advertising materials mentioning features or use of this software 17 * must display the following acknowledgement: 18 * This product includes software developed by the University of 19 * California, Berkeley and its contributors. 20 * 4. 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 * from: @(#)clock.c 7.2 (Berkeley) 5/12/91 37 * $Id: clock.c,v 1.21 1994/09/20 21:20:46 bde Exp $ 38 */ 39 40/* 41 * inittodr, settodr and support routines written 42 * by Christoph Robitschko <chmr@edvz.tu-graz.ac.at> 43 * 44 * reintroduced and updated by Chris Stenton <chris@gnome.co.uk> 8/10/94 45 */ 46 47/* 48 * Primitive clock interrupt routines. 49 */ 50#include <sys/param.h> 51#include <sys/systm.h> 52#include <sys/time.h> 53#include <sys/kernel.h> 54#include <machine/frame.h> 55#include <i386/isa/icu.h> 56#include <i386/isa/isa.h> 57#include <i386/isa/rtc.h> 58#include <i386/isa/timerreg.h> 59 60/* 61 * 32-bit time_t's can't reach leap years before 1904 or after 2036, so we 62 * can use a simple formula for leap years. 63 */ 64#define LEAPYEAR(y) ((u_int)(y) % 4 == 0) 65#define DAYSPERYEAR (31+28+31+30+31+30+31+31+30+31+30+31) 66 67/* X-tals being what they are, it's nice to be able to fudge this one... */ 68/* Note, the name changed here from XTALSPEED to TIMER_FREQ rgrimes 4/26/93 */ 69#ifndef TIMER_FREQ 70#define TIMER_FREQ 1193182 /* XXX - should be in isa.h */ 71#endif 72#define TIMER_DIV(x) ((TIMER_FREQ+(x)/2)/(x)) 73 74static int beeping; 75int timer0_divisor = TIMER_DIV(100); /* XXX should be hz */ 76u_int timer0_prescale; 77int adjkerntz = 0; /* offset from CMOS clock */ 78static char timer0_state = 0, timer2_state = 0; 79static char timer0_reprogram = 0; 80static void (*timer_func)() = hardclock; 81static void (*new_function)(); 82static u_int new_rate; 83static u_int hardclock_divisor; 84static const u_char daysinmonth[] = {31,28,31,30,31,30,31,31,30,31,30,31}; 85static u_char rtc_statusa = RTCSA_DIVIDER | RTCSA_NOPROF; 86 87#ifdef I586_CPU 88int pentium_mhz = 0; 89#endif 90 91#if 0 92void 93clkintr(struct clockframe frame) 94{ 95 hardclock(&frame); 96} 97#else 98void 99clkintr(struct clockframe frame) 100{ 101 timer_func(&frame); 102 switch (timer0_state) { 103 case 0: 104 break; 105 case 1: 106 if ((timer0_prescale+=timer0_divisor) >= hardclock_divisor) { 107 hardclock(&frame); 108 timer0_prescale = 0; 109 } 110 break; 111 case 2: 112 disable_intr(); 113 outb(TIMER_MODE, TIMER_SEL0|TIMER_RATEGEN|TIMER_16BIT); 114 outb(TIMER_CNTR0, TIMER_DIV(new_rate)%256); 115 outb(TIMER_CNTR0, TIMER_DIV(new_rate)/256); 116 enable_intr(); 117 timer0_divisor = TIMER_DIV(new_rate); 118 timer0_prescale = 0; 119 timer_func = new_function; 120 timer0_state = 1; 121 break; 122 case 3: 123 if ((timer0_prescale+=timer0_divisor) >= hardclock_divisor) { 124 hardclock(&frame); 125 disable_intr(); 126 outb(TIMER_MODE, TIMER_SEL0|TIMER_RATEGEN|TIMER_16BIT); 127 outb(TIMER_CNTR0, TIMER_DIV(hz)%256); 128 outb(TIMER_CNTR0, TIMER_DIV(hz)/256); 129 enable_intr(); 130 timer0_divisor = TIMER_DIV(hz); 131 timer0_prescale = 0; 132 timer_func = hardclock;; 133 timer0_state = 0; 134 } 135 break; 136 } 137} 138#endif 139 140int 141acquire_timer0(int rate, void (*function)() ) 142{ 143 if (timer0_state || !function) 144 return -1; 145 new_function = function; 146 new_rate = rate; 147 timer0_state = 2; 148 return 0; 149} 150 151int 152acquire_timer2(int mode) 153{ 154 if (timer2_state) 155 return -1; 156 timer2_state = 1; 157 outb(TIMER_MODE, TIMER_SEL2 | (mode &0x3f)); 158 return 0; 159} 160 161int 162release_timer0() 163{ 164 if (!timer0_state) 165 return -1; 166 timer0_state = 3; 167 return 0; 168} 169 170int 171release_timer2() 172{ 173 if (!timer2_state) 174 return -1; 175 timer2_state = 0; 176 outb(TIMER_MODE, TIMER_SEL2|TIMER_SQWAVE|TIMER_16BIT); 177 return 0; 178} 179 180/* 181 * This routine receives statistical clock interrupts from the RTC. 182 * As explained above, these occur at 128 interrupts per second. 183 * When profiling, we receive interrupts at a rate of 1024 Hz. 184 * 185 * This does not actually add as much overhead as it sounds, because 186 * when the statistical clock is active, the hardclock driver no longer 187 * needs to keep (inaccurate) statistics on its own. This decouples 188 * statistics gathering from scheduling interrupts. 189 * 190 * The RTC chip requires that we read status register C (RTC_INTR) 191 * to acknowledge an interrupt, before it will generate the next one. 192 */ 193void 194rtcintr(struct clockframe frame) 195{ 196 u_char stat; 197 stat = rtcin(RTC_INTR); 198 if(stat & RTCIR_PERIOD) { 199 statclock(&frame); 200 } 201} 202 203#ifdef DEBUG 204void 205printrtc(void) 206{ 207 outb(IO_RTC, RTC_STATUSA); 208 printf("RTC status A = %x", inb(IO_RTC+1)); 209 outb(IO_RTC, RTC_STATUSB); 210 printf(", B = %x", inb(IO_RTC+1)); 211 outb(IO_RTC, RTC_INTR); 212 printf(", C = %x\n", inb(IO_RTC+1)); 213} 214#endif 215 216static int 217getit() 218{ 219 int high, low; 220 221 disable_intr(); 222 /* select timer0 and latch counter value */ 223 outb(TIMER_MODE, TIMER_SEL0); 224 low = inb(TIMER_CNTR0); 225 high = inb(TIMER_CNTR0); 226 enable_intr(); 227 return ((high << 8) | low); 228} 229 230#ifdef I586_CPU 231static long long cycles_per_sec = 0; 232 233/* 234 * Figure out how fast the cyclecounter runs. This must be run with 235 * clock interrupts disabled, but with the timer/counter programmed 236 * and running. 237 */ 238void 239calibrate_cyclecounter(void) 240{ 241 volatile long edx, eax, lasteax, lastedx; 242 243 __asm __volatile(".byte 0x0f, 0x31" : "=a"(lasteax), "=d"(lastedx) : ); 244 DELAY(1000000); 245 __asm __volatile(".byte 0x0f, 0x31" : "=a"(eax), "=d"(edx) : ); 246 247 /* 248 * This assumes that you will never have a clock rate higher 249 * than 4GHz, probably a good assumption. 250 */ 251 cycles_per_sec = (long long)edx + eax; 252 cycles_per_sec -= (long long)lastedx + lasteax; 253 pentium_mhz = ((long)cycles_per_sec + 500000) / 1000000; /* round up */ 254} 255#endif 256 257/* 258 * Wait "n" microseconds. 259 * Relies on timer 1 counting down from (TIMER_FREQ / hz) 260 * Note: timer had better have been programmed before this is first used! 261 */ 262void 263DELAY(int n) 264{ 265 int prev_tick, tick, ticks_left, sec, usec; 266 267#ifdef DELAYDEBUG 268 int getit_calls = 1; 269 int n1; 270 static int state = 0; 271 272 if (state == 0) { 273 state = 1; 274 for (n1 = 1; n1 <= 10000000; n1 *= 10) 275 DELAY(n1); 276 state = 2; 277 } 278 if (state == 1) 279 printf("DELAY(%d)...", n); 280#endif 281 /* 282 * Read the counter first, so that the rest of the setup overhead is 283 * counted. Guess the initial overhead is 20 usec (on most systems it 284 * takes about 1.5 usec for each of the i/o's in getit(). The loop 285 * takes about 6 usec on a 486/33 and 13 usec on a 386/20. The 286 * multiplications and divisions to scale the count take a while). 287 */ 288 prev_tick = getit(0, 0); 289 n -= 20; 290 /* 291 * Calculate (n * (TIMER_FREQ / 1e6)) without using floating point 292 * and without any avoidable overflows. 293 */ 294 sec = n / 1000000; 295 usec = n - sec * 1000000; 296 ticks_left = sec * TIMER_FREQ 297 + usec * (TIMER_FREQ / 1000000) 298 + usec * ((TIMER_FREQ % 1000000) / 1000) / 1000 299 + usec * (TIMER_FREQ % 1000) / 1000000; 300 301 while (ticks_left > 0) { 302 tick = getit(0, 0); 303#ifdef DELAYDEBUG 304 ++getit_calls; 305#endif 306 if (tick > prev_tick) 307 ticks_left -= prev_tick - (tick - timer0_divisor); 308 else 309 ticks_left -= prev_tick - tick; 310 prev_tick = tick; 311 } 312#ifdef DELAYDEBUG 313 if (state == 1) 314 printf(" %d calls to getit() at %d usec each\n", 315 getit_calls, (n + 5) / getit_calls); 316#endif 317} 318 319static void 320sysbeepstop(void *chan) 321{ 322 outb(IO_PPI, inb(IO_PPI)&0xFC); /* disable counter2 output to speaker */ 323 release_timer2(); 324 beeping = 0; 325} 326 327int 328sysbeep(int pitch, int period) 329{ 330 331 if (acquire_timer2(TIMER_SQWAVE|TIMER_16BIT)) 332 return -1; 333 disable_intr(); 334 outb(TIMER_CNTR2, pitch); 335 outb(TIMER_CNTR2, (pitch>>8)); 336 enable_intr(); 337 if (!beeping) { 338 outb(IO_PPI, inb(IO_PPI) | 3); /* enable counter2 output to speaker */ 339 beeping = period; 340 timeout(sysbeepstop, (void *)NULL, period); 341 } 342 return 0; 343} 344 345/* 346 * RTC support routines 347 */ 348static int 349bcd2int(int bcd) 350{ 351 return(bcd/16 * 10 + bcd%16); 352} 353 354static int 355int2bcd(int dez) 356{ 357 return(dez/10 * 16 + dez%10); 358} 359 360static void 361writertc(int port, int val) 362{ 363 outb(IO_RTC, port); 364 outb(IO_RTC+1, val); 365} 366 367static int 368readrtc(int port) 369{ 370 return(bcd2int(rtcin(port))); 371} 372 373void 374startrtclock() 375{ 376 int s; 377 378 /* initialize 8253 clock */ 379 outb(TIMER_MODE, TIMER_SEL0|TIMER_RATEGEN|TIMER_16BIT); 380 381 /* Correct rounding will buy us a better precision in timekeeping */ 382 outb (IO_TIMER1, TIMER_DIV(hz)%256); 383 outb (IO_TIMER1, TIMER_DIV(hz)/256); 384 timer0_divisor = hardclock_divisor = TIMER_DIV(hz); 385 386 /* initialize brain-dead battery powered clock */ 387 outb (IO_RTC, RTC_STATUSA); 388 outb (IO_RTC+1, rtc_statusa); 389 outb (IO_RTC, RTC_STATUSB); 390 outb (IO_RTC+1, RTCSB_24HR); 391 outb (IO_RTC, RTC_DIAG); 392 if (s = inb (IO_RTC+1)) 393 printf("RTC BIOS diagnostic error %b\n", s, RTCDG_BITS); 394 writertc(RTC_DIAG, 0); 395} 396 397/* 398 * Initialize the time of day register, based on the time base which is, e.g. 399 * from a filesystem. 400 */ 401void 402inittodr(time_t base) 403{ 404 unsigned long sec, days; 405 int yd; 406 int year, month; 407 int y, m, s; 408 409 s = splclock(); 410 time.tv_sec = base; 411 time.tv_usec = 0; 412 splx(s); 413 414 /* Look if we have a RTC present and the time is valid */ 415 if (rtcin(RTC_STATUSD) != RTCSD_PWR) 416 goto wrong_time; 417 418 /* wait for time update to complete */ 419 /* If RTCSA_TUP is zero, we have at least 244us before next update */ 420 while (rtcin(RTC_STATUSA) & RTCSA_TUP); 421 422 days = 0; 423 year = readrtc(RTC_YEAR) + readrtc(RTC_CENTURY) * 100; 424 if (year < 1970) 425 goto wrong_time; 426 month = readrtc(RTC_MONTH); 427 for (m = 1; m < month; m++) 428 days += daysinmonth[m-1]; 429 if ((month > 2) && LEAPYEAR(year)) 430 days ++; 431 days += readrtc(RTC_DAY) - 1; 432 yd = days; 433 for (y = 1970; y < year; y++) 434 days += DAYSPERYEAR + LEAPYEAR(y); 435 sec = ((( days * 24 + 436 readrtc(RTC_HRS)) * 60 + 437 readrtc(RTC_MIN)) * 60 + 438 readrtc(RTC_SEC)); 439 /* sec now contains the number of seconds, since Jan 1 1970, 440 in the local time zone */ 441 442 sec += tz.tz_minuteswest * 60; 443 444 s = splclock(); 445 time.tv_sec = sec; 446 splx(s); 447 return; 448 449wrong_time: 450 printf("Invalid time in real time clock.\n"); 451 printf("Check and reset the date immediately!\n"); 452} 453 454/* 455 * Write system time back to RTC 456 */ 457void resettodr() 458{ 459 unsigned long tm; 460 int y, m, fd, r, s; 461 462 s = splclock(); 463 tm = time.tv_sec; 464 splx(s); 465 466 /* First, disable clock updates */ 467 writertc(RTC_STATUSB, RTCSB_HALT | RTCSB_24HR); 468 469 /* Calculate local time to put in CMOS */ 470 471 tm -= tz.tz_minuteswest * 60 + adjkerntz; 472 473 writertc(RTC_SEC, int2bcd(tm%60)); tm /= 60; /* Write back Seconds */ 474 writertc(RTC_MIN, int2bcd(tm%60)); tm /= 60; /* Write back Minutes */ 475 writertc(RTC_HRS, int2bcd(tm%24)); tm /= 24; /* Write back Hours */ 476 477 /* We have now the days since 01-01-1970 in tm */ 478 writertc(RTC_WDAY, (tm+4)%7); /* Write back Weekday */ 479 for (y=1970;; y++) 480 if ((tm - DAYSPERYEAR - LEAPYEAR(y)) > tm) 481 break; 482 else 483 tm -= DAYSPERYEAR + LEAPYEAR(y); 484 485 /* Now we have the years in y and the day-of-the-year in tm */ 486 writertc(RTC_YEAR, int2bcd(y%100)); /* Write back Year */ 487 writertc(RTC_CENTURY, int2bcd(y/100)); /* ... and Century */ 488 if (LEAPYEAR(y) && (tm >= 31+29)) 489 tm--; /* Subtract Feb-29 */ 490 for (m=1;; m++) 491 if (tm - daysinmonth[m-1] > tm) 492 break; 493 else 494 tm -= daysinmonth[m-1]; 495 496 writertc(RTC_MONTH, int2bcd(m)); /* Write back Month */ 497 writertc(RTC_DAY, int2bcd(tm+1)); /* Write back Day */ 498 499 /* enable time updates */ 500 writertc(RTC_STATUSB, RTCSB_PINTR | RTCSB_24HR); 501} 502 503#ifdef garbage 504/* 505 * Initialze the time of day register, based on the time base which is, e.g. 506 * from a filesystem. 507 */ 508test_inittodr(time_t base) 509{ 510 511 outb(IO_RTC,9); /* year */ 512 printf("%d ",bcd(inb(IO_RTC+1))); 513 outb(IO_RTC,8); /* month */ 514 printf("%d ",bcd(inb(IO_RTC+1))); 515 outb(IO_RTC,7); /* day */ 516 printf("%d ",bcd(inb(IO_RTC+1))); 517 outb(IO_RTC,4); /* hour */ 518 printf("%d ",bcd(inb(IO_RTC+1))); 519 outb(IO_RTC,2); /* minutes */ 520 printf("%d ",bcd(inb(IO_RTC+1))); 521 outb(IO_RTC,0); /* seconds */ 522 printf("%d\n",bcd(inb(IO_RTC+1))); 523 524 time.tv_sec = base; 525} 526#endif 527 528/* 529 * Wire clock interrupt in. 530 */ 531void 532enablertclock() 533{ 534 register_intr(/* irq */ 0, /* XXX id */ 0, /* flags */ 0, clkintr, 535 HWI_MASK | SWI_MASK, /* unit */ 0); 536 INTREN(IRQ0); 537 register_intr(/* irq */ 8, /* XXX id */ 1, /* flags */ 0, rtcintr, 538 SWI_CLOCK_MASK, /* unit */ 0); 539 INTREN(IRQ8); 540 outb(IO_RTC, RTC_STATUSB); 541 outb(IO_RTC+1, RTCSB_PINTR | RTCSB_24HR); 542} 543 544void 545cpu_initclocks() 546{ 547 stathz = RTC_NOPROFRATE; 548 profhz = RTC_PROFRATE; 549 enablertclock(); 550} 551 552void 553setstatclockrate(int newhz) 554{ 555 if(newhz == RTC_PROFRATE) { 556 rtc_statusa = RTCSA_DIVIDER | RTCSA_PROF; 557 } else { 558 rtc_statusa = RTCSA_DIVIDER | RTCSA_NOPROF; 559 } 560 outb(IO_RTC, RTC_STATUSA); 561 outb(IO_RTC+1, rtc_statusa); 562} 563