1 /* $NetBSD: clock.c,v 1.41 2020/05/29 12:30:40 rin Exp $ */
2
3 /*
4 * Copyright (c) 1988 University of Utah.
5 * Copyright (c) 1982, 1990, 1993
6 * The Regents of the University of California. 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 * from: Utah $Hdr: clock.c 1.18 91/01/21$
37 *
38 * @(#)clock.c 8.2 (Berkeley) 1/12/94
39 */
40
41 /*
42 * HPs use the MC6840 PTM with the following arrangement:
43 * Timers 1 and 3 are externally driver from a 25 MHz source.
44 * Output from timer 3 is tied to the input of timer 2.
45 * The latter makes it possible to use timers 3 and 2 together to get
46 * a 32-bit countdown timer.
47 */
48
49 #include <sys/cdefs.h>
50 __KERNEL_RCSID(0, "$NetBSD: clock.c,v 1.41 2020/05/29 12:30:40 rin Exp $");
51
52 #include <sys/param.h>
53 #include <sys/systm.h>
54 #include <sys/kernel.h>
55 #include <sys/timetc.h>
56
57 #include <machine/psl.h>
58 #include <machine/cpu.h>
59 #include <machine/hp300spu.h>
60
61 #include <hp300/hp300/clockreg.h>
62
63 #ifdef GPROF
64 #include <sys/gmon.h>
65 #endif
66
67 void statintr(struct clockframe *);
68 static u_int mc6840_counter(struct timecounter *);
69
70 static int clkstd[1];
71
72 int clkint; /* clock interval, as loaded */
73 uint32_t clkcounter; /* for timecounter */
74
75 /*
76 * Statistics clock interval and variance, in usec. Variance must be a
77 * power of two. Since this gives us an even number, not an odd number,
78 * we discard one case and compensate. That is, a variance of 1024 would
79 * give us offsets in [0..1023]. Instead, we take offsets in [1..1023].
80 * This is symmetric about the point 512, or statvar/2, and thus averages
81 * to that value (assuming uniform random numbers).
82 */
83 static int statvar = 1024 / 4; /* {stat,prof}clock variance */
84 static int statmin; /* statclock interval - variance/2 */
85 static int profmin; /* profclock interval - variance/2 */
86 static int timer3min; /* current, from above choices */
87 static int statprev; /* previous value in stat timer */
88
89 /*
90 * Machine-dependent clock routines.
91 *
92 * A note on the real-time clock:
93 * We actually load the clock with interval-1 instead of interval.
94 * This is because the counter decrements to zero after N+1 enabled clock
95 * periods where N is the value loaded into the counter.
96 *
97 * The frequencies of the HP300 clocks must be a multiple of four
98 * microseconds (since the clock counts in 4 us units).
99 */
100 #define COUNTS_PER_SEC (1000000 / CLK_RESOLUTION)
101
102 /*
103 * Calibrate the delay constant, based on Chuck Cranor's
104 * mvme68k delay calibration algorithm.
105 */
106 void
hp300_calibrate_delay(void)107 hp300_calibrate_delay(void)
108 {
109 extern int delay_divisor;
110 volatile struct clkreg *clk;
111 volatile u_char csr;
112 int intvl;
113
114 clkstd[0] = IIOV(0x5F8000); /* XXX yuck */
115 clk = (volatile struct clkreg *)clkstd[0];
116
117 /*
118 * Calibrate delay() using the 4 usec counter.
119 * We adjust delay_divisor until we get the result we want.
120 * We assume we've been called at splhigh().
121 */
122 for (delay_divisor = 140; delay_divisor > 1; delay_divisor--) {
123 /* Reset clock chip */
124 clk->clk_cr2 = CLK_CR1;
125 clk->clk_cr1 = CLK_RESET;
126
127 /*
128 * Prime the timer. We're looking for
129 * 10,000 usec (10ms). See interval comment
130 * above.
131 */
132 intvl = (10000 / CLK_RESOLUTION) - 1;
133 __asm volatile(" movpw %0,%1@(5)" : : "d" (intvl), "a" (clk));
134
135 /* Enable the timer */
136 clk->clk_cr2 = CLK_CR1;
137 clk->clk_cr1 = CLK_IENAB;
138
139 delay(10000);
140
141 /* Timer1 interrupt flag high? */
142 csr = clk->clk_sr;
143 if (csr & CLK_INT1) {
144 /*
145 * Got it. Clear interrupt and get outta here.
146 */
147 __asm volatile(" movpw %0@(5),%1" : :
148 "a" (clk), "d" (intvl));
149 break;
150 }
151
152 /*
153 * Nope. Poll for completion of the interval,
154 * clear interrupt, and try again.
155 */
156 do {
157 csr = clk->clk_sr;
158 } while ((csr & CLK_INT1) == 0);
159
160 __asm volatile(" movpw %0@(5),%1" : : "a" (clk), "d" (intvl));
161 }
162
163 /*
164 * Make sure the clock interrupt is disabled. Otherwise,
165 * we can end up calling hardclock() before proc0 is set up,
166 * causing a bad pointer deref.
167 */
168 clk->clk_cr2 = CLK_CR1;
169 clk->clk_cr1 = CLK_RESET;
170
171 /*
172 * Sanity check the delay_divisor value. If we totally lost,
173 * assume a 50MHz CPU;
174 */
175 if (delay_divisor == 0)
176 delay_divisor = 2048 / 50;
177
178 /* Calculate CPU speed. */
179 cpuspeed = 2048 / delay_divisor;
180 }
181
182 /*
183 * Set up the real-time and statistics clocks. Leave stathz 0 only if
184 * no alternative timer is available.
185 */
186 void
cpu_initclocks(void)187 cpu_initclocks(void)
188 {
189 volatile struct clkreg *clk;
190 int intvl, statint, profint, minint;
191 static struct timecounter tc = {
192 .tc_get_timecount = mc6840_counter,
193 .tc_counter_mask = ~0,
194 .tc_frequency = COUNTS_PER_SEC,
195 .tc_name = "mc6840",
196 .tc_quality = 100,
197 };
198
199 clkstd[0] = IIOV(0x5F8000); /* XXX grot */
200 clk = (volatile struct clkreg *)clkstd[0];
201
202 if (COUNTS_PER_SEC % hz) {
203 printf("cannot get %d Hz clock; using 100 Hz\n", hz);
204 hz = 100;
205 }
206 /*
207 * Clock has several counters, so we can always use separate
208 * statclock.
209 */
210 if (stathz == 0) /* XXX should be set in param.c */
211 stathz = hz;
212 else if (COUNTS_PER_SEC % stathz) {
213 printf("cannot get %d Hz statclock; using 100 Hz\n", stathz);
214 stathz = 100;
215 }
216 if (profhz == 0) /* XXX should be set in param.c */
217 profhz = stathz * 5;
218 else if (profhz < stathz || COUNTS_PER_SEC % profhz) {
219 printf("cannot get %d Hz profclock; using %d Hz\n",
220 profhz, stathz);
221 profhz = stathz;
222 }
223
224 intvl = COUNTS_PER_SEC / hz;
225 statint = COUNTS_PER_SEC / stathz;
226 profint = COUNTS_PER_SEC / profhz;
227 minint = statint / 2 + 100;
228 while (statvar > minint)
229 statvar >>= 1;
230
231 tick = intvl * CLK_RESOLUTION;
232
233 /* adjust interval counts, per note above */
234 intvl--;
235 statint--;
236 profint--;
237
238 /* calculate base reload values */
239 clkint = intvl;
240 statmin = statint - (statvar >> 1);
241 profmin = profint - (statvar >> 1);
242 timer3min = statmin;
243 statprev = statint;
244
245 /* finally, load hardware */
246 clk->clk_cr2 = CLK_CR1;
247 clk->clk_cr1 = CLK_RESET;
248 __asm volatile(" movpw %0,%1@(5)" : : "d" (intvl), "a" (clk));
249 __asm volatile(" movpw %0,%1@(9)" : : "d" (0), "a" (clk));
250 __asm volatile(" movpw %0,%1@(13)" : : "d" (statint), "a" (clk));
251 clk->clk_cr2 = CLK_CR1;
252 clk->clk_cr1 = CLK_IENAB;
253 clk->clk_cr2 = CLK_CR3;
254 clk->clk_cr3 = CLK_IENAB;
255
256 tc_init(&tc);
257 }
258
259 /*
260 * We assume newhz is either stathz or profhz, and that neither will
261 * change after being set up above. Could recalculate intervals here
262 * but that would be a drag.
263 */
264 void
setstatclockrate(int newhz)265 setstatclockrate(int newhz)
266 {
267
268 if (newhz == stathz)
269 timer3min = statmin;
270 else
271 timer3min = profmin;
272 }
273
274 /*
275 * Statistics/profiling clock interrupt. Compute a new interval.
276 * Interrupt has already been cleared.
277 *
278 * DO THIS INLINE IN locore.s?
279 */
280 void
statintr(struct clockframe * fp)281 statintr(struct clockframe *fp)
282 {
283 volatile struct clkreg *clk;
284 int newint, r, var;
285
286 clk = (volatile struct clkreg *)clkstd[0];
287 var = statvar;
288 do {
289 r = random() & (var - 1);
290 } while (r == 0);
291 newint = timer3min + r;
292
293 /*
294 * The timer was automatically reloaded with the previous latch
295 * value at the time of the interrupt. Compensate now for the
296 * amount of time that has run off since then (minimum of 2-12
297 * timer ticks depending on CPU type) plus one tick roundoff.
298 * This should keep us closer to the mean.
299 */
300 __asm volatile(" clrl %0; movpw %1@(13),%0" : "=d" (r) : "a" (clk));
301 newint -= (statprev - r + 1);
302
303 __asm volatile(" movpw %0,%1@(13)" : : "d" (newint), "a" (clk));
304 statprev = newint;
305 statclock(fp);
306 }
307
308 u_int
mc6840_counter(struct timecounter * tc)309 mc6840_counter(struct timecounter *tc)
310 {
311 volatile struct clkreg *clk;
312 uint32_t ccounter, count;
313 static uint32_t lastcount;
314 int s;
315
316 clk = (volatile struct clkreg *)clkstd[0];
317
318 s = splclock();
319 ccounter = clkcounter;
320 /* XXX reading counter clears interrupt flag?? */
321 __asm volatile (" clrl %0; movpw %1@(5),%0"
322 : "=d" (count) : "a" (clk));
323 splx(s);
324
325 count = ccounter + (clkint - count);
326 if ((int32_t)(count - lastcount) < 0) {
327 /* XXX wrapped; maybe hardclock() is blocked more than 1/HZ */
328 count = lastcount + 1;
329 }
330 lastcount = count;
331
332 return count;
333 }
334