Annotation of sys/arch/sparc/sparc/clock.c, Revision 1.1.1.1
1.1 nbrk 1: /* $OpenBSD: clock.c,v 1.24 2006/12/10 16:12:41 miod Exp $ */
2: /* $NetBSD: clock.c,v 1.52 1997/05/24 20:16:05 pk Exp $ */
3:
4: /*
5: * Copyright (c) 1992, 1993
6: * The Regents of the University of California. All rights reserved.
7: * Copyright (c) 1994 Gordon W. Ross
8: * Copyright (c) 1993 Adam Glass
9: * Copyright (c) 1996 Paul Kranenburg
10: * Copyright (c) 1996
11: * The President and Fellows of Harvard College. All rights reserved.
12: *
13: * This software was developed by the Computer Systems Engineering group
14: * at Lawrence Berkeley Laboratory under DARPA contract BG 91-66 and
15: * contributed to Berkeley.
16: *
17: * All advertising materials mentioning features or use of this software
18: * must display the following acknowledgement:
19: * This product includes software developed by Harvard University.
20: * This product includes software developed by the University of
21: * California, Lawrence Berkeley Laboratory.
22: *
23: * Redistribution and use in source and binary forms, with or without
24: * modification, are permitted provided that the following conditions
25: * are met:
26: *
27: * 1. Redistributions of source code must retain the above copyright
28: * notice, this list of conditions and the following disclaimer.
29: * 2. Redistributions in binary form must reproduce the above copyright
30: * notice, this list of conditions and the following disclaimer in the
31: * documentation and/or other materials provided with the distribution.
32: * 3. All advertising materials mentioning features or use of this software
33: * must display the following acknowledgement:
34: * This product includes software developed by the University of
35: * California, Berkeley and its contributors.
36: * This product includes software developed by Paul Kranenburg.
37: * This product includes software developed by Harvard University.
38: * 4. Neither the name of the University nor the names of its contributors
39: * may be used to endorse or promote products derived from this software
40: * without specific prior written permission.
41: *
42: * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
43: * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
44: * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
45: * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
46: * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
47: * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
48: * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
49: * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
50: * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
51: * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
52: * SUCH DAMAGE.
53: *
54: * @(#)clock.c 8.1 (Berkeley) 6/11/93
55: *
56: */
57:
58: /*
59: * Clock driver. This is the id prom and eeprom driver as well
60: * and includes the timer register functions too.
61: */
62:
63: #include <sys/param.h>
64: #include <sys/kernel.h>
65: #include <sys/device.h>
66: #include <sys/proc.h>
67: #include <sys/resourcevar.h>
68: #include <sys/malloc.h>
69: #include <sys/systm.h>
70: #ifdef GPROF
71: #include <sys/gmon.h>
72: #endif
73:
74: #include <uvm/uvm_extern.h>
75:
76: #include <machine/autoconf.h>
77: #include <machine/eeprom.h>
78: #include <machine/cpu.h>
79:
80: #include <sparc/sparc/vaddrs.h>
81: #include <sparc/sparc/cpuvar.h>
82: #include <sparc/sparc/clockreg.h>
83: #include <sparc/sparc/intreg.h>
84: #include <sparc/sparc/timerreg.h>
85:
86: /*
87: * Statistics clock interval and variance, in usec. Variance must be a
88: * power of two. Since this gives us an even number, not an odd number,
89: * we discard one case and compensate. That is, a variance of 1024 would
90: * give us offsets in [0..1023]. Instead, we take offsets in [1..1023].
91: * This is symmetric about the point 512, or statvar/2, and thus averages
92: * to that value (assuming uniform random numbers).
93: */
94: /* XXX fix comment to match value */
95: int statvar = 8192;
96: int statmin; /* statclock interval - 1/2*variance */
97: int timerok;
98:
99: #include <dev/ic/intersil7170.h>
100:
101: extern struct idprom idprom;
102:
103: #define intersil_command(run, interrupt) \
104: (run | interrupt | INTERSIL_CMD_FREQ_32K | INTERSIL_CMD_24HR_MODE | \
105: INTERSIL_CMD_NORMAL_MODE)
106:
107: #define intersil_disable(CLOCK) \
108: CLOCK->clk_cmd_reg = \
109: intersil_command(INTERSIL_CMD_RUN, INTERSIL_CMD_IDISABLE)
110:
111: #define intersil_enable(CLOCK) \
112: CLOCK->clk_cmd_reg = \
113: intersil_command(INTERSIL_CMD_RUN, INTERSIL_CMD_IENABLE)
114:
115: #define intersil_clear(CLOCK) CLOCK->clk_intr_reg
116:
117: #if defined(SUN4)
118: /*
119: * OCLOCK support: 4/100's and 4/200's have the old clock.
120: */
121: static int oldclk = 0;
122: struct intersil7170 *i7;
123:
124: long oclk_get_secs(void);
125: void oclk_get_dt(struct intersil_dt *);
126: void oclk_set_dt(struct intersil_dt *);
127: void oclk_set_secs(long);
128: #endif
129:
130: int oclockmatch(struct device *, void *, void *);
131: void oclockattach(struct device *, struct device *, void *);
132:
133: struct cfattach oclock_ca = {
134: sizeof(struct device), oclockmatch, oclockattach
135: };
136:
137: struct cfdriver oclock_cd = {
138: NULL, "oclock", DV_DULL
139: };
140:
141: /*
142: * Sun 4 machines use the old-style (a'la Sun 3) EEPROM. On the
143: * 4/100's and 4/200's, this is at a separate obio space. On the
144: * 4/300's and 4/400's, however, it is the cl_nvram[] chunk of the
145: * Mostek chip. Therefore, eeprom_match will only return true on
146: * the 100/200 models, and the eeprom will be attached separately.
147: * On the 300/400 models, the eeprom will be dealt with when the clock is
148: * attached.
149: */
150: char *eeprom_va = NULL;
151: #if defined(SUN4)
152: static int eeprom_busy = 0;
153: static int eeprom_wanted = 0;
154: static int eeprom_nvram = 0; /* non-zero if eeprom is on Mostek */
155: int eeprom_take(void);
156: void eeprom_give(void);
157: int eeprom_update(char *, int, int);
158: #endif
159:
160: int eeprom_match(struct device *, void *, void *);
161: void eeprom_attach(struct device *, struct device *, void *);
162:
163: struct cfattach eeprom_ca = {
164: sizeof(struct device), eeprom_match, eeprom_attach
165: };
166:
167: struct cfdriver eeprom_cd = {
168: NULL, "eeprom", DV_DULL
169: };
170:
171: int clockmatch(struct device *, void *, void *);
172: void clockattach(struct device *, struct device *, void *);
173:
174: struct cfattach clock_ca = {
175: sizeof(struct device), clockmatch, clockattach
176: };
177:
178: struct cfdriver clock_cd = {
179: NULL, "clock", DV_DULL
180: };
181:
182: int timermatch(struct device *, void *, void *);
183: void timerattach(struct device *, struct device *, void *);
184:
185: struct timer_4m *timerreg_4m; /* XXX - need more cleanup */
186: struct counter_4m *counterreg_4m;
187: #define timerreg4 ((struct timerreg_4 *)TIMERREG_VA)
188:
189: struct cfattach timer_ca = {
190: sizeof(struct device), timermatch, timerattach
191: };
192:
193: struct cfdriver timer_cd = {
194: NULL, "timer", DV_DULL
195: };
196:
197: void clk_wenable(int);
198: void myetheraddr(u_char *);
199:
200: int timerblurb = 10; /* Guess a value; used before clock is attached */
201:
202: /*
203: * old clock match routine
204: */
205: int
206: oclockmatch(parent, vcf, aux)
207: struct device *parent;
208: void *vcf, *aux;
209: {
210: struct confargs *ca = aux;
211:
212: /* Only these sun4s have oclock */
213: if (!CPU_ISSUN4 ||
214: (cpuinfo.cpu_type != CPUTYP_4_100 &&
215: cpuinfo.cpu_type != CPUTYP_4_200))
216: return (0);
217:
218: /* Check configuration name */
219: if (strcmp(oclock_cd.cd_name, ca->ca_ra.ra_name) != 0)
220: return (0);
221:
222: /* Make sure there is something there */
223: if (probeget(ca->ca_ra.ra_vaddr, 1) == -1)
224: return (0);
225:
226: return (1);
227: }
228:
229: /* ARGSUSED */
230: void
231: oclockattach(parent, self, aux)
232: struct device *parent, *self;
233: void *aux;
234: {
235: #if defined(SUN4)
236: struct confargs *ca = aux;
237: struct romaux *ra = &ca->ca_ra;
238: struct idprom *idp;
239: int h;
240:
241: oldclk = 1; /* we've got an oldie! */
242:
243: i7 = (struct intersil7170 *) mapiodev(ra->ra_reg, 0, sizeof(*i7));
244:
245: idp = &idprom;
246: h = idp->id_machine << 24;
247: h |= idp->id_hostid[0] << 16;
248: h |= idp->id_hostid[1] << 8;
249: h |= idp->id_hostid[2];
250: hostid = h;
251:
252: /*
253: * calibrate delay()
254: */
255: ienab_bic(IE_L14 | IE_L10); /* disable all clock intrs */
256: for (timerblurb = 1; ; timerblurb++) {
257: volatile register char *ireg = &i7->clk_intr_reg;
258: int ival;
259: *ireg = INTERSIL_INTER_CSECONDS; /* 1/100 sec */
260: intersil_enable(i7); /* enable clock */
261: while ((*ireg & INTERSIL_INTER_PENDING) == 0)
262: /* sync with interrupt */;
263: while ((*ireg & INTERSIL_INTER_PENDING) == 0)
264: /* XXX: do it again, seems to need it */;
265: delay(10000); /* Probe 1/100 sec delay */
266: ival = *ireg; /* clear, save value */
267: intersil_disable(i7); /* disable clock */
268: if (ival & INTERSIL_INTER_PENDING) {
269: printf(" delay constant %d%s\n", timerblurb,
270: (timerblurb == 1) ? " [TOO SMALL?]" : "");
271: break;
272: }
273: if (timerblurb > 10) {
274: printf("\noclock: calibration failing; clamped at %d\n",
275: timerblurb);
276: break;
277: }
278: }
279: #endif /* SUN4 */
280: }
281:
282: /*
283: * Sun 4/100, 4/200 EEPROM match routine.
284: */
285: int
286: eeprom_match(parent, vcf, aux)
287: struct device *parent;
288: void *vcf, *aux;
289: {
290: struct cfdata *cf = vcf;
291: struct confargs *ca = aux;
292:
293: if (!CPU_ISSUN4)
294: return (0);
295:
296: if (cf->cf_unit != 0)
297: return (0);
298:
299: if (cpuinfo.cpu_type != CPUTYP_4_100 &&
300: cpuinfo.cpu_type != CPUTYP_4_200)
301: return (0);
302:
303: if (strcmp(eeprom_cd.cd_name, ca->ca_ra.ra_name) != 0)
304: return (0);
305:
306: /*
307: * Make sure there's something there...
308: * This is especially important if we want to
309: * use the same kernel on a 4/100 as a 4/200.
310: */
311: if (probeget(ca->ca_ra.ra_vaddr, 1) == -1)
312: return (0);
313:
314: /* Passed all tests */
315: return (1);
316: }
317:
318: void
319: eeprom_attach(parent, self, aux)
320: struct device *parent, *self;
321: void *aux;
322: {
323: #if defined(SUN4)
324: struct confargs *ca = aux;
325: struct romaux *ra = &ca->ca_ra;
326:
327: printf("\n");
328:
329: eeprom_va = (char *)mapiodev(ra->ra_reg, 0, EEPROM_SIZE);
330:
331: eeprom_nvram = 0;
332: #endif /* SUN4 */
333: }
334:
335: /*
336: * The OPENPROM calls the clock the "eeprom", so we have to have our
337: * own special match function to call it the "clock".
338: */
339: int
340: clockmatch(parent, vcf, aux)
341: struct device *parent;
342: void *vcf, *aux;
343: {
344: struct confargs *ca = aux;
345:
346: if (CPU_ISSUN4) {
347: /* Only these sun4s have "clock" (others have "oclock") */
348: if (cpuinfo.cpu_type != CPUTYP_4_300 &&
349: cpuinfo.cpu_type != CPUTYP_4_400)
350: return (0);
351:
352: if (strcmp(clock_cd.cd_name, ca->ca_ra.ra_name) != 0)
353: return (0);
354:
355: /* Make sure there is something there */
356: if (probeget(ca->ca_ra.ra_vaddr, 1) == -1)
357: return (0);
358:
359: return (1);
360: }
361:
362: return (strcmp("eeprom", ca->ca_ra.ra_name) == 0);
363: }
364:
365: /* ARGSUSED */
366: void
367: clockattach(parent, self, aux)
368: struct device *parent, *self;
369: void *aux;
370: {
371: int h;
372: struct clockreg *cl;
373: struct idprom *idp;
374: struct confargs *ca = aux;
375: struct romaux *ra = &ca->ca_ra;
376: char *prop = NULL;
377:
378: if (CPU_ISSUN4)
379: prop = "mk48t02";
380:
381: else if (CPU_ISSUN4COR4M)
382: prop = getpropstring(ra->ra_node, "model");
383:
384: printf(": %s (eeprom)\n", prop);
385:
386: /*
387: * We ignore any existing virtual address as we need to map
388: * this read-only and make it read-write only temporarily,
389: * whenever we read or write the clock chip. The clock also
390: * contains the ID ``PROM'', and I have already had the pleasure
391: * of reloading the cpu type, Ethernet address, etc, by hand from
392: * the console FORTH interpreter. I intend not to enjoy it again.
393: */
394: if (strcmp(prop, "mk48t08") == 0) {
395: /*
396: * the MK48T08 is 8K
397: */
398: cl = (struct clockreg *)mapiodev(ra->ra_reg, 0, 8192);
399: pmap_changeprot(pmap_kernel(), (vaddr_t)cl, VM_PROT_READ, 1);
400: pmap_changeprot(pmap_kernel(), (vaddr_t)cl + 4096,
401: VM_PROT_READ, 1);
402: cl = (struct clockreg *)((int)cl + CLK_MK48T08_OFF);
403: } else {
404: /*
405: * the MK48T02 is 2K
406: */
407: cl = (struct clockreg *)mapiodev(ra->ra_reg, 0,
408: sizeof *clockreg);
409: pmap_changeprot(pmap_kernel(), (vaddr_t)cl, VM_PROT_READ, 1);
410: }
411: idp = &cl->cl_idprom;
412:
413: #if defined(SUN4)
414: if (CPU_ISSUN4) {
415: idp = &idprom;
416:
417: if (cpuinfo.cpu_type == CPUTYP_4_300 ||
418: cpuinfo.cpu_type == CPUTYP_4_400) {
419: eeprom_va = (char *)cl->cl_nvram;
420: eeprom_nvram = 1;
421: }
422: }
423: #endif
424:
425: h = idp->id_machine << 24;
426: h |= idp->id_hostid[0] << 16;
427: h |= idp->id_hostid[1] << 8;
428: h |= idp->id_hostid[2];
429: hostid = h;
430: clockreg = cl;
431: }
432:
433: /*
434: * The OPENPROM calls the timer the "counter-timer".
435: */
436: int
437: timermatch(parent, vcf, aux)
438: struct device *parent;
439: void *vcf, *aux;
440: {
441: struct confargs *ca = aux;
442:
443: if (CPU_ISSUN4) {
444: if (cpuinfo.cpu_type != CPUTYP_4_300 &&
445: cpuinfo.cpu_type != CPUTYP_4_400)
446: return (0);
447:
448: if (strcmp("timer", ca->ca_ra.ra_name) != 0)
449: return (0);
450:
451: /* Make sure there is something there */
452: if (probeget(ca->ca_ra.ra_vaddr, 4) == -1)
453: return (0);
454:
455: return (1);
456: }
457:
458: if (CPU_ISSUN4C) {
459: return (strcmp("counter-timer", ca->ca_ra.ra_name) == 0);
460: }
461:
462: if (CPU_ISSUN4M) {
463: return (strcmp("counter", ca->ca_ra.ra_name) == 0);
464: }
465:
466: return (0);
467: }
468:
469: /* ARGSUSED */
470: void
471: timerattach(parent, self, aux)
472: struct device *parent, *self;
473: void *aux;
474: {
475: struct confargs *ca = aux;
476: struct romaux *ra = &ca->ca_ra;
477: volatile int *cnt = NULL, *lim = NULL;
478: /* XXX: must init to NULL to avoid stupid gcc -Wall warning */
479:
480: if (CPU_ISSUN4M) {
481: (void)mapdev(&ra->ra_reg[ra->ra_nreg-1], TIMERREG_VA, 0,
482: sizeof(struct timer_4m));
483: (void)mapdev(&ra->ra_reg[0], COUNTERREG_VA, 0,
484: sizeof(struct counter_4m));
485: timerreg_4m = (struct timer_4m *)TIMERREG_VA;
486: counterreg_4m = (struct counter_4m *)COUNTERREG_VA;
487:
488: /* Put processor counter in "timer" mode */
489: timerreg_4m->t_cfg = 0;
490:
491: cnt = &counterreg_4m->t_counter;
492: lim = &counterreg_4m->t_limit;
493: }
494:
495: if (CPU_ISSUN4OR4C) {
496: /*
497: * This time, we ignore any existing virtual address because
498: * we have a fixed virtual address for the timer, to make
499: * microtime() faster (in SUN4/SUN4C kernel only).
500: */
501: (void)mapdev(ra->ra_reg, TIMERREG_VA, 0,
502: sizeof(struct timerreg_4));
503:
504: cnt = &timerreg4->t_c14.t_counter;
505: lim = &timerreg4->t_c14.t_limit;
506: }
507:
508: timerok = 1;
509:
510: /*
511: * Calibrate delay() by tweaking the magic constant
512: * until a delay(100) actually reads (at least) 100 us on the clock.
513: * Note: sun4m clocks tick with 500ns periods.
514: */
515:
516: for (timerblurb = 1; ; timerblurb++) {
517: volatile int discard;
518: int t0, t1;
519:
520: /* Reset counter register by writing some large limit value */
521: discard = *lim;
522: *lim = tmr_ustolim(TMR_MASK-1);
523:
524: t0 = *cnt;
525: delay(100);
526: t1 = *cnt;
527:
528: if (t1 & TMR_LIMIT)
529: panic("delay calibration");
530:
531: t0 = (t0 >> TMR_SHIFT) & TMR_MASK;
532: t1 = (t1 >> TMR_SHIFT) & TMR_MASK;
533:
534: if (t1 >= t0 + 100)
535: break;
536:
537: }
538:
539: printf(" delay constant %d\n", timerblurb);
540:
541: /* should link interrupt handlers here, rather than compiled-in? */
542: }
543:
544: /*
545: * Write en/dis-able clock registers. We coordinate so that several
546: * writers can run simultaneously.
547: */
548: void
549: clk_wenable(onoff)
550: int onoff;
551: {
552: int s;
553: vm_prot_t prot;/* nonzero => change prot */
554: static int writers;
555:
556: s = splhigh();
557: if (onoff)
558: prot = writers++ == 0 ? VM_PROT_READ|VM_PROT_WRITE : 0;
559: else
560: prot = --writers == 0 ? VM_PROT_READ : 0;
561: splx(s);
562: if (prot)
563: pmap_changeprot(pmap_kernel(), (vaddr_t)clockreg & ~(NBPG-1),
564: prot, 1);
565: }
566:
567: /*
568: * XXX this belongs elsewhere
569: */
570: void
571: myetheraddr(cp)
572: u_char *cp;
573: {
574: struct clockreg *cl = clockreg;
575: struct idprom *idp = &cl->cl_idprom;
576:
577: #if defined(SUN4)
578: if (CPU_ISSUN4)
579: idp = &idprom;
580: #endif
581:
582: cp[0] = idp->id_ether[0];
583: cp[1] = idp->id_ether[1];
584: cp[2] = idp->id_ether[2];
585: cp[3] = idp->id_ether[3];
586: cp[4] = idp->id_ether[4];
587: cp[5] = idp->id_ether[5];
588: }
589:
590: /*
591: * Set up the real-time and statistics clocks. Leave stathz 0 only if
592: * no alternative timer is available.
593: *
594: * The frequencies of these clocks must be an even number of microseconds.
595: */
596: void
597: cpu_initclocks()
598: {
599: int statint, minint;
600:
601: #if defined(SUN4)
602: if (oldclk) {
603: int dummy;
604:
605: if (hz != 100) {
606: printf("oclock0: cannot get %d Hz clock; using 100 Hz\n", hz);
607: }
608:
609: profhz = hz = 100;
610: tick = 1000000 / hz;
611:
612: i7->clk_intr_reg = INTERSIL_INTER_CSECONDS; /* 1/100 sec */
613:
614: ienab_bic(IE_L14 | IE_L10); /* disable all clock intrs */
615: intersil_disable(i7); /* disable clock */
616: dummy = intersil_clear(i7); /* clear interrupts */
617: ienab_bis(IE_L10); /* enable l10 interrupt */
618: intersil_enable(i7); /* enable clock */
619:
620: return;
621: }
622: #endif /* SUN4 */
623:
624: if (1000000 % hz) {
625: printf("clock0: cannot get %d Hz clock; using 100 Hz\n", hz);
626: hz = 100;
627: tick = 1000000 / hz;
628: }
629: if (stathz == 0)
630: stathz = hz;
631: if (1000000 % stathz) {
632: printf("clock0: cannot get %d Hz statclock; using 100 Hz\n", stathz);
633: stathz = 100;
634: }
635: profhz = stathz; /* always */
636:
637: statint = 1000000 / stathz;
638: minint = statint / 2 + 100;
639: while (statvar > minint)
640: statvar >>= 1;
641:
642: if (CPU_ISSUN4M) {
643: timerreg_4m->t_limit = tmr_ustolim4m(tick);
644: counterreg_4m->t_limit = tmr_ustolim4m(statint);
645: }
646:
647: if (CPU_ISSUN4OR4C) {
648: timerreg4->t_c10.t_limit = tmr_ustolim(tick);
649: timerreg4->t_c14.t_limit = tmr_ustolim(statint);
650: }
651:
652: statmin = statint - (statvar >> 1);
653:
654: #if defined(SUN4M)
655: if (CPU_ISSUN4M)
656: ienab_bic(SINTR_T);
657: #endif
658:
659: if (CPU_ISSUN4OR4C)
660: ienab_bis(IE_L14 | IE_L10);
661:
662: }
663:
664: /*
665: * Dummy setstatclockrate(), since we know profhz==hz.
666: */
667: /* ARGSUSED */
668: void
669: setstatclockrate(newhz)
670: int newhz;
671: {
672: /* nothing */
673: }
674:
675: /*
676: * Level 10 (clock) interrupts. If we are using the FORTH PROM for
677: * console input, we need to check for that here as well, and generate
678: * a software interrupt to read it.
679: */
680: int
681: clockintr(cap)
682: void *cap;
683: {
684: volatile int discard;
685: int s;
686:
687: /*
688: * Protect the clearing of the clock interrupt. If we don't
689: * do this, and we're interrupted (by the zs, for example),
690: * the clock stops!
691: * XXX WHY DOES THIS HAPPEN?
692: */
693: s = splhigh();
694:
695: #if defined(SUN4)
696: if (oldclk) {
697: discard = intersil_clear(i7);
698: ienab_bic(IE_L10); /* clear interrupt */
699: ienab_bis(IE_L10); /* enable interrupt */
700: goto forward;
701: }
702: #endif
703: #if defined(SUN4M)
704: /* read the limit register to clear the interrupt */
705: if (CPU_ISSUN4M) {
706: discard = timerreg_4m->t_limit;
707: }
708: #endif
709: #if defined(SUN4) || defined(SUN4C)
710: if (CPU_ISSUN4OR4C) {
711: discard = timerreg4->t_c10.t_limit;
712: }
713: #endif
714: #if defined(SUN4)
715: forward:
716: #endif
717: splx(s);
718:
719: hardclock((struct clockframe *)cap);
720:
721: return (1);
722: }
723:
724: /*
725: * Level 14 (stat clock) interrupt handler.
726: */
727: int
728: statintr(cap)
729: void *cap;
730: {
731: volatile int discard;
732: u_long newint, r, var;
733:
734: #if defined(SUN4)
735: if (oldclk) {
736: panic("oldclk statintr");
737: return (1);
738: }
739: #endif
740:
741: /* read the limit register to clear the interrupt */
742: if (CPU_ISSUN4M) {
743: discard = counterreg_4m->t_limit;
744: if (timerok == 0) {
745: /* Stop the clock */
746: counterreg_4m->t_limit = 0;
747: counterreg_4m->t_ss = 0;
748: timerreg_4m->t_cfg = TMR_CFG_USER;
749: return 1;
750: }
751: }
752:
753: if (CPU_ISSUN4OR4C) {
754: discard = timerreg4->t_c14.t_limit;
755: }
756: statclock((struct clockframe *)cap);
757:
758: /*
759: * Compute new randomized interval. The intervals are uniformly
760: * distributed on [statint - statvar / 2, statint + statvar / 2],
761: * and therefore have mean statint, giving a stathz frequency clock.
762: */
763: var = statvar;
764: do {
765: r = random() & (var - 1);
766: } while (r == 0);
767: newint = statmin + r;
768:
769: if (CPU_ISSUN4M) {
770: counterreg_4m->t_limit = tmr_ustolim4m(newint);
771: }
772:
773: if (CPU_ISSUN4OR4C) {
774: timerreg4->t_c14.t_limit = tmr_ustolim(newint);
775: }
776: return (1);
777: }
778:
779: /*
780: * Set up the system's time, given a `reasonable' time value.
781: */
782: void
783: inittodr(base)
784: time_t base;
785: {
786: struct clockreg *cl = clockreg;
787: struct clock_ymdhms dt;
788: int badbase = 0, waszero = base == 0;
789: char *bad = NULL;
790:
791: if (base < 5 * SECYR) {
792: /*
793: * If base is 0, assume filesystem time is just unknown
794: * in stead of preposterous. Don't bark.
795: */
796: if (base != 0)
797: printf("WARNING: preposterous time in file system\n");
798: /* not going to use it anyway, if the chip is readable */
799: base = 21*SECYR + 186*SECDAY + SECDAY/2;
800: badbase = 1;
801: }
802: #if defined(SUN4)
803: if (oldclk) {
804: time.tv_sec = oclk_get_secs();
805: goto forward;
806: }
807: #endif
808: clk_wenable(1);
809: cl->cl_csr |= CLK_READ; /* enable read (stop time) */
810: dt.dt_sec = FROMBCD(cl->cl_sec);
811: dt.dt_min = FROMBCD(cl->cl_min);
812: dt.dt_hour = FROMBCD(cl->cl_hour);
813: dt.dt_day = FROMBCD(cl->cl_mday);
814: dt.dt_mon = FROMBCD(cl->cl_month);
815: dt.dt_year = FROMBCD(cl->cl_year) + CLOCK_BASE_YEAR;
816: cl->cl_csr &= ~CLK_READ; /* time wears on */
817: clk_wenable(0);
818: time.tv_sec = clock_ymdhms_to_secs(&dt);
819:
820: #if defined(SUN4)
821: forward:
822: #endif
823: if (time.tv_sec == 0) {
824: /*
825: * Believe the time in the file system for lack of
826: * anything better, resetting the clock.
827: */
828: bad = "WARNING: bad date in battery clock";
829: time.tv_sec = base;
830: if (!badbase)
831: resettodr();
832: } else {
833: int deltat = time.tv_sec - base;
834:
835: if (deltat < 0)
836: deltat = -deltat;
837: if (waszero || deltat < 2 * SECDAY)
838: return;
839:
840: #ifndef SMALL_KERNEL
841: printf("WARNING: clock %s %d days",
842: time.tv_sec < base ? "lost" : "gained", deltat / SECDAY);
843: bad = "";
844: #endif
845: }
846: if (bad) {
847: printf("%s", bad);
848: printf(" -- CHECK AND RESET THE DATE!\n");
849: }
850: }
851:
852: /*
853: * Reset the clock based on the current time.
854: * Used when the current clock is preposterous, when the time is changed,
855: * and when rebooting. Do nothing if the time is not yet known, e.g.,
856: * when crashing during autoconfig.
857: */
858: void
859: resettodr()
860: {
861: struct clockreg *cl;
862: struct clock_ymdhms dt;
863:
864: #if defined(SUN4)
865: if (oldclk) {
866: if (!time.tv_sec || i7 == NULL)
867: return;
868: oclk_set_secs(time.tv_sec);
869: return;
870: }
871: #endif
872:
873: if (!time.tv_sec || (cl = clockreg) == NULL)
874: return;
875:
876: clock_secs_to_ymdhms(time.tv_sec, &dt);
877:
878: clk_wenable(1);
879: cl->cl_csr |= CLK_WRITE; /* enable write */
880: cl->cl_sec = TOBCD(dt.dt_sec);
881: cl->cl_min = TOBCD(dt.dt_min);
882: cl->cl_hour = TOBCD(dt.dt_hour);
883: cl->cl_wday = TOBCD(dt.dt_wday);
884: cl->cl_mday = TOBCD(dt.dt_day);
885: cl->cl_month = TOBCD(dt.dt_mon);
886: cl->cl_year = TOBCD(dt.dt_year - CLOCK_BASE_YEAR);
887: cl->cl_csr &= ~CLK_WRITE; /* load them up */
888: clk_wenable(0);
889: }
890:
891: #if defined(SUN4)
892: /*
893: * Now routines to get and set clock as POSIX time.
894: */
895: long
896: oclk_get_secs()
897: {
898: struct intersil_dt idt;
899: struct clock_ymdhms dt;
900:
901: oclk_get_dt(&idt);
902: dt.dt_sec = idt.dt_sec;
903: dt.dt_min = idt.dt_min;
904: dt.dt_hour = idt.dt_hour;
905: dt.dt_day = idt.dt_day;
906: dt.dt_mon = idt.dt_month;
907: dt.dt_year = idt.dt_year + CLOCK_BASE_YEAR;
908: return clock_ymdhms_to_secs(&dt);
909: }
910:
911: void
912: oclk_set_secs(secs)
913: long secs;
914: {
915: struct intersil_dt idt;
916: struct clock_ymdhms dt;
917:
918: clock_secs_to_ymdhms(secs, &dt);
919:
920: idt.dt_hour = dt.dt_hour;
921: idt.dt_min = dt.dt_min;
922: idt.dt_sec = dt.dt_sec;
923: idt.dt_month = dt.dt_mon;
924: idt.dt_day = dt.dt_day;
925: idt.dt_year = dt.dt_year - CLOCK_BASE_YEAR;
926: idt.dt_dow = dt.dt_wday;
927: oclk_set_dt(&idt);
928: }
929:
930: /*
931: * Routine to copy state into and out of the clock.
932: * The clock registers have to be read or written
933: * in sequential order (or so it appears). -gwr
934: */
935: void
936: oclk_get_dt(dt)
937: struct intersil_dt *dt;
938: {
939: int s;
940: register volatile char *src, *dst;
941:
942: src = (char *) &i7->counters;
943:
944: s = splhigh();
945: i7->clk_cmd_reg =
946: intersil_command(INTERSIL_CMD_STOP, INTERSIL_CMD_IENABLE);
947:
948: dst = (char *) dt;
949: dt++; /* end marker */
950: do {
951: *dst++ = *src++;
952: } while (dst < (char *)dt);
953:
954: i7->clk_cmd_reg =
955: intersil_command(INTERSIL_CMD_RUN, INTERSIL_CMD_IENABLE);
956: splx(s);
957: }
958:
959: void
960: oclk_set_dt(dt)
961: struct intersil_dt *dt;
962: {
963: int s;
964: register volatile char *src, *dst;
965:
966: dst = (char *) &i7->counters;
967:
968: s = splhigh();
969: i7->clk_cmd_reg =
970: intersil_command(INTERSIL_CMD_STOP, INTERSIL_CMD_IENABLE);
971:
972: src = (char *) dt;
973: dt++; /* end marker */
974: do {
975: *dst++ = *src++;
976: } while (src < (char *)dt);
977:
978: i7->clk_cmd_reg =
979: intersil_command(INTERSIL_CMD_RUN, INTERSIL_CMD_IENABLE);
980: splx(s);
981: }
982: #endif /* SUN4 */
983:
984: #if defined(SUN4)
985: /*
986: * Return the best possible estimate of the time in the timeval
987: * to which tvp points. We do this by returning the current time
988: * plus the amount of time since the last clock interrupt.
989: *
990: * Check that this time is no less than any previously-reported time,
991: * which could happen around the time of a clock adjustment. Just for
992: * fun, we guarantee that the time will be greater than the value
993: * obtained by a previous call.
994: */
995: void
996: microtime(tvp)
997: struct timeval *tvp;
998: {
999: int s;
1000: static struct timeval lasttime;
1001: static struct timeval oneusec = {0, 1};
1002:
1003: if (!oldclk) {
1004: lo_microtime(tvp);
1005: return;
1006: }
1007:
1008: s = splhigh();
1009: *tvp = time;
1010: splx(s);
1011:
1012: if (timercmp(tvp, &lasttime, <=))
1013: timeradd(&lasttime, &oneusec, tvp);
1014:
1015: lasttime = *tvp;
1016: }
1017: #endif /* SUN4 */
1018:
1019: /*
1020: * XXX: these may actually belong somewhere else, but since the
1021: * EEPROM is so closely tied to the clock on some models, perhaps
1022: * it needs to stay here...
1023: */
1024: int
1025: eeprom_uio(uio)
1026: struct uio *uio;
1027: {
1028: #if defined(SUN4)
1029: int error;
1030: int off; /* NOT off_t */
1031: u_int cnt, bcnt;
1032: caddr_t buf = NULL;
1033:
1034: if (!CPU_ISSUN4)
1035: return (ENODEV);
1036:
1037: off = uio->uio_offset;
1038: if (off > EEPROM_SIZE)
1039: return (EFAULT);
1040:
1041: cnt = uio->uio_resid;
1042: if (cnt > (EEPROM_SIZE - off))
1043: cnt = (EEPROM_SIZE - off);
1044:
1045: if ((error = eeprom_take()) != 0)
1046: return (error);
1047:
1048: if (eeprom_va == NULL) {
1049: error = ENXIO;
1050: goto out;
1051: }
1052:
1053: /*
1054: * The EEPROM can only be accessed one byte at a time, yet
1055: * uiomove() will attempt long-word access. To circumvent
1056: * this, we byte-by-byte copy the eeprom contents into a
1057: * temporary buffer.
1058: */
1059: buf = malloc(EEPROM_SIZE, M_DEVBUF, M_WAITOK);
1060:
1061: if (uio->uio_rw == UIO_READ)
1062: for (bcnt = 0; bcnt < EEPROM_SIZE; ++bcnt)
1063: *(char *)(buf + bcnt) = *(char *)(eeprom_va + bcnt);
1064:
1065: if ((error = uiomove(buf + off, (int)cnt, uio)) != 0)
1066: goto out;
1067:
1068: if (uio->uio_rw != UIO_READ)
1069: error = eeprom_update(buf, off, cnt);
1070:
1071: out:
1072: if (buf)
1073: free(buf, M_DEVBUF);
1074: eeprom_give();
1075: return (error);
1076: #else /* ! SUN4 */
1077: return (ENODEV);
1078: #endif /* SUN4 */
1079: }
1080:
1081: #if defined(SUN4)
1082: /*
1083: * Update the EEPROM from the passed buf.
1084: */
1085: int
1086: eeprom_update(buf, off, cnt)
1087: char *buf;
1088: int off, cnt;
1089: {
1090: int error = 0;
1091: volatile char *ep;
1092: char *bp;
1093:
1094: if (eeprom_va == NULL)
1095: return (ENXIO);
1096:
1097: ep = eeprom_va + off;
1098: bp = buf + off;
1099:
1100: if (eeprom_nvram)
1101: clk_wenable(1);
1102:
1103: while (cnt > 0) {
1104: /*
1105: * DO NOT WRITE IT UNLESS WE HAVE TO because the
1106: * EEPROM has a limited number of write cycles.
1107: * After some number of writes it just fails!
1108: */
1109: if (*ep != *bp) {
1110: *ep = *bp;
1111: /*
1112: * We have written the EEPROM, so now we must
1113: * sleep for at least 10 milliseconds while
1114: * holding the lock to prevent all access to
1115: * the EEPROM while it recovers.
1116: */
1117: (void)tsleep(eeprom_va, PZERO - 1, "eeprom", hz/50);
1118: }
1119: /* Make sure the write worked. */
1120: if (*ep != *bp) {
1121: error = EIO;
1122: goto out;
1123: }
1124: ++ep;
1125: ++bp;
1126: --cnt;
1127: }
1128: out:
1129: if (eeprom_nvram)
1130: clk_wenable(0);
1131:
1132: return (error);
1133: }
1134:
1135: /* Take a lock on the eeprom. */
1136: int
1137: eeprom_take()
1138: {
1139: int error = 0;
1140:
1141: while (eeprom_busy) {
1142: eeprom_wanted = 1;
1143: error = tsleep(&eeprom_busy, PZERO | PCATCH, "eeprom", 0);
1144: eeprom_wanted = 0;
1145: if (error) /* interrupted */
1146: goto out;
1147: }
1148: eeprom_busy = 1;
1149: out:
1150: return (error);
1151: }
1152:
1153: /* Give a lock on the eeprom away. */
1154: void
1155: eeprom_give()
1156: {
1157:
1158: eeprom_busy = 0;
1159: if (eeprom_wanted) {
1160: eeprom_wanted = 0;
1161: wakeup(&eeprom_busy);
1162: }
1163: }
1164: #endif /* SUN4 */
CVSweb