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