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