xref: /original-bsd/sys/hp300/hp300/clock.c (revision 13ec26c3)
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.12 (Berkeley) 06/20/92
15  */
16 
17 #include "param.h"
18 #include "kernel.h"
19 #include "hp/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/vnode.h"
129 #include "sys/specdev.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,
244 			   (vm_offset_t)addr, PAGE_SIZE);
245 	return(rv == KERN_SUCCESS ? 0 : EINVAL);
246 }
247 
248 startclock()
249 {
250 	register struct clkreg *clk = (struct clkreg *)clkstd[0];
251 
252 	clk->clk_msb2 = -1; clk->clk_lsb2 = -1;
253 	clk->clk_msb3 = -1; clk->clk_lsb3 = -1;
254 
255 	clk->clk_cr2 = CLK_CR3;
256 	clk->clk_cr3 = CLK_OENAB|CLK_8BIT;
257 	clk->clk_cr2 = CLK_CR1;
258 	clk->clk_cr1 = CLK_IENAB;
259 }
260 
261 stopclock()
262 {
263 	register struct clkreg *clk = (struct clkreg *)clkstd[0];
264 
265 	clk->clk_cr2 = CLK_CR3;
266 	clk->clk_cr3 = 0;
267 	clk->clk_cr2 = CLK_CR1;
268 	clk->clk_cr1 = CLK_IENAB;
269 }
270 #endif
271 
272 #ifdef PROFTIMER
273 /*
274  * This code allows the hp300 kernel to use one of the extra timers on
275  * the clock chip for profiling, instead of the regular system timer.
276  * The advantage of this is that the profiling timer can be turned up to
277  * a higher interrupt rate, giving finer resolution timing. The profclock
278  * routine is called from the lev6intr in locore, and is a specialized
279  * routine that calls addupc. The overhead then is far less than if
280  * hardclock/softclock was called. Further, the context switch code in
281  * locore has been changed to turn the profile clock on/off when switching
282  * into/out of a process that is profiling (startprofclock/stopprofclock).
283  * This reduces the impact of the profiling clock on other users, and might
284  * possibly increase the accuracy of the profiling.
285  */
286 int  profint   = PRF_INTERVAL;	/* Clock ticks between interrupts */
287 int  profscale = 0;		/* Scale factor from sys clock to prof clock */
288 char profon    = 0;		/* Is profiling clock on? */
289 
290 /* profon values - do not change, locore.s assumes these values */
291 #define PRF_NONE	0x00
292 #define	PRF_USER	0x01
293 #define	PRF_KERNEL	0x80
294 
295 initprofclock(profprocs)
296 	int profprocs;
297 {
298 	register struct clkreg *clk = (struct clkreg *)clkstd[0];
299 
300 	/*
301 	 * The profile interrupt interval must be an even divisor
302 	 * of the CLK_INTERVAL so that scaling from a system clock
303 	 * tick to a profile clock tick is possible using integer math.
304 	 */
305 	if (profint > CLK_INTERVAL || (CLK_INTERVAL % profint) != 0)
306 		profint = CLK_INTERVAL;
307 	profscale = CLK_INTERVAL / profint;
308 	profhz = hz * profscale;
309 	/*
310 	 * If a process maps the clock, we fail silently.
311 	 * Unfortunately, this gets reflected back to the user not as
312 	 * an error but as a lack of results.
313 	 */
314 	if (clockon)
315 		return;
316 	switch (profprocs) {
317 	case 1:
318 		/* start clock */
319 		clk->clk_msb3 = (profint-1) >> 8 & 0xFF;
320 		clk->clk_lsb3 = (profint-1) & 0xFF;
321 		clk->clk_cr2 = CLK_CR3;
322 		clk->clk_cr3 = CLK_IENAB;
323 		break;
324 	case 0:
325 		/* stop clock */
326 		clk->clk_cr2 = CLK_CR3;
327 		clk->clk_cr3 = 0;
328 		break;
329 	}
330 }
331 
332 #ifdef GPROF
333 /*
334  * profclock() is expanded in line in lev6intr() unless profiling kernel.
335  * Assumes it is called with clock interrupts blocked.
336  */
337 profclock(pc, ps)
338 	caddr_t pc;
339 	int ps;
340 {
341 	/*
342 	 * Came from user mode.
343 	 * If this process is being profiled record the tick.
344 	 */
345 	if (USERMODE(ps)) {
346 		if (curproc->p_stats->p_prof.pr_scale)
347 			addupc(pc, &curproc->p_stats->p_prof, 1);
348 	}
349 	/*
350 	 * Came from kernel (supervisor) mode.
351 	 * If we are profiling the kernel, record the tick.
352 	 */
353 	else if (profiling < 2) {
354 		register int s = pc - s_lowpc;
355 
356 		if (s < s_textsize)
357 			kcount[s / (HISTFRACTION * sizeof (*kcount))]++;
358 	}
359 	/*
360 	 * Kernel profiling was on but has been disabled.
361 	 * Mark as no longer profiling kernel and if all profiling done,
362 	 * disable the clock.
363 	 */
364 	if (profiling && (profon & PRF_KERNEL)) {
365 		profon &= ~PRF_KERNEL;
366 		if (profon == PRF_NONE)
367 			stopprofclock();
368 	}
369 }
370 #endif
371 #endif
372 
373 /*
374  * Initialize the time of day register, based on the time base which is, e.g.
375  * from a filesystem.
376  */
377 inittodr(base)
378 	time_t base;
379 {
380 	u_long timbuf = base;	/* assume no battery clock exists */
381 	static int bbcinited = 0;
382 
383 	/* XXX */
384 	if (!bbcinited) {
385 		if (badbaddr(&BBCADDR->hil_stat))
386 			printf("WARNING: no battery clock\n");
387 		else
388 			bbcaddr = BBCADDR;
389 		bbcinited = 1;
390 	}
391 
392 	/*
393 	 * bbc_to_gmt converts and stores the gmt in timbuf.
394 	 * If an error is detected in bbc_to_gmt, or if the filesystem
395 	 * time is more recent than the gmt time in the clock,
396 	 * then use the filesystem time and warn the user.
397  	 */
398 	if (!bbc_to_gmt(&timbuf) || timbuf < base) {
399 		printf("WARNING: bad date in battery clock\n");
400 		timbuf = base;
401 	}
402 	if (base < 5*SECYR) {
403 		printf("WARNING: preposterous time in file system");
404 		timbuf = 6*SECYR + 186*SECDAY + SECDAY/2;
405 		printf(" -- CHECK AND RESET THE DATE!\n");
406 	}
407 
408 	/* Battery clock does not store usec's, so forget about it. */
409 	time.tv_sec = timbuf;
410 }
411 
412 resettodr()
413 {
414 	register int i;
415 	register struct bbc_tm *tmptr;
416 
417 	tmptr = gmt_to_bbc(time.tv_sec);
418 
419 	decimal_to_bbc(0, 1,  tmptr->tm_sec);
420 	decimal_to_bbc(2, 3,  tmptr->tm_min);
421 	decimal_to_bbc(4, 5,  tmptr->tm_hour);
422 	decimal_to_bbc(7, 8,  tmptr->tm_mday);
423 	decimal_to_bbc(9, 10, tmptr->tm_mon);
424 	decimal_to_bbc(11, 12, tmptr->tm_year);
425 
426 	/* Some bogusness to deal with seemingly broken hardware. Nonsense */
427 	bbc_registers[5] = ((tmptr->tm_hour / 10) & 0x03) + 8;
428 
429 	write_bbc_reg(15, 13);	/* reset prescalar */
430 
431 	for (i = 0; i <= NUM_BBC_REGS; i++)
432 	  	if (bbc_registers[i] != write_bbc_reg(i, bbc_registers[i])) {
433 			printf("Cannot set battery backed clock\n");
434 			break;
435 		}
436 }
437 
438 struct bbc_tm *
439 gmt_to_bbc(tim)
440 	long tim;
441 {
442 	register int i;
443 	register long hms, day;
444 	static struct bbc_tm rt;
445 
446 	day = tim / SECDAY;
447 	hms = tim % SECDAY;
448 
449 	/* Hours, minutes, seconds are easy */
450 	rt.tm_hour = hms / 3600;
451 	rt.tm_min  = (hms % 3600) / 60;
452 	rt.tm_sec  = (hms % 3600) % 60;
453 
454 	/* Number of years in days */
455 	for (i = STARTOFTIME - 1900; day >= days_in_year(i); i++)
456 	  	day -= days_in_year(i);
457 	rt.tm_year = i;
458 
459 	/* Number of months in days left */
460 	if (leapyear(rt.tm_year))
461 		days_in_month(FEBRUARY) = 29;
462 	for (i = 1; day >= days_in_month(i); i++)
463 		day -= days_in_month(i);
464 	days_in_month(FEBRUARY) = 28;
465 	rt.tm_mon = i;
466 
467 	/* Days are what is left over (+1) from all that. */
468 	rt.tm_mday = day + 1;
469 
470 	return(&rt);
471 }
472 
473 bbc_to_gmt(timbuf)
474 	u_long *timbuf;
475 {
476 	register int i;
477 	register u_long tmp;
478 	int year, month, day, hour, min, sec;
479 
480 	read_bbc();
481 
482 	sec = bbc_to_decimal(1, 0);
483 	min = bbc_to_decimal(3, 2);
484 
485 	/*
486 	 * Hours are different for some reason. Makes no sense really.
487 	 */
488 	hour  = ((bbc_registers[5] & 0x03) * 10) + bbc_registers[4];
489 	day   = bbc_to_decimal(8, 7);
490 	month = bbc_to_decimal(10, 9);
491 	year  = bbc_to_decimal(12, 11) + 1900;
492 
493 	range_test(hour, 0, 23);
494 	range_test(day, 1, 31);
495 	range_test(month, 1, 12);
496 	range_test(year, STARTOFTIME, 2000);
497 
498 	tmp = 0;
499 
500 	for (i = STARTOFTIME; i < year; i++)
501 		tmp += days_in_year(i);
502 	if (leapyear(year) && month > FEBRUARY)
503 		tmp++;
504 
505 	for (i = 1; i < month; i++)
506 	  	tmp += days_in_month(i);
507 
508 	tmp += (day - 1);
509 	tmp = ((tmp * 24 + hour) * 60 + min) * 60 + sec;
510 
511 	*timbuf = tmp;
512 	return(1);
513 }
514 
515 read_bbc()
516 {
517   	register int i, read_okay;
518 
519 	read_okay = 0;
520 	while (!read_okay) {
521 		read_okay = 1;
522 		for (i = 0; i <= NUM_BBC_REGS; i++)
523 			bbc_registers[i] = read_bbc_reg(i);
524 		for (i = 0; i <= NUM_BBC_REGS; i++)
525 			if (bbc_registers[i] != read_bbc_reg(i))
526 				read_okay = 0;
527 	}
528 }
529 
530 u_char
531 read_bbc_reg(reg)
532 	int reg;
533 {
534 	u_char data = reg;
535 
536 	if (bbcaddr) {
537 		send_hil_cmd(bbcaddr, BBC_SET_REG, &data, 1, NULL);
538 		send_hil_cmd(bbcaddr, BBC_READ_REG, NULL, 0, &data);
539 	}
540 	return(data);
541 }
542 
543 u_char
544 write_bbc_reg(reg, data)
545 	u_int data;
546 {
547 	u_char tmp;
548 
549 	tmp = (u_char) ((data << HIL_SSHIFT) | reg);
550 
551 	if (bbcaddr) {
552 		send_hil_cmd(bbcaddr, BBC_SET_REG, &tmp, 1, NULL);
553 		send_hil_cmd(bbcaddr, BBC_WRITE_REG, NULL, 0, NULL);
554 		send_hil_cmd(bbcaddr, BBC_READ_REG, NULL, 0, &tmp);
555 	}
556 	return(tmp);
557 }
558