1 /* $NetBSD: clock.c,v 1.47 2008/01/06 18:50:31 mhitch Exp $ */ 2 3 /* 4 * Copyright (c) 1982, 1990 The Regents of the University of California. 5 * All rights reserved. 6 * 7 * This code is derived from software contributed to Berkeley by 8 * the Systems Programming Group of the University of Utah Computer 9 * Science Department. 10 * 11 * Redistribution and use in source and binary forms, with or without 12 * modification, are permitted provided that the following conditions 13 * are met: 14 * 1. Redistributions of source code must retain the above copyright 15 * notice, this list of conditions and the following disclaimer. 16 * 2. Redistributions in binary form must reproduce the above copyright 17 * notice, this list of conditions and the following disclaimer in the 18 * documentation and/or other materials provided with the distribution. 19 * 3. Neither the name of the University nor the names of its contributors 20 * may be used to endorse or promote products derived from this software 21 * without specific prior written permission. 22 * 23 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND 24 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 25 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 26 * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE 27 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 28 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 29 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 30 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 31 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 32 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 33 * SUCH DAMAGE. 34 * 35 * from: Utah $Hdr: clock.c 1.18 91/01/21$ 36 * 37 * @(#)clock.c 7.6 (Berkeley) 5/7/91 38 */ 39 /* 40 * Copyright (c) 1988 University of Utah. 41 * 42 * This code is derived from software contributed to Berkeley by 43 * the Systems Programming Group of the University of Utah Computer 44 * Science Department. 45 * 46 * Redistribution and use in source and binary forms, with or without 47 * modification, are permitted provided that the following conditions 48 * are met: 49 * 1. Redistributions of source code must retain the above copyright 50 * notice, this list of conditions and the following disclaimer. 51 * 2. Redistributions in binary form must reproduce the above copyright 52 * notice, this list of conditions and the following disclaimer in the 53 * documentation and/or other materials provided with the distribution. 54 * 3. All advertising materials mentioning features or use of this software 55 * must display the following acknowledgement: 56 * This product includes software developed by the University of 57 * California, Berkeley and its contributors. 58 * 4. Neither the name of the University nor the names of its contributors 59 * may be used to endorse or promote products derived from this software 60 * without specific prior written permission. 61 * 62 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND 63 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 64 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 65 * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE 66 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 67 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 68 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 69 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 70 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 71 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 72 * SUCH DAMAGE. 73 * 74 * from: Utah $Hdr: clock.c 1.18 91/01/21$ 75 * 76 * @(#)clock.c 7.6 (Berkeley) 5/7/91 77 */ 78 79 #include <sys/cdefs.h> 80 __KERNEL_RCSID(0, "$NetBSD: clock.c,v 1.47 2008/01/06 18:50:31 mhitch Exp $"); 81 82 #include <sys/param.h> 83 #include <sys/kernel.h> 84 #include <sys/device.h> 85 #include <sys/systm.h> 86 #include <sys/timetc.h> 87 #include <machine/psl.h> 88 #include <machine/cpu.h> 89 #include <amiga/amiga/device.h> 90 #include <amiga/amiga/custom.h> 91 #include <amiga/amiga/cia.h> 92 #ifdef DRACO 93 #include <amiga/amiga/drcustom.h> 94 #include <m68k/include/asm_single.h> 95 #endif 96 #include <amiga/dev/rtc.h> 97 #include <amiga/dev/zbusvar.h> 98 99 #if defined(PROF) && defined(PROFTIMER) 100 #include <sys/PROF.h> 101 #endif 102 103 /* the clocks run at NTSC: 715.909kHz or PAL: 709.379kHz. 104 We're using a 100 Hz clock. */ 105 int amiga_clk_interval; 106 int eclockfreq; 107 unsigned int fast_delay_limit; 108 struct CIA *clockcia; 109 110 static u_int clk_getcounter(struct timecounter *); 111 112 static struct timecounter clk_timecounter = { 113 clk_getcounter, /* get_timecount */ 114 0, /* no poll_pps */ 115 ~0u, /* counter_mask */ 116 0, /* frequency */ 117 "clock", /* name, overriden later */ 118 100, /* quality */ 119 NULL, /* prev */ 120 NULL, /* next */ 121 }; 122 123 /* 124 * Machine-dependent clock routines. 125 * 126 * Startrtclock restarts the real-time clock, which provides 127 * hardclock interrupts to kern_clock.c. 128 * 129 * Inittodr initializes the time of day hardware which provides 130 * date functions. 131 * 132 * Resettodr restores the time of day hardware after a time change. 133 * 134 * A note on the real-time clock: 135 * We actually load the clock with amiga_clk_interval-1 instead of amiga_clk_interval. 136 * This is because the counter decrements to zero after N+1 enabled clock 137 * periods where N is the value loaded into the counter. 138 */ 139 140 int clockmatch(struct device *, struct cfdata *, void *); 141 void clockattach(struct device *, struct device *, void *); 142 void cpu_initclocks(void); 143 144 CFATTACH_DECL(clock, sizeof(struct device), 145 clockmatch, clockattach, NULL, NULL); 146 147 int 148 clockmatch(struct device *pdp, struct cfdata *cfp, void *auxp) 149 { 150 if (matchname("clock", auxp)) 151 return(1); 152 return(0); 153 } 154 155 /* 156 * Start the real-time clock. 157 */ 158 void 159 clockattach(struct device *pdp, struct device *dp, void *auxp) 160 { 161 const char *clockchip; 162 unsigned short interval; 163 #ifdef DRACO 164 u_char dracorev; 165 #endif 166 167 #ifdef DRACO 168 dracorev = is_draco(); 169 #endif 170 171 if (eclockfreq == 0) 172 eclockfreq = 715909; /* guess NTSC */ 173 174 #ifdef DRACO 175 if (dracorev >= 4) { 176 if (amiga_clk_interval == 0) /* Only do this 1st time */ 177 eclockfreq /= 7; 178 clockchip = "QuickLogic"; 179 } else if (dracorev) { 180 clockcia = (struct CIA *)CIAAbase; 181 clockchip = "CIA A"; 182 } else 183 #endif 184 { 185 clockcia = (struct CIA *)CIABbase; 186 clockchip = "CIA B"; 187 } 188 189 amiga_clk_interval = (eclockfreq / hz); 190 191 clk_timecounter.tc_name = clockchip; 192 clk_timecounter.tc_frequency = eclockfreq; 193 194 fast_delay_limit = UINT_MAX / amiga_clk_interval; 195 196 if (dp != NULL) { /* real autoconfig? */ 197 printf(": %s system hz %d hardware hz %d\n", clockchip, hz, 198 eclockfreq); 199 tc_init(&clk_timecounter); 200 } 201 202 #ifdef DRACO 203 if (dracorev >= 4) { 204 /* 205 * can't preload anything beforehand, timer is free_running; 206 * but need this for delay calibration. 207 */ 208 209 draco_ioct->io_timerlo = amiga_clk_interval & 0xff; 210 draco_ioct->io_timerhi = amiga_clk_interval >> 8; 211 212 return; 213 } 214 #endif 215 /* 216 * stop timer A 217 */ 218 clockcia->cra = clockcia->cra & 0xc0; 219 clockcia->icr = 1 << 0; /* disable timer A interrupt */ 220 interval = clockcia->icr; /* and make sure it's clear */ 221 222 /* 223 * load interval into registers. 224 * the clocks run at NTSC: 715.909kHz or PAL: 709.379kHz 225 * supprort for PAL WHEN?!?! XXX 226 */ 227 interval = amiga_clk_interval - 1; 228 229 /* 230 * order of setting is important ! 231 */ 232 clockcia->talo = interval & 0xff; 233 clockcia->tahi = interval >> 8; 234 /* 235 * start timer A in continuous mode 236 */ 237 clockcia->cra = (clockcia->cra & 0xc0) | 1; 238 } 239 240 void 241 cpu_initclocks(void) 242 { 243 #ifdef DRACO 244 unsigned char dracorev; 245 dracorev = is_draco(); 246 if (dracorev >= 4) { 247 draco_ioct->io_timerlo = amiga_clk_interval & 0xFF; 248 draco_ioct->io_timerhi = amiga_clk_interval >> 8; 249 draco_ioct->io_timerrst = 0; /* any value resets */ 250 single_inst_bset_b(draco_ioct->io_status2, DRSTAT2_TMRINTENA); 251 252 return; 253 } 254 #endif 255 /* 256 * enable interrupts for timer A 257 */ 258 clockcia->icr = (1<<7) | (1<<0); 259 260 /* 261 * start timer A in continuous shot mode 262 */ 263 clockcia->cra = (clockcia->cra & 0xc0) | 1; 264 265 /* 266 * and globally enable interrupts for ciab 267 */ 268 #ifdef DRACO 269 if (dracorev) /* we use cia a on DraCo */ 270 single_inst_bset_b(*draco_intena, DRIRQ_INT2); 271 else 272 #endif 273 custom.intena = INTF_SETCLR | INTF_EXTER; 274 275 } 276 277 void 278 setstatclockrate(int hertz) 279 { 280 } 281 282 /* 283 * Returns ticks since last recorded clock "tick" 284 * (i.e. clock interrupt). 285 */ 286 static u_int 287 clk_gettick(void) 288 { 289 u_int interval; 290 u_char hi, hi2, lo; 291 292 #ifdef DRACO 293 if (is_draco() >= 4) { 294 hi2 = draco_ioct->io_chiprev; /* latch timer */ 295 hi = draco_ioct->io_timerhi; 296 lo = draco_ioct->io_timerlo; 297 interval = ((hi<<8) | lo); 298 if (interval > amiga_clk_interval) /* timer underflow */ 299 interval = 65536 + amiga_clk_interval - interval; 300 else 301 interval = amiga_clk_interval - interval; 302 303 } else 304 #endif 305 { 306 hi = clockcia->tahi; 307 lo = clockcia->talo; 308 hi2 = clockcia->tahi; 309 if (hi != hi2) { 310 lo = clockcia->talo; 311 hi = hi2; 312 } 313 314 interval = (amiga_clk_interval - 1) - ((hi<<8) | lo); 315 316 /* 317 * should read ICR and if there's an int pending, adjust 318 * interval. However, since reading ICR clears the interrupt, 319 * we'd lose a hardclock int, and this is not tolerable. 320 */ 321 } 322 323 return interval; 324 } 325 326 static u_int 327 clk_getcounter(struct timecounter *tc) 328 { 329 int old_hardclock_ticks; 330 u_int clock_tick; 331 332 do { 333 old_hardclock_ticks = hardclock_ticks; 334 clock_tick = clk_gettick(); 335 } while (old_hardclock_ticks != hardclock_ticks); 336 337 return old_hardclock_ticks * amiga_clk_interval + clock_tick; 338 } 339 340 #if notyet 341 342 /* implement this later. I'd suggest using both timers in CIA-A, they're 343 not yet used. */ 344 345 #include "clock.h" 346 #if NCLOCK > 0 347 /* 348 * /dev/clock: mappable high resolution timer. 349 * 350 * This code implements a 32-bit recycling counter (with a 4 usec period) 351 * using timers 2 & 3 on the 6840 clock chip. The counter can be mapped 352 * RO into a user's address space to achieve low overhead (no system calls), 353 * high-precision timing. 354 * 355 * Note that timer 3 is also used for the high precision profiling timer 356 * (PROFTIMER code above). Care should be taken when both uses are 357 * configured as only a token effort is made to avoid conflicting use. 358 */ 359 #include <sys/proc.h> 360 #include <sys/resourcevar.h> 361 #include <sys/ioctl.h> 362 #include <sys/malloc.h> 363 #include <uvm/uvm_extern.h> 364 #include <amiga/amiga/clockioctl.h> 365 #include <sys/specdev.h> 366 #include <sys/vnode.h> 367 #include <sys/mman.h> 368 369 int clockon = 0; /* non-zero if high-res timer enabled */ 370 #ifdef PROFTIMER 371 int profprocs = 0; /* # of procs using profiling timer */ 372 #endif 373 #ifdef DEBUG 374 int clockdebug = 0; 375 #endif 376 377 /*ARGSUSED*/ 378 int 379 clockopen(dev_t dev, int flags) 380 { 381 #ifdef PROFTIMER 382 #ifdef PROF 383 /* 384 * Kernel profiling enabled, give up. 385 */ 386 if (profiling) 387 return(EBUSY); 388 #endif 389 /* 390 * If any user processes are profiling, give up. 391 */ 392 if (profprocs) 393 return(EBUSY); 394 #endif 395 if (!clockon) { 396 startclock(); 397 clockon++; 398 } 399 return(0); 400 } 401 402 /*ARGSUSED*/ 403 int 404 clockclose(dev_t dev, int flags) 405 { 406 (void) clockunmmap(dev, (void *)0, curproc); /* XXX */ 407 stopclock(); 408 clockon = 0; 409 return(0); 410 } 411 412 /*ARGSUSED*/ 413 int 414 clockioctl(dev_t dev, u_long cmd, void *data, int flag, struct proc *p) 415 { 416 int error = 0; 417 418 switch (cmd) { 419 420 case CLOCKMAP: 421 error = clockmmap(dev, (void **)data, p); 422 break; 423 424 case CLOCKUNMAP: 425 error = clockunmmap(dev, *(void **)data, p); 426 break; 427 428 case CLOCKGETRES: 429 *(int *)data = CLK_RESOLUTION; 430 break; 431 432 default: 433 error = EINVAL; 434 break; 435 } 436 return(error); 437 } 438 439 /*ARGSUSED*/ 440 void 441 clockmap(dev_t dev, int off, int prot) 442 { 443 return((off + (INTIOBASE+CLKBASE+CLKSR-1)) >> PGSHIFT); 444 } 445 446 int 447 clockmmap(dev_t dev, void **addrp, struct proc *p) 448 { 449 int error; 450 struct vnode vn; 451 struct specinfo si; 452 int flags; 453 454 flags = MAP_FILE|MAP_SHARED; 455 if (*addrp) 456 flags |= MAP_FIXED; 457 else 458 *addrp = (void *)0x1000000; /* XXX */ 459 vn.v_type = VCHR; /* XXX */ 460 vn.v_specinfo = &si; /* XXX */ 461 vn.v_rdev = dev; /* XXX */ 462 error = vm_mmap(&p->p_vmspace->vm_map, (vm_offset_t *)addrp, 463 PAGE_SIZE, VM_PROT_ALL, flags, (void *)&vn, 0); 464 return(error); 465 } 466 467 int 468 clockunmmap(dev_t dev, void *addr, struct proc *p) 469 { 470 int rv; 471 472 if (addr == 0) 473 return(EINVAL); /* XXX: how do we deal with this? */ 474 uvm_deallocate(p->p_vmspace->vm_map, (vm_offset_t)addr, PAGE_SIZE); 475 return 0; 476 } 477 478 void 479 startclock(void) 480 { 481 register struct clkreg *clk = (struct clkreg *)clkstd[0]; 482 483 clk->clk_msb2 = -1; clk->clk_lsb2 = -1; 484 clk->clk_msb3 = -1; clk->clk_lsb3 = -1; 485 486 clk->clk_cr2 = CLK_CR3; 487 clk->clk_cr3 = CLK_OENAB|CLK_8BIT; 488 clk->clk_cr2 = CLK_CR1; 489 clk->clk_cr1 = CLK_IENAB; 490 } 491 492 void 493 stopclock(void) 494 { 495 register struct clkreg *clk = (struct clkreg *)clkstd[0]; 496 497 clk->clk_cr2 = CLK_CR3; 498 clk->clk_cr3 = 0; 499 clk->clk_cr2 = CLK_CR1; 500 clk->clk_cr1 = CLK_IENAB; 501 } 502 #endif 503 504 #endif 505 506 507 #ifdef PROFTIMER 508 /* 509 * This code allows the amiga kernel to use one of the extra timers on 510 * the clock chip for profiling, instead of the regular system timer. 511 * The advantage of this is that the profiling timer can be turned up to 512 * a higher interrupt rate, giving finer resolution timing. The profclock 513 * routine is called from the lev6intr in locore, and is a specialized 514 * routine that calls addupc. The overhead then is far less than if 515 * hardclock/softclock was called. Further, the context switch code in 516 * locore has been changed to turn the profile clock on/off when switching 517 * into/out of a process that is profiling (startprofclock/stopprofclock). 518 * This reduces the impact of the profiling clock on other users, and might 519 * possibly increase the accuracy of the profiling. 520 */ 521 int profint = PRF_INTERVAL; /* Clock ticks between interrupts */ 522 int profscale = 0; /* Scale factor from sys clock to prof clock */ 523 char profon = 0; /* Is profiling clock on? */ 524 525 /* profon values - do not change, locore.s assumes these values */ 526 #define PRF_NONE 0x00 527 #define PRF_USER 0x01 528 #define PRF_KERNEL 0x80 529 530 void 531 initprofclock(void) 532 { 533 #if NCLOCK > 0 534 struct proc *p = curproc; /* XXX */ 535 536 /* 537 * If the high-res timer is running, force profiling off. 538 * Unfortunately, this gets reflected back to the user not as 539 * an error but as a lack of results. 540 */ 541 if (clockon) { 542 p->p_stats->p_prof.pr_scale = 0; 543 return; 544 } 545 /* 546 * Keep track of the number of user processes that are profiling 547 * by checking the scale value. 548 * 549 * XXX: this all assumes that the profiling code is well behaved; 550 * i.e. profil() is called once per process with pcscale non-zero 551 * to turn it on, and once with pcscale zero to turn it off. 552 * Also assumes you don't do any forks or execs. Oh well, there 553 * is always adb... 554 */ 555 if (p->p_stats->p_prof.pr_scale) 556 profprocs++; 557 else 558 profprocs--; 559 #endif 560 /* 561 * The profile interrupt interval must be an even divisor 562 * of the amiga_clk_interval so that scaling from a system clock 563 * tick to a profile clock tick is possible using integer math. 564 */ 565 if (profint > amiga_clk_interval || (amiga_clk_interval % profint) != 0) 566 profint = amiga_clk_interval; 567 profscale = amiga_clk_interval / profint; 568 } 569 570 void 571 startprofclock(void) 572 { 573 unsigned short interval; 574 575 /* stop timer B */ 576 clockcia->crb = clockcia->crb & 0xc0; 577 578 /* load interval into registers. 579 the clocks run at NTSC: 715.909kHz or PAL: 709.379kHz */ 580 581 interval = profint - 1; 582 583 /* order of setting is important ! */ 584 clockcia->tblo = interval & 0xff; 585 clockcia->tbhi = interval >> 8; 586 587 /* enable interrupts for timer B */ 588 clockcia->icr = (1<<7) | (1<<1); 589 590 /* start timer B in continuous shot mode */ 591 clockcia->crb = (clockcia->crb & 0xc0) | 1; 592 } 593 594 void 595 stopprofclock(void) 596 { 597 /* stop timer B */ 598 clockcia->crb = clockcia->crb & 0xc0; 599 } 600 601 #ifdef PROF 602 /* 603 * profclock() is expanded in line in lev6intr() unless profiling kernel. 604 * Assumes it is called with clock interrupts blocked. 605 */ 606 void 607 profclock(void *pc, int ps) 608 { 609 /* 610 * Came from user mode. 611 * If this process is being profiled record the tick. 612 */ 613 if (USERMODE(ps)) { 614 if (p->p_stats.p_prof.pr_scale) 615 addupc(pc, &curproc->p_stats.p_prof, 1); 616 } 617 /* 618 * Came from kernel (supervisor) mode. 619 * If we are profiling the kernel, record the tick. 620 */ 621 else if (profiling < 2) { 622 register int s = pc - s_lowpc; 623 624 if (s < s_textsize) 625 kcount[s / (HISTFRACTION * sizeof (*kcount))]++; 626 } 627 /* 628 * Kernel profiling was on but has been disabled. 629 * Mark as no longer profiling kernel and if all profiling done, 630 * disable the clock. 631 */ 632 if (profiling && (profon & PRF_KERNEL)) { 633 profon &= ~PRF_KERNEL; 634 if (profon == PRF_NONE) 635 stopprofclock(); 636 } 637 } 638 #endif 639 #endif 640 641 void 642 delay(unsigned int n) 643 { 644 unsigned int cur_tick, initial_tick; 645 int remaining; 646 647 /* 648 * Read the counter first, so that the rest of the setup overhead is 649 * counted. 650 */ 651 initial_tick = clk_gettick(); 652 653 if (amiga_clk_interval == 0) { 654 /* 655 * Clock is not initialised yet, 656 * so just do some ad-hoc loop. 657 */ 658 static uint32_t dummy; 659 660 n *= 4; 661 while (n--) 662 dummy *= eclockfreq; 663 return; 664 } 665 666 if (n <= fast_delay_limit) { 667 /* 668 * For unsigned arithmetic, division can be replaced with 669 * multiplication with the inverse and a shift. 670 */ 671 remaining = n * eclockfreq / 1000000; 672 } else { 673 /* This is a very long delay. 674 * Being slow here doesn't matter. 675 */ 676 remaining = (unsigned long long) n * eclockfreq / 1000000; 677 } 678 679 while (remaining > 0) { 680 cur_tick = clk_gettick(); 681 if (cur_tick > initial_tick) 682 remaining -= amiga_clk_interval - (cur_tick - initial_tick); 683 else 684 remaining -= initial_tick - cur_tick; 685 initial_tick = cur_tick; 686 } 687 } 688