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