1 /* $NetBSD: clock.c,v 1.59 2023/12/20 00:40:42 thorpej 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.59 2023/12/20 00:40:42 thorpej 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 <uvm/uvm_extern.h> 396 #include <amiga/amiga/clockioctl.h> 397 #include <sys/specdev.h> 398 #include <sys/vnode.h> 399 #include <sys/mman.h> 400 401 int clockon = 0; /* non-zero if high-res timer enabled */ 402 #ifdef PROFTIMER 403 int profprocs = 0; /* # of procs using profiling timer */ 404 #endif 405 #ifdef DEBUG 406 int clockdebug = 0; 407 #endif 408 409 /*ARGSUSED*/ 410 int 411 clockopen(dev_t dev, int flags) 412 { 413 #ifdef PROFTIMER 414 #ifdef PROF 415 /* 416 * Kernel profiling enabled, give up. 417 */ 418 if (profiling) 419 return(EBUSY); 420 #endif 421 /* 422 * If any user processes are profiling, give up. 423 */ 424 if (profprocs) 425 return(EBUSY); 426 #endif 427 if (!clockon) { 428 startclock(); 429 clockon++; 430 } 431 return(0); 432 } 433 434 /*ARGSUSED*/ 435 int 436 clockclose(dev_t dev, int flags) 437 { 438 (void) clockunmmap(dev, (void *)0, curproc); /* XXX */ 439 stopclock(); 440 clockon = 0; 441 return(0); 442 } 443 444 /*ARGSUSED*/ 445 int 446 clockioctl(dev_t dev, u_long cmd, void *data, int flag, struct proc *p) 447 { 448 int error = 0; 449 450 switch (cmd) { 451 452 case CLOCKMAP: 453 error = clockmmap(dev, (void **)data, p); 454 break; 455 456 case CLOCKUNMAP: 457 error = clockunmmap(dev, *(void **)data, p); 458 break; 459 460 case CLOCKGETRES: 461 *(int *)data = CLK_RESOLUTION; 462 break; 463 464 default: 465 error = EINVAL; 466 break; 467 } 468 return(error); 469 } 470 471 /*ARGSUSED*/ 472 void 473 clockmap(dev_t dev, int off, int prot) 474 { 475 return MD_BTOP(off + (INTIOBASE+CLKBASE+CLKSR-1)); 476 } 477 478 int 479 clockmmap(dev_t dev, void **addrp, struct proc *p) 480 { 481 int error; 482 struct vnode vn; 483 struct specinfo si; 484 int flags; 485 486 flags = MAP_FILE|MAP_SHARED; 487 if (*addrp) 488 flags |= MAP_FIXED; 489 else 490 *addrp = (void *)0x1000000; /* XXX */ 491 vn.v_type = VCHR; /* XXX */ 492 vn.v_specinfo = &si; /* XXX */ 493 vn.v_rdev = dev; /* XXX */ 494 error = vm_mmap(&p->p_vmspace->vm_map, (vm_offset_t *)addrp, 495 PAGE_SIZE, VM_PROT_ALL, flags, (void *)&vn, 0); 496 return(error); 497 } 498 499 int 500 clockunmmap(dev_t dev, void *addr, struct proc *p) 501 { 502 int rv; 503 504 if (addr == 0) 505 return(EINVAL); /* XXX: how do we deal with this? */ 506 uvm_deallocate(p->p_vmspace->vm_map, (vm_offset_t)addr, PAGE_SIZE); 507 return 0; 508 } 509 510 void 511 startclock(void) 512 { 513 register struct clkreg *clk = (struct clkreg *)clkstd[0]; 514 515 clk->clk_msb2 = -1; clk->clk_lsb2 = -1; 516 clk->clk_msb3 = -1; clk->clk_lsb3 = -1; 517 518 clk->clk_cr2 = CLK_CR3; 519 clk->clk_cr3 = CLK_OENAB|CLK_8BIT; 520 clk->clk_cr2 = CLK_CR1; 521 clk->clk_cr1 = CLK_IENAB; 522 } 523 524 void 525 stopclock(void) 526 { 527 register struct clkreg *clk = (struct clkreg *)clkstd[0]; 528 529 clk->clk_cr2 = CLK_CR3; 530 clk->clk_cr3 = 0; 531 clk->clk_cr2 = CLK_CR1; 532 clk->clk_cr1 = CLK_IENAB; 533 } 534 #endif 535 536 #endif 537 538 539 #ifdef PROFTIMER 540 /* 541 * This code allows the amiga kernel to use one of the extra timers on 542 * the clock chip for profiling, instead of the regular system timer. 543 * The advantage of this is that the profiling timer can be turned up to 544 * a higher interrupt rate, giving finer resolution timing. The profclock 545 * routine is called from the lev6intr in locore, and is a specialized 546 * routine that calls addupc. The overhead then is far less than if 547 * hardclock/softclock was called. Further, the context switch code in 548 * locore has been changed to turn the profile clock on/off when switching 549 * into/out of a process that is profiling (startprofclock/stopprofclock). 550 * This reduces the impact of the profiling clock on other users, and might 551 * possibly increase the accuracy of the profiling. 552 */ 553 int profint = PRF_INTERVAL; /* Clock ticks between interrupts */ 554 int profscale = 0; /* Scale factor from sys clock to prof clock */ 555 char profon = 0; /* Is profiling clock on? */ 556 557 /* profon values - do not change, locore.s assumes these values */ 558 #define PRF_NONE 0x00 559 #define PRF_USER 0x01 560 #define PRF_KERNEL 0x80 561 562 void 563 initprofclock(void) 564 { 565 #if NCLOCK > 0 566 struct proc *p = curproc; /* XXX */ 567 568 /* 569 * If the high-res timer is running, force profiling off. 570 * Unfortunately, this gets reflected back to the user not as 571 * an error but as a lack of results. 572 */ 573 if (clockon) { 574 p->p_stats->p_prof.pr_scale = 0; 575 return; 576 } 577 /* 578 * Keep track of the number of user processes that are profiling 579 * by checking the scale value. 580 * 581 * XXX: this all assumes that the profiling code is well behaved; 582 * i.e. profil() is called once per process with pcscale non-zero 583 * to turn it on, and once with pcscale zero to turn it off. 584 * Also assumes you don't do any forks or execs. Oh well, there 585 * is always adb... 586 */ 587 if (p->p_stats->p_prof.pr_scale) 588 profprocs++; 589 else 590 profprocs--; 591 #endif 592 /* 593 * The profile interrupt interval must be an even divisor 594 * of the amiga_clk_interval so that scaling from a system clock 595 * tick to a profile clock tick is possible using integer math. 596 */ 597 if (profint > amiga_clk_interval || (amiga_clk_interval % profint) != 0) 598 profint = amiga_clk_interval; 599 profscale = amiga_clk_interval / profint; 600 } 601 602 void 603 startprofclock(void) 604 { 605 unsigned short interval; 606 607 /* stop timer B */ 608 clockcia->crb = clockcia->crb & 0xc0; 609 610 /* load interval into registers. 611 the clocks run at NTSC: 715.909kHz or PAL: 709.379kHz */ 612 613 interval = profint - 1; 614 615 /* order of setting is important ! */ 616 clockcia->tblo = interval & 0xff; 617 clockcia->tbhi = interval >> 8; 618 619 /* enable interrupts for timer B */ 620 clockcia->icr = (1<<7) | (1<<1); 621 622 /* start timer B in continuous shot mode */ 623 clockcia->crb = (clockcia->crb & 0xc0) | 1; 624 } 625 626 void 627 stopprofclock(void) 628 { 629 /* stop timer B */ 630 clockcia->crb = clockcia->crb & 0xc0; 631 } 632 633 #ifdef PROF 634 /* 635 * profclock() is expanded in line in lev6intr() unless profiling kernel. 636 * Assumes it is called with clock interrupts blocked. 637 */ 638 void 639 profclock(void *pc, int ps) 640 { 641 /* 642 * Came from user mode. 643 * If this process is being profiled record the tick. 644 */ 645 if (USERMODE(ps)) { 646 if (p->p_stats.p_prof.pr_scale) 647 addupc(pc, &curproc->p_stats.p_prof, 1); 648 } 649 /* 650 * Came from kernel (supervisor) mode. 651 * If we are profiling the kernel, record the tick. 652 */ 653 else if (profiling < 2) { 654 register int s = pc - s_lowpc; 655 656 if (s < s_textsize) 657 kcount[s / (HISTFRACTION * sizeof (*kcount))]++; 658 } 659 /* 660 * Kernel profiling was on but has been disabled. 661 * Mark as no longer profiling kernel and if all profiling done, 662 * disable the clock. 663 */ 664 if (profiling && (profon & PRF_KERNEL)) { 665 profon &= ~PRF_KERNEL; 666 if (profon == PRF_NONE) 667 stopprofclock(); 668 } 669 } 670 #endif 671 #endif 672