Line data Source code
1 : /* $OpenBSD: clock.c,v 1.28 2018/07/27 21:11:31 kettenis Exp $ */
2 : /* $NetBSD: clock.c,v 1.1 2003/04/26 18:39:50 fvdl Exp $ */
3 :
4 : /*-
5 : * Copyright (c) 1993, 1994 Charles M. 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 :
89 : /* #define CLOCKDEBUG */
90 : /* #define CLOCK_PARANOIA */
91 :
92 : #include <sys/param.h>
93 : #include <sys/systm.h>
94 : #include <sys/time.h>
95 : #include <sys/kernel.h>
96 : #include <sys/timeout.h>
97 : #include <sys/timetc.h>
98 :
99 : #include <machine/cpu.h>
100 : #include <machine/intr.h>
101 : #include <machine/pio.h>
102 : #include <machine/cpufunc.h>
103 :
104 : #include <dev/isa/isareg.h>
105 : #include <dev/isa/isavar.h>
106 : #include <dev/ic/mc146818reg.h>
107 : #include <dev/ic/i8253reg.h>
108 : #include <amd64/isa/nvram.h>
109 :
110 : /* Timecounter on the i8254 */
111 : u_int32_t i8254_lastcount;
112 : u_int32_t i8254_offset;
113 : int i8254_ticked;
114 : u_int i8254_get_timecount(struct timecounter *tc);
115 :
116 : u_int i8254_simple_get_timecount(struct timecounter *tc);
117 :
118 : static struct timecounter i8254_timecounter = {
119 : i8254_get_timecount, NULL, ~0u, TIMER_FREQ, "i8254", 0, NULL
120 : };
121 :
122 : int clockintr(void *);
123 : int rtcintr(void *);
124 : int gettick(void);
125 : void rtcdrain(void *v);
126 : int rtcget(mc_todregs *);
127 : void rtcput(mc_todregs *);
128 : int bcdtobin(int);
129 : int bintobcd(int);
130 :
131 : u_int mc146818_read(void *, u_int);
132 : void mc146818_write(void *, u_int, u_int);
133 :
134 : u_int
135 0 : mc146818_read(void *sc, u_int reg)
136 : {
137 0 : outb(IO_RTC, reg);
138 0 : DELAY(1);
139 0 : return (inb(IO_RTC+1));
140 : }
141 :
142 : void
143 0 : mc146818_write(void *sc, u_int reg, u_int datum)
144 : {
145 0 : outb(IO_RTC, reg);
146 0 : DELAY(1);
147 0 : outb(IO_RTC+1, datum);
148 0 : DELAY(1);
149 0 : }
150 :
151 : struct mutex timer_mutex = MUTEX_INITIALIZER(IPL_HIGH);
152 :
153 : u_long rtclock_tval;
154 :
155 : void
156 0 : startclocks(void)
157 : {
158 0 : mtx_enter(&timer_mutex);
159 0 : rtclock_tval = TIMER_DIV(hz);
160 0 : i8254_startclock();
161 0 : mtx_leave(&timer_mutex);
162 0 : }
163 :
164 : int
165 0 : clockintr(void *arg)
166 : {
167 0 : struct clockframe *frame = arg;
168 :
169 0 : if (timecounter->tc_get_timecount == i8254_get_timecount) {
170 0 : if (i8254_ticked) {
171 0 : i8254_ticked = 0;
172 0 : } else {
173 0 : i8254_offset += rtclock_tval;
174 0 : i8254_lastcount = 0;
175 : }
176 : }
177 :
178 0 : hardclock(frame);
179 :
180 0 : return 1;
181 : }
182 :
183 : int
184 0 : rtcintr(void *arg)
185 : {
186 0 : struct clockframe *frame = arg;
187 : u_int stat = 0;
188 :
189 : /*
190 : * If rtcintr is 'late', next intr may happen immediately.
191 : * Get them all. (Also, see comment in cpu_initclocks().)
192 : */
193 0 : while (mc146818_read(NULL, MC_REGC) & MC_REGC_PF) {
194 0 : statclock(frame);
195 : stat = 1;
196 : }
197 :
198 0 : return (stat);
199 : }
200 :
201 : int
202 0 : gettick(void)
203 : {
204 : u_long s;
205 : u_char lo, hi;
206 :
207 : /* Don't want someone screwing with the counter while we're here. */
208 0 : mtx_enter(&timer_mutex);
209 0 : s = intr_disable();
210 : /* Select counter 0 and latch it. */
211 0 : outb(IO_TIMER1+TIMER_MODE, TIMER_SEL0 | TIMER_LATCH);
212 0 : lo = inb(IO_TIMER1+TIMER_CNTR0);
213 0 : hi = inb(IO_TIMER1+TIMER_CNTR0);
214 0 : intr_restore(s);
215 0 : mtx_leave(&timer_mutex);
216 0 : return ((hi << 8) | lo);
217 : }
218 :
219 : /*
220 : * Wait "n" microseconds.
221 : * Relies on timer 1 counting down from (TIMER_FREQ / hz) at TIMER_FREQ Hz.
222 : * Note: timer had better have been programmed before this is first used!
223 : * (Note that we use `rate generator' mode, which counts at 1:1; `square
224 : * wave' mode counts at 2:1).
225 : */
226 : void
227 0 : i8254_delay(int n)
228 : {
229 : int limit, tick, otick;
230 : static const int delaytab[26] = {
231 : 0, 2, 3, 4, 5, 6, 7, 9, 10, 11,
232 : 12, 13, 15, 16, 17, 18, 19, 21, 22, 23,
233 : 24, 25, 27, 28, 29, 30,
234 : };
235 :
236 : /*
237 : * Read the counter first, so that the rest of the setup overhead is
238 : * counted.
239 : */
240 0 : otick = gettick();
241 :
242 0 : if (n <= 25)
243 0 : n = delaytab[n];
244 : else {
245 : #ifdef __GNUC__
246 : /*
247 : * Calculate ((n * TIMER_FREQ) / 1e6) using explicit assembler
248 : * code so we can take advantage of the intermediate 64-bit
249 : * quantity to prevent loss of significance.
250 : */
251 : int m;
252 0 : __asm volatile("mul %3"
253 : : "=a" (n), "=d" (m)
254 : : "0" (n), "r" (TIMER_FREQ));
255 0 : __asm volatile("div %4"
256 : : "=a" (n), "=d" (m)
257 : : "0" (n), "1" (m), "r" (1000000));
258 : #else
259 : /*
260 : * Calculate ((n * TIMER_FREQ) / 1e6) without using floating
261 : * point and without any avoidable overflows.
262 : */
263 : int sec = n / 1000000,
264 : usec = n % 1000000;
265 : n = sec * TIMER_FREQ +
266 : usec * (TIMER_FREQ / 1000000) +
267 : usec * ((TIMER_FREQ % 1000000) / 1000) / 1000 +
268 : usec * (TIMER_FREQ % 1000) / 1000000;
269 : #endif
270 : }
271 :
272 0 : limit = TIMER_FREQ / hz;
273 :
274 0 : while (n > 0) {
275 0 : tick = gettick();
276 0 : if (tick > otick)
277 0 : n -= limit - (tick - otick);
278 : else
279 0 : n -= otick - tick;
280 : otick = tick;
281 : }
282 0 : }
283 :
284 : void
285 0 : rtcdrain(void *v)
286 : {
287 0 : struct timeout *to = (struct timeout *)v;
288 :
289 0 : if (to != NULL)
290 0 : timeout_del(to);
291 :
292 : /*
293 : * Drain any un-acknowledged RTC interrupts.
294 : * See comment in cpu_initclocks().
295 : */
296 0 : while (mc146818_read(NULL, MC_REGC) & MC_REGC_PF)
297 : ; /* Nothing. */
298 0 : }
299 :
300 : void
301 0 : i8254_initclocks(void)
302 : {
303 0 : stathz = 128;
304 0 : profhz = 1024;
305 :
306 : /*
307 : * While the clock interrupt handler isn't really MPSAFE, the
308 : * i8254 can't really be used as a clock on a true MP system.
309 : */
310 0 : isa_intr_establish(NULL, 0, IST_PULSE, IPL_CLOCK | IPL_MPSAFE,
311 : clockintr, 0, "clock");
312 0 : isa_intr_establish(NULL, 8, IST_PULSE, IPL_STATCLOCK | IPL_MPSAFE,
313 : rtcintr, 0, "rtc");
314 :
315 0 : rtcstart(); /* start the mc146818 clock */
316 :
317 0 : i8254_inittimecounter(); /* hook the interrupt-based i8254 tc */
318 0 : }
319 :
320 : void
321 0 : rtcstart(void)
322 : {
323 : static struct timeout rtcdrain_timeout;
324 :
325 0 : mc146818_write(NULL, MC_REGA, MC_BASE_32_KHz | MC_RATE_128_Hz);
326 0 : mc146818_write(NULL, MC_REGB, MC_REGB_24HR | MC_REGB_PIE);
327 :
328 : /*
329 : * On a number of i386 systems, the rtc will fail to start when booting
330 : * the system. This is due to us missing to acknowledge an interrupt
331 : * during early stages of the boot process. If we do not acknowledge
332 : * the interrupt, the rtc clock will not generate further interrupts.
333 : * To solve this, once interrupts are enabled, use a timeout (once)
334 : * to drain any un-acknowledged rtc interrupt(s).
335 : */
336 0 : timeout_set(&rtcdrain_timeout, rtcdrain, (void *)&rtcdrain_timeout);
337 0 : timeout_add(&rtcdrain_timeout, 1);
338 0 : }
339 :
340 : void
341 0 : rtcstop(void)
342 : {
343 0 : mc146818_write(NULL, MC_REGB, MC_REGB_24HR);
344 0 : }
345 :
346 : int
347 0 : rtcget(mc_todregs *regs)
348 : {
349 0 : if ((mc146818_read(NULL, MC_REGD) & MC_REGD_VRT) == 0) /* XXX softc */
350 0 : return (-1);
351 0 : MC146818_GETTOD(NULL, regs); /* XXX softc */
352 0 : return (0);
353 0 : }
354 :
355 : void
356 0 : rtcput(mc_todregs *regs)
357 : {
358 0 : MC146818_PUTTOD(NULL, regs); /* XXX softc */
359 0 : }
360 :
361 : int
362 0 : bcdtobin(int n)
363 : {
364 0 : return (((n >> 4) & 0x0f) * 10 + (n & 0x0f));
365 : }
366 :
367 : int
368 0 : bintobcd(int n)
369 : {
370 0 : return ((u_char)(((n / 10) << 4) & 0xf0) | ((n % 10) & 0x0f));
371 : }
372 :
373 : static int timeset;
374 :
375 : /*
376 : * check whether the CMOS layout is "standard"-like (ie, not PS/2-like),
377 : * to be called at splclock()
378 : */
379 : static int cmoscheck(void);
380 : static int
381 0 : cmoscheck(void)
382 : {
383 : int i;
384 : unsigned short cksum = 0;
385 :
386 0 : for (i = 0x10; i <= 0x2d; i++)
387 0 : cksum += mc146818_read(NULL, i); /* XXX softc */
388 :
389 0 : return (cksum == (mc146818_read(NULL, 0x2e) << 8)
390 0 : + mc146818_read(NULL, 0x2f));
391 : }
392 :
393 : /*
394 : * patchable to control century byte handling:
395 : * 1: always update
396 : * -1: never touch
397 : * 0: try to figure out itself
398 : */
399 : int rtc_update_century = 0;
400 :
401 : /*
402 : * Expand a two-digit year as read from the clock chip
403 : * into full width.
404 : * Being here, deal with the CMOS century byte.
405 : */
406 : static int centb = NVRAM_CENTURY;
407 : static int clock_expandyear(int);
408 : static int
409 0 : clock_expandyear(int clockyear)
410 : {
411 : int s, clockcentury, cmoscentury;
412 :
413 0 : clockcentury = (clockyear < 70) ? 20 : 19;
414 0 : clockyear += 100 * clockcentury;
415 :
416 0 : if (rtc_update_century < 0)
417 0 : return (clockyear);
418 :
419 0 : s = splclock();
420 0 : if (cmoscheck())
421 0 : cmoscentury = mc146818_read(NULL, NVRAM_CENTURY);
422 : else
423 : cmoscentury = 0;
424 0 : splx(s);
425 0 : if (!cmoscentury)
426 0 : return (clockyear);
427 :
428 0 : cmoscentury = bcdtobin(cmoscentury);
429 :
430 0 : if (cmoscentury != clockcentury) {
431 : /* XXX note: saying "century is 20" might confuse the naive. */
432 0 : printf("WARNING: NVRAM century is %d but RTC year is %d\n",
433 : cmoscentury, clockyear);
434 :
435 : /* Kludge to roll over century. */
436 0 : if ((rtc_update_century > 0) ||
437 0 : ((cmoscentury == 19) && (clockcentury == 20) &&
438 0 : (clockyear == 2000))) {
439 0 : printf("WARNING: Setting NVRAM century to %d\n",
440 : clockcentury);
441 0 : s = splclock();
442 0 : mc146818_write(NULL, centb, bintobcd(clockcentury));
443 0 : splx(s);
444 0 : }
445 0 : } else if (cmoscentury == 19 && rtc_update_century == 0)
446 0 : rtc_update_century = 1; /* will update later in resettodr() */
447 :
448 0 : return (clockyear);
449 0 : }
450 :
451 : /*
452 : * Initialize the time of day register, based on the time base which is, e.g.
453 : * from a filesystem.
454 : */
455 : void
456 0 : inittodr(time_t base)
457 : {
458 0 : struct timespec ts;
459 0 : mc_todregs rtclk;
460 0 : struct clock_ymdhms dt;
461 : int s;
462 :
463 0 : ts.tv_nsec = 0;
464 :
465 : /*
466 : * We mostly ignore the suggested time (which comes from the
467 : * file system) and go for the RTC clock time stored in the
468 : * CMOS RAM. If the time can't be obtained from the CMOS, or
469 : * if the time obtained from the CMOS is 5 or more years less
470 : * than the suggested time, we used the suggested time. (In
471 : * the latter case, it's likely that the CMOS battery has
472 : * died.)
473 : */
474 :
475 : /*
476 : * if the file system time is more than a year older than the
477 : * kernel, warn and then set the base time to the CONFIG_TIME.
478 : */
479 0 : if (base < 30*SECYR) { /* if before 2000, something's odd... */
480 0 : printf("WARNING: preposterous time in file system\n");
481 : base = 30*SECYR;
482 0 : }
483 :
484 0 : s = splclock();
485 0 : if (rtcget(&rtclk)) {
486 : splx(s);
487 0 : printf("WARNING: invalid time in clock chip\n");
488 0 : goto fstime;
489 : }
490 : splx(s);
491 : #ifdef DEBUG_CLOCK
492 : printf("readclock: %x/%x/%x %x:%x:%x\n", rtclk[MC_YEAR],
493 : rtclk[MC_MONTH], rtclk[MC_DOM], rtclk[MC_HOUR], rtclk[MC_MIN],
494 : rtclk[MC_SEC]);
495 : #endif
496 :
497 0 : dt.dt_sec = bcdtobin(rtclk[MC_SEC]);
498 0 : dt.dt_min = bcdtobin(rtclk[MC_MIN]);
499 0 : dt.dt_hour = bcdtobin(rtclk[MC_HOUR]);
500 0 : dt.dt_day = bcdtobin(rtclk[MC_DOM]);
501 0 : dt.dt_mon = bcdtobin(rtclk[MC_MONTH]);
502 0 : dt.dt_year = clock_expandyear(bcdtobin(rtclk[MC_YEAR]));
503 :
504 0 : ts.tv_sec = clock_ymdhms_to_secs(&dt) + tz.tz_minuteswest * 60;
505 0 : if (tz.tz_dsttime)
506 0 : ts.tv_sec -= 3600;
507 :
508 0 : if (base != 0 && base < ts.tv_sec - 5*SECYR)
509 0 : printf("WARNING: file system time much less than clock time\n");
510 0 : else if (base > ts.tv_sec + 5*SECYR) {
511 0 : printf("WARNING: clock time much less than file system time\n");
512 0 : printf("WARNING: using file system time\n");
513 0 : goto fstime;
514 : }
515 :
516 0 : tc_setclock(&ts);
517 0 : timeset = 1;
518 0 : return;
519 :
520 : fstime:
521 0 : ts.tv_sec = base;
522 0 : tc_setclock(&ts);
523 0 : timeset = 1;
524 0 : printf("WARNING: CHECK AND RESET THE DATE!\n");
525 0 : }
526 :
527 : /*
528 : * Reset the clock.
529 : */
530 : void
531 0 : resettodr(void)
532 : {
533 0 : mc_todregs rtclk;
534 0 : struct clock_ymdhms dt;
535 : int century, diff, s;
536 :
537 : /*
538 : * We might have been called by boot() due to a crash early
539 : * on. Don't reset the clock chip in this case.
540 : */
541 0 : if (!timeset)
542 0 : return;
543 :
544 0 : s = splclock();
545 0 : if (rtcget(&rtclk))
546 0 : memset(&rtclk, 0, sizeof(rtclk));
547 0 : splx(s);
548 :
549 0 : diff = tz.tz_minuteswest * 60;
550 0 : if (tz.tz_dsttime)
551 0 : diff -= 3600;
552 0 : clock_secs_to_ymdhms(time_second - diff, &dt);
553 :
554 0 : rtclk[MC_SEC] = bintobcd(dt.dt_sec);
555 0 : rtclk[MC_MIN] = bintobcd(dt.dt_min);
556 0 : rtclk[MC_HOUR] = bintobcd(dt.dt_hour);
557 0 : rtclk[MC_DOW] = dt.dt_wday + 1;
558 0 : rtclk[MC_YEAR] = bintobcd(dt.dt_year % 100);
559 0 : rtclk[MC_MONTH] = bintobcd(dt.dt_mon);
560 0 : rtclk[MC_DOM] = bintobcd(dt.dt_day);
561 :
562 : #ifdef DEBUG_CLOCK
563 : printf("setclock: %x/%x/%x %x:%x:%x\n", rtclk[MC_YEAR], rtclk[MC_MONTH],
564 : rtclk[MC_DOM], rtclk[MC_HOUR], rtclk[MC_MIN], rtclk[MC_SEC]);
565 : #endif
566 0 : s = splclock();
567 0 : rtcput(&rtclk);
568 0 : if (rtc_update_century > 0) {
569 0 : century = bintobcd(dt.dt_year / 100);
570 0 : mc146818_write(NULL, centb, century); /* XXX softc */
571 0 : }
572 0 : splx(s);
573 0 : }
574 :
575 : void
576 0 : setstatclockrate(int arg)
577 : {
578 0 : if (initclock_func == i8254_initclocks) {
579 0 : if (arg == stathz)
580 0 : mc146818_write(NULL, MC_REGA,
581 : MC_BASE_32_KHz | MC_RATE_128_Hz);
582 : else
583 0 : mc146818_write(NULL, MC_REGA,
584 : MC_BASE_32_KHz | MC_RATE_1024_Hz);
585 : }
586 0 : }
587 :
588 : void
589 0 : i8254_inittimecounter(void)
590 : {
591 0 : tc_init(&i8254_timecounter);
592 0 : }
593 :
594 : /*
595 : * If we're using lapic to drive hardclock, we can use a simpler
596 : * algorithm for the i8254 timecounters.
597 : */
598 : void
599 0 : i8254_inittimecounter_simple(void)
600 : {
601 0 : i8254_timecounter.tc_get_timecount = i8254_simple_get_timecount;
602 0 : i8254_timecounter.tc_counter_mask = 0x7fff;
603 0 : i8254_timecounter.tc_frequency = TIMER_FREQ;
604 :
605 0 : mtx_enter(&timer_mutex);
606 0 : rtclock_tval = 0x8000;
607 0 : i8254_startclock();
608 0 : mtx_leave(&timer_mutex);
609 :
610 0 : tc_init(&i8254_timecounter);
611 0 : }
612 :
613 : void
614 0 : i8254_startclock(void)
615 : {
616 0 : u_long tval = rtclock_tval;
617 :
618 0 : outb(IO_TIMER1 + TIMER_MODE, TIMER_SEL0 | TIMER_RATEGEN | TIMER_16BIT);
619 0 : outb(IO_TIMER1 + TIMER_CNTR0, tval & 0xff);
620 0 : outb(IO_TIMER1 + TIMER_CNTR0, tval >> 8);
621 0 : }
622 :
623 : u_int
624 0 : i8254_simple_get_timecount(struct timecounter *tc)
625 : {
626 0 : return (rtclock_tval - gettick());
627 : }
628 :
629 : u_int
630 0 : i8254_get_timecount(struct timecounter *tc)
631 : {
632 : u_char hi, lo;
633 : u_int count;
634 : u_long s;
635 :
636 0 : s = intr_disable();
637 :
638 0 : outb(IO_TIMER1+TIMER_MODE, TIMER_SEL0 | TIMER_LATCH);
639 0 : lo = inb(IO_TIMER1+TIMER_CNTR0);
640 0 : hi = inb(IO_TIMER1+TIMER_CNTR0);
641 :
642 0 : count = rtclock_tval - ((hi << 8) | lo);
643 :
644 0 : if (count < i8254_lastcount) {
645 0 : i8254_ticked = 1;
646 0 : i8254_offset += rtclock_tval;
647 0 : }
648 0 : i8254_lastcount = count;
649 0 : count += i8254_offset;
650 :
651 0 : intr_restore(s);
652 :
653 0 : return (count);
654 : }
|