123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342 |
- #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"
- // 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.
- enum
- {
- // address of chip on serial interface
- Serialaddr= 0x2d,
- // parallel access registers
- Rpaddr= 0x5,
- Bbusy= (1<<7),
- Rpdata= 0x6,
- // internal register addresses
- Rconfig= 0x40,
- 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),
- 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,
- Qlm78vram,
- };
- static Dirtab lm78dir[] = {
- "lm78vram", { Qlm78vram, 0 }, 0, 0444,
- };
- // interface type
- enum
- {
- None= 0,
- Smbus,
- Parallel,
- };
- static struct {
- QLock;
- int probed;
- int ifc; // which interface is connected
- SMBus *smbus; // serial interface
- int port; // parallel interface
- } lm78;
- extern SMBus* piix4smbus(void);
- // 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");
- }
- // routines that actually touch the device
- static void
- lm78wrreg(int reg, uchar val)
- {
- if(waserror()){
- qunlock(&lm78);
- nexterror();
- }
- qlock(&lm78);
- switch(lm78.ifc){
- case Smbus:
- lm78.smbus->transact(lm78.smbus, SMBbytewrite, Serialaddr, reg, &val);
- break;
- case Parallel:
- setreg(reg);
- outb(lm78.port+Rpdata, val);
- break;
- default:
- error(Enodev);
- break;
- }
- qunlock(&lm78);
- poperror();
- }
- static int
- lm78rdreg(int reg)
- {
- uchar val;
- if(waserror()){
- qunlock(&lm78);
- nexterror();
- }
- qlock(&lm78);
- switch(lm78.ifc){
- case Smbus:
- lm78.smbus->transact(lm78.smbus, SMBsend, Serialaddr, reg, nil);
- lm78.smbus->transact(lm78.smbus, SMBrecv, Serialaddr, 0, &val);
- break;
- case Parallel:
- setreg(reg);
- val = inb(lm78.port+Rpdata);
- break;
- default:
- error(Enodev);
- break;
- }
- qunlock(&lm78);
- poperror();
- return val;
- }
- // 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)
- {
- uchar config;
- if(lm78.ifc == None)
- error(Enodev);
- if(lm78.probed == 0){
- // make sure its really there
- if(lm78rdreg(Raddr) != Serialaddr){
- lm78.ifc = None;
- error(Enodev);
- } else {
- // start the sampling
- config = lm78rdreg(Rconfig);
- config = (config | Bstart) & ~(Bintclr|Binit);
- lm78wrreg(Rconfig, config);
- pprint("Rvidfan %2.2ux\n", lm78rdreg(Rconfig), lm78rdreg(Rvidfan));
- }
- lm78.probed = 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 we could have
- void
- lm78reset(void)
- {
- 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)
- {
- lm78enable();
- return devattach('T', spec);
- }
- int
- lm78walk(Chan* c, char* name)
- {
- return devwalk(c, name, lm78dir, nelem(lm78dir), devgen);
- }
- static void
- lm78stat(Chan* c, char* dp)
- {
- devstat(c, dp, lm78dir, nelem(lm78dir), devgen);
- }
- static Chan*
- lm78open(Chan* c, int omode)
- {
- return devopen(c, omode, lm78dir, nelem(lm78dir), devgen);
- }
- static void
- lm78close(Chan*)
- {
- }
- enum
- {
- Linelen= 25,
- };
- static long
- lm78read(Chan *c, void *a, long n, vlong offset)
- {
- 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 Qlm78vram:
- if(off >= VRsize)
- return 0;
- e = off + n;
- if(e > VRsize)
- e = VRsize;
- for(; off < e; off++)
- *va++ = lm78rdreg(Rvalue+off);
- return va - (uchar*)a;
- }
- return 0;
- }
- static long
- lm78write(Chan *c, void *a, long n, vlong offset)
- {
- 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++)
- lm78wrreg(Rvalue+off, *va++);
- return va - (uchar*)a;
- }
- return 0;
- }
- Dev lm78devtab = {
- 'T',
- "lm78",
- lm78reset,
- devinit,
- lm78attach,
- devclone,
- lm78walk,
- lm78stat,
- lm78open,
- devcreate,
- lm78close,
- lm78read,
- devbread,
- lm78write,
- devbwrite,
- devremove,
- devwstat,
- };
|