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