plan 9 kernel history: overview | file list | diff list

1999/0726/pc/devlm78.c (diff list | history)

pc/devlm78.c on 1999/0715
1999/0715    
#include "u.h" 
#include "../port/lib.h" 
#include "mem.h" 
#include "dat.h" 
#include "fns.h" 
#include "io.h" 
#include "ureg.h" 
#include "../port/error.h" 
1999/0708    
 
1999/0726    
// this driver doesn't implement the management interrupts.  we 
// leave the LM78 interrupts set to whatever the BIOS did.  we do 
// allow reading and writing the the readouts and alarm values. 
// Read(2)ing or write(2)ing at offset 0x0-0x1f, is 
// equivalent to reading or writing lm78 registers 0x20-0x3f. 
1999/0725    
enum 
{ 
	// address of chip on serial interface 
	Serialaddr=	0x2d, 
 
1999/0726    
	// parallel access registers 
	Rpaddr=		0x5, 
	 Bbusy=		 (1<<7), 
	Rpdata=		0x6, 
 
1999/0725    
	// internal register addresses 
	Rconfig=	0x40, 
1999/0726    
	 Bstart=	 (1<<0), 
	 Bsmiena=	 (1<<1), 
	 Birqena=	 (1<<2), 
	 Bintclr=	 (1<<3), 
	 Breset=	 (1<<4), 
	 Bnmi=		 (1<<5),	// if set, use nmi, else irq 
	 Bpowbypass=	 (1<<6), 
	 Binit=		 (1<<7), 
1999/0725    
	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 
{ 
1999/0708    
	Qdir, 
1999/0725    
	Qlm78vram, 
1999/0708    
}; 
 
1999/0715    
static Dirtab lm78dir[] = { 
1999/0725    
	"lm78vram",		{ Qlm78vram, 0 },		0,	0444, 
1999/0708    
}; 
 
1999/0725    
// interface type 
enum 
{ 
	None=	0, 
	Smbus, 
	Parallel, 
}; 
1999/0708    
 
1999/0725    
static struct { 
	QLock; 
1999/0726    
	int	probed; 
	int 	ifc;	// which interface is connected 
	SMBus	*smbus;	// serial interface 
	int	port;	// parallel interface 
1999/0725    
} lm78; 
 
1999/0715    
extern SMBus*	piix4smbus(void); 
1999/0708    
 
1999/0726    
// wait for device to become quiescent and then set the 
// register address 
static void 
setreg(int reg) 
{ 
	int tries; 
 
	for(tries = 0; tries < 1000000; tries++) 
		if((inb(lm78.port+Rpaddr) & Bbusy) == 0){ 
			outb(lm78.port+Rpaddr, reg); 
			return; 
		} 
	error("lm78 broken"); 
} 
 
1999/0725    
// routines that actually touch the device 
1999/0726    
static void 
1999/0725    
lm78wrreg(int reg, uchar val) 
{ 
1999/0726    
	if(waserror()){ 
		qunlock(&lm78); 
		nexterror(); 
	} 
	qlock(&lm78); 
 
1999/0725    
	switch(lm78.ifc){ 
	case Smbus: 
		lm78.smbus->transact(lm78.smbus, SMBbytewrite, Serialaddr, reg, &val); 
1999/0726    
		break; 
	case Parallel: 
		setreg(reg); 
		outb(lm78.port+Rpdata, val); 
		break; 
	default: 
		error(Enodev); 
		break; 
1999/0725    
	} 
1999/0726    
 
	qunlock(&lm78); 
	poperror(); 
1999/0725    
} 
 
static int 
lm78rdreg(int reg) 
{ 
1999/0726    
	uchar val; 
1999/0725    
 
1999/0726    
	if(waserror()){ 
		qunlock(&lm78); 
		nexterror(); 
	} 
	qlock(&lm78); 
 
1999/0725    
	switch(lm78.ifc){ 
	case Smbus: 
		lm78.smbus->transact(lm78.smbus, SMBsend, Serialaddr, reg, nil); 
1999/0726    
		lm78.smbus->transact(lm78.smbus, SMBrecv, Serialaddr, 0, &val); 
		break; 
	case Parallel: 
		setreg(reg); 
		val = inb(lm78.port+Rpdata); 
		break; 
	default: 
		error(Enodev); 
		break; 
1999/0725    
	} 
1999/0726    
 
	qunlock(&lm78); 
	poperror(); 
	return val; 
1999/0725    
} 
 
1999/0726    
// start the chip monitoring but don't change any smi 
// interrupts and/or alarms that the BIOS may have set up. 
// 
// this isn't locked because it's thought to be idempotent 
static void 
lm78enable(void) 
1999/0725    
{ 
1999/0726    
	uchar config; 
 
	if(lm78.ifc == None) 
		error(Enodev); 
 
	if(lm78.probed == 0){ 
		// make sure its really there 
1999/0725    
		if(lm78rdreg(Raddr) != Serialaddr){ 
			lm78.ifc = None; 
1999/0726    
			error(Enodev); 
		} else { 
			// start the sampling 
			config = lm78rdreg(Rconfig); 
pprint("config before %2.2ux\n", config); 
			config = (config | Bstart) & ~(Bintclr|Binit); 
			lm78wrreg(Rconfig, config); 
pprint("config after %2.2ux\n", lm78rdreg(Rconfig)); 
1999/0725    
		} 
1999/0726    
		lm78.probed = 1; 
1999/0725    
	} 
} 
 
enum 
{ 
	IntelVendID=	0x8086, 
	PiixID=		0x122E, 
	Piix3ID=	0x7000, 
 
