## diffname pc/i8253.c 1997/0327 ## diff -e /dev/null /n/emeliedump/1997/0327/sys/src/brazil/pc/i8253.c 0a #include "u.h" #include "../port/lib.h" #include "mem.h" #include "dat.h" #include "fns.h" #include "io.h" /* * 8253 timer */ enum { T0cntr= 0x40, /* counter ports */ T1cntr= 0x41, /* ... */ T2cntr= 0x42, /* ... */ Tmode= 0x43, /* mode port */ /* commands */ Latch0= 0x00, /* latch counter 0's value */ Load0= 0x30, /* load counter 0 with 2 bytes */ /* modes */ Square= 0x36, /* periodic square wave */ Trigger= 0x30, /* interrupt on terminal count */ Freq= 1193182, /* Real clock frequency */ }; void i8253init(int aalcycles) { int cpufreq, loops, incr, x, y; static int initialised; if(initialised == 0){ initialised = 1; /* * set clock for 1/HZ seconds */ outb(Tmode, Load0|Square); outb(T0cntr, (Freq/HZ)); /* low byte */ outb(T0cntr, (Freq/HZ)>>8); /* high byte */ } /* find biggest loop that doesn't wrap */ incr = 16000000/(aalcycles*HZ*2); x = 2000; for(loops = incr; loops < 64*1024; loops += incr) { /* * measure time for the loop * * MOVL loops,CX * aaml1: AAM * LOOP aaml1 * * the time for the loop should be independent of external * cache and memory system since it fits in the execution * prefetch buffer. * */ outb(Tmode, Latch0); x = inb(T0cntr); x |= inb(T0cntr)<<8; aamloop(loops); outb(Tmode, Latch0); y = inb(T0cntr); y |= inb(T0cntr)<<8; x -= y; if(x < 0) x += Freq/HZ; if(x > Freq/(3*HZ)) break; } /* * counter goes at twice the frequency, once per transition, * i.e., twice per square wave */ x >>= 1; /* * figure out clock frequency and a loop multiplier for delay(). */ cpufreq = loops*((aalcycles*Freq)/x); m->loopconst = (cpufreq/1000)/aalcycles; /* AAM+LOOP's for 1 ms */ /* * add in possible 0.5% error and convert to MHz */ m->cpumhz = (cpufreq + cpufreq/200)/1000000; } void i8253enable(void) { intrenable(VectorCLOCK, clockintr, 0, BUSUNKNOWN); } . ## diffname pc/i8253.c 1998/0710 ## diff -e /n/emeliedump/1997/0327/sys/src/brazil/pc/i8253.c /n/emeliedump/1998/0710/sys/src/brazil/pc/i8253.c 100a Lock i8253lock; /* * return time elapsed since clock start in * 10ths of nanoseconds */ uvlong i8253read(uvlong *hz) { if(hz) *hz = HZ; return m->ticks; } . ## diffname pc/i8253.c 1998/0819 ## diff -e /n/emeliedump/1998/0710/sys/src/brazil/pc/i8253.c /n/emeliedump/1998/0819/sys/src/brazil/pc/i8253.c 102,103d ## diffname pc/i8253.c 1998/0903 ## diff -e /n/emeliedump/1998/0819/sys/src/brazil/pc/i8253.c /n/emeliedump/1998/0903/sys/src/brazil/pc/i8253.c 42a /* * Introduce a little delay to make sure the count is * latched and the timer is counting down; with a fast * enough processor this may not be the case. * The i8254 (which this probably is) has a read-back * command which can be used to make sure the counting * register has been written into the counting element. */ x = (Freq/HZ); for(loops = 0; loops < 100000 && x >= (Freq/HZ); loops++){ outb(Tmode, Latch0); x = inb(T0cntr); x |= inb(T0cntr)<<8; } . ## diffname pc/i8253.c 1998/0910 ## diff -e /n/emeliedump/1998/0903/sys/src/brazil/pc/i8253.c /n/emeliedump/1998/0910/sys/src/brazil/pc/i8253.c 114c intrenable(IrqCLOCK, clockintr, 0, BUSUNKNOWN); . ## diffname pc/i8253.c 1999/0131 ## diff -e /n/emeliedump/1998/0910/sys/src/brazil/pc/i8253.c /n/emeliedump/1999/0131/sys/src/brazil/pc/i8253.c 105,108c if(havecycleclock){ /* counter goes up by 2*Freq */ b = (b-a)<<1; b *= Freq; b /= x; /* * round to the nearest megahz */ m->cpumhz = (b+500000)/1000000L; m->cpuhz = b; } else { /* * add in possible 0.5% error and convert to MHz */ m->cpumhz = (cpufreq + cpufreq/200)/1000000; m->cpuhz = cpufreq; } . 102c cpufreq = loops*((aalcycles*2*Freq)/x); . 100a * n.b. counter goes up by 2*Freq . 94,99d 81a if(havecycleclock) rdmsr(0x10, &b); . 77a if(havecycleclock) rdmsr(0x10, &a); . 32a vlong a, b; . 30c i8253init(int aalcycles, int havecycleclock) . ## diffname pc/i8253.c 1999/0228 ## diff -e /n/emeliedump/1999/0131/sys/src/brazil/pc/i8253.c /n/emeliedump/1999/0228/sys/src/brazil/pc/i8253.c 140,141c *hz = HZ*100; return m->ticks*100; . 134c * 100 times hz . ## diffname pc/i8253.c 1999/0714 ## diff -e /n/emeliedump/1999/0228/sys/src/brazil/pc/i8253.c /n/emeliedump/1999/0714/sys/src/brazil/pc/i8253.c 37a ioalloc(T0cntr, 4, 0, "i8253"); . ## diffname pc/i8253.c 1999/0819 ## diff -e /n/emeliedump/1999/0714/sys/src/brazil/pc/i8253.c /n/emeliedump/1999/0819/sys/src/brazil/pc/i8253.c 130c intrenable(IrqCLOCK, clockintr, 0, BUSUNKNOWN, "clock"); . ## diffname pc/i8253.c 2000/0621 ## diff -e /n/emeliedump/1999/0819/sys/src/brazil/pc/i8253.c /n/emeliedump/2000/0621/sys/src/9/pc/i8253.c 130c intrenable(IrqCLOCK, clockintr0, 0, BUSUNKNOWN, "clock"); . 126a static void clockintr0(Ureg* ureg, void *v) { loopbackintr(); clockintr(ureg, v); } . ## diffname pc/i8253.c 2000/0622 ## diff -e /n/emeliedump/2000/0621/sys/src/9/pc/i8253.c /n/emeliedump/2000/0622/sys/src/9/pc/i8253.c 130c loopbackintr(ureg); . 126a int i8253readcnt(void) { ilock(&i8253lock); iunlock(&i8253lock); } . 28a static Lock i8253lock; . 23,24c Square= 0x6, /* periodic square wave */ Trigger= 0x0, /* interrupt on terminal count */ . 19a Load0l= 0x10, /* load counter 0's lsb */ Load0m= 0x20, /* load counter 0's msb */ . 16c Tmode= 0x43, /* mode port (control word register) */ . ## diffname pc/i8253.c 2000/0623 ## diff -e /n/emeliedump/2000/0622/sys/src/9/pc/i8253.c /n/emeliedump/2000/0623/sys/src/9/pc/i8253.c 147a i8253.when = fastticks(nil); i8253.fastperiod = (m->cpuhz + HZ/2) / HZ; i8253.fast2freq = (vlong)m->cpuhz * FreqMul / Freq; . 141c vlong now; long set; now = fastticks(nil); while(i8253.when < now) i8253.when += i8253.fastperiod; set = (long)(i8253.when - now) * FreqMul / i8253.fast2freq; set -= 3; /* three cycles for the count to take effect: outb, outb, wait */ lastfast = now; outb(T0cntr, set); /* low byte */ outb(T0cntr, set>>8); /* high byte */ checkcycintr(ureg, v); . 134,135c int v; ilock(&i8253.lock); if(cntr == 2){ outb(Tmode, Rdback|Rd2cntr); v = inb(T2cntr) << 16; v |= inb(T2cntr); v |= inb(T2cntr) << 8; }else if(cntr == 0){ outb(Tmode, Rdback|Rd0cntr); v = inb(T0cntr) << 16; v |= inb(T0cntr); v |= inb(T0cntr) << 8; }else if(cntr == 3){ vlong nf = fastticks(nil); long set; set = (long)(nf - lastfast) * 100 / (long)((vlong)m->cpuhz * 100 / Freq); set = (Freq/HZ) - set; set -= 3 - 1; /* outb, outb, wait - outb(mode) */ outb(Tmode, Rdback|Rdnstat|Rd0cntr); v = inb(T0cntr); v |= inb(T0cntr) << 8; v = set - v; }else if(cntr == 4){ vlong nf = fastticks(nil); long set; set = (long)(nf - lastfast) * 16 / (long)((vlong)m->cpuhz * 16 / Freq); set = (Freq/HZ) - set; set -= 3 - 1; /* outb, outb, wait - outb(mode) */ outb(Tmode, Rdback|Rdnstat|Rd0cntr); v = inb(T0cntr); v |= inb(T0cntr) << 8; v = set - v; }else{ vlong nf = fastticks(nil); long set; set = (nf - lastfast) * Freq / m->cpuhz; set = (Freq/HZ) - set; set -= 3 - 1; /* outb, outb, wait - outb(mode) */ outb(Tmode, Rdback|Rdnstat|Rd0cntr); v = inb(T0cntr); v |= inb(T0cntr) << 8; v = set - v; } iunlock(&i8253.lock); return v; . 132c i8253readcnt(int cntr) . 130a static vlong lastfast; . 128a outb(Tmode, Load0|Trigger); outb(T0cntr, (Freq/HZ)); /* low byte */ outb(T0cntr, (Freq/HZ)>>8); /* high byte */ . 42a ioalloc(T2ctl, 1, 0, "i8253.cntr2ctl"); . 31c static struct { Lock lock; vlong when; /* next fastticks a clock interrupt should occur */ long fastperiod; /* fastticks/hz */ long fast2freq; /* fastticks*FreqMul/Freq */ }i8253; . 28a FreqMul=16, /* extra accuracy in fastticks/Freq calculation; ok up to ~8ghz */ . 27a /* counter 2 controls */ C2gate= 0x1, C2speak=0x2, C2out= 0x10, . 26c Trigger=0x0, /* interrupt on terminal count */ Sstrobe=0x8, /* software triggered strobe */ . 24a ModeMsk=0xe, . 23a /* 8254 read-back command: everything > pc-at has an 8254 */ Rdback= 0xc0, /* readback counters & status */ Rdnstat=0x10, /* don't read status */ Rdncnt= 0x20, /* don't read counter value */ Rd0cntr=0x02, /* read back for which counter */ Rd1cntr=0x04, Rd2cntr=0x08, . 22a Latch2= 0x80, /* latch counter 2's value */ Load2l= 0x90, /* load counter 2's lsb */ Load2m= 0xa0, /* load counter 2's msb */ Load2= 0xb0, /* load counter 2 with 2 bytes */ . 16a T2ctl= 0x61, /* counter 2 control port */ . ## diffname pc/i8253.c 2000/0624 ## diff -e /n/emeliedump/2000/0623/sys/src/9/pc/i8253.c /n/emeliedump/2000/0624/sys/src/9/pc/i8253.c 234a #endif . 232d 223a #ifdef SETNEXT . 167,220d 165c #ifdef SETNEXT set up clock so it is reloaded every interrupt outb(Tmode, Load0|Trigger); outb(T0cntr, (Freq/HZ)); /* low byte */ outb(T0cntr, (Freq/HZ)>>8); /* high byte */ #endif . 159,162d ## diffname pc/i8253.c 2000/0627 ## diff -e /n/emeliedump/2000/0624/sys/src/9/pc/i8253.c /n/emeliedump/2000/0627/sys/src/9/pc/i8253.c 184,185c void clockintrsched(void) { vlong next; ilock(&i8253); next = cycintrnext(); if(next != i8253.cycwhen) clockintrsched0(next); iunlock(&i8253); } static void clockintr0(Ureg* ureg, void *v) { vlong now, when; now = fastticks(nil); when = i8253.when; if(now >= i8253.when){ clockintr(ureg, v); do i8253.when += i8253.fastperiod; while(i8253.when < now); } if(i8253.cycwhen || i8253.mode != Square) clockintrsched0(checkcycintr(ureg, v)); . 176,182c if(next || now < i8253.when - i8253.fastperiod || now > i8253.when - ((3*i8253.fastperiod)>>2)){ if(next > i8253.when) next = i8253.when; set = (long)(next - now) * FreqMul / i8253.fast2freq; set -= 3; /* three cycles for the count to take effect: outb, outb, wait */ if(i8253.mode != Trigger){ outb(Tmode, Load0|Trigger); i8253.mode = Trigger; set--; } if(set < 3) set = 5; outb(T0cntr, set); /* low byte */ outb(T0cntr, set>>8); /* high byte */ }else if(i8253.mode != Square){ i8253.mode = Square; outb(Tmode, Load0|Square); outb(T0cntr, (Freq/HZ)); /* low byte */ outb(T0cntr, (Freq/HZ)>>8); /* high byte */ } } . 174a i8253.cycwhen = next; if(i8253.mode == Square && next == 0) return; . 171d 169c clockintrsched0(vlong next) . 161,167c /* * schedule the next interrupt * cycwhen is the next time for the cycle counter routines */ . 76a i8253.mode = Square; . 56a vlong cycwhen; /* next fastticks a cycintr happens; 0 == infinity */ . 55c Lock; int mode; . ## diffname pc/i8253.c 2000/0701 ## diff -e /n/emeliedump/2000/0627/sys/src/9/pc/i8253.c /n/emeliedump/2000/0701/sys/src/9/pc/i8253.c 235a i8253.enabled = 1; . 207,209c if(i8253.enabled){ next = cycintrnext(); if(next != i8253.cycwhen) clockintrsched0(next); } . 200a int havecycintr(void) { return i8253.enabled; } . 55a int enabled; . ## diffname pc/i8253.c 2000/0706 ## diff -e /n/emeliedump/2000/0701/sys/src/9/pc/i8253.c /n/emeliedump/2000/0706/sys/src/9/pc/i8253.c 190,191c if(set < Minusec) set = Minusec; . 49a Minusec=20, /* minimum cycles for a clock interrupt */ . ## diffname pc/i8253.c 2000/0713 ## diff -e /n/emeliedump/2000/0706/sys/src/9/pc/i8253.c /n/emeliedump/2000/0713/sys/src/9/pc/i8253.c 227a if(!i8253.havecyc){ clockintr(ureg, v); return; } . 216c if(i8253.enabled && i8253.havecyc){ . 207c return i8253.havecyc && i8253.enabled; . 74a i8253.havecyc = havecycleclock; . 57a int havecyc; . ## diffname pc/i8253.c 2001/0611 ## diff -e /n/emeliedump/2000/0713/sys/src/9/pc/i8253.c /n/emeliedump/2001/0611/sys/src/9/pc/i8253.c 237a /* * If we suspend the machine and resume, the * processor cycle counter gets reset. Detect this * and deal with it. */ if(now < i8253.when && i8253.when-now > 3*i8253.fastperiod) i8253.when = now; . ## diffname pc/i8253.c 2001/1130 ## diff -e /n/emeliedump/2001/0611/sys/src/9/pc/i8253.c /n/emeliedump/2001/1130/sys/src/9/pc/i8253.c 129c rdtsc(&b); . 123c rdtsc(&a); . ## diffname pc/i8253.c 2002/0221 ## diff -e /n/emeliedump/2001/1130/sys/src/9/pc/i8253.c /n/emeliedump/2002/0221/sys/src/9/pc/i8253.c 262c i8253.fast2freq = m->cpuhz * FreqMul / Freq; . ## diffname pc/i8253.c 2002/0314 ## diff -e /n/emeliedump/2002/0221/sys/src/9/pc/i8253.c /n/emeliedump/2002/0314/sys/src/9/pc/i8253.c 262c i8253.fast2freq = (vlong)m->cpuhz * FreqMul / Freq; . ## diffname pc/i8253.c 2002/0404 ## diff -e /n/emeliedump/2002/0314/sys/src/9/pc/i8253.c /n/emeliedump/2002/0404/sys/src/9/pc/i8253.c 147d 140d 133c . 127d 123a outb(Tmode, Latch0); . 121d ## diffname pc/i8253.c 2002/0405 ## diff -e /n/emeliedump/2002/0404/sys/src/9/pc/i8253.c /n/emeliedump/2002/0405/sys/src/9/pc/i8253.c 272,273c *hz = Freq<<8; ilock(&i8253.lock1); outb(Tmode, Latch1); y = inb(T1cntr); y |= inb(T1cntr)<<8; if(y < i8253.last1) x = i8253.last1 - y; else x = i8253.last1 + (0xfffe - y); i8253.last1 = y; i8253.ticks1 += x>>1; ticks = i8253.ticks1; iunlock(&i8253.lock1); return ticks<<8; } /* * return value and speed of timer set in arch->clockenable */ vlong fastticks(uvlong *hz) { return (*arch->fastclock)(hz); . 270a ushort y, x; uvlong ticks; . 265,266c * return the total ticks of counter 1. We shift by * 8 to give timesync more wriggle room for interpretation * of the frequency . 257,259c uvlong hz; i8253.when = fastticks(&hz); i8253.fastperiod = (hz + HZ/2) / HZ; i8253.fast2freq = (hz * FreqMul) / Freq; . 250,251c if(i8253.timerwhen || i8253.mode != Square) clockintrsched0(checktimer(ureg, v)); . 236,243d 228,231c nclockintr0++; /* goes with syncing in squidboy */ if(m->havetsc) rdtsc(&m->lasttsc); . 222a int nclockintr0; . 215,217c if(i8253.enabled){ next = timernext(); if(next != i8253.timerwhen) . 206c return i8253.enabled; . 204c havetimer(void) . 176c i8253.timerwhen = next; . 168c * timerwhen is the time of the next interrupt . 146a if(m->havetsc){ . 145d 138a . 132c . 127c outb(Tmode, Latch0); if(m->havetsc) . 123a if(m->havetsc) rdtsc(&a); . 121,122d 103a void guesscpuhz(int aalcycles) { int cpufreq, loops, incr, x, y; vlong a, b; . 102a } . 88,101c /* * set time clock */ outb(Tmode, Load1|Square); i8253.mode = Square; outb(T1cntr, 0xfe); /* low byte */ outb(T1cntr, 0xff); /* high byte */ /* * Introduce a little delay to make sure the count is * latched and the timer is counting down; with a fast * enough processor this may not be the case. * The i8254 (which this probably is) has a read-back * command which can be used to make sure the counting * register has been written into the counting element. */ x = (Freq/HZ); for(loops = 0; loops < 100000 && x >= (Freq/HZ); loops++){ outb(Tmode, Latch0); x = inb(T0cntr); x |= inb(T0cntr)<<8; . 80,86c /* * set interrupting clock for 1/HZ seconds */ outb(Tmode, Load0|Square); i8253.mode = Square; outb(T0cntr, (Freq/HZ)); /* low byte */ outb(T0cntr, (Freq/HZ)>>8); /* high byte */ . 74,78c ioalloc(T0cntr, 4, 0, "i8253"); ioalloc(T2ctl, 1, 0, "i8253.cntr2ctl"); . 70,72c int loops, x; . 68c i8253init(void) . 64a Lock lock1; /* mutex for the following */ ushort last1; /* last value of clock 1 */ uvlong ticks1; /* cumulative ticks of counter 1 */ . 62c vlong timerwhen; /* next fastticks a cycintr happens; 0 == infinity */ . 58d 23a Latch1= 0x40, /* latch counter 1's value */ Load1l= 0x50, /* load counter 1's lsb */ Load1m= 0x60, /* load counter 1's msb */ Load1= 0x70, /* load counter 1 with 2 bytes */ . ## diffname pc/i8253.c 2002/0406 ## diff -e /n/emeliedump/2002/0405/sys/src/9/pc/i8253.c /n/emeliedump/2002/0406/sys/src/9/pc/i8253.c 256a if(i8253.last1 == 0 || i8253.last1 >= 0xffe0) i8253ding[1]++; else i8253ding[0]++; . 250d 244c ulong i8253ding[2]; . ## diffname pc/i8253.c 2002/0409 ## diff -e /n/emeliedump/2002/0406/sys/src/9/pc/i8253.c /n/emeliedump/2002/0409/sys/src/9/pc/i8253.c 314c return ticks<>1; ticks = i8253.ticks; iunlock(&i8253); . 305,306c if(y < i8253.last) x = i8253.last - y; . 300,303c ilock(&i8253); outb(Tmode, Latch2); y = inb(T2cntr); y |= inb(T2cntr)<<8; . 298c *hz = Freq<loopconst; if(millisecs <= 0) millisecs = 1; aamloop(millisecs); } void microdelay(int microsecs) { microsecs *= m->loopconst; microsecs /= 1000; if(microsecs <= 0) microsecs = 1; aamloop(microsecs); } ulong TK2MS(ulong ticks) { uvlong t, hz; t = ticks; hz = HZ; t *= 1000L; t = t/hz; ticks = t; return ticks; . 316,320c void delay(int millisecs) . 307c x = i8253.last + (0x10000 - y); . 297c *hz = i8253.hz; . 282c i8253.period = Freq/HZ; intrenable(IrqCLOCK, i8253clock, 0, BUSUNKNOWN, "clock"); . 276,280d 254,270c timerintr(ureg, 0); . 252c i8253clock(Ureg* ureg, void*) . 248d 237,246c /* remember period */ i8253.period = period; i8253periodset++; iunlock(&i8253); . 231,235c /* histeresis */ if(i8253.period != period){ ilock(&i8253); /* load new value */ outb(Tmode, Load0|Square); outb(T0cntr, period); /* low byte */ outb(T0cntr, period>>8); /* high byte */ . 229d 208,227c period = MaxPeriod; if(next != 0){ period = want - now; if(period < MinPeriod) period = MinPeriod; else if(period > (4*MaxPeriod)/5) /* strong attraction to MaxPeriod */ period = MaxPeriod; . 204,206c want = next>>Tickshift; now = i8253.ticks; /* assuming whomever called us just did fastticks() */ . 201,202c ulong period; ulong want; ulong now; . 194,199c ulong i8253periodset; void i8253timerset(uvlong next) . 191a i8253.hz = Freq<>8); return n; } if(n==2 && memcmp(a, "on", 2) == 0){ i8253dotimerset = 1; return n; } error("invalid control message"); return -1; } void i8253link(void) { addarchfile("i8253timerset", 0664, i8253timerread, i8253timerwrite); . 196a if(i8253dotimerset == 0) return; . 188a int i8253dotimerset = 1; . ## diffname pc/i8253.c 2002/0703 ## diff -e /n/emeliedump/2002/0609/sys/src/9/pc/i8253.c /n/emeliedump/2002/0703/sys/src/9/pc/i8253.c 213c /* hysteresis */ . ## diffname pc/i8253.c 2002/0710 ## diff -e /n/emeliedump/2002/0703/sys/src/9/pc/i8253.c /n/emeliedump/2002/0710/sys/src/9/pc/i8253.c 325,337d ## diffname pc/i8253.c 2002/0822 ## diff -e /n/emeliedump/2002/0710/sys/src/9/pc/i8253.c /n/emeliedump/2002/0822/sys/src/9/pc/i8253.c 324a /* * performance measurement ticks. must be low overhead. * doesn't have to count over a second. */ ulong perfticks(void) { uvlong x; if(!m->havetsc) return m->ticks; rdtsc(&x); return x; } . ## diffname pc/i8253.c 2002/1218 ## diff -e /n/emeliedump/2002/0822/sys/src/9/pc/i8253.c /n/emeliedump/2002/1218/sys/src/9/pc/i8253.c 219c outb(T0cntr, period>>8); /* high byte */ . 214a memmove(&phist[0], &phist[1], sizeof(phist)-sizeof(ulong)); phist[nelem(phist)-1] = period; . 190a ulong phist[128]; . ## diffname pc/i8253.c 2002/1219 ## diff -e /n/emeliedump/2002/1218/sys/src/9/pc/i8253.c /n/emeliedump/2002/1219/sys/src/9/pc/i8253.c 223c outb(T0cntr, period >> 8); /* high byte */ . 196c long period; . 94d 86d 79a i8253.period = Freq/HZ; . ## diffname pc/i8253.c 2003/0326 ## diff -e /n/emeliedump/2002/1219/sys/src/9/pc/i8253.c /n/emeliedump/2003/0326/sys/src/9/pc/i8253.c 277d 246,273d