17dd7cddfSDavid du Colombier #include "u.h"
27dd7cddfSDavid du Colombier #include "../port/lib.h"
37dd7cddfSDavid du Colombier #include "mem.h"
47dd7cddfSDavid du Colombier #include "dat.h"
57dd7cddfSDavid du Colombier #include "fns.h"
67dd7cddfSDavid du Colombier #include "io.h"
77dd7cddfSDavid du Colombier
8*41dd6b47SDavid du Colombier /*
9*41dd6b47SDavid du Colombier * SMBus support for the PIIX4
10*41dd6b47SDavid du Colombier */
117dd7cddfSDavid du Colombier enum
127dd7cddfSDavid du Colombier {
137dd7cddfSDavid du Colombier IntelVendID= 0x8086,
147dd7cddfSDavid du Colombier Piix4PMID= 0x7113, /* PIIX4 power management function */
157dd7cddfSDavid du Colombier
16*41dd6b47SDavid du Colombier /* SMBus configuration registers (function 3) */
17*41dd6b47SDavid du Colombier SMBbase= 0x90, /* 4 byte base address (bit 0 == 1, bit 3:1 == 0) */
187dd7cddfSDavid du Colombier SMBconfig= 0xd2,
197dd7cddfSDavid du Colombier SMBintrselect= (7<<1),
20*41dd6b47SDavid du Colombier SMIenable= (0<<1), /* interrupts sent to SMI# */
21*41dd6b47SDavid du Colombier IRQ9enable= (4<<1), /* interrupts sent to IRQ9 */
22*41dd6b47SDavid du Colombier SMBenable= (1<<0), /* 1 enables */
237dd7cddfSDavid du Colombier
24*41dd6b47SDavid du Colombier /* SMBus IO space registers */
25*41dd6b47SDavid du Colombier Hoststatus= 0x0, /* (writing 1 bits reset the interrupt bits) */
26*41dd6b47SDavid du Colombier Failed= (1<<4), /* transaction terminated by KILL */
27*41dd6b47SDavid du Colombier Bus_error= (1<<3), /* transaction collision */
28*41dd6b47SDavid du Colombier Dev_error= (1<<2), /* device error interrupt */
29*41dd6b47SDavid du Colombier Host_complete= (1<<1), /* host command completion interrupt */
30*41dd6b47SDavid du Colombier Host_busy= (1<<0), /* */
31*41dd6b47SDavid du Colombier Slavestatus= 0x1, /* (writing 1 bits reset) */
32*41dd6b47SDavid du Colombier Alert_sts= (1<<5), /* someone asserted SMBALERT# */
33*41dd6b47SDavid du Colombier Shdw2_sts= (1<<4), /* slave accessed shadow 2 port */
34*41dd6b47SDavid du Colombier Shdw1_sts= (1<<3), /* slave accessed shadow 1 port */
35*41dd6b47SDavid du Colombier Slv_sts= (1<<2), /* slave accessed shadow 1 port */
367dd7cddfSDavid du Colombier Slv_bsy= (1<<0),
377dd7cddfSDavid du Colombier Hostcontrol= 0x2,
38*41dd6b47SDavid du Colombier Start= (1<<6), /* start execution */
39*41dd6b47SDavid du Colombier Cmd_prot= (7<<2), /* command protocol mask */
40*41dd6b47SDavid du Colombier Quick= (0<<2), /* address only */
41*41dd6b47SDavid du Colombier Byte= (1<<2), /* address + cmd */
42*41dd6b47SDavid du Colombier ByteData= (2<<2), /* address + cmd + data */
43*41dd6b47SDavid du Colombier WordData= (3<<2), /* address + cmd + data + data */
44*41dd6b47SDavid du Colombier Kill= (1<<1), /* abort in progress command */
45*41dd6b47SDavid du Colombier Ienable= (1<<0), /* enable completion interrupts */
467dd7cddfSDavid du Colombier Hostcommand= 0x3,
477dd7cddfSDavid du Colombier Hostaddress= 0x4,
48*41dd6b47SDavid du Colombier AddressMask= (0x7f<<1), /* target address */
49*41dd6b47SDavid du Colombier Read= (1<<0), /* 1 == read, 0 == write */
507dd7cddfSDavid du Colombier Hostdata0= 0x5,
517dd7cddfSDavid du Colombier Hostdata1= 0x6,
527dd7cddfSDavid du Colombier Blockdata= 0x7,
537dd7cddfSDavid du Colombier Slavecontrol= 0x8,
54*41dd6b47SDavid du Colombier Alert_en= (1<<3), /* enable inter on SMBALERT# */
55*41dd6b47SDavid du Colombier Shdw2_en= (1<<2), /* enable inter on external shadow 2 access */
56*41dd6b47SDavid du Colombier Shdw1_en= (1<<1), /* enable inter on external shadow 1 access */
57*41dd6b47SDavid du Colombier Slv_en= (1<<0), /* enable inter on access of host ctlr slave port */
587dd7cddfSDavid du Colombier Shadowcommand= 0x9,
597dd7cddfSDavid du Colombier Slaveevent= 0xa,
607dd7cddfSDavid du Colombier Slavedata= 0xc,
617dd7cddfSDavid du Colombier };
627dd7cddfSDavid du Colombier
637dd7cddfSDavid du Colombier static struct
647dd7cddfSDavid du Colombier {
657dd7cddfSDavid du Colombier int rw;
667dd7cddfSDavid du Colombier int cmd;
677dd7cddfSDavid du Colombier int len;
687dd7cddfSDavid du Colombier int proto;
697dd7cddfSDavid du Colombier } proto[] =
707dd7cddfSDavid du Colombier {
717dd7cddfSDavid du Colombier [SMBquick] { 0, 0, 0, Quick },
727dd7cddfSDavid du Colombier [SMBsend] { 0, 1, 0, Byte },
737dd7cddfSDavid du Colombier [SMBbytewrite] { 0, 1, 1, ByteData },
747dd7cddfSDavid du Colombier [SMBwordwrite] { 0, 1, 2, WordData },
757dd7cddfSDavid du Colombier [SMBrecv] { Read, 0, 1, Byte },
767dd7cddfSDavid du Colombier [SMBbyteread] { Read, 1, 1, ByteData },
777dd7cddfSDavid du Colombier [SMBwordread] { Read, 1, 2, WordData },
787dd7cddfSDavid du Colombier };
797dd7cddfSDavid du Colombier
807dd7cddfSDavid du Colombier static void
transact(SMBus * s,int type,int addr,int cmd,uchar * data)817dd7cddfSDavid du Colombier transact(SMBus *s, int type, int addr, int cmd, uchar *data)
827dd7cddfSDavid du Colombier {
837dd7cddfSDavid du Colombier int tries, status;
84fd597ed8SDavid du Colombier char err[256];
857dd7cddfSDavid du Colombier
867dd7cddfSDavid du Colombier if(type < 0 || type > nelem(proto))
877dd7cddfSDavid du Colombier panic("piix4smbus: illegal transaction type %d", type);
887dd7cddfSDavid du Colombier
897dd7cddfSDavid du Colombier if(waserror()){
907dd7cddfSDavid du Colombier qunlock(s);
917dd7cddfSDavid du Colombier nexterror();
927dd7cddfSDavid du Colombier }
937dd7cddfSDavid du Colombier qlock(s);
947dd7cddfSDavid du Colombier
95*41dd6b47SDavid du Colombier /* wait a while for the host interface to be available */
967dd7cddfSDavid du Colombier for(tries = 0; tries < 1000000; tries++){
977dd7cddfSDavid du Colombier if((inb(s->base+Hoststatus) & Host_busy) == 0)
987dd7cddfSDavid du Colombier break;
99fd597ed8SDavid du Colombier sched();
1007dd7cddfSDavid du Colombier }
1017dd7cddfSDavid du Colombier if(tries >= 1000000){
102*41dd6b47SDavid du Colombier /* try aborting current transaction */
1037dd7cddfSDavid du Colombier outb(s->base+Hostcontrol, Kill);
1047dd7cddfSDavid du Colombier for(tries = 0; tries < 1000000; tries++){
1057dd7cddfSDavid du Colombier if((inb(s->base+Hoststatus) & Host_busy) == 0)
1067dd7cddfSDavid du Colombier break;
107fd597ed8SDavid du Colombier sched();
1087dd7cddfSDavid du Colombier }
1097dd7cddfSDavid du Colombier if(tries >= 1000000){
1107dd7cddfSDavid du Colombier snprint(err, sizeof(err), "SMBus jammed: %2.2ux", inb(s->base+Hoststatus));
1117dd7cddfSDavid du Colombier error(err);
1127dd7cddfSDavid du Colombier }
1137dd7cddfSDavid du Colombier }
1147dd7cddfSDavid du Colombier
115*41dd6b47SDavid du Colombier /* set up for transaction */
1167dd7cddfSDavid du Colombier outb(s->base+Hostaddress, (addr<<1)|proto[type].rw);
1177dd7cddfSDavid du Colombier if(proto[type].cmd)
1187dd7cddfSDavid du Colombier outb(s->base+Hostcommand, cmd);
1197dd7cddfSDavid du Colombier if(proto[type].rw != Read){
1207dd7cddfSDavid du Colombier switch(proto[type].len){
1217dd7cddfSDavid du Colombier case 2:
1227dd7cddfSDavid du Colombier outb(s->base+Hostdata1, data[1]);
123*41dd6b47SDavid du Colombier /* fall through */
1247dd7cddfSDavid du Colombier case 1:
1257dd7cddfSDavid du Colombier outb(s->base+Hostdata0, data[0]);
1267dd7cddfSDavid du Colombier break;
1277dd7cddfSDavid du Colombier }
1287dd7cddfSDavid du Colombier }
1297dd7cddfSDavid du Colombier
1307dd7cddfSDavid du Colombier
131*41dd6b47SDavid du Colombier /* reset the completion/error bits and start transaction */
1327dd7cddfSDavid du Colombier outb(s->base+Hoststatus, Failed|Bus_error|Dev_error|Host_complete);
1337dd7cddfSDavid du Colombier outb(s->base+Hostcontrol, Start|proto[type].proto);
1347dd7cddfSDavid du Colombier
135*41dd6b47SDavid du Colombier /* wait for completion */
1367dd7cddfSDavid du Colombier status = 0;
1377dd7cddfSDavid du Colombier for(tries = 0; tries < 1000000; tries++){
1387dd7cddfSDavid du Colombier status = inb(s->base+Hoststatus);
1397dd7cddfSDavid du Colombier if(status & (Failed|Bus_error|Dev_error|Host_complete))
1407dd7cddfSDavid du Colombier break;
141fd597ed8SDavid du Colombier sched();
1427dd7cddfSDavid du Colombier }
1437dd7cddfSDavid du Colombier if((status & Host_complete) == 0){
1447dd7cddfSDavid du Colombier snprint(err, sizeof(err), "SMBus request failed: %2.2ux", status);
1457dd7cddfSDavid du Colombier error(err);
1467dd7cddfSDavid du Colombier }
1477dd7cddfSDavid du Colombier
148*41dd6b47SDavid du Colombier /* get results */
1497dd7cddfSDavid du Colombier if(proto[type].rw == Read){
1507dd7cddfSDavid du Colombier switch(proto[type].len){
1517dd7cddfSDavid du Colombier case 2:
1527dd7cddfSDavid du Colombier data[1] = inb(s->base+Hostdata1);
153*41dd6b47SDavid du Colombier /* fall through */
1547dd7cddfSDavid du Colombier case 1:
1557dd7cddfSDavid du Colombier data[0] = inb(s->base+Hostdata0);
1567dd7cddfSDavid du Colombier break;
1577dd7cddfSDavid du Colombier }
1587dd7cddfSDavid du Colombier }
1597dd7cddfSDavid du Colombier qunlock(s);
1607dd7cddfSDavid du Colombier poperror();
1617dd7cddfSDavid du Colombier }
1627dd7cddfSDavid du Colombier
1637dd7cddfSDavid du Colombier static SMBus smbusproto =
1647dd7cddfSDavid du Colombier {
1657dd7cddfSDavid du Colombier .transact = transact,
1667dd7cddfSDavid du Colombier };
1677dd7cddfSDavid du Colombier
168*41dd6b47SDavid du Colombier /*
169*41dd6b47SDavid du Colombier * return 0 if this is a piix4 with an smbus interface
170*41dd6b47SDavid du Colombier */
1717dd7cddfSDavid du Colombier SMBus*
piix4smbus(void)1727dd7cddfSDavid du Colombier piix4smbus(void)
1737dd7cddfSDavid du Colombier {
1747dd7cddfSDavid du Colombier Pcidev *p;
1757dd7cddfSDavid du Colombier static SMBus *s;
1767dd7cddfSDavid du Colombier
1777dd7cddfSDavid du Colombier if(s != nil)
1787dd7cddfSDavid du Colombier return s;
1797dd7cddfSDavid du Colombier
1807dd7cddfSDavid du Colombier p = pcimatch(nil, IntelVendID, Piix4PMID);
1817dd7cddfSDavid du Colombier if(p == nil)
1827dd7cddfSDavid du Colombier return nil;
1837dd7cddfSDavid du Colombier
1847dd7cddfSDavid du Colombier s = smalloc(sizeof(*s));
1857dd7cddfSDavid du Colombier memmove(s, &smbusproto, sizeof(*s));
1867dd7cddfSDavid du Colombier s->arg = p;
1877dd7cddfSDavid du Colombier
188*41dd6b47SDavid du Colombier /* disable the smbus */
1897dd7cddfSDavid du Colombier pcicfgw8(p, SMBconfig, IRQ9enable|0);
1907dd7cddfSDavid du Colombier
191*41dd6b47SDavid du Colombier /* see if bios gave us a viable port space */
1927dd7cddfSDavid du Colombier s->base = pcicfgr32(p, SMBbase) & ~1;
1937dd7cddfSDavid du Colombier print("SMB base from bios is 0x%lux\n", s->base);
1947dd7cddfSDavid du Colombier if(ioalloc(s->base, 0xd, 0, "piix4smbus") < 0){
1957dd7cddfSDavid du Colombier s->base = ioalloc(-1, 0xd, 2, "piix4smbus");
1967dd7cddfSDavid du Colombier if(s->base < 0){
1977dd7cddfSDavid du Colombier free(s);
1987dd7cddfSDavid du Colombier print("piix4smbus: can't allocate io port\n");
1997dd7cddfSDavid du Colombier return nil;
2007dd7cddfSDavid du Colombier }
2017dd7cddfSDavid du Colombier print("SMB base ialloc is 0x%lux\n", s->base);
2027dd7cddfSDavid du Colombier pcicfgw32(p, SMBbase, s->base|1);
2037dd7cddfSDavid du Colombier }
2047dd7cddfSDavid du Colombier
205*41dd6b47SDavid du Colombier /* disable SMBus interrupts, abort any transaction in progress */
2067dd7cddfSDavid du Colombier outb(s->base+Hostcontrol, Kill);
2077dd7cddfSDavid du Colombier outb(s->base+Slavecontrol, 0);
2087dd7cddfSDavid du Colombier
209*41dd6b47SDavid du Colombier /* enable the smbus */
2107dd7cddfSDavid du Colombier pcicfgw8(p, SMBconfig, IRQ9enable|SMBenable);
2117dd7cddfSDavid du Colombier
2127dd7cddfSDavid du Colombier return s;
2137dd7cddfSDavid du Colombier }
214