1 /* $NetBSD: clock.c,v 1.20 2023/12/09 00:02:10 andvar 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@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.20 2023/12/09 00:02:10 andvar 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/irqhandler.h>
169 #include <machine/pio.h>
170 #include <arm/cpufunc.h>
171
172 #include <dev/isa/isareg.h>
173 #include <dev/isa/isavar.h>
174 #include <dev/ic/mc146818reg.h>
175 #include <dev/ic/i8253reg.h>
176 #include <shark/isa/nvram.h>
177 #include <shark/isa/spkrreg.h>
178 #include <shark/shark/hat.h>
179
180 void sysbeepstop(void *);
181 void sysbeep(int, int);
182 void rtcinit(void);
183 int timer_hz_to_count(int);
184
185 static void findcpuspeed(void);
186 static void delayloop(int);
187 static int clockintr(void *);
188 static int gettick(void);
189 static void tc_init_i8253(void);
190
191 void startrtclock(void);
192
193 inline unsigned mc146818_read(void *, unsigned);
194 inline void mc146818_write(void *, unsigned, unsigned);
195
196 inline unsigned
mc146818_read(void * sc,unsigned reg)197 mc146818_read(void *sc, unsigned reg)
198 {
199
200 outb(IO_RTC, reg);
201 return (inb(IO_RTC+1));
202 }
203
204 inline void
mc146818_write(void * sc,unsigned reg,unsigned datum)205 mc146818_write(void *sc, unsigned reg, unsigned datum)
206 {
207
208 outb(IO_RTC, reg);
209 outb(IO_RTC+1, datum);
210 }
211
212 unsigned int count1024usec; /* calibrated loop variable (1024 microseconds) */
213
214 /* number of timer ticks in a Musec = 2^20 usecs */
215 #define TIMER_MUSECFREQ\
216 (((((((TIMER_FREQ) * 1024) + 999) / 1000) * 1024) + 999) / 1000)
217 #define TIMER_MUSECDIV(x) ((TIMER_MUSECFREQ+(x)/2)/(x))
218
219 /*
220 * microtime() makes use of the following globals.
221 * timer_msb_table[] and timer_lsb_table[] are used to compute the
222 * microsecond increment.
223 *
224 * time.tv_usec += isa_timer_msb_table[cnt_msb] + isa_timer_lsb_table[cnt_lsb];
225 */
226
227 u_short isa_timer_lsb_table[256]; /* timer->usec conversion for LSB */
228
229 /* 64 bit counts from timer 0 */
230 struct count64 {
231 unsigned lo; /* low 32 bits */
232 unsigned hi; /* high 32 bits */
233 };
234
235 #define TIMER0_ROLLOVER 0xFFFF /* maximum rollover for 8254 counter */
236
237 struct count64 timer0count;
238 struct count64 timer0_at_last_clockintr;
239 unsigned timer0last;
240
241 /*#define TESTHAT*/
242 #ifdef TESTHAT
243 #define HATSTACKSIZE 1024
244 #define HATHZ 50000
245 #define HATHZ2 10000
246 unsigned char hatStack[HATSTACKSIZE];
247
248 unsigned testHatOn = 0;
249 unsigned nHats = 0;
250 unsigned nHatWedges = 0;
251 unsigned fiqReason = 0;
252 unsigned hatCount = 0;
253 unsigned hatCount2 = 0;
254
hatTest(int testReason)255 static void hatTest(int testReason)
256 {
257
258 fiqReason |= testReason;
259 nHats++;
260 }
261
hatWedge(int nFIQs)262 static void hatWedge(int nFIQs)
263 {
264
265 printf("Unwedging the HAT. fiqs_happened = %d\n", nFIQs);
266 nHatWedges++;
267 }
268 #endif
269
270 void
startrtclock(void)271 startrtclock(void)
272 {
273
274 findcpuspeed(); /* use the clock (while it's free) to
275 find the CPU speed */
276
277 timer0count.lo = 0;
278 timer0count.hi = 0;
279 timer0_at_last_clockintr.lo = 0;
280 timer0_at_last_clockintr.hi = 0;
281 timer0last = 0;
282
283 /* initialize 8253 clock */
284 outb(IO_TIMER1 + TIMER_MODE, TIMER_SEL0|TIMER_RATEGEN|TIMER_16BIT);
285 outb(IO_TIMER1 + TIMER_CNTR0, TIMER0_ROLLOVER % 256);
286 outb(IO_TIMER1 + TIMER_CNTR0, TIMER0_ROLLOVER / 256);
287
288 #ifdef TESTHAT
289 hatCount = timer_hz_to_count(HATHZ);
290 hatCount2 = timer_hz_to_count(HATHZ2);
291 printf("HAT test on @ %d Hz = %d ticks\n", HATHZ, hatCount);
292 #endif
293 }
294
295 int
timer_hz_to_count(int timer_hz)296 timer_hz_to_count(int timer_hz)
297 {
298 u_long tval;
299
300 tval = (TIMER_FREQ * 2) / (u_long) timer_hz;
301 tval = (tval / 2) + (tval & 0x1);
302
303 return (int)tval;
304 }
305
306 void gettimer0count(struct count64 *);
307
308 /* must be called at SPL_CLOCK or higher */
gettimer0count(struct count64 * pcount)309 void gettimer0count(struct count64 *pcount)
310 {
311 unsigned current, ticks, oldlo;
312
313 /*
314 * Latch the current value of the timer and then read it.
315 * This guarantees an atomic reading of the time.
316 */
317
318 current = gettick();
319
320 if (timer0last >= current)
321 ticks = timer0last - current;
322 else
323 ticks = timer0last + (TIMER0_ROLLOVER - current);
324
325 timer0last = current;
326
327 oldlo = timer0count.lo;
328
329 if (oldlo > (timer0count.lo = oldlo + ticks)) /* carry? */
330 timer0count.hi++;
331
332 *pcount = timer0count;
333 }
334
335 static int
clockintr(void * arg)336 clockintr(void *arg)
337 {
338 struct clockframe *frame = arg; /* not strictly necessary */
339 extern void isa_specific_eoi(int irq);
340 #ifdef TESTHAT
341 static int ticks = 0;
342 #endif
343 static int hatUnwedgeCtr = 0;
344
345 gettimer0count(&timer0_at_last_clockintr);
346
347 mc146818_read(NULL, MC_REGC); /* clear the clock interrupt */
348
349 /* check to see if the high-availability timer needs to be unwedged */
350 if (++hatUnwedgeCtr >= (hz / HAT_MIN_FREQ)) {
351 hatUnwedgeCtr = 0;
352 hatUnwedge();
353 }
354
355 #ifdef TESTHAT
356 ++ticks;
357
358 if (testHatOn && ((ticks & 0x3f) == 0)) {
359 if (testHatOn == 1) {
360 hatClkAdjust(hatCount2);
361 testHatOn = 2;
362 } else {
363 testHatOn = 0;
364 hatClkOff();
365 printf("hat off status: %d %d %x\n", nHats,
366 nHatWedges, fiqReason);
367 }
368 } else if (!testHatOn && (ticks & 0x1ff) == 0) {
369 printf("hat on status: %d %d %x\n",
370 nHats, nHatWedges, fiqReason);
371 testHatOn = 1;
372 nHats = 0;
373 fiqReason = 0;
374 hatClkOn(hatCount, hatTest, 0xfeedface,
375 hatStack + HATSTACKSIZE - sizeof(unsigned),
376 hatWedge);
377 }
378 #endif
379 hardclock(frame);
380 return(1);
381 }
382
383 static int
gettick(void)384 gettick(void)
385 {
386 u_char lo, hi;
387 u_int savedints;
388
389 /* Don't want someone screwing with the counter while we're here. */
390 savedints = disable_interrupts(I32_bit);
391 /* Select counter 0 and latch it. */
392 outb(IO_TIMER1 + TIMER_MODE, TIMER_SEL0 | TIMER_LATCH);
393 lo = inb(IO_TIMER1 + TIMER_CNTR0);
394 hi = inb(IO_TIMER1 + TIMER_CNTR0);
395 restore_interrupts(savedints);
396 return ((hi << 8) | lo);
397 }
398
399 /* modifications from i386 to shark isa version:
400 - removed hardcoded "n -=" values that approximated the time to
401 calculate delay ticks
402 - made the time to calculate delay ticks almost negligible. 4 multiplies
403 = maximum of 12 cycles = 75ns on a slow SA-110, plus a bunch of shifts;
404 as opposed to 4 multiplies plus a bunch of divides.
405 - removed i386 assembly language hack
406 - put code in findcpuspeed that works even if FIRST_GUESS is orders
407 of magnitude low
408 - put code in delay() to use delayloop() for short delays
409 - microtime no longer in assembly language
410 */
411
412 /*
413 * Wait "n" microseconds.
414 * Relies on timer 1 counting down from (TIMER_FREQ / hz) at TIMER_FREQ Hz.
415 * Note: timer had better have been programmed before this is first used!
416 * (Note that we use `rate generator' mode, which counts at 1:1; `square
417 * wave' mode counts at 2:1).
418 */
419 void
delay(unsigned n)420 delay(unsigned n)
421 {
422 int ticks, otick;
423 int nticks;
424
425 if (n < 100) {
426 /* it can take a long time (1 usec or longer) just for
427 1 ISA read, so it's best not to use the timer for
428 short delays */
429 delayloop((n * count1024usec) >> 10);
430 return;
431 }
432
433 /*
434 * Read the counter first, so that the rest of the setup overhead is
435 * counted.
436 */
437 otick = gettick();
438
439 /*
440 * Calculate ((n * TIMER_FREQ) / 1e6) without using floating point and
441 * without any avoidable overflows.
442 */
443 {
444 /* a Musec = 2^20 usec */
445 int Musec = n >> 20,
446 usec = n & ((1 << 20) - 1);
447 nticks
448 = (Musec * TIMER_MUSECFREQ) +
449 (usec * (TIMER_MUSECFREQ >> 20)) +
450 ((usec * ((TIMER_MUSECFREQ & ((1 <<20) - 1)) >>10)) >>10) +
451 ((usec * (TIMER_MUSECFREQ & ((1 << 10) - 1))) >> 20);
452 }
453
454 while (nticks > 0) {
455 ticks = gettick();
456 if (ticks > otick)
457 nticks -= TIMER0_ROLLOVER - (ticks - otick);
458 else
459 nticks -= otick - ticks;
460 otick = ticks;
461 }
462
463 }
464
465 void
sysbeepstop(void * arg)466 sysbeepstop(void *arg)
467 {
468 }
469
470 void
sysbeep(int pitch,int period)471 sysbeep(int pitch, int period)
472 {
473 }
474
475 #define FIRST_GUESS 0x2000
476
477 static void
findcpuspeed(void)478 findcpuspeed(void)
479 {
480 int ticks;
481 unsigned int guess = FIRST_GUESS;
482
483 while (1) { /* loop until accurate enough */
484 /* Put counter in count down mode */
485 outb(IO_TIMER1 + TIMER_MODE,
486 TIMER_SEL0 | TIMER_16BIT | TIMER_RATEGEN);
487 outb(IO_TIMER1 + TIMER_CNTR0, 0xff);
488 outb(IO_TIMER1 + TIMER_CNTR0, 0xff);
489 delayloop(guess);
490
491 /* Read the value left in the counter */
492 /*
493 * Formula for delaycount is:
494 * (loopcount * timer clock speed) / (counter ticks * 1000)
495 */
496 ticks = 0xFFFF - gettick();
497 if (ticks == 0) ticks = 1; /* just in case */
498 if (ticks < (TIMER_MUSECDIV(1024))) { /* not accurate enough */
499 guess *= uimax(2, (TIMER_MUSECDIV(1024) / ticks));
500 continue;
501 }
502 count1024usec = (guess * (TIMER_MUSECDIV(1024))) / ticks;
503 return;
504 }
505 }
506
507 static void
delayloop(int counts)508 delayloop(int counts)
509 {
510 while (counts--)
511 __insn_barrier();
512 }
513
514 void
cpu_initclocks(void)515 cpu_initclocks(void)
516 {
517 unsigned hzval;
518
519 printf("clock: hz=%d stathz = %d profhz = %d\n", hz, stathz, profhz);
520
521 /* install RTC interrupt handler */
522 (void)isa_intr_establish(NULL, IRQ_RTC, IST_LEVEL, IPL_CLOCK,
523 clockintr, 0);
524
525 /* set up periodic interrupt @ hz
526 this is the subset of hz values in kern_clock.c that are
527 supported by the ISA RTC */
528 switch (hz) {
529 case 64:
530 hzval = MC_RATE_64_Hz;
531 break;
532 case 128:
533 hzval = MC_RATE_128_Hz;
534 break;
535 case 256:
536 hzval = MC_RATE_256_Hz;
537 break;
538 case 1024:
539 hzval = MC_RATE_1024_Hz;
540 break;
541 default:
542 panic("cannot configure hz = %d", hz);
543 }
544
545 rtcinit(); /* make sure basics are done by now */
546
547 /* blast values to set up clock interrupt */
548 mc146818_write(NULL, MC_REGA, MC_BASE_32_KHz | hzval);
549 /* enable periodic interrupt */
550 mc146818_write(NULL, MC_REGB,
551 mc146818_read(NULL, MC_REGB) | MC_REGB_PIE);
552
553 tc_init_i8253();
554 }
555
556 void
rtcinit(void)557 rtcinit(void)
558 {
559 static int first_rtcopen_ever = 1;
560
561 if (!first_rtcopen_ever)
562 return;
563 first_rtcopen_ever = 0;
564
565 mc146818_write(NULL, MC_REGA, /* XXX softc */
566 MC_BASE_32_KHz | MC_RATE_1024_Hz);
567 mc146818_write(NULL, MC_REGB, MC_REGB_24HR); /* XXX softc */
568 }
569
570 void
setstatclockrate(int arg)571 setstatclockrate(int arg)
572 {
573 }
574
575 static uint32_t
i8253_get_timecount(struct timecounter * tc)576 i8253_get_timecount(struct timecounter *tc)
577 {
578 return (TIMER0_ROLLOVER - gettick());
579 }
580
581 void
tc_init_i8253(void)582 tc_init_i8253(void)
583 {
584 static struct timecounter i8253_tc = {
585 .tc_get_timecount = i8253_get_timecount,
586 .tc_counter_mask = TIMER0_ROLLOVER,
587 .tc_frequency = TIMER_FREQ,
588 .tc_name = "i8253",
589 .tc_quality = 100
590 };
591
592 tc_init(&i8253_tc);
593 }
594
595 /* End of clock.c */
596