]> git.lizzy.rs Git - plan9front.git/blob - sys/src/9/pc/i8253.c
kernel: cleanup the software mouse cursor mess
[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         if(m->machno != 0){
134                 m->cpuhz = MACHP(0)->cpuhz;
135                 m->cpumhz = MACHP(0)->cpumhz;
136                 m->cyclefreq = MACHP(0)->cyclefreq;
137                 m->loopconst = MACHP(0)->loopconst;
138                 return;
139         }
140
141         ilock(&i8253);
142         for(loops = 1000;;loops += 1000) {
143                 /*
144                  *  measure time for the loop
145                  *
146                  *                      MOVL    loops,CX
147                  *      aaml1:          AAM
148                  *                      LOOP    aaml1
149                  *
150                  *  the time for the loop should be independent of external
151                  *  cache and memory system since it fits in the execution
152                  *  prefetch buffer.
153                  *
154                  */
155                 outb(Tmode, Latch2);
156                 cycles(&a);
157                 x = inb(T2cntr);
158                 x |= inb(T2cntr)<<8;
159                 aamloop(loops);
160                 outb(Tmode, Latch2);
161                 cycles(&b);
162                 y = inb(T2cntr);
163                 y |= inb(T2cntr)<<8;
164
165                 x -= y;
166                 if(x < 0)
167                         x += 0x10000;
168
169                 if(x >= MaxPeriod || loops >= 1000000)
170                         break;
171         }
172         iunlock(&i8253);
173
174         /* avoid division by zero on vmware 7 */
175         if(x == 0)
176                 x = 1;
177
178         /*
179          *  figure out clock frequency and a loop multiplier for delay().
180          *  n.b. counter goes up by 2*Freq
181          */
182         cpufreq = (vlong)loops*((aalcycles*2*Freq)/x);
183         m->loopconst = (cpufreq/1000)/aalcycles;        /* AAM+LOOP's for 1 ms */
184
185         /* a == b means virtualbox has confused us */
186         if(m->havetsc && b > a){
187                 b -= a;
188                 b *= 2*Freq;
189                 b /= x;
190                 m->cyclefreq = b;
191                 cpufreq = b;
192         }
193         m->cpuhz = cpufreq;
194
195         /*
196          *  round to the nearest megahz
197          */
198         m->cpumhz = (cpufreq+500000)/1000000L;
199         if(m->cpumhz == 0)
200                 m->cpumhz = 1;
201 }
202
203 void
204 i8253timerset(uvlong next)
205 {
206         long period;
207         ulong want;
208         ulong now;
209
210         want = next>>Tickshift;
211         now = i8253.ticks;      /* assuming whomever called us just did fastticks() */
212
213         period = want - now;
214         if(period < MinPeriod)
215                 period = MinPeriod;
216         else if(period > MaxPeriod)
217                 period = MaxPeriod;
218
219         /* hysteresis */
220         if(i8253.period != period){
221                 ilock(&i8253);
222                 /* load new value */
223                 outb(Tmode, Load0|Square);
224                 outb(T0cntr, period);           /* low byte */
225                 outb(T0cntr, period >> 8);      /* high byte */
226
227                 /* remember period */
228                 i8253.period = period;
229                 i8253.periodset++;
230                 iunlock(&i8253);
231         }
232 }
233
234 static void
235 i8253clock(Ureg* ureg, void*)
236 {
237         timerintr(ureg, 0);
238 }
239
240 void
241 i8253enable(void)
242 {
243         intrenable(IrqCLOCK, i8253clock, 0, BUSUNKNOWN, "clock");
244 }
245
246 /*
247  *  return the total ticks of counter 2.  We shift by
248  *  8 to give timesync more wriggle room for interpretation
249  *  of the frequency
250  */
251 uvlong
252 i8253read(uvlong *hz)
253 {
254         ushort y, x;
255         uvlong ticks;
256
257         if(hz)
258                 *hz = Freq<<Tickshift;
259
260         ilock(&i8253);
261         outb(Tmode, Latch2);
262         y = inb(T2cntr);
263         y |= inb(T2cntr)<<8;
264
265         if(y < i8253.last)
266                 x = i8253.last - y;
267         else {
268                 x = i8253.last + (0x10000 - y);
269                 if (x > 3*MaxPeriod) {
270                         outb(Tmode, Load2|Square);
271                         outb(T2cntr, 0);                /* low byte */
272                         outb(T2cntr, 0);                /* high byte */
273                         y = 0xFFFF;
274                         x = i8253.period;
275                 }
276         }
277         i8253.last = y;
278         i8253.ticks += x>>1;
279         ticks = i8253.ticks;
280         iunlock(&i8253);
281
282         return ticks<<Tickshift;
283 }
284
285 void
286 delay(int millisecs)
287 {
288         millisecs *= m->loopconst;
289         if(millisecs <= 0)
290                 millisecs = 1;
291         aamloop(millisecs);
292 }
293
294 void
295 microdelay(int microsecs)
296 {
297         microsecs *= m->loopconst;
298         microsecs /= 1000;
299         if(microsecs <= 0)
300                 microsecs = 1;
301         aamloop(microsecs);
302 }
303
304 /*  
305  *  performance measurement ticks.  must be low overhead.
306  *  doesn't have to count over a second.
307  */
308 ulong
309 perfticks(void)
310 {
311         uvlong x;
312
313         if(m->havetsc)
314                 cycles(&x);
315         else
316                 x = 0;
317         return x;
318 }