xref: /plan9-contrib/sys/src/9/pc/i8253.c (revision a650be7d984b0294fadd12cd786574e09003d20c)
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