ref: a05bab362f66ddd6fa65f2e7cda9eaaa0217ec08
parent: 999e98b9b856ae4fc75b3ad33783488e33cdd426
author: cinap_lenrek <[email protected]>
date: Sun Jan 17 16: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;