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