plan9fox/sys/src/9/pc/hpet.c
cinap_lenrek a05bab362f 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).
2021-01-17 21:21:12 +01:00

127 lines
2.1 KiB
C

#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;
}