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