~kris/9p

9hist

c8a2f948ae9e5c2ea4bda742e879302a8133cb5d — David du Colombier 27 years ago ced466f
Plan 9 from Bell Labs 1999-07-25
2 files changed, 244 insertions(+), 46 deletions(-)

M pc/devlm78.c
M pc/piix4smbus.c
M pc/devlm78.c => pc/devlm78.c +203 -14
@@ 7,30 7,175 @@
#include "ureg.h"
#include "../port/error.h"

enum {
// this driver assumes that noone has changed the serial address
// of the device.  if they have, there's no way we can figure it
// out -- presotto

enum
{
	// address of chip on serial interface
	Serialaddr=	0x2d,

	// internal register addresses
	Rconfig=	0x40,
	Ristat1=	0x41,
	Ristat2=	0x42,
	Rsmimask1=	0x43,
	Rsmimask2=	0x44,
	Rnmimask1=	0x45,
	Rnmimask2=	0x46,
	Rvidfan=	0x47,		// set fan counter, and read voltage level
	 Mvid=		 0x0f,
	 Mfan=		 0xf0,
	Raddr=		0x48,		// address used on serial bus
	Rresetid=	0x49,		// chip reset and ID register
	Rpost=		0x00,		// start of post ram
	Rvalue=		0x20,		// start of value ram

	VRsize=		0x20,		// size of value ram
};

enum
{
	Qdir,
	Qtemp,
	Qlm78vram,
};

static Dirtab lm78dir[] = {
	"temp",		{ Qtemp, 0 },		0,	0444,
	"lm78vram",		{ Qlm78vram, 0 },		0,	0444,
};

// interface type
enum
{
	None=	0,
	Smbus,
	Parallel,
};

SMBus *lm78smbus;
static struct {
	QLock;

	int 	ifc;

	// serial interface
	SMBus	*smbus;

	// parallel interface
	int	port;
} lm78;

extern SMBus*	piix4smbus(void);

// routines that actually touch the device
static int
lm78wrreg(int reg, uchar val)
{
	switch(lm78.ifc){
	case Smbus:
		lm78.smbus->transact(lm78.smbus, SMBbytewrite, Serialaddr, reg, &val);
		return 0;
	}
	return -1;
}

static int
lm78rdreg(int reg)
{
	uchar rv;

	switch(lm78.ifc){
	case Smbus:
		lm78.smbus->transact(lm78.smbus, SMBsend, Serialaddr, reg, nil);
		lm78.smbus->transact(lm78.smbus, SMBrecv, Serialaddr, 0, &rv);
		return rv;
	}
	return -1;
}

static int
lm78probe(void)
{
	switch(lm78.ifc){
	case Smbus:
		if(lm78rdreg(Raddr) != Serialaddr){
			lm78.ifc = None;
			break;
		}
		return 0;
	}
	return -1;
}

enum
{
	IntelVendID=	0x8086,
	PiixID=		0x122E,
	Piix3ID=	0x7000,

	Piix4PMID=	0x7113,		// PIIX4 power management function

