xref: /original-bsd/sys/sparc/sparc/clock.c (revision 3efa343e)
1 /*
2  * Copyright (c) 1992 The Regents of the University of California.
3  * All rights reserved.
4  *
5  * This software was developed by the Computer Systems Engineering group
6  * at Lawrence Berkeley Laboratory under DARPA contract BG 91-66 and
7  * contributed to Berkeley.
8  *
9  * %sccs.include.redist.c%
10  *
11  *	@(#)clock.c	7.1 (Berkeley) 07/13/92
12  *
13  * from: $Header: clock.c,v 1.14 92/07/07 05:34:08 leres Exp $ (LBL)
14  */
15 
16 /*
17  * Clock driver.  This is the id prom (``eeprom'') driver as well
18  * and includes the timer register functions too.
19  */
20 
21 #include "sys/param.h"
22 #include "sys/kernel.h"
23 #include "sys/device.h"
24 #include "sys/proc.h"
25 #include "sys/resourcevar.h"
26 #ifdef GPROF
27 #include "sys/gmon.h"
28 #endif
29 
30 #include "vm/vm.h"
31 
32 #include "machine/autoconf.h"
33 #ifdef notdef
34 #include "machine/psl.h"
35 #endif
36 
37 #include "clockreg.h"
38 #include "intreg.h"
39 #include "timerreg.h"
40 
41 /*
42  * Statistics clock interval and variance, in usec.  Variance must be a
43  * power of two.  Since this gives us an even number, not an odd number,
44  * we discard one case and compensate.  That is, a variance of 1024 would
45  * give us offsets in [0..1023].  Instead, we take offsets in [1..1023].
46  * This is symmetric about the point 512, or statvar/2, and thus averages
47  * to that value (assuming uniform random numbers).
48  */
49 static int statvar = 1024;
50 static int statmin;		/* statclock interval - 1/2*variance */
51 
52 static void clockattach __P((struct device *, struct device *, void *));
53 struct cfdriver clockcd =
54     { NULL, "eeprom", matchbyname, clockattach,
55       DV_DULL, sizeof(struct device) };
56 
57 static void timerattach __P((struct device *, struct device *, void *));
58 struct cfdriver timercd =
59     { NULL, "counter-timer", matchbyname, timerattach, DV_DULL,
60       sizeof(struct device) };
61 
62 /* ARGSUSED */
63 static void
64 clockattach(parent, self, aux)
65 	struct device *parent, *self;
66 	void *aux;
67 {
68 	register int h;
69 	register struct clockreg *cl;
70 	struct romaux *ra = aux;
71 
72 	printf(": %s\n", getpropstring(ra->ra_node, "model"));
73 	/*
74 	 * We ignore any existing virtual address as we need to map
75 	 * this read-only and make it read-write only temporarily,
76 	 * whenever we read or write the clock chip.  The clock also
77 	 * contains the ID ``PROM'', and I have already had the pleasure
78 	 * of reloading the cpu type, Ethernet address, etc, by hand from
79 	 * the console FORTH interpreter.  I intend not to enjoy it again.
80 	 */
81 	cl = (struct clockreg *)mapiodev(ra->ra_paddr, sizeof *clockreg);
82 	pmap_changeprot(kernel_pmap, (vm_offset_t)clockreg, VM_PROT_READ, 1);
83 	h = cl->cl_idprom.id_machine << 24;
84 	h |= cl->cl_idprom.id_hostid[0] << 16;
85 	h |= cl->cl_idprom.id_hostid[1] << 8;
86 	h |= cl->cl_idprom.id_hostid[0];
87 	hostid = h;
88 	clockreg = cl;
89 }
90 
91 /* ARGSUSED */
92 static void
93 timerattach(parent, self, aux)
94 	struct device *parent, *self;
95 	void *aux;
96 {
97 	register struct romaux *ra = aux;
98 
99 	printf("\n");
100 	/*
101 	 * This time, we ignore any existing virtual address because
102 	 * we have a fixed virtual address for the timer, to make
103 	 * microtime() faster.
104 	 */
105 	(void)mapdev(ra->ra_paddr, TIMERREG_VA, sizeof(struct timerreg));
106 	/* should link interrupt handlers here, rather than compiled-in? */
107 }
108 
109 /*
110  * Write en/dis-able clock registers.  We coordinate so that several
111  * writers can run simultaneously.
112  */
113 void
114 clk_wenable(onoff)
115 	int onoff;
116 {
117 	register int s;
118 	register vm_prot_t prot;/* nonzero => change prot */
119 	static int writers;
120 
121 	s = splhigh();
122 	if (onoff)
123 		prot = writers++ == 0 ? VM_PROT_READ|VM_PROT_WRITE : 0;
124 	else
125 		prot = --writers == 0 ? VM_PROT_READ : 0;
126 	splx(s);
127 	if (prot)
128 		pmap_changeprot(kernel_pmap, (vm_offset_t)clockreg, prot, 1);
129 }
130 
131 /*
132  * XXX this belongs elsewhere
133  */
134 void
135 myetheraddr(cp)
136 	u_char *cp;
137 {
138 	register struct clockreg *cl = clockreg;
139 
140 	cp[0] = cl->cl_idprom.id_ether[0];
141 	cp[1] = cl->cl_idprom.id_ether[1];
142 	cp[2] = cl->cl_idprom.id_ether[2];
143 	cp[3] = cl->cl_idprom.id_ether[3];
144 	cp[4] = cl->cl_idprom.id_ether[4];
145 	cp[5] = cl->cl_idprom.id_ether[5];
146 }
147 
148 /*
149  * Delay: wait for `about' n microseconds to pass.
150  * This is easy to do on the SparcStation since we have
151  * freerunning microsecond timers -- no need to guess at
152  * cpu speed factors.  We just wait for it to change n times
153  * (if we calculated a limit, we might overshoot, and precision
154  * is irrelevant here---we want less object code).
155  */
156 delay(n)
157 	register int n;
158 {
159 	register int c, t;
160 
161 	if (timercd.cd_ndevs == 0)
162 		panic("delay");
163 	c = TIMERREG->t_c10.t_counter;
164 	while (--n >= 0) {
165 		while ((t = TIMERREG->t_c10.t_counter) == c)
166 			continue;
167 		c = t;
168 	}
169 }
170 
171 /*
172  * Set up the real-time and statistics clocks.  Leave stathz 0 only if
173  * no alternative timer is available.
174  *
175  * The frequencies of these clocks must be an even number of microseconds.
176  */
177 cpu_initclocks()
178 {
179 	register int statint, minint;
180 
181 	if (1000000 % hz) {
182 		printf("cannot get %d Hz clock; using 100 Hz\n", hz);
183 		hz = 100;
184 		tick = 1000000 / hz;
185 	}
186 	if (stathz == 0)
187 		stathz = hz;
188 	if (1000000 % stathz) {
189 		printf("cannot get %d Hz statclock; using 100 Hz\n", stathz);
190 		stathz = 100;
191 	}
192 	profhz = stathz;		/* always */
193 
194 	statint = 1000000 / stathz;
195 	minint = statint / 2 + 100;
196 	while (statvar > minint)
197 		statvar >>= 1;
198 	TIMERREG->t_c10.t_limit = tmr_ustolim(tick);
199 	TIMERREG->t_c14.t_limit = tmr_ustolim(statint);
200 	statmin = statint - (statvar >> 1);
201 	ienab_bis(IE_L14 | IE_L10);
202 }
203 
204 /*
205  * Dummy setstatclockrate(), since we know profhz==hz.
206  */
207 /* ARGSUSED */
208 void
209 setstatclockrate(newhz)
210 	int newhz;
211 {
212 	/* nothing */
213 }
214 
215 /*
216  * Level 10 (clock) interrupts.  If we are using the FORTH PROM for
217  * console input, we need to check for that here as well, and generate
218  * a software interrupt to read it.
219  */
220 int
221 clockintr(cap)
222 	void *cap;
223 {
224 	register int discard;
225 	extern int rom_console_input;
226 
227 	/* read the limit register to clear the interrupt */
228 	discard = TIMERREG->t_c10.t_limit;
229 	hardclock((struct clockframe *)cap);
230 	if (rom_console_input && cnrom())
231 		setsoftint();
232 
233 	return (1);
234 }
235 
236 /*
237  * Level 14 (stat clock) interrupt handler.
238  */
239 int
240 statintr(cap)
241 	void *cap;
242 {
243 	register int discard;
244 	register u_long newint, r, var;
245 
246 	/* read the limit register to clear the interrupt */
247 	discard = TIMERREG->t_c14.t_limit;
248 	statclock((struct clockframe *)cap);
249 
250 	/*
251 	 * Compute new randomized interval.  The intervals are uniformly
252 	 * distributed on [statint - statvar / 2, statint + statvar / 2],
253 	 * and therefore have mean statint, giving a stathz frequency clock.
254 	 */
255 	var = statvar;
256 	do {
257 		r = random() & (var - 1);
258 	} while (r == 0);
259 	newint = statmin + r;
260 
261 	TIMERREG->t_c14.t_limit = tmr_ustolim(newint);
262 	return (1);
263 }
264 
265 /*
266  * BCD to decimal and decimal to BCD.
267  */
268 #define	FROMBCD(x)	(((x) >> 4) * 10 + ((x) & 0xf))
269 #define	TOBCD(x)	(((x) / 10 * 16) + ((x) % 10))
270 
271 #define	SECDAY		(24 * 60 * 60)
272 #define	SECYR		(SECDAY * 365)
273 #define	LEAPYEAR(y)	(((y) & 3) == 0)
274 
275 /*
276  * This code is defunct after 2068.
277  * Will Unix still be here then??
278  */
279 const short dayyr[12] =
280     { 0, 31, 59, 90, 120, 151, 181, 212, 243, 273, 304, 334 };
281 
282 chiptotime(sec, min, hour, day, mon, year)
283 	register int sec, min, hour, day, mon, year;
284 {
285 	register int days, yr;
286 
287 	sec = FROMBCD(sec);
288 	min = FROMBCD(min);
289 	hour = FROMBCD(hour);
290 	day = FROMBCD(day);
291 	mon = FROMBCD(mon);
292 	year = FROMBCD(year) + YEAR0;
293 
294 	/* simple sanity checks */
295 	if (year < 70 || mon < 1 || mon > 12 || day < 1 || day > 31)
296 		return (0);
297 	days = 0;
298 	for (yr = 70; yr < year; yr++)
299 		days += LEAPYEAR(yr) ? 366 : 365;
300 	days += dayyr[mon - 1] + day - 1;
301 	if (LEAPYEAR(yr) && mon > 2)
302 		days++;
303 	/* now have days since Jan 1, 1970; the rest is easy... */
304 	return (days * SECDAY + hour * 3600 + min * 60 + sec);
305 }
306 
307 struct chiptime {
308 	int	sec;
309 	int	min;
310 	int	hour;
311 	int	wday;
312 	int	day;
313 	int	mon;
314 	int	year;
315 };
316 
317 timetochip(c)
318 	register struct chiptime *c;
319 {
320 	register int t, t2, t3, now = time.tv_sec;
321 
322 	/* compute the year */
323 	t2 = now / SECDAY;
324 	t3 = (t2 + 2) % 7;	/* day of week */
325 	c->wday = TOBCD(t3 + 1);
326 
327 	t = 69;
328 	while (t2 >= 0) {	/* whittle off years */
329 		t3 = t2;
330 		t++;
331 		t2 -= LEAPYEAR(t) ? 366 : 365;
332 	}
333 	c->year = t;
334 
335 	/* t3 = month + day; separate */
336 	t = LEAPYEAR(t);
337 	for (t2 = 1; t2 < 12; t2++)
338 		if (t3 < dayyr[t2] + (t && t2 > 1))
339 			break;
340 
341 	/* t2 is month */
342 	c->mon = t2;
343 	c->day = t3 - dayyr[t2 - 1] + 1;
344 	if (t && t2 > 2)
345 		c->day--;
346 
347 	/* the rest is easy */
348 	t = now % SECDAY;
349 	c->hour = t / 3600;
350 	t %= 3600;
351 	c->min = t / 60;
352 	c->sec = t % 60;
353 
354 	c->sec = TOBCD(c->sec);
355 	c->min = TOBCD(c->min);
356 	c->hour = TOBCD(c->hour);
357 	c->day = TOBCD(c->day);
358 	c->mon = TOBCD(c->mon);
359 	c->year = TOBCD(c->year - YEAR0);
360 }
361 
362 /*
363  * Set up the system's time, given a `reasonable' time value.
364  */
365 inittodr(base)
366 	time_t base;
367 {
368 	register struct clockreg *cl = clockreg;
369 	int sec, min, hour, day, mon, year;
370 	int badbase = 0;
371 
372 	if (base < 5 * SECYR) {
373 		printf("WARNING: preposterous time in file system\n");
374 		/* not going to use it anyway, if the chip is readable */
375 		base = 21*SECYR + 186*SECDAY + SECDAY/2;
376 		badbase = 1;
377 	}
378 	clk_wenable(1);
379 	cl->cl_csr |= CLK_READ;		/* enable read (stop time) */
380 	sec = cl->cl_sec;
381 	min = cl->cl_min;
382 	hour = cl->cl_hour;
383 	day = cl->cl_mday;
384 	mon = cl->cl_month;
385 	year = cl->cl_year;
386 	cl->cl_csr &= ~CLK_READ;	/* time wears on */
387 	clk_wenable(0);
388 	if ((time.tv_sec = chiptotime(sec, min, hour, day, mon, year)) == 0) {
389 		printf("WARNING: bad date in battery clock");
390 		/*
391 		 * Believe the time in the file system for lack of
392 		 * anything better, resetting the clock.
393 		 */
394 		time.tv_sec = base;
395 		if (!badbase)
396 			resettodr();
397 	} else {
398 		int deltat = time.tv_sec - base;
399 
400 		if (deltat < 0)
401 			deltat = -deltat;
402 		if (deltat < 2 * SECDAY)
403 			return;
404 		printf("WARNING: clock %s %d days",
405 		    time.tv_sec < base ? "lost" : "gained", deltat / SECDAY);
406 	}
407 	printf(" -- CHECK AND RESET THE DATE!\n");
408 }
409 
410 /*
411  * Reset the clock based on the current time.
412  * Used when the current clock is preposterous, when the time is changed,
413  * and when rebooting.  Do nothing if the time is not yet known, e.g.,
414  * when crashing during autoconfig.
415  */
416 resettodr()
417 {
418 	register struct clockreg *cl;
419 	struct chiptime c;
420 
421 	if (!time.tv_sec || (cl = clockreg) == NULL)
422 		return;
423 	timetochip(&c);
424 	clk_wenable(1);
425 	cl->cl_csr |= CLK_WRITE;	/* enable write */
426 	cl->cl_sec = c.sec;
427 	cl->cl_min = c.min;
428 	cl->cl_hour = c.hour;
429 	cl->cl_wday = c.wday;
430 	cl->cl_mday = c.day;
431 	cl->cl_month = c.mon;
432 	cl->cl_year = c.year;
433 	cl->cl_csr &= ~CLK_WRITE;	/* load them up */
434 	clk_wenable(0);
435 }
436