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