From c8a2f948ae9e5c2ea4bda742e879302a8133cb5d Mon Sep 17 00:00:00 2001 From: David du Colombier <0intro@gmail.com> Date: Sun, 25 Jul 1999 00:00:00 +0000 Subject: [PATCH] Plan 9 from Bell Labs 1999-07-25 --- pc/devlm78.c | 217 ++++++++++++++++++++++++++++++++++++++++++++---- pc/piix4smbus.c | 73 +++++++++------- 2 files changed, 244 insertions(+), 46 deletions(-) diff --git a/pc/devlm78.c b/pc/devlm78.c index 5e59d7a5e009d17b9952699bf6fe4320319b2c92..bdcd58564bc9249f5507581556f4270c57493d89 100644 --- a/pc/devlm78.c +++ b/pc/devlm78.c @@ -7,30 +7,175 @@ #include "ureg.h" #include "../port/error.h" -enum { +// this driver assumes that noone has changed the serial address +// of the device. if they have, there's no way we can figure it +// out -- presotto + +enum +{ + // address of chip on serial interface + Serialaddr= 0x2d, + + // internal register addresses + Rconfig= 0x40, + 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, - Qtemp, + Qlm78vram, }; static Dirtab lm78dir[] = { - "temp", { Qtemp, 0 }, 0, 0444, + "lm78vram", { Qlm78vram, 0 }, 0, 0444, +}; + +// interface type +enum +{ + None= 0, + Smbus, + Parallel, }; -SMBus *lm78smbus; +static struct { + QLock; + + int ifc; + + // serial interface + SMBus *smbus; + + // parallel interface + int port; +} lm78; extern SMBus* piix4smbus(void); +// routines that actually touch the device +static int +lm78wrreg(int reg, uchar val) +{ + switch(lm78.ifc){ + case Smbus: + lm78.smbus->transact(lm78.smbus, SMBbytewrite, Serialaddr, reg, &val); + return 0; + } + return -1; +} + +static int +lm78rdreg(int reg) +{ + uchar rv; + + switch(lm78.ifc){ + case Smbus: + lm78.smbus->transact(lm78.smbus, SMBsend, Serialaddr, reg, nil); + lm78.smbus->transact(lm78.smbus, SMBrecv, Serialaddr, 0, &rv); + return rv; + } + return -1; +} + +static int +lm78probe(void) +{ + switch(lm78.ifc){ + case Smbus: + if(lm78rdreg(Raddr) != Serialaddr){ + lm78.ifc = None; + break; + } + return 0; + } + return -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 and if there's an lm78 there void -lm78init(void) +lm78reset(void) { - lm78smbus = piix4smbus(); - if(lm78smbus != nil) - print("found piix4 smbus, base %lud\n", lm78smbus->base); + 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) { + static int probed; + + if(lm78.ifc == None) + error(Enodev); + + if(probed == 0){ + if(lm78probe() < 0) + error(Enodev); + probed = 1; + } return devattach('T', spec); } @@ -65,13 +210,31 @@ enum static long lm78read(Chan *c, void *a, long n, vlong offset) { - switch(c->qid.path & ~CHDIR){ + 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 Qtemp: - error(Eperm); + case Qlm78vram: + if(off >= VRsize) + return 0; + if(waserror()){ + qunlock(&lm78); + nexterror(); + } + qlock(&lm78); + e = off + n; + if(e > VRsize) + e = VRsize; + for(; off < e; off++) + *va++ = lm78rdreg(off); + qunlock(&lm78); + poperror(); + return va - (uchar*)a; } return 0; } @@ -79,7 +242,32 @@ lm78read(Chan *c, void *a, long n, vlong offset) static long lm78write(Chan *c, void *a, long n, vlong offset) { - error(Eperm); + uchar *va = a; + int off, e; + + off = offset; + + switch(c->qid.path){ + default: + error(Eperm); + + case Qlm78vram: + if(off >= VRsize) + return 0; + if(waserror()){ + qunlock(&lm78); + nexterror(); + } + qlock(&lm78); + e = off + n; + if(e > VRsize) + e = VRsize; + for(; off < e; off++) + lm78wrreg(off, *va++); + qunlock(&lm78); + poperror(); + return va - (uchar*)a; + } return 0; } @@ -87,8 +275,8 @@ Dev lm78devtab = { 'T', "lm78", - devreset, - lm78init, + lm78reset, + devinit, lm78attach, devclone, lm78walk, @@ -103,3 +291,4 @@ Dev lm78devtab = { devremove, devwstat, }; + diff --git a/pc/piix4smbus.c b/pc/piix4smbus.c index d1c07de2048e50b91a086fd03f37bbe5e7264fa2..58489aa7eed44c59f6da75fe36ad2514e82deca3 100644 --- a/pc/piix4smbus.c +++ b/pc/piix4smbus.c @@ -26,7 +26,7 @@ enum Failed= (1<<4), // transaction terminated by KILL Bus_error= (1<<3), // transactio collision Dev_error= (1<<2), // device error interrupt - Host_complete= (1<<2), // host command completion interrupt + Host_complete= (1<<1), // host command completion interrupt Host_busy= (1<<0), // Slavestatus= 0x1, // (writing 1 bits reset) Alert_sts= (1<<5), // someone asserted SMBALERT# @@ -72,28 +72,15 @@ static struct [SMBsend] { 0, 1, 0, Byte }, [SMBbytewrite] { 0, 1, 1, ByteData }, [SMBwordwrite] { 0, 1, 2, WordData }, - [SMBrecv] { Read, 1, 0, Byte }, + [SMBrecv] { Read, 0, 1, Byte }, [SMBbyteread] { Read, 1, 1, ByteData }, [SMBwordread] { Read, 1, 2, WordData }, }; -static int -spin(Pcidev *p, int bit) -{ - int tries; - - for(tries = 0; tries < 1000000; tries++){ - if((pcicfgr8(p, Hoststatus) & bit) == 0) - return 0; - sched(); - } - return -1; -} - static void transact(SMBus *s, int type, int addr, int cmd, uchar *data) { - Pcidev *p = s->arg; + int tries; if(type < 0 || type > nelem(proto)) panic("piix4smbus: illegal transaction type %d", type); @@ -105,48 +92,70 @@ transact(SMBus *s, int type, int addr, int cmd, uchar *data) qlock(s); // wait a while for the host interface to be available - if(spin(p, Host_busy) != 0){ + for(tries = 0; tries < 1000000; tries++){ + if((inb(s->base+Hoststatus) & Host_busy) == 0) + break; + sched(); + } + if(tries >= 1000000){ // try aborting current transaction - pcicfgw8(p, Hostcontrol, Kill); - if(spin(p, Host_busy) < 0) - error("SMBus broken"); + outb(s->base+Hostcontrol, Kill); + for(tries = 0; tries < 1000000; tries++){ + if((inb(s->base+Hoststatus) & Host_busy) == 0) + break; + sched(); + } + if(tries >= 1000000){ + print("SMBus status: %2.2ux", inb(s->base+Hoststatus)); + error("SMBus broken1"); + } } // set up for transaction - pcicfgw8(p, Hostaddress, (addr<<1)|proto[type].rw); + outb(s->base+Hostaddress, (addr<<1)|proto[type].rw); if(proto[type].cmd) - pcicfgw8(p, Hostcommand, cmd); + outb(s->base+Hostcommand, cmd); if(proto[type].rw != Read){ switch(proto[type].len){ case 2: - pcicfgw8(p, Hostdata1, data[1]); + outb(s->base+Hostdata1, data[1]); // fall through case 1: - pcicfgw8(p, Hostdata0, data[0]); + outb(s->base+Hostdata0, data[0]); break; } } // reset the completion bit and start transaction - pcicfgw8(p, Hoststatus, Host_complete); - pcicfgw8(p, Hostcontrol, Start|proto[type].proto); + outb(s->base+Hoststatus, Host_complete); +print("before %2.2ux\n", inb(s->base+Hoststatus)); + outb(s->base+Hostcontrol, Start|proto[type].proto); +print("after %2.2ux\n", inb(s->base+Hoststatus)); // wait for completion - if(spin(p, Host_complete) < 0) - error("SMBus broken"); + for(tries = 0; tries < 1000000; tries++){ + if(inb(s->base+Hoststatus) & Host_complete) + break; + sched(); + } + if(tries >= 1000000){ + print("SMBus status: %2.2ux", inb(s->base+Hoststatus)); + error("SMBus broken2"); + } // get results if(proto[type].rw == Read){ switch(proto[type].len){ case 2: - data[1] = pcicfgr8(p, Hostdata1); + data[1] = inb(s->base+Hostdata1); // fall through case 1: - data[0] = pcicfgr8(p, Hostdata0); + data[0] = inb(s->base+Hostdata0); break; } } qunlock(s); + poperror(); } static SMBus smbusproto = @@ -192,8 +201,8 @@ print("SMB base ialloc is 0x%lux\n", s->base); } // disable SMBus interrupts, abort any transaction in progress - pcicfgw8(p, Hostcontrol, Kill); - pcicfgw8(p, Slavecontrol, 0); + outb(s->base+Hostcontrol, Kill); + outb(s->base+Slavecontrol, 0); // enable the smbus pcicfgw8(p, SMBconfig, IRQ9enable|SMBenable);