Annotation of sys/arch/hp300/hp300/clock.c, Revision 1.1.1.1
1.1 nbrk 1: /* $OpenBSD: clock.c,v 1.13 2005/12/03 21:36:17 brad Exp $ */
2: /* $NetBSD: clock.c,v 1.20 1997/04/27 20:43:38 thorpej Exp $ */
3:
4: /*
5: * Copyright (c) 1988 University of Utah.
6: * Copyright (c) 1982, 1990, 1993
7: * The Regents of the University of California. All rights reserved.
8: *
9: * This code is derived from software contributed to Berkeley by
10: * the Systems Programming Group of the University of Utah Computer
11: * Science Department.
12: *
13: * Redistribution and use in source and binary forms, with or without
14: * modification, are permitted provided that the following conditions
15: * are met:
16: * 1. Redistributions of source code must retain the above copyright
17: * notice, this list of conditions and the following disclaimer.
18: * 2. Redistributions in binary form must reproduce the above copyright
19: * notice, this list of conditions and the following disclaimer in the
20: * documentation and/or other materials provided with the distribution.
21: * 3. Neither the name of the University nor the names of its contributors
22: * may be used to endorse or promote products derived from this software
23: * without specific prior written permission.
24: *
25: * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
26: * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
27: * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
28: * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
29: * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
30: * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
31: * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
32: * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
33: * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
34: * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
35: * SUCH DAMAGE.
36: *
37: * from: Utah $Hdr: clock.c 1.18 91/01/21$
38: *
39: * @(#)clock.c 8.2 (Berkeley) 1/12/94
40: */
41:
42: /*
43: * HPs use the MC6840 PTM with the following arrangement:
44: * Timers 1 and 3 are externally driver from a 25MHz source.
45: * Output from timer 3 is tied to the input of timer 2.
46: * The latter makes it possible to use timers 3 and 2 together to get
47: * a 32-bit countdown timer.
48: */
49:
50: #include <sys/param.h>
51: #include <sys/systm.h>
52: #include <sys/kernel.h>
53: #include <sys/tty.h>
54: #include <sys/evcount.h>
55:
56: #include <machine/psl.h>
57: #include <machine/cpu.h>
58: #include <machine/hp300spu.h>
59:
60: #include <dev/hil/hilreg.h> /* for BBC */
61: #include <hp300/hp300/clockreg.h>
62:
63: #ifdef GPROF
64: #include <sys/gmon.h>
65: #endif
66:
67: int clkstd[1];
68:
69: static int clkint; /* clock interval, as loaded */
70: /*
71: * Statistics clock interval and variance, in usec. Variance must be a
72: * power of two. Since this gives us an even number, not an odd number,
73: * we discard one case and compensate. That is, a variance of 1024 would
74: * give us offsets in [0..1023]. Instead, we take offsets in [1..1023].
75: * This is symmetric about the point 512, or statvar/2, and thus averages
76: * to that value (assuming uniform random numbers).
77: */
78: static int statvar = 1024 / 4; /* {stat,prof}clock variance */
79: static int statmin; /* statclock interval - variance/2 */
80: static int profmin; /* profclock interval - variance/2 */
81: static int timer3min; /* current, from above choices */
82: static int statprev; /* previous value in stat timer */
83:
84: static int month_days[12] = {
85: 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31
86: };
87: u_char bbc_registers[13];
88: volatile u_int8_t *bbcaddr = NULL;
89:
90: void clockintr(struct clockframe *);
91: void statintr(struct clockframe *);
92:
93: void hp300_calibrate_delay(void);
94: struct bbc_tm *gmt_to_bbc(long);
95: int bbc_to_gmt(u_long *);
96: void read_bbc(void);
97: u_char read_bbc_reg(int);
98: void send_clock_cmd(volatile u_int8_t *, u_int8_t, u_int8_t *,
99: u_int8_t, u_int8_t *);
100: u_char write_bbc_reg(int, u_int);
101:
102: static int clock_ipl = IPL_CLOCK;
103: static int stat_ipl = IPL_STATCLOCK;
104: struct evcount clockcnt;
105: struct evcount statcnt;
106:
107: /*
108: * Machine-dependent clock routines.
109: *
110: * A note on the real-time clock:
111: * We actually load the clock with interval-1 instead of interval.
112: * This is because the counter decrements to zero after N+1 enabled clock
113: * periods where N is the value loaded into the counter.
114: *
115: * The frequencies of the HP300 clocks must be a multiple of four
116: * microseconds (since the clock counts in 4 us units).
117: */
118: #define COUNTS_PER_SEC (1000000 / CLK_RESOLUTION)
119:
120: /*
121: * Calibrate the delay constant, based on Chuck Cranor's
122: * mvme68k delay calibration algorithm.
123: */
124: void
125: hp300_calibrate_delay()
126: {
127: extern int delay_divisor;
128: volatile struct clkreg *clk;
129: volatile u_char csr;
130: int intvl;
131:
132: clkstd[0] = IIOV(0x5F8000); /* XXX yuck */
133: clk = (volatile struct clkreg *)clkstd[0];
134:
135: /*
136: * Calibrate delay() using the 4 usec counter.
137: * We adjust delay_divisor until we get the result we want.
138: * We assume we've been called at splhigh().
139: */
140: for (delay_divisor = 140; delay_divisor > 1; delay_divisor--) {
141: /* Reset clock chip */
142: clk->clk_cr2 = CLK_CR1;
143: clk->clk_cr1 = CLK_RESET;
144:
145: /*
146: * Prime the timer. We're looking for
147: * 10,000 usec (10ms). See interval comment
148: * above.
149: */
150: intvl = (10000 / CLK_RESOLUTION) - 1;
151: asm volatile(" movpw %0,%1@(5)" : : "d" (intvl), "a" (clk));
152:
153: /* Enable the timer */
154: clk->clk_cr2 = CLK_CR1;
155: clk->clk_cr1 = CLK_IENAB;
156:
157: delay(10000);
158:
159: /* Timer1 interrupt flag high? */
160: csr = clk->clk_sr;
161: if (csr & CLK_INT1) {
162: /*
163: * Got it. Clear interrupt and get outta here.
164: */
165: asm volatile(" movpw %0@(5),%1" : :
166: "a" (clk), "d" (intvl));
167: break;
168: }
169:
170: /*
171: * Nope. Poll for completion of the interval,
172: * clear interrupt, and try again.
173: */
174: do {
175: csr = clk->clk_sr;
176: } while ((csr & CLK_INT1) == 0);
177:
178: asm volatile(" movpw %0@(5),%1" : : "a" (clk), "d" (intvl));
179: }
180:
181: /*
182: * Make sure the clock interrupt is disabled. Otherwise,
183: * we can end up calling hardclock() before proc0 is set up,
184: * causing a bad pointer deref.
185: */
186: clk->clk_cr2 = CLK_CR1;
187: clk->clk_cr1 = CLK_RESET;
188:
189: /*
190: * Sanity check the delay_divisor value. If we totally lost,
191: * assume a 50MHz CPU;
192: */
193: if (delay_divisor == 0)
194: delay_divisor = 2048 / 50;
195:
196: /* Calculate CPU speed. */
197: cpuspeed = 2048 / delay_divisor;
198: }
199:
200: /*
201: * Set up the real-time and statistics clocks. Leave stathz 0 only if
202: * no alternative timer is available.
203: */
204: void
205: cpu_initclocks()
206: {
207: volatile struct clkreg *clk;
208: int intvl, statint, profint, minint;
209:
210: clkstd[0] = IIOV(0x5F8000); /* XXX grot */
211: clk = (volatile struct clkreg *)clkstd[0];
212:
213: if (COUNTS_PER_SEC % hz) {
214: printf("cannot get %d Hz clock; using 100 Hz\n", hz);
215: hz = 100;
216: }
217: /*
218: * Clock has several counters, so we can always use separate
219: * statclock.
220: */
221: if (stathz == 0) /* XXX should be set in param.c */
222: stathz = hz;
223: else if (COUNTS_PER_SEC % stathz) {
224: printf("cannot get %d Hz statclock; using 100 Hz\n", stathz);
225: stathz = 100;
226: }
227: if (profhz == 0) /* XXX should be set in param.c */
228: profhz = stathz * 5;
229: else if (profhz < stathz || COUNTS_PER_SEC % profhz) {
230: printf("cannot get %d Hz profclock; using %d Hz\n",
231: profhz, stathz);
232: profhz = stathz;
233: }
234:
235: intvl = COUNTS_PER_SEC / hz;
236: statint = COUNTS_PER_SEC / stathz;
237: profint = COUNTS_PER_SEC / profhz;
238: minint = statint / 2 + 100;
239: while (statvar > minint)
240: statvar >>= 1;
241:
242: tick = intvl * CLK_RESOLUTION;
243:
244: /* adjust interval counts, per note above */
245: intvl--;
246: statint--;
247: profint--;
248:
249: /* calculate base reload values */
250: clkint = intvl;
251: statmin = statint - (statvar >> 1);
252: profmin = profint - (statvar >> 1);
253: timer3min = statmin;
254: statprev = statint;
255:
256: evcount_attach(&statcnt, "stat", &stat_ipl, &evcount_intr);
257: evcount_attach(&clockcnt, "clock", &clock_ipl, &evcount_intr);
258:
259: /* finally, load hardware */
260: clk->clk_cr2 = CLK_CR1;
261: clk->clk_cr1 = CLK_RESET;
262: asm volatile(" movpw %0,%1@(5)" : : "d" (intvl), "a" (clk));
263: asm volatile(" movpw %0,%1@(9)" : : "d" (0), "a" (clk));
264: asm volatile(" movpw %0,%1@(13)" : : "d" (statint), "a" (clk));
265: clk->clk_cr2 = CLK_CR1;
266: clk->clk_cr1 = CLK_IENAB;
267: clk->clk_cr2 = CLK_CR3;
268: clk->clk_cr3 = CLK_IENAB;
269: }
270:
271: /*
272: * We assume newhz is either stathz or profhz, and that neither will
273: * change after being set up above. Could recalculate intervals here
274: * but that would be a drag.
275: */
276: void
277: setstatclockrate(newhz)
278: int newhz;
279: {
280:
281: if (newhz == stathz)
282: timer3min = statmin;
283: else
284: timer3min = profmin;
285: }
286:
287: /*
288: * Timer clock interrupt.
289: */
290: void
291: clockintr(fp)
292: struct clockframe *fp;
293: {
294: clockcnt.ec_count++;
295: hardclock(fp);
296: }
297:
298: /*
299: * Statistics/profiling clock interrupt. Compute a new interval.
300: * Interrupt has already been cleared.
301: */
302: void
303: statintr(fp)
304: struct clockframe *fp;
305: {
306: volatile struct clkreg *clk;
307: int newint, r, var;
308:
309: clk = (volatile struct clkreg *)clkstd[0];
310: var = statvar;
311: do {
312: r = random() & (var - 1);
313: } while (r == 0);
314: newint = timer3min + r;
315:
316: /*
317: * The timer was automatically reloaded with the previous latch
318: * value at the time of the interrupt. Compensate now for the
319: * amount of time that has run off since then (minimum of 2-12
320: * timer ticks depending on CPU type) plus one tick roundoff.
321: * This should keep us closer to the mean.
322: */
323: asm volatile(" clrl %0; movpw %1@(13),%0" : "=d" (r) : "a" (clk));
324: newint -= (statprev - r + 1);
325:
326: asm volatile(" movpw %0,%1@(13)" : : "d" (newint), "a" (clk));
327: statprev = newint;
328: statcnt.ec_count++;
329: statclock(fp);
330: }
331:
332: /*
333: * Return the best possible estimate of the current time.
334: */
335: void
336: microtime(tvp)
337: struct timeval *tvp;
338: {
339: volatile struct clkreg *clk;
340: int s, u, t, u2, s2;
341:
342: /*
343: * Read registers from slowest-changing to fastest-changing,
344: * then re-read out to slowest. If the values read before the
345: * innermost match those read after, the innermost value is
346: * consistent with the outer values. If not, it may not be and
347: * we must retry. Typically this loop runs only once; occasionally
348: * it runs twice, and only rarely does it run longer.
349: *
350: * (Using this loop avoids the need to block interrupts.)
351: */
352: clk = (volatile struct clkreg *)clkstd[0];
353: do {
354: s = time.tv_sec;
355: u = time.tv_usec;
356: asm volatile (" clrl %0; movpw %1@(5),%0"
357: : "=d" (t) : "a" (clk));
358: u2 = time.tv_usec;
359: s2 = time.tv_sec;
360: } while (u != u2 || s != s2);
361:
362: u += (clkint - t) * CLK_RESOLUTION;
363: if (u >= 1000000) { /* normalize */
364: s++;
365: u -= 1000000;
366: }
367: tvp->tv_sec = s;
368: tvp->tv_usec = u;
369: }
370:
371: /*
372: * Initialize the time of day register, based on the time base which is, e.g.
373: * from a filesystem.
374: */
375: void
376: inittodr(base)
377: time_t base;
378: {
379: u_long timbuf = base; /* assume no battery clock exists */
380: static int bbcinited = 0;
381:
382: /* XXX */
383: if (!bbcinited) {
384: if (machineid == HP_425 && mmuid == MMUID_425_E)
385: bbcaddr = NULL;
386: else {
387: if (badbaddr((caddr_t)(BBCADDR + HILP_STAT)))
388: printf("WARNING: no battery clock\n");
389: else
390: bbcaddr = BBCADDR;
391: }
392: bbcinited = 1;
393: }
394:
395: /*
396: * bbc_to_gmt converts and stores the gmt in timbuf.
397: * If an error is detected in bbc_to_gmt, or if the filesystem
398: * time is more recent than the gmt time in the clock,
399: * then use the filesystem time and warn the user.
400: */
401: if (bbcaddr != NULL) {
402: if (!bbc_to_gmt(&timbuf) || timbuf < base) {
403: printf("WARNING: bad date in battery clock\n");
404: timbuf = base;
405: }
406: }
407: if (base < 5*SECYR) {
408: printf("WARNING: preposterous time in file system");
409: timbuf = 6*SECYR + 186*SECDAY + SECDAY/2;
410: printf(" -- CHECK AND RESET THE DATE!\n");
411: }
412:
413: /* Battery clock does not store usec's, so forget about it. */
414: time.tv_sec = timbuf;
415: }
416:
417: /*
418: * Restore the time of day hardware after a time change.
419: */
420: void
421: resettodr()
422: {
423: int i;
424: struct bbc_tm *tmptr;
425:
426: if (bbcaddr == NULL)
427: return;
428:
429: tmptr = gmt_to_bbc(time.tv_sec);
430:
431: decimal_to_bbc(0, 1, tmptr->tm_sec);
432: decimal_to_bbc(2, 3, tmptr->tm_min);
433: decimal_to_bbc(4, 5, tmptr->tm_hour);
434: decimal_to_bbc(7, 8, tmptr->tm_mday);
435: decimal_to_bbc(9, 10, tmptr->tm_mon);
436: decimal_to_bbc(11, 12, tmptr->tm_year);
437:
438: /* Some bogusness to deal with seemingly broken hardware. Nonsense */
439: bbc_registers[5] = ((tmptr->tm_hour / 10) & 0x03) + 8;
440:
441: write_bbc_reg(15, 13); /* reset prescalar */
442:
443: for (i = 0; i <= NUM_BBC_REGS; i++)
444: if (bbc_registers[i] != write_bbc_reg(i, bbc_registers[i])) {
445: printf("Cannot set battery backed clock\n");
446: break;
447: }
448: }
449:
450: struct bbc_tm *
451: gmt_to_bbc(tim)
452: long tim;
453: {
454: int i;
455: long hms, day;
456: static struct bbc_tm rt;
457:
458: day = tim / SECDAY;
459: hms = tim % SECDAY;
460:
461: /* Hours, minutes, seconds are easy */
462: rt.tm_hour = hms / 3600;
463: rt.tm_min = (hms % 3600) / 60;
464: rt.tm_sec = (hms % 3600) % 60;
465:
466: /* Number of years in days */
467: for (i = STARTOFTIME - 1900; day >= days_in_year(i); i++)
468: day -= days_in_year(i);
469: rt.tm_year = i;
470:
471: /* Number of months in days left */
472: if (leapyear(rt.tm_year))
473: days_in_month(FEBRUARY) = 29;
474: for (i = 1; day >= days_in_month(i); i++)
475: day -= days_in_month(i);
476: days_in_month(FEBRUARY) = 28;
477: rt.tm_mon = i;
478:
479: /* Days are what is left over (+1) from all that. */
480: rt.tm_mday = day + 1;
481:
482: return(&rt);
483: }
484:
485: int
486: bbc_to_gmt(timbuf)
487: u_long *timbuf;
488: {
489: int i;
490: u_long tmp;
491: int year, month, day, hour, min, sec;
492:
493: read_bbc();
494:
495: sec = bbc_to_decimal(1, 0);
496: min = bbc_to_decimal(3, 2);
497:
498: /*
499: * Hours are different for some reason. Makes no sense really.
500: */
501: hour = ((bbc_registers[5] & 0x03) * 10) + bbc_registers[4];
502: day = bbc_to_decimal(8, 7);
503: month = bbc_to_decimal(10, 9);
504: year = bbc_to_decimal(12, 11) + 1900;
505:
506: range_test(hour, 0, 23);
507: range_test(day, 1, 31);
508: range_test(month, 1, 12);
509: range_test(year, STARTOFTIME, 2038); /* 2038 is the end of time. */
510:
511: tmp = 0;
512:
513: for (i = STARTOFTIME; i < year; i++)
514: tmp += days_in_year(i);
515: if (leapyear(year) && month > FEBRUARY)
516: tmp++;
517:
518: for (i = 1; i < month; i++)
519: tmp += days_in_month(i);
520:
521: tmp += (day - 1);
522: tmp = ((tmp * 24 + hour) * 60 + min) * 60 + sec;
523:
524: *timbuf = tmp;
525: return(1);
526: }
527:
528: void
529: read_bbc()
530: {
531: int i, read_okay;
532:
533: read_okay = 0;
534: while (!read_okay) {
535: read_okay = 1;
536: for (i = 0; i <= NUM_BBC_REGS; i++)
537: bbc_registers[i] = read_bbc_reg(i);
538: for (i = 0; i <= NUM_BBC_REGS; i++)
539: if (bbc_registers[i] != read_bbc_reg(i))
540: read_okay = 0;
541: }
542: }
543:
544: u_char
545: read_bbc_reg(reg)
546: int reg;
547: {
548: u_char data = reg;
549:
550: if (bbcaddr != NULL) {
551: send_clock_cmd(bbcaddr, BBC_SET_REG, &data, 1, NULL);
552: send_clock_cmd(bbcaddr, BBC_READ_REG, NULL, 0, &data);
553: }
554: return(data);
555: }
556:
557: u_char
558: write_bbc_reg(reg, data)
559: int reg;
560: u_int data;
561: {
562: u_char tmp;
563:
564: tmp = (u_char) ((data << HIL_SSHIFT) | reg);
565:
566: if (bbcaddr != NULL) {
567: send_clock_cmd(bbcaddr, BBC_SET_REG, &tmp, 1, NULL);
568: send_clock_cmd(bbcaddr, BBC_WRITE_REG, NULL, 0, NULL);
569: send_clock_cmd(bbcaddr, BBC_READ_REG, NULL, 0, &tmp);
570: }
571: return(tmp);
572: }
573:
574: /*
575: * Battery-backed clock command interface.
576: * The BBC appears to have an HIL-like command interface, but can not attach
577: * as a complete HIL device to an HIL controller driver.
578: * The following routine is a simplified command loop.
579: */
580: void
581: send_clock_cmd(volatile u_int8_t *address, u_int8_t cmd, u_int8_t *data,
582: u_int8_t dlen, u_int8_t *rdata)
583: {
584: u_int8_t status;
585: int s;
586:
587: s = splvm();
588:
589: while ((address[HILP_STAT] & HIL_BUSY) != 0)
590: DELAY(1);
591: address[HILP_CMD] = cmd;
592: while (dlen--) {
593: while ((address[HILP_STAT] & HIL_BUSY) != 0)
594: DELAY(1);
595: address[HILP_DATA] = *data++;
596: DELAY(1);
597: }
598: if (rdata != NULL) {
599: do {
600: while ((address[HILP_STAT] & HIL_DATA_RDY) == 0)
601: DELAY(1);
602: status = address[HILP_STAT];
603: *rdata = address[HILP_DATA];
604: DELAY(1);
605: } while (((status >> HIL_SSHIFT) & HIL_SMASK) != HIL_68K);
606: }
607:
608: splx(s);
609: }
CVSweb