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