include reordering due to vn_if change
[unix-history] / usr / src / sys / hp300 / hp300 / clock.c
CommitLineData
88a7e859
KM
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 *
c5235cef 12 * from: Utah $Hdr: clock.c 1.18 91/01/21$
88a7e859 13 *
8642881d 14 * @(#)clock.c 7.10 (Berkeley) %G%
88a7e859
KM
15 */
16
bc632365
MK
17#include "param.h"
18#include "kernel.h"
b28b3a13 19#include "../dev/hilreg.h"
88a7e859
KM
20#include "clockreg.h"
21
b28b3a13
KB
22#include "../include/psl.h"
23#include "../include/cpu.h"
88a7e859
KM
24
25#if defined(GPROF) && defined(PROFTIMER)
b28b3a13 26#include "sys/gprof.h"
88a7e859
KM
27#endif
28
22d09b27 29int clkstd[1];
07c3e286 30int profhz;
88a7e859
KM
31
32static int month_days[12] = {
33 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31
34};
35struct bbc_tm *gmt_to_bbc();
36u_char bbc_registers[13];
37u_char write_bbc_reg(), read_bbc_reg();
38struct 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 */
60startrtclock()
61{
22d09b27
KM
62 register struct clkreg *clk;
63
c5235cef 64 clkstd[0] = IIOV(0x5F8000);
22d09b27 65 clk = (struct clkreg *) clkstd[0];
88a7e859
KM
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;
07c3e286
KM
79
80 tick = CLK_INTERVAL * CLK_RESOLUTION;
81 hz = 1000000 / (CLK_INTERVAL * CLK_RESOLUTION);
88a7e859
KM
82}
83
84/*
85 * Returns number of usec since last recorded clock "tick"
86 * (i.e. clock interrupt).
87 */
88clkread()
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 */
b28b3a13 122#include "sys/proc.h"
c5235cef 123#include "sys/resourcevar.h"
b28b3a13
KB
124#include "sys/ioctl.h"
125#include "sys/malloc.h"
c5235cef 126#include "vm/vm.h"
88a7e859 127#include "clockioctl.h"
b28b3a13 128#include "sys/vnode.h"
8642881d 129#include "sys/specdev.h"
b28b3a13 130#include "sys/mman.h"
88a7e859
KM
131
132int clockon = 0; /* non-zero if high-res timer enabled */
133#ifdef PROFTIMER
134int profprocs = 0; /* # of procs using profiling timer */
135#endif
136#ifdef DEBUG
137int clockdebug = 0;
138#endif
139
140/*ARGSUSED*/
141clockopen(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*/
166clockclose(dev, flags)
167 dev_t dev;
168{
bc632365 169 (void) clockunmmap(dev, (caddr_t)0, curproc); /* XXX */
88a7e859
KM
170 stopclock();
171 clockon = 0;
172 return(0);
173}
174
175/*ARGSUSED*/
bc632365 176clockioctl(dev, cmd, data, flag, p)
88a7e859
KM
177 dev_t dev;
178 caddr_t data;
bc632365 179 struct proc *p;
88a7e859
KM
180{
181 int error = 0;
182
183 switch (cmd) {
184
88a7e859 185 case CLOCKMAP:
bc632365 186 error = clockmmap(dev, (caddr_t *)data, p);
88a7e859
KM
187 break;
188
189 case CLOCKUNMAP:
bc632365 190 error = clockunmmap(dev, *(caddr_t *)data, p);
88a7e859
KM
191 break;
192
193 case CLOCKGETRES:
194 *(int *)data = CLK_RESOLUTION;
195 break;
88a7e859
KM
196
197 default:
198 error = EINVAL;
199 break;
200 }
201 return(error);
202}
203
204/*ARGSUSED*/
205clockmap(dev, off, prot)
206 dev_t dev;
207{
c5235cef 208 return((off + (INTIOBASE+CLKBASE+CLKSR-1)) >> PGSHIFT);
88a7e859
KM
209}
210
bc632365 211clockmmap(dev, addrp, p)
88a7e859
KM
212 dev_t dev;
213 caddr_t *addrp;
bc632365 214 struct proc *p;
88a7e859 215{
22d09b27
KM
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 */
bc632365 229 error = vm_mmap(&p->p_vmspace->vm_map, (vm_offset_t *)addrp,
22d09b27 230 PAGE_SIZE, VM_PROT_ALL, flags, (caddr_t)&vn, 0);
d0cc9c12 231 return(error);
88a7e859
KM
232}
233
bc632365 234clockunmmap(dev, addr, p)
88a7e859
KM
235 dev_t dev;
236 caddr_t addr;
bc632365 237 struct proc *p;
88a7e859 238{
22d09b27 239 int rv;
88a7e859 240
22d09b27
KM
241 if (addr == 0)
242 return(EINVAL); /* XXX: how do we deal with this? */
6055b25f 243 rv = vm_deallocate(&p->p_vmspace->vm_map, (vm_offset_t)addr, PAGE_SIZE);
22d09b27 244 return(rv == KERN_SUCCESS ? 0 : EINVAL);
88a7e859
KM
245}
246
88a7e859
KM
247startclock()
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
260stopclock()
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 */
285int profint = PRF_INTERVAL; /* Clock ticks between interrupts */
286int profscale = 0; /* Scale factor from sys clock to prof clock */
287char 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
294initprofclock()
295{
296#if NCLOCK > 0
bc632365 297 struct proc *p = curproc; /* XXX */
c5235cef 298
88a7e859
KM
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) {
bc632365 305 p->p_stats->p_prof.pr_scale = 0;
88a7e859
KM
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 */
bc632365 318 if (p->p_stats->p_prof.pr_scale)
88a7e859
KM
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;
07c3e286 331 profhz = hz * profscale;
88a7e859
KM
332}
333
334startprofclock()
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
345stopprofclock()
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 */
358profclock(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)) {
4f0a0b43
KM
367 if (curproc->p_stats->p_prof.pr_scale)
368 addupc(pc, &curproc->p_stats->p_prof, 1);
88a7e859
KM
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 */
398inittodr(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
433resettodr()
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
459struct bbc_tm *
460gmt_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
494bbc_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
536read_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
551u_char
552read_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
564u_char
565write_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}