17dd7cddfSDavid du Colombier #include "u.h"
27dd7cddfSDavid du Colombier #include "../port/lib.h"
37dd7cddfSDavid du Colombier #include "mem.h"
47dd7cddfSDavid du Colombier #include "dat.h"
57dd7cddfSDavid du Colombier #include "fns.h"
67dd7cddfSDavid du Colombier #include "io.h"
77dd7cddfSDavid du Colombier
87dd7cddfSDavid du Colombier /*
97dd7cddfSDavid du Colombier * 8253 timer
107dd7cddfSDavid du Colombier */
117dd7cddfSDavid du Colombier enum
127dd7cddfSDavid du Colombier {
137dd7cddfSDavid du Colombier T0cntr= 0x40, /* counter ports */
147dd7cddfSDavid du Colombier T1cntr= 0x41, /* ... */
157dd7cddfSDavid du Colombier T2cntr= 0x42, /* ... */
1659cc4ca5SDavid du Colombier Tmode= 0x43, /* mode port (control word register) */
1759cc4ca5SDavid du Colombier T2ctl= 0x61, /* counter 2 control port */
187dd7cddfSDavid du Colombier
197dd7cddfSDavid du Colombier /* commands */
207dd7cddfSDavid du Colombier Latch0= 0x00, /* latch counter 0's value */
2159cc4ca5SDavid du Colombier Load0l= 0x10, /* load counter 0's lsb */
2259cc4ca5SDavid du Colombier Load0m= 0x20, /* load counter 0's msb */
237dd7cddfSDavid du Colombier Load0= 0x30, /* load counter 0 with 2 bytes */
249a747e4fSDavid du Colombier
259a747e4fSDavid du Colombier Latch1= 0x40, /* latch counter 1's value */
269a747e4fSDavid du Colombier Load1l= 0x50, /* load counter 1's lsb */
279a747e4fSDavid du Colombier Load1m= 0x60, /* load counter 1's msb */
289a747e4fSDavid du Colombier Load1= 0x70, /* load counter 1 with 2 bytes */
299a747e4fSDavid du Colombier
3059cc4ca5SDavid du Colombier Latch2= 0x80, /* latch counter 2's value */
3159cc4ca5SDavid du Colombier Load2l= 0x90, /* load counter 2's lsb */
3259cc4ca5SDavid du Colombier Load2m= 0xa0, /* load counter 2's msb */
3359cc4ca5SDavid du Colombier Load2= 0xb0, /* load counter 2 with 2 bytes */
3459cc4ca5SDavid du Colombier
3559cc4ca5SDavid du Colombier /* 8254 read-back command: everything > pc-at has an 8254 */
3659cc4ca5SDavid du Colombier Rdback= 0xc0, /* readback counters & status */
3759cc4ca5SDavid du Colombier Rdnstat=0x10, /* don't read status */
3859cc4ca5SDavid du Colombier Rdncnt= 0x20, /* don't read counter value */
3959cc4ca5SDavid du Colombier Rd0cntr=0x02, /* read back for which counter */
4059cc4ca5SDavid du Colombier Rd1cntr=0x04,
4159cc4ca5SDavid du Colombier Rd2cntr=0x08,
427dd7cddfSDavid du Colombier
437dd7cddfSDavid du Colombier /* modes */
4459cc4ca5SDavid du Colombier ModeMsk=0xe,
4559cc4ca5SDavid du Colombier Square= 0x6, /* periodic square wave */
4659cc4ca5SDavid du Colombier Trigger=0x0, /* interrupt on terminal count */
4759cc4ca5SDavid du Colombier Sstrobe=0x8, /* software triggered strobe */
4859cc4ca5SDavid du Colombier
499a747e4fSDavid du Colombier /* T2ctl bits */
509a747e4fSDavid du Colombier T2gate= (1<<0), /* enable T2 counting */
519a747e4fSDavid du Colombier T2spkr= (1<<1), /* connect T2 out to speaker */
529a747e4fSDavid du Colombier T2out= (1<<5), /* output of T2 */
537dd7cddfSDavid du Colombier
547dd7cddfSDavid du Colombier Freq= 1193182, /* Real clock frequency */
559a747e4fSDavid du Colombier Tickshift=8, /* extra accuracy */
569a747e4fSDavid du Colombier MaxPeriod=Freq/HZ,
579a747e4fSDavid du Colombier MinPeriod=Freq/(100*HZ),
58*a650be7dSDavid du Colombier
59*a650be7dSDavid du Colombier Wdogms = 200, /* ms between strokes */
607dd7cddfSDavid du Colombier };
617dd7cddfSDavid du Colombier
6259c21d95SDavid du Colombier typedef struct I8253 I8253;
6359c21d95SDavid du Colombier struct I8253
6459cc4ca5SDavid du Colombier {
6559cc4ca5SDavid du Colombier Lock;
669a747e4fSDavid du Colombier ulong period; /* current clock period */
6759cc4ca5SDavid du Colombier int enabled;
689a747e4fSDavid du Colombier uvlong hz;
699a747e4fSDavid du Colombier
709a747e4fSDavid du Colombier ushort last; /* last value of clock 1 */
719a747e4fSDavid du Colombier uvlong ticks; /* cumulative ticks of counter 1 */
72dc5a79c1SDavid du Colombier
73dc5a79c1SDavid du Colombier ulong periodset;
7459c21d95SDavid du Colombier };
7559c21d95SDavid du Colombier I8253 i8253;
7659cc4ca5SDavid du Colombier
779a747e4fSDavid du Colombier void
i8253init(void)789a747e4fSDavid du Colombier i8253init(void)
799a747e4fSDavid du Colombier {
809a747e4fSDavid du Colombier int loops, x;
819a747e4fSDavid du Colombier
827dd7cddfSDavid du Colombier ioalloc(T0cntr, 4, 0, "i8253");
8359cc4ca5SDavid du Colombier ioalloc(T2ctl, 1, 0, "i8253.cntr2ctl");
8459cc4ca5SDavid du Colombier
85ef9eff0bSDavid du Colombier i8253.period = Freq/HZ;
86ef9eff0bSDavid du Colombier
877dd7cddfSDavid du Colombier /*
889a747e4fSDavid du Colombier * enable a 1/HZ interrupt for providing scheduling interrupts
897dd7cddfSDavid du Colombier */
907dd7cddfSDavid du Colombier outb(Tmode, Load0|Square);
917dd7cddfSDavid du Colombier outb(T0cntr, (Freq/HZ)); /* low byte */
927dd7cddfSDavid du Colombier outb(T0cntr, (Freq/HZ)>>8); /* high byte */
939a747e4fSDavid du Colombier
949a747e4fSDavid du Colombier /*
959a747e4fSDavid du Colombier * enable a longer period counter to use as a clock
969a747e4fSDavid du Colombier */
979a747e4fSDavid du Colombier outb(Tmode, Load2|Square);
989a747e4fSDavid du Colombier outb(T2cntr, 0); /* low byte */
999a747e4fSDavid du Colombier outb(T2cntr, 0); /* high byte */
1009a747e4fSDavid du Colombier x = inb(T2ctl);
1019a747e4fSDavid du Colombier x |= T2gate;
1029a747e4fSDavid du Colombier outb(T2ctl, x);
1037dd7cddfSDavid du Colombier
1047dd7cddfSDavid du Colombier /*
1057dd7cddfSDavid du Colombier * Introduce a little delay to make sure the count is
1067dd7cddfSDavid du Colombier * latched and the timer is counting down; with a fast
1077dd7cddfSDavid du Colombier * enough processor this may not be the case.
1087dd7cddfSDavid du Colombier * The i8254 (which this probably is) has a read-back
1097dd7cddfSDavid du Colombier * command which can be used to make sure the counting
1107dd7cddfSDavid du Colombier * register has been written into the counting element.
1117dd7cddfSDavid du Colombier */
1127dd7cddfSDavid du Colombier x = (Freq/HZ);
1137dd7cddfSDavid du Colombier for(loops = 0; loops < 100000 && x >= (Freq/HZ); loops++){
1147dd7cddfSDavid du Colombier outb(Tmode, Latch0);
1157dd7cddfSDavid du Colombier x = inb(T0cntr);
1167dd7cddfSDavid du Colombier x |= inb(T0cntr)<<8;
1177dd7cddfSDavid du Colombier }
1187dd7cddfSDavid du Colombier }
1197dd7cddfSDavid du Colombier
120*a650be7dSDavid du Colombier /*
121*a650be7dSDavid du Colombier * if the watchdog is running and we're on cpu 0 and ignoring (clock)
122*a650be7dSDavid du Colombier * interrupts, disable the watchdog temporarily so that the (presumed)
123*a650be7dSDavid du Colombier * long-running loop to follow will not trigger an NMI.
124*a650be7dSDavid du Colombier * wdogresume restarts the watchdog if wdogpause stopped it.
125*a650be7dSDavid du Colombier */
126*a650be7dSDavid du Colombier static int
wdogpause(void)127*a650be7dSDavid du Colombier wdogpause(void)
128*a650be7dSDavid du Colombier {
129*a650be7dSDavid du Colombier int turndogoff;
130*a650be7dSDavid du Colombier
131*a650be7dSDavid du Colombier turndogoff = watchdogon && m->machno == 0 && !islo();
132*a650be7dSDavid du Colombier if (turndogoff) {
133*a650be7dSDavid du Colombier watchdog->disable();
134*a650be7dSDavid du Colombier watchdogon = 0;
135*a650be7dSDavid du Colombier }
136*a650be7dSDavid du Colombier return turndogoff;
137*a650be7dSDavid du Colombier }
138*a650be7dSDavid du Colombier
139*a650be7dSDavid du Colombier static void
wdogresume(int resume)140*a650be7dSDavid du Colombier wdogresume(int resume)
141*a650be7dSDavid du Colombier {
142*a650be7dSDavid du Colombier if (resume) {
143*a650be7dSDavid du Colombier watchdog->enable();
144*a650be7dSDavid du Colombier watchdogon = 1;
145*a650be7dSDavid du Colombier }
146*a650be7dSDavid du Colombier }
147*a650be7dSDavid du Colombier
1489a747e4fSDavid du Colombier void
guesscpuhz(int aalcycles)1499a747e4fSDavid du Colombier guesscpuhz(int aalcycles)
1509a747e4fSDavid du Colombier {
151*a650be7dSDavid du Colombier int loops, incr, x, y, dogwason;
152a30303efSDavid du Colombier uvlong a, b, cpufreq;
1539a747e4fSDavid du Colombier
154*a650be7dSDavid du Colombier dogwason = wdogpause(); /* don't get NMI while busy looping */
155*a650be7dSDavid du Colombier
1567dd7cddfSDavid du Colombier /* find biggest loop that doesn't wrap */
1577dd7cddfSDavid du Colombier incr = 16000000/(aalcycles*HZ*2);
1587dd7cddfSDavid du Colombier x = 2000;
1597dd7cddfSDavid du Colombier for(loops = incr; loops < 64*1024; loops += incr) {
1607dd7cddfSDavid du Colombier
1617dd7cddfSDavid du Colombier /*
1627dd7cddfSDavid du Colombier * measure time for the loop
1637dd7cddfSDavid du Colombier *
1647dd7cddfSDavid du Colombier * MOVL loops,CX
1657dd7cddfSDavid du Colombier * aaml1: AAM
1667dd7cddfSDavid du Colombier * LOOP aaml1
1677dd7cddfSDavid du Colombier *
1687dd7cddfSDavid du Colombier * the time for the loop should be independent of external
1697dd7cddfSDavid du Colombier * cache and memory system since it fits in the execution
1707dd7cddfSDavid du Colombier * prefetch buffer.
1717dd7cddfSDavid du Colombier *
1727dd7cddfSDavid du Colombier */
1737dd7cddfSDavid du Colombier outb(Tmode, Latch0);
174e288d156SDavid du Colombier cycles(&a);
1757dd7cddfSDavid du Colombier x = inb(T0cntr);
1767dd7cddfSDavid du Colombier x |= inb(T0cntr)<<8;
1777dd7cddfSDavid du Colombier aamloop(loops);
1787dd7cddfSDavid du Colombier outb(Tmode, Latch0);
179e288d156SDavid du Colombier cycles(&b);
1807dd7cddfSDavid du Colombier y = inb(T0cntr);
1817dd7cddfSDavid du Colombier y |= inb(T0cntr)<<8;
1827dd7cddfSDavid du Colombier x -= y;
1837dd7cddfSDavid du Colombier
1847dd7cddfSDavid du Colombier if(x < 0)
1857dd7cddfSDavid du Colombier x += Freq/HZ;
1867dd7cddfSDavid du Colombier
1877dd7cddfSDavid du Colombier if(x > Freq/(3*HZ))
1887dd7cddfSDavid du Colombier break;
1897dd7cddfSDavid du Colombier }
190*a650be7dSDavid du Colombier wdogresume(dogwason);
1917dd7cddfSDavid du Colombier
1927dd7cddfSDavid du Colombier /*
1937dd7cddfSDavid du Colombier * figure out clock frequency and a loop multiplier for delay().
1947dd7cddfSDavid du Colombier * n.b. counter goes up by 2*Freq
1957dd7cddfSDavid du Colombier */
1965e9f80a7SDavid du Colombier if(x == 0)
1975e9f80a7SDavid du Colombier x = 1; /* avoid division by zero on vmware 7 */
198a30303efSDavid du Colombier cpufreq = (vlong)loops*((aalcycles*2*Freq)/x);
1997dd7cddfSDavid du Colombier m->loopconst = (cpufreq/1000)/aalcycles; /* AAM+LOOP's for 1 ms */
2007dd7cddfSDavid du Colombier
20100a4193cSDavid du Colombier if(m->havetsc && a != b){ /* a == b means virtualbox has confused us */
2027dd7cddfSDavid du Colombier /* counter goes up by 2*Freq */
2037dd7cddfSDavid du Colombier b = (b-a)<<1;
2047dd7cddfSDavid du Colombier b *= Freq;
2057dd7cddfSDavid du Colombier b /= x;
2067dd7cddfSDavid du Colombier
2077dd7cddfSDavid du Colombier /*
2087dd7cddfSDavid du Colombier * round to the nearest megahz
2097dd7cddfSDavid du Colombier */
2107dd7cddfSDavid du Colombier m->cpumhz = (b+500000)/1000000L;
2117dd7cddfSDavid du Colombier m->cpuhz = b;
212e288d156SDavid du Colombier m->cyclefreq = b;
2137dd7cddfSDavid du Colombier } else {
2147dd7cddfSDavid du Colombier /*
2157dd7cddfSDavid du Colombier * add in possible 0.5% error and convert to MHz
2167dd7cddfSDavid du Colombier */
2177dd7cddfSDavid du Colombier m->cpumhz = (cpufreq + cpufreq/200)/1000000;
2187dd7cddfSDavid du Colombier m->cpuhz = cpufreq;
2197dd7cddfSDavid du Colombier }
2209a747e4fSDavid du Colombier
22100a4193cSDavid du Colombier /* don't divide by zero in trap.c */
22200a4193cSDavid du Colombier if (m->cpumhz == 0)
22300a4193cSDavid du Colombier panic("guesscpuhz: zero m->cpumhz");
2249a747e4fSDavid du Colombier i8253.hz = Freq<<Tickshift;
2257dd7cddfSDavid du Colombier }
2267dd7cddfSDavid du Colombier
22759cc4ca5SDavid du Colombier void
i8253timerset(uvlong next)2289a747e4fSDavid du Colombier i8253timerset(uvlong next)
22959cc4ca5SDavid du Colombier {
230ef9eff0bSDavid du Colombier long period;
2319a747e4fSDavid du Colombier ulong want;
2329a747e4fSDavid du Colombier ulong now;
23359cc4ca5SDavid du Colombier
234dc5a79c1SDavid du Colombier period = MaxPeriod;
235dc5a79c1SDavid du Colombier if(next != 0){
2369a747e4fSDavid du Colombier want = next>>Tickshift;
2379a747e4fSDavid du Colombier now = i8253.ticks; /* assuming whomever called us just did fastticks() */
2389a747e4fSDavid du Colombier
2399a747e4fSDavid du Colombier period = want - now;
2409a747e4fSDavid du Colombier if(period < MinPeriod)
2419a747e4fSDavid du Colombier period = MinPeriod;
2420b9a5132SDavid du Colombier else if(period > MaxPeriod)
2439a747e4fSDavid du Colombier period = MaxPeriod;
24459cc4ca5SDavid du Colombier }
2459a747e4fSDavid du Colombier
2463ff48bf5SDavid du Colombier /* hysteresis */
2479a747e4fSDavid du Colombier if(i8253.period != period){
2489a747e4fSDavid du Colombier ilock(&i8253);
2499a747e4fSDavid du Colombier /* load new value */
2509a747e4fSDavid du Colombier outb(Tmode, Load0|Square);
2519a747e4fSDavid du Colombier outb(T0cntr, period); /* low byte */
2529a747e4fSDavid du Colombier outb(T0cntr, period >> 8); /* high byte */
2539a747e4fSDavid du Colombier
2549a747e4fSDavid du Colombier /* remember period */
2559a747e4fSDavid du Colombier i8253.period = period;
256dc5a79c1SDavid du Colombier i8253.periodset++;
25759cc4ca5SDavid du Colombier iunlock(&i8253);
25859cc4ca5SDavid du Colombier }
2599a747e4fSDavid du Colombier }
26059cc4ca5SDavid du Colombier
26159cc4ca5SDavid du Colombier static void
i8253clock(Ureg * ureg,void *)2629a747e4fSDavid du Colombier i8253clock(Ureg* ureg, void*)
26359cc4ca5SDavid du Colombier {
2649a747e4fSDavid du Colombier timerintr(ureg, 0);
26559cc4ca5SDavid du Colombier }
26659cc4ca5SDavid du Colombier
2677dd7cddfSDavid du Colombier void
i8253enable(void)2687dd7cddfSDavid du Colombier i8253enable(void)
2697dd7cddfSDavid du Colombier {
27059cc4ca5SDavid du Colombier i8253.enabled = 1;
2719a747e4fSDavid du Colombier i8253.period = Freq/HZ;
2729a747e4fSDavid du Colombier intrenable(IrqCLOCK, i8253clock, 0, BUSUNKNOWN, "clock");
2737dd7cddfSDavid du Colombier }
2747dd7cddfSDavid du Colombier
2753ff48bf5SDavid du Colombier void
i8253link(void)2763ff48bf5SDavid du Colombier i8253link(void)
2773ff48bf5SDavid du Colombier {
2783ff48bf5SDavid du Colombier }
2793ff48bf5SDavid du Colombier
2807dd7cddfSDavid du Colombier /*
281c3c4501eSDavid du Colombier * return the total ticks of counter 2. We shift by
2829a747e4fSDavid du Colombier * 8 to give timesync more wriggle room for interpretation
2839a747e4fSDavid du Colombier * of the frequency
2847dd7cddfSDavid du Colombier */
2857dd7cddfSDavid du Colombier uvlong
i8253read(uvlong * hz)2867dd7cddfSDavid du Colombier i8253read(uvlong *hz)
2877dd7cddfSDavid du Colombier {
2889a747e4fSDavid du Colombier ushort y, x;
2899a747e4fSDavid du Colombier uvlong ticks;
2909a747e4fSDavid du Colombier
2917dd7cddfSDavid du Colombier if(hz)
2929a747e4fSDavid du Colombier *hz = i8253.hz;
2939a747e4fSDavid du Colombier
2949a747e4fSDavid du Colombier ilock(&i8253);
2959a747e4fSDavid du Colombier outb(Tmode, Latch2);
2969a747e4fSDavid du Colombier y = inb(T2cntr);
2979a747e4fSDavid du Colombier y |= inb(T2cntr)<<8;
2989a747e4fSDavid du Colombier
2999a747e4fSDavid du Colombier if(y < i8253.last)
3009a747e4fSDavid du Colombier x = i8253.last - y;
301c3c4501eSDavid du Colombier else {
3029a747e4fSDavid du Colombier x = i8253.last + (0x10000 - y);
303c3c4501eSDavid du Colombier if (x > 3*MaxPeriod) {
304c3c4501eSDavid du Colombier outb(Tmode, Load2|Square);
305c3c4501eSDavid du Colombier outb(T2cntr, 0); /* low byte */
306c3c4501eSDavid du Colombier outb(T2cntr, 0); /* high byte */
307c3c4501eSDavid du Colombier y = 0xFFFF;
308c3c4501eSDavid du Colombier x = i8253.period;
309c3c4501eSDavid du Colombier }
310c3c4501eSDavid du Colombier }
3119a747e4fSDavid du Colombier i8253.last = y;
3129a747e4fSDavid du Colombier i8253.ticks += x>>1;
3139a747e4fSDavid du Colombier ticks = i8253.ticks;
3149a747e4fSDavid du Colombier iunlock(&i8253);
3159a747e4fSDavid du Colombier
3169a747e4fSDavid du Colombier return ticks<<Tickshift;
3179a747e4fSDavid du Colombier }
3189a747e4fSDavid du Colombier
3199a747e4fSDavid du Colombier void
delay(int millisecs)3209a747e4fSDavid du Colombier delay(int millisecs)
3219a747e4fSDavid du Colombier {
322*a650be7dSDavid du Colombier if (millisecs > 10*1000)
323*a650be7dSDavid du Colombier iprint("delay(%d) from %#p\n", millisecs,
324*a650be7dSDavid du Colombier getcallerpc(&millisecs));
325*a650be7dSDavid du Colombier if (watchdogon && m->machno == 0 && !islo())
326*a650be7dSDavid du Colombier for (; millisecs > Wdogms; millisecs -= Wdogms) {
327*a650be7dSDavid du Colombier delay(Wdogms);
328*a650be7dSDavid du Colombier watchdog->restart();
329*a650be7dSDavid du Colombier }
3309a747e4fSDavid du Colombier millisecs *= m->loopconst;
3319a747e4fSDavid du Colombier if(millisecs <= 0)
3329a747e4fSDavid du Colombier millisecs = 1;
3339a747e4fSDavid du Colombier aamloop(millisecs);
3349a747e4fSDavid du Colombier }
3359a747e4fSDavid du Colombier
3369a747e4fSDavid du Colombier void
microdelay(int microsecs)3379a747e4fSDavid du Colombier microdelay(int microsecs)
3389a747e4fSDavid du Colombier {
339*a650be7dSDavid du Colombier if (watchdogon && m->machno == 0 && !islo())
340*a650be7dSDavid du Colombier for (; microsecs > Wdogms*1000; microsecs -= Wdogms*1000) {
341*a650be7dSDavid du Colombier delay(Wdogms);
342*a650be7dSDavid du Colombier watchdog->restart();
343*a650be7dSDavid du Colombier }
3449a747e4fSDavid du Colombier microsecs *= m->loopconst;
3459a747e4fSDavid du Colombier microsecs /= 1000;
3469a747e4fSDavid du Colombier if(microsecs <= 0)
3479a747e4fSDavid du Colombier microsecs = 1;
3489a747e4fSDavid du Colombier aamloop(microsecs);
3499a747e4fSDavid du Colombier }
3509a747e4fSDavid du Colombier
3513ff48bf5SDavid du Colombier /*
3523ff48bf5SDavid du Colombier * performance measurement ticks. must be low overhead.
3533ff48bf5SDavid du Colombier * doesn't have to count over a second.
3543ff48bf5SDavid du Colombier */
3559a747e4fSDavid du Colombier ulong
perfticks(void)3563ff48bf5SDavid du Colombier perfticks(void)
3579a747e4fSDavid du Colombier {
3583ff48bf5SDavid du Colombier uvlong x;
3599a747e4fSDavid du Colombier
360da51d93aSDavid du Colombier if(m->havetsc)
361e288d156SDavid du Colombier cycles(&x);
362da51d93aSDavid du Colombier else
363da51d93aSDavid du Colombier x = 0;
3643ff48bf5SDavid du Colombier return x;
3657dd7cddfSDavid du Colombier }
366