1 /* $NetBSD: mips_mcclock.c,v 1.11 2002/03/05 15:54:33 simonb Exp $ */ 2 3 /* 4 * Copyright (c) 1997 Jonathan Stone (hereinafter referred to as the author) 5 * All rights reserved. 6 * 7 * Redistribution and use in source and binary forms, with or without 8 * modification, are permitted provided that the following conditions 9 * are met: 10 * 1. Redistributions of source code must retain the above copyright 11 * notice, this list of conditions and the following disclaimer. 12 * 2. Redistributions in binary form must reproduce the above copyright 13 * notice, this list of conditions and the following disclaimer in the 14 * documentation and/or other materials provided with the distribution. 15 * 3. All advertising materials mentioning features or use of this software 16 * must display the following acknowledgement: 17 * This product includes software developed by Jonathan Stone for 18 * the NetBSD Project. 19 * 4. The name of the author may not be used to endorse or promote products 20 * derived from this software without specific prior written permission. 21 * 22 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND 23 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 24 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 25 * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE 26 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 27 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 28 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 29 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 30 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 31 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 32 * SUCH DAMAGE. 33 */ 34 35 #include <sys/cdefs.h> /* RCS ID & Copyright macro defns */ 36 37 __KERNEL_RCSID(0, "$NetBSD: mips_mcclock.c,v 1.11 2002/03/05 15:54:33 simonb Exp $"); 38 39 #include <sys/param.h> 40 #include <sys/systm.h> 41 #include <sys/device.h> 42 43 #include <dev/ic/mc146818reg.h> 44 #include <dev/dec/mcclockvar.h> 45 #include <dev/dec/mcclock_pad32.h> 46 47 #include <mips/cpu.h> /* MIPS_HAS_CLOCK */ 48 #include <mips/locore.h> /* mips_cp0_cause_read() */ 49 #include <mips/mips/mips_mcclock.h> 50 51 52 unsigned mips_mc_cpuspeed(void *, int, int (*)(void *, int)); 53 int mips_mcclock_tickloop(void *, int); 54 unsigned mips_mcclock_to_mhz(unsigned iters); 55 56 57 /* 58 * Compute MHz and DELAY() constants using the default 59 * polling function. 60 */ 61 unsigned 62 mc_cpuspeed(mcclock_addr, cpuintmask) 63 vaddr_t mcclock_addr; 64 int cpuintmask; 65 { 66 return mips_mc_cpuspeed((void *)mcclock_addr, cpuintmask, 67 mips_mcclock_tickloop); 68 } 69 70 71 /* 72 * Estimate CPU cycle speed by counting cycles (acutally executions of a 73 * one-line loop) between two adjacent ticks of an mc146818 clock. 74 * Return loop iteration count so hand-calibrated MD code can 75 * estimate clock speed from cycles. 76 * 77 * Runs before CPU is attached (so we can print CPU speed) which is 78 * before the clock is attached, so we can't use the normal clock driver. 79 */ 80 unsigned 81 mips_mc_cpuspeed(mcclock_addr, clockmask, tickpollfn) 82 void *mcclock_addr; 83 int clockmask; 84 int (*tickpollfn)(void *mcclock_addr, int clockmask); 85 { 86 int s; 87 int iters = 0; 88 int saved_rega, saved_regb; 89 volatile struct mcclock_pad32_clockdatum *clk = (void *)mcclock_addr; 90 91 /* 92 * Block all interrupts, including clock ticks. 93 */ 94 s = splhigh(); 95 96 /* 97 * Enable periodic interrupst on the mc146818, 98 * and set it up for 256Hz (4ms) interrupts. 99 * Save any state we change so we can restore it on exit. 100 */ 101 saved_rega = clk[MC_REGA].datum; 102 saved_regb = clk[MC_REGB].datum; 103 104 #if 0 105 mcclock_addr->rega = (saved_rega & ~MC_BASE_RESET) | MC_RATE_256_Hz; 106 mcclock_addr->regb = MC_REGB_BINARY|MC_REGB_24HR|MC_REGB_PIE; 107 #else 108 clk[MC_REGA].datum = MC_BASE_32_KHz | MC_RATE_256_Hz; 109 clk[MC_REGB].datum = MC_REGB_BINARY|MC_REGB_24HR|MC_REGB_PIE| MC_REGB_SQWE; 110 #endif 111 /* count loop iterations between ticks */ 112 iters = (*tickpollfn)(mcclock_addr, clockmask); 113 114 /* Restore mcclock registers */ 115 clk[MC_REGA].datum = saved_rega; 116 clk[MC_REGB].datum = saved_regb; 117 118 splx(s); 119 120 /* 121 * Compute approximate CPU speed in MHz, and an 122 * appropriate base for DELAY() and delay(), from 123 * the number of completed iterations. 124 */ 125 cpu_mhz = mips_mcclock_to_mhz(iters); 126 127 #if defined(DEBUG) 128 printf("mcclock: iters %d computed MHz %d, instrs per usec=%d\n", 129 iters, cpu_mhz, cpuspeed); 130 #endif 131 return (iters); 132 } 133 134 135 /* 136 * Poll mcclock chip for the next tick interrupt and count 137 * instructions until the subsequent tick. 138 * 139 * XXX Assumes the mcclock chip has exclusive use of a CPU interrupt line. 140 * XXX Assumes bus access to clock registers is cheap (not a function call). 141 * MD alternatives must be used where this doesn't hold. 142 */ 143 144 int 145 mips_mcclock_tickloop(mcclock_addr, clockmask) 146 void *mcclock_addr; 147 int clockmask; 148 { 149 int iters = 0; 150 volatile int junk; 151 volatile struct mcclock_pad32_clockdatum *clk = mcclock_addr; 152 153 /* clear any old pending interrupts */ 154 junk = clk[MC_REGC].datum; 155 junk++; junk++; junk++; junk++; 156 157 /* Poll clock interrupt, waiting for next tick to happen. */ 158 while ((mips_cp0_cause_read() & clockmask) == 0) 159 ; 160 161 /* Ack the mc146818 interrupt caused by starting tick. */ 162 junk = clk[MC_REGC].datum; 163 164 junk++; junk++; junk++; junk++; 165 166 /* Count loops until next tick-interrupt request occurs (4ms). */ 167 if (MIPS_HAS_CLOCK) { 168 while ((mips_cp0_cause_read() & clockmask) == 0) { 169 __asm __volatile ("nop; nop; nop; nop"); 170 iters++; 171 } 172 } else { 173 while ((mips_cp0_cause_read() & clockmask) == 0) { 174 __asm __volatile ("nop; nop;"); 175 iters++; 176 } 177 } 178 179 /* Ack the interrupt from the just-gone-off tick */ 180 junk = clk[MC_REGC].datum; 181 182 return (iters); 183 } 184 185 186 /* 187 * mips_mcclock_to_mhz(iters) -- convert an mcclock cycles-per-tick count 188 * to a CPU speed in MHz. 189 * 190 * Side Effects: 191 * set the global variables "cpuspeed", used by DELAY() and delay() 192 * as an instructions-per-microsecond multiplier, to an value appropriate 193 * for the estimated clock speed. 194 */ 195 unsigned 196 mips_mcclock_to_mhz(unsigned iters) 197 { 198 unsigned mhz = 0; 199 200 /* XXX KN01? */ 201 202 /* 203 * Measured thresholds for Digital systems from Sean Davidson. 204 * 205 * r3000-core DECstations values fit to: 206 * iters per 4ms tick = 425 * MHz) 207 * instructions per mhz = kHz * 575 208 * with about 2 Mhz slop to allow for variation. 209 */ 210 211 #ifdef MIPS3_PLUS 212 if (CPUISMIPS3) { 213 if (iters < 18100) { 214 /* error */ 215 printf("mcclock loop count %d too low for r4000\n", 216 iters); 217 mhz = 45; 218 cpuspeed = 20; /* XXX */ 219 } else if (iters < 21000) { 220 mhz = 50; 221 cpuspeed = 25; /* XXX */ 222 } else if (iters < 25315) { 223 mhz = 60; 224 cpuspeed = 27; /* XXX */ 225 } else if (iters < 28497) { 226 mhz = 67; 227 cpuspeed = 33; /* XXX */ 228 } else if (iters < 31500) { 229 mhz = 75; 230 cpuspeed = 38; /* XXX */ 231 } 232 } 233 #endif /* MIPS3_PLUS */ 234 235 #ifdef MIPS1 236 if (!CPUISMIPS3) { 237 if (iters < 5100) { 238 /* assume memory-bound DS 2100 */ 239 mhz = 12; /* 12.5 MHz? */ 240 cpuspeed = 8; 241 } else if (iters < 6700) { 242 /* assume memory-bound DS3100 */ 243 mhz = 15; 244 cpuspeed = 8; 245 } else if (iters < 8800) { 246 mhz = 20; 247 cpuspeed = 11; 248 } else if (iters < 11300) { 249 mhz = 25; 250 cpuspeed = 13; 251 } else if (iters < 14000) { 252 mhz = 33; 253 cpuspeed = 19; 254 } else if (iters < 15000) { 255 mhz = 36; 256 cpuspeed = 21; 257 } else if (iters < 16000) { 258 mhz = 40; 259 cpuspeed = 23; 260 } else if (iters < 18800) { 261 mhz = 45; 262 cpuspeed = 25; 263 } else if (iters < 21000) { 264 mhz = 50; 265 cpuspeed = 29; 266 } else if (iters < 26000) { 267 mhz = 60; 268 cpuspeed = 35; 269 } else { 270 /* XXX */ 271 mhz = 70; 272 cpuspeed = 40; 273 } 274 } 275 #endif /* MIPS1 */ 276 277 return (mhz); 278 } 279