Plan 9 from Bell Labs’s /usr/web/sources/contrib/quanstro/root/sys/src/fs/pc/8253.c

Copyright © 2021 Plan 9 Foundation.
Distributed under the MIT License.
Download the Plan 9 distribution.


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

Bell Labs OSI certified Powered by Plan 9

(Return to Plan 9 Home Page)

Copyright © 2021 Plan 9 Foundation. All Rights Reserved.
Comments to webmaster@9p.io.