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