#include "all.h"
#include "mem.h"
#include "ureg.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 */
};
static uvlong cpufreq = 66000000;
static int loopconst = 100;
static void
clockintr(Ureg *ur, void *v)
{
USED(v);
clock(0, ur->pc);
}
#define STEPPING(x) ((x) & 0x0F)
#define MODEL(x) ((x)>>4 & 0xf | (x)>>12 & 0xf0)
#define FAMILY(x) ((x)>>8 & 0xf | (x)>>16 & 0xf0)
enum
{
/* flags */
CpuidFPU = 0x001, /* on-chip floating point unit */
CpuidMCE = 0x080, /* machine check exception */
CpuidCX8 = 0x100, /* CMPXCHG8B instruction */
};
typedef struct
{
int family;
int model;
int aalcycles;
char *name;
} X86type;
static X86type x86intel[] =
{
{ 4, 0, 22, "486DX", }, /* known chips */
{ 4, 1, 22, "486DX50", },
{ 4, 2, 22, "486SX", },
{ 4, 3, 22, "486DX2", },
{ 4, 4, 22, "486SL", },
{ 4, 5, 22, "486SX2", },
{ 4, 7, 22, "DX2WB", }, /* P24D */
{ 4, 8, 22, "DX4", }, /* P24C */
{ 4, 9, 22, "DX4WB", }, /* P24CT */
{ 5, 0, 23, "P5", },
{ 5, 1, 23, "P5", },
{ 5, 2, 23, "P54C", },
{ 5, 3, 23, "P24T", },
{ 5, 4, 23, "P55C MMX", },
{ 5, 7, 23, "P54C VRT", },
{ 6, 1, 16, "PentiumPro", },/* trial and error */
{ 6, 3, 16, "PentiumII", },
{ 6, 5, 16, "PentiumII/Xeon", },
{ 6, 6, 16, "Celeron", },
{ 6, 7, 16, "PentiumIII/Xeon", },
{ 6, 8, 16, "PentiumIII/Xeon", },
{ 6, 0xB, 16, "PentiumIII/Xeon", },
{ 6, 0xF, 16, "Xeon5000", },
{ 6, 0x16, 16, "ConroeL", },
{ 6, 0x17, 16, "Xeon5400", },
{ 6, 0x1a, 16, "Xeon3500/i7", },
{ 6, 0x1c, 16, "Atom", },
{ 6, 0x1d, 16, "Xeon7500", },
{ 6, 0x1e, 16, "Xeon3400", },
{ 6, 0x2c, 16, "Xeon[35]600", },
{ 0xF, 1, 16, "P4", }, /* P4 */
{ 0xF, 2, 16, "PentiumIV/Xeon", },
{ 0xF, 4, 16, "PentiumIV/Xeon", },
{ 3, -1, 32, "386", }, /* family defaults */
{ 4, -1, 22, "486", },
{ 5, -1, 23, "P5", },
{ 6, -1, 16, "P6", },
{ 0xF, -1, 16, "P4", }, /* P4 */
{ -1, -1, 16, "unknown", }, /* total default */
};
/*
* The AMD processors all implement the CPUID instruction.
* The later ones also return the processor name via functions
* 0x80000002, 0x80000003 and 0x80000004 in registers AX, BX, CX
* and DX:
* K5 "AMD-K5(tm) Processor"
* K6 "AMD-K6tm w/ multimedia extensions"
* K6 3D "AMD-K6(tm) 3D processor"
* K6 3D+ ?
*/
static X86type x86amd[] =
{
{ 5, 0, 23, "AMD-K5", }, /* guesswork */
{ 5, 1, 23, "AMD-K5", }, /* guesswork */
{ 5, 2, 23, "AMD-K5", }, /* guesswork */
{ 5, 3, 23, "AMD-K5", }, /* guesswork */
{ 5, 4, 23, "AMD Geode GX1", }, /* guesswork */
{ 5, 5, 23, "AMD Geode GX2", }, /* guesswork */
{ 5, 6, 11, "AMD-K6", }, /* trial and error */
{ 5, 7, 11, "AMD-K6", }, /* trial and error */
{ 5, 8, 11, "AMD-K6-2", }, /* trial and error */
{ 5, 9, 11, "AMD-K6-III", },/* trial and error */
{ 5, 0xa, 23, "AMD Geode LX", }, /* guesswork */
{ 0x1F, 9, 11, "AMD-K10 Opteron G34", },/* guesswork */
{ 6, 1, 11, "AMD-Athlon", },/* trial and error */
{ 6, 2, 11, "AMD-Athlon", },/* trial and error */
{ 4, -1, 22, "Am486", }, /* guesswork */
{ 5, -1, 23, "AMD-K5/K6", }, /* guesswork */
{ 6, -1, 11, "AMD-Athlon", },/* guesswork */
{ 0xF, -1, 11, "AMD64", }, /* guesswork */
{ 0x1f, 2, 11, "AMD64-K10 AM2", }, /* guesswork */
{ 0x1f, 4, 11, "AMD64-K10 AM2", }, /* guesswork */
{ 0x1f, 6, 11, "AMD64-K10 AM3", }, /* guesswork */
{ 0x1f, 8, 11, "AMD64-K10 x6", }, /* guesswork */
{ 0x1f, -1, 11, "AMD64-K10", },
{ -1, -1, 11, "AMD unknown", }, /* total default */
};
/*
* WinChip 240MHz
*/
static X86type x86winchip[] =
{
{5, 4, 23, "Winchip",}, /* guesswork */
{6, 7, 23, "Via C3 Samuel 2 or Ezra",},
{6, 8, 23, "Via C3 Ezra-T",},
{6, 9, 23, "Via C3 Eden-N",},
{ -1, -1, 23, "unknown", }, /* total default */
};
/*
* SiS 55x
*/
static X86type x86sis[] =
{
{5, 0, 23, "SiS 55x",}, /* guesswork */
{ -1, -1, 23, "unknown", }, /* total default */
};
static X86type *cputype;
static void
simplecycles(uvlong*x)
{
*x = m->ticks;
}
void (*cycles)(uvlong*) = simplecycles;
void _cycles(uvlong*); /* in l.s */
static void
nop(void)
{
}
void (*coherence)(void) = nop;
/*
* delay for l milliseconds more or less. delayloop is set by
* clockinit() to match the actual CPU speed.
*/
void
delay(int l)
{
l *= loopconst;
if(l <= 0)
l = 1;
aamloop(l);
}
/*
* microsecond delay
*/
void
microdelay(int l)
{
l *= loopconst;
l /= 1000;
if(l <= 0)
l = 1;
aamloop(l);
}
void
printcpufreq(void)
{
int i;
char buf[128];
i = sprint(buf, "cpu%d: %dMHz ", 0, m->cpumhz);
if(m->cpuidid[0])
i += sprint(buf+i, "%s ", m->cpuidid);
sprint(buf+i, "%s (cpuid: AX 0x%4.4ulx DX 0x%4.4ulx)\n",
cputype->name, m->cpuidax, m->cpuiddx);
print(buf);
}
void
cpuidentify(void)
{
int family, model;
X86type *t;
cpuid(m->cpuidid, &m->cpuidax, &m->cpuiddx);
if(strncmp(m->cpuidid, "AuthenticAMD", 12) == 0)
t = x86amd;
else if(strncmp(m->cpuidid, "CentaurHauls", 12) == 0)
t = x86winchip;
else if(strncmp(m->cpuidid, "SiS SiS SiS ", 12) == 0)
t = x86sis;
else
t = x86intel;
family = FAMILY(m->cpuidax);
model = MODEL(m->cpuidax);
while(t->name){
if((t->family == family && t->model == model)
|| (t->family == family && t->model == -1)
|| (t->family == -1))
break;
t++;
}
cputype = t;
if(family >= 5)
coherence = mb586;
if(m->cpuiddx&Sse2)
coherence = mfence;
}
void
clockinit(void)
{
int x, y; /* change in counter */
int loops, incr;
uvlong a, b;
X86type *t;
t = cputype;
/*
* set vector for clock interrupts
*/
setvec(Clockvec, clockintr, 0);
/*
* if there is one, set tsc to a known value
*/
if(m->cpuiddx & 0x10){
m->havetsc = 1;
cycles = _cycles;
if(m->cpuiddx & 0x20)
wrmsr(0x10, 0);
}
/*
* set clock for 1/HZ seconds
*/
outb(Tmode, Load0|Square);
outb(T0cntr, (Freq/HZ)); /* low byte */
outb(T0cntr, (Freq/HZ)>>8); /* 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;
}
/* find biggest loop that doesn't wrap */
incr = 16000000/(t->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);
cycles(&a);
x = inb(T0cntr);
x |= inb(T0cntr)<<8;
aamloop(loops);
outb(Tmode, Latch0);
cycles(&b);
y = inb(T0cntr);
y |= inb(T0cntr)<<8;
x -= y;
if(x < 0)
x += Freq/HZ;
if(x > Freq/(3*HZ))
break;
}
/*
* figure out clock frequency and a loop multiplier for delay().
* n.b. counter goes up by 2*Freq
*/
cpufreq = (vlong)loops*((t->aalcycles*2*Freq)/x);
loopconst = (cpufreq/1000)/t->aalcycles; /* AAM+LOOP's for 1 ms */
if (0)
print("loops %d x %d cpufreq %,lld loopconst %d\n", loops, x, cpufreq, loopconst);
if(m->havetsc){
/* 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;
}
if (0) {
print("cpuhz %,lld cpumhz %d\n", m->cpuhz, m->cpumhz);
delay(10*1000);
}
}
void
clockreload(Timet n)
{
USED(n);
}
|