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