Annotation of src/sys/arch/x86/isa/clock.c, Revision 1.4.2.2
1.4.2.2 ! ad 1: /* $NetBSD: clock.c,v 1.8 2006/12/08 15:05:18 yamt Exp $ */
1.1 perry 2:
3: /*-
4: * Copyright (c) 1990 The Regents of the University of California.
5: * All rights reserved.
6: *
7: * This code is derived from software contributed to Berkeley by
8: * William Jolitz and Don Ahn.
9: *
10: * Redistribution and use in source and binary forms, with or without
11: * modification, are permitted provided that the following conditions
12: * are met:
13: * 1. Redistributions of source code must retain the above copyright
14: * notice, this list of conditions and the following disclaimer.
15: * 2. Redistributions in binary form must reproduce the above copyright
16: * notice, this list of conditions and the following disclaimer in the
17: * documentation and/or other materials provided with the distribution.
18: * 3. Neither the name of the University nor the names of its contributors
19: * may be used to endorse or promote products derived from this software
20: * without specific prior written permission.
21: *
22: * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
23: * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
24: * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
25: * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
26: * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
27: * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
28: * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
29: * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
30: * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
31: * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
32: * SUCH DAMAGE.
33: *
34: * @(#)clock.c 7.2 (Berkeley) 5/12/91
35: */
36: /*-
37: * Copyright (c) 1993, 1994 Charles M. Hannum.
38: *
39: * This code is derived from software contributed to Berkeley by
40: * William Jolitz and Don Ahn.
41: *
42: * Redistribution and use in source and binary forms, with or without
43: * modification, are permitted provided that the following conditions
44: * are met:
45: * 1. Redistributions of source code must retain the above copyright
46: * notice, this list of conditions and the following disclaimer.
47: * 2. Redistributions in binary form must reproduce the above copyright
48: * notice, this list of conditions and the following disclaimer in the
49: * documentation and/or other materials provided with the distribution.
50: * 3. All advertising materials mentioning features or use of this software
51: * must display the following acknowledgement:
52: * This product includes software developed by the University of
53: * California, Berkeley and its contributors.
54: * 4. Neither the name of the University nor the names of its contributors
55: * may be used to endorse or promote products derived from this software
56: * without specific prior written permission.
57: *
58: * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
59: * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
60: * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
61: * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
62: * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
63: * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
64: * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
65: * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
66: * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
67: * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
68: * SUCH DAMAGE.
69: *
70: * @(#)clock.c 7.2 (Berkeley) 5/12/91
71: */
72: /*
73: * Mach Operating System
74: * Copyright (c) 1991,1990,1989 Carnegie Mellon University
75: * All Rights Reserved.
76: *
77: * Permission to use, copy, modify and distribute this software and its
78: * documentation is hereby granted, provided that both the copyright
79: * notice and this permission notice appear in all copies of the
80: * software, derivative works or modified versions, and any portions
81: * thereof, and that both notices appear in supporting documentation.
82: *
83: * CARNEGIE MELLON ALLOWS FREE USE OF THIS SOFTWARE IN ITS "AS IS"
84: * CONDITION. CARNEGIE MELLON DISCLAIMS ANY LIABILITY OF ANY KIND FOR
85: * ANY DAMAGES WHATSOEVER RESULTING FROM THE USE OF THIS SOFTWARE.
86: *
87: * Carnegie Mellon requests users of this software to return to
88: *
89: * Software Distribution Coordinator or Software.Distribution@CS.CMU.EDU
90: * School of Computer Science
91: * Carnegie Mellon University
92: * Pittsburgh PA 15213-3890
93: *
94: * any improvements or extensions that they make and grant Carnegie Mellon
95: * the rights to redistribute these changes.
96: */
97: /*
98: Copyright 1988, 1989 by Intel Corporation, Santa Clara, California.
99:
100: All Rights Reserved
101:
102: Permission to use, copy, modify, and distribute this software and
103: its documentation for any purpose and without fee is hereby
104: granted, provided that the above copyright notice appears in all
105: copies and that both the copyright notice and this permission notice
106: appear in supporting documentation, and that the name of Intel
107: not be used in advertising or publicity pertaining to distribution
108: of the software without specific, written prior permission.
109:
110: INTEL DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE
111: INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS,
112: IN NO EVENT SHALL INTEL BE LIABLE FOR ANY SPECIAL, INDIRECT, OR
113: CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
114: LOSS OF USE, DATA OR PROFITS, WHETHER IN ACTION OF CONTRACT,
115: NEGLIGENCE, OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION
116: WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
117: */
118:
119: /*
120: * Primitive clock interrupt routines.
121: */
122:
123: #include <sys/cdefs.h>
1.4.2.2 ! ad 124: __KERNEL_RCSID(0, "$NetBSD: clock.c,v 1.8 2006/12/08 15:05:18 yamt Exp $");
1.1 perry 125:
126: /* #define CLOCKDEBUG */
127: /* #define CLOCK_PARANOIA */
128:
129: #include "opt_multiprocessor.h"
130: #include "opt_ntp.h"
131:
132: #include <sys/param.h>
133: #include <sys/systm.h>
134: #include <sys/time.h>
135: #include <sys/timetc.h>
136: #include <sys/kernel.h>
137: #include <sys/device.h>
138:
139: #include <machine/cpu.h>
140: #include <machine/intr.h>
141: #include <machine/pio.h>
142: #include <machine/cpufunc.h>
143:
144: #include <dev/isa/isareg.h>
145: #include <dev/isa/isavar.h>
146: #include <dev/ic/mc146818reg.h>
147: #include <dev/ic/i8253reg.h>
148: #include <i386/isa/nvram.h>
149: #include <x86/x86/tsc.h>
150: #include <dev/clock_subr.h>
151: #include <machine/specialreg.h>
152:
153: #include "config_time.h" /* for CONFIG_TIME */
154:
155: #ifndef __x86_64__
156: #include "mca.h"
157: #endif
158: #if NMCA > 0
159: #include <machine/mca_machdep.h> /* for MCA_system */
160: #endif
161:
162: #include "pcppi.h"
163: #if (NPCPPI > 0)
164: #include <dev/isa/pcppivar.h>
165:
166: int sysbeepmatch(struct device *, struct cfdata *, void *);
167: void sysbeepattach(struct device *, struct device *, void *);
168:
169: CFATTACH_DECL(sysbeep, sizeof(struct device),
170: sysbeepmatch, sysbeepattach, NULL, NULL);
171:
172: static int ppi_attached;
173: static pcppi_tag_t ppicookie;
174: #endif /* PCPPI */
175:
176: #ifdef __x86_64__
177: #define READ_FLAGS() read_rflags()
178: #define WRITE_FLAGS(x) write_rflags(x)
179: #else /* i386 architecture processor */
180: #define READ_FLAGS() read_eflags()
181: #define WRITE_FLAGS(x) write_eflags(x)
182: #endif
183:
184: #ifdef CLOCKDEBUG
185: int clock_debug = 0;
186: #define DPRINTF(arg) if (clock_debug) printf arg
187: #else
188: #define DPRINTF(arg)
189: #endif
190:
1.2 perry 191: int gettick(void);
1.1 perry 192: void sysbeep(int, int);
193: static void tickle_tc(void);
194:
1.4.2.2 ! ad 195: static int clockintr(void *, struct intrframe *);
1.1 perry 196: static void rtcinit(void);
197: static int rtcget(mc_todregs *);
198: static void rtcput(mc_todregs *);
199:
200: static int cmoscheck(void);
201:
202: static int clock_expandyear(int);
203:
204: static inline int gettick_broken_latch(void);
205:
206: static volatile uint32_t i8254_lastcount;
207: static volatile uint32_t i8254_offset;
208: static volatile int i8254_ticked;
209:
210: static struct simplelock tmr_lock = SIMPLELOCK_INITIALIZER; /* protect TC timer variables */
211:
212: inline u_int mc146818_read(void *, u_int);
213: inline void mc146818_write(void *, u_int, u_int);
214:
215: u_int i8254_get_timecount(struct timecounter *);
216: static void rtc_register(void);
217:
218: static struct timecounter i8254_timecounter = {
219: i8254_get_timecount, /* get_timecount */
220: 0, /* no poll_pps */
221: ~0u, /* counter_mask */
222: TIMER_FREQ, /* frequency */
223: "i8254", /* name */
224: 100, /* quality */
225: NULL, /* prev */
226: NULL, /* next */
227: };
228:
229: /* XXX use sc? */
230: inline u_int
231: mc146818_read(void *sc, u_int reg)
232: {
233:
234: outb(IO_RTC, reg);
235: return (inb(IO_RTC+1));
236: }
237:
238: /* XXX use sc? */
239: inline void
240: mc146818_write(void *sc, u_int reg, u_int datum)
241: {
242:
243: outb(IO_RTC, reg);
244: outb(IO_RTC+1, datum);
245: }
246:
247: u_long rtclock_tval; /* i8254 reload value for countdown */
248: int rtclock_init = 0;
249:
250: int clock_broken_latch = 0;
251:
252: #ifdef CLOCK_PARANOIA
253: static int ticks[6];
254: #endif
255: /*
256: * i8254 latch check routine:
257: * National Geode (formerly Cyrix MediaGX) has a serious bug in
258: * its built-in i8254-compatible clock module.
259: * machdep sets the variable 'clock_broken_latch' to indicate it.
260: */
261:
262: int
263: gettick_broken_latch(void)
264: {
265: u_long flags;
266: int v1, v2, v3;
267: int w1, w2, w3;
268:
269: /* Don't want someone screwing with the counter while we're here. */
270: flags = READ_FLAGS();
271: disable_intr();
272:
273: v1 = inb(IO_TIMER1+TIMER_CNTR0);
274: v1 |= inb(IO_TIMER1+TIMER_CNTR0) << 8;
275: v2 = inb(IO_TIMER1+TIMER_CNTR0);
276: v2 |= inb(IO_TIMER1+TIMER_CNTR0) << 8;
277: v3 = inb(IO_TIMER1+TIMER_CNTR0);
278: v3 |= inb(IO_TIMER1+TIMER_CNTR0) << 8;
279:
280: WRITE_FLAGS(flags);
281:
282: #ifdef CLOCK_PARANOIA
283: if (clock_debug) {
284: ticks[0] = ticks[3];
285: ticks[1] = ticks[4];
286: ticks[2] = ticks[5];
287: ticks[3] = v1;
288: ticks[4] = v2;
289: ticks[5] = v3;
290: }
291: #endif
292:
293: if (v1 >= v2 && v2 >= v3 && v1 - v3 < 0x200)
294: return (v2);
295:
296: #define _swap_val(a, b) do { \
297: int c = a; \
298: a = b; \
299: b = c; \
300: } while (0)
301:
302: /*
303: * sort v1 v2 v3
304: */
305: if (v1 < v2)
306: _swap_val(v1, v2);
307: if (v2 < v3)
308: _swap_val(v2, v3);
309: if (v1 < v2)
310: _swap_val(v1, v2);
311:
312: /*
313: * compute the middle value
314: */
315:
316: if (v1 - v3 < 0x200)
317: return (v2);
318:
319: w1 = v2 - v3;
320: w2 = v3 - v1 + rtclock_tval;
321: w3 = v1 - v2;
322: if (w1 >= w2) {
323: if (w1 >= w3)
324: return (v1);
325: } else {
326: if (w2 >= w3)
327: return (v2);
328: }
329: return (v3);
330: }
331:
332: /* minimal initialization, enough for delay() */
333: void
334: initrtclock(u_long freq)
335: {
336: u_long tval;
337: /*
338: * Compute timer_count, the count-down count the timer will be
339: * set to. Also, correctly round
340: * this by carrying an extra bit through the division.
341: */
342: tval = (freq * 2) / (u_long) hz;
343: tval = (tval / 2) + (tval & 0x1);
344:
345: /* initialize 8254 clock */
346: outb(IO_TIMER1+TIMER_MODE, TIMER_SEL0|TIMER_RATEGEN|TIMER_16BIT);
347:
348: /* Correct rounding will buy us a better precision in timekeeping */
349: outb(IO_TIMER1+TIMER_CNTR0, tval % 256);
350: outb(IO_TIMER1+TIMER_CNTR0, tval / 256);
351:
352: rtclock_tval = tval ? tval : 0xFFFF;
353: rtclock_init = 1;
354: }
355:
356: void
357: startrtclock(void)
358: {
359: int s;
360:
361: if (!rtclock_init)
362: initrtclock(TIMER_FREQ);
363:
364: /* Check diagnostic status */
365: if ((s = mc146818_read(NULL, NVRAM_DIAG)) != 0) { /* XXX softc */
366: char bits[128];
367: printf("RTC BIOS diagnostic error %s\n",
368: bitmask_snprintf(s, NVRAM_DIAG_BITS, bits, sizeof(bits)));
369: }
370:
371: tc_init(&i8254_timecounter);
372:
373: #if defined(I586_CPU) || defined(I686_CPU) || defined(__x86_64__)
374: init_TSC();
375: #endif
376:
377: rtc_register();
378: }
379:
380:
381: static void
382: tickle_tc(void)
383: {
384: #if defined(MULTIPROCESSOR)
385: struct cpu_info *ci = curcpu();
386: /*
387: * If we are not the primary CPU, we're not allowed to do
388: * any more work.
389: */
390: if (CPU_IS_PRIMARY(ci) == 0)
391: return;
392: #endif
393: if (rtclock_tval && timecounter->tc_get_timecount == i8254_get_timecount) {
394: simple_lock(&tmr_lock);
395: if (i8254_ticked)
396: i8254_ticked = 0;
397: else {
398: i8254_offset += rtclock_tval;
399: i8254_lastcount = 0;
400: }
401: simple_unlock(&tmr_lock);
402: }
403:
404: }
405:
406: static int
1.4.2.2 ! ad 407: clockintr(void *arg, struct intrframe *frame)
1.1 perry 408: {
409: tickle_tc();
410:
1.4.2.2 ! ad 411: hardclock((struct clockframe *)frame);
1.1 perry 412:
413: #if NMCA > 0
414: if (MCA_system) {
415: /* Reset PS/2 clock interrupt by asserting bit 7 of port 0x61 */
416: outb(0x61, inb(0x61) | 0x80);
417: }
418: #endif
419: return -1;
420: }
421:
422: u_int
423: i8254_get_timecount(struct timecounter *tc)
424: {
425: u_int count;
426: u_char high, low;
427: u_long flags;
428:
429: /* Don't want someone screwing with the counter while we're here. */
430: flags = READ_FLAGS();
431: disable_intr();
432:
433: simple_lock(&tmr_lock);
434:
435: /* Select timer0 and latch counter value. */
436: outb(IO_TIMER1 + TIMER_MODE, TIMER_SEL0 | TIMER_LATCH);
437:
438: low = inb(IO_TIMER1 + TIMER_CNTR0);
439: high = inb(IO_TIMER1 + TIMER_CNTR0);
440: count = rtclock_tval - ((high << 8) | low);
441:
442: if (rtclock_tval && (count < i8254_lastcount || !i8254_ticked)) {
443: i8254_ticked = 1;
444: i8254_offset += rtclock_tval;
445: }
446:
447: i8254_lastcount = count;
448: count += i8254_offset;
449:
450: simple_unlock(&tmr_lock);
451:
452: WRITE_FLAGS(flags);
453: return (count);
454: }
455:
1.2 perry 456: int
1.1 perry 457: gettick(void)
458: {
459: u_long flags;
460: u_char lo, hi;
461:
462: if (clock_broken_latch)
463: return (gettick_broken_latch());
464:
465: /* Don't want someone screwing with the counter while we're here. */
466: flags = READ_FLAGS();
467: disable_intr();
468: /* Select counter 0 and latch it. */
469: outb(IO_TIMER1+TIMER_MODE, TIMER_SEL0 | TIMER_LATCH);
470: lo = inb(IO_TIMER1+TIMER_CNTR0);
471: hi = inb(IO_TIMER1+TIMER_CNTR0);
472: WRITE_FLAGS(flags);
473: return ((hi << 8) | lo);
474: }
475:
476: /*
477: * Wait approximately `n' microseconds.
478: * Relies on timer 1 counting down from (TIMER_FREQ / hz) at TIMER_FREQ Hz.
479: * Note: timer had better have been programmed before this is first used!
480: * (Note that we use `rate generator' mode, which counts at 1:1; `square
481: * wave' mode counts at 2:1).
482: * Don't rely on this being particularly accurate.
483: */
484: void
485: i8254_delay(int n)
486: {
487: int delay_tick, odelay_tick;
488: static const int delaytab[26] = {
489: 0, 2, 3, 4, 5, 6, 7, 9, 10, 11,
490: 12, 13, 15, 16, 17, 18, 19, 21, 22, 23,
491: 24, 25, 27, 28, 29, 30,
492: };
493:
494: /* allow DELAY() to be used before startrtclock() */
495: if (!rtclock_init)
496: initrtclock(TIMER_FREQ);
497:
498: /*
499: * Read the counter first, so that the rest of the setup overhead is
500: * counted.
501: */
502: odelay_tick = gettick();
503:
504: if (n <= 25)
505: n = delaytab[n];
506: else {
507: #ifdef __GNUC__
508: /*
509: * Calculate ((n * TIMER_FREQ) / 1e6) using explicit assembler
510: * code so we can take advantage of the intermediate 64-bit
511: * quantity to prevent loss of significance.
512: */
513: int m;
514: __asm volatile("mul %3"
515: : "=a" (n), "=d" (m)
516: : "0" (n), "r" (TIMER_FREQ));
517: __asm volatile("div %4"
518: : "=a" (n), "=d" (m)
519: : "0" (n), "1" (m), "r" (1000000));
520: #else
521: /*
522: * Calculate ((n * TIMER_FREQ) / 1e6) without using floating
523: * point and without any avoidable overflows.
524: */
525: int sec = n / 1000000,
526: usec = n % 1000000;
527: n = sec * TIMER_FREQ +
528: usec * (TIMER_FREQ / 1000000) +
529: usec * ((TIMER_FREQ % 1000000) / 1000) / 1000 +
530: usec * (TIMER_FREQ % 1000) / 1000000;
531: #endif
532: }
533:
534: while (n > 0) {
535: #ifdef CLOCK_PARANOIA
536: int delta;
537: delay_tick = gettick();
538: if (delay_tick > odelay_tick)
539: delta = rtclock_tval - (delay_tick - odelay_tick);
540: else
541: delta = odelay_tick - delay_tick;
542: if (delta < 0 || delta >= rtclock_tval / 2) {
543: DPRINTF(("delay: ignore ticks %.4x-%.4x",
544: odelay_tick, delay_tick));
545: if (clock_broken_latch) {
546: DPRINTF((" (%.4x %.4x %.4x %.4x %.4x %.4x)\n",
547: ticks[0], ticks[1], ticks[2],
548: ticks[3], ticks[4], ticks[5]));
549: } else {
550: DPRINTF(("\n"));
551: }
552: } else
553: n -= delta;
554: #else
555: delay_tick = gettick();
556: if (delay_tick > odelay_tick)
557: n -= rtclock_tval - (delay_tick - odelay_tick);
558: else
559: n -= odelay_tick - delay_tick;
560: #endif
561: odelay_tick = delay_tick;
562: }
563: }
564:
565: #if (NPCPPI > 0)
566: int
1.4.2.1 ad 567: sysbeepmatch(struct device *parent, struct cfdata *match,
568: void *aux)
1.1 perry 569: {
570: return (!ppi_attached);
571: }
572:
573: void
1.4.2.1 ad 574: sysbeepattach(struct device *parent, struct device *self,
575: void *aux)
1.1 perry 576: {
577: aprint_naive("\n");
578: aprint_normal("\n");
579:
580: ppicookie = ((struct pcppi_attach_args *)aux)->pa_cookie;
581: ppi_attached = 1;
582: }
583: #endif
584:
585: void
586: sysbeep(int pitch, int period)
587: {
588: #if (NPCPPI > 0)
589: if (ppi_attached)
590: pcppi_bell(ppicookie, pitch, period, 0);
591: #endif
592: }
593:
594: void
595: i8254_initclocks(void)
596: {
597:
598: /*
599: * XXX If you're doing strange things with multiple clocks, you might
600: * want to keep track of clock handlers.
601: */
602: (void)isa_intr_establish(NULL, 0, IST_PULSE, IPL_CLOCK,
603: (int (*)(void *))clockintr, 0);
604: }
605:
606: static void
607: rtcinit(void)
608: {
609: static int first_rtcopen_ever = 1;
610:
611: if (!first_rtcopen_ever)
612: return;
613: first_rtcopen_ever = 0;
614:
615: mc146818_write(NULL, MC_REGA, /* XXX softc */
616: MC_BASE_32_KHz | MC_RATE_1024_Hz);
617: mc146818_write(NULL, MC_REGB, MC_REGB_24HR); /* XXX softc */
618: }
619:
620: static int
621: rtcget(mc_todregs *regs)
622: {
623:
624: rtcinit();
625: if ((mc146818_read(NULL, MC_REGD) & MC_REGD_VRT) == 0) /* XXX softc */
626: return (-1);
627: MC146818_GETTOD(NULL, regs); /* XXX softc */
628: return (0);
629: }
630:
631: static void
632: rtcput(mc_todregs *regs)
633: {
634:
635: rtcinit();
636: MC146818_PUTTOD(NULL, regs); /* XXX softc */
637: }
638:
639: /*
640: * check whether the CMOS layout is "standard"-like (ie, not PS/2-like),
641: * to be called at splclock()
642: */
643: static int
644: cmoscheck(void)
645: {
646: int i;
647: unsigned short cksum = 0;
648:
649: for (i = 0x10; i <= 0x2d; i++)
650: cksum += mc146818_read(NULL, i); /* XXX softc */
651:
652: return (cksum == (mc146818_read(NULL, 0x2e) << 8)
653: + mc146818_read(NULL, 0x2f));
654: }
655:
656: #if NMCA > 0
657: /*
658: * Check whether the CMOS layout is PS/2 like, to be called at splclock().
659: */
660: static int cmoscheckps2(void);
661: static int
662: cmoscheckps2(void)
663: {
664: #if 0
665: /* Disabled until I find out the CRC checksum algorithm IBM uses */
666: int i;
667: unsigned short cksum = 0;
668:
669: for (i = 0x10; i <= 0x31; i++)
670: cksum += mc146818_read(NULL, i); /* XXX softc */
671:
672: return (cksum == (mc146818_read(NULL, 0x32) << 8)
673: + mc146818_read(NULL, 0x33));
674: #else
675: /* Check 'incorrect checksum' bit of IBM PS/2 Diagnostic Status Byte */
676: return ((mc146818_read(NULL, NVRAM_DIAG) & (1<<6)) == 0);
677: #endif
678: }
679: #endif /* NMCA > 0 */
680:
681: /*
682: * patchable to control century byte handling:
683: * 1: always update
684: * -1: never touch
685: * 0: try to figure out itself
686: */
687: int rtc_update_century = 0;
688:
689: /*
690: * Expand a two-digit year as read from the clock chip
691: * into full width.
692: * Being here, deal with the CMOS century byte.
693: */
694: static int centb = NVRAM_CENTURY;
695: static int
696: clock_expandyear(int clockyear)
697: {
698: int s, clockcentury, cmoscentury;
699:
700: clockcentury = (clockyear < 70) ? 20 : 19;
701: clockyear += 100 * clockcentury;
702:
703: if (rtc_update_century < 0)
704: return (clockyear);
705:
706: s = splclock();
707: if (cmoscheck())
708: cmoscentury = mc146818_read(NULL, NVRAM_CENTURY);
709: #if NMCA > 0
710: else if (MCA_system && cmoscheckps2())
711: cmoscentury = mc146818_read(NULL, (centb = 0x37));
712: #endif
713: else
714: cmoscentury = 0;
715: splx(s);
716: if (!cmoscentury) {
717: #ifdef DIAGNOSTIC
718: printf("clock: unknown CMOS layout\n");
719: #endif
720: return (clockyear);
721: }
722: cmoscentury = bcdtobin(cmoscentury);
723:
724: if (cmoscentury != clockcentury) {
725: /* XXX note: saying "century is 20" might confuse the naive. */
726: printf("WARNING: NVRAM century is %d but RTC year is %d\n",
727: cmoscentury, clockyear);
728:
729: /* Kludge to roll over century. */
730: if ((rtc_update_century > 0) ||
731: ((cmoscentury == 19) && (clockcentury == 20) &&
732: (clockyear == 2000))) {
733: printf("WARNING: Setting NVRAM century to %d\n",
734: clockcentury);
735: s = splclock();
736: mc146818_write(NULL, centb, bintobcd(clockcentury));
737: splx(s);
738: }
739: } else if (cmoscentury == 19 && rtc_update_century == 0)
740: rtc_update_century = 1; /* will update later in resettodr() */
741:
742: return (clockyear);
743: }
744:
745: static int
1.4 gdamore 746: rtc_get_ymdhms(todr_chip_handle_t tch, struct clock_ymdhms *dt)
1.1 perry 747: {
748: int s;
749: mc_todregs rtclk;
750:
751: s = splclock();
752: if (rtcget(&rtclk)) {
753: splx(s);
754: return -1;
755: }
756: splx(s);
757:
1.4 gdamore 758: dt->dt_sec = bcdtobin(rtclk[MC_SEC]);
759: dt->dt_min = bcdtobin(rtclk[MC_MIN]);
760: dt->dt_hour = bcdtobin(rtclk[MC_HOUR]);
761: dt->dt_day = bcdtobin(rtclk[MC_DOM]);
762: dt->dt_mon = bcdtobin(rtclk[MC_MONTH]);
763: dt->dt_year = clock_expandyear(bcdtobin(rtclk[MC_YEAR]));
1.1 perry 764:
765: return 0;
766: }
767:
768: static int
1.4 gdamore 769: rtc_set_ymdhms(todr_chip_handle_t tch, struct clock_ymdhms *dt)
1.1 perry 770: {
771: mc_todregs rtclk;
772: int century;
773: int s;
774:
775: s = splclock();
776: if (rtcget(&rtclk))
777: memset(&rtclk, 0, sizeof(rtclk));
778: splx(s);
779:
1.4 gdamore 780: rtclk[MC_SEC] = bintobcd(dt->dt_sec);
781: rtclk[MC_MIN] = bintobcd(dt->dt_min);
782: rtclk[MC_HOUR] = bintobcd(dt->dt_hour);
783: rtclk[MC_DOW] = dt->dt_wday + 1;
784: rtclk[MC_YEAR] = bintobcd(dt->dt_year % 100);
785: rtclk[MC_MONTH] = bintobcd(dt->dt_mon);
786: rtclk[MC_DOM] = bintobcd(dt->dt_day);
1.1 perry 787:
788: #ifdef DEBUG_CLOCK
789: printf("setclock: %x/%x/%x %x:%x:%x\n", rtclk[MC_YEAR], rtclk[MC_MONTH],
790: rtclk[MC_DOM], rtclk[MC_HOUR], rtclk[MC_MIN], rtclk[MC_SEC]);
791: #endif
792: s = splclock();
793: rtcput(&rtclk);
794: if (rtc_update_century > 0) {
1.4 gdamore 795: century = bintobcd(dt->dt_year / 100);
1.1 perry 796: mc146818_write(NULL, centb, century); /* XXX softc */
797: }
798: splx(s);
799: return 0;
800:
801: }
802:
803: static void
804: rtc_register(void)
805: {
806: static struct todr_chip_handle tch;
1.4 gdamore 807: tch.todr_gettime_ymdhms = rtc_get_ymdhms;
808: tch.todr_settime_ymdhms = rtc_set_ymdhms;
1.1 perry 809: tch.todr_setwen = NULL;
810:
811: todr_attach(&tch);
812: }
813:
814: void
815: setstatclockrate(int arg)
816: {
817: }
CVSweb <webmaster@jp.NetBSD.org>