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