1 /* $OpenBSD: clock.c,v 1.70 2024/06/08 00:24:00 jsg Exp $ */
2 /* $NetBSD: clock.c,v 1.39 1996/05/12 23:11:54 mycroft Exp $ */
3
4 /*-
5 * Copyright (c) 1993, 1994 Charles Hannum.
6 * Copyright (c) 1990 The Regents of the University of California.
7 * All rights reserved.
8 *
9 * This code is derived from software contributed to Berkeley by
10 * William Jolitz and Don Ahn.
11 *
12 * Redistribution and use in source and binary forms, with or without
13 * modification, are permitted provided that the following conditions
14 * are met:
15 * 1. Redistributions of source code must retain the above copyright
16 * notice, this list of conditions and the following disclaimer.
17 * 2. Redistributions in binary form must reproduce the above copyright
18 * notice, this list of conditions and the following disclaimer in the
19 * documentation and/or other materials provided with the distribution.
20 * 3. Neither the name of the University nor the names of its contributors
21 * may be used to endorse or promote products derived from this software
22 * without specific prior written permission.
23 *
24 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
25 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
26 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
27 * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
28 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
29 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
30 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
31 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
32 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
33 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
34 * SUCH DAMAGE.
35 *
36 * @(#)clock.c 7.2 (Berkeley) 5/12/91
37 */
38 /*
39 * Mach Operating System
40 * Copyright (c) 1991,1990,1989 Carnegie Mellon University
41 * All Rights Reserved.
42 *
43 * Permission to use, copy, modify and distribute this software and its
44 * documentation is hereby granted, provided that both the copyright
45 * notice and this permission notice appear in all copies of the
46 * software, derivative works or modified versions, and any portions
47 * thereof, and that both notices appear in supporting documentation.
48 *
49 * CARNEGIE MELLON ALLOWS FREE USE OF THIS SOFTWARE IN ITS "AS IS"
50 * CONDITION. CARNEGIE MELLON DISCLAIMS ANY LIABILITY OF ANY KIND FOR
51 * ANY DAMAGES WHATSOEVER RESULTING FROM THE USE OF THIS SOFTWARE.
52 *
53 * Carnegie Mellon requests users of this software to return to
54 *
55 * Software Distribution Coordinator or Software.Distribution@CS.CMU.EDU
56 * School of Computer Science
57 * Carnegie Mellon University
58 * Pittsburgh PA 15213-3890
59 *
60 * any improvements or extensions that they make and grant Carnegie Mellon
61 * the rights to redistribute these changes.
62 */
63 /*
64 Copyright 1988, 1989 by Intel Corporation, Santa Clara, California.
65
66 All Rights Reserved
67
68 Permission to use, copy, modify, and distribute this software and
69 its documentation for any purpose and without fee is hereby
70 granted, provided that the above copyright notice appears in all
71 copies and that both the copyright notice and this permission notice
72 appear in supporting documentation, and that the name of Intel
73 not be used in advertising or publicity pertaining to distribution
74 of the software without specific, written prior permission.
75
76 INTEL DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE
77 INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS,
78 IN NO EVENT SHALL INTEL BE LIABLE FOR ANY SPECIAL, INDIRECT, OR
79 CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
80 LOSS OF USE, DATA OR PROFITS, WHETHER IN ACTION OF CONTRACT,
81 NEGLIGENCE, OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION
82 WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
83 */
84
85 /*
86 * Primitive clock interrupt routines.
87 */
88 #include <sys/param.h>
89 #include <sys/systm.h>
90 #include <sys/time.h>
91 #include <sys/kernel.h>
92 #include <sys/clockintr.h>
93 #include <sys/timeout.h>
94 #include <sys/timetc.h>
95 #include <sys/mutex.h>
96
97 #include <machine/intr.h>
98 #include <machine/pio.h>
99 #include <machine/cpufunc.h>
100
101 #include <dev/clock_subr.h>
102 #include <dev/isa/isareg.h>
103 #include <dev/isa/isavar.h>
104 #include <dev/ic/mc146818reg.h>
105 #include <dev/ic/i8253reg.h>
106 #include <i386/isa/nvram.h>
107
108 int clockintr(void *);
109 int gettick(void);
110 int rtcget(mc_todregs *);
111 void rtcput(mc_todregs *);
112 int rtcintr(void *);
113 void rtcdrain(void *);
114 int calibrate_cyclecounter_ctr(void);
115
116 u_int mc146818_read(void *, u_int);
117 void mc146818_write(void *, u_int, u_int);
118
119 int cpuspeed;
120 int clock_broken_latch;
121
122 /* Timecounter on the i8254 */
123 uint32_t i8254_lastcount;
124 uint32_t i8254_offset;
125 int i8254_ticked;
126 u_int i8254_get_timecount(struct timecounter *tc);
127 u_int i8254_simple_get_timecount(struct timecounter *tc);
128
129 static struct timecounter i8254_timecounter = {
130 .tc_get_timecount = i8254_get_timecount,
131 .tc_counter_mask = ~0u,
132 .tc_frequency = TIMER_FREQ,
133 .tc_name = "i8254",
134 .tc_quality = 0,
135 .tc_priv = NULL,
136 .tc_user = 0,
137 };
138 struct mutex timer_mutex = MUTEX_INITIALIZER(IPL_HIGH);
139 u_long rtclock_tval;
140
141 u_int
mc146818_read(void * sc,u_int reg)142 mc146818_read(void *sc, u_int reg)
143 {
144 int s;
145 u_char v;
146
147 s = splhigh();
148 outb(IO_RTC, reg);
149 DELAY(1);
150 v = inb(IO_RTC+1);
151 DELAY(1);
152 splx(s);
153 return (v);
154 }
155
156 void
mc146818_write(void * sc,u_int reg,u_int datum)157 mc146818_write(void *sc, u_int reg, u_int datum)
158 {
159 int s;
160
161 s = splhigh();
162 outb(IO_RTC, reg);
163 DELAY(1);
164 outb(IO_RTC+1, datum);
165 DELAY(1);
166 splx(s);
167 }
168
169 void
startclocks(void)170 startclocks(void)
171 {
172 int s;
173
174 mtx_enter(&timer_mutex);
175 rtclock_tval = TIMER_DIV(hz);
176 i8254_startclock();
177 mtx_leave(&timer_mutex);
178
179 /* Check diagnostic status */
180 if ((s = mc146818_read(NULL, NVRAM_DIAG)) != 0) /* XXX softc */
181 printf("RTC BIOS diagnostic error %b\n", (unsigned int) s,
182 NVRAM_DIAG_BITS);
183 }
184
185 void
rtcdrain(void * v)186 rtcdrain(void *v)
187 {
188 struct timeout *to = (struct timeout *)v;
189
190 if (to != NULL)
191 timeout_del(to);
192
193 /* Drain any un-acknowledged RTC interrupts. */
194 while (mc146818_read(NULL, MC_REGC) & MC_REGC_PF)
195 ; /* Nothing. */
196 }
197
198 int
clockintr(void * frame)199 clockintr(void *frame)
200 {
201 if (timecounter->tc_get_timecount == i8254_get_timecount) {
202 if (i8254_ticked) {
203 i8254_ticked = 0;
204 } else {
205 i8254_offset += rtclock_tval;
206 i8254_lastcount = 0;
207 }
208 }
209 clockintr_dispatch(frame);
210 return (1);
211 }
212
213 int
rtcintr(void * frame)214 rtcintr(void *frame)
215 {
216 u_int stat = 0;
217
218 /*
219 * If rtcintr is 'late', next intr may happen immediately.
220 * Get them all. (Also, see comment in cpu_initclocks().)
221 */
222 while (mc146818_read(NULL, MC_REGC) & MC_REGC_PF)
223 stat = 1;
224
225 /* XXX Can rtcintr() run before i8254_initclocks() is complete? */
226 if (stathz != 0 && stat)
227 clockintr_dispatch(frame);
228
229 return (stat);
230 }
231
232 int
gettick(void)233 gettick(void)
234 {
235 u_long s;
236
237 if (clock_broken_latch) {
238 int v1, v2, v3;
239 int w1, w2, w3;
240
241 /*
242 * Don't lock the mutex in this case, clock_broken_latch
243 * CPUs don't do MP anyway.
244 */
245
246 s = intr_disable();
247
248 v1 = inb(IO_TIMER1 + TIMER_CNTR0);
249 v1 |= inb(IO_TIMER1 + TIMER_CNTR0) << 8;
250 v2 = inb(IO_TIMER1 + TIMER_CNTR0);
251 v2 |= inb(IO_TIMER1 + TIMER_CNTR0) << 8;
252 v3 = inb(IO_TIMER1 + TIMER_CNTR0);
253 v3 |= inb(IO_TIMER1 + TIMER_CNTR0) << 8;
254
255 intr_restore(s);
256
257 if (v1 >= v2 && v2 >= v3 && v1 - v3 < 0x200)
258 return (v2);
259
260 #define _swap_val(a, b) do { \
261 int c = a; \
262 a = b; \
263 b = c; \
264 } while (0)
265
266 /* sort v1 v2 v3 */
267 if (v1 < v2)
268 _swap_val(v1, v2);
269 if (v2 < v3)
270 _swap_val(v2, v3);
271 if (v1 < v2)
272 _swap_val(v1, v2);
273
274 /* compute the middle value */
275 if (v1 - v3 < 0x200)
276 return (v2);
277 w1 = v2 - v3;
278 w2 = v3 - v1 + TIMER_DIV(hz);
279 w3 = v1 - v2;
280 if (w1 >= w2) {
281 if (w1 >= w3)
282 return (v1);
283 } else {
284 if (w2 >= w3)
285 return (v2);
286 }
287 return (v3);
288 } else {
289 u_char lo, hi;
290
291 mtx_enter(&timer_mutex);
292 s = intr_disable();
293 /* Select counter 0 and latch it. */
294 outb(IO_TIMER1 + TIMER_MODE, TIMER_SEL0 | TIMER_LATCH);
295 lo = inb(IO_TIMER1 + TIMER_CNTR0);
296 hi = inb(IO_TIMER1 + TIMER_CNTR0);
297
298 intr_restore(s);
299 mtx_leave(&timer_mutex);
300 return ((hi << 8) | lo);
301 }
302 }
303
304 /*
305 * Wait "n" microseconds.
306 * Relies on timer 1 counting down from (TIMER_FREQ / hz) at TIMER_FREQ Hz.
307 * Note: timer had better have been programmed before this is first used!
308 * (Note that we use `rate generator' mode, which counts at 1:1; `square
309 * wave' mode counts at 2:1).
310 */
311 void
i8254_delay(int n)312 i8254_delay(int n)
313 {
314 int limit, tick, otick;
315
316 /*
317 * Read the counter first, so that the rest of the setup overhead is
318 * counted.
319 */
320 otick = gettick();
321
322 #ifdef __GNUC__
323 /*
324 * Calculate ((n * TIMER_FREQ) / 1e6) using explicit assembler code so
325 * we can take advantage of the intermediate 64-bit quantity to prevent
326 * loss of significance.
327 */
328 n -= 5;
329 if (n < 0)
330 return;
331 __asm volatile("mul %2\n\tdiv %3"
332 : "=a" (n)
333 : "0" (n), "r" (TIMER_FREQ), "r" (1000000)
334 : "%edx", "cc");
335 #else
336 /*
337 * Calculate ((n * TIMER_FREQ) / 1e6) without using floating point and
338 * without any avoidable overflows.
339 */
340 n -= 20;
341 {
342 int sec = n / 1000000,
343 usec = n % 1000000;
344 n = sec * TIMER_FREQ +
345 usec * (TIMER_FREQ / 1000000) +
346 usec * ((TIMER_FREQ % 1000000) / 1000) / 1000 +
347 usec * (TIMER_FREQ % 1000) / 1000000;
348 }
349 #endif
350
351 limit = TIMER_FREQ / hz;
352
353 while (n > 0) {
354 tick = gettick();
355 if (tick > otick)
356 n -= limit - (tick - otick);
357 else
358 n -= otick - tick;
359 otick = tick;
360 }
361 }
362
363 int
calibrate_cyclecounter_ctr(void)364 calibrate_cyclecounter_ctr(void)
365 {
366 struct cpu_info *ci = curcpu();
367 unsigned long long count, last_count, msr;
368
369 if ((ci->ci_flags & CPUF_CONST_TSC) == 0 ||
370 (cpu_perf_eax & CPUIDEAX_VERID) <= 1 ||
371 CPUIDEDX_NUM_FC(cpu_perf_edx) <= 1)
372 return (-1);
373
374 msr = rdmsr(MSR_PERF_FIXED_CTR_CTRL);
375 if (msr & MSR_PERF_FIXED_CTR_FC(1, MSR_PERF_FIXED_CTR_FC_MASK)) {
376 /* some hypervisor is dicking us around */
377 return (-1);
378 }
379
380 msr |= MSR_PERF_FIXED_CTR_FC(1, MSR_PERF_FIXED_CTR_FC_1);
381 wrmsr(MSR_PERF_FIXED_CTR_CTRL, msr);
382
383 msr = rdmsr(MSR_PERF_GLOBAL_CTRL) | MSR_PERF_GLOBAL_CTR1_EN;
384 wrmsr(MSR_PERF_GLOBAL_CTRL, msr);
385
386 last_count = rdmsr(MSR_PERF_FIXED_CTR1);
387 delay(1000000);
388 count = rdmsr(MSR_PERF_FIXED_CTR1);
389
390 msr = rdmsr(MSR_PERF_FIXED_CTR_CTRL);
391 msr &= MSR_PERF_FIXED_CTR_FC(1, MSR_PERF_FIXED_CTR_FC_MASK);
392 wrmsr(MSR_PERF_FIXED_CTR_CTRL, msr);
393
394 msr = rdmsr(MSR_PERF_GLOBAL_CTRL);
395 msr &= ~MSR_PERF_GLOBAL_CTR1_EN;
396 wrmsr(MSR_PERF_GLOBAL_CTRL, msr);
397
398 cpuspeed = ((count - last_count) + 999999) / 1000000;
399
400 return (cpuspeed == 0 ? -1 : 0);
401 }
402
403 void
calibrate_cyclecounter(void)404 calibrate_cyclecounter(void)
405 {
406 unsigned long long count, last_count;
407
408 if (calibrate_cyclecounter_ctr() == 0)
409 return;
410
411 __asm volatile("rdtsc" : "=A" (last_count));
412 delay(1000000);
413 __asm volatile("rdtsc" : "=A" (count));
414
415 cpuspeed = ((count - last_count) + 999999) / 1000000;
416 }
417
418 void
i8254_initclocks(void)419 i8254_initclocks(void)
420 {
421 i8254_inittimecounter(); /* hook the interrupt-based i8254 tc */
422
423 stathz = 128;
424 profhz = 1024; /* XXX does not divide into 1 billion */
425 }
426
427 void
i8254_start_both_clocks(void)428 i8254_start_both_clocks(void)
429 {
430 clockintr_cpu_init(NULL);
431
432 /*
433 * When using i8254 for clock, we also use the rtc for profclock.
434 *
435 * These IRQs are not MP-safe, but it is harmless to lie about it
436 * because we cannot reach this point unless we are only booting
437 * a single CPU.
438 */
439 (void)isa_intr_establish(NULL, 0, IST_PULSE, IPL_CLOCK | IPL_MPSAFE,
440 clockintr, 0, "clock");
441 (void)isa_intr_establish(NULL, 8, IST_PULSE, IPL_STATCLOCK | IPL_MPSAFE,
442 rtcintr, 0, "rtc");
443
444 rtcstart(); /* start the mc146818 clock */
445 }
446
447 void
rtcstart(void)448 rtcstart(void)
449 {
450 static struct timeout rtcdrain_timeout;
451
452 mc146818_write(NULL, MC_REGA, MC_BASE_32_KHz | MC_RATE_128_Hz);
453 mc146818_write(NULL, MC_REGB, MC_REGB_24HR | MC_REGB_PIE);
454
455 /*
456 * On a number of i386 systems, the rtc will fail to start when booting
457 * the system. This is due to us missing to acknowledge an interrupt
458 * during early stages of the boot process. If we do not acknowledge
459 * the interrupt, the rtc clock will not generate further interrupts.
460 * To solve this, once interrupts are enabled, use a timeout (once)
461 * to drain any un-acknowledged rtc interrupt(s).
462 */
463
464 timeout_set(&rtcdrain_timeout, rtcdrain, (void *)&rtcdrain_timeout);
465 timeout_add(&rtcdrain_timeout, 1);
466 }
467
468 void
rtcstop(void)469 rtcstop(void)
470 {
471 mc146818_write(NULL, MC_REGB, MC_REGB_24HR);
472 }
473
474 int
rtcget(mc_todregs * regs)475 rtcget(mc_todregs *regs)
476 {
477 if ((mc146818_read(NULL, MC_REGD) & MC_REGD_VRT) == 0) /* XXX softc */
478 return (-1);
479 MC146818_GETTOD(NULL, regs); /* XXX softc */
480 return (0);
481 }
482
483 void
rtcput(mc_todregs * regs)484 rtcput(mc_todregs *regs)
485 {
486 MC146818_PUTTOD(NULL, regs); /* XXX softc */
487 }
488
489 int
bcdtobin(int n)490 bcdtobin(int n)
491 {
492 return (((n >> 4) & 0x0f) * 10 + (n & 0x0f));
493 }
494
495 int
bintobcd(int n)496 bintobcd(int n)
497 {
498 return ((u_char)(((n / 10) << 4) & 0xf0) | ((n % 10) & 0x0f));
499 }
500
501 /*
502 * check whether the CMOS layout is "standard"-like (ie, not PS/2-like),
503 * to be called at splclock()
504 */
505 int cmoscheck(void);
506 int
cmoscheck(void)507 cmoscheck(void)
508 {
509 int i;
510 unsigned short cksum = 0;
511
512 for (i = 0x10; i <= 0x2d; i++)
513 cksum += mc146818_read(NULL, i); /* XXX softc */
514
515 return (cksum == (mc146818_read(NULL, 0x2e) << 8)
516 + mc146818_read(NULL, 0x2f));
517 }
518
519 /*
520 * patchable to control century byte handling:
521 * 1: always update
522 * -1: never touch
523 * 0: try to figure out itself
524 */
525 int rtc_update_century = 0;
526
527 /*
528 * Expand a two-digit year as read from the clock chip
529 * into full width.
530 * Being here, deal with the CMOS century byte.
531 */
532 int clock_expandyear(int);
533 int
clock_expandyear(int clockyear)534 clock_expandyear(int clockyear)
535 {
536 int s, clockcentury, cmoscentury;
537
538 clockcentury = (clockyear < 70) ? 20 : 19;
539 clockyear += 100 * clockcentury;
540
541 if (rtc_update_century < 0)
542 return (clockyear);
543
544 s = splclock();
545 if (cmoscheck())
546 cmoscentury = mc146818_read(NULL, NVRAM_CENTURY);
547 else
548 cmoscentury = 0;
549 splx(s);
550 if (!cmoscentury) {
551 #ifdef DIAGNOSTIC
552 printf("clock: unknown CMOS layout\n");
553 #endif
554 return (clockyear);
555 }
556 cmoscentury = bcdtobin(cmoscentury);
557
558 if (cmoscentury != clockcentury) {
559 /* XXX note: saying "century is 20" might confuse the naive. */
560 printf("WARNING: NVRAM century is %d but RTC year is %d\n",
561 cmoscentury, clockyear);
562
563 /* Kludge to roll over century. */
564 if ((rtc_update_century > 0) ||
565 ((cmoscentury == 19) && (clockcentury == 20) &&
566 (clockyear == 2000))) {
567 printf("WARNING: Setting NVRAM century to %d\n",
568 clockcentury);
569 s = splclock();
570 mc146818_write(NULL, NVRAM_CENTURY,
571 bintobcd(clockcentury));
572 splx(s);
573 }
574 } else if (cmoscentury == 19 && rtc_update_century == 0)
575 rtc_update_century = 1; /* will update later in resettodr() */
576
577 return (clockyear);
578 }
579
580 int
rtcgettime(struct todr_chip_handle * handle,struct timeval * tv)581 rtcgettime(struct todr_chip_handle *handle, struct timeval *tv)
582 {
583 mc_todregs rtclk;
584 struct clock_ymdhms dt;
585 int s;
586
587 s = splclock();
588 if (rtcget(&rtclk)) {
589 splx(s);
590 return EINVAL;
591 }
592 splx(s);
593
594 #ifdef CLOCK_DEBUG
595 printf("readclock: %x/%x/%x %x:%x:%x\n", rtclk[MC_YEAR],
596 rtclk[MC_MONTH], rtclk[MC_DOM], rtclk[MC_HOUR], rtclk[MC_MIN],
597 rtclk[MC_SEC]);
598 #endif
599
600 dt.dt_sec = bcdtobin(rtclk[MC_SEC]);
601 dt.dt_min = bcdtobin(rtclk[MC_MIN]);
602 dt.dt_hour = bcdtobin(rtclk[MC_HOUR]);
603 dt.dt_day = bcdtobin(rtclk[MC_DOM]);
604 dt.dt_mon = bcdtobin(rtclk[MC_MONTH]);
605 dt.dt_year = clock_expandyear(bcdtobin(rtclk[MC_YEAR]));
606
607 tv->tv_sec = clock_ymdhms_to_secs(&dt) - utc_offset;
608 tv->tv_usec = 0;
609 return 0;
610 }
611
612 int
rtcsettime(struct todr_chip_handle * handle,struct timeval * tv)613 rtcsettime(struct todr_chip_handle *handle, struct timeval *tv)
614 {
615 mc_todregs rtclk;
616 struct clock_ymdhms dt;
617 int century, s;
618
619 s = splclock();
620 if (rtcget(&rtclk))
621 memset(&rtclk, 0, sizeof(rtclk));
622 splx(s);
623
624 clock_secs_to_ymdhms(tv->tv_sec + utc_offset, &dt);
625
626 rtclk[MC_SEC] = bintobcd(dt.dt_sec);
627 rtclk[MC_MIN] = bintobcd(dt.dt_min);
628 rtclk[MC_HOUR] = bintobcd(dt.dt_hour);
629 rtclk[MC_DOW] = dt.dt_wday + 1;
630 rtclk[MC_YEAR] = bintobcd(dt.dt_year % 100);
631 rtclk[MC_MONTH] = bintobcd(dt.dt_mon);
632 rtclk[MC_DOM] = bintobcd(dt.dt_day);
633
634 #ifdef CLOCK_DEBUG
635 printf("setclock: %x/%x/%x %x:%x:%x\n", rtclk[MC_YEAR], rtclk[MC_MONTH],
636 rtclk[MC_DOM], rtclk[MC_HOUR], rtclk[MC_MIN], rtclk[MC_SEC]);
637 #endif
638
639 s = splclock();
640 rtcput(&rtclk);
641 if (rtc_update_century > 0) {
642 century = bintobcd(dt.dt_year / 100);
643 mc146818_write(NULL, NVRAM_CENTURY, century); /* XXX softc */
644 }
645 splx(s);
646 return 0;
647 }
648
649 struct todr_chip_handle rtc_todr;
650
651 void
rtcinit(void)652 rtcinit(void)
653 {
654 rtc_todr.todr_gettime = rtcgettime;
655 rtc_todr.todr_settime = rtcsettime;
656 rtc_todr.todr_quality = 0;
657 todr_attach(&rtc_todr);
658 }
659
660 void
setstatclockrate(int arg)661 setstatclockrate(int arg)
662 {
663 if (initclock_func == i8254_initclocks) {
664 if (arg == stathz)
665 mc146818_write(NULL, MC_REGA,
666 MC_BASE_32_KHz | MC_RATE_128_Hz);
667 else
668 mc146818_write(NULL, MC_REGA,
669 MC_BASE_32_KHz | MC_RATE_1024_Hz);
670 }
671 }
672
673 void
i8254_inittimecounter(void)674 i8254_inittimecounter(void)
675 {
676 tc_init(&i8254_timecounter);
677 }
678
679 /*
680 * If we're using lapic to drive hardclock, we can use a simpler
681 * algorithm for the i8254 timecounters.
682 */
683 void
i8254_inittimecounter_simple(void)684 i8254_inittimecounter_simple(void)
685 {
686 i8254_timecounter.tc_get_timecount = i8254_simple_get_timecount;
687 i8254_timecounter.tc_counter_mask = 0x7fff;
688 i8254_timecounter.tc_frequency = TIMER_FREQ;
689
690 mtx_enter(&timer_mutex);
691 rtclock_tval = 0x8000;
692 i8254_startclock();
693 mtx_leave(&timer_mutex);
694
695 tc_init(&i8254_timecounter);
696 }
697
698 void
i8254_startclock(void)699 i8254_startclock(void)
700 {
701 u_long tval = rtclock_tval;
702
703 outb(IO_TIMER1 + TIMER_MODE, TIMER_SEL0 | TIMER_RATEGEN | TIMER_16BIT);
704 outb(IO_TIMER1 + TIMER_CNTR0, tval & 0xff);
705 outb(IO_TIMER1 + TIMER_CNTR0, tval >> 8);
706 }
707
708 u_int
i8254_simple_get_timecount(struct timecounter * tc)709 i8254_simple_get_timecount(struct timecounter *tc)
710 {
711 return (rtclock_tval - gettick());
712 }
713
714 u_int
i8254_get_timecount(struct timecounter * tc)715 i8254_get_timecount(struct timecounter *tc)
716 {
717 u_char hi, lo;
718 u_int count;
719 u_long s;
720
721 s = intr_disable();
722
723 outb(IO_TIMER1 + TIMER_MODE, TIMER_SEL0 | TIMER_LATCH);
724 lo = inb(IO_TIMER1 + TIMER_CNTR0);
725 hi = inb(IO_TIMER1 + TIMER_CNTR0);
726
727 count = rtclock_tval - ((hi << 8) | lo);
728
729 if (count < i8254_lastcount) {
730 i8254_ticked = 1;
731 i8254_offset += rtclock_tval;
732 }
733 i8254_lastcount = count;
734 count += i8254_offset;
735
736 intr_restore(s);
737
738 return (count);
739 }
740