	PCSC=		0x78,		// programmable chip select control register
	PCSC8bytes=	0x01,
};

// figure out what kind of interface and if there's an lm78 there
void
lm78init(void)
lm78reset(void)
{
	lm78smbus = piix4smbus();
	if(lm78smbus != nil)
		print("found piix4 smbus, base %lud\n", lm78smbus->base);
	int pcs;
	Pcidev *p;

	lm78.ifc = None;
	p = nil;
	while((p = pcimatch(p, IntelVendID, 0)) != nil){
		switch(p->did){
		// these bridges use the PCSC to map the lm78 into port space.
		// for this case the lm78's CS# select is connected to the PIIX's
		// PCS# output and the bottom 3 bits of address are passed to the
		// LM78's A0-A2 inputs.
		case PiixID:
		case Piix3ID:
			pcs = pcicfgr16(p, PCSC);
			if(pcs & 3) {
				/* already enabled */
				lm78.port = pcs & ~3;
				lm78.ifc = Parallel;
				return;	
			}

			// enable the chip, use default address 0x50
			pcicfgw16(p, PCSC, 0x50|PCSC8bytes);
			pcs = pcicfgr16(p, PCSC);
			lm78.port = pcs & ~3;
			lm78.ifc = Parallel;
			return;

		// this bridge puts the lm78's serial interface on the smbus
		case Piix4PMID:
			lm78.smbus = piix4smbus();
			if(lm78.smbus == nil)
				continue;
			print("found piix4 smbus, base %lud\n", lm78.smbus->base);
			lm78.ifc = Smbus;
			return;
		}
	}
}

static Chan*
lm78attach(char* spec)
{
	static int probed;

	if(lm78.ifc == None)
		error(Enodev);

	if(probed == 0){
		if(lm78probe() < 0)
			error(Enodev);
		probed = 1;
	}
	return devattach('T', spec);
}



@@ 65,13 210,31 @@ enum
static long
lm78read(Chan *c, void *a, long n, vlong offset)
{
	switch(c->qid.path & ~CHDIR){
	uchar *va = a;
	int off, e;

	off = offset;

	switch(c->qid.path & ~CHDIR){
	case Qdir:
		return devdirread(c, a, n, lm78dir, nelem(lm78dir), devgen);

	case Qtemp:
		error(Eperm);
	case Qlm78vram:
		if(off >=  VRsize)
			return 0;
		if(waserror()){
			qunlock(&lm78);
			nexterror();
		}
		qlock(&lm78);
		e = off + n;
		if(e > VRsize)
			e = VRsize;
		for(; off < e; off++)
			*va++ = lm78rdreg(off);
		qunlock(&lm78);
		poperror();
		return va - (uchar*)a;
	}
	return 0;
}


@@ 79,7 242,32 @@ lm78read(Chan *c, void *a, long n, vlong offset)
static long
lm78write(Chan *c, void *a, long n, vlong offset)
{
	error(Eperm);
	uchar *va = a;
	int off, e;

	off = offset;

	switch(c->qid.path){
	default:
		error(Eperm);

	case Qlm78vram:
		if(off >=  VRsize)
			return 0;
		if(waserror()){
			qunlock(&lm78);
			nexterror();
		}
		qlock(&lm78);
		e = off + n;
		if(e > VRsize)
			e = VRsize;
		for(; off < e; off++)
			lm78wrreg(off, *va++);
		qunlock(&lm78);
		poperror();
		return va - (uchar*)a;
	}
	return 0;
}



@@ 87,8 275,8 @@ Dev lm78devtab = {
	'T',
	"lm78",

	devreset,
	lm78init,
	lm78reset,
	devinit,
	lm78attach,
	devclone,
	lm78walk,


@@ 103,3 291,4 @@ Dev lm78devtab = {
	devremove,
	devwstat,
};


M pc/piix4smbus.c => pc/piix4smbus.c +41 -32
@@ 26,7 26,7 @@ enum
	 Failed=	(1<<4),	 	//  transaction terminated by KILL
	 Bus_error=	(1<<3),		//  transactio collision
	 Dev_error=	(1<<2),		//  device error interrupt
	 Host_complete=	(1<<2),		//  host command completion interrupt 
	 Host_complete=	(1<<1),		//  host command completion interrupt 
	 Host_busy=	(1<<0),		//
	Slavestatus=	0x1,		//  (writing 1 bits reset)
	 Alert_sts=	(1<<5),		//  someone asserted SMBALERT#


@@ 72,28 72,15 @@ static struct
	[SMBsend]	{ 0,	1,	0,	Byte },
	[SMBbytewrite]	{ 0,	1,	1,	ByteData },
	[SMBwordwrite]	{ 0,	1,	2,	WordData },
	[SMBrecv]	{ Read,	1,	0, 	Byte },
	[SMBrecv]	{ Read,	0,	1, 	Byte },
	[SMBbyteread]	{ Read,	1,	1,	ByteData },
	[SMBwordread]	{ Read,	1,	2,	WordData },
};

static int
spin(Pcidev *p, int bit)
{
	int tries;

	for(tries = 0; tries < 1000000; tries++){
		if((pcicfgr8(p, Hoststatus) & bit) == 0)
			return 0;
		sched();
	}
	return -1;
}

static void
transact(SMBus *s, int type, int addr, int cmd, uchar *data)
{
	Pcidev *p = s->arg;
	int tries;

	if(type < 0 || type > nelem(proto))
		panic("piix4smbus: illegal transaction type %d", type);


@@ 105,48 92,70 @@ transact(SMBus *s, int type, int addr, int cmd, uchar *data)
	qlock(s);

	// wait a while for the host interface to be available
	if(spin(p, Host_busy) != 0){
	for(tries = 0; tries < 1000000; tries++){
		if((inb(s->base+Hoststatus) & Host_busy) == 0)
			break;
		sched();
	}
	if(tries >= 1000000){
		// try aborting current transaction
		pcicfgw8(p, Hostcontrol, Kill);
		if(spin(p, Host_busy) < 0)
			error("SMBus broken");
		outb(s->base+Hostcontrol, Kill);
		for(tries = 0; tries < 1000000; tries++){
			if((inb(s->base+Hoststatus) & Host_busy) == 0)
				break;
			sched();
		}
		if(tries >= 1000000){
			print("SMBus status: %2.2ux", inb(s->base+Hoststatus));
			error("SMBus broken1");
		}
	}

	// set up for transaction
	pcicfgw8(p, Hostaddress, (addr<<1)|proto[type].rw);
	outb(s->base+Hostaddress, (addr<<1)|proto[type].rw);
	if(proto[type].cmd)
		pcicfgw8(p, Hostcommand, cmd);
		outb(s->base+Hostcommand, cmd);
	if(proto[type].rw != Read){
		switch(proto[type].len){
		case 2:
			pcicfgw8(p, Hostdata1, data[1]);
			outb(s->base+Hostdata1, data[1]);
			// fall through
		case 1:
			pcicfgw8(p, Hostdata0, data[0]);
			outb(s->base+Hostdata0, data[0]);
			break;
		}
	}

	// reset the completion bit and start transaction
	pcicfgw8(p, Hoststatus, Host_complete);
	pcicfgw8(p, Hostcontrol, Start|proto[type].proto);
	outb(s->base+Hoststatus, Host_complete);
print("before %2.2ux\n", inb(s->base+Hoststatus));
	outb(s->base+Hostcontrol, Start|proto[type].proto);
print("after %2.2ux\n", inb(s->base+Hoststatus));

	// wait for completion
	if(spin(p, Host_complete) < 0)
		error("SMBus broken");
	for(tries = 0; tries < 1000000; tries++){
		if(inb(s->base+Hoststatus) & Host_complete)
			break;
		sched();
	}
	if(tries >= 1000000){
		print("SMBus status: %2.2ux", inb(s->base+Hoststatus));
		error("SMBus broken2");
	}

	// get results
	if(proto[type].rw == Read){
		switch(proto[type].len){
		case 2:
			data[1] = pcicfgr8(p, Hostdata1);
			data[1] = inb(s->base+Hostdata1);
			// fall through
		case 1:
			data[0] = pcicfgr8(p, Hostdata0);
			data[0] = inb(s->base+Hostdata0);
			break;
		}
	}
	qunlock(s);
	poperror();
}

static SMBus smbusproto =


@@ 192,8 201,8 @@ print("SMB base ialloc is 0x%lux\n", s->base);
	}

	// disable SMBus interrupts, abort any transaction in progress
	pcicfgw8(p, Hostcontrol, Kill);
	pcicfgw8(p, Slavecontrol, 0);
	outb(s->base+Hostcontrol, Kill);
	outb(s->base+Slavecontrol, 0);

	// enable the smbus
	pcicfgw8(p, SMBconfig, IRQ9enable|SMBenable);