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