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 82int clockmatch(device_t, cfdata_t, void *); 83void clockattach(device_t, device_t, void *); 84void cpu_initclocks(void); 85static 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. */ 89int amiga_clk_interval; 90int eclockfreq; 91struct CIA *clockcia; 92 93static u_int clk_getcounter(struct timecounter *); 94 95static struct timecounter clk_timecounter = { 96 .tc_get_timecount = clk_getcounter, 97 .tc_counter_mask = ~0u, 98 .tc_quality = 100, 99}; 100 101CFATTACH_DECL_NEW(clock, 0, 102 clockmatch, clockattach, NULL, NULL); 103 104int 105clockmatch(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 */ 115void 116clockattach(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 200void 201cpu_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 237void 238setstatclockrate(int hertz) 239{ 240} 241 242/* 243 * Returns ticks since last recorded clock "tick" 244 * (i.e. clock interrupt). 245 */ 246static u_int 247clk_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 286static u_int 287clk_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 */ 326static void 327calibrate_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 401int clockon = 0; /* non-zero if high-res timer enabled */ 402#ifdef PROFTIMER 403int profprocs = 0; /* # of procs using profiling timer */ 404#endif 405#ifdef DEBUG 406int clockdebug = 0; 407#endif 408 409/*ARGSUSED*/ 410int 411clockopen(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*/ 435int 436clockclose(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*/ 445int 446clockioctl(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*/ 472void 473clockmap(dev_t dev, int off, int prot) 474{ 475 return MD_BTOP(off + (INTIOBASE+CLKBASE+CLKSR-1)); 476} 477 478int 479clockmmap(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 499int 500clockunmmap(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 510void 511startclock(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 524void 525stopclock(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 */ 553int profint = PRF_INTERVAL; /* Clock ticks between interrupts */ 554int profscale = 0; /* Scale factor from sys clock to prof clock */ 555char 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 562void 563initprofclock(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 602void 603startprofclock(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 626void 627stopprofclock(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 */ 638void 639profclock(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