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