xref: /netbsd-src/sys/arch/atari/dev/clock.c (revision 76dfffe33547c37f8bdd446e3e4ab0f3c16cea4b)
1 /*	$NetBSD: clock.c,v 1.13 1996/10/13 04:10:51 christos 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 <machine/psl.h>
50 #include <machine/cpu.h>
51 #include <machine/iomap.h>
52 #include <machine/mfp.h>
53 #include <atari/dev/clockreg.h>
54 
55 #if defined(GPROF) && defined(PROFTIMER)
56 #include <machine/profile.h>
57 #endif
58 
59 /*
60  * The MFP clock runs at 2457600Hz. We use a {system,stat,prof}clock divider
61  * of 200. Therefore the timer runs at an effective rate of:
62  * 2457600/200 = 12288Hz.
63  */
64 #define CLOCK_HZ	12288
65 
66 /*
67  * Machine-dependent clock routines.
68  *
69  * Inittodr initializes the time of day hardware which provides
70  * date functions.
71  *
72  * Resettodr restores the time of day hardware after a time change.
73  */
74 
75 int	clockmatch __P((struct device *, void *, void *));
76 void	clockattach __P((struct device *, struct device *, void *));
77 
78 struct cfattach clock_ca = {
79 	sizeof(struct device), clockmatch, clockattach
80 };
81 
82 struct cfdriver clock_cd = {
83 	NULL, "clock", DV_DULL, NULL, 0
84 };
85 
86 void statintr __P((struct clockframe *));
87 
88 static u_long	gettod __P((void));
89 static int	settod __P((u_long));
90 
91 static int	divisor;	/* Systemclock divisor	*/
92 
93 /*
94  * Statistics and profile clock intervals and variances. Variance must
95  * be a power of 2. Since this gives us an even number, not an odd number,
96  * we discard one case and compensate. That is, a variance of 64 would
97  * give us offsets in [0..63]. Instead, we take offsets in [1..63].
98  * This is symetric around the point 32, or statvar/2, and thus averages
99  * to that value (assuming uniform random numbers).
100  */
101 #ifdef STATCLOCK
102 static int	statvar = 32;	/* {stat,prof}clock variance		*/
103 static int	statmin;	/* statclock divisor - variance/2	*/
104 static int	profmin;	/* profclock divisor - variance/2	*/
105 static int	clk2min;	/* current, from above choises		*/
106 #endif
107 
108 int
109 clockmatch(pdp, match, auxp)
110 struct device *pdp;
111 void *match, *auxp;
112 {
113 	if(!strcmp("clock", auxp))
114 		return(1);
115 	return(0);
116 }
117 
118 /*
119  * Start the real-time clock.
120  */
121 void clockattach(pdp, dp, auxp)
122 struct device	*pdp, *dp;
123 void			*auxp;
124 {
125 	/*
126 	 * Initialize Timer-A in the ST-MFP. We use a divisor of 200.
127 	 * The MFP clock runs at 2457600Hz. Therefore the timer runs
128 	 * at an effective rate of: 2457600/200 = 12288Hz. The
129 	 * following expression works for 48, 64 or 96 hz.
130 	 */
131 	divisor       = CLOCK_HZ/hz;
132 	MFP->mf_tacr  = 0;		/* Stop timer			*/
133 	MFP->mf_iera &= ~IA_TIMA;	/* Disable timer interrupts	*/
134 	MFP->mf_tadr  = divisor;	/* Set divisor			*/
135 
136 	if (hz != 48 && hz != 64 && hz != 96) { /* XXX */
137 		printf (": illegal value %d for systemclock, reset to %d\n\t",
138 								hz, 64);
139 		hz = 64;
140 	}
141 	printf(": system hz %d timer-A divisor 200/%d\n", hz, divisor);
142 
143 #ifdef STATCLOCK
144 	if ((stathz == 0) || (stathz > hz) || (CLOCK_HZ % stathz))
145 		stathz = hz;
146 	if ((profhz == 0) || (profhz > (hz << 1)) || (CLOCK_HZ % profhz))
147 		profhz = hz << 1;
148 
149 	MFP->mf_tcdcr &= 0x7;			/* Stop timer		*/
150 	MFP->mf_ierb  &= ~IB_TIMC;		/* Disable timer inter.	*/
151 	MFP->mf_tcdr   = CLOCK_HZ/stathz;	/* Set divisor		*/
152 
153 	statmin  = (CLOCK_HZ/stathz) - (statvar >> 1);
154 	profmin  = (CLOCK_HZ/profhz) - (statvar >> 1);
155 	clk2min  = statmin;
156 #endif /* STATCLOCK */
157 
158 	/*
159 	 * Initialize Timer-B in the ST-MFP. This timer is used by the 'delay'
160 	 * function below. This time is setup to be continueously counting from
161 	 * 255 back to zero at a frequency of 614400Hz.
162 	 */
163 	MFP->mf_tbcr  = 0;		/* Stop timer			*/
164 	MFP->mf_iera &= ~IA_TIMB;	/* Disable timer interrupts	*/
165 	MFP->mf_tbdr  = 0;
166 	MFP->mf_tbcr  = T_Q004;	/* Start timer			*/
167 
168 }
169 
170 void cpu_initclocks()
171 {
172 	MFP->mf_tacr  = T_Q200;		/* Start timer			*/
173 	MFP->mf_ipra &= ~IA_TIMA;	/* Clear pending interrupts	*/
174 	MFP->mf_iera |= IA_TIMA;	/* Enable timer interrupts	*/
175 	MFP->mf_imra |= IA_TIMA;	/*    .....			*/
176 
177 #ifdef STATCLOCK
178 	MFP->mf_tcdcr = (MFP->mf_tcdcr & 0x7) | (T_Q200<<4); /* Start	*/
179 	MFP->mf_iprb &= ~IB_TIMC;	/* Clear pending interrupts	*/
180 	MFP->mf_ierb |= IB_TIMC;	/* Enable timer interrupts	*/
181 	MFP->mf_imrb |= IB_TIMC;	/*    .....			*/
182 #endif /* STATCLOCK */
183 }
184 
185 void
186 setstatclockrate(newhz)
187 	int newhz;
188 {
189 #ifdef STATCLOCK
190 	if (newhz == stathz)
191 		clk2min = statmin;
192 	else clk2min = profmin;
193 #endif /* STATCLOCK */
194 }
195 
196 #ifdef STATCLOCK
197 void
198 statintr(frame)
199 	register struct clockframe *frame;
200 {
201 	register int	var, r;
202 
203 	var = statvar - 1;
204 	do {
205 		r = random() & var;
206 	} while(r == 0);
207 
208 	/*
209 	 * Note that we are always lagging behind as the new divisor
210 	 * value will not be loaded until the next interrupt. This
211 	 * shouldn't disturb the median frequency (I think ;-) ) as
212 	 * only the value used when switching frequencies is used
213 	 * twice. This shouldn't happen very often.
214 	 */
215 	MFP->mf_tcdr = clk2min + r;
216 
217 	statclock(frame);
218 }
219 #endif /* STATCLOCK */
220 
221 /*
222  * Returns number of usec since last recorded clock "tick"
223  * (i.e. clock interrupt).
224  */
225 long
226 clkread()
227 {
228 	u_int	delta;
229 
230 	delta = ((divisor - MFP->mf_tadr) * tick) / divisor;
231 	/*
232 	 * Account for pending clock interrupts
233 	 */
234 	if(MFP->mf_iera & IA_TIMA)
235 		return(delta + tick);
236 	return(delta);
237 }
238 
239 #define TIMB_FREQ	614400
240 #define TIMB_LIMIT	256
241 
242 /*
243  * Wait "n" microseconds.
244  * Relies on MFP-Timer B counting down from TIMB_LIMIT at TIMB_FREQ Hz.
245  * Note: timer had better have been programmed before this is first used!
246  */
247 void delay(n)
248 int	n;
249 {
250 	int	tick, otick;
251 
252 	/*
253 	 * Read the counter first, so that the rest of the setup overhead is
254 	 * counted.
255 	 */
256 	otick = MFP->mf_tbdr;
257 
258 	/*
259 	 * Calculate ((n * TIMER_FREQ) / 1e6) using explicit assembler code so
260 	 * we can take advantage of the intermediate 64-bit quantity to prevent
261 	 * loss of significance.
262 	 */
263 	n -= 5;
264 	if(n < 0)
265 		return;
266 	{
267 	    u_int	temp;
268 
269 	    __asm __volatile ("mulul %2,%1:%0" : "=d" (n), "=d" (temp)
270 					       : "d" (TIMB_FREQ));
271 	    __asm __volatile ("divul %1,%2:%0" : "=d" (n)
272 					       : "d"(1000000),"d"(temp),"0"(n));
273 	}
274 
275 	while(n > 0) {
276 		tick = MFP->mf_tbdr;
277 		if(tick > otick)
278 			n -= TIMB_LIMIT - (tick - otick);
279 		else n -= otick - tick;
280 		otick = tick;
281 	}
282 }
283 
284 #ifdef GPROF
285 /*
286  * profclock() is expanded in line in lev6intr() unless profiling kernel.
287  * Assumes it is called with clock interrupts blocked.
288  */
289 profclock(pc, ps)
290 	caddr_t pc;
291 	int ps;
292 {
293 	/*
294 	 * Came from user mode.
295 	 * If this process is being profiled record the tick.
296 	 */
297 	if (USERMODE(ps)) {
298 		if (p->p_stats.p_prof.pr_scale)
299 			addupc(pc, &curproc->p_stats.p_prof, 1);
300 	}
301 	/*
302 	 * Came from kernel (supervisor) mode.
303 	 * If we are profiling the kernel, record the tick.
304 	 */
305 	else if (profiling < 2) {
306 		register int s = pc - s_lowpc;
307 
308 		if (s < s_textsize)
309 			kcount[s / (HISTFRACTION * sizeof (*kcount))]++;
310 	}
311 	/*
312 	 * Kernel profiling was on but has been disabled.
313 	 * Mark as no longer profiling kernel and if all profiling done,
314 	 * disable the clock.
315 	 */
316 	if (profiling && (profon & PRF_KERNEL)) {
317 		profon &= ~PRF_KERNEL;
318 		if (profon == PRF_NONE)
319 			stopprofclock();
320 	}
321 }
322 #endif
323 
324 /***********************************************************************
325  *                   Real Time Clock support                           *
326  ***********************************************************************/
327 
328 u_int mc146818_read(rtc, regno)
329 void	*rtc;
330 u_int	regno;
331 {
332 	((struct rtc *)rtc)->rtc_regno = regno;
333 	return(((struct rtc *)rtc)->rtc_data & 0377);
334 }
335 
336 void mc146818_write(rtc, regno, value)
337 void	*rtc;
338 u_int	regno, value;
339 {
340 	((struct rtc *)rtc)->rtc_regno = regno;
341 	((struct rtc *)rtc)->rtc_data  = value;
342 }
343 
344 /*
345  * Initialize the time of day register, based on the time base which is, e.g.
346  * from a filesystem.
347  */
348 void
349 inittodr(base)
350 time_t base;
351 {
352 	u_long timbuf = base;	/* assume no battery clock exists */
353 
354 	timbuf = gettod();
355 
356 	if(timbuf < base) {
357 		printf("WARNING: bad date in battery clock\n");
358 		timbuf = base;
359 	}
360 
361 	/* Battery clock does not store usec's, so forget about it. */
362 	time.tv_sec  = timbuf;
363 	time.tv_usec = 0;
364 }
365 
366 void
367 resettodr()
368 {
369 	if(settod(time.tv_sec) == 1)
370 		return;
371 	printf("Cannot set battery backed clock\n");
372 }
373 
374 static	char	dmsize[12] =
375 {
376 	31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31
377 };
378 
379 static	char	ldmsize[12] =
380 {
381 	31, 29, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31
382 };
383 
384 static u_long
385 gettod()
386 {
387 	int		i, sps;
388 	u_long		new_time = 0;
389 	char		*msize;
390 	mc_todregs	clkregs;
391 
392 	sps = splhigh();
393 	MC146818_GETTOD(RTC, &clkregs);
394 	splx(sps);
395 
396 	if(clkregs[MC_SEC] > 59)
397 		return(0);
398 	if(clkregs[MC_MIN] > 59)
399 		return(0);
400 	if(clkregs[MC_HOUR] > 23)
401 		return(0);
402 	if(range_test(clkregs[MC_DOM], 1, 31))
403 		return(0);
404 	if (range_test(clkregs[MC_MONTH], 1, 12))
405 		return(0);
406 	if(clkregs[MC_YEAR] > (2000 - GEMSTARTOFTIME))
407 		return(0);
408 	clkregs[MC_YEAR] += GEMSTARTOFTIME;
409 
410 	for(i = BSDSTARTOFTIME; i < clkregs[MC_YEAR]; i++) {
411 		if(is_leap(i))
412 			new_time += 366;
413 		else new_time += 365;
414 	}
415 
416 	msize = is_leap(clkregs[MC_YEAR]) ? ldmsize : dmsize;
417 	for(i = 0; i < (clkregs[MC_MONTH] - 1); i++)
418 		new_time += msize[i];
419 	new_time += clkregs[MC_DOM] - 1;
420 	new_time *= SECS_DAY;
421 	new_time += (clkregs[MC_HOUR] * 3600) + (clkregs[MC_MIN] * 60);
422 	return(new_time + clkregs[MC_SEC]);
423 }
424 
425 static int
426 settod(newtime)
427 u_long	newtime;
428 {
429 	register long	days, rem, year;
430 	register char	*ml;
431 		 int	sps, sec, min, hour, month;
432 	mc_todregs	clkregs;
433 
434 	/* Number of days since Jan. 1 'BSDSTARTOFTIME'	*/
435 	days = newtime / SECS_DAY;
436 	rem  = newtime % SECS_DAY;
437 
438 	/*
439 	 * Calculate sec, min, hour
440 	 */
441 	hour = rem / SECS_HOUR;
442 	rem %= SECS_HOUR;
443 	min  = rem / 60;
444 	sec  = rem % 60;
445 
446 	/*
447 	 * Figure out the year. Day in year is left in 'days'.
448 	 */
449 	year = BSDSTARTOFTIME;
450 	while(days >= (rem = is_leap(year) ? 366 : 365)) {
451 		++year;
452 		days -= rem;
453 	}
454 
455 	/*
456 	 * Determine the month
457 	 */
458 	ml = is_leap(year) ? ldmsize : dmsize;
459 	for(month = 0; days >= ml[month]; ++month)
460 		days -= ml[month];
461 
462 	/*
463 	 * Now that everything is calculated, program the RTC
464 	 */
465 	mc146818_write(RTC, MC_REGA, MC_BASE_32_KHz);
466 	mc146818_write(RTC, MC_REGB, MC_REGB_24HR | MC_REGB_BINARY);
467 	sps = splhigh();
468 	MC146818_GETTOD(RTC, &clkregs);
469 	clkregs[MC_SEC]   = sec;
470 	clkregs[MC_MIN]   = min;
471 	clkregs[MC_HOUR]  = hour;
472 	clkregs[MC_DOM]   = days+1;
473 	clkregs[MC_MONTH] = month+1;
474 	clkregs[MC_YEAR]  = year - GEMSTARTOFTIME;
475 	MC146818_PUTTOD(RTC, &clkregs);
476 	splx(sps);
477 
478 	return(1);
479 }
480