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.12 (Berkeley) 06/20/92 15 */ 16 17 #include "param.h" 18 #include "kernel.h" 19 #include "hp/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, 244 (vm_offset_t)addr, PAGE_SIZE); 245 return(rv == KERN_SUCCESS ? 0 : EINVAL); 246 } 247 248 startclock() 249 { 250 register struct clkreg *clk = (struct clkreg *)clkstd[0]; 251 252 clk->clk_msb2 = -1; clk->clk_lsb2 = -1; 253 clk->clk_msb3 = -1; clk->clk_lsb3 = -1; 254 255 clk->clk_cr2 = CLK_CR3; 256 clk->clk_cr3 = CLK_OENAB|CLK_8BIT; 257 clk->clk_cr2 = CLK_CR1; 258 clk->clk_cr1 = CLK_IENAB; 259 } 260 261 stopclock() 262 { 263 register struct clkreg *clk = (struct clkreg *)clkstd[0]; 264 265 clk->clk_cr2 = CLK_CR3; 266 clk->clk_cr3 = 0; 267 clk->clk_cr2 = CLK_CR1; 268 clk->clk_cr1 = CLK_IENAB; 269 } 270 #endif 271 272 #ifdef PROFTIMER 273 /* 274 * This code allows the hp300 kernel to use one of the extra timers on 275 * the clock chip for profiling, instead of the regular system timer. 276 * The advantage of this is that the profiling timer can be turned up to 277 * a higher interrupt rate, giving finer resolution timing. The profclock 278 * routine is called from the lev6intr in locore, and is a specialized 279 * routine that calls addupc. The overhead then is far less than if 280 * hardclock/softclock was called. Further, the context switch code in 281 * locore has been changed to turn the profile clock on/off when switching 282 * into/out of a process that is profiling (startprofclock/stopprofclock). 283 * This reduces the impact of the profiling clock on other users, and might 284 * possibly increase the accuracy of the profiling. 285 */ 286 int profint = PRF_INTERVAL; /* Clock ticks between interrupts */ 287 int profscale = 0; /* Scale factor from sys clock to prof clock */ 288 char profon = 0; /* Is profiling clock on? */ 289 290 /* profon values - do not change, locore.s assumes these values */ 291 #define PRF_NONE 0x00 292 #define PRF_USER 0x01 293 #define PRF_KERNEL 0x80 294 295 initprofclock(profprocs) 296 int profprocs; 297 { 298 register struct clkreg *clk = (struct clkreg *)clkstd[0]; 299 300 /* 301 * The profile interrupt interval must be an even divisor 302 * of the CLK_INTERVAL so that scaling from a system clock 303 * tick to a profile clock tick is possible using integer math. 304 */ 305 if (profint > CLK_INTERVAL || (CLK_INTERVAL % profint) != 0) 306 profint = CLK_INTERVAL; 307 profscale = CLK_INTERVAL / profint; 308 profhz = hz * profscale; 309 /* 310 * If a process maps the clock, we fail silently. 311 * Unfortunately, this gets reflected back to the user not as 312 * an error but as a lack of results. 313 */ 314 if (clockon) 315 return; 316 switch (profprocs) { 317 case 1: 318 /* start clock */ 319 clk->clk_msb3 = (profint-1) >> 8 & 0xFF; 320 clk->clk_lsb3 = (profint-1) & 0xFF; 321 clk->clk_cr2 = CLK_CR3; 322 clk->clk_cr3 = CLK_IENAB; 323 break; 324 case 0: 325 /* stop clock */ 326 clk->clk_cr2 = CLK_CR3; 327 clk->clk_cr3 = 0; 328 break; 329 } 330 } 331 332 #ifdef GPROF 333 /* 334 * profclock() is expanded in line in lev6intr() unless profiling kernel. 335 * Assumes it is called with clock interrupts blocked. 336 */ 337 profclock(pc, ps) 338 caddr_t pc; 339 int ps; 340 { 341 /* 342 * Came from user mode. 343 * If this process is being profiled record the tick. 344 */ 345 if (USERMODE(ps)) { 346 if (curproc->p_stats->p_prof.pr_scale) 347 addupc(pc, &curproc->p_stats->p_prof, 1); 348 } 349 /* 350 * Came from kernel (supervisor) mode. 351 * If we are profiling the kernel, record the tick. 352 */ 353 else if (profiling < 2) { 354 register int s = pc - s_lowpc; 355 356 if (s < s_textsize) 357 kcount[s / (HISTFRACTION * sizeof (*kcount))]++; 358 } 359 /* 360 * Kernel profiling was on but has been disabled. 361 * Mark as no longer profiling kernel and if all profiling done, 362 * disable the clock. 363 */ 364 if (profiling && (profon & PRF_KERNEL)) { 365 profon &= ~PRF_KERNEL; 366 if (profon == PRF_NONE) 367 stopprofclock(); 368 } 369 } 370 #endif 371 #endif 372 373 /* 374 * Initialize the time of day register, based on the time base which is, e.g. 375 * from a filesystem. 376 */ 377 inittodr(base) 378 time_t base; 379 { 380 u_long timbuf = base; /* assume no battery clock exists */ 381 static int bbcinited = 0; 382 383 /* XXX */ 384 if (!bbcinited) { 385 if (badbaddr(&BBCADDR->hil_stat)) 386 printf("WARNING: no battery clock\n"); 387 else 388 bbcaddr = BBCADDR; 389 bbcinited = 1; 390 } 391 392 /* 393 * bbc_to_gmt converts and stores the gmt in timbuf. 394 * If an error is detected in bbc_to_gmt, or if the filesystem 395 * time is more recent than the gmt time in the clock, 396 * then use the filesystem time and warn the user. 397 */ 398 if (!bbc_to_gmt(&timbuf) || timbuf < base) { 399 printf("WARNING: bad date in battery clock\n"); 400 timbuf = base; 401 } 402 if (base < 5*SECYR) { 403 printf("WARNING: preposterous time in file system"); 404 timbuf = 6*SECYR + 186*SECDAY + SECDAY/2; 405 printf(" -- CHECK AND RESET THE DATE!\n"); 406 } 407 408 /* Battery clock does not store usec's, so forget about it. */ 409 time.tv_sec = timbuf; 410 } 411 412 resettodr() 413 { 414 register int i; 415 register struct bbc_tm *tmptr; 416 417 tmptr = gmt_to_bbc(time.tv_sec); 418 419 decimal_to_bbc(0, 1, tmptr->tm_sec); 420 decimal_to_bbc(2, 3, tmptr->tm_min); 421 decimal_to_bbc(4, 5, tmptr->tm_hour); 422 decimal_to_bbc(7, 8, tmptr->tm_mday); 423 decimal_to_bbc(9, 10, tmptr->tm_mon); 424 decimal_to_bbc(11, 12, tmptr->tm_year); 425 426 /* Some bogusness to deal with seemingly broken hardware. Nonsense */ 427 bbc_registers[5] = ((tmptr->tm_hour / 10) & 0x03) + 8; 428 429 write_bbc_reg(15, 13); /* reset prescalar */ 430 431 for (i = 0; i <= NUM_BBC_REGS; i++) 432 if (bbc_registers[i] != write_bbc_reg(i, bbc_registers[i])) { 433 printf("Cannot set battery backed clock\n"); 434 break; 435 } 436 } 437 438 struct bbc_tm * 439 gmt_to_bbc(tim) 440 long tim; 441 { 442 register int i; 443 register long hms, day; 444 static struct bbc_tm rt; 445 446 day = tim / SECDAY; 447 hms = tim % SECDAY; 448 449 /* Hours, minutes, seconds are easy */ 450 rt.tm_hour = hms / 3600; 451 rt.tm_min = (hms % 3600) / 60; 452 rt.tm_sec = (hms % 3600) % 60; 453 454 /* Number of years in days */ 455 for (i = STARTOFTIME - 1900; day >= days_in_year(i); i++) 456 day -= days_in_year(i); 457 rt.tm_year = i; 458 459 /* Number of months in days left */ 460 if (leapyear(rt.tm_year)) 461 days_in_month(FEBRUARY) = 29; 462 for (i = 1; day >= days_in_month(i); i++) 463 day -= days_in_month(i); 464 days_in_month(FEBRUARY) = 28; 465 rt.tm_mon = i; 466 467 /* Days are what is left over (+1) from all that. */ 468 rt.tm_mday = day + 1; 469 470 return(&rt); 471 } 472 473 bbc_to_gmt(timbuf) 474 u_long *timbuf; 475 { 476 register int i; 477 register u_long tmp; 478 int year, month, day, hour, min, sec; 479 480 read_bbc(); 481 482 sec = bbc_to_decimal(1, 0); 483 min = bbc_to_decimal(3, 2); 484 485 /* 486 * Hours are different for some reason. Makes no sense really. 487 */ 488 hour = ((bbc_registers[5] & 0x03) * 10) + bbc_registers[4]; 489 day = bbc_to_decimal(8, 7); 490 month = bbc_to_decimal(10, 9); 491 year = bbc_to_decimal(12, 11) + 1900; 492 493 range_test(hour, 0, 23); 494 range_test(day, 1, 31); 495 range_test(month, 1, 12); 496 range_test(year, STARTOFTIME, 2000); 497 498 tmp = 0; 499 500 for (i = STARTOFTIME; i < year; i++) 501 tmp += days_in_year(i); 502 if (leapyear(year) && month > FEBRUARY) 503 tmp++; 504 505 for (i = 1; i < month; i++) 506 tmp += days_in_month(i); 507 508 tmp += (day - 1); 509 tmp = ((tmp * 24 + hour) * 60 + min) * 60 + sec; 510 511 *timbuf = tmp; 512 return(1); 513 } 514 515 read_bbc() 516 { 517 register int i, read_okay; 518 519 read_okay = 0; 520 while (!read_okay) { 521 read_okay = 1; 522 for (i = 0; i <= NUM_BBC_REGS; i++) 523 bbc_registers[i] = read_bbc_reg(i); 524 for (i = 0; i <= NUM_BBC_REGS; i++) 525 if (bbc_registers[i] != read_bbc_reg(i)) 526 read_okay = 0; 527 } 528 } 529 530 u_char 531 read_bbc_reg(reg) 532 int reg; 533 { 534 u_char data = reg; 535 536 if (bbcaddr) { 537 send_hil_cmd(bbcaddr, BBC_SET_REG, &data, 1, NULL); 538 send_hil_cmd(bbcaddr, BBC_READ_REG, NULL, 0, &data); 539 } 540 return(data); 541 } 542 543 u_char 544 write_bbc_reg(reg, data) 545 u_int data; 546 { 547 u_char tmp; 548 549 tmp = (u_char) ((data << HIL_SSHIFT) | reg); 550 551 if (bbcaddr) { 552 send_hil_cmd(bbcaddr, BBC_SET_REG, &tmp, 1, NULL); 553 send_hil_cmd(bbcaddr, BBC_WRITE_REG, NULL, 0, NULL); 554 send_hil_cmd(bbcaddr, BBC_READ_REG, NULL, 0, &tmp); 555 } 556 return(tmp); 557 } 558