xref: /plan9-contrib/sys/src/9/pc/piix4smbus.c (revision 41dd6b4775bcffc7275c15aee7294944759a2ea7)
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