1 /*        $NetBSD: clock.c,v 1.50 2019/08/22 12:47:57 rin Exp $       */
2 
3 /*
4  * Copyright (c) 1988 University of Utah.
5  * Copyright (c) 1982, 1990 The Regents of the University of California.
6  * All rights reserved.
7  *
8  * This code is derived from software contributed to Berkeley by
9  * the Systems Programming Group of the University of Utah Computer
10  * Science Department.
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 
37 /*-
38  * Copyright (C) 1993         Allen K. Briggs, Chris P. Caputo,
39  *                            Michael L. Finch, Bradley A. Grantham, and
40  *                            Lawrence A. Kesteloot
41  * All rights reserved.
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. All advertising materials mentioning features or use of this software
52  *    must display the following acknowledgement:
53  *        This product includes software developed by the Alice Group.
54  * 4. The names of the Alice Group or any of its members may not be used
55  *    to endorse or promote products derived from this software without
56  *    specific prior written permission.
57  *
58  * THIS SOFTWARE IS PROVIDED BY THE ALICE GROUP ``AS IS'' AND ANY EXPRESS OR
59  * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
60  * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
61  * IN NO EVENT SHALL THE ALICE GROUP BE LIABLE FOR ANY DIRECT, INDIRECT,
62  * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
63  * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
64  * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
65  * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
66  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
67  * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
68  *
69  */
70 /*
71  *
72  * from: Utah $Hdr: clock.c 1.18 91/01/21$
73  *
74  *        @(#)clock.c   7.6 (Berkeley) 5/7/91
75  */
76 
77 #include <sys/cdefs.h>
78 __KERNEL_RCSID(0, "$NetBSD: clock.c,v 1.50 2019/08/22 12:47:57 rin Exp $");
79 
80 #include "opt_rtc_offset.h"
81 
82 #include <sys/param.h>
83 #include <sys/device.h>
84 #include <sys/kernel.h>
85 #include <sys/proc.h>
86 #include <sys/systm.h>
87 #include <sys/timetc.h>
88 
89 #include <dev/clock_subr.h>
90 
91 #include <machine/autoconf.h>
92 #include <machine/psl.h>
93 #include <machine/cpu.h>
94 #include <machine/limits.h>
95 
96 #if defined(GPROF) && defined(PROFTIMER)
97 #include <sys/gprof.h>
98 #endif
99 
100 #include <mac68k/mac68k/pram.h>
101 #include <mac68k/mac68k/clockreg.h>
102 #include <machine/viareg.h>
103 
104 #ifdef DEBUG
105 int       clock_debug = 0;
106 #endif
107 
108 void      rtclock_intr(void);
109 static int mac68k_gettime(todr_chip_handle_t, struct timeval *);
110 static int mac68k_settime(todr_chip_handle_t, struct timeval *);
111 static u_int via1_t2_get_timecount(struct timecounter *);
112 
113 #define   DIFF19041970        2082844800
114 #define   DIFF19701990        630720000
115 #define   DIFF19702010        1261440000
116 
117 
118 /*
119  * Mac II machine-dependent clock routines.
120  */
121 
122 /*
123  * Start the real-time clock; i.e. set timer latches and boot timer.
124  *
125  * We use VIA1 timer 1.
126  */
127 void
startrtclock(void)128 startrtclock(void)
129 {
130 /*
131  * BARF MF startrt clock is called twice in init_main, configure,
132  * the reason why is doced in configure
133  */
134           /* be certain all clock interrupts are off */
135           via_reg(VIA1, vIER) = V1IF_T1 | V1IF_T2;
136 
137           /* set timer latch */
138           via_reg(VIA1, vACR) |= ACR_T1LATCH;
139 
140           /* set VIA timer 1 latch to 60 Hz (100 Hz) */
141           via_reg(VIA1, vT1L) = CLK_INTL;
142           via_reg(VIA1, vT1LH) = CLK_INTH;
143 
144           /* set VIA timer 1 counter started for 60(100) Hz */
145           via_reg(VIA1, vT1C) = CLK_INTL;
146           via_reg(VIA1, vT1CH) = CLK_INTH;
147 
148           /*
149            * Set & start VIA1 timer 2 free-running for timecounter support.
150            * Since reading the LSB of the counter clears any pending
151            * interrupt, timer 1 is less suitable as a timecounter.
152            */
153           via_reg(VIA1, vT2C) = 0x0ff;
154           via_reg(VIA1, vT2CH) = 0x0ff;
155 }
156 
157 void
enablertclock(void)158 enablertclock(void)
159 {
160           /* clear then enable clock interrupt. */
161           via_reg(VIA1, vIFR) |= V1IF_T1;
162           via_reg(VIA1, vIER) = 0x80 | V1IF_T1;
163 }
164 
165 void
cpu_initclocks(void)166 cpu_initclocks(void)
167 {
168           static struct todr_chip_handle todr = {
169                     .todr_settime = mac68k_settime,
170                     .todr_gettime = mac68k_gettime,
171           };
172           static struct timecounter via1_t2_timecounter = {
173                     .tc_get_timecount = via1_t2_get_timecount,
174                     .tc_poll_pps          = 0,
175                     .tc_counter_mask  = 0x0ffffu,
176                     .tc_frequency         = CLK_FREQ,
177                     .tc_name    = "VIA1 T2",
178                     .tc_quality           = 100,
179                     .tc_priv    = NULL,
180                     .tc_next    = NULL
181           };
182 
183           enablertclock();
184           todr_attach(&todr);
185           tc_init(&via1_t2_timecounter);
186 }
187 
188 void
setstatclockrate(int rateinhz)189 setstatclockrate(int rateinhz)
190 {
191 }
192 
193 void
disablertclock(void)194 disablertclock(void)
195 {
196           /* disable clock interrupt */
197           via_reg(VIA1, vIER) = V1IF_T1;
198 }
199 
200 static u_int
via1_t2_get_timecount(struct timecounter * tc)201 via1_t2_get_timecount(struct timecounter *tc)
202 {
203           uint8_t high, high2, low;
204           int s;
205 
206           /* Guard HW timer access */
207           s = splhigh();
208 
209           high = via_reg(VIA1, vT2CH);
210           low = via_reg(VIA1, vT2C);
211 
212           high2 = via_reg(VIA1, vT2CH);
213 
214           /*
215            * If we find that the MSB has just been incremented, read
216            * the LSB again, to avoid a race that could leave us with a new
217            * MSB and an old LSB value.
218            * With timecounters, the difference is quite spectacular.
219            *
220            * is added that to port-amiga ten years ago. Thanks!
221            */
222           if (high != high2) {
223                     low = via_reg(VIA1, vT2C);
224                     high = high2;
225           }
226 
227           splx(s);
228 
229           return 0x0ffff - ((high << 8) | low);
230 }
231 
232 #ifdef PROFTIMER
233 /*
234  * Here, we have implemented code that causes VIA2's timer to count
235  * the profiling clock.  Following the HP300's lead, this reduces
236  * the impact on other tasks, since locore turns off the profiling clock
237  * on context switches.  If need be, the profiling clock's resolution can
238  * be cranked higher than the real-time clock's resolution, to prevent
239  * aliasing and allow higher accuracy.
240  */
241 int     profint = PRF_INTERVAL;         /* Clock ticks between interrupts */
242 int     profinthigh;
243 int     profintlow;
244 int     profscale = 0;                  /* Scale factor from sys clock to prof clock */
245 char    profon = 0;           /* Is profiling clock on? */
246 
247 /* profon values - do not change, locore.s assumes these values */
248 #define   PRF_NONE  0x00
249 #define   PRF_USER  0x01
250 #define   PRF_KERNEL          0x80
251 
252 void
initprofclock(void)253 initprofclock(void)
254 {
255           /* profile interval must be even divisor of system clock interval */
256           if (profint > CLK_INTERVAL)
257                     profint = CLK_INTERVAL;
258           else
259                     if (CLK_INTERVAL % profint != 0)
260                               /* try to intelligently fix clock interval */
261                               profint = CLK_INTERVAL / (CLK_INTERVAL / profint);
262 
263           profscale = CLK_INTERVAL / profint;
264 
265           profinthigh = profint >> 8;
266           profintlow = profint & 0xff;
267 }
268 
269 void
startprofclock(void)270 startprofclock(void)
271 {
272           via_reg(VIA2, vT1L) = (profint - 1) & 0xff;
273           via_reg(VIA2, vT1LH) = (profint - 1) >> 8;
274           via_reg(VIA2, vACR) |= ACR_T1LATCH;
275           via_reg(VIA2, vT1C) = (profint - 1) & 0xff;
276           via_reg(VIA2, vT1CH) = (profint - 1) >> 8;
277 }
278 
279 void
stopprofclock(void)280 stopprofclock(void)
281 {
282           via_reg(VIA2, vT1L) = 0;
283           via_reg(VIA2, vT1LH) = 0;
284           via_reg(VIA2, vT1C) = 0;
285           via_reg(VIA2, vT1CH) = 0;
286 }
287 
288 #ifdef GPROF
289 /*
290  * BARF: we should check this:
291  *
292  * profclock() is expanded in line in lev6intr() unless profiling kernel.
293  * Assumes it is called with clock interrupts blocked.
294  */
295 void
profclock(clockframe * pclk)296 profclock(clockframe *pclk)
297 {
298           /*
299            * Came from user mode.
300            * If this process is being profiled record the tick.
301            */
302           if (USERMODE(pclk->ps)) {
303                     if (curproc->p_stats.p_prof.pr_scale)
304                               addupc_task(&curproc, pclk->pc, 1);
305           }
306           /*
307            * Came from kernel (supervisor) mode.
308            * If we are profiling the kernel, record the tick.
309            */
310           else
311                     if (profiling < 2) {
312                               int s = pclk->pc - s_lowpc;
313 
314                               if (s < s_textsize)
315                                         kcount[s / (HISTFRACTION * sizeof(*kcount))]++;
316                     }
317           /*
318            * Kernel profiling was on but has been disabled.
319            * Mark as no longer profiling kernel and if all profiling done,
320            * disable the clock.
321            */
322           if (profiling && (profon & PRF_KERNEL)) {
323                     profon &= ~PRF_KERNEL;
324                     if (profon == PRF_NONE)
325                               stopprofclock();
326           }
327 }
328 #endif
329 #endif
330 
331 static u_long       ugmt_2_pramt(u_long);
332 static u_long       pramt_2_ugmt(u_long);
333 
334 /*
335  * Convert GMT to Mac PRAM time, using rtc_offset
336  * GMT bias adjustment is done elsewhere.
337  */
338 static u_long
ugmt_2_pramt(u_long t)339 ugmt_2_pramt(u_long t)
340 {
341           /* don't know how to open a file properly. */
342           /* assume compiled timezone is correct. */
343 
344           return (t = t + DIFF19041970);
345 }
346 
347 /*
348  * Convert a Mac PRAM time value to GMT, using rtc_offset
349  * GMT bias adjustment is done elsewhere.
350  */
351 static u_long
pramt_2_ugmt(u_long t)352 pramt_2_ugmt(u_long t)
353 {
354           return (t = t - DIFF19041970);
355 }
356 
357 /*
358  * Time from the booter.
359  */
360 u_long    macos_boottime;
361 
362 /*
363  * Bias in minutes east from GMT (also from booter).
364  */
365 long      macos_gmtbias;
366 
367 /*
368  * Flag for whether or not we can trust the PRAM.  If we don't
369  * trust it, we don't write to it, and we take the MacOS value
370  * that is passed from the booter (which will only be a second
371  * or two off by now).
372  */
373 int       mac68k_trust_pram = 1;
374 
375 /*
376  * Set global GMT time register, using a file system time base for comparison
377  * and sanity checking.
378  */
379 int
mac68k_gettime(todr_chip_handle_t tch,struct timeval * tvp)380 mac68k_gettime(todr_chip_handle_t tch, struct timeval *tvp)
381 {
382           u_long timbuf;
383 
384           timbuf = pramt_2_ugmt(pram_readtime());
385           if ((timbuf - macos_boottime) > 10 * 60) {
386 #if DIAGNOSTIC
387                     printf(
388                         "PRAM time does not appear to have been read correctly.\n");
389                     printf("PRAM: 0x%lx, macos_boottime: 0x%lx.\n",
390                         timbuf, macos_boottime);
391 #endif
392                     timbuf = macos_boottime;
393                     mac68k_trust_pram = 0;
394           }
395           tvp->tv_sec = timbuf;
396 #if !defined(RTC_OFFSET) || RTC_OFFSET == 0
397           /*
398            * Adjust GTM bias unless RTC_OFFSET is set explicitly.
399            */
400           tvp->tv_sec -= macos_gmtbias * 60;
401 #endif
402           tvp->tv_usec = 0;
403           return 0;
404 }
405 
406 int
mac68k_settime(todr_chip_handle_t tch,struct timeval * tvp)407 mac68k_settime(todr_chip_handle_t tch, struct timeval *tvp)
408 {
409           if (mac68k_trust_pram)
410                     /*
411                      * GMT bias is passed in from the Booter.
412                      * To get *our* time, add GMTBIAS to GMT.
413                      * (gmtbias is in minutes, multiply by 60).
414                      */
415                     pram_settime(ugmt_2_pramt(tvp->tv_sec + macos_gmtbias * 60));
416 #ifdef DEBUG
417           else if (clock_debug)
418                     printf("NetBSD/mac68k does not trust itself to try and write "
419                         "to the PRAM on this system.\n");
420 #endif
421           return 0;
422 }
423 
424 /*
425  * The Macintosh timers decrement once every 1.2766 microseconds.
426  * MGFH2, p. 180
427  */
428 #define   CLK_RATE  12766
429 
430 #define   DELAY_CALIBRATE     (0xffffff << 7)     /* Large value for calibration */
431 
432 u_int               delay_factor = DELAY_CALIBRATE;
433 volatile int        delay_flag = 1;
434 
435 int                 _delay(u_int);
436 static void         delay_timer1_irq(void *);
437 
438 static void
delay_timer1_irq(void * dummy)439 delay_timer1_irq(void *dummy)
440 {
441           delay_flag = 0;
442 }
443 
444 /*
445  * Calibrate delay_factor with VIA1 timer T1.
446  */
447 void
mac68k_calibrate_delay(void)448 mac68k_calibrate_delay(void)
449 {
450           u_int sum, n;
451 
452           (void)spl0();
453 
454           /* Disable VIA1 timer 1 interrupts and set up service routine */
455           via_reg(VIA1, vIER) = V1IF_T1;
456           via1_register_irq(VIA1_T1, delay_timer1_irq, NULL);
457 
458           /* Set the timer for one-shot mode, then clear and enable interrupts */
459           via_reg(VIA1, vACR) &= ~ACR_T1LATCH;
460           via_reg(VIA1, vIFR) = V1IF_T1;          /* (this is needed for IIsi) */
461           via_reg(VIA1, vIER) = 0x80 | V1IF_T1;
462 
463 #ifdef DEBUG
464           if (clock_debug)
465                     printf("mac68k_calibrate_delay(): entering timing loop\n");
466 #endif
467 
468           for (sum = 0, n = 8; n > 0; n--) {
469                     delay_flag = 1;
470                     via_reg(VIA1, vT1C) = 0;      /* 1024 clock ticks */
471                     via_reg(VIA1, vT1CH) = 4;     /* (approx 1.3 msec) */
472                     sum += ((delay_factor >> 7) - _delay(1));
473           }
474 
475           /* Disable timer interrupts and reset service routine */
476           via_reg(VIA1, vIER) = V1IF_T1;
477           via1_register_irq(VIA1_T1, (void (*)(void *))rtclock_intr, NULL);
478 
479           /*
480            * If this weren't integer math, the following would look
481            * a lot prettier.  It should really be something like
482            * this:
483            *        delay_factor = ((sum / 8) / (1024 * 1.2766)) * 128;
484            * That is, average the sum, divide by the number of usec,
485            * and multiply by a scale factor of 128.
486            *
487            * We can accomplish the same thing by simplifying and using
488            * shifts, being careful to avoid as much loss of precision
489            * as possible.  (If the sum exceeds UINT_MAX/10000, we need
490            * to rearrange the calculation slightly to do this.)
491            */
492           if (sum > (UINT_MAX / 10000)) /* This is a _fast_ machine! */
493                     delay_factor = (((sum >> 3) * 10000) / CLK_RATE) >> 3;
494           else
495                     delay_factor = (((sum * 10000) >> 3) / CLK_RATE) >> 3;
496 
497           /* Reset the delay_flag for normal use */
498           delay_flag = 1;
499 
500 #ifdef DEBUG
501           if (clock_debug)
502                     printf("mac68k_calibrate_delay(): delay_factor calibrated\n");
503 #endif
504 
505           (void)splhigh();
506 }
507