Home | History | Annotate | Line # | Download | only in hp300
      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
    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
    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
    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
    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
    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