1 /* $NetBSD: clock.c,v 1.36 2024/01/07 07:58:35 isaki Exp $ */ 2 3 /* 4 * Copyright (c) 1988 University of Utah. 5 * Copyright (c) 1982, 1990, 1993 6 * The Regents of the University of California. 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 8.2 (Berkeley) 1/12/94 39 */ 40 41 #include <sys/cdefs.h> 42 __KERNEL_RCSID(0, "$NetBSD: clock.c,v 1.36 2024/01/07 07:58:35 isaki Exp $"); 43 44 #include "clock.h" 45 46 #if NCLOCK > 0 47 48 #include <sys/param.h> 49 #include <sys/systm.h> 50 #include <sys/kernel.h> 51 #include <sys/device.h> 52 #include <sys/timetc.h> 53 54 #include <machine/psl.h> 55 #include <machine/cpu.h> 56 #include <machine/bus.h> 57 58 #include <dev/clock_subr.h> 59 60 #include <arch/x68k/dev/mfp.h> 61 62 static int clock_match(device_t, cfdata_t, void *); 63 static void clock_attach(device_t, device_t, void *); 64 65 CFATTACH_DECL_NEW(clock, 0, 66 clock_match, clock_attach, NULL, NULL); 67 68 static int clock_attached; 69 70 static unsigned int mfp_get_timecount(struct timecounter *); 71 72 static int 73 clock_match(device_t parent, cfdata_t cf, void *aux) 74 { 75 76 if (strcmp (aux, "clock") != 0) 77 return (0); 78 if (clock_attached) 79 return (0); 80 return 1; 81 } 82 83 84 static void 85 clock_attach(device_t parent, device_t self, void *aux) 86 { 87 88 clock_attached = 1; 89 90 aprint_normal(": MFP timer C\n"); 91 } 92 93 94 /* 95 * MFP of X68k uses 4MHz clock always and we use 1/200 prescaler here. 96 * Therefore, clock interval is 50 usec. 97 * 98 * Note that for timecounters, we'd like to use a finger grained clock, but 99 * since we only have an 8-bit clock, we can't do that without increasing 100 * the system clock rate. (Otherwise the counter would roll in less than 101 * a single system clock.) 102 */ 103 #define CLK_RESOLUTION (50) 104 #define CLOCKS_PER_SEC (1000000 / CLK_RESOLUTION) 105 106 /* 107 * Machine-dependent clock routines. 108 * 109 * Startrtclock restarts the real-time clock, which provides 110 * hardclock interrupts to kern_clock.c. 111 * 112 * A note on the real-time clock: 113 * We actually load the clock with CLK_INTERVAL-1 instead of CLK_INTERVAL. 114 * This is because the counter decrements to zero after N+1 enabled clock 115 * periods where N is the value loaded into the counter. 116 */ 117 118 /* 119 * Set up the real-time and statistics clocks. Leave stathz 0 only if 120 * no alternative timer is available. 121 * 122 */ 123 void 124 cpu_initclocks(void) 125 { 126 static struct timecounter tc = { 127 .tc_name = "mfp", 128 .tc_frequency = CLOCKS_PER_SEC, 129 .tc_counter_mask = 0xff, 130 .tc_get_timecount = mfp_get_timecount, 131 .tc_quality = 100, 132 }; 133 134 if (CLOCKS_PER_SEC % hz || 135 hz <= (CLOCKS_PER_SEC / 256) || hz > CLOCKS_PER_SEC) { 136 printf("cannot set %d Hz clock. using 100 Hz\n", hz); 137 hz = 100; 138 } 139 140 mfp_set_tcdcr(0); /* stop timers C and D */ 141 mfp_set_tcdcr(mfp_get_tcdcr() | 0x70); /* 1/200 delay mode */ 142 143 mfp_set_tcdr(CLOCKS_PER_SEC / hz); 144 mfp_bit_set_ierb(MFP_INTR_TIMER_C); 145 146 mfp_set_tddr(0); /* maximum free run -- only 8 bits wide */ 147 mfp_set_tcdcr(mfp_get_tcdcr() | 0x07); /* 1/200 prescaler */ 148 149 tc_init(&tc); 150 } 151 152 /* 153 * We assume newhz is either stathz or profhz, and that neither will 154 * change after being set up above. Could recalculate intervals here 155 * but that would be a drag. 156 */ 157 void 158 setstatclockrate(int newhz) 159 { 160 } 161 162 /* 163 * Returns number of usec since last recorded clock "tick" 164 * (i.e. clock interrupt). 165 */ 166 unsigned int 167 mfp_get_timecount(struct timecounter *tc) 168 { 169 uint8_t val; 170 val = ~(mfp_get_tddr()); 171 return (val); 172 } 173 174 #if 0 175 void 176 DELAY(int mic) 177 { 178 u_long n; 179 short hpos; 180 181 /* 182 * busy-poll for mic microseconds. This is *no* general timeout function, 183 * it's meant for timing in hardware control, and as such, may not lower 184 * interrupt priorities to really `sleep'. 185 */ 186 187 /* 188 * this function uses HSync pulses as base units. The custom chips 189 * display only deals with 31.6kHz/2 refresh, this gives us a 190 * resolution of 1/15800 s, which is ~63us (add some fuzz so we really 191 * wait awhile, even if using small timeouts) 192 */ 193 n = mic/32 + 2; 194 do { 195 while ((mfp_get_gpip() & MFP_GPIP_HSYNC) != 0) 196 __asm("nop"); 197 while ((mfp_get_gpip() & MFP_GPIP_HSYNC) == 0) 198 __asm("nop"); 199 } while (n--); 200 } 201 #endif 202 203 204 #if notyet 205 206 /* implement this later. I'd suggest using both timers in CIA-A, they're 207 not yet used. */ 208 209 /* 210 * /dev/clock: mappable high resolution timer. 211 * 212 * This code implements a 32-bit recycling counter (with a 4 usec period) 213 * using timers 2 & 3 on the 6840 clock chip. The counter can be mapped 214 * RO into a user's address space to achieve low overhead (no system calls), 215 * high-precision timing. 216 * 217 * Note that timer 3 is also used for the high precision profiling timer 218 * (PROFTIMER code above). Care should be taken when both uses are 219 * configured as only a token effort is made to avoid conflicting use. 220 */ 221 #include <sys/proc.h> 222 #include <sys/resourcevar.h> 223 #include <sys/ioctl.h> 224 #include <uvm/uvm_extern.h> /* XXX needed? */ 225 #include <x68k/x68k/clockioctl.h> 226 #include <sys/specdev.h> 227 #include <sys/vnode.h> 228 #include <sys/mman.h> 229 230 int clockon = 0; /* non-zero if high-res timer enabled */ 231 #ifdef PROFTIMER 232 int profprocs = 0; /* # of procs using profiling timer */ 233 #endif 234 #ifdef DEBUG 235 int clockdebug = 0; 236 #endif 237 238 /*ARGSUSED*/ 239 int 240 clockopen(dev_t dev, int flags) 241 { 242 #ifdef PROFTIMER 243 #ifdef PROF 244 /* 245 * Kernel profiling enabled, give up. 246 */ 247 if (profiling) 248 return(EBUSY); 249 #endif /* PROF */ 250 /* 251 * If any user processes are profiling, give up. 252 */ 253 if (profprocs) 254 return(EBUSY); 255 #endif /* PROFTIMER */ 256 if (!clockon) { 257 startclock(); 258 clockon++; 259 } 260 return(0); 261 } 262 263 /*ARGSUSED*/ 264 int 265 clockclose(dev_t dev, int flags) 266 { 267 (void) clockunmmap(dev, NULL, curproc); /* XXX */ 268 stopclock(); 269 clockon = 0; 270 return(0); 271 } 272 273 /*ARGSUSED*/ 274 int 275 clockioctl(dev_t dev, u_long cmd, void *data, int flag, struct proc *p) 276 { 277 int error = 0; 278 279 switch (cmd) { 280 281 case CLOCKMAP: 282 error = clockmmap(dev, (void **)data, p); 283 break; 284 285 case CLOCKUNMAP: 286 error = clockunmmap(dev, *(void **)data, p); 287 break; 288 289 case CLOCKGETRES: 290 *(int *)data = CLK_RESOLUTION; 291 break; 292 293 default: 294 error = EINVAL; 295 break; 296 } 297 return(error); 298 } 299 300 /*ARGSUSED*/ 301 int 302 clockmap(dev_t dev, off_t off, int prot) 303 { 304 return ((off + (INTIOBASE + CLKBASE + CLKSR - 1)) >> PGSHIFT); 305 } 306 307 int 308 clockmmap(dev_t dev, void **addrp, struct proc *p) 309 { 310 int error; 311 struct vnode vn; 312 struct specinfo si; 313 int flags; 314 315 flags = MAP_FILE|MAP_SHARED; 316 if (*addrp) 317 flags |= MAP_FIXED; 318 else 319 *addrp = (void *)0x1000000; /* XXX */ 320 vn.v_type = VCHR; /* XXX */ 321 vn.v_specinfo = &si; /* XXX */ 322 vn.v_rdev = dev; /* XXX */ 323 error = vm_mmap(&p->p_vmspace->vm_map, (vaddr_t *)addrp, 324 PAGE_SIZE, VM_PROT_ALL, flags, (void *)&vn, 0); 325 return(error); 326 } 327 328 int 329 clockunmmap(dev_t dev, void *addr, struct proc *p) 330 { 331 int rv; 332 333 if (addr == 0) 334 return(EINVAL); /* XXX: how do we deal with this? */ 335 uvm_deallocate(p->p_vmspace->vm_map, (vaddr_t)addr, PAGE_SIZE); 336 return 0; 337 } 338 339 void 340 startclock(void) 341 { 342 struct clkreg *clk = (struct clkreg *)clkstd[0]; 343 344 clk->clk_msb2 = -1; clk->clk_lsb2 = -1; 345 clk->clk_msb3 = -1; clk->clk_lsb3 = -1; 346 347 clk->clk_cr2 = CLK_CR3; 348 clk->clk_cr3 = CLK_OENAB|CLK_8BIT; 349 clk->clk_cr2 = CLK_CR1; 350 clk->clk_cr1 = CLK_IENAB; 351 } 352 353 void 354 stopclock(void) 355 { 356 struct clkreg *clk = (struct clkreg *)clkstd[0]; 357 358 clk->clk_cr2 = CLK_CR3; 359 clk->clk_cr3 = 0; 360 clk->clk_cr2 = CLK_CR1; 361 clk->clk_cr1 = CLK_IENAB; 362 } 363 364 #endif /* notyet */ 365 366 367 #ifdef PROFTIMER 368 /* 369 * This code allows the amiga kernel to use one of the extra timers on 370 * the clock chip for profiling, instead of the regular system timer. 371 * The advantage of this is that the profiling timer can be turned up to 372 * a higher interrupt rate, giving finer resolution timing. The profclock 373 * routine is called from the lev6intr in locore, and is a specialized 374 * routine that calls addupc. The overhead then is far less than if 375 * hardclock/softclock was called. Further, the context switch code in 376 * locore has been changed to turn the profile clock on/off when switching 377 * into/out of a process that is profiling (startprofclock/stopprofclock). 378 * This reduces the impact of the profiling clock on other users, and might 379 * possibly increase the accuracy of the profiling. 380 */ 381 int profint = PRF_INTERVAL; /* Clock ticks between interrupts */ 382 int profscale = 0; /* Scale factor from sys clock to prof clock */ 383 char profon = 0; /* Is profiling clock on? */ 384 385 /* profon values - do not change, locore.s assumes these values */ 386 #define PRF_NONE 0x00 387 #define PRF_USER 0x01 388 #define PRF_KERNEL 0x80 389 390 void 391 initprofclock(void) 392 { 393 struct proc *p = curproc; /* XXX */ 394 395 /* 396 * If the high-res timer is running, force profiling off. 397 * Unfortunately, this gets reflected back to the user not as 398 * an error but as a lack of results. 399 */ 400 if (clockon) { 401 p->p_stats->p_prof.pr_scale = 0; 402 return; 403 } 404 /* 405 * Keep track of the number of user processes that are profiling 406 * by checking the scale value. 407 * 408 * XXX: this all assumes that the profiling code is well behaved; 409 * i.e. profil() is called once per process with pcscale non-zero 410 * to turn it on, and once with pcscale zero to turn it off. 411 * Also assumes you don't do any forks or execs. Oh well, there 412 * is always adb... 413 */ 414 if (p->p_stats->p_prof.pr_scale) 415 profprocs++; 416 else 417 profprocs--; 418 /* 419 * The profile interrupt interval must be an even divisor 420 * of the CLK_INTERVAL so that scaling from a system clock 421 * tick to a profile clock tick is possible using integer math. 422 */ 423 if (profint > CLK_INTERVAL || (CLK_INTERVAL % profint) != 0) 424 profint = CLK_INTERVAL; 425 profscale = CLK_INTERVAL / profint; 426 } 427 428 void 429 startprofclock(void) 430 { 431 } 432 433 void 434 stopprofclock(void) 435 { 436 } 437 438 #ifdef PROF 439 /* 440 * profclock() is expanded in line in lev6intr() unless profiling kernel. 441 * Assumes it is called with clock interrupts blocked. 442 */ 443 void 444 profclock(void *pc, int ps) 445 { 446 447 /* 448 * Came from user mode. 449 * If this process is being profiled record the tick. 450 */ 451 if (USERMODE(ps)) { 452 if (p->p_stats.p_prof.pr_scale) 453 addupc(pc, &curproc->p_stats.p_prof, 1); 454 } 455 456 /* 457 * Came from kernel (supervisor) mode. 458 * If we are profiling the kernel, record the tick. 459 */ 460 else if (profiling < 2) { 461 int s = pc - s_lowpc; 462 463 if (s < s_textsize) 464 kcount[s / (HISTFRACTION * sizeof (*kcount))]++; 465 } 466 467 /* 468 * Kernel profiling was on but has been disabled. 469 * Mark as no longer profiling kernel and if all profiling done, 470 * disable the clock. 471 */ 472 if (profiling && (profon & PRF_KERNEL)) { 473 profon &= ~PRF_KERNEL; 474 if (profon == PRF_NONE) 475 stopprofclock(); 476 } 477 } 478 #endif /* PROF */ 479 #endif /* PROFTIMER */ 480 481 #else /* NCLOCK */ 482 #error loose. 483 #endif 484