xref: /original-bsd/sys/hp300/hp300/clock.c (revision 27393bdf)
1 /*
2  * Copyright (c) 1988 University of Utah.
3  * Copyright (c) 1982, 1990, 1993
4  *	The Regents of the University of California.  All rights reserved.
5  *
6  * This code is derived from software contributed to Berkeley by
7  * the Systems Programming Group of the University of Utah Computer
8  * Science Department.
9  *
10  * %sccs.include.redist.c%
11  *
12  * from: Utah $Hdr: clock.c 1.18 91/01/21$
13  *
14  *	@(#)clock.c	8.3 (Berkeley) 01/09/95
15  */
16 
17 /*
18  * HPs use the MC6840 PTM with the following arrangement:
19  *	Timers 1 and 3 are externally driver from a 25Mhz source.
20  *	Output from timer 3 is tied to the input of timer 2.
21  * The latter makes it possible to use timers 3 and 2 together to get
22  * a 32-bit countdown timer.
23  */
24 
25 #include <sys/param.h>
26 #include <sys/kernel.h>
27 #include <hp/dev/hilreg.h>
28 #include <hp300/hp300/clockreg.h>
29 
30 #include <machine/psl.h>
31 #include <machine/cpu.h>
32 
33 #ifdef GPROF
34 #include <sys/gmon.h>
35 #endif
36 
37 int    clkstd[1];
38 
39 static int clkint;		/* clock interval, as loaded */
40 /*
41  * Statistics clock interval and variance, in usec.  Variance must be a
42  * power of two.  Since this gives us an even number, not an odd number,
43  * we discard one case and compensate.  That is, a variance of 1024 would
44  * give us offsets in [0..1023].  Instead, we take offsets in [1..1023].
45  * This is symmetric about the point 512, or statvar/2, and thus averages
46  * to that value (assuming uniform random numbers).
47  */
48 static int statvar = 1024 / 4;	/* {stat,prof}clock variance */
49 static int statmin;		/* statclock interval - variance/2 */
50 static int profmin;		/* profclock interval - variance/2 */
51 static int timer3min;		/* current, from above choices */
52 static int statprev;		/* previous value in stat timer */
53 
54 static int month_days[12] = {
55 	31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31
56 };
57 struct bbc_tm *gmt_to_bbc();
58 u_char bbc_registers[13];
59 u_char write_bbc_reg(), read_bbc_reg();
60 struct hil_dev *bbcaddr = NULL;
61 
62 /*
63  * Machine-dependent clock routines.
64  *
65  * A note on the real-time clock:
66  * We actually load the clock with interval-1 instead of interval.
67  * This is because the counter decrements to zero after N+1 enabled clock
68  * periods where N is the value loaded into the counter.
69  *
70  * The frequencies of the HP300 clocks must be a multiple of four
71  * microseconds (since the clock counts in 4 us units).
72  */
73 #define	COUNTS_PER_SEC	(1000000 / CLK_RESOLUTION)
74 
75 /*
76  * Set up the real-time and statistics clocks.  Leave stathz 0 only if
77  * no alternative timer is available.
78  *
79  */
80 cpu_initclocks()
81 {
82 	register volatile struct clkreg *clk;
83 	register int intvl, statint, profint, minint;
84 
85 	clkstd[0] = IIOV(0x5F8000);		/* XXX grot */
86 	clk = (volatile struct clkreg *)clkstd[0];
87 
88 	if (COUNTS_PER_SEC % hz) {
89 		printf("cannot get %d Hz clock; using 100 Hz\n", hz);
90 		hz = 100;
91 	}
92 	/*
93 	 * Clock has several counters, so we can always use separate
94 	 * statclock.
95 	 */
96 	if (stathz == 0)		/* XXX should be set in param.c */
97 		stathz = hz;
98 	else if (COUNTS_PER_SEC % stathz) {
99 		printf("cannot get %d Hz statclock; using 100 Hz\n", stathz);
100 		stathz = 100;
101 	}
102 	if (profhz == 0)		/* XXX should be set in param.c */
103 		profhz = stathz * 5;
104 	else if (profhz < stathz || COUNTS_PER_SEC % profhz) {
105 		printf("cannot get %d Hz profclock; using %d Hz\n",
106 		    profhz, stathz);
107 		profhz = stathz;
108 	}
109 
110 	intvl = COUNTS_PER_SEC / hz;
111 	statint = COUNTS_PER_SEC / stathz;
112 	profint = COUNTS_PER_SEC / profhz;
113 	minint = statint / 2 + 100;
114 	while (statvar > minint)
115 		statvar >>= 1;
116 
117 	tick = intvl * CLK_RESOLUTION;
118 
119 	/* adjust interval counts, per note above */
120 	intvl--;
121 	statint--;
122 	profint--;
123 
124 	/* calculate base reload values */
125 	clkint = intvl;
126 	statmin = statint - (statvar >> 1);
127 	profmin = profint - (statvar >> 1);
128 	timer3min = statmin;
129 	statprev = statint;
130 
131 	/* finally, load hardware */
132 	clk->clk_cr2 = CLK_CR1;
133 	clk->clk_cr1 = CLK_RESET;
134 	asm volatile(" movpw %0,%1@(5)" : : "d" (intvl), "a" (clk));
135 	asm volatile(" movpw %0,%1@(9)" : : "d" (0), "a" (clk));
136 	asm volatile(" movpw %0,%1@(13)" : : "d" (statint), "a" (clk));
137 	clk->clk_cr2 = CLK_CR1;
138 	clk->clk_cr1 = CLK_IENAB;
139 	clk->clk_cr2 = CLK_CR3;
140 	clk->clk_cr3 = CLK_IENAB;
141 }
142 
143 /*
144  * We assume newhz is either stathz or profhz, and that neither will
145  * change after being set up above.  Could recalculate intervals here
146  * but that would be a drag.
147  */
148 void
149 setstatclockrate(newhz)
150 	int newhz;
151 {
152 
153 	if (newhz == stathz)
154 		timer3min = statmin;
155 	else
156 		timer3min = profmin;
157 }
158 
159 /*
160  * Statistics/profiling clock interrupt.  Compute a new interval.
161  * Interrupt has already been cleared.
162  *
163  * DO THIS INLINE IN locore.s?
164  */
165 void
166 statintr(fp)
167 	struct clockframe *fp;
168 {
169 	register volatile struct clkreg *clk;
170 	register int newint, r, var;
171 
172 	clk = (volatile struct clkreg *)clkstd[0];
173 	var = statvar;
174 	do {
175 		r = random() & (var - 1);
176 	} while (r == 0);
177 	newint = timer3min + r;
178 
179 	/*
180 	 * The timer was automatically reloaded with the previous latch
181 	 * value at the time of the interrupt.  Compensate now for the
182 	 * amount of time that has run off since then (minimum of 2-12
183 	 * timer ticks depending on CPU type) plus one tick roundoff.
184 	 * This should keep us closer to the mean.
185 	 */
186 	asm volatile(" clrl %0; movpw %1@(13),%0" : "=d" (r) : "a" (clk));
187 	newint -= (statprev - r + 1);
188 
189 	asm volatile(" movpw %0,%1@(13)" : : "d" (newint), "a" (clk));
190 	statprev = newint;
191 	statclock(fp);
192 }
193 
194 /*
195  * Return the best possible estimate of the current time.
196  */
197 void
198 microtime(tvp)
199 	register struct timeval *tvp;
200 {
201 	register volatile struct clkreg *clk;
202 	register int s, u, t, u2, s2;
203 
204 	/*
205 	 * Read registers from slowest-changing to fastest-changing,
206 	 * then re-read out to slowest.  If the values read before the
207 	 * innermost match those read after, the innermost value is
208 	 * consistent with the outer values.  If not, it may not be and
209 	 * we must retry.  Typically this loop runs only once; occasionally
210 	 * it runs twice, and only rarely does it run longer.
211 	 *
212 	 * (Using this loop avoids the need to block interrupts.)
213 	 */
214 	clk = (volatile struct clkreg *)clkstd[0];
215 	do {
216 		s = time.tv_sec;
217 		u = time.tv_usec;
218 		asm volatile (" clrl %0; movpw %1@(5),%0"
219 			      : "=d" (t) : "a" (clk));
220 		u2 = time.tv_usec;
221 		s2 = time.tv_sec;
222 	} while (u != u2 || s != s2);
223 
224 	u += (clkint - t) * CLK_RESOLUTION;
225 	if (u >= 1000000) {		/* normalize */
226 		s++;
227 		u -= 1000000;
228 	}
229 	tvp->tv_sec = s;
230 	tvp->tv_usec = u;
231 }
232 
233 /*
234  * Initialize the time of day register, based on the time base which is, e.g.
235  * from a filesystem.
236  */
237 inittodr(base)
238 	time_t base;
239 {
240 	u_long timbuf = base;	/* assume no battery clock exists */
241 	static int bbcinited = 0;
242 
243 	/* XXX */
244 	if (!bbcinited) {
245 		if (badbaddr(&BBCADDR->hil_stat))
246 			printf("WARNING: no battery clock\n");
247 		else
248 			bbcaddr = BBCADDR;
249 		bbcinited = 1;
250 	}
251 
252 	/*
253 	 * bbc_to_gmt converts and stores the gmt in timbuf.
254 	 * If an error is detected in bbc_to_gmt, or if the filesystem
255 	 * time is more recent than the gmt time in the clock,
256 	 * then use the filesystem time and warn the user.
257  	 */
258 	if (!bbc_to_gmt(&timbuf) || timbuf < base) {
259 		printf("WARNING: bad date in battery clock\n");
260 		timbuf = base;
261 	}
262 	if (base < 5*SECYR) {
263 		printf("WARNING: preposterous time in file system");
264 		timbuf = 6*SECYR + 186*SECDAY + SECDAY/2;
265 		printf(" -- CHECK AND RESET THE DATE!\n");
266 	}
267 
268 	/* Battery clock does not store usec's, so forget about it. */
269 	time.tv_sec = timbuf;
270 }
271 
272 /*
273  * Restore the time of day hardware after a time change.
274  */
275 resettodr()
276 {
277 	register int i;
278 	register struct bbc_tm *tmptr;
279 
280 	tmptr = gmt_to_bbc(time.tv_sec);
281 
282 	decimal_to_bbc(0, 1,  tmptr->tm_sec);
283 	decimal_to_bbc(2, 3,  tmptr->tm_min);
284 	decimal_to_bbc(4, 5,  tmptr->tm_hour);
285 	decimal_to_bbc(7, 8,  tmptr->tm_mday);
286 	decimal_to_bbc(9, 10, tmptr->tm_mon);
287 	decimal_to_bbc(11, 12, tmptr->tm_year);
288 
289 	/* Some bogusness to deal with seemingly broken hardware. Nonsense */
290 	bbc_registers[5] = ((tmptr->tm_hour / 10) & 0x03) + 8;
291 
292 	write_bbc_reg(15, 13);	/* reset prescalar */
293 
294 	for (i = 0; i <= NUM_BBC_REGS; i++)
295 	  	if (bbc_registers[i] != write_bbc_reg(i, bbc_registers[i])) {
296 			printf("Cannot set battery backed clock\n");
297 			break;
298 		}
299 }
300 
301 struct bbc_tm *
302 gmt_to_bbc(tim)
303 	long tim;
304 {
305 	register int i;
306 	register long hms, day;
307 	static struct bbc_tm rt;
308 
309 	day = tim / SECDAY;
310 	hms = tim % SECDAY;
311 
312 	/* Hours, minutes, seconds are easy */
313 	rt.tm_hour = hms / 3600;
314 	rt.tm_min  = (hms % 3600) / 60;
315 	rt.tm_sec  = (hms % 3600) % 60;
316 
317 	/* Number of years in days */
318 	for (i = STARTOFTIME - 1900; day >= days_in_year(i); i++)
319 	  	day -= days_in_year(i);
320 	rt.tm_year = i;
321 
322 	/* Number of months in days left */
323 	if (leapyear(rt.tm_year))
324 		days_in_month(FEBRUARY) = 29;
325 	for (i = 1; day >= days_in_month(i); i++)
326 		day -= days_in_month(i);
327 	days_in_month(FEBRUARY) = 28;
328 	rt.tm_mon = i;
329 
330 	/* Days are what is left over (+1) from all that. */
331 	rt.tm_mday = day + 1;
332 
333 	return(&rt);
334 }
335 
336 bbc_to_gmt(timbuf)
337 	u_long *timbuf;
338 {
339 	register int i;
340 	register u_long tmp;
341 	int year, month, day, hour, min, sec;
342 
343 	read_bbc();
344 
345 	sec = bbc_to_decimal(1, 0);
346 	min = bbc_to_decimal(3, 2);
347 
348 	/*
349 	 * Hours are different for some reason. Makes no sense really.
350 	 */
351 	hour  = ((bbc_registers[5] & 0x03) * 10) + bbc_registers[4];
352 	day   = bbc_to_decimal(8, 7);
353 	month = bbc_to_decimal(10, 9);
354 	year  = bbc_to_decimal(12, 11) + 1900;
355 
356 	range_test(hour, 0, 23);
357 	range_test(day, 1, 31);
358 	range_test(month, 1, 12);
359 	range_test(year, STARTOFTIME, 2000);
360 
361 	tmp = 0;
362 
363 	for (i = STARTOFTIME; i < year; i++)
364 		tmp += days_in_year(i);
365 	if (leapyear(year) && month > FEBRUARY)
366 		tmp++;
367 
368 	for (i = 1; i < month; i++)
369 	  	tmp += days_in_month(i);
370 
371 	tmp += (day - 1);
372 	tmp = ((tmp * 24 + hour) * 60 + min) * 60 + sec;
373 
374 	*timbuf = tmp;
375 	return(1);
376 }
377 
378 read_bbc()
379 {
380   	register int i, read_okay;
381 
382 	read_okay = 0;
383 	while (!read_okay) {
384 		read_okay = 1;
385 		for (i = 0; i <= NUM_BBC_REGS; i++)
386 			bbc_registers[i] = read_bbc_reg(i);
387 		for (i = 0; i <= NUM_BBC_REGS; i++)
388 			if (bbc_registers[i] != read_bbc_reg(i))
389 				read_okay = 0;
390 	}
391 }
392 
393 u_char
394 read_bbc_reg(reg)
395 	int reg;
396 {
397 	u_char data = reg;
398 
399 	if (bbcaddr) {
400 		send_hil_cmd(bbcaddr, BBC_SET_REG, &data, 1, NULL);
401 		send_hil_cmd(bbcaddr, BBC_READ_REG, NULL, 0, &data);
402 	}
403 	return(data);
404 }
405 
406 u_char
407 write_bbc_reg(reg, data)
408 	int reg;
409 	u_int data;
410 {
411 	u_char tmp;
412 
413 	tmp = (u_char) ((data << HIL_SSHIFT) | reg);
414 
415 	if (bbcaddr) {
416 		send_hil_cmd(bbcaddr, BBC_SET_REG, &tmp, 1, NULL);
417 		send_hil_cmd(bbcaddr, BBC_WRITE_REG, NULL, 0, NULL);
418 		send_hil_cmd(bbcaddr, BBC_READ_REG, NULL, 0, &tmp);
419 	}
420 	return(tmp);
421 }
422