1 /* $NetBSD: clock.c,v 1.50 2009/09/11 19:43:08 phx 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.50 2009/09/11 19:43:08 phx 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 static int last_hardclock_ticks; 330 static u_int last_clock_tick = 0; 331 int old_hardclock_ticks; 332 u_int clock_tick; 333 334 do { 335 old_hardclock_ticks = hardclock_ticks; 336 clock_tick = clk_gettick(); 337 } while (old_hardclock_ticks != hardclock_ticks); 338 339 /* 340 * Handle the situation of a wrapped interval counter, while 341 * the hardclock() interrupt was not yet executed to update 342 * hardclock_ticks. 343 */ 344 if (last_hardclock_ticks > old_hardclock_ticks) 345 old_hardclock_ticks = last_hardclock_ticks; 346 if (clock_tick < last_clock_tick && 347 old_hardclock_ticks == last_hardclock_ticks) 348 old_hardclock_ticks++; 349 last_hardclock_ticks = old_hardclock_ticks; 350 last_clock_tick = clock_tick; 351 352 return old_hardclock_ticks * amiga_clk_interval + clock_tick; 353 } 354 355 #if notyet 356 357 /* implement this later. I'd suggest using both timers in CIA-A, they're 358 not yet used. */ 359 360 #include "clock.h" 361 #if NCLOCK > 0 362 /* 363 * /dev/clock: mappable high resolution timer. 364 * 365 * This code implements a 32-bit recycling counter (with a 4 usec period) 366 * using timers 2 & 3 on the 6840 clock chip. The counter can be mapped 367 * RO into a user's address space to achieve low overhead (no system calls), 368 * high-precision timing. 369 * 370 * Note that timer 3 is also used for the high precision profiling timer 371 * (PROFTIMER code above). Care should be taken when both uses are 372 * configured as only a token effort is made to avoid conflicting use. 373 */ 374 #include <sys/proc.h> 375 #include <sys/resourcevar.h> 376 #include <sys/ioctl.h> 377 #include <sys/malloc.h> 378 #include <uvm/uvm_extern.h> 379 #include <amiga/amiga/clockioctl.h> 380 #include <sys/specdev.h> 381 #include <sys/vnode.h> 382 #include <sys/mman.h> 383 384 int clockon = 0; /* non-zero if high-res timer enabled */ 385 #ifdef PROFTIMER 386 int profprocs = 0; /* # of procs using profiling timer */ 387 #endif 388 #ifdef DEBUG 389 int clockdebug = 0; 390 #endif 391 392 /*ARGSUSED*/ 393 int 394 clockopen(dev_t dev, int flags) 395 { 396 #ifdef PROFTIMER 397 #ifdef PROF 398 /* 399 * Kernel profiling enabled, give up. 400 */ 401 if (profiling) 402 return(EBUSY); 403 #endif 404 /* 405 * If any user processes are profiling, give up. 406 */ 407 if (profprocs) 408 return(EBUSY); 409 #endif 410 if (!clockon) { 411 startclock(); 412 clockon++; 413 } 414 return(0); 415 } 416 417 /*ARGSUSED*/ 418 int 419 clockclose(dev_t dev, int flags) 420 { 421 (void) clockunmmap(dev, (void *)0, curproc); /* XXX */ 422 stopclock(); 423 clockon = 0; 424 return(0); 425 } 426 427 /*ARGSUSED*/ 428 int 429 clockioctl(dev_t dev, u_long cmd, void *data, int flag, struct proc *p) 430 { 431 int error = 0; 432 433 switch (cmd) { 434 435 case CLOCKMAP: 436 error = clockmmap(dev, (void **)data, p); 437 break; 438 439 case CLOCKUNMAP: 440 error = clockunmmap(dev, *(void **)data, p); 441 break; 442 443 case CLOCKGETRES: 444 *(int *)data = CLK_RESOLUTION; 445 break; 446 447 default: 448 error = EINVAL; 449 break; 450 } 451 return(error); 452 } 453 454 /*ARGSUSED*/ 455 void 456 clockmap(dev_t dev, int off, int prot) 457 { 458 return((off + (INTIOBASE+CLKBASE+CLKSR-1)) >> PGSHIFT); 459 } 460 461 int 462 clockmmap(dev_t dev, void **addrp, struct proc *p) 463 { 464 int error; 465 struct vnode vn; 466 struct specinfo si; 467 int flags; 468 469 flags = MAP_FILE|MAP_SHARED; 470 if (*addrp) 471 flags |= MAP_FIXED; 472 else 473 *addrp = (void *)0x1000000; /* XXX */ 474 vn.v_type = VCHR; /* XXX */ 475 vn.v_specinfo = &si; /* XXX */ 476 vn.v_rdev = dev; /* XXX */ 477 error = vm_mmap(&p->p_vmspace->vm_map, (vm_offset_t *)addrp, 478 PAGE_SIZE, VM_PROT_ALL, flags, (void *)&vn, 0); 479 return(error); 480 } 481 482 int 483 clockunmmap(dev_t dev, void *addr, struct proc *p) 484 { 485 int rv; 486 487 if (addr == 0) 488 return(EINVAL); /* XXX: how do we deal with this? */ 489 uvm_deallocate(p->p_vmspace->vm_map, (vm_offset_t)addr, PAGE_SIZE); 490 return 0; 491 } 492 493 void 494 startclock(void) 495 { 496 register struct clkreg *clk = (struct clkreg *)clkstd[0]; 497 498 clk->clk_msb2 = -1; clk->clk_lsb2 = -1; 499 clk->clk_msb3 = -1; clk->clk_lsb3 = -1; 500 501 clk->clk_cr2 = CLK_CR3; 502 clk->clk_cr3 = CLK_OENAB|CLK_8BIT; 503 clk->clk_cr2 = CLK_CR1; 504 clk->clk_cr1 = CLK_IENAB; 505 } 506 507 void 508 stopclock(void) 509 { 510 register struct clkreg *clk = (struct clkreg *)clkstd[0]; 511 512 clk->clk_cr2 = CLK_CR3; 513 clk->clk_cr3 = 0; 514 clk->clk_cr2 = CLK_CR1; 515 clk->clk_cr1 = CLK_IENAB; 516 } 517 #endif 518 519 #endif 520 521 522 #ifdef PROFTIMER 523 /* 524 * This code allows the amiga kernel to use one of the extra timers on 525 * the clock chip for profiling, instead of the regular system timer. 526 * The advantage of this is that the profiling timer can be turned up to 527 * a higher interrupt rate, giving finer resolution timing. The profclock 528 * routine is called from the lev6intr in locore, and is a specialized 529 * routine that calls addupc. The overhead then is far less than if 530 * hardclock/softclock was called. Further, the context switch code in 531 * locore has been changed to turn the profile clock on/off when switching 532 * into/out of a process that is profiling (startprofclock/stopprofclock). 533 * This reduces the impact of the profiling clock on other users, and might 534 * possibly increase the accuracy of the profiling. 535 */ 536 int profint = PRF_INTERVAL; /* Clock ticks between interrupts */ 537 int profscale = 0; /* Scale factor from sys clock to prof clock */ 538 char profon = 0; /* Is profiling clock on? */ 539 540 /* profon values - do not change, locore.s assumes these values */ 541 #define PRF_NONE 0x00 542 #define PRF_USER 0x01 543 #define PRF_KERNEL 0x80 544 545 void 546 initprofclock(void) 547 { 548 #if NCLOCK > 0 549 struct proc *p = curproc; /* XXX */ 550 551 /* 552 * If the high-res timer is running, force profiling off. 553 * Unfortunately, this gets reflected back to the user not as 554 * an error but as a lack of results. 555 */ 556 if (clockon) { 557 p->p_stats->p_prof.pr_scale = 0; 558 return; 559 } 560 /* 561 * Keep track of the number of user processes that are profiling 562 * by checking the scale value. 563 * 564 * XXX: this all assumes that the profiling code is well behaved; 565 * i.e. profil() is called once per process with pcscale non-zero 566 * to turn it on, and once with pcscale zero to turn it off. 567 * Also assumes you don't do any forks or execs. Oh well, there 568 * is always adb... 569 */ 570 if (p->p_stats->p_prof.pr_scale) 571 profprocs++; 572 else 573 profprocs--; 574 #endif 575 /* 576 * The profile interrupt interval must be an even divisor 577 * of the amiga_clk_interval so that scaling from a system clock 578 * tick to a profile clock tick is possible using integer math. 579 */ 580 if (profint > amiga_clk_interval || (amiga_clk_interval % profint) != 0) 581 profint = amiga_clk_interval; 582 profscale = amiga_clk_interval / profint; 583 } 584 585 void 586 startprofclock(void) 587 { 588 unsigned short interval; 589 590 /* stop timer B */ 591 clockcia->crb = clockcia->crb & 0xc0; 592 593 /* load interval into registers. 594 the clocks run at NTSC: 715.909kHz or PAL: 709.379kHz */ 595 596 interval = profint - 1; 597 598 /* order of setting is important ! */ 599 clockcia->tblo = interval & 0xff; 600 clockcia->tbhi = interval >> 8; 601 602 /* enable interrupts for timer B */ 603 clockcia->icr = (1<<7) | (1<<1); 604 605 /* start timer B in continuous shot mode */ 606 clockcia->crb = (clockcia->crb & 0xc0) | 1; 607 } 608 609 void 610 stopprofclock(void) 611 { 612 /* stop timer B */ 613 clockcia->crb = clockcia->crb & 0xc0; 614 } 615 616 #ifdef PROF 617 /* 618 * profclock() is expanded in line in lev6intr() unless profiling kernel. 619 * Assumes it is called with clock interrupts blocked. 620 */ 621 void 622 profclock(void *pc, int ps) 623 { 624 /* 625 * Came from user mode. 626 * If this process is being profiled record the tick. 627 */ 628 if (USERMODE(ps)) { 629 if (p->p_stats.p_prof.pr_scale) 630 addupc(pc, &curproc->p_stats.p_prof, 1); 631 } 632 /* 633 * Came from kernel (supervisor) mode. 634 * If we are profiling the kernel, record the tick. 635 */ 636 else if (profiling < 2) { 637 register int s = pc - s_lowpc; 638 639 if (s < s_textsize) 640 kcount[s / (HISTFRACTION * sizeof (*kcount))]++; 641 } 642 /* 643 * Kernel profiling was on but has been disabled. 644 * Mark as no longer profiling kernel and if all profiling done, 645 * disable the clock. 646 */ 647 if (profiling && (profon & PRF_KERNEL)) { 648 profon &= ~PRF_KERNEL; 649 if (profon == PRF_NONE) 650 stopprofclock(); 651 } 652 } 653 #endif 654 #endif 655 656 void 657 delay(unsigned int n) 658 { 659 unsigned int cur_tick, initial_tick; 660 int remaining; 661 662 /* 663 * Read the counter first, so that the rest of the setup overhead is 664 * counted. 665 */ 666 initial_tick = clk_gettick(); 667 668 if (amiga_clk_interval == 0) { 669 /* 670 * Clock is not initialised yet, 671 * so just do some ad-hoc loop. 672 */ 673 static uint32_t dummy; 674 675 n *= 4; 676 while (n--) 677 dummy *= eclockfreq; 678 return; 679 } 680 681 if (n <= fast_delay_limit) { 682 /* 683 * For unsigned arithmetic, division can be replaced with 684 * multiplication with the inverse and a shift. 685 */ 686 remaining = n * eclockfreq / 1000000; 687 } else { 688 /* This is a very long delay. 689 * Being slow here doesn't matter. 690 */ 691 remaining = (unsigned long long) n * eclockfreq / 1000000; 692 } 693 694 while (remaining > 0) { 695 cur_tick = clk_gettick(); 696 if (cur_tick > initial_tick) 697 remaining -= amiga_clk_interval - (cur_tick - initial_tick); 698 else 699 remaining -= initial_tick - cur_tick; 700 initial_tick = cur_tick; 701 } 702 } 703