Annotation of src/sys/arch/x86/isa/clock.c, Revision 1.8.2.5
1.8.2.5 ! yamt 1: /* $NetBSD: clock.c,v 1.8.2.4 2007/11/15 11:43:39 yamt Exp $ */
1.8.2.2 yamt 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.8.2.5 ! yamt 124: __KERNEL_RCSID(0, "$NetBSD: clock.c,v 1.8.2.4 2007/11/15 11:43:39 yamt Exp $");
1.8.2.2 yamt 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>
1.8.2.3 yamt 138: #include <sys/mutex.h>
1.8.2.2 yamt 139:
140: #include <machine/cpu.h>
141: #include <machine/intr.h>
142: #include <machine/pio.h>
143: #include <machine/cpufunc.h>
144:
145: #include <dev/isa/isareg.h>
146: #include <dev/isa/isavar.h>
147: #include <dev/ic/mc146818reg.h>
148: #include <dev/ic/i8253reg.h>
149: #include <i386/isa/nvram.h>
150: #include <x86/x86/tsc.h>
151: #include <dev/clock_subr.h>
152: #include <machine/specialreg.h>
153:
154: #include "config_time.h" /* for CONFIG_TIME */
155:
156: #ifndef __x86_64__
157: #include "mca.h"
158: #endif
159: #if NMCA > 0
160: #include <machine/mca_machdep.h> /* for MCA_system */
161: #endif
162:
163: #include "pcppi.h"
164: #if (NPCPPI > 0)
165: #include <dev/isa/pcppivar.h>
166:
167: int sysbeepmatch(struct device *, struct cfdata *, void *);
168: void sysbeepattach(struct device *, struct device *, void *);
169:
170: CFATTACH_DECL(sysbeep, sizeof(struct device),
171: sysbeepmatch, sysbeepattach, NULL, NULL);
172:
173: static int ppi_attached;
174: static pcppi_tag_t ppicookie;
175: #endif /* PCPPI */
176:
177: #ifdef CLOCKDEBUG
178: int clock_debug = 0;
179: #define DPRINTF(arg) if (clock_debug) printf arg
180: #else
181: #define DPRINTF(arg)
182: #endif
183:
1.8.2.4 yamt 184: /* Used by lapic.c */
185: unsigned int gettick(void);
1.8.2.2 yamt 186: void sysbeep(int, int);
187: static void tickle_tc(void);
188:
189: static int clockintr(void *, struct intrframe *);
190: static void rtcinit(void);
191: static int rtcget(mc_todregs *);
192: static void rtcput(mc_todregs *);
193:
194: static int cmoscheck(void);
195:
196: static int clock_expandyear(int);
197:
1.8.2.4 yamt 198: static unsigned int gettick_broken_latch(void);
1.8.2.2 yamt 199:
200: static volatile uint32_t i8254_lastcount;
201: static volatile uint32_t i8254_offset;
202: static volatile int i8254_ticked;
203:
1.8.2.3 yamt 204: /* to protect TC timer variables */
205: static __cpu_simple_lock_t tmr_lock = __SIMPLELOCK_UNLOCKED;
1.8.2.2 yamt 206:
207: inline u_int mc146818_read(void *, u_int);
208: inline void mc146818_write(void *, u_int, u_int);
209:
210: u_int i8254_get_timecount(struct timecounter *);
211: static void rtc_register(void);
212:
213: static struct timecounter i8254_timecounter = {
214: i8254_get_timecount, /* get_timecount */
215: 0, /* no poll_pps */
216: ~0u, /* counter_mask */
217: TIMER_FREQ, /* frequency */
218: "i8254", /* name */
219: 100, /* quality */
220: NULL, /* prev */
221: NULL, /* next */
222: };
223:
224: /* XXX use sc? */
225: inline u_int
226: mc146818_read(void *sc, u_int reg)
227: {
228:
229: outb(IO_RTC, reg);
230: return (inb(IO_RTC+1));
231: }
232:
233: /* XXX use sc? */
234: inline void
235: mc146818_write(void *sc, u_int reg, u_int datum)
236: {
237:
238: outb(IO_RTC, reg);
239: outb(IO_RTC+1, datum);
240: }
241:
242: u_long rtclock_tval; /* i8254 reload value for countdown */
243: int rtclock_init = 0;
244:
245: int clock_broken_latch = 0;
246:
247: #ifdef CLOCK_PARANOIA
248: static int ticks[6];
249: #endif
250: /*
251: * i8254 latch check routine:
252: * National Geode (formerly Cyrix MediaGX) has a serious bug in
253: * its built-in i8254-compatible clock module.
254: * machdep sets the variable 'clock_broken_latch' to indicate it.
255: */
256:
1.8.2.4 yamt 257: static unsigned int
1.8.2.2 yamt 258: gettick_broken_latch(void)
259: {
260: u_long flags;
261: int v1, v2, v3;
262: int w1, w2, w3;
263:
264: /* Don't want someone screwing with the counter while we're here. */
1.8.2.4 yamt 265: flags = x86_read_psl();
266: x86_disable_intr();
1.8.2.2 yamt 267:
268: v1 = inb(IO_TIMER1+TIMER_CNTR0);
269: v1 |= inb(IO_TIMER1+TIMER_CNTR0) << 8;
270: v2 = inb(IO_TIMER1+TIMER_CNTR0);
271: v2 |= inb(IO_TIMER1+TIMER_CNTR0) << 8;
272: v3 = inb(IO_TIMER1+TIMER_CNTR0);
273: v3 |= inb(IO_TIMER1+TIMER_CNTR0) << 8;
274:
1.8.2.4 yamt 275: x86_write_psl(flags);
1.8.2.2 yamt 276:
277: #ifdef CLOCK_PARANOIA
278: if (clock_debug) {
279: ticks[0] = ticks[3];
280: ticks[1] = ticks[4];
281: ticks[2] = ticks[5];
282: ticks[3] = v1;
283: ticks[4] = v2;
284: ticks[5] = v3;
285: }
286: #endif
287:
288: if (v1 >= v2 && v2 >= v3 && v1 - v3 < 0x200)
289: return (v2);
290:
291: #define _swap_val(a, b) do { \
292: int c = a; \
293: a = b; \
294: b = c; \
295: } while (0)
296:
297: /*
298: * sort v1 v2 v3
299: */
300: if (v1 < v2)
301: _swap_val(v1, v2);
302: if (v2 < v3)
303: _swap_val(v2, v3);
304: if (v1 < v2)
305: _swap_val(v1, v2);
306:
307: /*
308: * compute the middle value
309: */
310:
311: if (v1 - v3 < 0x200)
312: return (v2);
313:
314: w1 = v2 - v3;
315: w2 = v3 - v1 + rtclock_tval;
316: w3 = v1 - v2;
317: if (w1 >= w2) {
318: if (w1 >= w3)
319: return (v1);
320: } else {
321: if (w2 >= w3)
322: return (v2);
323: }
324: return (v3);
325: }
326:
327: /* minimal initialization, enough for delay() */
328: void
329: initrtclock(u_long freq)
330: {
331: u_long tval;
1.8.2.3 yamt 332:
1.8.2.2 yamt 333: /*
334: * Compute timer_count, the count-down count the timer will be
335: * set to. Also, correctly round
336: * this by carrying an extra bit through the division.
337: */
338: tval = (freq * 2) / (u_long) hz;
339: tval = (tval / 2) + (tval & 0x1);
340:
341: /* initialize 8254 clock */
342: outb(IO_TIMER1+TIMER_MODE, TIMER_SEL0|TIMER_RATEGEN|TIMER_16BIT);
343:
344: /* Correct rounding will buy us a better precision in timekeeping */
345: outb(IO_TIMER1+TIMER_CNTR0, tval % 256);
346: outb(IO_TIMER1+TIMER_CNTR0, tval / 256);
347:
348: rtclock_tval = tval ? tval : 0xFFFF;
349: rtclock_init = 1;
350: }
351:
352: void
353: startrtclock(void)
354: {
355: int s;
356:
357: if (!rtclock_init)
358: initrtclock(TIMER_FREQ);
359:
360: /* Check diagnostic status */
361: if ((s = mc146818_read(NULL, NVRAM_DIAG)) != 0) { /* XXX softc */
362: char bits[128];
363: printf("RTC BIOS diagnostic error %s\n",
364: bitmask_snprintf(s, NVRAM_DIAG_BITS, bits, sizeof(bits)));
365: }
366:
367: tc_init(&i8254_timecounter);
368:
369: init_TSC();
370: rtc_register();
371: }
372:
1.8.2.3 yamt 373: /*
374: * Must be called at splclock().
375: */
1.8.2.2 yamt 376: static void
377: tickle_tc(void)
378: {
379: #if defined(MULTIPROCESSOR)
380: struct cpu_info *ci = curcpu();
381: /*
382: * If we are not the primary CPU, we're not allowed to do
383: * any more work.
384: */
385: if (CPU_IS_PRIMARY(ci) == 0)
386: return;
387: #endif
388: if (rtclock_tval && timecounter->tc_get_timecount == i8254_get_timecount) {
1.8.2.3 yamt 389: __cpu_simple_lock(&tmr_lock);
1.8.2.2 yamt 390: if (i8254_ticked)
391: i8254_ticked = 0;
392: else {
393: i8254_offset += rtclock_tval;
394: i8254_lastcount = 0;
395: }
1.8.2.3 yamt 396: __cpu_simple_unlock(&tmr_lock);
1.8.2.2 yamt 397: }
398:
399: }
400:
401: static int
402: clockintr(void *arg, struct intrframe *frame)
403: {
404: tickle_tc();
405:
406: hardclock((struct clockframe *)frame);
407:
408: #if NMCA > 0
409: if (MCA_system) {
410: /* Reset PS/2 clock interrupt by asserting bit 7 of port 0x61 */
411: outb(0x61, inb(0x61) | 0x80);
412: }
413: #endif
414: return -1;
415: }
416:
417: u_int
418: i8254_get_timecount(struct timecounter *tc)
419: {
420: u_int count;
421: u_char high, low;
422: u_long flags;
423:
424: /* Don't want someone screwing with the counter while we're here. */
1.8.2.4 yamt 425: flags = x86_read_psl();
426: x86_disable_intr();
1.8.2.3 yamt 427: __cpu_simple_lock(&tmr_lock);
1.8.2.2 yamt 428:
429: /* Select timer0 and latch counter value. */
430: outb(IO_TIMER1 + TIMER_MODE, TIMER_SEL0 | TIMER_LATCH);
431:
432: low = inb(IO_TIMER1 + TIMER_CNTR0);
433: high = inb(IO_TIMER1 + TIMER_CNTR0);
434: count = rtclock_tval - ((high << 8) | low);
435:
436: if (rtclock_tval && (count < i8254_lastcount || !i8254_ticked)) {
437: i8254_ticked = 1;
438: i8254_offset += rtclock_tval;
439: }
440:
441: i8254_lastcount = count;
442: count += i8254_offset;
443:
1.8.2.3 yamt 444: __cpu_simple_unlock(&tmr_lock);
1.8.2.4 yamt 445: x86_write_psl(flags);
1.8.2.3 yamt 446:
1.8.2.2 yamt 447: return (count);
448: }
449:
1.8.2.4 yamt 450: unsigned int
1.8.2.2 yamt 451: gettick(void)
452: {
453: u_long flags;
454: u_char lo, hi;
455:
456: if (clock_broken_latch)
457: return (gettick_broken_latch());
458:
459: /* Don't want someone screwing with the counter while we're here. */
1.8.2.4 yamt 460: flags = x86_read_psl();
461: x86_disable_intr();
1.8.2.2 yamt 462: /* Select counter 0 and latch it. */
463: outb(IO_TIMER1+TIMER_MODE, TIMER_SEL0 | TIMER_LATCH);
464: lo = inb(IO_TIMER1+TIMER_CNTR0);
465: hi = inb(IO_TIMER1+TIMER_CNTR0);
1.8.2.4 yamt 466: x86_write_psl(flags);
1.8.2.2 yamt 467: return ((hi << 8) | lo);
468: }
469:
470: /*
471: * Wait approximately `n' microseconds.
472: * Relies on timer 1 counting down from (TIMER_FREQ / hz) at TIMER_FREQ Hz.
473: * Note: timer had better have been programmed before this is first used!
474: * (Note that we use `rate generator' mode, which counts at 1:1; `square
475: * wave' mode counts at 2:1).
476: * Don't rely on this being particularly accurate.
477: */
478: void
1.8.2.4 yamt 479: i8254_delay(unsigned int n)
1.8.2.2 yamt 480: {
1.8.2.4 yamt 481: unsigned int cur_tick, initial_tick;
482: int remaining;
1.8.2.2 yamt 483: static const int delaytab[26] = {
484: 0, 2, 3, 4, 5, 6, 7, 9, 10, 11,
485: 12, 13, 15, 16, 17, 18, 19, 21, 22, 23,
486: 24, 25, 27, 28, 29, 30,
487: };
488:
489: /* allow DELAY() to be used before startrtclock() */
490: if (!rtclock_init)
491: initrtclock(TIMER_FREQ);
492:
493: /*
494: * Read the counter first, so that the rest of the setup overhead is
495: * counted.
496: */
1.8.2.4 yamt 497: initial_tick = gettick();
1.8.2.2 yamt 498:
499: if (n <= 25)
1.8.2.4 yamt 500: remaining = delaytab[n];
501: else if (n <= UINT_MAX / TIMER_FREQ) {
1.8.2.2 yamt 502: /*
1.8.2.4 yamt 503: * For unsigned arithmetic, division can be replaced with
504: * multiplication with the inverse and a shift.
1.8.2.2 yamt 505: */
1.8.2.4 yamt 506: remaining = n * TIMER_FREQ / 1000000;
507: } else {
508: /* This is a very long delay.
509: * Being slow here doesn't matter.
1.8.2.2 yamt 510: */
1.8.2.4 yamt 511: remaining = (unsigned long long) n * TIMER_FREQ / 1000000;
1.8.2.2 yamt 512: }
513:
1.8.2.4 yamt 514: while (remaining > 0) {
1.8.2.2 yamt 515: #ifdef CLOCK_PARANOIA
516: int delta;
1.8.2.4 yamt 517: cur_tick = gettick();
518: if (cur_tick > initial_tick)
519: delta = rtclock_tval - (cur_tick - initial_tick);
1.8.2.2 yamt 520: else
1.8.2.4 yamt 521: delta = initial_tick - cur_tick;
1.8.2.2 yamt 522: if (delta < 0 || delta >= rtclock_tval / 2) {
523: DPRINTF(("delay: ignore ticks %.4x-%.4x",
1.8.2.4 yamt 524: initial_tick, cur_tick));
1.8.2.2 yamt 525: if (clock_broken_latch) {
526: DPRINTF((" (%.4x %.4x %.4x %.4x %.4x %.4x)\n",
527: ticks[0], ticks[1], ticks[2],
528: ticks[3], ticks[4], ticks[5]));
529: } else {
530: DPRINTF(("\n"));
531: }
532: } else
1.8.2.4 yamt 533: remaining -= delta;
1.8.2.2 yamt 534: #else
1.8.2.4 yamt 535: cur_tick = gettick();
536: if (cur_tick > initial_tick)
537: remaining -= rtclock_tval - (cur_tick - initial_tick);
1.8.2.2 yamt 538: else
1.8.2.4 yamt 539: remaining -= initial_tick - cur_tick;
1.8.2.2 yamt 540: #endif
1.8.2.4 yamt 541: initial_tick = cur_tick;
1.8.2.2 yamt 542: }
543: }
544:
545: #if (NPCPPI > 0)
546: int
547: sysbeepmatch(struct device *parent, struct cfdata *match,
548: void *aux)
549: {
550: return (!ppi_attached);
551: }
552:
553: void
554: sysbeepattach(struct device *parent, struct device *self,
555: void *aux)
556: {
557: aprint_naive("\n");
558: aprint_normal("\n");
559:
560: ppicookie = ((struct pcppi_attach_args *)aux)->pa_cookie;
561: ppi_attached = 1;
562: }
563: #endif
564:
565: void
566: sysbeep(int pitch, int period)
567: {
568: #if (NPCPPI > 0)
569: if (ppi_attached)
570: pcppi_bell(ppicookie, pitch, period, 0);
571: #endif
572: }
573:
574: void
575: i8254_initclocks(void)
576: {
577:
578: /*
579: * XXX If you're doing strange things with multiple clocks, you might
580: * want to keep track of clock handlers.
581: */
582: (void)isa_intr_establish(NULL, 0, IST_PULSE, IPL_CLOCK,
583: (int (*)(void *))clockintr, 0);
584: }
585:
586: static void
587: rtcinit(void)
588: {
589: static int first_rtcopen_ever = 1;
590:
591: if (!first_rtcopen_ever)
592: return;
593: first_rtcopen_ever = 0;
594:
595: mc146818_write(NULL, MC_REGA, /* XXX softc */
596: MC_BASE_32_KHz | MC_RATE_1024_Hz);
597: mc146818_write(NULL, MC_REGB, MC_REGB_24HR); /* XXX softc */
598: }
599:
600: static int
601: rtcget(mc_todregs *regs)
602: {
603:
604: rtcinit();
605: if ((mc146818_read(NULL, MC_REGD) & MC_REGD_VRT) == 0) /* XXX softc */
606: return (-1);
607: MC146818_GETTOD(NULL, regs); /* XXX softc */
608: return (0);
609: }
610:
611: static void
612: rtcput(mc_todregs *regs)
613: {
614:
615: rtcinit();
616: MC146818_PUTTOD(NULL, regs); /* XXX softc */
617: }
618:
619: /*
620: * check whether the CMOS layout is "standard"-like (ie, not PS/2-like),
621: * to be called at splclock()
622: */
623: static int
624: cmoscheck(void)
625: {
626: int i;
627: unsigned short cksum = 0;
628:
629: for (i = 0x10; i <= 0x2d; i++)
630: cksum += mc146818_read(NULL, i); /* XXX softc */
631:
632: return (cksum == (mc146818_read(NULL, 0x2e) << 8)
633: + mc146818_read(NULL, 0x2f));
634: }
635:
636: #if NMCA > 0
637: /*
638: * Check whether the CMOS layout is PS/2 like, to be called at splclock().
639: */
640: static int cmoscheckps2(void);
641: static int
642: cmoscheckps2(void)
643: {
644: #if 0
645: /* Disabled until I find out the CRC checksum algorithm IBM uses */
646: int i;
647: unsigned short cksum = 0;
648:
649: for (i = 0x10; i <= 0x31; i++)
650: cksum += mc146818_read(NULL, i); /* XXX softc */
651:
652: return (cksum == (mc146818_read(NULL, 0x32) << 8)
653: + mc146818_read(NULL, 0x33));
654: #else
655: /* Check 'incorrect checksum' bit of IBM PS/2 Diagnostic Status Byte */
656: return ((mc146818_read(NULL, NVRAM_DIAG) & (1<<6)) == 0);
657: #endif
658: }
659: #endif /* NMCA > 0 */
660:
661: /*
662: * patchable to control century byte handling:
663: * 1: always update
664: * -1: never touch
665: * 0: try to figure out itself
666: */
667: int rtc_update_century = 0;
668:
669: /*
670: * Expand a two-digit year as read from the clock chip
671: * into full width.
672: * Being here, deal with the CMOS century byte.
673: */
674: static int centb = NVRAM_CENTURY;
675: static int
676: clock_expandyear(int clockyear)
677: {
678: int s, clockcentury, cmoscentury;
679:
680: clockcentury = (clockyear < 70) ? 20 : 19;
681: clockyear += 100 * clockcentury;
682:
683: if (rtc_update_century < 0)
684: return (clockyear);
685:
686: s = splclock();
687: if (cmoscheck())
688: cmoscentury = mc146818_read(NULL, NVRAM_CENTURY);
689: #if NMCA > 0
690: else if (MCA_system && cmoscheckps2())
691: cmoscentury = mc146818_read(NULL, (centb = 0x37));
692: #endif
693: else
694: cmoscentury = 0;
695: splx(s);
696: if (!cmoscentury) {
697: #ifdef DIAGNOSTIC
698: printf("clock: unknown CMOS layout\n");
699: #endif
700: return (clockyear);
701: }
702: cmoscentury = bcdtobin(cmoscentury);
703:
704: if (cmoscentury != clockcentury) {
705: /* XXX note: saying "century is 20" might confuse the naive. */
706: printf("WARNING: NVRAM century is %d but RTC year is %d\n",
707: cmoscentury, clockyear);
708:
709: /* Kludge to roll over century. */
710: if ((rtc_update_century > 0) ||
711: ((cmoscentury == 19) && (clockcentury == 20) &&
712: (clockyear == 2000))) {
713: printf("WARNING: Setting NVRAM century to %d\n",
714: clockcentury);
715: s = splclock();
716: mc146818_write(NULL, centb, bintobcd(clockcentury));
717: splx(s);
718: }
719: } else if (cmoscentury == 19 && rtc_update_century == 0)
720: rtc_update_century = 1; /* will update later in resettodr() */
721:
722: return (clockyear);
723: }
724:
725: static int
726: rtc_get_ymdhms(todr_chip_handle_t tch, struct clock_ymdhms *dt)
727: {
728: int s;
729: mc_todregs rtclk;
730:
731: s = splclock();
732: if (rtcget(&rtclk)) {
733: splx(s);
734: return -1;
735: }
736: splx(s);
737:
738: dt->dt_sec = bcdtobin(rtclk[MC_SEC]);
739: dt->dt_min = bcdtobin(rtclk[MC_MIN]);
740: dt->dt_hour = bcdtobin(rtclk[MC_HOUR]);
741: dt->dt_day = bcdtobin(rtclk[MC_DOM]);
742: dt->dt_mon = bcdtobin(rtclk[MC_MONTH]);
743: dt->dt_year = clock_expandyear(bcdtobin(rtclk[MC_YEAR]));
744:
745: return 0;
746: }
747:
748: static int
749: rtc_set_ymdhms(todr_chip_handle_t tch, struct clock_ymdhms *dt)
750: {
751: mc_todregs rtclk;
752: int century;
753: int s;
754:
755: s = splclock();
756: if (rtcget(&rtclk))
757: memset(&rtclk, 0, sizeof(rtclk));
758: splx(s);
759:
760: rtclk[MC_SEC] = bintobcd(dt->dt_sec);
761: rtclk[MC_MIN] = bintobcd(dt->dt_min);
762: rtclk[MC_HOUR] = bintobcd(dt->dt_hour);
763: rtclk[MC_DOW] = dt->dt_wday + 1;
764: rtclk[MC_YEAR] = bintobcd(dt->dt_year % 100);
765: rtclk[MC_MONTH] = bintobcd(dt->dt_mon);
766: rtclk[MC_DOM] = bintobcd(dt->dt_day);
767:
768: #ifdef DEBUG_CLOCK
769: printf("setclock: %x/%x/%x %x:%x:%x\n", rtclk[MC_YEAR], rtclk[MC_MONTH],
770: rtclk[MC_DOM], rtclk[MC_HOUR], rtclk[MC_MIN], rtclk[MC_SEC]);
771: #endif
772: s = splclock();
773: rtcput(&rtclk);
774: if (rtc_update_century > 0) {
775: century = bintobcd(dt->dt_year / 100);
776: mc146818_write(NULL, centb, century); /* XXX softc */
777: }
778: splx(s);
779: return 0;
780:
781: }
782:
783: static void
784: rtc_register(void)
785: {
786: static struct todr_chip_handle tch;
787: tch.todr_gettime_ymdhms = rtc_get_ymdhms;
788: tch.todr_settime_ymdhms = rtc_set_ymdhms;
789: tch.todr_setwen = NULL;
790:
791: todr_attach(&tch);
792: }
793:
794: void
795: setstatclockrate(int arg)
796: {
797: }
CVSweb <webmaster@jp.NetBSD.org>