xref: /original-bsd/sys/hp300/hp300/clock.c (revision 30e23803)
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.17 89/11/30$
13  *
14  *	@(#)clock.c	7.2 (Berkeley) 06/22/90
15  */
16 
17 #include "param.h"
18 #include "user.h"
19 #include "kernel.h"
20 #include "../hpdev/hilreg.h"
21 #include "clockreg.h"
22 
23 #include "machine/psl.h"
24 #include "machine/cpu.h"
25 
26 #if defined(GPROF) && defined(PROFTIMER)
27 #include "gprof.h"
28 #endif
29 
30 int    clkstd[] = { IOV(0x5F8000) };
31 
32 static int month_days[12] = {
33 	31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31
34 };
35 struct bbc_tm *gmt_to_bbc();
36 u_char bbc_registers[13];
37 u_char write_bbc_reg(), read_bbc_reg();
38 struct hil_dev *bbcaddr = NULL;
39 
40 /*
41  * Machine-dependent clock routines.
42  *
43  * Startrtclock restarts the real-time clock, which provides
44  * hardclock interrupts to kern_clock.c.
45  *
46  * Inittodr initializes the time of day hardware which provides
47  * date functions.
48  *
49  * Resettodr restores the time of day hardware after a time change.
50  *
51  * A note on the real-time clock:
52  * We actually load the clock with CLK_INTERVAL-1 instead of CLK_INTERVAL.
53  * This is because the counter decrements to zero after N+1 enabled clock
54  * periods where N is the value loaded into the counter.
55  */
56 
57 /*
58  * Start the real-time clock.
59  */
60 startrtclock()
61 {
62 	register struct clkreg *clk = (struct clkreg *)clkstd[0];
63 
64 	clk->clk_cr2 = CLK_CR1;
65 	clk->clk_cr1 = CLK_RESET;
66 	clk->clk_cr2 = CLK_CR3;
67 	clk->clk_cr3 = 0;
68 	clk->clk_msb1 = (CLK_INTERVAL-1) >> 8 & 0xFF;
69 	clk->clk_lsb1 = (CLK_INTERVAL-1) & 0xFF;
70 	clk->clk_msb2 = 0;
71 	clk->clk_lsb2 = 0;
72 	clk->clk_msb3 = 0;
73 	clk->clk_lsb3 = 0;
74 	clk->clk_cr2 = CLK_CR1;
75 	clk->clk_cr1 = CLK_IENAB;
76 }
77 
78 /*
79  * Returns number of usec since last recorded clock "tick"
80  * (i.e. clock interrupt).
81  */
82 clkread()
83 {
84 	register struct clkreg *clk = (struct clkreg *) clkstd[0];
85 	register int high, low;
86 
87 	high = clk->clk_msb1;
88 	low = clk->clk_lsb1;
89 	if (high != clk->clk_msb1)
90 		high = clk->clk_msb1;
91 
92 	high = (CLK_INTERVAL-1) - ((high << 8) | low);
93 	/*
94 	 * Pending interrupt indicates that the counter has wrapped
95 	 * since we went to splhigh().  Need to compensate.
96 	 */
97 	if (clk->clk_sr & CLK_INT1)
98 		high += CLK_INTERVAL;
99 	return((high * tick) / CLK_INTERVAL);
100 }
101 
102 #include "clock.h"
103 #if NCLOCK > 0
104 /*
105  * /dev/clock: mappable high resolution timer.
106  *
107  * This code implements a 32-bit recycling counter (with a 4 usec period)
108  * using timers 2 & 3 on the 6840 clock chip.  The counter can be mapped
109  * RO into a user's address space to achieve low overhead (no system calls),
110  * high-precision timing.
111  *
112  * Note that timer 3 is also used for the high precision profiling timer
113  * (PROFTIMER code above).  Care should be taken when both uses are
114  * configured as only a token effort is made to avoid conflicting use.
115  */
116 #include "proc.h"
117 #include "ioctl.h"
118 #include "mapmem.h"
119 #include "malloc.h"
120 #include "clockioctl.h"
121 
122 int clockon = 0;		/* non-zero if high-res timer enabled */
123 #ifdef PROFTIMER
124 int  profprocs = 0;		/* # of procs using profiling timer */
125 #endif
126 #ifdef DEBUG
127 int clockdebug = 0;
128 #endif
129 
130 /*ARGSUSED*/
131 clockopen(dev, flags)
132 	dev_t dev;
133 {
134 #ifdef PROFTIMER
135 #ifdef GPROF
136 	/*
137 	 * Kernel profiling enabled, give up.
138 	 */
139 	if (profiling)
140 		return(EBUSY);
141 #endif
142 	/*
143 	 * If any user processes are profiling, give up.
144 	 */
145 	if (profprocs)
146 		return(EBUSY);
147 #endif
148 	if (!clockon) {
149 		startclock();
150 		clockon++;
151 	}
152 	return(0);
153 }
154 
155 /*ARGSUSED*/
156 clockclose(dev, flags)
157 	dev_t dev;
158 {
159 #ifdef MAPMEM
160 	(void) clockunmmap(dev, (caddr_t)0);
161 #endif
162 	stopclock();
163 	clockon = 0;
164 	return(0);
165 }
166 
167 /*ARGSUSED*/
168 clockioctl(dev, cmd, data, flag)
169 	dev_t dev;
170 	caddr_t data;
171 {
172 	int error = 0;
173 
174 	switch (cmd) {
175 
176 #ifdef MAPMEM
177 	case CLOCKMAP:
178 		error = clockmmap(dev, (caddr_t *)data);
179 		break;
180 
181 	case CLOCKUNMAP:
182 		error = clockunmmap(dev, *(caddr_t *)data);
183 		break;
184 
185 	case CLOCKGETRES:
186 		*(int *)data = CLK_RESOLUTION;
187 		break;
188 #endif
189 
190 	default:
191 		error = EINVAL;
192 		break;
193 	}
194 	return(error);
195 }
196 
197 /*ARGSUSED*/
198 clockmap(dev, off, prot)
199 	dev_t dev;
200 {
201 #ifdef MMAP
202 	return((off + (IOBASE+CLKSR-1)) >> PGSHIFT);
203 #endif
204 }
205 
206 #ifdef MAPMEM
207 
208 struct mapmemops clockops = {
209 	(int (*)())0, (int (*)())0, (int (*)())0, (int (*)())0
210 };
211 
212 clockmmap(dev, addrp)
213 	dev_t dev;
214 	caddr_t *addrp;
215 {
216 	struct proc *p = u.u_procp;		/* XXX */
217 	struct mapmem *mp;
218 	int id, error, clockmapin();
219 
220 	id = minor(dev);	/* XXX */
221 	error = mmalloc(p, id, addrp, NBPG, MM_RO|MM_CI|MM_NOCORE,
222 			&clockops, &mp);
223 #ifdef DEBUG
224 	if (clockdebug)
225 		printf("clockmmap(%d): addr %x\n", p->p_pid, *addrp);
226 #endif
227 	if (error == 0)
228 		if (error = mmmapin(p, mp, clockmapin))
229 			(void) mmfree(p, mp);
230 	return(error);
231 }
232 
233 clockunmmap(dev, addr)
234 	dev_t dev;
235 	caddr_t addr;
236 {
237 	struct proc *p = u.u_procp;		/* XXX */
238 	register struct mapmem *mp, **mpp;
239 	int found, id;
240 
241 #ifdef DEBUG
242 	if (clockdebug)
243 		printf("clockunmmap(%d): addr %x\n", p->p_pid, addr);
244 #endif
245 	id = minor(dev);	/* XXX */
246 	found = 0;
247 	mpp = &u.u_mmap;
248 	for (mp = *mpp; mp; mp = *mpp) {
249 		if (mp->mm_id != id || mp->mm_ops != &clockops) {
250 			mpp = &mp->mm_next;
251 			continue;
252 		}
253 		if (addr &&
254 		    (addr < mp->mm_uva || addr >= mp->mm_uva+mp->mm_size)) {
255 			mpp = &mp->mm_next;
256 			continue;
257 		}
258 		mmmapout(p, mp);
259 		(void) mmfree(p, mp);
260 		found++;
261 	}
262 	return(found ? 0 : EINVAL);
263 }
264 
265 /*ARGSUSED*/
266 clockmapin(mp, off)
267 	struct mapmem *mp;
268 {
269 	return((off + (IOBASE+CLKSR-1)) >> PGSHIFT);
270 }
271 #endif
272 
273 startclock()
274 {
275 	register struct clkreg *clk = (struct clkreg *)clkstd[0];
276 
277 	clk->clk_msb2 = -1; clk->clk_lsb2 = -1;
278 	clk->clk_msb3 = -1; clk->clk_lsb3 = -1;
279 
280 	clk->clk_cr2 = CLK_CR3;
281 	clk->clk_cr3 = CLK_OENAB|CLK_8BIT;
282 	clk->clk_cr2 = CLK_CR1;
283 	clk->clk_cr1 = CLK_IENAB;
284 }
285 
286 stopclock()
287 {
288 	register struct clkreg *clk = (struct clkreg *)clkstd[0];
289 
290 	clk->clk_cr2 = CLK_CR3;
291 	clk->clk_cr3 = 0;
292 	clk->clk_cr2 = CLK_CR1;
293 	clk->clk_cr1 = CLK_IENAB;
294 }
295 #endif
296 
297 #ifdef PROFTIMER
298 /*
299  * This code allows the hp300 kernel to use one of the extra timers on
300  * the clock chip for profiling, instead of the regular system timer.
301  * The advantage of this is that the profiling timer can be turned up to
302  * a higher interrupt rate, giving finer resolution timing. The profclock
303  * routine is called from the lev6intr in locore, and is a specialized
304  * routine that calls addupc. The overhead then is far less than if
305  * hardclock/softclock was called. Further, the context switch code in
306  * locore has been changed to turn the profile clock on/off when switching
307  * into/out of a process that is profiling (startprofclock/stopprofclock).
308  * This reduces the impact of the profiling clock on other users, and might
309  * possibly increase the accuracy of the profiling.
310  */
311 int  profint   = PRF_INTERVAL;	/* Clock ticks between interrupts */
312 int  profscale = 0;		/* Scale factor from sys clock to prof clock */
313 char profon    = 0;		/* Is profiling clock on? */
314 
315 /* profon values - do not change, locore.s assumes these values */
316 #define PRF_NONE	0x00
317 #define	PRF_USER	0x01
318 #define	PRF_KERNEL	0x80
319 
320 initprofclock()
321 {
322 #if NCLOCK > 0
323 	/*
324 	 * If the high-res timer is running, force profiling off.
325 	 * Unfortunately, this gets reflected back to the user not as
326 	 * an error but as a lack of results.
327 	 */
328 	if (clockon) {
329 		u.u_prof.pr_scale = 0;
330 		return;
331 	}
332 	/*
333 	 * Keep track of the number of user processes that are profiling
334 	 * by checking the scale value.
335 	 *
336 	 * XXX: this all assumes that the profiling code is well behaved;
337 	 * i.e. profil() is called once per process with pcscale non-zero
338 	 * to turn it on, and once with pcscale zero to turn it off.
339 	 * Also assumes you don't do any forks or execs.  Oh well, there
340 	 * is always adb...
341 	 */
342 	if (u.u_prof.pr_scale)
343 		profprocs++;
344 	else
345 		profprocs--;
346 #endif
347 	/*
348 	 * The profile interrupt interval must be an even divisor
349 	 * of the CLK_INTERVAL so that scaling from a system clock
350 	 * tick to a profile clock tick is possible using integer math.
351 	 */
352 	if (profint > CLK_INTERVAL || (CLK_INTERVAL % profint) != 0)
353 		profint = CLK_INTERVAL;
354 	profscale = CLK_INTERVAL / profint;
355 }
356 
357 startprofclock()
358 {
359 	register struct clkreg *clk = (struct clkreg *)clkstd[0];
360 
361 	clk->clk_msb3 = (profint-1) >> 8 & 0xFF;
362 	clk->clk_lsb3 = (profint-1) & 0xFF;
363 
364 	clk->clk_cr2 = CLK_CR3;
365 	clk->clk_cr3 = CLK_IENAB;
366 }
367 
368 stopprofclock()
369 {
370 	register struct clkreg *clk = (struct clkreg *)clkstd[0];
371 
372 	clk->clk_cr2 = CLK_CR3;
373 	clk->clk_cr3 = 0;
374 }
375 
376 #ifdef GPROF
377 /*
378  * profclock() is expanded in line in lev6intr() unless profiling kernel.
379  * Assumes it is called with clock interrupts blocked.
380  */
381 profclock(pc, ps)
382 	caddr_t pc;
383 	int ps;
384 {
385 	/*
386 	 * Came from user mode.
387 	 * If this process is being profiled record the tick.
388 	 */
389 	if (USERMODE(ps)) {
390 		if (u.u_prof.pr_scale)
391 			addupc(pc, &u.u_prof, 1);
392 	}
393 	/*
394 	 * Came from kernel (supervisor) mode.
395 	 * If we are profiling the kernel, record the tick.
396 	 */
397 	else if (profiling < 2) {
398 		register int s = pc - s_lowpc;
399 
400 		if (s < s_textsize)
401 			kcount[s / (HISTFRACTION * sizeof (*kcount))]++;
402 	}
403 	/*
404 	 * Kernel profiling was on but has been disabled.
405 	 * Mark as no longer profiling kernel and if all profiling done,
406 	 * disable the clock.
407 	 */
408 	if (profiling && (profon & PRF_KERNEL)) {
409 		profon &= ~PRF_KERNEL;
410 		if (profon == PRF_NONE)
411 			stopprofclock();
412 	}
413 }
414 #endif
415 #endif
416 
417 /*
418  * Initialize the time of day register, based on the time base which is, e.g.
419  * from a filesystem.
420  */
421 inittodr(base)
422 	time_t base;
423 {
424 	u_long timbuf = base;	/* assume no battery clock exists */
425 	static int bbcinited = 0;
426 
427 	/* XXX */
428 	if (!bbcinited) {
429 		if (badbaddr(&BBCADDR->hil_stat))
430 			printf("WARNING: no battery clock\n");
431 		else
432 			bbcaddr = BBCADDR;
433 		bbcinited = 1;
434 	}
435 
436 	/*
437 	 * bbc_to_gmt converts and stores the gmt in timbuf.
438 	 * If an error is detected in bbc_to_gmt, or if the filesystem
439 	 * time is more recent than the gmt time in the clock,
440 	 * then use the filesystem time and warn the user.
441  	 */
442 	if (!bbc_to_gmt(&timbuf) || timbuf < base) {
443 		printf("WARNING: bad date in battery clock\n");
444 		timbuf = base;
445 	}
446 	if (base < 5*SECYR) {
447 		printf("WARNING: preposterous time in file system");
448 		timbuf = 6*SECYR + 186*SECDAY + SECDAY/2;
449 		printf(" -- CHECK AND RESET THE DATE!\n");
450 	}
451 
452 	/* Battery clock does not store usec's, so forget about it. */
453 	time.tv_sec = timbuf;
454 }
455 
456 resettodr()
457 {
458 	register int i;
459 	register struct bbc_tm *tmptr;
460 
461 	tmptr = gmt_to_bbc(time.tv_sec);
462 
463 	decimal_to_bbc(0, 1,  tmptr->tm_sec);
464 	decimal_to_bbc(2, 3,  tmptr->tm_min);
465 	decimal_to_bbc(4, 5,  tmptr->tm_hour);
466 	decimal_to_bbc(7, 8,  tmptr->tm_mday);
467 	decimal_to_bbc(9, 10, tmptr->tm_mon);
468 	decimal_to_bbc(11, 12, tmptr->tm_year);
469 
470 	/* Some bogusness to deal with seemingly broken hardware. Nonsense */
471 	bbc_registers[5] = ((tmptr->tm_hour / 10) & 0x03) + 8;
472 
473 	write_bbc_reg(15, 13);	/* reset prescalar */
474 
475 	for (i = 0; i <= NUM_BBC_REGS; i++)
476 	  	if (bbc_registers[i] != write_bbc_reg(i, bbc_registers[i])) {
477 			printf("Cannot set battery backed clock\n");
478 			break;
479 		}
480 }
481 
482 struct bbc_tm *
483 gmt_to_bbc(tim)
484 	long tim;
485 {
486 	register int i;
487 	register long hms, day;
488 	static struct bbc_tm rt;
489 
490 	day = tim / SECDAY;
491 	hms = tim % SECDAY;
492 
493 	/* Hours, minutes, seconds are easy */
494 	rt.tm_hour = hms / 3600;
495 	rt.tm_min  = (hms % 3600) / 60;
496 	rt.tm_sec  = (hms % 3600) % 60;
497 
498 	/* Number of years in days */
499 	for (i = STARTOFTIME - 1900; day >= days_in_year(i); i++)
500 	  	day -= days_in_year(i);
501 	rt.tm_year = i;
502 
503 	/* Number of months in days left */
504 	if (leapyear(rt.tm_year))
505 		days_in_month(FEBRUARY) = 29;
506 	for (i = 1; day >= days_in_month(i); i++)
507 		day -= days_in_month(i);
508 	days_in_month(FEBRUARY) = 28;
509 	rt.tm_mon = i;
510 
511 	/* Days are what is left over (+1) from all that. */
512 	rt.tm_mday = day + 1;
513 
514 	return(&rt);
515 }
516 
517 bbc_to_gmt(timbuf)
518 	u_long *timbuf;
519 {
520 	register int i;
521 	register u_long tmp;
522 	int year, month, day, hour, min, sec;
523 
524 	read_bbc();
525 
526 	sec = bbc_to_decimal(1, 0);
527 	min = bbc_to_decimal(3, 2);
528 
529 	/*
530 	 * Hours are different for some reason. Makes no sense really.
531 	 */
532 	hour  = ((bbc_registers[5] & 0x03) * 10) + bbc_registers[4];
533 	day   = bbc_to_decimal(8, 7);
534 	month = bbc_to_decimal(10, 9);
535 	year  = bbc_to_decimal(12, 11) + 1900;
536 
537 	range_test(hour, 0, 23);
538 	range_test(day, 1, 31);
539 	range_test(month, 1, 12);
540 	range_test(year, STARTOFTIME, 2000);
541 
542 	tmp = 0;
543 
544 	for (i = STARTOFTIME; i < year; i++)
545 		tmp += days_in_year(i);
546 	if (leapyear(year) && month > FEBRUARY)
547 		tmp++;
548 
549 	for (i = 1; i < month; i++)
550 	  	tmp += days_in_month(i);
551 
552 	tmp += (day - 1);
553 	tmp = ((tmp * 24 + hour) * 60 + min) * 60 + sec;
554 
555 	*timbuf = tmp;
556 	return(1);
557 }
558 
559 read_bbc()
560 {
561   	register int i, read_okay;
562 
563 	read_okay = 0;
564 	while (!read_okay) {
565 		read_okay = 1;
566 		for (i = 0; i <= NUM_BBC_REGS; i++)
567 			bbc_registers[i] = read_bbc_reg(i);
568 		for (i = 0; i <= NUM_BBC_REGS; i++)
569 			if (bbc_registers[i] != read_bbc_reg(i))
570 				read_okay = 0;
571 	}
572 }
573 
574 u_char
575 read_bbc_reg(reg)
576 	int reg;
577 {
578 	u_char data = reg;
579 
580 	if (bbcaddr) {
581 		send_hil_cmd(bbcaddr, BBC_SET_REG, &data, 1, NULL);
582 		send_hil_cmd(bbcaddr, BBC_READ_REG, NULL, 0, &data);
583 	}
584 	return(data);
585 }
586 
587 u_char
588 write_bbc_reg(reg, data)
589 	u_int data;
590 {
591 	u_char tmp;
592 
593 	tmp = (u_char) ((data << HIL_SSHIFT) | reg);
594 
595 	if (bbcaddr) {
596 		send_hil_cmd(bbcaddr, BBC_SET_REG, &tmp, 1, NULL);
597 		send_hil_cmd(bbcaddr, BBC_WRITE_REG, NULL, 0, NULL);
598 		send_hil_cmd(bbcaddr, BBC_READ_REG, NULL, 0, &tmp);
599 	}
600 	return(tmp);
601 }
602