1 | /* $NetBSD: clock.c,v 1.33 2009/06/16 21:05:34 bouyer 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@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.33 2009/06/16 21:05:34 bouyer 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 | #include <sys/cpu.h> |
140 | #include <sys/intr.h> |
141 | |
142 | #include <machine/pio.h> |
143 | #include <machine/cpufunc.h> |
144 | #include <machine/lock.h> |
145 | |
146 | #include <dev/isa/isareg.h> |
147 | #include <dev/isa/isavar.h> |
148 | #include <dev/ic/mc146818reg.h> |
149 | #include <dev/ic/i8253reg.h> |
150 | #include <i386/isa/nvram.h> |
151 | #include <x86/x86/tsc.h> |
152 | #include <x86/lock.h> |
153 | #include <machine/specialreg.h> |
154 | #include <x86/rtc.h> |
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(device_t, cfdata_t, void *); |
168 | void sysbeepattach(device_t, device_t, void *); |
169 | int sysbeepdetach(device_t, int); |
170 | |
171 | CFATTACH_DECL3_NEW(sysbeep, 0, |
172 | sysbeepmatch, sysbeepattach, sysbeepdetach, NULL, NULL, NULL, |
173 | DVF_DETACH_SHUTDOWN); |
174 | |
175 | static int ppi_attached; |
176 | static pcppi_tag_t ppicookie; |
177 | #endif /* PCPPI */ |
178 | |
179 | #ifdef CLOCKDEBUG |
180 | int clock_debug = 0; |
181 | #define DPRINTF(arg) if (clock_debug) printf arg |
182 | #else |
183 | #define DPRINTF(arg) |
184 | #endif |
185 | |
186 | /* Used by lapic.c */ |
187 | unsigned int gettick(void); |
188 | void sysbeep(int, int); |
189 | static void tickle_tc(void); |
190 | |
191 | static int clockintr(void *, struct intrframe *); |
192 | |
193 | int sysbeepdetach(device_t, int); |
194 | |
195 | static unsigned int gettick_broken_latch(void); |
196 | |
197 | static volatile uint32_t i8254_lastcount; |
198 | static volatile uint32_t i8254_offset; |
199 | static volatile int i8254_ticked; |
200 | |
201 | /* to protect TC timer variables */ |
202 | static __cpu_simple_lock_t tmr_lock = __SIMPLELOCK_UNLOCKED; |
203 | |
204 | u_int i8254_get_timecount(struct timecounter *); |
205 | |
206 | static struct timecounter i8254_timecounter = { |
207 | i8254_get_timecount, /* get_timecount */ |
208 | 0, /* no poll_pps */ |
209 | ~0u, /* counter_mask */ |
210 | TIMER_FREQ, /* frequency */ |
211 | "i8254" , /* name */ |
212 | 100, /* quality */ |
213 | NULL, /* private data */ |
214 | NULL, /* next */ |
215 | }; |
216 | |
217 | u_long rtclock_tval; /* i8254 reload value for countdown */ |
218 | int rtclock_init = 0; |
219 | |
220 | int clock_broken_latch = 0; |
221 | |
222 | #ifdef CLOCK_PARANOIA |
223 | static int ticks[6]; |
224 | #endif |
225 | /* |
226 | * i8254 latch check routine: |
227 | * National Geode (formerly Cyrix MediaGX) has a serious bug in |
228 | * its built-in i8254-compatible clock module. |
229 | * machdep sets the variable 'clock_broken_latch' to indicate it. |
230 | */ |
231 | |
232 | static unsigned int |
233 | gettick_broken_latch(void) |
234 | { |
235 | int v1, v2, v3; |
236 | int w1, w2, w3; |
237 | int s; |
238 | |
239 | /* Don't want someone screwing with the counter while we're here. */ |
240 | s = splhigh(); |
241 | __cpu_simple_lock(&tmr_lock); |
242 | v1 = inb(IO_TIMER1+TIMER_CNTR0); |
243 | v1 |= inb(IO_TIMER1+TIMER_CNTR0) << 8; |
244 | v2 = inb(IO_TIMER1+TIMER_CNTR0); |
245 | v2 |= inb(IO_TIMER1+TIMER_CNTR0) << 8; |
246 | v3 = inb(IO_TIMER1+TIMER_CNTR0); |
247 | v3 |= inb(IO_TIMER1+TIMER_CNTR0) << 8; |
248 | __cpu_simple_unlock(&tmr_lock); |
249 | splx(s); |
250 | |
251 | #ifdef CLOCK_PARANOIA |
252 | if (clock_debug) { |
253 | ticks[0] = ticks[3]; |
254 | ticks[1] = ticks[4]; |
255 | ticks[2] = ticks[5]; |
256 | ticks[3] = v1; |
257 | ticks[4] = v2; |
258 | ticks[5] = v3; |
259 | } |
260 | #endif |
261 | |
262 | if (v1 >= v2 && v2 >= v3 && v1 - v3 < 0x200) |
263 | return (v2); |
264 | |
265 | #define _swap_val(a, b) do { \ |
266 | int c = a; \ |
267 | a = b; \ |
268 | b = c; \ |
269 | } while (0) |
270 | |
271 | /* |
272 | * sort v1 v2 v3 |
273 | */ |
274 | if (v1 < v2) |
275 | _swap_val(v1, v2); |
276 | if (v2 < v3) |
277 | _swap_val(v2, v3); |
278 | if (v1 < v2) |
279 | _swap_val(v1, v2); |
280 | |
281 | /* |
282 | * compute the middle value |
283 | */ |
284 | |
285 | if (v1 - v3 < 0x200) |
286 | return (v2); |
287 | |
288 | w1 = v2 - v3; |
289 | w2 = v3 - v1 + rtclock_tval; |
290 | w3 = v1 - v2; |
291 | if (w1 >= w2) { |
292 | if (w1 >= w3) |
293 | return (v1); |
294 | } else { |
295 | if (w2 >= w3) |
296 | return (v2); |
297 | } |
298 | return (v3); |
299 | } |
300 | |
301 | /* minimal initialization, enough for delay() */ |
302 | void |
303 | initrtclock(u_long freq) |
304 | { |
305 | u_long tval; |
306 | |
307 | /* |
308 | * Compute timer_count, the count-down count the timer will be |
309 | * set to. Also, correctly round |
310 | * this by carrying an extra bit through the division. |
311 | */ |
312 | tval = (freq * 2) / (u_long) hz; |
313 | tval = (tval / 2) + (tval & 0x1); |
314 | |
315 | /* initialize 8254 clock */ |
316 | outb(IO_TIMER1+TIMER_MODE, TIMER_SEL0|TIMER_RATEGEN|TIMER_16BIT); |
317 | |
318 | /* Correct rounding will buy us a better precision in timekeeping */ |
319 | outb(IO_TIMER1+TIMER_CNTR0, tval % 256); |
320 | outb(IO_TIMER1+TIMER_CNTR0, tval / 256); |
321 | |
322 | rtclock_tval = tval ? tval : 0xFFFF; |
323 | rtclock_init = 1; |
324 | } |
325 | |
326 | void |
327 | startrtclock(void) |
328 | { |
329 | int s; |
330 | |
331 | if (!rtclock_init) |
332 | initrtclock(TIMER_FREQ); |
333 | |
334 | /* Check diagnostic status */ |
335 | if ((s = mc146818_read(NULL, NVRAM_DIAG)) != 0) { /* XXX softc */ |
336 | char bits[128]; |
337 | snprintb(bits, sizeof(bits), NVRAM_DIAG_BITS, s); |
338 | printf("RTC BIOS diagnostic error %s\n" , bits); |
339 | } |
340 | |
341 | tc_init(&i8254_timecounter); |
342 | rtc_register(); |
343 | } |
344 | |
345 | /* |
346 | * Must be called at splsched(). |
347 | */ |
348 | static void |
349 | tickle_tc(void) |
350 | { |
351 | #if defined(MULTIPROCESSOR) |
352 | struct cpu_info *ci = curcpu(); |
353 | /* |
354 | * If we are not the primary CPU, we're not allowed to do |
355 | * any more work. |
356 | */ |
357 | if (CPU_IS_PRIMARY(ci) == 0) |
358 | return; |
359 | #endif |
360 | if (rtclock_tval && timecounter->tc_get_timecount == i8254_get_timecount) { |
361 | __cpu_simple_lock(&tmr_lock); |
362 | if (i8254_ticked) |
363 | i8254_ticked = 0; |
364 | else { |
365 | i8254_offset += rtclock_tval; |
366 | i8254_lastcount = 0; |
367 | } |
368 | __cpu_simple_unlock(&tmr_lock); |
369 | } |
370 | |
371 | } |
372 | |
373 | static int |
374 | clockintr(void *arg, struct intrframe *frame) |
375 | { |
376 | tickle_tc(); |
377 | |
378 | hardclock((struct clockframe *)frame); |
379 | |
380 | #if NMCA > 0 |
381 | if (MCA_system) { |
382 | /* Reset PS/2 clock interrupt by asserting bit 7 of port 0x61 */ |
383 | outb(0x61, inb(0x61) | 0x80); |
384 | } |
385 | #endif |
386 | return -1; |
387 | } |
388 | |
389 | u_int |
390 | i8254_get_timecount(struct timecounter *tc) |
391 | { |
392 | u_int count; |
393 | uint16_t rdval; |
394 | u_long psl; |
395 | |
396 | /* Don't want someone screwing with the counter while we're here. */ |
397 | psl = x86_read_psl(); |
398 | x86_disable_intr(); |
399 | __cpu_simple_lock(&tmr_lock); |
400 | /* Select timer0 and latch counter value. */ |
401 | outb(IO_TIMER1 + TIMER_MODE, TIMER_SEL0 | TIMER_LATCH); |
402 | /* insb to make the read atomic */ |
403 | rdval = inb(IO_TIMER1+TIMER_CNTR0); |
404 | rdval |= (inb(IO_TIMER1+TIMER_CNTR0) << 8); |
405 | count = rtclock_tval - rdval; |
406 | if (rtclock_tval && (count < i8254_lastcount && |
407 | (!i8254_ticked || rtclock_tval == 0xFFFF))) { |
408 | i8254_ticked = 1; |
409 | i8254_offset += rtclock_tval; |
410 | } |
411 | i8254_lastcount = count; |
412 | count += i8254_offset; |
413 | __cpu_simple_unlock(&tmr_lock); |
414 | x86_write_psl(psl); |
415 | |
416 | return (count); |
417 | } |
418 | |
419 | unsigned int |
420 | gettick(void) |
421 | { |
422 | uint16_t rdval; |
423 | u_long psl; |
424 | |
425 | if (clock_broken_latch) |
426 | return (gettick_broken_latch()); |
427 | |
428 | /* Don't want someone screwing with the counter while we're here. */ |
429 | psl = x86_read_psl(); |
430 | x86_disable_intr(); |
431 | __cpu_simple_lock(&tmr_lock); |
432 | /* Select counter 0 and latch it. */ |
433 | outb(IO_TIMER1+TIMER_MODE, TIMER_SEL0 | TIMER_LATCH); |
434 | rdval = inb(IO_TIMER1+TIMER_CNTR0); |
435 | rdval |= (inb(IO_TIMER1+TIMER_CNTR0) << 8); |
436 | __cpu_simple_unlock(&tmr_lock); |
437 | x86_write_psl(psl); |
438 | |
439 | return rdval; |
440 | } |
441 | |
442 | /* |
443 | * Wait approximately `n' microseconds. |
444 | * Relies on timer 1 counting down from (TIMER_FREQ / hz) at TIMER_FREQ Hz. |
445 | * Note: timer had better have been programmed before this is first used! |
446 | * (Note that we use `rate generator' mode, which counts at 1:1; `square |
447 | * wave' mode counts at 2:1). |
448 | * Don't rely on this being particularly accurate. |
449 | */ |
450 | void |
451 | i8254_delay(unsigned int n) |
452 | { |
453 | unsigned int cur_tick, initial_tick; |
454 | int remaining; |
455 | |
456 | /* allow DELAY() to be used before startrtclock() */ |
457 | if (!rtclock_init) |
458 | initrtclock(TIMER_FREQ); |
459 | |
460 | /* |
461 | * Read the counter first, so that the rest of the setup overhead is |
462 | * counted. |
463 | */ |
464 | initial_tick = gettick(); |
465 | |
466 | if (n <= UINT_MAX / TIMER_FREQ) { |
467 | /* |
468 | * For unsigned arithmetic, division can be replaced with |
469 | * multiplication with the inverse and a shift. |
470 | */ |
471 | remaining = n * TIMER_FREQ / 1000000; |
472 | } else { |
473 | /* This is a very long delay. |
474 | * Being slow here doesn't matter. |
475 | */ |
476 | remaining = (unsigned long long) n * TIMER_FREQ / 1000000; |
477 | } |
478 | |
479 | while (remaining > 1) { |
480 | #ifdef CLOCK_PARANOIA |
481 | int delta; |
482 | cur_tick = gettick(); |
483 | if (cur_tick > initial_tick) |
484 | delta = rtclock_tval - (cur_tick - initial_tick); |
485 | else |
486 | delta = initial_tick - cur_tick; |
487 | if (delta < 0 || delta >= rtclock_tval / 2) { |
488 | DPRINTF(("delay: ignore ticks %.4x-%.4x" , |
489 | initial_tick, cur_tick)); |
490 | if (clock_broken_latch) { |
491 | DPRINTF((" (%.4x %.4x %.4x %.4x %.4x %.4x)\n" , |
492 | ticks[0], ticks[1], ticks[2], |
493 | ticks[3], ticks[4], ticks[5])); |
494 | } else { |
495 | DPRINTF(("\n" )); |
496 | } |
497 | } else |
498 | remaining -= delta; |
499 | #else |
500 | cur_tick = gettick(); |
501 | if (cur_tick > initial_tick) |
502 | remaining -= rtclock_tval - (cur_tick - initial_tick); |
503 | else |
504 | remaining -= initial_tick - cur_tick; |
505 | #endif |
506 | initial_tick = cur_tick; |
507 | } |
508 | } |
509 | |
510 | #if (NPCPPI > 0) |
511 | int |
512 | sysbeepmatch(device_t parent, cfdata_t match, void *aux) |
513 | { |
514 | return (!ppi_attached); |
515 | } |
516 | |
517 | void |
518 | sysbeepattach(device_t parent, device_t self, void *aux) |
519 | { |
520 | aprint_naive("\n" ); |
521 | aprint_normal("\n" ); |
522 | |
523 | ppicookie = ((struct pcppi_attach_args *)aux)->pa_cookie; |
524 | ppi_attached = 1; |
525 | |
526 | if (!pmf_device_register(self, NULL, NULL)) |
527 | aprint_error_dev(self, "couldn't establish power handler\n" ); |
528 | } |
529 | |
530 | int |
531 | sysbeepdetach(device_t self, int flags) |
532 | { |
533 | pmf_device_deregister(self); |
534 | ppi_attached = 0; |
535 | return 0; |
536 | } |
537 | #endif |
538 | |
539 | void |
540 | sysbeep(int pitch, int period) |
541 | { |
542 | #if (NPCPPI > 0) |
543 | if (ppi_attached) |
544 | pcppi_bell(ppicookie, pitch, period, 0); |
545 | #endif |
546 | } |
547 | |
548 | void |
549 | i8254_initclocks(void) |
550 | { |
551 | |
552 | /* |
553 | * XXX If you're doing strange things with multiple clocks, you might |
554 | * want to keep track of clock handlers. |
555 | */ |
556 | (void)isa_intr_establish(NULL, 0, IST_PULSE, IPL_CLOCK, |
557 | (int (*)(void *))clockintr, 0); |
558 | } |
559 | |
560 | void |
561 | setstatclockrate(int arg) |
562 | { |
563 | } |
564 | |