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