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