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