1 /* $NetBSD: clock.c,v 1.5 2003/07/15 03:36:01 lukem Exp $ */ 2 3 /* 4 * Copyright 1997 5 * Digital Equipment Corporation. All rights reserved. 6 * 7 * This software is furnished under license and may be used and 8 * copied only in accordance with the following terms and conditions. 9 * Subject to these conditions, you may download, copy, install, 10 * use, modify and distribute this software in source and/or binary 11 * form. No title or ownership is transferred hereby. 12 * 13 * 1) Any source code used, modified or distributed must reproduce 14 * and retain this copyright notice and list of conditions as 15 * they appear in the source file. 16 * 17 * 2) No right is granted to use any trade name, trademark, or logo of 18 * Digital Equipment Corporation. Neither the "Digital Equipment 19 * Corporation" name nor any trademark or logo of Digital Equipment 20 * Corporation may be used to endorse or promote products derived 21 * from this software without the prior written permission of 22 * Digital Equipment Corporation. 23 * 24 * 3) This software is provided "AS-IS" and any express or implied 25 * warranties, including but not limited to, any implied warranties 26 * of merchantability, fitness for a particular purpose, or 27 * non-infringement are disclaimed. In no event shall DIGITAL be 28 * liable for any damages whatsoever, and in particular, DIGITAL 29 * shall not be liable for special, indirect, consequential, or 30 * incidental damages or damages for lost profits, loss of 31 * revenue or loss of use, whether such damages arise in contract, 32 * negligence, tort, under statute, in equity, at law or otherwise, 33 * even if advised of the possibility of such damage. 34 */ 35 36 /*- 37 * Copyright (c) 1993, 1994 Charles M. Hannum. 38 * Copyright (c) 1990 The Regents of the University of California. 39 * All rights reserved. 40 * 41 * This code is derived from software contributed to Berkeley by 42 * William Jolitz and Don Ahn. 43 * 44 * Redistribution and use in source and binary forms, with or without 45 * modification, are permitted provided that the following conditions 46 * are met: 47 * 1. Redistributions of source code must retain the above copyright 48 * notice, this list of conditions and the following disclaimer. 49 * 2. Redistributions in binary form must reproduce the above copyright 50 * notice, this list of conditions and the following disclaimer in the 51 * documentation and/or other materials provided with the distribution. 52 * 3. All advertising materials mentioning features or use of this software 53 * must display the following acknowledgement: 54 * This product includes software developed by the University of 55 * California, Berkeley and its contributors. 56 * 4. Neither the name of the University nor the names of its contributors 57 * may be used to endorse or promote products derived from this software 58 * without specific prior written permission. 59 * 60 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND 61 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 62 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 63 * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE 64 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 65 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 66 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 67 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 68 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 69 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 70 * SUCH DAMAGE. 71 * 72 * @(#)clock.c 7.2 (Berkeley) 5/12/91 73 */ 74 /* 75 * Mach Operating System 76 * Copyright (c) 1991,1990,1989 Carnegie Mellon University 77 * All Rights Reserved. 78 * 79 * Permission to use, copy, modify and distribute this software and its 80 * documentation is hereby granted, provided that both the copyright 81 * notice and this permission notice appear in all copies of the 82 * software, derivative works or modified versions, and any portions 83 * thereof, and that both notices appear in supporting documentation. 84 * 85 * CARNEGIE MELLON ALLOWS FREE USE OF THIS SOFTWARE IN ITS "AS IS" 86 * CONDITION. CARNEGIE MELLON DISCLAIMS ANY LIABILITY OF ANY KIND FOR 87 * ANY DAMAGES WHATSOEVER RESULTING FROM THE USE OF THIS SOFTWARE. 88 * 89 * Carnegie Mellon requests users of this software to return to 90 * 91 * Software Distribution Coordinator or Software.Distribution@CS.CMU.EDU 92 * School of Computer Science 93 * Carnegie Mellon University 94 * Pittsburgh PA 15213-3890 95 * 96 * any improvements or extensions that they make and grant Carnegie Mellon 97 * the rights to redistribute these changes. 98 */ 99 /* 100 Copyright 1988, 1989 by Intel Corporation, Santa Clara, California. 101 102 All Rights Reserved 103 104 Permission to use, copy, modify, and distribute this software and 105 its documentation for any purpose and without fee is hereby 106 granted, provided that the above copyright notice appears in all 107 copies and that both the copyright notice and this permission notice 108 appear in supporting documentation, and that the name of Intel 109 not be used in advertising or publicity pertaining to distribution 110 of the software without specific, written prior permission. 111 112 INTEL DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE 113 INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, 114 IN NO EVENT SHALL INTEL BE LIABLE FOR ANY SPECIAL, INDIRECT, OR 115 CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM 116 LOSS OF USE, DATA OR PROFITS, WHETHER IN ACTION OF CONTRACT, 117 NEGLIGENCE, OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION 118 WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 119 */ 120 121 /* 122 * Primitive clock interrupt routines. 123 */ 124 125 #include <sys/cdefs.h> 126 __KERNEL_RCSID(0, "$NetBSD: clock.c,v 1.5 2003/07/15 03:36:01 lukem Exp $"); 127 128 #include <sys/param.h> 129 #include <sys/systm.h> 130 #include <sys/time.h> 131 #include <sys/kernel.h> 132 #include <sys/device.h> 133 134 #include <machine/cpu.h> 135 #include <machine/intr.h> 136 #include <machine/pio.h> 137 #include <arm/cpufunc.h> 138 139 #include <dev/isa/isareg.h> 140 #include <dev/isa/isavar.h> 141 #include <dev/ic/mc146818reg.h> 142 #include <dev/ic/i8253reg.h> 143 #include <shark/isa/nvram.h> 144 #include <shark/isa/spkrreg.h> 145 #include <shark/shark/hat.h> 146 147 void sysbeepstop(void *); 148 void sysbeep(int, int); 149 void rtcinit(void); 150 int timer_hz_to_count(int); 151 152 static void findcpuspeed(void); 153 static void init_isa_timer_tables(void); 154 static void delayloop(int); 155 static int clockintr(void *); 156 static int gettick(void); 157 158 void startrtclock(void); 159 160 __inline u_int mc146818_read(void *, u_int); 161 __inline void mc146818_write(void *, u_int, u_int); 162 163 #define SECMIN ((unsigned)60) /* seconds per minute */ 164 #define SECHOUR ((unsigned)(60*SECMIN)) /* seconds per hour */ 165 #define SECDAY ((unsigned)(24*SECHOUR)) /* seconds per day */ 166 #define SECYR ((unsigned)(365*SECDAY)) /* seconds per common year */ 167 168 __inline u_int 169 mc146818_read(sc, reg) 170 void *sc; /* XXX use it? */ 171 u_int reg; 172 { 173 174 outb(IO_RTC, reg); 175 return (inb(IO_RTC+1)); 176 } 177 178 __inline void 179 mc146818_write(sc, reg, datum) 180 void *sc; /* XXX use it? */ 181 u_int reg, datum; 182 { 183 184 outb(IO_RTC, reg); 185 outb(IO_RTC+1, datum); 186 } 187 188 unsigned int count1024usec; /* calibrated loop variable (1024 microseconds) */ 189 190 /* number of timer ticks in a Musec = 2^20 usecs */ 191 #define TIMER_MUSECFREQ\ 192 (((((((TIMER_FREQ) * 1024) + 999) / 1000) * 1024) + 999) / 1000) 193 #define TIMER_MUSECDIV(x) ((TIMER_MUSECFREQ+(x)/2)/(x)) 194 195 /* 196 * microtime() makes use of the following globals. 197 * timer_msb_table[] and timer_lsb_table[] are used to compute the 198 * microsecond increment. 199 * 200 * time.tv_usec += isa_timer_msb_table[cnt_msb] + isa_timer_lsb_table[cnt_lsb]; 201 */ 202 203 u_short isa_timer_msb_table[256]; /* timer->usec MSB */ 204 u_short isa_timer_lsb_table[256]; /* timer->usec conversion for LSB */ 205 206 /* 64 bit counts from timer 0 */ 207 struct count64 { 208 unsigned lo; /* low 32 bits */ 209 unsigned hi; /* high 32 bits */ 210 }; 211 212 #define TIMER0_ROLLOVER 0xFFFF /* maximum rollover for 8254 counter */ 213 214 struct count64 timer0count; 215 struct count64 timer0_at_last_clockintr; 216 unsigned timer0last; 217 218 /*#define TESTHAT*/ 219 #ifdef TESTHAT 220 #define HATSTACKSIZE 1024 221 #define HATHZ 50000 222 #define HATHZ2 10000 223 unsigned char hatStack[HATSTACKSIZE]; 224 225 unsigned testHatOn = 0; 226 unsigned nHats = 0; 227 unsigned nHatWedges = 0; 228 unsigned fiqReason = 0; 229 unsigned hatCount = 0; 230 unsigned hatCount2 = 0; 231 232 void hatTest(int testReason) 233 { 234 fiqReason |= testReason; 235 nHats++; 236 237 } 238 239 void hatWedge(int nFIQs) 240 { 241 printf("Unwedging the HAT. fiqs_happened = %d\n", nFIQs); 242 nHatWedges++; 243 } 244 #endif 245 246 void 247 startrtclock() 248 { 249 findcpuspeed(); /* use the clock (while it's free) 250 to find the cpu speed */ 251 252 init_isa_timer_tables(); 253 254 timer0count.lo = 0; 255 timer0count.hi = 0; 256 timer0_at_last_clockintr.lo = 0; 257 timer0_at_last_clockintr.hi = 0; 258 timer0last = 0; 259 260 /* initialize 8253 clock */ 261 outb(IO_TIMER1 + TIMER_MODE, TIMER_SEL0|TIMER_RATEGEN|TIMER_16BIT); 262 outb(IO_TIMER1 + TIMER_CNTR0, TIMER0_ROLLOVER % 256); 263 outb(IO_TIMER1 + TIMER_CNTR0, TIMER0_ROLLOVER / 256); 264 265 #ifdef TESTHAT 266 hatCount = timer_hz_to_count(HATHZ); 267 hatCount2 = timer_hz_to_count(HATHZ2); 268 printf("HAT test on @ %d Hz = %d ticks\n", HATHZ, hatCount); 269 #endif 270 } 271 272 static void 273 init_isa_timer_tables() 274 { 275 int s; 276 u_long t, msbmillion, quotient, remainder; 277 278 for (s = 0; s < 256; s++) { 279 /* LSB table is easy, just divide and round */ 280 t = ((u_long) s * 1000000 * 2) / TIMER_FREQ; 281 isa_timer_lsb_table[s] = (u_short) ((t / 2) + (t & 0x1)); 282 283 msbmillion = s * 1000000; 284 quotient = msbmillion / TIMER_FREQ; 285 remainder = msbmillion % TIMER_FREQ; 286 t = (remainder * 256 * 2) / TIMER_FREQ; 287 isa_timer_msb_table[s] = 288 (u_short)((t / 2) + (t & 1) + (quotient * 256)); 289 290 #ifdef DIAGNOSTIC 291 if ((s > 0) && 292 (isa_timer_msb_table[s] < 293 (isa_timer_msb_table[s - 1] + isa_timer_lsb_table[0xFF]))) 294 panic ("time tables not monotonic %d: %d < (%d + %d) = %d\n", 295 s, isa_timer_msb_table[s], 296 isa_timer_msb_table[s - 1], 297 isa_timer_lsb_table[0xFF], 298 isa_timer_msb_table[s - 1] + 299 isa_timer_lsb_table[0xFF]); 300 #endif 301 } /* END for */ 302 } 303 304 int 305 timer_hz_to_count(timer_hz) 306 int timer_hz; 307 { 308 u_long tval; 309 310 tval = (TIMER_FREQ * 2) / (u_long) timer_hz; 311 tval = (tval / 2) + (tval & 0x1); 312 313 return (int)tval; 314 315 } 316 317 void gettimer0count(struct count64 *); 318 319 /* must be called at SPL_CLOCK or higher */ 320 void gettimer0count(pcount) 321 struct count64 *pcount; 322 { 323 unsigned current, ticks, oldlo; 324 325 /* 326 * Latch the current value of the timer and then read it. 327 * This guarantees an atomic reading of the time. 328 */ 329 330 current = gettick(); 331 332 if (timer0last >= current) 333 ticks = timer0last - current; 334 else 335 ticks = timer0last + (TIMER0_ROLLOVER - current); 336 337 timer0last = current; 338 339 oldlo = timer0count.lo; 340 341 if (oldlo > (timer0count.lo = oldlo + ticks)) /* carry? */ 342 timer0count.hi++; 343 344 *pcount = timer0count; 345 } 346 347 static int 348 clockintr(arg) 349 void *arg; 350 { 351 struct clockframe *frame = arg; /* not strictly necessary */ 352 extern void isa_specific_eoi(int irq); 353 #ifdef TESTHAT 354 static int ticks = 0; 355 #endif 356 static int hatUnwedgeCtr = 0; 357 358 gettimer0count(&timer0_at_last_clockintr); 359 360 mc146818_read(NULL, MC_REGC); /* clear the clock interrupt */ 361 362 /* check to see if the high-availability timer needs to be unwedged */ 363 if (++hatUnwedgeCtr >= (hz / HAT_MIN_FREQ)) { 364 hatUnwedgeCtr = 0; 365 hatUnwedge(); 366 } 367 368 #ifdef TESTHAT 369 ++ticks; 370 371 if (testHatOn && ((ticks & 0x3f) == 0)) { 372 if (testHatOn == 1) { 373 hatClkAdjust(hatCount2); 374 testHatOn = 2; 375 } else { 376 testHatOn = 0; 377 hatClkOff(); 378 printf("hat off status: %d %d %x\n", nHats, nHatWedges, fiqReason); 379 } 380 } else if (!testHatOn && (ticks & 0x1ff) == 0) { 381 printf("hat on status: %d %d %x\n", nHats, nHatWedges, fiqReason); 382 testHatOn = 1; 383 nHats = 0; 384 fiqReason = 0; 385 hatClkOn(hatCount, hatTest, 0xfeedface, 386 hatStack + HATSTACKSIZE - sizeof(unsigned), 387 hatWedge); 388 } 389 #endif 390 hardclock(frame); 391 return(0); 392 } 393 394 static int 395 gettick() 396 { 397 u_char lo, hi; 398 u_int savedints; 399 400 /* Don't want someone screwing with the counter while we're here. */ 401 savedints = disable_interrupts(I32_bit); 402 /* Select counter 0 and latch it. */ 403 outb(IO_TIMER1 + TIMER_MODE, TIMER_SEL0 | TIMER_LATCH); 404 lo = inb(IO_TIMER1 + TIMER_CNTR0); 405 hi = inb(IO_TIMER1 + TIMER_CNTR0); 406 restore_interrupts(savedints); 407 return ((hi << 8) | lo); 408 } 409 410 /* modifications from i386 to shark isa version: 411 - removed hardcoded "n -=" values that approximated the time to 412 calculate delay ticks 413 - made the time to calculate delay ticks almost negligable. 4 multiplies 414 = maximum of 12 cycles = 75ns on a slow SA-110, plus a bunch of shifts; 415 as opposed to 4 multiplies plus a bunch of divides. 416 - removed i386 assembly language hack 417 - put code in findcpuspeed that works even if FIRST_GUESS is orders 418 of magnitude low 419 - put code in delay() to use delayloop() for short delays 420 - microtime no longer in assembly language 421 */ 422 423 /* 424 * Wait "n" microseconds. 425 * Relies on timer 1 counting down from (TIMER_FREQ / hz) at TIMER_FREQ Hz. 426 * Note: timer had better have been programmed before this is first used! 427 * (Note that we use `rate generator' mode, which counts at 1:1; `square 428 * wave' mode counts at 2:1). 429 */ 430 void 431 delay(n) 432 unsigned n; 433 { 434 int tick, otick; 435 int nticks; 436 437 if (n < 100) { 438 /* it can take a long time (1 usec or longer) just for 1 ISA read, 439 so it's best not to use the timer for short delays */ 440 delayloop((n * count1024usec) >> 10); 441 return; 442 } 443 444 /* 445 * Read the counter first, so that the rest of the setup overhead is 446 * counted. 447 */ 448 otick = gettick(); 449 450 /* 451 * Calculate ((n * TIMER_FREQ) / 1e6) without using floating point and 452 * without any avoidable overflows. 453 */ 454 { 455 /* a Musec = 2^20 usec */ 456 int Musec = n >> 20, 457 usec = n & ((1 << 20) - 1); 458 nticks 459 = (Musec * TIMER_MUSECFREQ) + 460 (usec * (TIMER_MUSECFREQ >> 20)) + 461 ((usec * ((TIMER_MUSECFREQ & ((1 <<20) - 1)) >>10)) >>10) + 462 ((usec * (TIMER_MUSECFREQ & ((1 << 10) - 1))) >> 20); 463 } 464 465 while (nticks > 0) { 466 tick = gettick(); 467 if (tick > otick) 468 nticks -= TIMER0_ROLLOVER - (tick - otick); 469 else 470 nticks -= otick - tick; 471 otick = tick; 472 } 473 474 } 475 476 void 477 sysbeepstop(arg) 478 void *arg; 479 { 480 } 481 482 void 483 sysbeep(pitch, period) 484 int pitch, period; 485 { 486 } 487 488 #define FIRST_GUESS 0x2000 489 490 static void 491 findcpuspeed() 492 { 493 int ticks; 494 unsigned int guess = FIRST_GUESS; 495 496 while (1) { /* loop until accurate enough */ 497 /* Put counter in count down mode */ 498 outb(IO_TIMER1 + TIMER_MODE, TIMER_SEL0 | TIMER_16BIT | TIMER_RATEGEN); 499 outb(IO_TIMER1 + TIMER_CNTR0, 0xff); 500 outb(IO_TIMER1 + TIMER_CNTR0, 0xff); 501 delayloop(guess); 502 503 /* Read the value left in the counter */ 504 /* 505 * Formula for delaycount is: 506 * (loopcount * timer clock speed) / (counter ticks * 1000) 507 */ 508 ticks = 0xFFFF - gettick(); 509 if (ticks == 0) ticks = 1; /* just in case */ 510 if (ticks < (TIMER_MUSECDIV(1024))) { /* not accurate enough */ 511 guess *= max(2, (TIMER_MUSECDIV(1024) / ticks)); 512 continue; 513 } 514 count1024usec = (guess * (TIMER_MUSECDIV(1024))) / ticks; 515 return; 516 } 517 } 518 519 static void 520 delayloop(counts) 521 { 522 while (counts--); 523 } 524 525 void 526 cpu_initclocks() 527 { 528 unsigned hzval; 529 530 printf("clock: hz=%d stathz = %d profhz = %d\n", hz, stathz, profhz); 531 532 /* install RTC interrupt handler */ 533 (void)isa_intr_establish(NULL, IRQ_RTC, IST_LEVEL, IPL_CLOCK, 534 clockintr, 0); 535 536 /* code for values of hz that don't divide 1000000 exactly */ 537 tickfix = 1000000 - (hz * tick); 538 if (tickfix) { 539 int ftp; 540 541 ftp = min(ffs(tickfix), ffs(hz)); 542 tickfix >>= (ftp - 1); 543 tickfixinterval = hz >> (ftp - 1); 544 } 545 546 /* set up periodic interrupt @ hz 547 this is the subset of hz values in kern_clock.c that are 548 supported by the ISA RTC */ 549 switch (hz) { 550 case 64: 551 hzval = MC_RATE_64_Hz; 552 break; 553 case 128: 554 hzval = MC_RATE_128_Hz; 555 break; 556 case 256: 557 hzval = MC_RATE_256_Hz; 558 break; 559 case 1024: 560 hzval = MC_RATE_1024_Hz; 561 break; 562 default: 563 panic("cannot configure hz = %d", hz); 564 } 565 566 rtcinit(); /* make sure basics are done by now */ 567 568 /* blast values to set up clock interrupt */ 569 mc146818_write(NULL, MC_REGA, MC_BASE_32_KHz | hzval); 570 /* enable periodic interrupt */ 571 mc146818_write(NULL, MC_REGB, 572 mc146818_read(NULL, MC_REGB) | MC_REGB_PIE); 573 } 574 575 void 576 rtcinit() 577 { 578 static int first_rtcopen_ever = 1; 579 580 if (!first_rtcopen_ever) 581 return; 582 first_rtcopen_ever = 0; 583 584 mc146818_write(NULL, MC_REGA, /* XXX softc */ 585 MC_BASE_32_KHz | MC_RATE_1024_Hz); 586 mc146818_write(NULL, MC_REGB, MC_REGB_24HR); /* XXX softc */ 587 } 588 589 void 590 setstatclockrate(arg) 591 int arg; 592 { 593 } 594 595 /* 596 * void microtime(struct timeval *tvp) 597 * 598 * Fill in the specified timeval struct with the current time 599 * accurate to the microsecond. 600 */ 601 602 void 603 microtime(tvp) 604 struct timeval *tvp; 605 { 606 int s; 607 unsigned lsb, msb; 608 int tm; 609 static struct timeval oldtv; 610 struct count64 timer0current; 611 int ticks; 612 613 s = splstatclock(); 614 615 gettimer0count(&timer0current); 616 617 tm = time.tv_usec; 618 619 /* unsigned arithmetic should take care of overflow */ 620 /* with a >= 32 Hz clock, ticks will always be < 0x7FFF */ 621 ticks = (int)((unsigned) 622 (timer0current.lo - timer0_at_last_clockintr.lo)); 623 624 #ifdef DIAGNOSTIC 625 if ((ticks < 0) || (ticks > 0xffff)) 626 printf("microtime bug: ticks = %x\n", ticks); 627 #endif 628 629 while (ticks > 0) { 630 631 if (ticks < 0xffff) { 632 msb = (ticks >> 8) & 0xFF; 633 lsb = ticks & 0xFF; 634 } else { 635 msb = 0xff; 636 lsb = 0xff; 637 } 638 639 /* see comments above */ 640 tm += isa_timer_msb_table[msb] + isa_timer_lsb_table[lsb]; 641 642 /* for a 64 Hz RTC, ticks will never overflow table */ 643 /* microtime will be less accurate if the RTC is < 36 Hz */ 644 ticks -= 0xffff; 645 } 646 647 tvp->tv_sec = time.tv_sec; 648 if (tm >= 1000000) { 649 tvp->tv_sec += 1; 650 tm -= 1000000; 651 } 652 653 tvp->tv_usec = tm; 654 655 /* Make sure the time has advanced. */ 656 657 if (tvp->tv_sec == oldtv.tv_sec && 658 tvp->tv_usec <= oldtv.tv_usec) { 659 tvp->tv_usec = oldtv.tv_usec + 1; 660 if (tvp->tv_usec >= 1000000) { 661 tvp->tv_usec -= 1000000; 662 ++tvp->tv_sec; 663 } 664 } 665 666 oldtv = *tvp; 667 (void)splx(s); 668 } 669 670 /* End of clock.c */ 671