1 #include "u.h"
2 #include "lib.h"
3 #include "mem.h"
4 #include "dat.h"
5 #include "fns.h"
6 #include "io.h"
7 #include "ureg.h"
8
9 /*
10 * 8253 timer
11 */
12 enum
13 {
14 T0cntr= 0x40, /* counter ports */
15 T1cntr= 0x41, /* ... */
16 T2cntr= 0x42, /* ... */
17 Tmode= 0x43, /* mode port */
18
19 /* commands */
20 Latch0= 0x00, /* latch counter 0's value */
21 Load0= 0x30, /* load counter 0 with 2 bytes */
22
23 /* modes */
24 Square= 0x36, /* perioic square wave */
25
26 Freq= 1193182, /* Real clock frequency */
27 };
28
29 static uvlong cpuhz = 66000000;
30 static int cpumhz = 66;
31 static int loopconst = 100;
32 int cpuidax, cpuiddx;
33 int havetsc;
34
35 extern void _cycles(uvlong*); /* in l.s */
36 extern void wrmsr(int, vlong);
37
38 static void
clockintr(Ureg *,void *)39 clockintr(Ureg*, void*)
40 {
41 m->ticks++;
42 checkalarms();
43 }
44
45 #define STEPPING(x) ((x)&0xf)
46 #define X86MODEL(x) (((x)>>4)&0xf)
47 #define X86FAMILY(x) (((x)>>8)&0xf)
48
49 enum
50 {
51 /* flags */
52 CpuidFPU = 0x001, /* on-chip floating point unit */
53 CpuidMCE = 0x080, /* machine check exception */
54 CpuidCX8 = 0x100, /* CMPXCHG8B instruction */
55 };
56
57 typedef struct
58 {
59 int family;
60 int model;
61 int aalcycles;
62 char *name;
63 } X86type;
64
65 X86type x86intel[] =
66 {
67 { 4, 0, 22, "486DX", }, /* known chips */
68 { 4, 1, 22, "486DX50", },
69 { 4, 2, 22, "486SX", },
70 { 4, 3, 22, "486DX2", },
71 { 4, 4, 22, "486SL", },
72 { 4, 5, 22, "486SX2", },
73 { 4, 7, 22, "DX2WB", }, /* P24D */
74 { 4, 8, 22, "DX4", }, /* P24C */
75 { 4, 9, 22, "DX4WB", }, /* P24CT */
76 { 5, 0, 23, "P5", },
77 { 5, 1, 23, "P5", },
78 { 5, 2, 23, "P54C", },
79 { 5, 3, 23, "P24T", },
80 { 5, 4, 23, "P55C MMX", },
81 { 5, 7, 23, "P54C VRT", },
82 { 6, 1, 16, "PentiumPro", },/* trial and error */
83 { 6, 3, 16, "PentiumII", },
84 { 6, 5, 16, "PentiumII/Xeon", },
85 { 6, 6, 16, "Celeron", },
86 { 6, 7, 16, "PentiumIII/Xeon", },
87 { 6, 8, 16, "PentiumIII/Xeon", },
88 { 6, 0xB, 16, "PentiumIII/Xeon", },
89 { 0xF, 1, 16, "P4", }, /* P4 */
90 { 0xF, 2, 16, "PentiumIV/Xeon", },
91
92 { 3, -1, 32, "386", }, /* family defaults */
93 { 4, -1, 22, "486", },
94 { 5, -1, 23, "P5", },
95 { 6, -1, 16, "P6", },
96 { 0xF, -1, 16, "P4", }, /* P4 */
97
98 { -1, -1, 16, "unknown", }, /* total default */
99 };
100
101
102 /*
103 * The AMD processors all implement the CPUID instruction.
104 * The later ones also return the processor name via functions
105 * 0x80000002, 0x80000003 and 0x80000004 in registers AX, BX, CX
106 * and DX:
107 * K5 "AMD-K5(tm) Processor"
108 * K6 "AMD-K6tm w/ multimedia extensions"
109 * K6 3D "AMD-K6(tm) 3D processor"
110 * K6 3D+ ?
111 */
112 static X86type x86amd[] =
113 {
114 { 5, 0, 23, "AMD-K5", }, /* guesswork */
115 { 5, 1, 23, "AMD-K5", }, /* guesswork */
116 { 5, 2, 23, "AMD-K5", }, /* guesswork */
117 { 5, 3, 23, "AMD-K5", }, /* guesswork */
118 { 5, 4, 23, "AMD Geode GX1", }, /* guesswork */
119 { 5, 5, 23, "AMD Geode GX2", }, /* guesswork */
120 { 5, 6, 11, "AMD-K6", }, /* trial and error */
121 { 5, 7, 11, "AMD-K6", }, /* trial and error */
122 { 5, 8, 11, "AMD-K6-2", }, /* trial and error */
123 { 5, 9, 11, "AMD-K6-III", },/* trial and error */
124 { 5, 0xa, 23, "AMD Geode LX", }, /* guesswork */
125
126 { 6, 1, 11, "AMD-Athlon", },/* trial and error */
127 { 6, 2, 11, "AMD-Athlon", },/* trial and error */
128
129 { 4, -1, 22, "Am486", }, /* guesswork */
130 { 5, -1, 23, "AMD-K5/K6", }, /* guesswork */
131 { 6, -1, 11, "AMD-Athlon", },/* guesswork */
132 { 0xF, -1, 11, "AMD64", }, /* guesswork */
133
134 { -1, -1, 11, "unknown", }, /* total default */
135 };
136
137 static X86type *cputype;
138
139
140 void
delay(int millisecs)141 delay(int millisecs)
142 {
143 millisecs *= loopconst;
144 if(millisecs <= 0)
145 millisecs = 1;
146 aamloop(millisecs);
147 }
148
149 void
microdelay(int microsecs)150 microdelay(int microsecs)
151 {
152 microsecs *= loopconst;
153 microsecs /= 1000;
154 if(microsecs <= 0)
155 microsecs = 1;
156 aamloop(microsecs);
157 }
158
159 extern void cpuid(char*, int*, int*);
160
161 X86type*
cpuidentify(void)162 cpuidentify(void)
163 {
164 int family, model;
165 X86type *t;
166 char cpuidid[16];
167 int cpuidax, cpuiddx;
168
169 cpuid(cpuidid, &cpuidax, &cpuiddx);
170 if(strncmp(cpuidid, "AuthenticAMD", 12) == 0 ||
171 strncmp(cpuidid, "Geode by NSC", 12) == 0)
172 t = x86amd;
173 else
174 t = x86intel;
175 family = X86FAMILY(cpuidax);
176 model = X86MODEL(cpuidax);
177 if (0)
178 print("cpuidentify: cpuidax 0x%ux cpuiddx 0x%ux\n",
179 cpuidax, cpuiddx);
180 while(t->name){
181 if((t->family == family && t->model == model)
182 || (t->family == family && t->model == -1)
183 || (t->family == -1))
184 break;
185 t++;
186 }
187 if(t->name == nil)
188 panic("cpuidentify");
189
190 if(cpuiddx & 0x10){
191 havetsc = 1;
192 if(cpuiddx & 0x20)
193 wrmsr(0x10, 0);
194 }
195
196 return t;
197 }
198
199 void
clockinit(void)200 clockinit(void)
201 {
202 uvlong a, b, cpufreq;
203 int loops, incr, x, y;
204 X86type *t;
205
206 /*
207 * set vector for clock interrupts
208 */
209 setvec(VectorCLOCK, clockintr, 0);
210
211 t = cpuidentify();
212
213 /*
214 * set clock for 1/HZ seconds
215 */
216 outb(Tmode, Load0|Square);
217 outb(T0cntr, (Freq/HZ)); /* low byte */
218 outb(T0cntr, (Freq/HZ)>>8); /* high byte */
219
220 /*
221 * Introduce a little delay to make sure the count is
222 * latched and the timer is counting down; with a fast
223 * enough processor this may not be the case.
224 * The i8254 (which this probably is) has a read-back
225 * command which can be used to make sure the counting
226 * register has been written into the counting element.
227 */
228 x = (Freq/HZ);
229 for(loops = 0; loops < 100000 && x >= (Freq/HZ); loops++){
230 outb(Tmode, Latch0);
231 x = inb(T0cntr);
232 x |= inb(T0cntr)<<8;
233 }
234
235 /* find biggest loop that doesn't wrap */
236 incr = 16000000/(t->aalcycles*HZ*2);
237 x = 2000;
238 for(loops = incr; loops < 64*1024; loops += incr) {
239
240 /*
241 * measure time for the loop
242 *
243 * MOVL loops,CX
244 * aaml1: AAM
245 * LOOP aaml1
246 *
247 * the time for the loop should be independent of external
248 * cache and memory system since it fits in the execution
249 * prefetch buffer.
250 *
251 */
252 outb(Tmode, Latch0);
253 if(havetsc)
254 _cycles(&a);
255 x = inb(T0cntr);
256 x |= inb(T0cntr)<<8;
257 aamloop(loops);
258 outb(Tmode, Latch0);
259 if(havetsc)
260 _cycles(&b);
261 y = inb(T0cntr);
262 y |= inb(T0cntr)<<8;
263 x -= y;
264
265 if(x < 0)
266 x += Freq/HZ;
267
268 if(x > Freq/(3*HZ))
269 break;
270 }
271
272 /*
273 * figure out clock frequency and a loop multiplier for delay().
274 * counter goes at twice the frequency, once per transition,
275 * i.e., twice per square wave
276 */
277 cpufreq = (vlong)loops*((t->aalcycles*2*Freq)/x);
278 loopconst = (cpufreq/1000)/t->aalcycles; /* AAM+LOOP's for 1 ms */
279
280 if(havetsc){
281 /* counter goes up by 2*Freq */
282 b = (b-a)<<1;
283 b *= Freq;
284 b /= x;
285
286 /*
287 * round to the nearest megahz
288 */
289 cpumhz = (b+500000)/1000000L;
290 cpuhz = b;
291 }
292 else{
293 /*
294 * add in possible .5% error and convert to MHz
295 */
296 cpumhz = (cpufreq + cpufreq/200)/1000000;
297 cpuhz = cpufreq;
298 }
299
300 if(debug){
301 int timeo;
302
303 print("%dMHz %s loop %d\n", cpumhz, t->name, loopconst);
304 print("tick...");
305 for(timeo = 0; timeo < 10; timeo++)
306 delay(1000);
307 print("tock...\n");
308 }
309 }
310