xref: /netbsd-src/sys/arch/atari/dev/clock.c (revision 2a399c6883d870daece976daec6ffa7bb7f934ce)
1 /*	$NetBSD: clock.c,v 1.18 1997/02/26 12:26:44 leo Exp $	*/
2 
3 /*
4  * Copyright (c) 1988 University of Utah.
5  * Copyright (c) 1982, 1990 The Regents of the University of California.
6  * 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	7.6 (Berkeley) 5/7/91
43  */
44 
45 #include <sys/param.h>
46 #include <sys/kernel.h>
47 #include <sys/systm.h>
48 #include <sys/device.h>
49 #include <sys/uio.h>
50 #include <sys/conf.h>
51 
52 #include <dev/clock_subr.h>
53 
54 #include <machine/psl.h>
55 #include <machine/cpu.h>
56 #include <machine/iomap.h>
57 #include <machine/mfp.h>
58 #include <atari/dev/clockreg.h>
59 #include <atari/atari/device.h>
60 
61 #if defined(GPROF) && defined(PROFTIMER)
62 #include <machine/profile.h>
63 #endif
64 
65 /*
66  * The MFP clock runs at 2457600Hz. We use a {system,stat,prof}clock divider
67  * of 200. Therefore the timer runs at an effective rate of:
68  * 2457600/200 = 12288Hz.
69  */
70 #define CLOCK_HZ	12288
71 
72 /*
73  * Machine-dependent clock routines.
74  *
75  * Inittodr initializes the time of day hardware which provides
76  * date functions.
77  *
78  * Resettodr restores the time of day hardware after a time change.
79  */
80 
81 struct clock_softc {
82 	struct device	sc_dev;
83 	int		sc_flags;
84 };
85 
86 /*
87  *  'sc_flags' state info. Only used by the rtc-device functions.
88  */
89 #define	RTC_OPEN	1
90 
91 /* {b,c}devsw[] function prototypes for rtc functions */
92 dev_type_open(rtcopen);
93 dev_type_close(rtcclose);
94 dev_type_read(rtcread);
95 dev_type_write(rtcwrite);
96 
97 static void	clockattach __P((struct device *, struct device *, void *));
98 static int	clockmatch __P((struct device *, struct cfdata *, void *));
99 
100 struct cfattach clock_ca = {
101 	sizeof(struct clock_softc), clockmatch, clockattach
102 };
103 
104 struct cfdriver clock_cd = {
105 	NULL, "clock", DV_DULL, NULL, 0
106 };
107 
108 void statintr __P((struct clockframe));
109 
110 static u_long	gettod __P((void));
111 static int	twodigits __P((char *, int));
112 
113 static int	divisor;	/* Systemclock divisor	*/
114 
115 /*
116  * Statistics and profile clock intervals and variances. Variance must
117  * be a power of 2. Since this gives us an even number, not an odd number,
118  * we discard one case and compensate. That is, a variance of 64 would
119  * give us offsets in [0..63]. Instead, we take offsets in [1..63].
120  * This is symetric around the point 32, or statvar/2, and thus averages
121  * to that value (assuming uniform random numbers).
122  */
123 #ifdef STATCLOCK
124 static int	statvar = 32;	/* {stat,prof}clock variance		*/
125 static int	statmin;	/* statclock divisor - variance/2	*/
126 static int	profmin;	/* profclock divisor - variance/2	*/
127 static int	clk2min;	/* current, from above choises		*/
128 #endif
129 
130 int
131 clockmatch(pdp, cfp, auxp)
132 struct device	*pdp;
133 struct cfdata	*cfp;
134 void		*auxp;
135 {
136 	if (!atari_realconfig) {
137 	    /*
138 	     * Initialize Timer-B in the ST-MFP. This timer is used by
139 	     * the 'delay' function below. This timer is setup to be
140 	     * continueously counting from 255 back to zero at a
141 	     * frequency of 614400Hz. We do this *early* in the
142 	     * initialisation process.
143 	     */
144 	    MFP->mf_tbcr  = 0;		/* Stop timer			*/
145 	    MFP->mf_iera &= ~IA_TIMB;	/* Disable timer interrupts	*/
146 	    MFP->mf_tbdr  = 0;
147 	    MFP->mf_tbcr  = T_Q004;	/* Start timer			*/
148 
149 	    /*
150 	     * Initialize the time structure
151 	     */
152 	    time.tv_sec  = 0;
153 	    time.tv_usec = 0;
154 
155 	    return 0;
156 	}
157 	if(!strcmp("clock", auxp))
158 		return(1);
159 	return(0);
160 }
161 
162 /*
163  * Start the real-time clock.
164  */
165 void clockattach(pdp, dp, auxp)
166 struct device	*pdp, *dp;
167 void		*auxp;
168 {
169 	struct clock_softc *sc = (void *)dp;
170 
171 	sc->sc_flags = 0;
172 
173 	/*
174 	 * Initialize Timer-A in the ST-MFP. We use a divisor of 200.
175 	 * The MFP clock runs at 2457600Hz. Therefore the timer runs
176 	 * at an effective rate of: 2457600/200 = 12288Hz. The
177 	 * following expression works for 48, 64 or 96 hz.
178 	 */
179 	divisor       = CLOCK_HZ/hz;
180 	MFP->mf_tacr  = 0;		/* Stop timer			*/
181 	MFP->mf_iera &= ~IA_TIMA;	/* Disable timer interrupts	*/
182 	MFP->mf_tadr  = divisor;	/* Set divisor			*/
183 
184 	if (hz != 48 && hz != 64 && hz != 96) { /* XXX */
185 		printf (": illegal value %d for systemclock, reset to %d\n\t",
186 								hz, 64);
187 		hz = 64;
188 	}
189 	printf(": system hz %d timer-A divisor 200/%d\n", hz, divisor);
190 
191 #ifdef STATCLOCK
192 	if ((stathz == 0) || (stathz > hz) || (CLOCK_HZ % stathz))
193 		stathz = hz;
194 	if ((profhz == 0) || (profhz > (hz << 1)) || (CLOCK_HZ % profhz))
195 		profhz = hz << 1;
196 
197 	MFP->mf_tcdcr &= 0x7;			/* Stop timer		*/
198 	MFP->mf_ierb  &= ~IB_TIMC;		/* Disable timer inter.	*/
199 	MFP->mf_tcdr   = CLOCK_HZ/stathz;	/* Set divisor		*/
200 
201 	statmin  = (CLOCK_HZ/stathz) - (statvar >> 1);
202 	profmin  = (CLOCK_HZ/profhz) - (statvar >> 1);
203 	clk2min  = statmin;
204 #endif /* STATCLOCK */
205 
206 }
207 
208 void cpu_initclocks()
209 {
210 	MFP->mf_tacr  = T_Q200;		/* Start timer			*/
211 	MFP->mf_ipra &= ~IA_TIMA;	/* Clear pending interrupts	*/
212 	MFP->mf_iera |= IA_TIMA;	/* Enable timer interrupts	*/
213 	MFP->mf_imra |= IA_TIMA;	/*    .....			*/
214 
215 #ifdef STATCLOCK
216 	MFP->mf_tcdcr = (MFP->mf_tcdcr & 0x7) | (T_Q200<<4); /* Start	*/
217 	MFP->mf_iprb &= ~IB_TIMC;	/* Clear pending interrupts	*/
218 	MFP->mf_ierb |= IB_TIMC;	/* Enable timer interrupts	*/
219 	MFP->mf_imrb |= IB_TIMC;	/*    .....			*/
220 #endif /* STATCLOCK */
221 }
222 
223 void
224 setstatclockrate(newhz)
225 	int newhz;
226 {
227 #ifdef STATCLOCK
228 	if (newhz == stathz)
229 		clk2min = statmin;
230 	else clk2min = profmin;
231 #endif /* STATCLOCK */
232 }
233 
234 #ifdef STATCLOCK
235 void
236 statintr(frame)
237 	struct clockframe frame;
238 {
239 	register int	var, r;
240 
241 	var = statvar - 1;
242 	do {
243 		r = random() & var;
244 	} while(r == 0);
245 
246 	/*
247 	 * Note that we are always lagging behind as the new divisor
248 	 * value will not be loaded until the next interrupt. This
249 	 * shouldn't disturb the median frequency (I think ;-) ) as
250 	 * only the value used when switching frequencies is used
251 	 * twice. This shouldn't happen very often.
252 	 */
253 	MFP->mf_tcdr = clk2min + r;
254 
255 	statclock(&frame);
256 }
257 #endif /* STATCLOCK */
258 
259 /*
260  * Returns number of usec since last recorded clock "tick"
261  * (i.e. clock interrupt).
262  */
263 long
264 clkread()
265 {
266 	u_int	delta;
267 
268 	delta = ((divisor - MFP->mf_tadr) * tick) / divisor;
269 	/*
270 	 * Account for pending clock interrupts
271 	 */
272 	if(MFP->mf_iera & IA_TIMA)
273 		return(delta + tick);
274 	return(delta);
275 }
276 
277 #define TIMB_FREQ	614400
278 #define TIMB_LIMIT	256
279 
280 /*
281  * Wait "n" microseconds.
282  * Relies on MFP-Timer B counting down from TIMB_LIMIT at TIMB_FREQ Hz.
283  * Note: timer had better have been programmed before this is first used!
284  */
285 void
286 delay(n)
287 int	n;
288 {
289 	int	tick, otick;
290 
291 	/*
292 	 * Read the counter first, so that the rest of the setup overhead is
293 	 * counted.
294 	 */
295 	otick = MFP->mf_tbdr;
296 
297 	/*
298 	 * Calculate ((n * TIMER_FREQ) / 1e6) using explicit assembler code so
299 	 * we can take advantage of the intermediate 64-bit quantity to prevent
300 	 * loss of significance.
301 	 */
302 	n -= 5;
303 	if(n < 0)
304 		return;
305 	{
306 	    u_int	temp;
307 
308 	    __asm __volatile ("mulul %2,%1:%0" : "=d" (n), "=d" (temp)
309 					       : "d" (TIMB_FREQ));
310 	    __asm __volatile ("divul %1,%2:%0" : "=d" (n)
311 					       : "d"(1000000),"d"(temp),"0"(n));
312 	}
313 
314 	while(n > 0) {
315 		tick = MFP->mf_tbdr;
316 		if(tick > otick)
317 			n -= TIMB_LIMIT - (tick - otick);
318 		else n -= otick - tick;
319 		otick = tick;
320 	}
321 }
322 
323 #ifdef GPROF
324 /*
325  * profclock() is expanded in line in lev6intr() unless profiling kernel.
326  * Assumes it is called with clock interrupts blocked.
327  */
328 profclock(pc, ps)
329 	caddr_t pc;
330 	int ps;
331 {
332 	/*
333 	 * Came from user mode.
334 	 * If this process is being profiled record the tick.
335 	 */
336 	if (USERMODE(ps)) {
337 		if (p->p_stats.p_prof.pr_scale)
338 			addupc(pc, &curproc->p_stats.p_prof, 1);
339 	}
340 	/*
341 	 * Came from kernel (supervisor) mode.
342 	 * If we are profiling the kernel, record the tick.
343 	 */
344 	else if (profiling < 2) {
345 		register int s = pc - s_lowpc;
346 
347 		if (s < s_textsize)
348 			kcount[s / (HISTFRACTION * sizeof (*kcount))]++;
349 	}
350 	/*
351 	 * Kernel profiling was on but has been disabled.
352 	 * Mark as no longer profiling kernel and if all profiling done,
353 	 * disable the clock.
354 	 */
355 	if (profiling && (profon & PRF_KERNEL)) {
356 		profon &= ~PRF_KERNEL;
357 		if (profon == PRF_NONE)
358 			stopprofclock();
359 	}
360 }
361 #endif
362 
363 /***********************************************************************
364  *                   Real Time Clock support                           *
365  ***********************************************************************/
366 
367 u_int mc146818_read(rtc, regno)
368 void	*rtc;
369 u_int	regno;
370 {
371 	((struct rtc *)rtc)->rtc_regno = regno;
372 	return(((struct rtc *)rtc)->rtc_data & 0377);
373 }
374 
375 void mc146818_write(rtc, regno, value)
376 void	*rtc;
377 u_int	regno, value;
378 {
379 	((struct rtc *)rtc)->rtc_regno = regno;
380 	((struct rtc *)rtc)->rtc_data  = value;
381 }
382 
383 /*
384  * Initialize the time of day register, assuming the RTC runs in UTC.
385  * Since we've got the 'rtc' device, this functionality should be removed
386  * from the kernel. The only problem to be solved before that can happen
387  * is the possibility of init(1) providing a way (rc.boot?) to set
388  * the RTC before single-user mode is entered.
389  */
390 void
391 inittodr(base)
392 time_t base;
393 {
394 	/* Battery clock does not store usec's, so forget about it. */
395 	time.tv_sec  = gettod();
396 	time.tv_usec = 0;
397 }
398 
399 /*
400  * Function turned into a No-op. Use /dev/rtc to update the RTC.
401  */
402 void
403 resettodr()
404 {
405 	return;
406 }
407 
408 static u_long
409 gettod()
410 {
411 	int			sps;
412 	mc_todregs		clkregs;
413 	struct clock_ymdhms	dt;
414 
415 	sps = splhigh();
416 	MC146818_GETTOD(RTC, &clkregs);
417 	splx(sps);
418 
419 	if(clkregs[MC_SEC] > 59)
420 		return(0);
421 	if(clkregs[MC_MIN] > 59)
422 		return(0);
423 	if(clkregs[MC_HOUR] > 23)
424 		return(0);
425 	if(range_test(clkregs[MC_DOM], 1, 31))
426 		return(0);
427 	if (range_test(clkregs[MC_MONTH], 1, 12))
428 		return(0);
429 	if(clkregs[MC_YEAR] > (2000 - GEMSTARTOFTIME))
430 		return(0);
431 
432 	dt.dt_year = clkregs[MC_YEAR] + GEMSTARTOFTIME;
433 	dt.dt_mon  = clkregs[MC_MONTH];
434 	dt.dt_day  = clkregs[MC_DOM];
435 	dt.dt_hour = clkregs[MC_HOUR];
436 	dt.dt_min  = clkregs[MC_MIN];
437 	dt.dt_sec  = clkregs[MC_SEC];
438 
439 	return(clock_ymdhms_to_secs(&dt));
440 }
441 /***********************************************************************
442  *                   RTC-device support				       *
443  ***********************************************************************/
444 int
445 rtcopen(dev, flag, mode, p)
446 	dev_t		dev;
447 	int		flag, mode;
448 	struct proc	*p;
449 {
450 	int			unit = minor(dev);
451 	struct clock_softc	*sc;
452 
453 	if (unit >= clock_cd.cd_ndevs)
454 		return ENXIO;
455 	sc = clock_cd.cd_devs[unit];
456 	if (!sc)
457 		return ENXIO;
458 	if (sc->sc_flags & RTC_OPEN)
459 		return EBUSY;
460 
461 	sc->sc_flags = RTC_OPEN;
462 	return 0;
463 }
464 
465 int
466 rtcclose(dev, flag, mode, p)
467 	dev_t		dev;
468 	int		flag;
469 	int		mode;
470 	struct proc	*p;
471 {
472 	int			unit = minor(dev);
473 	struct clock_softc	*sc = clock_cd.cd_devs[unit];
474 
475 	sc->sc_flags = 0;
476 	return 0;
477 }
478 
479 int
480 rtcread(dev, uio, flags)
481 	dev_t		dev;
482 	struct uio	*uio;
483 	int		flags;
484 {
485 	struct clock_softc	*sc;
486 	mc_todregs		clkregs;
487 	int			s, length;
488 	char			buffer[16];
489 
490 	sc = clock_cd.cd_devs[minor(dev)];
491 
492 	s = splhigh();
493 	MC146818_GETTOD(RTC, &clkregs);
494 	splx(s);
495 
496 	sprintf(buffer, "%02d%02d%02d%02d%02d.%02d\n",
497 	    clkregs[MC_YEAR] + GEMSTARTOFTIME - 1900,
498 	    clkregs[MC_MONTH], clkregs[MC_DOM],
499 	    clkregs[MC_HOUR], clkregs[MC_MIN], clkregs[MC_SEC]);
500 
501 	if (uio->uio_offset > strlen(buffer))
502 		return 0;
503 
504 	length = strlen(buffer) - uio->uio_offset;
505 	if (length > uio->uio_resid)
506 		length = uio->uio_resid;
507 
508 	return(uiomove((caddr_t)buffer, length, uio));
509 }
510 
511 static int
512 twodigits(buffer, pos)
513 	char *buffer;
514 	int pos;
515 {
516 	int result = 0;
517 
518 	if (buffer[pos] >= '0' && buffer[pos] <= '9')
519 		result = (buffer[pos] - '0') * 10;
520 	if (buffer[pos+1] >= '0' && buffer[pos+1] <= '9')
521 		result += (buffer[pos+1] - '0');
522 	return(result);
523 }
524 
525 int
526 rtcwrite(dev, uio, flags)
527 	dev_t		dev;
528 	struct uio	*uio;
529 	int		flags;
530 {
531 	mc_todregs		clkregs;
532 	int			s, length, error;
533 	char			buffer[14];
534 
535 	/*
536 	 * We require atomic updates!
537 	 */
538 	length = uio->uio_resid;
539 	if (uio->uio_offset || (length != sizeof(buffer)
540 	  && length != sizeof(buffer - 1)))
541 		return(EINVAL);
542 
543 	if ((error = uiomove((caddr_t)buffer, sizeof(buffer), uio)))
544 		return(error);
545 
546 	if (length == sizeof(buffer) && buffer[sizeof(buffer) - 1] != '\n')
547 		return(EINVAL);
548 
549 	s = splclock();
550 	MC146818_GETTOD(RTC, &clkregs);
551 	splx(s);
552 
553 	clkregs[MC_SEC]   = twodigits(buffer, 11);
554 	clkregs[MC_MIN]   = twodigits(buffer, 8);
555 	clkregs[MC_HOUR]  = twodigits(buffer, 6);
556 	clkregs[MC_DOM]   = twodigits(buffer, 4);
557 	clkregs[MC_MONTH] = twodigits(buffer, 2);
558 	s = twodigits(buffer, 0);
559 	s = (s < 70) ? s + 2000 : s + 1900;
560 	clkregs[MC_YEAR]  = s - GEMSTARTOFTIME;
561 
562 	s = splclock();
563 	MC146818_PUTTOD(RTC, &clkregs);
564 	splx(s);
565 
566 	return(0);
567 }
568