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