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