1 /* $NetBSD: clock.c,v 1.48 2008/12/07 03:48:43 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.48 2008/12/07 03:48:43 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 0x0fffu, /* 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 u_int counter_mask; 163 unsigned short interval; 164 #ifdef DRACO 165 u_char dracorev; 166 #endif 167 168 #ifdef DRACO 169 dracorev = is_draco(); 170 #endif 171 172 if (eclockfreq == 0) 173 eclockfreq = 715909; /* guess NTSC */ 174 175 #ifdef DRACO 176 if (dracorev >= 4) { 177 if (amiga_clk_interval == 0) /* Only do this 1st time */ 178 eclockfreq /= 7; 179 clockchip = "QuickLogic"; 180 } else if (dracorev) { 181 clockcia = (struct CIA *)CIAAbase; 182 clockchip = "CIA A"; 183 } else 184 #endif 185 { 186 clockcia = (struct CIA *)CIABbase; 187 clockchip = "CIA B"; 188 } 189 190 amiga_clk_interval = (eclockfreq / hz); 191 192 counter_mask = 0x8000; 193 while (counter_mask != 0 && (counter_mask & amiga_clk_interval) == 0) 194 counter_mask >>= 1; 195 counter_mask -= 1; 196 197 clk_timecounter.tc_name = clockchip; 198 clk_timecounter.tc_frequency = eclockfreq; 199 clk_timecounter.tc_counter_mask = counter_mask; 200 201 fast_delay_limit = UINT_MAX / amiga_clk_interval; 202 203 if (dp != NULL) { /* real autoconfig? */ 204 printf(": %s system hz %d hardware hz %d\n", clockchip, hz, 205 eclockfreq); 206 tc_init(&clk_timecounter); 207 } 208 209 #ifdef DRACO 210 if (dracorev >= 4) { 211 /* 212 * can't preload anything beforehand, timer is free_running; 213 * but need this for delay calibration. 214 */ 215 216 draco_ioct->io_timerlo = amiga_clk_interval & 0xff; 217 draco_ioct->io_timerhi = amiga_clk_interval >> 8; 218 219 return; 220 } 221 #endif 222 /* 223 * stop timer A 224 */ 225 clockcia->cra = clockcia->cra & 0xc0; 226 clockcia->icr = 1 << 0; /* disable timer A interrupt */ 227 interval = clockcia->icr; /* and make sure it's clear */ 228 229 /* 230 * load interval into registers. 231 * the clocks run at NTSC: 715.909kHz or PAL: 709.379kHz 232 * supprort for PAL WHEN?!?! XXX 233 */ 234 interval = amiga_clk_interval - 1; 235 236 /* 237 * order of setting is important ! 238 */ 239 clockcia->talo = interval & 0xff; 240 clockcia->tahi = interval >> 8; 241 /* 242 * start timer A in continuous mode 243 */ 244 clockcia->cra = (clockcia->cra & 0xc0) | 1; 245 } 246 247 void 248 cpu_initclocks(void) 249 { 250 #ifdef DRACO 251 unsigned char dracorev; 252 dracorev = is_draco(); 253 if (dracorev >= 4) { 254 draco_ioct->io_timerlo = amiga_clk_interval & 0xFF; 255 draco_ioct->io_timerhi = amiga_clk_interval >> 8; 256 draco_ioct->io_timerrst = 0; /* any value resets */ 257 single_inst_bset_b(draco_ioct->io_status2, DRSTAT2_TMRINTENA); 258 259 return; 260 } 261 #endif 262 /* 263 * enable interrupts for timer A 264 */ 265 clockcia->icr = (1<<7) | (1<<0); 266 267 /* 268 * start timer A in continuous shot mode 269 */ 270 clockcia->cra = (clockcia->cra & 0xc0) | 1; 271 272 /* 273 * and globally enable interrupts for ciab 274 */ 275 #ifdef DRACO 276 if (dracorev) /* we use cia a on DraCo */ 277 single_inst_bset_b(*draco_intena, DRIRQ_INT2); 278 else 279 #endif 280 custom.intena = INTF_SETCLR | INTF_EXTER; 281 282 } 283 284 void 285 setstatclockrate(int hertz) 286 { 287 } 288 289 /* 290 * Returns ticks since last recorded clock "tick" 291 * (i.e. clock interrupt). 292 */ 293 static u_int 294 clk_gettick(void) 295 { 296 u_int interval; 297 u_char hi, hi2, lo; 298 299 #ifdef DRACO 300 if (is_draco() >= 4) { 301 hi2 = draco_ioct->io_chiprev; /* latch timer */ 302 hi = draco_ioct->io_timerhi; 303 lo = draco_ioct->io_timerlo; 304 interval = ((hi<<8) | lo); 305 if (interval > amiga_clk_interval) /* timer underflow */ 306 interval = 65536 + amiga_clk_interval - interval; 307 else 308 interval = amiga_clk_interval - interval; 309 310 } else 311 #endif 312 { 313 hi = clockcia->tahi; 314 lo = clockcia->talo; 315 hi2 = clockcia->tahi; 316 if (hi != hi2) { 317 lo = clockcia->talo; 318 hi = hi2; 319 } 320 321 interval = (amiga_clk_interval - 1) - ((hi<<8) | lo); 322 323 /* 324 * should read ICR and if there's an int pending, adjust 325 * interval. However, since reading ICR clears the interrupt, 326 * we'd lose a hardclock int, and this is not tolerable. 327 */ 328 } 329 330 return interval; 331 } 332 333 static u_int 334 clk_getcounter(struct timecounter *tc) 335 { 336 int old_hardclock_ticks; 337 u_int clock_tick; 338 339 do { 340 old_hardclock_ticks = hardclock_ticks; 341 clock_tick = clk_gettick(); 342 } while (old_hardclock_ticks != hardclock_ticks); 343 344 return old_hardclock_ticks * amiga_clk_interval + clock_tick; 345 } 346 347 #if notyet 348 349 /* implement this later. I'd suggest using both timers in CIA-A, they're 350 not yet used. */ 351 352 #include "clock.h" 353 #if NCLOCK > 0 354 /* 355 * /dev/clock: mappable high resolution timer. 356 * 357 * This code implements a 32-bit recycling counter (with a 4 usec period) 358 * using timers 2 & 3 on the 6840 clock chip. The counter can be mapped 359 * RO into a user's address space to achieve low overhead (no system calls), 360 * high-precision timing. 361 * 362 * Note that timer 3 is also used for the high precision profiling timer 363 * (PROFTIMER code above). Care should be taken when both uses are 364 * configured as only a token effort is made to avoid conflicting use. 365 */ 366 #include <sys/proc.h> 367 #include <sys/resourcevar.h> 368 #include <sys/ioctl.h> 369 #include <sys/malloc.h> 370 #include <uvm/uvm_extern.h> 371 #include <amiga/amiga/clockioctl.h> 372 #include <sys/specdev.h> 373 #include <sys/vnode.h> 374 #include <sys/mman.h> 375 376 int clockon = 0; /* non-zero if high-res timer enabled */ 377 #ifdef PROFTIMER 378 int profprocs = 0; /* # of procs using profiling timer */ 379 #endif 380 #ifdef DEBUG 381 int clockdebug = 0; 382 #endif 383 384 /*ARGSUSED*/ 385 int 386 clockopen(dev_t dev, int flags) 387 { 388 #ifdef PROFTIMER 389 #ifdef PROF 390 /* 391 * Kernel profiling enabled, give up. 392 */ 393 if (profiling) 394 return(EBUSY); 395 #endif 396 /* 397 * If any user processes are profiling, give up. 398 */ 399 if (profprocs) 400 return(EBUSY); 401 #endif 402 if (!clockon) { 403 startclock(); 404 clockon++; 405 } 406 return(0); 407 } 408 409 /*ARGSUSED*/ 410 int 411 clockclose(dev_t dev, int flags) 412 { 413 (void) clockunmmap(dev, (void *)0, curproc); /* XXX */ 414 stopclock(); 415 clockon = 0; 416 return(0); 417 } 418 419 /*ARGSUSED*/ 420 int 421 clockioctl(dev_t dev, u_long cmd, void *data, int flag, struct proc *p) 422 { 423 int error = 0; 424 425 switch (cmd) { 426 427 case CLOCKMAP: 428 error = clockmmap(dev, (void **)data, p); 429 break; 430 431 case CLOCKUNMAP: 432 error = clockunmmap(dev, *(void **)data, p); 433 break; 434 435 case CLOCKGETRES: 436 *(int *)data = CLK_RESOLUTION; 437 break; 438 439 default: 440 error = EINVAL; 441 break; 442 } 443 return(error); 444 } 445 446 /*ARGSUSED*/ 447 void 448 clockmap(dev_t dev, int off, int prot) 449 { 450 return((off + (INTIOBASE+CLKBASE+CLKSR-1)) >> PGSHIFT); 451 } 452 453 int 454 clockmmap(dev_t dev, void **addrp, struct proc *p) 455 { 456 int error; 457 struct vnode vn; 458 struct specinfo si; 459 int flags; 460 461 flags = MAP_FILE|MAP_SHARED; 462 if (*addrp) 463 flags |= MAP_FIXED; 464 else 465 *addrp = (void *)0x1000000; /* XXX */ 466 vn.v_type = VCHR; /* XXX */ 467 vn.v_specinfo = &si; /* XXX */ 468 vn.v_rdev = dev; /* XXX */ 469 error = vm_mmap(&p->p_vmspace->vm_map, (vm_offset_t *)addrp, 470 PAGE_SIZE, VM_PROT_ALL, flags, (void *)&vn, 0); 471 return(error); 472 } 473 474 int 475 clockunmmap(dev_t dev, void *addr, struct proc *p) 476 { 477 int rv; 478 479 if (addr == 0) 480 return(EINVAL); /* XXX: how do we deal with this? */ 481 uvm_deallocate(p->p_vmspace->vm_map, (vm_offset_t)addr, PAGE_SIZE); 482 return 0; 483 } 484 485 void 486 startclock(void) 487 { 488 register struct clkreg *clk = (struct clkreg *)clkstd[0]; 489 490 clk->clk_msb2 = -1; clk->clk_lsb2 = -1; 491 clk->clk_msb3 = -1; clk->clk_lsb3 = -1; 492 493 clk->clk_cr2 = CLK_CR3; 494 clk->clk_cr3 = CLK_OENAB|CLK_8BIT; 495 clk->clk_cr2 = CLK_CR1; 496 clk->clk_cr1 = CLK_IENAB; 497 } 498 499 void 500 stopclock(void) 501 { 502 register struct clkreg *clk = (struct clkreg *)clkstd[0]; 503 504 clk->clk_cr2 = CLK_CR3; 505 clk->clk_cr3 = 0; 506 clk->clk_cr2 = CLK_CR1; 507 clk->clk_cr1 = CLK_IENAB; 508 } 509 #endif 510 511 #endif 512 513 514 #ifdef PROFTIMER 515 /* 516 * This code allows the amiga kernel to use one of the extra timers on 517 * the clock chip for profiling, instead of the regular system timer. 518 * The advantage of this is that the profiling timer can be turned up to 519 * a higher interrupt rate, giving finer resolution timing. The profclock 520 * routine is called from the lev6intr in locore, and is a specialized 521 * routine that calls addupc. The overhead then is far less than if 522 * hardclock/softclock was called. Further, the context switch code in 523 * locore has been changed to turn the profile clock on/off when switching 524 * into/out of a process that is profiling (startprofclock/stopprofclock). 525 * This reduces the impact of the profiling clock on other users, and might 526 * possibly increase the accuracy of the profiling. 527 */ 528 int profint = PRF_INTERVAL; /* Clock ticks between interrupts */ 529 int profscale = 0; /* Scale factor from sys clock to prof clock */ 530 char profon = 0; /* Is profiling clock on? */ 531 532 /* profon values - do not change, locore.s assumes these values */ 533 #define PRF_NONE 0x00 534 #define PRF_USER 0x01 535 #define PRF_KERNEL 0x80 536 537 void 538 initprofclock(void) 539 { 540 #if NCLOCK > 0 541 struct proc *p = curproc; /* XXX */ 542 543 /* 544 * If the high-res timer is running, force profiling off. 545 * Unfortunately, this gets reflected back to the user not as 546 * an error but as a lack of results. 547 */ 548 if (clockon) { 549 p->p_stats->p_prof.pr_scale = 0; 550 return; 551 } 552 /* 553 * Keep track of the number of user processes that are profiling 554 * by checking the scale value. 555 * 556 * XXX: this all assumes that the profiling code is well behaved; 557 * i.e. profil() is called once per process with pcscale non-zero 558 * to turn it on, and once with pcscale zero to turn it off. 559 * Also assumes you don't do any forks or execs. Oh well, there 560 * is always adb... 561 */ 562 if (p->p_stats->p_prof.pr_scale) 563 profprocs++; 564 else 565 profprocs--; 566 #endif 567 /* 568 * The profile interrupt interval must be an even divisor 569 * of the amiga_clk_interval so that scaling from a system clock 570 * tick to a profile clock tick is possible using integer math. 571 */ 572 if (profint > amiga_clk_interval || (amiga_clk_interval % profint) != 0) 573 profint = amiga_clk_interval; 574 profscale = amiga_clk_interval / profint; 575 } 576 577 void 578 startprofclock(void) 579 { 580 unsigned short interval; 581 582 /* stop timer B */ 583 clockcia->crb = clockcia->crb & 0xc0; 584 585 /* load interval into registers. 586 the clocks run at NTSC: 715.909kHz or PAL: 709.379kHz */ 587 588 interval = profint - 1; 589 590 /* order of setting is important ! */ 591 clockcia->tblo = interval & 0xff; 592 clockcia->tbhi = interval >> 8; 593 594 /* enable interrupts for timer B */ 595 clockcia->icr = (1<<7) | (1<<1); 596 597 /* start timer B in continuous shot mode */ 598 clockcia->crb = (clockcia->crb & 0xc0) | 1; 599 } 600 601 void 602 stopprofclock(void) 603 { 604 /* stop timer B */ 605 clockcia->crb = clockcia->crb & 0xc0; 606 } 607 608 #ifdef PROF 609 /* 610 * profclock() is expanded in line in lev6intr() unless profiling kernel. 611 * Assumes it is called with clock interrupts blocked. 612 */ 613 void 614 profclock(void *pc, int ps) 615 { 616 /* 617 * Came from user mode. 618 * If this process is being profiled record the tick. 619 */ 620 if (USERMODE(ps)) { 621 if (p->p_stats.p_prof.pr_scale) 622 addupc(pc, &curproc->p_stats.p_prof, 1); 623 } 624 /* 625 * Came from kernel (supervisor) mode. 626 * If we are profiling the kernel, record the tick. 627 */ 628 else if (profiling < 2) { 629 register int s = pc - s_lowpc; 630 631 if (s < s_textsize) 632 kcount[s / (HISTFRACTION * sizeof (*kcount))]++; 633 } 634 /* 635 * Kernel profiling was on but has been disabled. 636 * Mark as no longer profiling kernel and if all profiling done, 637 * disable the clock. 638 */ 639 if (profiling && (profon & PRF_KERNEL)) { 640 profon &= ~PRF_KERNEL; 641 if (profon == PRF_NONE) 642 stopprofclock(); 643 } 644 } 645 #endif 646 #endif 647 648 void 649 delay(unsigned int n) 650 { 651 unsigned int cur_tick, initial_tick; 652 int remaining; 653 654 /* 655 * Read the counter first, so that the rest of the setup overhead is 656 * counted. 657 */ 658 initial_tick = clk_gettick(); 659 660 if (amiga_clk_interval == 0) { 661 /* 662 * Clock is not initialised yet, 663 * so just do some ad-hoc loop. 664 */ 665 static uint32_t dummy; 666 667 n *= 4; 668 while (n--) 669 dummy *= eclockfreq; 670 return; 671 } 672 673 if (n <= fast_delay_limit) { 674 /* 675 * For unsigned arithmetic, division can be replaced with 676 * multiplication with the inverse and a shift. 677 */ 678 remaining = n * eclockfreq / 1000000; 679 } else { 680 /* This is a very long delay. 681 * Being slow here doesn't matter. 682 */ 683 remaining = (unsigned long long) n * eclockfreq / 1000000; 684 } 685 686 while (remaining > 0) { 687 cur_tick = clk_gettick(); 688 if (cur_tick > initial_tick) 689 remaining -= amiga_clk_interval - (cur_tick - initial_tick); 690 else 691 remaining -= initial_tick - cur_tick; 692 initial_tick = cur_tick; 693 } 694 } 695