|
|
1.1 ! root 1: /* ! 2: * Copyright (c) 1988 University of Utah. ! 3: * Copyright (c) 1982, 1990 The Regents of the University of California. ! 4: * All rights reserved. ! 5: * ! 6: * This code is derived from software contributed to Berkeley by ! 7: * the Systems Programming Group of the University of Utah Computer ! 8: * Science Department. ! 9: * ! 10: * Redistribution is only permitted until one year after the first shipment ! 11: * of 4.4BSD by the Regents. Otherwise, redistribution and use in source and ! 12: * binary forms are permitted provided that: (1) source distributions retain ! 13: * this entire copyright notice and comment, and (2) distributions including ! 14: * binaries display the following acknowledgement: This product includes ! 15: * software developed by the University of California, Berkeley and its ! 16: * contributors'' in the documentation or other materials provided with the ! 17: * distribution and in all advertising materials mentioning features or use ! 18: * of this software. Neither the name of the University nor the names of ! 19: * its contributors may be used to endorse or promote products derived from ! 20: * this software without specific prior written permission. ! 21: * THIS SOFTWARE IS PROVIDED AS IS'' AND WITHOUT ANY EXPRESS OR IMPLIED ! 22: * WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED WARRANTIES OF ! 23: * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. ! 24: * ! 25: * from: Utah $Hdr: clock.c 1.17 89/11/30$ ! 26: * ! 27: * @(#)clock.c 7.2 (Berkeley) 6/22/90 ! 28: */ ! 29: ! 30: #include "param.h" ! 31: #include "user.h" ! 32: #include "kernel.h" ! 33: #include "../hpdev/hilreg.h" ! 34: #include "clockreg.h" ! 35: ! 36: #include "machine/psl.h" ! 37: #include "machine/cpu.h" ! 38: ! 39: #if defined(GPROF) && defined(PROFTIMER) ! 40: #include "gprof.h" ! 41: #endif ! 42: ! 43: int clkstd[] = { IOV(0x5F8000) }; ! 44: ! 45: static int month_days[12] = { ! 46: 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 ! 47: }; ! 48: struct bbc_tm *gmt_to_bbc(); ! 49: u_char bbc_registers[13]; ! 50: u_char write_bbc_reg(), read_bbc_reg(); ! 51: struct hil_dev *bbcaddr = NULL; ! 52: ! 53: /* ! 54: * Machine-dependent clock routines. ! 55: * ! 56: * Startrtclock restarts the real-time clock, which provides ! 57: * hardclock interrupts to kern_clock.c. ! 58: * ! 59: * Inittodr initializes the time of day hardware which provides ! 60: * date functions. ! 61: * ! 62: * Resettodr restores the time of day hardware after a time change. ! 63: * ! 64: * A note on the real-time clock: ! 65: * We actually load the clock with CLK_INTERVAL-1 instead of CLK_INTERVAL. ! 66: * This is because the counter decrements to zero after N+1 enabled clock ! 67: * periods where N is the value loaded into the counter. ! 68: */ ! 69: ! 70: /* ! 71: * Start the real-time clock. ! 72: */ ! 73: startrtclock() ! 74: { ! 75: register struct clkreg *clk = (struct clkreg *)clkstd[0]; ! 76: ! 77: clk->clk_cr2 = CLK_CR1; ! 78: clk->clk_cr1 = CLK_RESET; ! 79: clk->clk_cr2 = CLK_CR3; ! 80: clk->clk_cr3 = 0; ! 81: clk->clk_msb1 = (CLK_INTERVAL-1) >> 8 & 0xFF; ! 82: clk->clk_lsb1 = (CLK_INTERVAL-1) & 0xFF; ! 83: clk->clk_msb2 = 0; ! 84: clk->clk_lsb2 = 0; ! 85: clk->clk_msb3 = 0; ! 86: clk->clk_lsb3 = 0; ! 87: clk->clk_cr2 = CLK_CR1; ! 88: clk->clk_cr1 = CLK_IENAB; ! 89: } ! 90: ! 91: /* ! 92: * Returns number of usec since last recorded clock "tick" ! 93: * (i.e. clock interrupt). ! 94: */ ! 95: clkread() ! 96: { ! 97: register struct clkreg *clk = (struct clkreg *) clkstd[0]; ! 98: register int high, low; ! 99: ! 100: high = clk->clk_msb1; ! 101: low = clk->clk_lsb1; ! 102: if (high != clk->clk_msb1) ! 103: high = clk->clk_msb1; ! 104: ! 105: high = (CLK_INTERVAL-1) - ((high << 8) | low); ! 106: /* ! 107: * Pending interrupt indicates that the counter has wrapped ! 108: * since we went to splhigh(). Need to compensate. ! 109: */ ! 110: if (clk->clk_sr & CLK_INT1) ! 111: high += CLK_INTERVAL; ! 112: return((high * tick) / CLK_INTERVAL); ! 113: } ! 114: ! 115: #include "clock.h" ! 116: #if NCLOCK > 0 ! 117: /* ! 118: * /dev/clock: mappable high resolution timer. ! 119: * ! 120: * This code implements a 32-bit recycling counter (with a 4 usec period) ! 121: * using timers 2 & 3 on the 6840 clock chip. The counter can be mapped ! 122: * RO into a user's address space to achieve low overhead (no system calls), ! 123: * high-precision timing. ! 124: * ! 125: * Note that timer 3 is also used for the high precision profiling timer ! 126: * (PROFTIMER code above). Care should be taken when both uses are ! 127: * configured as only a token effort is made to avoid conflicting use. ! 128: */ ! 129: #include "proc.h" ! 130: #include "ioctl.h" ! 131: #include "mapmem.h" ! 132: #include "malloc.h" ! 133: #include "clockioctl.h" ! 134: ! 135: int clockon = 0; /* non-zero if high-res timer enabled */ ! 136: #ifdef PROFTIMER ! 137: int profprocs = 0; /* # of procs using profiling timer */ ! 138: #endif ! 139: #ifdef DEBUG ! 140: int clockdebug = 0; ! 141: #endif ! 142: ! 143: /*ARGSUSED*/ ! 144: clockopen(dev, flags) ! 145: dev_t dev; ! 146: { ! 147: #ifdef PROFTIMER ! 148: #ifdef GPROF ! 149: /* ! 150: * Kernel profiling enabled, give up. ! 151: */ ! 152: if (profiling) ! 153: return(EBUSY); ! 154: #endif ! 155: /* ! 156: * If any user processes are profiling, give up. ! 157: */ ! 158: if (profprocs) ! 159: return(EBUSY); ! 160: #endif ! 161: if (!clockon) { ! 162: startclock(); ! 163: clockon++; ! 164: } ! 165: return(0); ! 166: } ! 167: ! 168: /*ARGSUSED*/ ! 169: clockclose(dev, flags) ! 170: dev_t dev; ! 171: { ! 172: #ifdef MAPMEM ! 173: (void) clockunmmap(dev, (caddr_t)0); ! 174: #endif ! 175: stopclock(); ! 176: clockon = 0; ! 177: return(0); ! 178: } ! 179: ! 180: /*ARGSUSED*/ ! 181: clockioctl(dev, cmd, data, flag) ! 182: dev_t dev; ! 183: caddr_t data; ! 184: { ! 185: int error = 0; ! 186: ! 187: switch (cmd) { ! 188: ! 189: #ifdef MAPMEM ! 190: case CLOCKMAP: ! 191: error = clockmmap(dev, (caddr_t *)data); ! 192: break; ! 193: ! 194: case CLOCKUNMAP: ! 195: error = clockunmmap(dev, *(caddr_t *)data); ! 196: break; ! 197: ! 198: case CLOCKGETRES: ! 199: *(int *)data = CLK_RESOLUTION; ! 200: break; ! 201: #endif ! 202: ! 203: default: ! 204: error = EINVAL; ! 205: break; ! 206: } ! 207: return(error); ! 208: } ! 209: ! 210: /*ARGSUSED*/ ! 211: clockmap(dev, off, prot) ! 212: dev_t dev; ! 213: { ! 214: #ifdef MMAP ! 215: return((off + (IOBASE+CLKSR-1)) >> PGSHIFT); ! 216: #endif ! 217: } ! 218: ! 219: #ifdef MAPMEM ! 220: ! 221: struct mapmemops clockops = { ! 222: (int (*)())0, (int (*)())0, (int (*)())0, (int (*)())0 ! 223: }; ! 224: ! 225: clockmmap(dev, addrp) ! 226: dev_t dev; ! 227: caddr_t *addrp; ! 228: { ! 229: struct proc *p = u.u_procp; /* XXX */ ! 230: struct mapmem *mp; ! 231: int id, error, clockmapin(); ! 232: ! 233: id = minor(dev); /* XXX */ ! 234: error = mmalloc(p, id, addrp, NBPG, MM_RO|MM_CI|MM_NOCORE, ! 235: &clockops, &mp); ! 236: #ifdef DEBUG ! 237: if (clockdebug) ! 238: printf("clockmmap(%d): addr %x\n", p->p_pid, *addrp); ! 239: #endif ! 240: if (error == 0) ! 241: if (error = mmmapin(p, mp, clockmapin)) ! 242: (void) mmfree(p, mp); ! 243: return(error); ! 244: } ! 245: ! 246: clockunmmap(dev, addr) ! 247: dev_t dev; ! 248: caddr_t addr; ! 249: { ! 250: struct proc *p = u.u_procp; /* XXX */ ! 251: register struct mapmem *mp, **mpp; ! 252: int found, id; ! 253: ! 254: #ifdef DEBUG ! 255: if (clockdebug) ! 256: printf("clockunmmap(%d): addr %x\n", p->p_pid, addr); ! 257: #endif ! 258: id = minor(dev); /* XXX */ ! 259: found = 0; ! 260: mpp = &u.u_mmap; ! 261: for (mp = *mpp; mp; mp = *mpp) { ! 262: if (mp->mm_id != id || mp->mm_ops != &clockops) { ! 263: mpp = &mp->mm_next; ! 264: continue; ! 265: } ! 266: if (addr && ! 267: (addr < mp->mm_uva || addr >= mp->mm_uva+mp->mm_size)) { ! 268: mpp = &mp->mm_next; ! 269: continue; ! 270: } ! 271: mmmapout(p, mp); ! 272: (void) mmfree(p, mp); ! 273: found++; ! 274: } ! 275: return(found ? 0 : EINVAL); ! 276: } ! 277: ! 278: /*ARGSUSED*/ ! 279: clockmapin(mp, off) ! 280: struct mapmem *mp; ! 281: { ! 282: return((off + (IOBASE+CLKSR-1)) >> PGSHIFT); ! 283: } ! 284: #endif ! 285: ! 286: startclock() ! 287: { ! 288: register struct clkreg *clk = (struct clkreg *)clkstd[0]; ! 289: ! 290: clk->clk_msb2 = -1; clk->clk_lsb2 = -1; ! 291: clk->clk_msb3 = -1; clk->clk_lsb3 = -1; ! 292: ! 293: clk->clk_cr2 = CLK_CR3; ! 294: clk->clk_cr3 = CLK_OENAB|CLK_8BIT; ! 295: clk->clk_cr2 = CLK_CR1; ! 296: clk->clk_cr1 = CLK_IENAB; ! 297: } ! 298: ! 299: stopclock() ! 300: { ! 301: register struct clkreg *clk = (struct clkreg *)clkstd[0]; ! 302: ! 303: clk->clk_cr2 = CLK_CR3; ! 304: clk->clk_cr3 = 0; ! 305: clk->clk_cr2 = CLK_CR1; ! 306: clk->clk_cr1 = CLK_IENAB; ! 307: } ! 308: #endif ! 309: ! 310: #ifdef PROFTIMER ! 311: /* ! 312: * This code allows the hp300 kernel to use one of the extra timers on ! 313: * the clock chip for profiling, instead of the regular system timer. ! 314: * The advantage of this is that the profiling timer can be turned up to ! 315: * a higher interrupt rate, giving finer resolution timing. The profclock ! 316: * routine is called from the lev6intr in locore, and is a specialized ! 317: * routine that calls addupc. The overhead then is far less than if ! 318: * hardclock/softclock was called. Further, the context switch code in ! 319: * locore has been changed to turn the profile clock on/off when switching ! 320: * into/out of a process that is profiling (startprofclock/stopprofclock). ! 321: * This reduces the impact of the profiling clock on other users, and might ! 322: * possibly increase the accuracy of the profiling. ! 323: */ ! 324: int profint = PRF_INTERVAL; /* Clock ticks between interrupts */ ! 325: int profscale = 0; /* Scale factor from sys clock to prof clock */ ! 326: char profon = 0; /* Is profiling clock on? */ ! 327: ! 328: /* profon values - do not change, locore.s assumes these values */ ! 329: #define PRF_NONE 0x00 ! 330: #define PRF_USER 0x01 ! 331: #define PRF_KERNEL 0x80 ! 332: ! 333: initprofclock() ! 334: { ! 335: #if NCLOCK > 0 ! 336: /* ! 337: * If the high-res timer is running, force profiling off. ! 338: * Unfortunately, this gets reflected back to the user not as ! 339: * an error but as a lack of results. ! 340: */ ! 341: if (clockon) { ! 342: u.u_prof.pr_scale = 0; ! 343: return; ! 344: } ! 345: /* ! 346: * Keep track of the number of user processes that are profiling ! 347: * by checking the scale value. ! 348: * ! 349: * XXX: this all assumes that the profiling code is well behaved; ! 350: * i.e. profil() is called once per process with pcscale non-zero ! 351: * to turn it on, and once with pcscale zero to turn it off. ! 352: * Also assumes you don't do any forks or execs. Oh well, there ! 353: * is always adb... ! 354: */ ! 355: if (u.u_prof.pr_scale) ! 356: profprocs++; ! 357: else ! 358: profprocs--; ! 359: #endif ! 360: /* ! 361: * The profile interrupt interval must be an even divisor ! 362: * of the CLK_INTERVAL so that scaling from a system clock ! 363: * tick to a profile clock tick is possible using integer math. ! 364: */ ! 365: if (profint > CLK_INTERVAL || (CLK_INTERVAL % profint) != 0) ! 366: profint = CLK_INTERVAL; ! 367: profscale = CLK_INTERVAL / profint; ! 368: } ! 369: ! 370: startprofclock() ! 371: { ! 372: register struct clkreg *clk = (struct clkreg *)clkstd[0]; ! 373: ! 374: clk->clk_msb3 = (profint-1) >> 8 & 0xFF; ! 375: clk->clk_lsb3 = (profint-1) & 0xFF; ! 376: ! 377: clk->clk_cr2 = CLK_CR3; ! 378: clk->clk_cr3 = CLK_IENAB; ! 379: } ! 380: ! 381: stopprofclock() ! 382: { ! 383: register struct clkreg *clk = (struct clkreg *)clkstd[0]; ! 384: ! 385: clk->clk_cr2 = CLK_CR3; ! 386: clk->clk_cr3 = 0; ! 387: } ! 388: ! 389: #ifdef GPROF ! 390: /* ! 391: * profclock() is expanded in line in lev6intr() unless profiling kernel. ! 392: * Assumes it is called with clock interrupts blocked. ! 393: */ ! 394: profclock(pc, ps) ! 395: caddr_t pc; ! 396: int ps; ! 397: { ! 398: /* ! 399: * Came from user mode. ! 400: * If this process is being profiled record the tick. ! 401: */ ! 402: if (USERMODE(ps)) { ! 403: if (u.u_prof.pr_scale) ! 404: addupc(pc, &u.u_prof, 1); ! 405: } ! 406: /* ! 407: * Came from kernel (supervisor) mode. ! 408: * If we are profiling the kernel, record the tick. ! 409: */ ! 410: else if (profiling < 2) { ! 411: register int s = pc - s_lowpc; ! 412: ! 413: if (s < s_textsize) ! 414: kcount[s / (HISTFRACTION * sizeof (*kcount))]++; ! 415: } ! 416: /* ! 417: * Kernel profiling was on but has been disabled. ! 418: * Mark as no longer profiling kernel and if all profiling done, ! 419: * disable the clock. ! 420: */ ! 421: if (profiling && (profon & PRF_KERNEL)) { ! 422: profon &= ~PRF_KERNEL; ! 423: if (profon == PRF_NONE) ! 424: stopprofclock(); ! 425: } ! 426: } ! 427: #endif ! 428: #endif ! 429: ! 430: /* ! 431: * Initialize the time of day register, based on the time base which is, e.g. ! 432: * from a filesystem. ! 433: */ ! 434: inittodr(base) ! 435: time_t base; ! 436: { ! 437: u_long timbuf = base; /* assume no battery clock exists */ ! 438: static int bbcinited = 0; ! 439: ! 440: /* XXX */ ! 441: if (!bbcinited) { ! 442: if (badbaddr(&BBCADDR->hil_stat)) ! 443: printf("WARNING: no battery clock\n"); ! 444: else ! 445: bbcaddr = BBCADDR; ! 446: bbcinited = 1; ! 447: } ! 448: ! 449: /* ! 450: * bbc_to_gmt converts and stores the gmt in timbuf. ! 451: * If an error is detected in bbc_to_gmt, or if the filesystem ! 452: * time is more recent than the gmt time in the clock, ! 453: * then use the filesystem time and warn the user. ! 454: */ ! 455: if (!bbc_to_gmt(&timbuf) || timbuf < base) { ! 456: printf("WARNING: bad date in battery clock\n"); ! 457: timbuf = base; ! 458: } ! 459: if (base < 5*SECYR) { ! 460: printf("WARNING: preposterous time in file system"); ! 461: timbuf = 6*SECYR + 186*SECDAY + SECDAY/2; ! 462: printf(" -- CHECK AND RESET THE DATE!\n"); ! 463: } ! 464: ! 465: /* Battery clock does not store usec's, so forget about it. */ ! 466: time.tv_sec = timbuf; ! 467: } ! 468: ! 469: resettodr() ! 470: { ! 471: register int i; ! 472: register struct bbc_tm *tmptr; ! 473: ! 474: tmptr = gmt_to_bbc(time.tv_sec); ! 475: ! 476: decimal_to_bbc(0, 1, tmptr->tm_sec); ! 477: decimal_to_bbc(2, 3, tmptr->tm_min); ! 478: decimal_to_bbc(4, 5, tmptr->tm_hour); ! 479: decimal_to_bbc(7, 8, tmptr->tm_mday); ! 480: decimal_to_bbc(9, 10, tmptr->tm_mon); ! 481: decimal_to_bbc(11, 12, tmptr->tm_year); ! 482: ! 483: /* Some bogusness to deal with seemingly broken hardware. Nonsense */ ! 484: bbc_registers[5] = ((tmptr->tm_hour / 10) & 0x03) + 8; ! 485: ! 486: write_bbc_reg(15, 13); /* reset prescalar */ ! 487: ! 488: for (i = 0; i <= NUM_BBC_REGS; i++) ! 489: if (bbc_registers[i] != write_bbc_reg(i, bbc_registers[i])) { ! 490: printf("Cannot set battery backed clock\n"); ! 491: break; ! 492: } ! 493: } ! 494: ! 495: struct bbc_tm * ! 496: gmt_to_bbc(tim) ! 497: long tim; ! 498: { ! 499: register int i; ! 500: register long hms, day; ! 501: static struct bbc_tm rt; ! 502: ! 503: day = tim / SECDAY; ! 504: hms = tim % SECDAY; ! 505: ! 506: /* Hours, minutes, seconds are easy */ ! 507: rt.tm_hour = hms / 3600; ! 508: rt.tm_min = (hms % 3600) / 60; ! 509: rt.tm_sec = (hms % 3600) % 60; ! 510: ! 511: /* Number of years in days */ ! 512: for (i = STARTOFTIME - 1900; day >= days_in_year(i); i++) ! 513: day -= days_in_year(i); ! 514: rt.tm_year = i; ! 515: ! 516: /* Number of months in days left */ ! 517: if (leapyear(rt.tm_year)) ! 518: days_in_month(FEBRUARY) = 29; ! 519: for (i = 1; day >= days_in_month(i); i++) ! 520: day -= days_in_month(i); ! 521: days_in_month(FEBRUARY) = 28; ! 522: rt.tm_mon = i; ! 523: ! 524: /* Days are what is left over (+1) from all that. */ ! 525: rt.tm_mday = day + 1; ! 526: ! 527: return(&rt); ! 528: } ! 529: ! 530: bbc_to_gmt(timbuf) ! 531: u_long *timbuf; ! 532: { ! 533: register int i; ! 534: register u_long tmp; ! 535: int year, month, day, hour, min, sec; ! 536: ! 537: read_bbc(); ! 538: ! 539: sec = bbc_to_decimal(1, 0); ! 540: min = bbc_to_decimal(3, 2); ! 541: ! 542: /* ! 543: * Hours are different for some reason. Makes no sense really. ! 544: */ ! 545: hour = ((bbc_registers[5] & 0x03) * 10) + bbc_registers[4]; ! 546: day = bbc_to_decimal(8, 7); ! 547: month = bbc_to_decimal(10, 9); ! 548: year = bbc_to_decimal(12, 11) + 1900; ! 549: ! 550: range_test(hour, 0, 23); ! 551: range_test(day, 1, 31); ! 552: range_test(month, 1, 12); ! 553: range_test(year, STARTOFTIME, 2000); ! 554: ! 555: tmp = 0; ! 556: ! 557: for (i = STARTOFTIME; i < year; i++) ! 558: tmp += days_in_year(i); ! 559: if (leapyear(year) && month > FEBRUARY) ! 560: tmp++; ! 561: ! 562: for (i = 1; i < month; i++) ! 563: tmp += days_in_month(i); ! 564: ! 565: tmp += (day - 1); ! 566: tmp = ((tmp * 24 + hour) * 60 + min) * 60 + sec; ! 567: ! 568: *timbuf = tmp; ! 569: return(1); ! 570: } ! 571: ! 572: read_bbc() ! 573: { ! 574: register int i, read_okay; ! 575: ! 576: read_okay = 0; ! 577: while (!read_okay) { ! 578: read_okay = 1; ! 579: for (i = 0; i <= NUM_BBC_REGS; i++) ! 580: bbc_registers[i] = read_bbc_reg(i); ! 581: for (i = 0; i <= NUM_BBC_REGS; i++) ! 582: if (bbc_registers[i] != read_bbc_reg(i)) ! 583: read_okay = 0; ! 584: } ! 585: } ! 586: ! 587: u_char ! 588: read_bbc_reg(reg) ! 589: int reg; ! 590: { ! 591: u_char data = reg; ! 592: ! 593: if (bbcaddr) { ! 594: send_hil_cmd(bbcaddr, BBC_SET_REG, &data, 1, NULL); ! 595: send_hil_cmd(bbcaddr, BBC_READ_REG, NULL, 0, &data); ! 596: } ! 597: return(data); ! 598: } ! 599: ! 600: u_char ! 601: write_bbc_reg(reg, data) ! 602: u_int data; ! 603: { ! 604: u_char tmp; ! 605: ! 606: tmp = (u_char) ((data << HIL_SSHIFT) | reg); ! 607: ! 608: if (bbcaddr) { ! 609: send_hil_cmd(bbcaddr, BBC_SET_REG, &tmp, 1, NULL); ! 610: send_hil_cmd(bbcaddr, BBC_WRITE_REG, NULL, 0, NULL); ! 611: send_hil_cmd(bbcaddr, BBC_READ_REG, NULL, 0, &tmp); ! 612: } ! 613: return(tmp); ! 614: }
This archive runs on limited infrastructure. Preserving old code on modern bandwidth. Automated agents are requested to crawl responsibly.