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