]> git.lizzy.rs Git - plan9front.git/blob - sys/src/9/pc/i8253.c
pc, pc64: more conservative pcirouting
[plan9front.git] / sys / src / 9 / 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
66         ushort  last;           /* last value of clock 1 */
67         uvlong  ticks;          /* cumulative ticks of counter 1 */
68
69         ulong   periodset;
70 };
71 static I8253 i8253;
72
73 void
74 i8253reset(void)
75 {
76         int loops, x;
77
78         ilock(&i8253);
79
80         i8253.last = 0;
81         i8253.period = Freq/HZ;
82
83         /*
84          *  enable a 1/HZ interrupt for providing scheduling interrupts
85          */
86         outb(Tmode, Load0|Square);
87         outb(T0cntr, (Freq/HZ));        /* low byte */
88         outb(T0cntr, (Freq/HZ)>>8);     /* high byte */
89
90         /*
91          *  enable a longer period counter to use as a clock
92          */
93         outb(Tmode, Load2|Square);
94         outb(T2cntr, 0);                /* low byte */
95         outb(T2cntr, 0);                /* high byte */
96         x = inb(T2ctl);
97         x |= T2gate;
98         outb(T2ctl, x);
99         
100         /*
101          * Introduce a little delay to make sure the count is
102          * latched and the timer is counting down; with a fast
103          * enough processor this may not be the case.
104          * The i8254 (which this probably is) has a read-back
105          * command which can be used to make sure the counting
106          * register has been written into the counting element.
107          */
108         x = (Freq/HZ);
109         for(loops = 0; loops < 100000 && x >= (Freq/HZ); loops++){
110                 outb(Tmode, Latch0);
111                 x = inb(T0cntr);
112                 x |= inb(T0cntr)<<8;
113         }
114
115         iunlock(&i8253);
116 }
117
118 void
119 i8253init(void)
120 {
121         ioalloc(T0cntr, 4, 0, "i8253");
122         ioalloc(T2ctl, 1, 0, "i8253.cntr2ctl");
123
124         i8253reset();
125 }
126
127 void
128 guesscpuhz(int aalcycles)
129 {
130         int loops, x, y;
131         uvlong a, b, cpufreq;
132
133         ilock(&i8253);
134         for(loops = 1000;;loops += 1000) {
135                 /*
136                  *  measure time for the loop
137                  *
138                  *                      MOVL    loops,CX
139                  *      aaml1:          AAM
140                  *                      LOOP    aaml1
141                  *
142                  *  the time for the loop should be independent of external
143                  *  cache and memory system since it fits in the execution
144                  *  prefetch buffer.
145                  *
146                  */
147                 outb(Tmode, Latch2);
148                 cycles(&a);
149                 x = inb(T2cntr);
150                 x |= inb(T2cntr)<<8;
151                 aamloop(loops);
152                 outb(Tmode, Latch2);
153                 cycles(&b);
154                 y = inb(T2cntr);
155                 y |= inb(T2cntr)<<8;
156
157                 x -= y;
158                 if(x < 0)
159                         x += 0x10000;
160
161                 if(x >= MaxPeriod || loops >= 1000000)
162                         break;
163         }
164         iunlock(&i8253);
165
166         /* avoid division by zero on vmware 7 */
167         if(x == 0)
168                 x = 1;
169
170         /*
171          *  figure out clock frequency and a loop multiplier for delay().
172          *  n.b. counter goes up by 2*Freq
173          */
174         cpufreq = (vlong)loops*((aalcycles*2*Freq)/x);
175         m->loopconst = (cpufreq/1000)/aalcycles;        /* AAM+LOOP's for 1 ms */
176
177         /* a == b means virtualbox has confused us */
178         if(m->havetsc && b > a){
179                 b -= a;
180                 b *= 2*Freq;
181                 b /= x;
182                 m->cyclefreq = b;
183                 cpufreq = b;
184         }
185         m->cpuhz = cpufreq;
186
187         /*
188          *  round to the nearest megahz
189          */
190         m->cpumhz = (cpufreq+500000)/1000000L;
191         if(m->cpumhz == 0)
192                 m->cpumhz = 1;
193 }
194
195 void
196 i8253timerset(uvlong next)
197 {
198         long period;
199         ulong want;
200         ulong now;
201
202         period = MaxPeriod;
203         if(next != 0){
204                 want = next>>Tickshift;
205                 now = i8253.ticks;      /* assuming whomever called us just did fastticks() */
206
207                 period = want - now;
208                 if(period < MinPeriod)
209                         period = MinPeriod;
210                 else if(period > MaxPeriod)
211                         period = MaxPeriod;
212         }
213
214         /* hysteresis */
215         if(i8253.period != period){
216                 ilock(&i8253);
217                 /* load new value */
218                 outb(Tmode, Load0|Square);
219                 outb(T0cntr, period);           /* low byte */
220                 outb(T0cntr, period >> 8);      /* high byte */
221
222                 /* remember period */
223                 i8253.period = period;
224                 i8253.periodset++;
225                 iunlock(&i8253);
226         }
227 }
228
229 static void
230 i8253clock(Ureg* ureg, void*)
231 {
232         timerintr(ureg, 0);
233 }
234
235 void
236 i8253enable(void)
237 {
238         intrenable(IrqCLOCK, i8253clock, 0, BUSUNKNOWN, "clock");
239 }
240
241 /*
242  *  return the total ticks of counter 2.  We shift by
243  *  8 to give timesync more wriggle room for interpretation
244  *  of the frequency
245  */
246 uvlong
247 i8253read(uvlong *hz)
248 {
249         ushort y, x;
250         uvlong ticks;
251
252         if(hz)
253                 *hz = Freq<<Tickshift;
254
255         ilock(&i8253);
256         outb(Tmode, Latch2);
257         y = inb(T2cntr);
258         y |= inb(T2cntr)<<8;
259
260         if(y < i8253.last)
261                 x = i8253.last - y;
262         else {
263                 x = i8253.last + (0x10000 - y);
264                 if (x > 3*MaxPeriod) {
265                         outb(Tmode, Load2|Square);
266                         outb(T2cntr, 0);                /* low byte */
267                         outb(T2cntr, 0);                /* high byte */
268                         y = 0xFFFF;
269                         x = i8253.period;
270                 }
271         }
272         i8253.last = y;
273         i8253.ticks += x>>1;
274         ticks = i8253.ticks;
275         iunlock(&i8253);
276
277         return ticks<<Tickshift;
278 }
279
280 void
281 delay(int millisecs)
282 {
283         millisecs *= m->loopconst;
284         if(millisecs <= 0)
285                 millisecs = 1;
286         aamloop(millisecs);
287 }
288
289 void
290 microdelay(int microsecs)
291 {
292         microsecs *= m->loopconst;
293         microsecs /= 1000;
294         if(microsecs <= 0)
295                 microsecs = 1;
296         aamloop(microsecs);
297 }
298
299 /*  
300  *  performance measurement ticks.  must be low overhead.
301  *  doesn't have to count over a second.
302  */
303 ulong
304 perfticks(void)
305 {
306         uvlong x;
307
308         if(m->havetsc)
309                 cycles(&x);
310         else
311                 x = 0;
312         return x;
313 }