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