xref: /netbsd-src/sys/arch/hp300/hp300/clock.c (revision 482eef70502290f7cbd2cb9a24a4f41e6bacd98d)
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