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