1 /* $NetBSD: clock.c,v 1.43 2026/04/23 02:54:39 thorpej 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.43 2026/04/23 02:54:39 thorpej 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 volatile struct clkreg *clk; 110 volatile u_char csr; 111 int intvl; 112 113 clkstd[0] = IIOV(0x5F8000); /* XXX yuck */ 114 clk = (volatile struct clkreg *)clkstd[0]; 115 116 /* 117 * Calibrate delay() using the 4 usec counter. 118 * We adjust delay_divisor until we get the result we want. 119 * We assume we've been called at splhigh(). 120 * 121 * See delay_divisor_est() definition and recommendation to 122 * assume a bit slower than you'll actually see. 123 */ 124 for (delay_divisor = delay_divisor_est(delay_calibration_weight(16)); 125 delay_divisor > 1; delay_divisor--) { 126 /* Reset clock chip */ 127 clk->clk_cr2 = CLK_CR1; 128 clk->clk_cr1 = CLK_RESET; 129 130 /* 131 * Prime the timer. We're looking for 132 * 10,000 usec (10ms). See interval comment 133 * above. 134 */ 135 intvl = (10000 / CLK_RESOLUTION) - 1; 136 __asm volatile(" movpw %0,%1@(5)" : : "d" (intvl), "a" (clk)); 137 138 /* Enable the timer */ 139 clk->clk_cr2 = CLK_CR1; 140 clk->clk_cr1 = CLK_IENAB; 141 142 delay(10000); 143 144 /* Timer1 interrupt flag high? */ 145 csr = clk->clk_sr; 146 if (csr & CLK_INT1) { 147 /* 148 * Got it. Clear interrupt and get outta here. 149 */ 150 __asm volatile(" movpw %0@(5),%1" : : 151 "a" (clk), "d" (intvl)); 152 break; 153 } 154 155 /* 156 * Nope. Poll for completion of the interval, 157 * clear interrupt, and try again. 158 */ 159 do { 160 csr = clk->clk_sr; 161 } while ((csr & CLK_INT1) == 0); 162 163 __asm volatile(" movpw %0@(5),%1" : : "a" (clk), "d" (intvl)); 164 } 165 166 /* 167 * Make sure the clock interrupt is disabled. Otherwise, 168 * we can end up calling hardclock() before proc0 is set up, 169 * causing a bad pointer deref. 170 */ 171 clk->clk_cr2 = CLK_CR1; 172 clk->clk_cr1 = CLK_RESET; 173 174 /* 175 * Sanity check the delay_divisor value. If we totally lost, 176 * assume the fastest machine HP shipped for the CPU class. 177 */ 178 if (delay_divisor == 0) { 179 delay_divisor = (cputype == CPU_68040) 180 ? delay_divisor_est40(33) 181 : delay_divisor_est(50); 182 } 183 } 184 185 /* 186 * Set up the real-time and statistics clocks. Leave stathz 0 only if 187 * no alternative timer is available. 188 */ 189 void 190 cpu_initclocks(void) 191 { 192 volatile struct clkreg *clk; 193 int intvl, statint, profint, minint; 194 static struct timecounter tc = { 195 .tc_get_timecount = mc6840_counter, 196 .tc_counter_mask = ~0, 197 .tc_frequency = COUNTS_PER_SEC, 198 .tc_name = "mc6840", 199 .tc_quality = 100, 200 }; 201 202 clkstd[0] = IIOV(0x5F8000); /* XXX grot */ 203 clk = (volatile struct clkreg *)clkstd[0]; 204 205 if (COUNTS_PER_SEC % hz) { 206 printf("cannot get %d Hz clock; using 100 Hz\n", hz); 207 hz = 100; 208 } 209 /* 210 * Clock has several counters, so we can always use separate 211 * statclock. 212 */ 213 if (stathz == 0) /* XXX should be set in param.c */ 214 stathz = hz; 215 else if (COUNTS_PER_SEC % stathz) { 216 printf("cannot get %d Hz statclock; using 100 Hz\n", stathz); 217 stathz = 100; 218 } 219 if (profhz == 0) /* XXX should be set in param.c */ 220 profhz = stathz * 5; 221 else if (profhz < stathz || COUNTS_PER_SEC % profhz) { 222 printf("cannot get %d Hz profclock; using %d Hz\n", 223 profhz, stathz); 224 profhz = stathz; 225 } 226 227 intvl = COUNTS_PER_SEC / hz; 228 statint = COUNTS_PER_SEC / stathz; 229 profint = COUNTS_PER_SEC / profhz; 230 minint = statint / 2 + 100; 231 while (statvar > minint) 232 statvar >>= 1; 233 234 tick = intvl * CLK_RESOLUTION; 235 236 /* adjust interval counts, per note above */ 237 intvl--; 238 statint--; 239 profint--; 240 241 /* calculate base reload values */ 242 clkint = intvl; 243 statmin = statint - (statvar >> 1); 244 profmin = profint - (statvar >> 1); 245 timer3min = statmin; 246 statprev = statint; 247 248 /* finally, load hardware */ 249 clk->clk_cr2 = CLK_CR1; 250 clk->clk_cr1 = CLK_RESET; 251 __asm volatile(" movpw %0,%1@(5)" : : "d" (intvl), "a" (clk)); 252 __asm volatile(" movpw %0,%1@(9)" : : "d" (0), "a" (clk)); 253 __asm volatile(" movpw %0,%1@(13)" : : "d" (statint), "a" (clk)); 254 clk->clk_cr2 = CLK_CR1; 255 clk->clk_cr1 = CLK_IENAB; 256 clk->clk_cr2 = CLK_CR3; 257 clk->clk_cr3 = CLK_IENAB; 258 259 tc_init(&tc); 260 } 261 262 /* 263 * We assume newhz is either stathz or profhz, and that neither will 264 * change after being set up above. Could recalculate intervals here 265 * but that would be a drag. 266 */ 267 void 268 setstatclockrate(int newhz) 269 { 270 271 if (newhz == stathz) 272 timer3min = statmin; 273 else 274 timer3min = profmin; 275 } 276 277 /* 278 * Statistics/profiling clock interrupt. Compute a new interval. 279 * Interrupt has already been cleared. 280 * 281 * DO THIS INLINE IN locore.s? 282 */ 283 void 284 statintr(struct clockframe *fp) 285 { 286 volatile struct clkreg *clk; 287 int newint, r, var; 288 289 clk = (volatile struct clkreg *)clkstd[0]; 290 var = statvar; 291 do { 292 r = random() & (var - 1); 293 } while (r == 0); 294 newint = timer3min + r; 295 296 /* 297 * The timer was automatically reloaded with the previous latch 298 * value at the time of the interrupt. Compensate now for the 299 * amount of time that has run off since then (minimum of 2-12 300 * timer ticks depending on CPU type) plus one tick roundoff. 301 * This should keep us closer to the mean. 302 */ 303 __asm volatile(" clrl %0; movpw %1@(13),%0" : "=d" (r) : "a" (clk)); 304 newint -= (statprev - r + 1); 305 306 __asm volatile(" movpw %0,%1@(13)" : : "d" (newint), "a" (clk)); 307 statprev = newint; 308 statclock(fp); 309 } 310 311 u_int 312 mc6840_counter(struct timecounter *tc) 313 { 314 volatile struct clkreg *clk; 315 uint32_t ccounter, count; 316 static uint32_t lastcount; 317 int s; 318 319 clk = (volatile struct clkreg *)clkstd[0]; 320 321 s = splclock(); 322 ccounter = clkcounter; 323 /* XXX reading counter clears interrupt flag?? */ 324 __asm volatile (" clrl %0; movpw %1@(5),%0" 325 : "=d" (count) : "a" (clk)); 326 splx(s); 327 328 count = ccounter + (clkint - count); 329 if ((int32_t)(count - lastcount) < 0) { 330 /* XXX wrapped; maybe hardclock() is blocked more than 1/HZ */ 331 count = lastcount + 1; 332 } 333 lastcount = count; 334 335 return count; 336 } 337