xref: /plan9-contrib/sys/src/9/pc/devlm78.c (revision 68412abfd32cf2b40bc99e2a1363ebd4a35a4480)
13ff48bf5SDavid du Colombier #include "u.h"
23ff48bf5SDavid du Colombier #include "../port/lib.h"
33ff48bf5SDavid du Colombier #include "mem.h"
43ff48bf5SDavid du Colombier #include "dat.h"
53ff48bf5SDavid du Colombier #include "fns.h"
63ff48bf5SDavid du Colombier #include "io.h"
73ff48bf5SDavid du Colombier #include "ureg.h"
83ff48bf5SDavid du Colombier #include "../port/error.h"
93ff48bf5SDavid du Colombier 
10fd597ed8SDavid du Colombier /* this driver doesn't implement the management interrupts.  we
11fd597ed8SDavid du Colombier  * leave the LM78 interrupts set to whatever the BIOS did.  we do
12fd597ed8SDavid du Colombier  * allow reading and writing the the readouts and alarm values.
13fd597ed8SDavid du Colombier  * Read(2)ing or write(2)ing at offset 0x0-0x1f, is
14fd597ed8SDavid du Colombier  * equivalent to reading or writing lm78 registers 0x20-0x3f.
15fd597ed8SDavid du Colombier  */
163ff48bf5SDavid du Colombier enum
173ff48bf5SDavid du Colombier {
18fd597ed8SDavid du Colombier 	/*  address of chip on serial interface */
193ff48bf5SDavid du Colombier 	Serialaddr=	0x2d,
203ff48bf5SDavid du Colombier 
21fd597ed8SDavid du Colombier 	/*  parallel access registers */
223ff48bf5SDavid du Colombier 	Rpaddr=		0x5,
233ff48bf5SDavid du Colombier 	Bbusy=		 (1<<7),
243ff48bf5SDavid du Colombier 	Rpdata=		0x6,
253ff48bf5SDavid du Colombier 
26fd597ed8SDavid du Colombier 	/*  internal register addresses */
273ff48bf5SDavid du Colombier 	Rconfig=	0x40,
283ff48bf5SDavid du Colombier 	Bstart=		 (1<<0),
293ff48bf5SDavid du Colombier 	Bsmiena=	 (1<<1),
303ff48bf5SDavid du Colombier 	Birqena=	 (1<<2),
313ff48bf5SDavid du Colombier 	Bintclr=	 (1<<3),
323ff48bf5SDavid du Colombier 	Breset=		 (1<<4),
33fd597ed8SDavid du Colombier 	Bnmi=		 (1<<5),	/*  if set, use nmi, else irq */
343ff48bf5SDavid du Colombier 	Bpowbypass=	 (1<<6),
353ff48bf5SDavid du Colombier 	Binit=		 (1<<7),
363ff48bf5SDavid du Colombier 	Ristat1=	0x41,
373ff48bf5SDavid du Colombier 	Ristat2=	0x42,
383ff48bf5SDavid du Colombier 	Rsmimask1=	0x43,
393ff48bf5SDavid du Colombier 	Rsmimask2=	0x44,
403ff48bf5SDavid du Colombier 	Rnmimask1=	0x45,
413ff48bf5SDavid du Colombier 	Rnmimask2=	0x46,
42fd597ed8SDavid du Colombier 	Rvidfan=	0x47,		/*  set fan counter, and read voltage level */
433ff48bf5SDavid du Colombier 	Mvid=		 0x0f,
443ff48bf5SDavid du Colombier 	Mfan=		 0xf0,
45fd597ed8SDavid du Colombier 	Raddr=		0x48,		/*  address used on serial bus */
46fd597ed8SDavid du Colombier 	Rresetid=	0x49,		/*  chip reset and ID register */
47fd597ed8SDavid du Colombier 	Rpost=		0x00,		/*  start of post ram */
48fd597ed8SDavid du Colombier 	Rvalue=		0x20,		/*  start of value ram */
493ff48bf5SDavid du Colombier 
50fd597ed8SDavid du Colombier 	VRsize=		0x20,		/*  size of value ram */
513ff48bf5SDavid du Colombier };
523ff48bf5SDavid du Colombier 
533ff48bf5SDavid du Colombier enum
543ff48bf5SDavid du Colombier {
553ff48bf5SDavid du Colombier 	Qdir,
563ff48bf5SDavid du Colombier 	Qlm78vram,
573ff48bf5SDavid du Colombier };
583ff48bf5SDavid du Colombier 
593ff48bf5SDavid du Colombier static Dirtab lm78dir[] = {
60*68412abfSDavid du Colombier 	".",			{ Qdir, 0, QTDIR},	0,	0555,
613ff48bf5SDavid du Colombier 	"lm78vram",	{ Qlm78vram, 0 },	0,	0444,
623ff48bf5SDavid du Colombier };
633ff48bf5SDavid du Colombier 
64fd597ed8SDavid du Colombier /*  interface type */
653ff48bf5SDavid du Colombier enum
663ff48bf5SDavid du Colombier {
673ff48bf5SDavid du Colombier 	None=	0,
683ff48bf5SDavid du Colombier 	Smbus,
693ff48bf5SDavid du Colombier 	Parallel,
703ff48bf5SDavid du Colombier };
713ff48bf5SDavid du Colombier 
723ff48bf5SDavid du Colombier static struct {
733ff48bf5SDavid du Colombier 	QLock;
743ff48bf5SDavid du Colombier 	int	probed;
75fd597ed8SDavid du Colombier 	int 	ifc;	/*  which interface is connected */
76fd597ed8SDavid du Colombier 	SMBus	*smbus;	/*  serial interface */
77fd597ed8SDavid du Colombier 	int	port;	/*  parallel interface */
783ff48bf5SDavid du Colombier } lm78;
793ff48bf5SDavid du Colombier 
803ff48bf5SDavid du Colombier extern SMBus*	piix4smbus(void);
813ff48bf5SDavid du Colombier 
82fd597ed8SDavid du Colombier /*  wait for device to become quiescent and then set the */
83fd597ed8SDavid du Colombier /*  register address */
843ff48bf5SDavid du Colombier static void
setreg(int reg)853ff48bf5SDavid du Colombier setreg(int reg)
863ff48bf5SDavid du Colombier {
873ff48bf5SDavid du Colombier 	int tries;
883ff48bf5SDavid du Colombier 
893ff48bf5SDavid du Colombier 	for(tries = 0; tries < 1000000; tries++)
903ff48bf5SDavid du Colombier 		if((inb(lm78.port+Rpaddr) & Bbusy) == 0){
913ff48bf5SDavid du Colombier 			outb(lm78.port+Rpaddr, reg);
923ff48bf5SDavid du Colombier 			return;
933ff48bf5SDavid du Colombier 		}
943ff48bf5SDavid du Colombier 	error("lm78 broken");
953ff48bf5SDavid du Colombier }
963ff48bf5SDavid du Colombier 
97fd597ed8SDavid du Colombier /*  routines that actually touch the device */
983ff48bf5SDavid du Colombier static void
lm78wrreg(int reg,uchar val)993ff48bf5SDavid du Colombier lm78wrreg(int reg, uchar val)
1003ff48bf5SDavid du Colombier {
1013ff48bf5SDavid du Colombier 	if(waserror()){
1023ff48bf5SDavid du Colombier 		qunlock(&lm78);
1033ff48bf5SDavid du Colombier 		nexterror();
1043ff48bf5SDavid du Colombier 	}
1053ff48bf5SDavid du Colombier 	qlock(&lm78);
1063ff48bf5SDavid du Colombier 
1073ff48bf5SDavid du Colombier 	switch(lm78.ifc){
1083ff48bf5SDavid du Colombier 	case Smbus:
1093ff48bf5SDavid du Colombier 		lm78.smbus->transact(lm78.smbus, SMBbytewrite, Serialaddr, reg, &val);
1103ff48bf5SDavid du Colombier 		break;
1113ff48bf5SDavid du Colombier 	case Parallel:
1123ff48bf5SDavid du Colombier 		setreg(reg);
1133ff48bf5SDavid du Colombier 		outb(lm78.port+Rpdata, val);
1143ff48bf5SDavid du Colombier 		break;
1153ff48bf5SDavid du Colombier 	default:
1163ff48bf5SDavid du Colombier 		error(Enodev);
1173ff48bf5SDavid du Colombier 		break;
1183ff48bf5SDavid du Colombier 	}
1193ff48bf5SDavid du Colombier 
1203ff48bf5SDavid du Colombier 	qunlock(&lm78);
1213ff48bf5SDavid du Colombier 	poperror();
1223ff48bf5SDavid du Colombier }
1233ff48bf5SDavid du Colombier 
1243ff48bf5SDavid du Colombier static int
lm78rdreg(int reg)1253ff48bf5SDavid du Colombier lm78rdreg(int reg)
1263ff48bf5SDavid du Colombier {
1273ff48bf5SDavid du Colombier 	uchar val;
1283ff48bf5SDavid du Colombier 
1293ff48bf5SDavid du Colombier 	if(waserror()){
1303ff48bf5SDavid du Colombier 		qunlock(&lm78);
1313ff48bf5SDavid du Colombier 		nexterror();
1323ff48bf5SDavid du Colombier 	}
1333ff48bf5SDavid du Colombier 	qlock(&lm78);
1343ff48bf5SDavid du Colombier 
1353ff48bf5SDavid du Colombier 	switch(lm78.ifc){
1363ff48bf5SDavid du Colombier 	case Smbus:
1373ff48bf5SDavid du Colombier 		lm78.smbus->transact(lm78.smbus, SMBsend, Serialaddr, reg, nil);
1383ff48bf5SDavid du Colombier 		lm78.smbus->transact(lm78.smbus, SMBrecv, Serialaddr, 0, &val);
1393ff48bf5SDavid du Colombier 		break;
1403ff48bf5SDavid du Colombier 	case Parallel:
1413ff48bf5SDavid du Colombier 		setreg(reg);
1423ff48bf5SDavid du Colombier 		val = inb(lm78.port+Rpdata);
1433ff48bf5SDavid du Colombier 		break;
1443ff48bf5SDavid du Colombier 	default:
1453ff48bf5SDavid du Colombier 		error(Enodev);
1463ff48bf5SDavid du Colombier 		break;
1473ff48bf5SDavid du Colombier 	}
1483ff48bf5SDavid du Colombier 
1493ff48bf5SDavid du Colombier 	qunlock(&lm78);
1503ff48bf5SDavid du Colombier 	poperror();
1513ff48bf5SDavid du Colombier 	return val;
1523ff48bf5SDavid du Colombier }
1533ff48bf5SDavid du Colombier 
154fd597ed8SDavid du Colombier /*  start the chip monitoring but don't change any smi
155fd597ed8SDavid du Colombier  *  interrupts and/or alarms that the BIOS may have set up.
156fd597ed8SDavid du Colombier  *  this isn't locked because it's thought to be idempotent
157fd597ed8SDavid du Colombier  */
1583ff48bf5SDavid du Colombier static void
lm78enable(void)1593ff48bf5SDavid du Colombier lm78enable(void)
1603ff48bf5SDavid du Colombier {
1613ff48bf5SDavid du Colombier 	uchar config;
1623ff48bf5SDavid du Colombier 
1633ff48bf5SDavid du Colombier 	if(lm78.ifc == None)
1643ff48bf5SDavid du Colombier 		error(Enodev);
1653ff48bf5SDavid du Colombier 
1663ff48bf5SDavid du Colombier 	if(lm78.probed == 0){
167fd597ed8SDavid du Colombier 		/*  make sure its really there */
1683ff48bf5SDavid du Colombier 		if(lm78rdreg(Raddr) != Serialaddr){
1693ff48bf5SDavid du Colombier 			lm78.ifc = None;
1703ff48bf5SDavid du Colombier 			error(Enodev);
1713ff48bf5SDavid du Colombier 		} else {
172fd597ed8SDavid du Colombier 			/*  start the sampling */
1733ff48bf5SDavid du Colombier 			config = lm78rdreg(Rconfig);
1743ff48bf5SDavid du Colombier 			config = (config | Bstart) & ~(Bintclr|Binit);
1753ff48bf5SDavid du Colombier 			lm78wrreg(Rconfig, config);
1763ff48bf5SDavid du Colombier pprint("Rvidfan %2.2ux\n", lm78rdreg(Rconfig), lm78rdreg(Rvidfan));
1773ff48bf5SDavid du Colombier 		}
1783ff48bf5SDavid du Colombier 		lm78.probed = 1;
1793ff48bf5SDavid du Colombier 	}
1803ff48bf5SDavid du Colombier }
1813ff48bf5SDavid du Colombier 
1823ff48bf5SDavid du Colombier enum
1833ff48bf5SDavid du Colombier {
1843ff48bf5SDavid du Colombier 	IntelVendID=	0x8086,
1853ff48bf5SDavid du Colombier 	PiixID=		0x122E,
1863ff48bf5SDavid du Colombier 	Piix3ID=	0x7000,
1873ff48bf5SDavid du Colombier 
188fd597ed8SDavid du Colombier 	Piix4PMID=	0x7113,		/*  PIIX4 power management function */
1893ff48bf5SDavid du Colombier 
190fd597ed8SDavid du Colombier 	PCSC=		0x78,		/*  programmable chip select control register */
1913ff48bf5SDavid du Colombier 	PCSC8bytes=	0x01,
1923ff48bf5SDavid du Colombier };
1933ff48bf5SDavid du Colombier 
194fd597ed8SDavid du Colombier /*  figure out what kind of interface we could have */
1953ff48bf5SDavid du Colombier void
lm78reset(void)1963ff48bf5SDavid du Colombier lm78reset(void)
1973ff48bf5SDavid du Colombier {
1983ff48bf5SDavid du Colombier 	int pcs;
1993ff48bf5SDavid du Colombier 	Pcidev *p;
2003ff48bf5SDavid du Colombier 
2013ff48bf5SDavid du Colombier 	lm78.ifc = None;
2023ff48bf5SDavid du Colombier 	p = nil;
2033ff48bf5SDavid du Colombier 	while((p = pcimatch(p, IntelVendID, 0)) != nil){
2043ff48bf5SDavid du Colombier 		switch(p->did){
205fd597ed8SDavid du Colombier 		/*  these bridges use the PCSC to map the lm78 into port space. */
206fd597ed8SDavid du Colombier 		/*  for this case the lm78's CS# select is connected to the PIIX's */
207fd597ed8SDavid du Colombier 		/*  PCS# output and the bottom 3 bits of address are passed to the */
208fd597ed8SDavid du Colombier 		/*  LM78's A0-A2 inputs. */
2093ff48bf5SDavid du Colombier 		case PiixID:
2103ff48bf5SDavid du Colombier 		case Piix3ID:
2113ff48bf5SDavid du Colombier 			pcs = pcicfgr16(p, PCSC);
2123ff48bf5SDavid du Colombier 			if(pcs & 3) {
2133ff48bf5SDavid du Colombier 				/* already enabled */
2143ff48bf5SDavid du Colombier 				lm78.port = pcs & ~3;
2153ff48bf5SDavid du Colombier 				lm78.ifc = Parallel;
2163ff48bf5SDavid du Colombier 				return;
2173ff48bf5SDavid du Colombier 			}
2183ff48bf5SDavid du Colombier 
219fd597ed8SDavid du Colombier 			/*  enable the chip, use default address 0x50 */
2203ff48bf5SDavid du Colombier 			pcicfgw16(p, PCSC, 0x50|PCSC8bytes);
2213ff48bf5SDavid du Colombier 			pcs = pcicfgr16(p, PCSC);
2223ff48bf5SDavid du Colombier 			lm78.port = pcs & ~3;
2233ff48bf5SDavid du Colombier 			lm78.ifc = Parallel;
2243ff48bf5SDavid du Colombier 			return;
2253ff48bf5SDavid du Colombier 
226fd597ed8SDavid du Colombier 		/*  this bridge puts the lm78's serial interface on the smbus */
2273ff48bf5SDavid du Colombier 		case Piix4PMID:
2283ff48bf5SDavid du Colombier 			lm78.smbus = piix4smbus();
2293ff48bf5SDavid du Colombier 			if(lm78.smbus == nil)
2303ff48bf5SDavid du Colombier 				continue;
2313ff48bf5SDavid du Colombier 			print("found piix4 smbus, base %lud\n", lm78.smbus->base);
2323ff48bf5SDavid du Colombier 			lm78.ifc = Smbus;
2333ff48bf5SDavid du Colombier 			return;
2343ff48bf5SDavid du Colombier 		}
2353ff48bf5SDavid du Colombier 	}
2363ff48bf5SDavid du Colombier }
2373ff48bf5SDavid du Colombier 
238fd597ed8SDavid du Colombier Walkqid *
lm78walk(Chan * c,Chan * nc,char ** name,int nname)239fd597ed8SDavid du Colombier lm78walk(Chan* c, Chan *nc, char** name, int nname)
2403ff48bf5SDavid du Colombier {
241fd597ed8SDavid du Colombier 	return devwalk(c, nc, name, nname, lm78dir, nelem(lm78dir), devgen);
2423ff48bf5SDavid du Colombier }
2433ff48bf5SDavid du Colombier 
244fd597ed8SDavid du Colombier static int
lm78stat(Chan * c,uchar * dp,int n)245fd597ed8SDavid du Colombier lm78stat(Chan* c, uchar* dp, int n)
2463ff48bf5SDavid du Colombier {
247fd597ed8SDavid du Colombier 	return devstat(c, dp, n, lm78dir, nelem(lm78dir), devgen);
2483ff48bf5SDavid du Colombier }
2493ff48bf5SDavid du Colombier 
2503ff48bf5SDavid du Colombier static Chan*
lm78open(Chan * c,int omode)2513ff48bf5SDavid du Colombier lm78open(Chan* c, int omode)
2523ff48bf5SDavid du Colombier {
2533ff48bf5SDavid du Colombier 	return devopen(c, omode, lm78dir, nelem(lm78dir), devgen);
2543ff48bf5SDavid du Colombier }
2553ff48bf5SDavid du Colombier 
2563ff48bf5SDavid du Colombier static void
lm78close(Chan *)2573ff48bf5SDavid du Colombier lm78close(Chan*)
2583ff48bf5SDavid du Colombier {
2593ff48bf5SDavid du Colombier }
2603ff48bf5SDavid du Colombier 
2613ff48bf5SDavid du Colombier enum
2623ff48bf5SDavid du Colombier {
2633ff48bf5SDavid du Colombier 	Linelen= 25,
2643ff48bf5SDavid du Colombier };
2653ff48bf5SDavid du Colombier 
2663ff48bf5SDavid du Colombier static long
lm78read(Chan * c,void * a,long n,vlong offset)2673ff48bf5SDavid du Colombier lm78read(Chan *c, void *a, long n, vlong offset)
2683ff48bf5SDavid du Colombier {
2693ff48bf5SDavid du Colombier 	uchar *va = a;
2703ff48bf5SDavid du Colombier 	int off, e;
2713ff48bf5SDavid du Colombier 
2723ff48bf5SDavid du Colombier 	off = offset;
2733ff48bf5SDavid du Colombier 
274fd597ed8SDavid du Colombier 	switch((ulong)c->qid.path){
2753ff48bf5SDavid du Colombier 	case Qdir:
2763ff48bf5SDavid du Colombier 		return devdirread(c, a, n, lm78dir, nelem(lm78dir), devgen);
2773ff48bf5SDavid du Colombier 
2783ff48bf5SDavid du Colombier 	case Qlm78vram:
2793ff48bf5SDavid du Colombier 		if(off >=  VRsize)
2803ff48bf5SDavid du Colombier 			return 0;
2813ff48bf5SDavid du Colombier 		e = off + n;
2823ff48bf5SDavid du Colombier 		if(e > VRsize)
2833ff48bf5SDavid du Colombier 			e = VRsize;
2843ff48bf5SDavid du Colombier 		for(; off < e; off++)
2853ff48bf5SDavid du Colombier 			*va++ = lm78rdreg(Rvalue+off);
286fd597ed8SDavid du Colombier 		return (int)(va - (uchar*)a);
2873ff48bf5SDavid du Colombier 	}
2883ff48bf5SDavid du Colombier 	return 0;
2893ff48bf5SDavid du Colombier }
2903ff48bf5SDavid du Colombier 
2913ff48bf5SDavid du Colombier static long
lm78write(Chan * c,void * a,long n,vlong offset)2923ff48bf5SDavid du Colombier lm78write(Chan *c, void *a, long n, vlong offset)
2933ff48bf5SDavid du Colombier {
2943ff48bf5SDavid du Colombier 	uchar *va = a;
2953ff48bf5SDavid du Colombier 	int off, e;
2963ff48bf5SDavid du Colombier 
2973ff48bf5SDavid du Colombier 	off = offset;
2983ff48bf5SDavid du Colombier 
299fd597ed8SDavid du Colombier 	switch((ulong)c->qid.path){
3003ff48bf5SDavid du Colombier 	default:
3013ff48bf5SDavid du Colombier 		error(Eperm);
3023ff48bf5SDavid du Colombier 
3033ff48bf5SDavid du Colombier 	case Qlm78vram:
3043ff48bf5SDavid du Colombier 		if(off >=  VRsize)
3053ff48bf5SDavid du Colombier 			return 0;
3063ff48bf5SDavid du Colombier 		e = off + n;
3073ff48bf5SDavid du Colombier 		if(e > VRsize)
3083ff48bf5SDavid du Colombier 			e = VRsize;
3093ff48bf5SDavid du Colombier 		for(; off < e; off++)
3103ff48bf5SDavid du Colombier 			lm78wrreg(Rvalue+off, *va++);
3113ff48bf5SDavid du Colombier 		return va - (uchar*)a;
3123ff48bf5SDavid du Colombier 	}
3133ff48bf5SDavid du Colombier 	return 0;
3143ff48bf5SDavid du Colombier }
3153ff48bf5SDavid du Colombier 
316fd597ed8SDavid du Colombier extern Dev lm78devtab;
317fd597ed8SDavid du Colombier 
318fd597ed8SDavid du Colombier static Chan*
lm78attach(char * spec)319fd597ed8SDavid du Colombier lm78attach(char* spec)
320fd597ed8SDavid du Colombier {
321fd597ed8SDavid du Colombier 	lm78enable();
322fd597ed8SDavid du Colombier 
323fd597ed8SDavid du Colombier 	return devattach(lm78devtab.dc, spec);
324fd597ed8SDavid du Colombier }
325fd597ed8SDavid du Colombier 
3263ff48bf5SDavid du Colombier Dev lm78devtab = {
3273ff48bf5SDavid du Colombier 	'T',
3283ff48bf5SDavid du Colombier 	"lm78",
3293ff48bf5SDavid du Colombier 
3303ff48bf5SDavid du Colombier 	lm78reset,
3313ff48bf5SDavid du Colombier 	devinit,
332fd597ed8SDavid du Colombier 	devshutdown,
3333ff48bf5SDavid du Colombier 	lm78attach,
3343ff48bf5SDavid du Colombier 	lm78walk,
3353ff48bf5SDavid du Colombier 	lm78stat,
3363ff48bf5SDavid du Colombier 	lm78open,
3373ff48bf5SDavid du Colombier 	devcreate,
3383ff48bf5SDavid du Colombier 	lm78close,
3393ff48bf5SDavid du Colombier 	lm78read,
3403ff48bf5SDavid du Colombier 	devbread,
3413ff48bf5SDavid du Colombier 	lm78write,
3423ff48bf5SDavid du Colombier 	devbwrite,
3433ff48bf5SDavid du Colombier 	devremove,
3443ff48bf5SDavid du Colombier 	devwstat,
3453ff48bf5SDavid du Colombier };
3463ff48bf5SDavid du Colombier 
347