clock.c revision 1.13 1 /* $NetBSD: clock.c,v 1.13 2006/09/13 07:13:03 gdamore Exp $ */
2
3 /*
4 * Copyright 1997
5 * Digital Equipment Corporation. All rights reserved.
6 *
7 * This software is furnished under license and may be used and
8 * copied only in accordance with the following terms and conditions.
9 * Subject to these conditions, you may download, copy, install,
10 * use, modify and distribute this software in source and/or binary
11 * form. No title or ownership is transferred hereby.
12 *
13 * 1) Any source code used, modified or distributed must reproduce
14 * and retain this copyright notice and list of conditions as
15 * they appear in the source file.
16 *
17 * 2) No right is granted to use any trade name, trademark, or logo of
18 * Digital Equipment Corporation. Neither the "Digital Equipment
19 * Corporation" name nor any trademark or logo of Digital Equipment
20 * Corporation may be used to endorse or promote products derived
21 * from this software without the prior written permission of
22 * Digital Equipment Corporation.
23 *
24 * 3) This software is provided "AS-IS" and any express or implied
25 * warranties, including but not limited to, any implied warranties
26 * of merchantability, fitness for a particular purpose, or
27 * non-infringement are disclaimed. In no event shall DIGITAL be
28 * liable for any damages whatsoever, and in particular, DIGITAL
29 * shall not be liable for special, indirect, consequential, or
30 * incidental damages or damages for lost profits, loss of
31 * revenue or loss of use, whether such damages arise in contract,
32 * negligence, tort, under statute, in equity, at law or otherwise,
33 * even if advised of the possibility of such damage.
34 */
35
36 /*-
37 * Copyright (c) 1990 The Regents of the University of California.
38 * All rights reserved.
39 *
40 * This code is derived from software contributed to Berkeley by
41 * William Jolitz and Don Ahn.
42 *
43 * Redistribution and use in source and binary forms, with or without
44 * modification, are permitted provided that the following conditions
45 * are met:
46 * 1. Redistributions of source code must retain the above copyright
47 * notice, this list of conditions and the following disclaimer.
48 * 2. Redistributions in binary form must reproduce the above copyright
49 * notice, this list of conditions and the following disclaimer in the
50 * documentation and/or other materials provided with the distribution.
51 * 3. Neither the name of the University nor the names of its contributors
52 * may be used to endorse or promote products derived from this software
53 * without specific prior written permission.
54 *
55 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
56 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
57 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
58 * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
59 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
60 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
61 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
62 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
63 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
64 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
65 * SUCH DAMAGE.
66 *
67 * @(#)clock.c 7.2 (Berkeley) 5/12/91
68 */
69 /*-
70 * Copyright (c) 1993, 1994 Charles M. Hannum.
71 *
72 * This code is derived from software contributed to Berkeley by
73 * William Jolitz and Don Ahn.
74 *
75 * Redistribution and use in source and binary forms, with or without
76 * modification, are permitted provided that the following conditions
77 * are met:
78 * 1. Redistributions of source code must retain the above copyright
79 * notice, this list of conditions and the following disclaimer.
80 * 2. Redistributions in binary form must reproduce the above copyright
81 * notice, this list of conditions and the following disclaimer in the
82 * documentation and/or other materials provided with the distribution.
83 * 3. All advertising materials mentioning features or use of this software
84 * must display the following acknowledgement:
85 * This product includes software developed by the University of
86 * California, Berkeley and its contributors.
87 * 4. Neither the name of the University nor the names of its contributors
88 * may be used to endorse or promote products derived from this software
89 * without specific prior written permission.
90 *
91 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
92 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
93 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
94 * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
95 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
96 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
97 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
98 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
99 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
100 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
101 * SUCH DAMAGE.
102 *
103 * @(#)clock.c 7.2 (Berkeley) 5/12/91
104 */
105 /*
106 * Mach Operating System
107 * Copyright (c) 1991,1990,1989 Carnegie Mellon University
108 * All Rights Reserved.
109 *
110 * Permission to use, copy, modify and distribute this software and its
111 * documentation is hereby granted, provided that both the copyright
112 * notice and this permission notice appear in all copies of the
113 * software, derivative works or modified versions, and any portions
114 * thereof, and that both notices appear in supporting documentation.
115 *
116 * CARNEGIE MELLON ALLOWS FREE USE OF THIS SOFTWARE IN ITS "AS IS"
117 * CONDITION. CARNEGIE MELLON DISCLAIMS ANY LIABILITY OF ANY KIND FOR
118 * ANY DAMAGES WHATSOEVER RESULTING FROM THE USE OF THIS SOFTWARE.
119 *
120 * Carnegie Mellon requests users of this software to return to
121 *
122 * Software Distribution Coordinator or Software.Distribution (at) CS.CMU.EDU
123 * School of Computer Science
124 * Carnegie Mellon University
125 * Pittsburgh PA 15213-3890
126 *
127 * any improvements or extensions that they make and grant Carnegie Mellon
128 * the rights to redistribute these changes.
129 */
130 /*
131 Copyright 1988, 1989 by Intel Corporation, Santa Clara, California.
132
133 All Rights Reserved
134
135 Permission to use, copy, modify, and distribute this software and
136 its documentation for any purpose and without fee is hereby
137 granted, provided that the above copyright notice appears in all
138 copies and that both the copyright notice and this permission notice
139 appear in supporting documentation, and that the name of Intel
140 not be used in advertising or publicity pertaining to distribution
141 of the software without specific, written prior permission.
142
143 INTEL DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE
144 INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS,
145 IN NO EVENT SHALL INTEL BE LIABLE FOR ANY SPECIAL, INDIRECT, OR
146 CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
147 LOSS OF USE, DATA OR PROFITS, WHETHER IN ACTION OF CONTRACT,
148 NEGLIGENCE, OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION
149 WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
150 */
151
152 /*
153 * Primitive clock interrupt routines.
154 */
155
156 #include <sys/cdefs.h>
157 __KERNEL_RCSID(0, "$NetBSD: clock.c,v 1.13 2006/09/13 07:13:03 gdamore Exp $");
158
159 #include <sys/param.h>
160 #include <sys/systm.h>
161 #include <sys/time.h>
162 #include <sys/timetc.h>
163 #include <sys/kernel.h>
164 #include <sys/device.h>
165
166 #include <machine/cpu.h>
167 #include <machine/intr.h>
168 #include <machine/pio.h>
169 #include <arm/cpufunc.h>
170
171 #include <dev/isa/isareg.h>
172 #include <dev/isa/isavar.h>
173 #include <dev/ic/mc146818reg.h>
174 #include <dev/ic/i8253reg.h>
175 #include <shark/isa/nvram.h>
176 #include <shark/isa/spkrreg.h>
177 #include <shark/shark/hat.h>
178
179 void sysbeepstop(void *);
180 void sysbeep(int, int);
181 void rtcinit(void);
182 int timer_hz_to_count(int);
183
184 static void findcpuspeed(void);
185 static void delayloop(int);
186 static int clockintr(void *);
187 static int gettick(void);
188 static void tc_init_i8253(void);
189
190 void startrtclock(void);
191
192 inline unsigned mc146818_read(void *, unsigned);
193 inline void mc146818_write(void *, unsigned, unsigned);
194
195 inline unsigned
196 mc146818_read(void *sc, unsigned reg)
197 {
198
199 outb(IO_RTC, reg);
200 return (inb(IO_RTC+1));
201 }
202
203 inline void
204 mc146818_write(void *sc, unsigned reg, unsigned datum)
205 {
206
207 outb(IO_RTC, reg);
208 outb(IO_RTC+1, datum);
209 }
210
211 unsigned int count1024usec; /* calibrated loop variable (1024 microseconds) */
212
213 /* number of timer ticks in a Musec = 2^20 usecs */
214 #define TIMER_MUSECFREQ\
215 (((((((TIMER_FREQ) * 1024) + 999) / 1000) * 1024) + 999) / 1000)
216 #define TIMER_MUSECDIV(x) ((TIMER_MUSECFREQ+(x)/2)/(x))
217
218 /*
219 * microtime() makes use of the following globals.
220 * timer_msb_table[] and timer_lsb_table[] are used to compute the
221 * microsecond increment.
222 *
223 * time.tv_usec += isa_timer_msb_table[cnt_msb] + isa_timer_lsb_table[cnt_lsb];
224 */
225
226 u_short isa_timer_lsb_table[256]; /* timer->usec conversion for LSB */
227
228 /* 64 bit counts from timer 0 */
229 struct count64 {
230 unsigned lo; /* low 32 bits */
231 unsigned hi; /* high 32 bits */
232 };
233
234 #define TIMER0_ROLLOVER 0xFFFF /* maximum rollover for 8254 counter */
235
236 struct count64 timer0count;
237 struct count64 timer0_at_last_clockintr;
238 unsigned timer0last;
239
240 /*#define TESTHAT*/
241 #ifdef TESTHAT
242 #define HATSTACKSIZE 1024
243 #define HATHZ 50000
244 #define HATHZ2 10000
245 unsigned char hatStack[HATSTACKSIZE];
246
247 unsigned testHatOn = 0;
248 unsigned nHats = 0;
249 unsigned nHatWedges = 0;
250 unsigned fiqReason = 0;
251 unsigned hatCount = 0;
252 unsigned hatCount2 = 0;
253
254 void hatTest(int testReason)
255 {
256
257 fiqReason |= testReason;
258 nHats++;
259 }
260
261 void hatWedge(int nFIQs)
262 {
263
264 printf("Unwedging the HAT. fiqs_happened = %d\n", nFIQs);
265 nHatWedges++;
266 }
267 #endif
268
269 void
270 startrtclock(void)
271 {
272
273 findcpuspeed(); /* use the clock (while it's free) to
274 find the CPU speed */
275
276 timer0count.lo = 0;
277 timer0count.hi = 0;
278 timer0_at_last_clockintr.lo = 0;
279 timer0_at_last_clockintr.hi = 0;
280 timer0last = 0;
281
282 /* initialize 8253 clock */
283 outb(IO_TIMER1 + TIMER_MODE, TIMER_SEL0|TIMER_RATEGEN|TIMER_16BIT);
284 outb(IO_TIMER1 + TIMER_CNTR0, TIMER0_ROLLOVER % 256);
285 outb(IO_TIMER1 + TIMER_CNTR0, TIMER0_ROLLOVER / 256);
286
287 #ifdef TESTHAT
288 hatCount = timer_hz_to_count(HATHZ);
289 hatCount2 = timer_hz_to_count(HATHZ2);
290 printf("HAT test on @ %d Hz = %d ticks\n", HATHZ, hatCount);
291 #endif
292 }
293
294 int
295 timer_hz_to_count(int timer_hz)
296 {
297 u_long tval;
298
299 tval = (TIMER_FREQ * 2) / (u_long) timer_hz;
300 tval = (tval / 2) + (tval & 0x1);
301
302 return (int)tval;
303 }
304
305 void gettimer0count(struct count64 *);
306
307 /* must be called at SPL_CLOCK or higher */
308 void gettimer0count(struct count64 *pcount)
309 {
310 unsigned current, ticks, oldlo;
311
312 /*
313 * Latch the current value of the timer and then read it.
314 * This guarantees an atomic reading of the time.
315 */
316
317 current = gettick();
318
319 if (timer0last >= current)
320 ticks = timer0last - current;
321 else
322 ticks = timer0last + (TIMER0_ROLLOVER - current);
323
324 timer0last = current;
325
326 oldlo = timer0count.lo;
327
328 if (oldlo > (timer0count.lo = oldlo + ticks)) /* carry? */
329 timer0count.hi++;
330
331 *pcount = timer0count;
332 }
333
334 static int
335 clockintr(void *arg)
336 {
337 struct clockframe *frame = arg; /* not strictly necessary */
338 extern void isa_specific_eoi(int irq);
339 #ifdef TESTHAT
340 static int ticks = 0;
341 #endif
342 static int hatUnwedgeCtr = 0;
343
344 gettimer0count(&timer0_at_last_clockintr);
345
346 mc146818_read(NULL, MC_REGC); /* clear the clock interrupt */
347
348 /* check to see if the high-availability timer needs to be unwedged */
349 if (++hatUnwedgeCtr >= (hz / HAT_MIN_FREQ)) {
350 hatUnwedgeCtr = 0;
351 hatUnwedge();
352 }
353
354 #ifdef TESTHAT
355 ++ticks;
356
357 if (testHatOn && ((ticks & 0x3f) == 0)) {
358 if (testHatOn == 1) {
359 hatClkAdjust(hatCount2);
360 testHatOn = 2;
361 } else {
362 testHatOn = 0;
363 hatClkOff();
364 printf("hat off status: %d %d %x\n", nHats,
365 nHatWedges, fiqReason);
366 }
367 } else if (!testHatOn && (ticks & 0x1ff) == 0) {
368 printf("hat on status: %d %d %x\n",
369 nHats, nHatWedges, fiqReason);
370 testHatOn = 1;
371 nHats = 0;
372 fiqReason = 0;
373 hatClkOn(hatCount, hatTest, 0xfeedface,
374 hatStack + HATSTACKSIZE - sizeof(unsigned),
375 hatWedge);
376 }
377 #endif
378 hardclock(frame);
379 return(0);
380 }
381
382 static int
383 gettick(void)
384 {
385 u_char lo, hi;
386 u_int savedints;
387
388 /* Don't want someone screwing with the counter while we're here. */
389 savedints = disable_interrupts(I32_bit);
390 /* Select counter 0 and latch it. */
391 outb(IO_TIMER1 + TIMER_MODE, TIMER_SEL0 | TIMER_LATCH);
392 lo = inb(IO_TIMER1 + TIMER_CNTR0);
393 hi = inb(IO_TIMER1 + TIMER_CNTR0);
394 restore_interrupts(savedints);
395 return ((hi << 8) | lo);
396 }
397
398 /* modifications from i386 to shark isa version:
399 - removed hardcoded "n -=" values that approximated the time to
400 calculate delay ticks
401 - made the time to calculate delay ticks almost negligable. 4 multiplies
402 = maximum of 12 cycles = 75ns on a slow SA-110, plus a bunch of shifts;
403 as opposed to 4 multiplies plus a bunch of divides.
404 - removed i386 assembly language hack
405 - put code in findcpuspeed that works even if FIRST_GUESS is orders
406 of magnitude low
407 - put code in delay() to use delayloop() for short delays
408 - microtime no longer in assembly language
409 */
410
411 /*
412 * Wait "n" microseconds.
413 * Relies on timer 1 counting down from (TIMER_FREQ / hz) at TIMER_FREQ Hz.
414 * Note: timer had better have been programmed before this is first used!
415 * (Note that we use `rate generator' mode, which counts at 1:1; `square
416 * wave' mode counts at 2:1).
417 */
418 void
419 delay(unsigned n)
420 {
421 int ticks, otick;
422 int nticks;
423
424 if (n < 100) {
425 /* it can take a long time (1 usec or longer) just for
426 1 ISA read, so it's best not to use the timer for
427 short delays */
428 delayloop((n * count1024usec) >> 10);
429 return;
430 }
431
432 /*
433 * Read the counter first, so that the rest of the setup overhead is
434 * counted.
435 */
436 otick = gettick();
437
438 /*
439 * Calculate ((n * TIMER_FREQ) / 1e6) without using floating point and
440 * without any avoidable overflows.
441 */
442 {
443 /* a Musec = 2^20 usec */
444 int Musec = n >> 20,
445 usec = n & ((1 << 20) - 1);
446 nticks
447 = (Musec * TIMER_MUSECFREQ) +
448 (usec * (TIMER_MUSECFREQ >> 20)) +
449 ((usec * ((TIMER_MUSECFREQ & ((1 <<20) - 1)) >>10)) >>10) +
450 ((usec * (TIMER_MUSECFREQ & ((1 << 10) - 1))) >> 20);
451 }
452
453 while (nticks > 0) {
454 ticks = gettick();
455 if (ticks > otick)
456 nticks -= TIMER0_ROLLOVER - (ticks - otick);
457 else
458 nticks -= otick - ticks;
459 otick = ticks;
460 }
461
462 }
463
464 void
465 sysbeepstop(void *arg)
466 {
467 }
468
469 void
470 sysbeep(int pitch, int period)
471 {
472 }
473
474 #define FIRST_GUESS 0x2000
475
476 static void
477 findcpuspeed(void)
478 {
479 int ticks;
480 unsigned int guess = FIRST_GUESS;
481
482 while (1) { /* loop until accurate enough */
483 /* Put counter in count down mode */
484 outb(IO_TIMER1 + TIMER_MODE,
485 TIMER_SEL0 | TIMER_16BIT | TIMER_RATEGEN);
486 outb(IO_TIMER1 + TIMER_CNTR0, 0xff);
487 outb(IO_TIMER1 + TIMER_CNTR0, 0xff);
488 delayloop(guess);
489
490 /* Read the value left in the counter */
491 /*
492 * Formula for delaycount is:
493 * (loopcount * timer clock speed) / (counter ticks * 1000)
494 */
495 ticks = 0xFFFF - gettick();
496 if (ticks == 0) ticks = 1; /* just in case */
497 if (ticks < (TIMER_MUSECDIV(1024))) { /* not accurate enough */
498 guess *= max(2, (TIMER_MUSECDIV(1024) / ticks));
499 continue;
500 }
501 count1024usec = (guess * (TIMER_MUSECDIV(1024))) / ticks;
502 return;
503 }
504 }
505
506 static void
507 delayloop(int counts)
508 {
509 while (counts--)
510 __insn_barrier();
511 }
512
513 void
514 cpu_initclocks(void)
515 {
516 unsigned hzval;
517
518 printf("clock: hz=%d stathz = %d profhz = %d\n", hz, stathz, profhz);
519
520 /* install RTC interrupt handler */
521 (void)isa_intr_establish(NULL, IRQ_RTC, IST_LEVEL, IPL_CLOCK,
522 clockintr, 0);
523
524 /* set up periodic interrupt @ hz
525 this is the subset of hz values in kern_clock.c that are
526 supported by the ISA RTC */
527 switch (hz) {
528 case 64:
529 hzval = MC_RATE_64_Hz;
530 break;
531 case 128:
532 hzval = MC_RATE_128_Hz;
533 break;
534 case 256:
535 hzval = MC_RATE_256_Hz;
536 break;
537 case 1024:
538 hzval = MC_RATE_1024_Hz;
539 break;
540 default:
541 panic("cannot configure hz = %d", hz);
542 }
543
544 rtcinit(); /* make sure basics are done by now */
545
546 /* blast values to set up clock interrupt */
547 mc146818_write(NULL, MC_REGA, MC_BASE_32_KHz | hzval);
548 /* enable periodic interrupt */
549 mc146818_write(NULL, MC_REGB,
550 mc146818_read(NULL, MC_REGB) | MC_REGB_PIE);
551
552 tc_init_i8253();
553 }
554
555 void
556 rtcinit(void)
557 {
558 static int first_rtcopen_ever = 1;
559
560 if (!first_rtcopen_ever)
561 return;
562 first_rtcopen_ever = 0;
563
564 mc146818_write(NULL, MC_REGA, /* XXX softc */
565 MC_BASE_32_KHz | MC_RATE_1024_Hz);
566 mc146818_write(NULL, MC_REGB, MC_REGB_24HR); /* XXX softc */
567 }
568
569 void
570 setstatclockrate(int arg)
571 {
572 }
573
574 static uint32_t
575 i8253_get_timecount(struct timecounter *tc)
576 {
577 return (TIMER0_ROLLOVER - gettick());
578 }
579
580 void
581 tc_init_i8253(void)
582 {
583 static struct timecounter i8253_tc = {
584 .tc_get_timecount = i8253_get_timecount,
585 .tc_counter_mask = TIMER0_ROLLOVER,
586 .tc_frequency = TIMER_FREQ,
587 .tc_name = "i8253",
588 .tc_quality = 100
589 };
590
591 tc_init(&i8253_tc);
592 }
593
594 /* End of clock.c */
595