	Piix4PMID=	0x7113,		// PIIX4 power management function 
 
	PCSC=		0x78,		// programmable chip select control register 
	PCSC8bytes=	0x01, 
}; 
 
1999/0726    
// figure out what kind of interface we could have 
1999/0715    
void 
1999/0725    
lm78reset(void) 
1999/0708    
{ 
1999/0725    
	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; 
		} 
	} 
1999/0708    
} 
 
static Chan* 
1999/0715    
lm78attach(char* spec) 
1999/0708    
{ 
1999/0726    
	lm78enable(); 
1999/0725    
 
1999/0715    
	return devattach('T', spec); 
1999/0708    
} 
 
1999/0715    
int 
1999/0708    
lm78walk(Chan* c, char* name) 
{ 
1999/0715    
	return devwalk(c, name, lm78dir, nelem(lm78dir), devgen); 
1999/0708    
} 
 
static void 
1999/0715    
lm78stat(Chan* c, char* dp) 
{ 
	devstat(c, dp, lm78dir, nelem(lm78dir), devgen); 
1999/0708    
} 
 
static Chan* 
lm78open(Chan* c, int omode) 
{ 
1999/0715    
	return devopen(c, omode, lm78dir, nelem(lm78dir), devgen); 
1999/0708    
} 
 
static void 
1999/0715    
lm78close(Chan*) 
1999/0708    
{ 
} 
 
1999/0715    
enum 
1999/0708    
{ 
1999/0715    
	Linelen= 25, 
}; 
1999/0708    
 
static long 
1999/0715    
lm78read(Chan *c, void *a, long n, vlong offset) 
1999/0708    
{ 
1999/0725    
	uchar *va = a; 
	int off, e; 
1999/0715    
 
1999/0725    
	off = offset; 
 
	switch(c->qid.path & ~CHDIR){ 
1999/0708    
	case Qdir: 
1999/0715    
		return devdirread(c, a, n, lm78dir, nelem(lm78dir), devgen); 
1999/0708    
 
1999/0725    
	case Qlm78vram: 
		if(off >=  VRsize) 
			return 0; 
		e = off + n; 
		if(e > VRsize) 
			e = VRsize; 
		for(; off < e; off++) 
1999/0726    
			*va++ = lm78rdreg(Rvalue+off); 
1999/0725    
		return va - (uchar*)a; 
1999/0708    
	} 
1999/0715    
	return 0; 
1999/0708    
} 
 
static long 
1999/0715    
lm78write(Chan *c, void *a, long n, vlong offset) 
1999/0708    
{ 
1999/0725    
	uchar *va = a; 
	int off, e; 
 
	off = offset; 
 
	switch(c->qid.path){ 
	default: 
		error(Eperm); 
 
	case Qlm78vram: 
		if(off >=  VRsize) 
			return 0; 
		e = off + n; 
		if(e > VRsize) 
			e = VRsize; 
		for(; off < e; off++) 
1999/0726    
			lm78wrreg(Rvalue+off, *va++); 
1999/0725    
		return va - (uchar*)a; 
	} 
1999/0715    
	return 0; 
1999/0708    
} 
 
Dev lm78devtab = { 
1999/0715    
	'T', 
1999/0708    
	"lm78", 
1999/0715    
 
1999/0725    
	lm78reset, 
	devinit, 
1999/0708    
	lm78attach, 
1999/0715    
	devclone, 
1999/0708    
	lm78walk, 
	lm78stat, 
	lm78open, 
1999/0715    
	devcreate, 
1999/0708    
	lm78close, 
	lm78read, 
1999/0715    
	devbread, 
1999/0708    
	lm78write, 
1999/0715    
	devbwrite, 
	devremove, 
	devwstat, 
1999/0708    
}; 
1999/0725    
 


source code copyright © 1990-2005 Lucent Technologies; see license
Plan 9 distribution
comments to russ cox (rsc@swtch.com)