1 #include "u.h" 2 #include "../port/lib.h" 3 #include "mem.h" 4 #include "dat.h" 5 #include "fns.h" 6 #include "io.h" 7 8 /* 9 * 8253 timer 10 */ 11 enum 12 { 13 T0cntr= 0x40, /* counter ports */ 14 T1cntr= 0x41, /* ... */ 15 T2cntr= 0x42, /* ... */ 16 Tmode= 0x43, /* mode port (control word register) */ 17 T2ctl= 0x61, /* counter 2 control port */ 18 19 /* commands */ 20 Latch0= 0x00, /* latch counter 0's value */ 21 Load0l= 0x10, /* load counter 0's lsb */ 22 Load0m= 0x20, /* load counter 0's msb */ 23 Load0= 0x30, /* load counter 0 with 2 bytes */ 24 25 Latch1= 0x40, /* latch counter 1's value */ 26 Load1l= 0x50, /* load counter 1's lsb */ 27 Load1m= 0x60, /* load counter 1's msb */ 28 Load1= 0x70, /* load counter 1 with 2 bytes */ 29 30 Latch2= 0x80, /* latch counter 2's value */ 31 Load2l= 0x90, /* load counter 2's lsb */ 32 Load2m= 0xa0, /* load counter 2's msb */ 33 Load2= 0xb0, /* load counter 2 with 2 bytes */ 34 35 /* 8254 read-back command: everything > pc-at has an 8254 */ 36 Rdback= 0xc0, /* readback counters & status */ 37 Rdnstat=0x10, /* don't read status */ 38 Rdncnt= 0x20, /* don't read counter value */ 39 Rd0cntr=0x02, /* read back for which counter */ 40 Rd1cntr=0x04, 41 Rd2cntr=0x08, 42 43 /* modes */ 44 ModeMsk=0xe, 45 Square= 0x6, /* periodic square wave */ 46 Trigger=0x0, /* interrupt on terminal count */ 47 Sstrobe=0x8, /* software triggered strobe */ 48 49 /* T2ctl bits */ 50 T2gate= (1<<0), /* enable T2 counting */ 51 T2spkr= (1<<1), /* connect T2 out to speaker */ 52 T2out= (1<<5), /* output of T2 */ 53 54 Freq= 1193182, /* Real clock frequency */ 55 Tickshift=8, /* extra accuracy */ 56 MaxPeriod=Freq/HZ, 57 MinPeriod=Freq/(100*HZ), 58 }; 59 60 typedef struct I8253 I8253; 61 struct I8253 62 { 63 Lock; 64 ulong period; /* current clock period */ 65 int enabled; 66 uvlong hz; 67 68 ushort last; /* last value of clock 1 */ 69 uvlong ticks; /* cumulative ticks of counter 1 */ 70 71 ulong periodset; 72 }; 73 I8253 i8253; 74 75 void 76 i8253init(void) 77 { 78 int loops, x; 79 80 ioalloc(T0cntr, 4, 0, "i8253"); 81 ioalloc(T2ctl, 1, 0, "i8253.cntr2ctl"); 82 83 i8253.period = Freq/HZ; 84 85 /* 86 * enable a 1/HZ interrupt for providing scheduling interrupts 87 */ 88 outb(Tmode, Load0|Square); 89 outb(T0cntr, (Freq/HZ)); /* low byte */ 90 outb(T0cntr, (Freq/HZ)>>8); /* high byte */ 91 92 /* 93 * enable a longer period counter to use as a clock 94 */ 95 outb(Tmode, Load2|Square); 96 outb(T2cntr, 0); /* low byte */ 97 outb(T2cntr, 0); /* high byte */ 98 x = inb(T2ctl); 99 x |= T2gate; 100 outb(T2ctl, x); 101 102 /* 103 * Introduce a little delay to make sure the count is 104 * latched and the timer is counting down; with a fast 105 * enough processor this may not be the case. 106 * The i8254 (which this probably is) has a read-back 107 * command which can be used to make sure the counting 108 * register has been written into the counting element. 109 */ 110 x = (Freq/HZ); 111 for(loops = 0; loops < 100000 && x >= (Freq/HZ); loops++){ 112 outb(Tmode, Latch0); 113 x = inb(T0cntr); 114 x |= inb(T0cntr)<<8; 115 } 116 } 117 118 void 119 guesscpuhz(int aalcycles) 120 { 121 int loops, incr, x, y; 122 uvlong a, b, cpufreq; 123 124 /* find biggest loop that doesn't wrap */ 125 incr = 16000000/(aalcycles*HZ*2); 126 x = 2000; 127 for(loops = incr; loops < 64*1024; loops += incr) { 128 129 /* 130 * measure time for the loop 131 * 132 * MOVL loops,CX 133 * aaml1: AAM 134 * LOOP aaml1 135 * 136 * the time for the loop should be independent of external 137 * cache and memory system since it fits in the execution 138 * prefetch buffer. 139 * 140 */ 141 outb(Tmode, Latch0); 142 cycles(&a); 143 x = inb(T0cntr); 144 x |= inb(T0cntr)<<8; 145 aamloop(loops); 146 outb(Tmode, Latch0); 147 cycles(&b); 148 y = inb(T0cntr); 149 y |= inb(T0cntr)<<8; 150 x -= y; 151 152 if(x < 0) 153 x += Freq/HZ; 154 155 if(x > Freq/(3*HZ)) 156 break; 157 } 158 159 /* 160 * figure out clock frequency and a loop multiplier for delay(). 161 * n.b. counter goes up by 2*Freq 162 */ 163 if(x == 0) 164 x = 1; /* avoid division by zero on vmware 7 */ 165 cpufreq = (vlong)loops*((aalcycles*2*Freq)/x); 166 m->loopconst = (cpufreq/1000)/aalcycles; /* AAM+LOOP's for 1 ms */ 167 168 if(m->havetsc && a != b){ /* a == b means virtualbox has confused us */ 169 /* counter goes up by 2*Freq */ 170 b = (b-a)<<1; 171 b *= Freq; 172 b /= x; 173 174 /* 175 * round to the nearest megahz 176 */ 177 m->cpumhz = (b+500000)/1000000L; 178 m->cpuhz = b; 179 m->cyclefreq = b; 180 } else { 181 /* 182 * add in possible 0.5% error and convert to MHz 183 */ 184 m->cpumhz = (cpufreq + cpufreq/200)/1000000; 185 m->cpuhz = cpufreq; 186 } 187 188 /* don't divide by zero in trap.c */ 189 if (m->cpumhz == 0) 190 panic("guesscpuhz: zero m->cpumhz"); 191 i8253.hz = Freq<<Tickshift; 192 } 193 194 void 195 i8253timerset(uvlong next) 196 { 197 long period; 198 ulong want; 199 ulong now; 200 201 period = MaxPeriod; 202 if(next != 0){ 203 want = next>>Tickshift; 204 now = i8253.ticks; /* assuming whomever called us just did fastticks() */ 205 206 period = want - now; 207 if(period < MinPeriod) 208 period = MinPeriod; 209 else if(period > MaxPeriod) 210 period = MaxPeriod; 211 } 212 213 /* hysteresis */ 214 if(i8253.period != period){ 215 ilock(&i8253); 216 /* load new value */ 217 outb(Tmode, Load0|Square); 218 outb(T0cntr, period); /* low byte */ 219 outb(T0cntr, period >> 8); /* high byte */ 220 221 /* remember period */ 222 i8253.period = period; 223 i8253.periodset++; 224 iunlock(&i8253); 225 } 226 } 227 228 static void 229 i8253clock(Ureg* ureg, void*) 230 { 231 timerintr(ureg, 0); 232 } 233 234 void 235 i8253enable(void) 236 { 237 i8253.enabled = 1; 238 i8253.period = Freq/HZ; 239 intrenable(IrqCLOCK, i8253clock, 0, BUSUNKNOWN, "clock"); 240 } 241 242 void 243 i8253link(void) 244 { 245 } 246 247 /* 248 * return the total ticks of counter 2. We shift by 249 * 8 to give timesync more wriggle room for interpretation 250 * of the frequency 251 */ 252 uvlong 253 i8253read(uvlong *hz) 254 { 255 ushort y, x; 256 uvlong ticks; 257 258 if(hz) 259 *hz = i8253.hz; 260 261 ilock(&i8253); 262 outb(Tmode, Latch2); 263 y = inb(T2cntr); 264 y |= inb(T2cntr)<<8; 265 266 if(y < i8253.last) 267 x = i8253.last - y; 268 else { 269 x = i8253.last + (0x10000 - y); 270 if (x > 3*MaxPeriod) { 271 outb(Tmode, Load2|Square); 272 outb(T2cntr, 0); /* low byte */ 273 outb(T2cntr, 0); /* high byte */ 274 y = 0xFFFF; 275 x = i8253.period; 276 } 277 } 278 i8253.last = y; 279 i8253.ticks += x>>1; 280 ticks = i8253.ticks; 281 iunlock(&i8253); 282 283 return ticks<<Tickshift; 284 } 285 286 void 287 delay(int millisecs) 288 { 289 millisecs *= m->loopconst; 290 if(millisecs <= 0) 291 millisecs = 1; 292 aamloop(millisecs); 293 } 294 295 void 296 microdelay(int microsecs) 297 { 298 microsecs *= m->loopconst; 299 microsecs /= 1000; 300 if(microsecs <= 0) 301 microsecs = 1; 302 aamloop(microsecs); 303 } 304 305 /* 306 * performance measurement ticks. must be low overhead. 307 * doesn't have to count over a second. 308 */ 309 ulong 310 perfticks(void) 311 { 312 uvlong x; 313 314 if(m->havetsc) 315 cycles(&x); 316 else 317 x = 0; 318 return x; 319 } 320