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