2 #include "../port/lib.h"
8 #include "../port/pci.h"
9 #include "../port/error.h"
11 /* this driver doesn't implement the management interrupts. we
12 * leave the LM78 interrupts set to whatever the BIOS did. we do
13 * allow reading and writing the the readouts and alarm values.
14 * Read(2)ing or write(2)ing at offset 0x0-0x1f, is
15 * equivalent to reading or writing lm78 registers 0x20-0x3f.
19 /* address of chip on serial interface */
22 /* parallel access registers */
27 /* internal register addresses */
34 Bnmi= (1<<5), /* if set, use nmi, else irq */
43 Rvidfan= 0x47, /* set fan counter, and read voltage level */
46 Raddr= 0x48, /* address used on serial bus */
47 Rresetid= 0x49, /* chip reset and ID register */
48 Rpost= 0x00, /* start of post ram */
49 Rvalue= 0x20, /* start of value ram */
51 VRsize= 0x20, /* size of value ram */
60 static Dirtab lm78dir[] = {
61 ".", { Qdir, 0, QTDIR}, 0, 0555,
62 "lm78vram", { Qlm78vram, 0 }, 0, 0444,
76 int ifc; /* which interface is connected */
77 SMBus *smbus; /* serial interface */
78 int port; /* parallel interface */
81 extern SMBus* piix4smbus(void);
83 /* wait for device to become quiescent and then set the */
84 /* register address */
90 for(tries = 0; tries < 1000000; tries++)
91 if((inb(lm78.port+Rpaddr) & Bbusy) == 0){
92 outb(lm78.port+Rpaddr, reg);
98 /* routines that actually touch the device */
100 lm78wrreg(int reg, uchar val)
110 lm78.smbus->transact(lm78.smbus, SMBbytewrite, Serialaddr, reg, &val);
114 outb(lm78.port+Rpdata, val);
138 lm78.smbus->transact(lm78.smbus, SMBsend, Serialaddr, reg, nil);
139 lm78.smbus->transact(lm78.smbus, SMBrecv, Serialaddr, 0, &val);
143 val = inb(lm78.port+Rpdata);
155 /* start the chip monitoring but don't change any smi
156 * interrupts and/or alarms that the BIOS may have set up.
157 * this isn't locked because it's thought to be idempotent
167 if(lm78.probed == 0){
168 /* make sure its really there */
169 if(lm78rdreg(Raddr) != Serialaddr){
173 /* start the sampling */
174 config = lm78rdreg(Rconfig);
175 config = (config | Bstart) & ~(Bintclr|Binit);
176 lm78wrreg(Rconfig, config);
177 pprint("Rvidfan %2.2ux\n", lm78rdreg(Rconfig), lm78rdreg(Rvidfan));
189 Piix4PMID= 0x7113, /* PIIX4 power management function */
191 PCSC= 0x78, /* programmable chip select control register */
195 /* figure out what kind of interface we could have */
204 while((p = pcimatch(p, IntelVendID, 0)) != nil){
206 /* these bridges use the PCSC to map the lm78 into port space. */
207 /* for this case the lm78's CS# select is connected to the PIIX's */
208 /* PCS# output and the bottom 3 bits of address are passed to the */
209 /* LM78's A0-A2 inputs. */
212 pcs = pcicfgr16(p, PCSC);
214 /* already enabled */
215 lm78.port = pcs & ~3;
220 /* enable the chip, use default address 0x50 */
221 pcicfgw16(p, PCSC, 0x50|PCSC8bytes);
222 pcs = pcicfgr16(p, PCSC);
223 lm78.port = pcs & ~3;
227 /* this bridge puts the lm78's serial interface on the smbus */
229 lm78.smbus = piix4smbus();
230 if(lm78.smbus == nil)
232 print("found piix4 smbus, base %lud\n", lm78.smbus->base);
240 lm78walk(Chan* c, Chan *nc, char** name, int nname)
242 return devwalk(c, nc, name, nname, lm78dir, nelem(lm78dir), devgen);
246 lm78stat(Chan* c, uchar* dp, int n)
248 return devstat(c, dp, n, lm78dir, nelem(lm78dir), devgen);
252 lm78open(Chan* c, int omode)
254 return devopen(c, omode, lm78dir, nelem(lm78dir), devgen);
268 lm78read(Chan *c, void *a, long n, vlong offset)
275 switch((ulong)c->qid.path){
277 return devdirread(c, a, n, lm78dir, nelem(lm78dir), devgen);
285 for(; off < e; off++)
286 *va++ = lm78rdreg(Rvalue+off);
287 return (int)(va - (uchar*)a);
293 lm78write(Chan *c, void *a, long n, vlong offset)
300 switch((ulong)c->qid.path){
310 for(; off < e; off++)
311 lm78wrreg(Rvalue+off, *va++);
312 return va - (uchar*)a;
317 extern Dev lm78devtab;
320 lm78attach(char* spec)
324 return devattach(lm78devtab.dc, spec);