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