1 /* $NetBSD: clock.c,v 1.15 2002/10/02 16:02:45 thorpej Exp $ */ 2 3 /* 4 * Copyright (c) 1988 University of Utah. 5 * Copyright (c) 1982, 1990, 1993 6 * The Regents of the University of California. All rights reserved. 7 * 8 * This code is derived from software contributed to Berkeley by 9 * the Systems Programming Group of the University of Utah Computer 10 * Science Department. 11 * 12 * Redistribution and use in source and binary forms, with or without 13 * modification, are permitted provided that the following conditions 14 * are met: 15 * 1. Redistributions of source code must retain the above copyright 16 * notice, this list of conditions and the following disclaimer. 17 * 2. Redistributions in binary form must reproduce the above copyright 18 * notice, this list of conditions and the following disclaimer in the 19 * documentation and/or other materials provided with the distribution. 20 * 3. All advertising materials mentioning features or use of this software 21 * must display the following acknowledgement: 22 * This product includes software developed by the University of 23 * California, Berkeley and its contributors. 24 * 4. Neither the name of the University nor the names of its contributors 25 * may be used to endorse or promote products derived from this software 26 * without specific prior written permission. 27 * 28 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND 29 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 30 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 31 * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE 32 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 33 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 34 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 35 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 36 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 37 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 38 * SUCH DAMAGE. 39 * 40 * from: Utah $Hdr: clock.c 1.18 91/01/21$ 41 * 42 * @(#)clock.c 8.2 (Berkeley) 1/12/94 43 */ 44 45 #include "clock.h" 46 47 #if NCLOCK > 0 48 49 #include <sys/param.h> 50 #include <sys/systm.h> 51 #include <sys/kernel.h> 52 #include <sys/device.h> 53 54 #include <machine/psl.h> 55 #include <machine/cpu.h> 56 #include <machine/bus.h> 57 58 #include <dev/clock_subr.h> 59 60 #include <arch/x68k/dev/mfp.h> 61 #include <arch/x68k/dev/rtclock_var.h> 62 63 64 struct clock_softc { 65 struct device sc_dev; 66 }; 67 68 static int clock_match __P((struct device *, struct cfdata *, void *)); 69 static void clock_attach __P((struct device *, struct device *, void *)); 70 71 CFATTACH_DECL(clock, sizeof(struct clock_softc), 72 clock_match, clock_attach, NULL, NULL); 73 74 static int 75 clock_match(parent, cf, aux) 76 struct device *parent; 77 struct cfdata *cf; 78 void *aux; 79 { 80 if (strcmp (aux, "clock") != 0) 81 return (0); 82 if (cf->cf_unit != 0) 83 return (0); 84 return 1; 85 } 86 87 88 static void 89 clock_attach(parent, self, aux) 90 struct device *parent, *self; 91 void *aux; 92 { 93 printf (": MFP timer C\n"); 94 95 return; 96 } 97 98 99 /* 100 * MFP of X68k uses 4MHz clock always and we use 1/200 prescaler here. 101 * Therefore, clock interval is 50 usec. 102 */ 103 #define CLK_RESOLUTION (50) 104 #define CLOCKS_PER_SEC (1000000 / CLK_RESOLUTION) 105 106 static int clkint; /* clock interval */ 107 108 static int clkread __P((void)); 109 110 /* 111 * Machine-dependent clock routines. 112 * 113 * Startrtclock restarts the real-time clock, which provides 114 * hardclock interrupts to kern_clock.c. 115 * 116 * Inittodr initializes the time of day hardware which provides 117 * date functions. 118 * 119 * Resettodr restores the time of day hardware after a time change. 120 * 121 * A note on the real-time clock: 122 * We actually load the clock with CLK_INTERVAL-1 instead of CLK_INTERVAL. 123 * This is because the counter decrements to zero after N+1 enabled clock 124 * periods where N is the value loaded into the counter. 125 */ 126 127 /* 128 * Set up the real-time and statistics clocks. Leave stathz 0 only if 129 * no alternative timer is available. 130 * 131 */ 132 void 133 cpu_initclocks() 134 { 135 if (CLOCKS_PER_SEC % hz || 136 hz <= (CLOCKS_PER_SEC / 256) || hz > CLOCKS_PER_SEC) { 137 printf("cannot set %d Hz clock. using 100 Hz\n", hz); 138 hz = 100; 139 } 140 clkint = CLOCKS_PER_SEC / hz; 141 142 mfp_set_tcdcr(mfp_get_tcdcr() & 0x0f); /* stop timer C */ 143 mfp_set_tcdcr(mfp_get_tcdcr() | 0x70); /* 1/200 delay mode */ 144 145 mfp_set_tcdr(clkint); 146 mfp_bit_set_ierb(MFP_INTR_TIMER_C); 147 } 148 149 /* 150 * We assume newhz is either stathz or profhz, and that neither will 151 * change after being set up above. Could recalculate intervals here 152 * but that would be a drag. 153 */ 154 void 155 setstatclockrate(hz) 156 int hz; 157 { 158 } 159 160 /* 161 * Returns number of usec since last recorded clock "tick" 162 * (i.e. clock interrupt). 163 */ 164 int 165 clkread() 166 { 167 return (clkint - mfp_get_tcdr()) * CLK_RESOLUTION; 168 } 169 170 171 #if 0 172 void 173 DELAY(mic) 174 int mic; 175 { 176 u_long n; 177 short hpos; 178 179 /* 180 * busy-poll for mic microseconds. This is *no* general timeout function, 181 * it's meant for timing in hardware control, and as such, may not lower 182 * interrupt priorities to really `sleep'. 183 */ 184 185 /* 186 * this function uses HSync pulses as base units. The custom chips 187 * display only deals with 31.6kHz/2 refresh, this gives us a 188 * resolution of 1/15800 s, which is ~63us (add some fuzz so we really 189 * wait awhile, even if using small timeouts) 190 */ 191 n = mic/32 + 2; 192 do { 193 while ((mfp.gpip & MFP_GPIP_HSYNC) != 0) 194 asm("nop"); 195 while ((mfp.gpip & MFP_GPIP_HSYNC) == 0) 196 asm("nop"); 197 } while (n--); 198 } 199 #endif 200 201 202 #if notyet 203 204 /* implement this later. I'd suggest using both timers in CIA-A, they're 205 not yet used. */ 206 207 /* 208 * /dev/clock: mappable high resolution timer. 209 * 210 * This code implements a 32-bit recycling counter (with a 4 usec period) 211 * using timers 2 & 3 on the 6840 clock chip. The counter can be mapped 212 * RO into a user's address space to achieve low overhead (no system calls), 213 * high-precision timing. 214 * 215 * Note that timer 3 is also used for the high precision profiling timer 216 * (PROFTIMER code above). Care should be taken when both uses are 217 * configured as only a token effort is made to avoid conflicting use. 218 */ 219 #include <sys/proc.h> 220 #include <sys/resourcevar.h> 221 #include <sys/ioctl.h> 222 #include <sys/malloc.h> 223 #include <uvm/uvm_extern.h> /* XXX needed? */ 224 #include <x68k/x68k/clockioctl.h> 225 #include <sys/specdev.h> 226 #include <sys/vnode.h> 227 #include <sys/mman.h> 228 229 int clockon = 0; /* non-zero if high-res timer enabled */ 230 #ifdef PROFTIMER 231 int profprocs = 0; /* # of procs using profiling timer */ 232 #endif 233 #ifdef DEBUG 234 int clockdebug = 0; 235 #endif 236 237 /*ARGSUSED*/ 238 clockopen(dev, flags) 239 dev_t dev; 240 { 241 #ifdef PROFTIMER 242 #ifdef PROF 243 /* 244 * Kernel profiling enabled, give up. 245 */ 246 if (profiling) 247 return(EBUSY); 248 #endif /* PROF */ 249 /* 250 * If any user processes are profiling, give up. 251 */ 252 if (profprocs) 253 return(EBUSY); 254 #endif /* PROFTIMER */ 255 if (!clockon) { 256 startclock(); 257 clockon++; 258 } 259 return(0); 260 } 261 262 /*ARGSUSED*/ 263 clockclose(dev, flags) 264 dev_t dev; 265 { 266 (void) clockunmmap(dev, (caddr_t)0, curproc); /* XXX */ 267 stopclock(); 268 clockon = 0; 269 return(0); 270 } 271 272 /*ARGSUSED*/ 273 clockioctl(dev, cmd, data, flag, p) 274 dev_t dev; 275 caddr_t data; 276 struct proc *p; 277 { 278 int error = 0; 279 280 switch (cmd) { 281 282 case CLOCKMAP: 283 error = clockmmap(dev, (caddr_t *)data, p); 284 break; 285 286 case CLOCKUNMAP: 287 error = clockunmmap(dev, *(caddr_t *)data, p); 288 break; 289 290 case CLOCKGETRES: 291 *(int *)data = CLK_RESOLUTION; 292 break; 293 294 default: 295 error = EINVAL; 296 break; 297 } 298 return(error); 299 } 300 301 /*ARGSUSED*/ 302 clockmap(dev, off, prot) 303 dev_t dev; 304 { 305 return((off + (INTIOBASE+CLKBASE+CLKSR-1)) >> PGSHIFT); 306 } 307 308 clockmmap(dev, addrp, p) 309 dev_t dev; 310 caddr_t *addrp; 311 struct proc *p; 312 { 313 int error; 314 struct vnode vn; 315 struct specinfo si; 316 int flags; 317 318 flags = MAP_FILE|MAP_SHARED; 319 if (*addrp) 320 flags |= MAP_FIXED; 321 else 322 *addrp = (caddr_t)0x1000000; /* XXX */ 323 vn.v_type = VCHR; /* XXX */ 324 vn.v_specinfo = &si; /* XXX */ 325 vn.v_rdev = dev; /* XXX */ 326 error = vm_mmap(&p->p_vmspace->vm_map, (vaddr_t *)addrp, 327 PAGE_SIZE, VM_PROT_ALL, flags, (caddr_t)&vn, 0); 328 return(error); 329 } 330 331 clockunmmap(dev, addr, p) 332 dev_t dev; 333 caddr_t addr; 334 struct proc *p; 335 { 336 int rv; 337 338 if (addr == 0) 339 return(EINVAL); /* XXX: how do we deal with this? */ 340 uvm_deallocate(p->p_vmspace->vm_map, (vaddr_t)addr, PAGE_SIZE); 341 return 0; 342 } 343 344 startclock() 345 { 346 register struct clkreg *clk = (struct clkreg *)clkstd[0]; 347 348 clk->clk_msb2 = -1; clk->clk_lsb2 = -1; 349 clk->clk_msb3 = -1; clk->clk_lsb3 = -1; 350 351 clk->clk_cr2 = CLK_CR3; 352 clk->clk_cr3 = CLK_OENAB|CLK_8BIT; 353 clk->clk_cr2 = CLK_CR1; 354 clk->clk_cr1 = CLK_IENAB; 355 } 356 357 stopclock() 358 { 359 register struct clkreg *clk = (struct clkreg *)clkstd[0]; 360 361 clk->clk_cr2 = CLK_CR3; 362 clk->clk_cr3 = 0; 363 clk->clk_cr2 = CLK_CR1; 364 clk->clk_cr1 = CLK_IENAB; 365 } 366 367 #endif /* notyet */ 368 369 370 #ifdef PROFTIMER 371 /* 372 * This code allows the amiga kernel to use one of the extra timers on 373 * the clock chip for profiling, instead of the regular system timer. 374 * The advantage of this is that the profiling timer can be turned up to 375 * a higher interrupt rate, giving finer resolution timing. The profclock 376 * routine is called from the lev6intr in locore, and is a specialized 377 * routine that calls addupc. The overhead then is far less than if 378 * hardclock/softclock was called. Further, the context switch code in 379 * locore has been changed to turn the profile clock on/off when switching 380 * into/out of a process that is profiling (startprofclock/stopprofclock). 381 * This reduces the impact of the profiling clock on other users, and might 382 * possibly increase the accuracy of the profiling. 383 */ 384 int profint = PRF_INTERVAL; /* Clock ticks between interrupts */ 385 int profscale = 0; /* Scale factor from sys clock to prof clock */ 386 char profon = 0; /* Is profiling clock on? */ 387 388 /* profon values - do not change, locore.s assumes these values */ 389 #define PRF_NONE 0x00 390 #define PRF_USER 0x01 391 #define PRF_KERNEL 0x80 392 393 initprofclock() 394 { 395 struct proc *p = curproc; /* XXX */ 396 397 /* 398 * If the high-res timer is running, force profiling off. 399 * Unfortunately, this gets reflected back to the user not as 400 * an error but as a lack of results. 401 */ 402 if (clockon) { 403 p->p_stats->p_prof.pr_scale = 0; 404 return; 405 } 406 /* 407 * Keep track of the number of user processes that are profiling 408 * by checking the scale value. 409 * 410 * XXX: this all assumes that the profiling code is well behaved; 411 * i.e. profil() is called once per process with pcscale non-zero 412 * to turn it on, and once with pcscale zero to turn it off. 413 * Also assumes you don't do any forks or execs. Oh well, there 414 * is always adb... 415 */ 416 if (p->p_stats->p_prof.pr_scale) 417 profprocs++; 418 else 419 profprocs--; 420 /* 421 * The profile interrupt interval must be an even divisor 422 * of the CLK_INTERVAL so that scaling from a system clock 423 * tick to a profile clock tick is possible using integer math. 424 */ 425 if (profint > CLK_INTERVAL || (CLK_INTERVAL % profint) != 0) 426 profint = CLK_INTERVAL; 427 profscale = CLK_INTERVAL / profint; 428 } 429 430 startprofclock() 431 { 432 } 433 434 stopprofclock() 435 { 436 } 437 438 #ifdef PROF 439 /* 440 * profclock() is expanded in line in lev6intr() unless profiling kernel. 441 * Assumes it is called with clock interrupts blocked. 442 */ 443 profclock(pc, ps) 444 caddr_t pc; 445 int ps; 446 { 447 /* 448 * Came from user mode. 449 * If this process is being profiled record the tick. 450 */ 451 if (USERMODE(ps)) { 452 if (p->p_stats.p_prof.pr_scale) 453 addupc(pc, &curproc->p_stats.p_prof, 1); 454 } 455 /* 456 * Came from kernel (supervisor) mode. 457 * If we are profiling the kernel, record the tick. 458 */ 459 else if (profiling < 2) { 460 register int s = pc - s_lowpc; 461 462 if (s < s_textsize) 463 kcount[s / (HISTFRACTION * sizeof (*kcount))]++; 464 } 465 /* 466 * Kernel profiling was on but has been disabled. 467 * Mark as no longer profiling kernel and if all profiling done, 468 * disable the clock. 469 */ 470 if (profiling && (profon & PRF_KERNEL)) { 471 profon &= ~PRF_KERNEL; 472 if (profon == PRF_NONE) 473 stopprofclock(); 474 } 475 } 476 #endif /* PROF */ 477 #endif /* PROFTIMER */ 478 479 /* 480 * Return the best possible estimate of the current time. 481 */ 482 void 483 microtime(tvp) 484 register struct timeval *tvp; 485 { 486 static struct timeval lasttime; 487 488 *tvp = time; 489 tvp->tv_usec += clkread(); 490 while (tvp->tv_usec >= 1000000) { 491 tvp->tv_sec++; 492 tvp->tv_usec -= 1000000; 493 } 494 if (tvp->tv_sec == lasttime.tv_sec && 495 tvp->tv_usec <= lasttime.tv_usec && 496 (tvp->tv_usec = lasttime.tv_usec + 1) >= 1000000) { 497 tvp->tv_sec++; 498 tvp->tv_usec -= 1000000; 499 } 500 lasttime = *tvp; 501 } 502 503 /* this is a hook set by a clock driver for the configured realtime clock, 504 returning plain current unix-time */ 505 time_t (*gettod) __P((void)) = 0; 506 int (*settod) __P((long)) = 0; 507 508 /* 509 * Initialize the time of day register, based on the time base which is, e.g. 510 * from a filesystem. 511 */ 512 void 513 inittodr(base) 514 time_t base; 515 { 516 u_long timbuf = base; /* assume no battery clock exists */ 517 518 if (!gettod) 519 printf ("WARNING: no battery clock\n"); 520 else 521 timbuf = gettod(); 522 523 if (timbuf < base) { 524 printf ("WARNING: bad date in battery clock\n"); 525 timbuf = base; 526 } 527 if (base < 5*SECYR) { 528 printf("WARNING: preposterous time in file system"); 529 timbuf = 6*SECYR + 186*SECDAY + SECDAY/2; 530 printf(" -- CHECK AND RESET THE DATE!\n"); 531 } 532 533 /* Battery clock does not store usec's, so forget about it. */ 534 time.tv_sec = timbuf; 535 } 536 537 void 538 resettodr() 539 { 540 if (settod) 541 if (settod (time.tv_sec) != 1) 542 printf("Cannot set battery backed clock\n"); 543 } 544 #else /* NCLOCK */ 545 #error loose. 546 #endif 547