shithub: plan9front

Download patch

ref: a05bab362f66ddd6fa65f2e7cda9eaaa0217ec08
parent: 999e98b9b856ae4fc75b3ad33783488e33cdd426
author: cinap_lenrek <cinap_lenrek@felloff.net>
date: Sun Jan 17 15:21:12 EST 2021

pc, pc64: add minimal HPET driver to measure LAPIC and TSC frequencies

This adds the new function pointer PCArch.clockinit(),
which is a timer dependent initialization routine.
It also takes over the job of guesscpuhz(). This way, the
architecture ident code can switch between different
timers (i8253, HPET and XEN timer).

--- a/sys/src/9/pc/archacpi.c
+++ b/sys/src/9/pc/archacpi.c
@@ -180,6 +180,20 @@
 	}
 }
 
+static Tbl*
+findtable(char sig[4])
+{
+	Tbl *t;
+	int i;
+
+	for(i=0; i<ntblmap; i++){
+		t = tblmap[i];
+		if(memcmp(t->sig, sig, 4) == 0)
+			return t;
+	}
+	return nil;
+}
+
 static Apic*
 findapic(int gsi, int *pintin)
 {
@@ -569,13 +583,9 @@
 	amlinit();
 
 	/* load DSDT */
-	for(i=0; i<ntblmap; i++){
-		t = tblmap[i];
-		if(memcmp(t->sig, "DSDT", 4) == 0){
-			amlintmask = (~0ULL) >> (t->rev <= 1)*32;
-			amlload(t->data, tbldlen(t));
-			break;
-		}
+	if((t = findtable("DSDT")) != nil){
+		amlintmask = (~0ULL) >> (t->rev <= 1)*32;
+		amlload(t->data, tbldlen(t));
 	}
 
 	/* load SSDT, there can be multiple tables */
@@ -588,15 +598,10 @@
 	/* set APIC mode */
 	amleval(amlwalk(amlroot, "_PIC"), "i", 1, nil);
 
-	for(i=0; i<ntblmap; i++){
-		t = tblmap[i];
-		if(memcmp(t->sig, "APIC", 4) == 0)
-			goto Foundapic;
-	}
-	panic("acpiinit: no MADT (APIC) table");
-	return;
+	t = findtable("APIC");
+	if(t == nil)
+		panic("acpiinit: no MADT (APIC) table");
 
-Foundapic:
 	s = t->data;
 	e = s + tbldlen(t);
 	lapicbase = get32(s); s += 8;
@@ -708,16 +713,12 @@
 {
 	uchar *p;
 	Tbl *t;
-	int i;
 
 	/* stop application processors */
 	mpshutdown();
 
 	/* locate and write platform reset register */
-	for(i=0; i < ntblmap; i++){
-		t = tblmap[i];
-		if(memcmp(t->sig, "FACP", 4) != 0)
-			continue;
+	while((t = findtable("FACP")) != nil){
 		if(get32(t->len) <= 128)
 			break;
 		p = (uchar*)t;
@@ -735,7 +736,12 @@
 
 static int identify(void);
 extern int i8259irqno(int, int);
+extern void i8253init(void);
 
+extern int hpetprobe(uvlong);
+extern void hpetinit(void);
+extern uvlong hpetread(uvlong*);
+
 PCArch archacpi = {
 .id=		"ACPI",	
 .ident=		identify,
@@ -745,6 +751,7 @@
 .intrirqno=	i8259irqno,
 .intron=	lapicintron,
 .introff=	lapicintroff,
+.clockinit=	i8253init,
 .fastclock=	i8253read,
 .timerset=	lapictimerset,
 };
@@ -782,6 +789,7 @@
 {
 	uvlong pa;
 	char *cp;
+	Tbl *t;
 
 	if((cp = getconf("*acpi")) == nil)
 		return 1;
@@ -799,12 +807,20 @@
 	maptables();
 	addarchfile("acpitbls", 0444, readtbls, nil);
 	addarchfile("acpimem", 0600, readmem, writemem);
-	if(strcmp(cp, "0") == 0)
+	if(strcmp(cp, "0") == 0 || findtable("APIC") == nil)
 		return 1;
 	if((cp = getconf("*nomp")) != nil && strcmp(cp, "0") != 0)
 		return 1;
+	if(getconf("*nohpet") == nil
+	&& (t = findtable("HPET")) != nil
+	&& ((uchar*)t)[40] == 0
+	&& hpetprobe(get64((uchar*)t+44)) == 0){
+		archacpi.clockinit = hpetinit;
+		archacpi.fastclock = hpetread;
+	}
 	if(m->havetsc && getconf("*notsc") == nil)
 		archacpi.fastclock = tscticks;
+
 	return 0;
 }
 
--- a/sys/src/9/pc/archgeneric.c
+++ b/sys/src/9/pc/archgeneric.c
@@ -40,6 +40,41 @@
 		idle();
 }
 
+void
+delay(int millisecs)
+{
+	millisecs *= m->loopconst;
+	if(millisecs <= 0)
+		millisecs = 1;
+	aamloop(millisecs);
+}
+
+void
+microdelay(int microsecs)
+{
+	microsecs *= m->loopconst;
+	microsecs /= 1000;
+	if(microsecs <= 0)
+		microsecs = 1;
+	aamloop(microsecs);
+}
+
+/*  
+ *  performance measurement ticks.  must be low overhead.
+ *  doesn't have to count over a second.
+ */
+ulong
+perfticks(void)
+{
+	uvlong x;
+
+	if(m->havetsc)
+		cycles(&x);
+	else
+		x = 0;
+	return x;
+}
+
 PCArch archgeneric = {
 .id=		"generic",
 .ident=		0,
@@ -53,6 +88,7 @@
 .intron=	i8259on,
 .introff=	i8259off,
 
+.clockinit=	i8253init,
 .clockenable=	i8253enable,
 .fastclock=	i8253read,
 .timerset=	i8253timerset,
--- a/sys/src/9/pc/dat.h
+++ b/sys/src/9/pc/dat.h
@@ -229,6 +229,7 @@
 	int	lastintr;
 
 	int	loopconst;
+	int	aalcycles;
 
 	int	cpumhz;
 	uvlong	cyclefreq;		/* Frequency of user readable cycle counter */
@@ -289,6 +290,7 @@
 	void	(*introff)(void);
 	void	(*intron)(void);
 
+	void	(*clockinit)(void);
 	void	(*clockenable)(void);
 	uvlong	(*fastclock)(uvlong*);
 	void	(*timerset)(uvlong);
--- a/sys/src/9/pc/devarch.c
+++ b/sys/src/9/pc/devarch.c
@@ -461,8 +461,6 @@
 	{ -1,	-1,	23,	"unknown", },	/* total default */
 };
 
-static X86type *cputype;
-
 static void	simplecycles(uvlong*);
 void	(*cycles)(uvlong*) = simplecycles;
 void	_cycles(uvlong*);	/* in l.s */
@@ -547,6 +545,7 @@
 		|| (t->family == -1))
 			break;
 
+	m->aalcycles = t->aalcycles;
 	m->cpuidtype = t->name;
 
 	/*
@@ -560,11 +559,6 @@
 	}
 
 	/*
-	 *  use i8253 to guess our cpu speed
-	 */
-	guesscpuhz(t->aalcycles);
-
-	/*
 	 * If machine check exception, page size extensions or page global bit
 	 * are supported enable them in CR4 and clear any other set extensions.
 	 * If machine check was enabled clear out any lingering status.
@@ -690,7 +684,6 @@
 
 	fpuinit();
 
-	cputype = t;
 	return t->family;
 }
 
@@ -702,7 +695,7 @@
 
 	mhz = (m->cpuhz+999999)/1000000;
 
-	snprint(str, sizeof(str), "%s %lud\n", cputype->name, mhz);
+	snprint(str, sizeof(str), "%s %lud\n", m->cpuidtype, mhz);
 	return readstr(offset, a, n, str);
 }
 
@@ -715,7 +708,7 @@
 	p = buf = smalloc(READSTR);
 	ep = p + READSTR;
 	p = seprint(p, ep, "cpu %s %lud%s\n",
-		cputype->name, (ulong)(m->cpuhz+999999)/1000000,
+		m->cpuidtype, (ulong)(m->cpuhz+999999)/1000000,
 		m->havepge ? " pge" : "");
 	p = seprint(p, ep, "pge %s\n", getcr4()&0x80 ? "on" : "off");
 	p = seprint(p, ep, "coherence ");
@@ -877,6 +870,8 @@
 			arch->intrinit = knownarch[0]->intrinit;
 		if(arch->intrassign == nil)
 			arch->intrassign = knownarch[0]->intrassign;
+		if(arch->clockinit == nil)
+			arch->clockinit = knownarch[0]->clockinit;
 	}
 
 	/*
--- a/sys/src/9/pc/fns.h
+++ b/sys/src/9/pc/fns.h
@@ -51,7 +51,6 @@
 ulong	getcr4(void);
 u32int	getdr6(void);
 char*	getconf(char*);
-void	guesscpuhz(int);
 void	halt(void);
 void	mwait(void*);
 int	i8042auxcmd(int);
--- /dev/null
+++ b/sys/src/9/pc/hpet.c
@@ -1,0 +1,126 @@
+#include "u.h"
+#include "../port/lib.h"
+#include "mem.h"
+#include "dat.h"
+#include "fns.h"
+#include "io.h"
+
+/*
+ * HPET timer
+ *
+ * The HPET timer is memory mapped which allows
+ * faster access compared to the classic i8253.
+ * This timer is not used to generate interrupts
+ * as we use the LAPIC timer for that.
+ * Its purpose is to measure the LAPIC timer
+ * and TSC frequencies.
+ */
+
+enum {
+	Cap	= 0x00/4,
+	Period	= 0x04/4,
+	Config	= 0x10/4,
+	Isr	= 0x20/4,
+	Ctrlo	= 0xF0/4,
+	Ctrhi	= 0xF4/4,
+};
+
+static struct {
+	Lock;
+	u32int	*mmio;
+	uvlong	last;
+	uvlong	freq;
+} hpet;
+
+int
+hpetprobe(uvlong pa)
+{
+	u32int cap, period;
+	int mhz;
+
+	if((hpet.mmio = vmap(pa, 1024)) == nil)
+		return -1;
+	cap = hpet.mmio[Cap];
+	period = hpet.mmio[Period];
+	if(period == 0 || period > 0x05F4E100)
+		return -1;
+	hpet.freq = 1000000000000000ULL / period;
+	mhz = (hpet.freq + 500000) / 1000000;
+
+	print("HPET: %llux %.8ux %d MHz \n", pa, cap, mhz);
+
+	return 0;
+}
+
+static uvlong
+hpetcpufreq(void)
+{
+	u32int x, y;
+	uvlong a, b;
+	int loops;
+
+	ilock(&hpet);
+	for(loops = 1000;;loops += 1000){
+		cycles(&a);
+		x = hpet.mmio[Ctrlo];
+		aamloop(loops);
+		cycles(&b);
+		y = hpet.mmio[Ctrlo] - x;
+		if(y >= hpet.freq/HZ || loops >= 1000000)
+			break;
+	}
+	iunlock(&hpet);
+
+	if(m->havetsc && b > a){
+		b -= a;
+		m->cyclefreq = b * hpet.freq / y;
+		m->aalcycles = (b + loops-1) / loops;
+		return m->cyclefreq;
+	}
+	return (vlong)loops*m->aalcycles * hpet.freq / y;
+}
+
+void
+hpetinit(void)
+{
+	uvlong cpufreq;
+
+	if(m->machno != 0){
+		m->cpuhz = MACHP(0)->cpuhz;
+		m->cpumhz = MACHP(0)->cpumhz;
+		m->cyclefreq = MACHP(0)->cyclefreq;
+		m->loopconst = MACHP(0)->loopconst;
+		return;
+	}
+
+	/* start counting */
+	hpet.mmio[Config] |= 1;
+
+	/* measure loopconst for delay() and tsc frequencies */
+	cpufreq = hpetcpufreq();
+
+	m->loopconst = (cpufreq/1000)/m->aalcycles;	/* AAM+LOOP's for 1 ms */
+	m->cpuhz = cpufreq;
+
+	/* round to the nearest megahz */
+	m->cpumhz = (cpufreq+500000)/1000000L;
+	if(m->cpumhz == 0)
+		m->cpumhz = 1;
+}
+
+uvlong
+hpetread(uvlong *hz)
+{
+	uvlong ticks;
+
+	if(hz != nil)
+		*hz = hpet.freq;
+
+	ilock(&hpet);
+	ticks = hpet.last;
+	ticks += hpet.mmio[Ctrlo] - (u32int)ticks;
+	hpet.last = ticks;
+	iunlock(&hpet);
+
+	return ticks;
+}
--- a/sys/src/9/pc/i8253.c
+++ b/sys/src/9/pc/i8253.c
@@ -115,29 +115,12 @@
 	iunlock(&i8253);
 }
 
-void
-i8253init(void)
+static uvlong
+i8253cpufreq(void)
 {
-	ioalloc(T0cntr, 4, 0, "i8253");
-	ioalloc(T2ctl, 1, 0, "i8253.cntr2ctl");
-
-	i8253reset();
-}
-
-void
-guesscpuhz(int aalcycles)
-{
 	int loops, x, y;
-	uvlong a, b, cpufreq;
+	uvlong a, b;
 
-	if(m->machno != 0){
-		m->cpuhz = MACHP(0)->cpuhz;
-		m->cpumhz = MACHP(0)->cpumhz;
-		m->cyclefreq = MACHP(0)->cyclefreq;
-		m->loopconst = MACHP(0)->loopconst;
-		return;
-	}
-
 	ilock(&i8253);
 	for(loops = 1000;;loops += 1000) {
 		/*
@@ -175,21 +158,38 @@
 	if(x == 0)
 		x = 1;
 
-	/*
- 	 *  figure out clock frequency and a loop multiplier for delay().
-	 *  n.b. counter goes up by 2*Freq
-	 */
-	cpufreq = (vlong)loops*((aalcycles*2*Freq)/x);
-	m->loopconst = (cpufreq/1000)/aalcycles;	/* AAM+LOOP's for 1 ms */
-
-	/* a == b means virtualbox has confused us */
 	if(m->havetsc && b > a){
 		b -= a;
-		b *= 2*Freq;
-		b /= x;
-		m->cyclefreq = b;
-		cpufreq = b;
+		m->cyclefreq = b * 2*Freq / x;
+		m->aalcycles = (b + loops-1) / loops;
+
+		return m->cyclefreq;
 	}
+
+	return (vlong)loops*m->aalcycles * 2*Freq / x;
+}
+
+void
+i8253init(void)
+{
+	uvlong cpufreq;
+
+	if(m->machno != 0){
+		m->cpuhz = MACHP(0)->cpuhz;
+		m->cpumhz = MACHP(0)->cpumhz;
+		m->cyclefreq = MACHP(0)->cyclefreq;
+		m->loopconst = MACHP(0)->loopconst;
+		return;
+	}
+
+	ioalloc(T0cntr, 4, 0, "i8253");
+	ioalloc(T2ctl, 1, 0, "i8253.cntr2ctl");
+
+	i8253reset();
+
+	cpufreq = i8253cpufreq();
+
+	m->loopconst = (cpufreq/1000)/m->aalcycles;	/* AAM+LOOP's for 1 ms */
 	m->cpuhz = cpufreq;
 
 	/*
@@ -280,39 +280,4 @@
 	iunlock(&i8253);
 
 	return ticks<<Tickshift;
-}
-
-void
-delay(int millisecs)
-{
-	millisecs *= m->loopconst;
-	if(millisecs <= 0)
-		millisecs = 1;
-	aamloop(millisecs);
-}
-
-void
-microdelay(int microsecs)
-{
-	microsecs *= m->loopconst;
-	microsecs /= 1000;
-	if(microsecs <= 0)
-		microsecs = 1;
-	aamloop(microsecs);
-}
-
-/*  
- *  performance measurement ticks.  must be low overhead.
- *  doesn't have to count over a second.
- */
-ulong
-perfticks(void)
-{
-	uvlong x;
-
-	if(m->havetsc)
-		cycles(&x);
-	else
-		x = 0;
-	return x;
 }
--- a/sys/src/9/pc/main.c
+++ b/sys/src/9/pc/main.c
@@ -31,10 +31,11 @@
 	quotefmtinstall();
 	screeninit();
 	print("\nPlan 9\n");
-	i8253init();
 	cpuidentify();
 	meminit0();
 	archinit();
+	if(arch->clockinit)
+		arch->clockinit();
 	meminit();
 	ramdiskinit();
 	confinit();
--- a/sys/src/9/pc/pc
+++ b/sys/src/9/pc/pc
@@ -97,7 +97,7 @@
 	pci		pcipc
 
 	archgeneric	devkbd i8259 i8253
-	archacpi	mp apic squidboy ec
+	archacpi	mp apic squidboy ec hpet
 	archmp		mp apic squidboy
 	mtrr
 
--- a/sys/src/9/pc/squidboy.c
+++ b/sys/src/9/pc/squidboy.c
@@ -15,6 +15,8 @@
 	machinit();
 	mmuinit();
 	cpuidentify();
+	if(arch->clockinit)
+		arch->clockinit();
 	cpuidprint();
 	syncclock();
 	active.machs[m->machno] = 1;
--- a/sys/src/9/pc64/dat.h
+++ b/sys/src/9/pc64/dat.h
@@ -221,6 +221,7 @@
 	int	lastintr;
 
 	int	loopconst;
+	int	aalcycles;
 
 	int	cpumhz;
 	uvlong	cyclefreq;		/* Frequency of user readable cycle counter */
@@ -278,6 +279,7 @@
 	void	(*introff)(void);
 	void	(*intron)(void);
 
+	void	(*clockinit)(void);
 	void	(*clockenable)(void);
 	uvlong	(*fastclock)(uvlong*);
 	void	(*timerset)(uvlong);
--- a/sys/src/9/pc64/fns.h
+++ b/sys/src/9/pc64/fns.h
@@ -52,7 +52,6 @@
 u64int	getxcr0(void);
 u64int	getdr6(void);
 char*	getconf(char*);
-void	guesscpuhz(int);
 void	halt(void);
 void	mwait(void*);
 int	i8042auxcmd(int);
--- a/sys/src/9/pc64/main.c
+++ b/sys/src/9/pc64/main.c
@@ -183,10 +183,11 @@
 	quotefmtinstall();
 	screeninit();
 	print("\nPlan 9\n");
-	i8253init();
 	cpuidentify();
 	meminit0();
 	archinit();
+	if(arch->clockinit)
+		arch->clockinit();
 	meminit();
 	ramdiskinit();
 	confinit();
--- a/sys/src/9/pc64/pc64
+++ b/sys/src/9/pc64/pc64
@@ -94,7 +94,7 @@
 misc
 	pci		pcipc
 	archgeneric	devkbd i8259 i8253
-	archacpi	mp apic squidboy ec
+	archacpi	mp apic squidboy ec hpet
 	archmp		mp apic squidboy
 	mtrr
 
--- a/sys/src/9/pc64/squidboy.c
+++ b/sys/src/9/pc64/squidboy.c
@@ -16,6 +16,8 @@
 	machinit();
 	mmuinit();
 	cpuidentify();
+	if(arch->clockinit)
+		arch->clockinit();
 	cpuidprint();
 	syncclock();
 	active.machs[m->machno] = 1;
--- a/sys/src/9/xen/archxen.c
+++ b/sys/src/9/xen/archxen.c
@@ -52,10 +52,11 @@
 	HYPERVISOR_shutdown(1);
 }
 
-int xenintrassign(Vctl *v);
-void	xentimerenable(void);
-uvlong	xentimerread(uvlong*);
-void	xentimerset(uvlong);
+extern int	xenintrassign(Vctl *v);
+extern void	xentimerinit(void);
+extern void	xentimerenable(void);
+extern uvlong	xentimerread(uvlong*);
+extern void	xentimerset(uvlong);
 
 PCArch archxen = {
 .id=		"Xen",	
@@ -63,6 +64,7 @@
 .reset=		shutdown,
 .intrinit=	intrinit,
 .intrassign=	xenintrassign,
+.clockinit=	xentimerinit,
 .clockenable=	xentimerenable,
 .fastclock=	xentimerread,
 .timerset=	xentimerset,
--- a/sys/src/9/xen/fns.h
+++ b/sys/src/9/xen/fns.h
@@ -27,7 +27,6 @@
 void	(*fpsave)(FPsave*);
 ulong	getcr4(void);
 char*	getconf(char*);
-void	guesscpuhz(int);
 void	halt(void);
 void	mwait(void*);
 void	i8042reset(void);
--- a/sys/src/9/xen/main.c
+++ b/sys/src/9/xen/main.c
@@ -76,6 +76,8 @@
 	// meminit() is not for us
 	confinit();
 	archinit();
+	if(arch->clockinit)
+		arch->clockinit();
 	xinit();
 	trapinit();
 	printinit();
--- a/sys/src/9/xen/xentimer.c
+++ b/sys/src/9/xen/xentimer.c
@@ -34,7 +34,7 @@
 
 /* just get it from the shared info */
 void
-guesscpuhz(int) // XXX no arg!
+xentimerinit(void)
 {
 	vcpu_time_info_t *t;