Home | History | Annotate | Line # | Download | only in x68k
      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