FreeBSD/Linux Kernel Cross Reference
sys/pc/i8253.c
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 cpufreq = (vlong)loops*((aalcycles*2*Freq)/x);
164 m->loopconst = (cpufreq/1000)/aalcycles; /* AAM+LOOP's for 1 ms */
165
166 if(m->havetsc){
167 /* counter goes up by 2*Freq */
168 b = (b-a)<<1;
169 b *= Freq;
170 b /= x;
171
172 /*
173 * round to the nearest megahz
174 */
175 m->cpumhz = (b+500000)/1000000L;
176 m->cpuhz = b;
177 m->cyclefreq = b;
178 } else {
179 /*
180 * add in possible 0.5% error and convert to MHz
181 */
182 m->cpumhz = (cpufreq + cpufreq/200)/1000000;
183 m->cpuhz = cpufreq;
184 }
185
186 i8253.hz = Freq<<Tickshift;
187 }
188
189 void
190 i8253timerset(uvlong next)
191 {
192 long period;
193 ulong want;
194 ulong now;
195
196 period = MaxPeriod;
197 if(next != 0){
198 want = next>>Tickshift;
199 now = i8253.ticks; /* assuming whomever called us just did fastticks() */
200
201 period = want - now;
202 if(period < MinPeriod)
203 period = MinPeriod;
204 else if(period > MaxPeriod)
205 period = MaxPeriod;
206 }
207
208 /* hysteresis */
209 if(i8253.period != period){
210 ilock(&i8253);
211 /* load new value */
212 outb(Tmode, Load0|Square);
213 outb(T0cntr, period); /* low byte */
214 outb(T0cntr, period >> 8); /* high byte */
215
216 /* remember period */
217 i8253.period = period;
218 i8253.periodset++;
219 iunlock(&i8253);
220 }
221 }
222
223 static void
224 i8253clock(Ureg* ureg, void*)
225 {
226 timerintr(ureg, 0);
227 }
228
229 void
230 i8253enable(void)
231 {
232 i8253.enabled = 1;
233 i8253.period = Freq/HZ;
234 intrenable(IrqCLOCK, i8253clock, 0, BUSUNKNOWN, "clock");
235 }
236
237 void
238 i8253link(void)
239 {
240 }
241
242 /*
243 * return the total ticks of counter 2. We shift by
244 * 8 to give timesync more wriggle room for interpretation
245 * of the frequency
246 */
247 uvlong
248 i8253read(uvlong *hz)
249 {
250 ushort y, x;
251 uvlong ticks;
252
253 if(hz)
254 *hz = i8253.hz;
255
256 ilock(&i8253);
257 outb(Tmode, Latch2);
258 y = inb(T2cntr);
259 y |= inb(T2cntr)<<8;
260
261 if(y < i8253.last)
262 x = i8253.last - y;
263 else {
264 x = i8253.last + (0x10000 - y);
265 if (x > 3*MaxPeriod) {
266 outb(Tmode, Load2|Square);
267 outb(T2cntr, 0); /* low byte */
268 outb(T2cntr, 0); /* high byte */
269 y = 0xFFFF;
270 x = i8253.period;
271 }
272 }
273 i8253.last = y;
274 i8253.ticks += x>>1;
275 ticks = i8253.ticks;
276 iunlock(&i8253);
277
278 return ticks<<Tickshift;
279 }
280
281 void
282 delay(int millisecs)
283 {
284 millisecs *= m->loopconst;
285 if(millisecs <= 0)
286 millisecs = 1;
287 aamloop(millisecs);
288 }
289
290 void
291 microdelay(int microsecs)
292 {
293 microsecs *= m->loopconst;
294 microsecs /= 1000;
295 if(microsecs <= 0)
296 microsecs = 1;
297 aamloop(microsecs);
298 }
299
300 /*
301 * performance measurement ticks. must be low overhead.
302 * doesn't have to count over a second.
303 */
304 ulong
305 perfticks(void)
306 {
307 uvlong x;
308
309 if(m->havetsc)
310 cycles(&x);
311 else
312 x = 0;
313 return x;
314 }
Cache object: 22b481a235c684a1d7d9767be2e27277
|