xref: /original-bsd/sys/hp300/hp300/clock.c (revision 333da485)
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.2 (Berkeley) 01/12/94
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 microtime(tvp)
198 	register struct timeval *tvp;
199 {
200 	register volatile struct clkreg *clk;
201 	register int s, u, t, u2, s2;
202 
203 	/*
204 	 * Read registers from slowest-changing to fastest-changing,
205 	 * then re-read out to slowest.  If the values read before the
206 	 * innermost match those read after, the innermost value is
207 	 * consistent with the outer values.  If not, it may not be and
208 	 * we must retry.  Typically this loop runs only once; occasionally
209 	 * it runs twice, and only rarely does it run longer.
210 	 *
211 	 * (Using this loop avoids the need to block interrupts.)
212 	 */
213 	clk = (volatile struct clkreg *)clkstd[0];
214 	do {
215 		s = time.tv_sec;
216 		u = time.tv_usec;
217 		asm volatile (" clrl %0; movpw %1@(5),%0"
218 			      : "=d" (t) : "a" (clk));
219 		u2 = time.tv_usec;
220 		s2 = time.tv_sec;
221 	} while (u != u2 || s != s2);
222 
223 	u += (clkint - t) * CLK_RESOLUTION;
224 	if (u >= 1000000) {		/* normalize */
225 		s++;
226 		u -= 1000000;
227 	}
228 	tvp->tv_sec = s;
229 	tvp->tv_usec = u;
230 }
231 
232 /*
233  * Initialize the time of day register, based on the time base which is, e.g.
234  * from a filesystem.
235  */
236 inittodr(base)
237 	time_t base;
238 {
239 	u_long timbuf = base;	/* assume no battery clock exists */
240 	static int bbcinited = 0;
241 
242 	/* XXX */
243 	if (!bbcinited) {
244 		if (badbaddr(&BBCADDR->hil_stat))
245 			printf("WARNING: no battery clock\n");
246 		else
247 			bbcaddr = BBCADDR;
248 		bbcinited = 1;
249 	}
250 
251 	/*
252 	 * bbc_to_gmt converts and stores the gmt in timbuf.
253 	 * If an error is detected in bbc_to_gmt, or if the filesystem
254 	 * time is more recent than the gmt time in the clock,
255 	 * then use the filesystem time and warn the user.
256  	 */
257 	if (!bbc_to_gmt(&timbuf) || timbuf < base) {
258 		printf("WARNING: bad date in battery clock\n");
259 		timbuf = base;
260 	}
261 	if (base < 5*SECYR) {
262 		printf("WARNING: preposterous time in file system");
263 		timbuf = 6*SECYR + 186*SECDAY + SECDAY/2;
264 		printf(" -- CHECK AND RESET THE DATE!\n");
265 	}
266 
267 	/* Battery clock does not store usec's, so forget about it. */
268 	time.tv_sec = timbuf;
269 }
270 
271 /*
272  * Restore the time of day hardware after a time change.
273  */
274 resettodr()
275 {
276 	register int i;
277 	register struct bbc_tm *tmptr;
278 
279 	tmptr = gmt_to_bbc(time.tv_sec);
280 
281 	decimal_to_bbc(0, 1,  tmptr->tm_sec);
282 	decimal_to_bbc(2, 3,  tmptr->tm_min);
283 	decimal_to_bbc(4, 5,  tmptr->tm_hour);
284 	decimal_to_bbc(7, 8,  tmptr->tm_mday);
285 	decimal_to_bbc(9, 10, tmptr->tm_mon);
286 	decimal_to_bbc(11, 12, tmptr->tm_year);
287 
288 	/* Some bogusness to deal with seemingly broken hardware. Nonsense */
289 	bbc_registers[5] = ((tmptr->tm_hour / 10) & 0x03) + 8;
290 
291 	write_bbc_reg(15, 13);	/* reset prescalar */
292 
293 	for (i = 0; i <= NUM_BBC_REGS; i++)
294 	  	if (bbc_registers[i] != write_bbc_reg(i, bbc_registers[i])) {
295 			printf("Cannot set battery backed clock\n");
296 			break;
297 		}
298 }
299 
300 struct bbc_tm *
301 gmt_to_bbc(tim)
302 	long tim;
303 {
304 	register int i;
305 	register long hms, day;
306 	static struct bbc_tm rt;
307 
308 	day = tim / SECDAY;
309 	hms = tim % SECDAY;
310 
311 	/* Hours, minutes, seconds are easy */
312 	rt.tm_hour = hms / 3600;
313 	rt.tm_min  = (hms % 3600) / 60;
314 	rt.tm_sec  = (hms % 3600) % 60;
315 
316 	/* Number of years in days */
317 	for (i = STARTOFTIME - 1900; day >= days_in_year(i); i++)
318 	  	day -= days_in_year(i);
319 	rt.tm_year = i;
320 
321 	/* Number of months in days left */
322 	if (leapyear(rt.tm_year))
323 		days_in_month(FEBRUARY) = 29;
324 	for (i = 1; day >= days_in_month(i); i++)
325 		day -= days_in_month(i);
326 	days_in_month(FEBRUARY) = 28;
327 	rt.tm_mon = i;
328 
329 	/* Days are what is left over (+1) from all that. */
330 	rt.tm_mday = day + 1;
331 
332 	return(&rt);
333 }
334 
335 bbc_to_gmt(timbuf)
336 	u_long *timbuf;
337 {
338 	register int i;
339 	register u_long tmp;
340 	int year, month, day, hour, min, sec;
341 
342 	read_bbc();
343 
344 	sec = bbc_to_decimal(1, 0);
345 	min = bbc_to_decimal(3, 2);
346 
347 	/*
348 	 * Hours are different for some reason. Makes no sense really.
349 	 */
350 	hour  = ((bbc_registers[5] & 0x03) * 10) + bbc_registers[4];
351 	day   = bbc_to_decimal(8, 7);
352 	month = bbc_to_decimal(10, 9);
353 	year  = bbc_to_decimal(12, 11) + 1900;
354 
355 	range_test(hour, 0, 23);
356 	range_test(day, 1, 31);
357 	range_test(month, 1, 12);
358 	range_test(year, STARTOFTIME, 2000);
359 
360 	tmp = 0;
361 
362 	for (i = STARTOFTIME; i < year; i++)
363 		tmp += days_in_year(i);
364 	if (leapyear(year) && month > FEBRUARY)
365 		tmp++;
366 
367 	for (i = 1; i < month; i++)
368 	  	tmp += days_in_month(i);
369 
370 	tmp += (day - 1);
371 	tmp = ((tmp * 24 + hour) * 60 + min) * 60 + sec;
372 
373 	*timbuf = tmp;
374 	return(1);
375 }
376 
377 read_bbc()
378 {
379   	register int i, read_okay;
380 
381 	read_okay = 0;
382 	while (!read_okay) {
383 		read_okay = 1;
384 		for (i = 0; i <= NUM_BBC_REGS; i++)
385 			bbc_registers[i] = read_bbc_reg(i);
386 		for (i = 0; i <= NUM_BBC_REGS; i++)
387 			if (bbc_registers[i] != read_bbc_reg(i))
388 				read_okay = 0;
389 	}
390 }
391 
392 u_char
393 read_bbc_reg(reg)
394 	int reg;
395 {
396 	u_char data = reg;
397 
398 	if (bbcaddr) {
399 		send_hil_cmd(bbcaddr, BBC_SET_REG, &data, 1, NULL);
400 		send_hil_cmd(bbcaddr, BBC_READ_REG, NULL, 0, &data);
401 	}
402 	return(data);
403 }
404 
405 u_char
406 write_bbc_reg(reg, data)
407 	int reg;
408 	u_int data;
409 {
410 	u_char tmp;
411 
412 	tmp = (u_char) ((data << HIL_SSHIFT) | reg);
413 
414 	if (bbcaddr) {
415 		send_hil_cmd(bbcaddr, BBC_SET_REG, &tmp, 1, NULL);
416 		send_hil_cmd(bbcaddr, BBC_WRITE_REG, NULL, 0, NULL);
417 		send_hil_cmd(bbcaddr, BBC_READ_REG, NULL, 0, &tmp);
418 	}
419 	return(tmp);
420 }
421