xref: /plan9/sys/src/9/pc/piix4smbus.c (revision 41dd6b4775bcffc7275c15aee7294944759a2ea7)
1 #include	"u.h"
2 #include	"../port/lib.h"
3 #include	"mem.h"
4 #include	"dat.h"
5 #include	"fns.h"
6 #include	"io.h"
7 
8 /*
9  *	SMBus support for the PIIX4
10  */
11 enum
12 {
13 	IntelVendID=	0x8086,
14 	Piix4PMID=	0x7113,		/* PIIX4 power management function */
15 
16 	/* SMBus configuration registers (function 3) */
17 	SMBbase=	0x90, /* 4 byte base address (bit 0 == 1, bit 3:1 == 0) */
18 	SMBconfig=	0xd2,
19 	SMBintrselect=	(7<<1),
20 	SMIenable=	(0<<1),		/* interrupts sent to SMI# */
21 	IRQ9enable=	(4<<1),		/* interrupts sent to IRQ9 */
22 	SMBenable=	(1<<0),		/* 1 enables */
23 
24 	/* SMBus IO space registers */
25 	Hoststatus=	0x0,	/* (writing 1 bits reset the interrupt bits) */
26 	Failed=		(1<<4),		/* transaction terminated by KILL */
27 	Bus_error=	(1<<3),		/* transaction collision */
28 	Dev_error=	(1<<2),		/* device error interrupt */
29 	Host_complete=	(1<<1),		/* host command completion interrupt */
30 	Host_busy=	(1<<0),		/*  */
31 	Slavestatus=	0x1,	/* (writing 1 bits reset) */
32 	Alert_sts=	(1<<5),		/* someone asserted SMBALERT# */
33 	Shdw2_sts=	(1<<4),		/* slave accessed shadow 2 port */
34 	Shdw1_sts=	(1<<3),		/* slave accessed shadow 1 port */
35 	Slv_sts=	(1<<2),		/* slave accessed shadow 1 port */
36 	Slv_bsy=	(1<<0),
37 	Hostcontrol=	0x2,
38 	Start=		(1<<6),		/* start execution */
39 	Cmd_prot=	(7<<2),		/* command protocol mask */
40 	Quick=		(0<<2),		/*  address only */
41 	Byte=		(1<<2),		/*  address + cmd */
42 	ByteData=	(2<<2),		/*  address + cmd + data */
43 	WordData=	(3<<2),		/*  address + cmd + data + data */
44 	Kill=		(1<<1),		/* abort in progress command */
45 	Ienable=	(1<<0),		/* enable completion interrupts */
46 	Hostcommand=	0x3,
47 	Hostaddress=	0x4,
48 	AddressMask=	(0x7f<<1),	/* target address */
49 	Read=		(1<<0),		/* 1 == read, 0 == write */
50 	Hostdata0=	0x5,
51 	Hostdata1=	0x6,
52 	Blockdata=	0x7,
53 	Slavecontrol=	0x8,
54 	Alert_en=	(1<<3),	/* enable inter on SMBALERT# */
55 	Shdw2_en=	(1<<2),	/* enable inter on external shadow 2 access */
56 	Shdw1_en=	(1<<1),	/* enable inter on external shadow 1 access */
57 	Slv_en=		(1<<0),	/* enable inter on access of host ctlr slave port */
58 	Shadowcommand=	0x9,
59 	Slaveevent=	0xa,
60 	Slavedata=	0xc,
61 };
62 
63 static struct
64 {
65 	int	rw;
66 	int	cmd;
67 	int	len;
68 	int	proto;
69 } proto[] =
70 {
71 	[SMBquick]	{ 0,	0,	0,	Quick },
72 	[SMBsend]	{ 0,	1,	0,	Byte },
73 	[SMBbytewrite]	{ 0,	1,	1,	ByteData },
74 	[SMBwordwrite]	{ 0,	1,	2,	WordData },
75 	[SMBrecv]	{ Read,	0,	1, 	Byte },
76 	[SMBbyteread]	{ Read,	1,	1,	ByteData },
77 	[SMBwordread]	{ Read,	1,	2,	WordData },
78 };
79 
80 static void
transact(SMBus * s,int type,int addr,int cmd,uchar * data)81 transact(SMBus *s, int type, int addr, int cmd, uchar *data)
82 {
83 	int tries, status;
84 	char err[256];
85 
86 	if(type < 0 || type > nelem(proto))
87 		panic("piix4smbus: illegal transaction type %d", type);
88 
89 	if(waserror()){
90 		qunlock(s);
91 		nexterror();
92 	}
93 	qlock(s);
94 
95 	/* wait a while for the host interface to be available */
96 	for(tries = 0; tries < 1000000; tries++){
97 		if((inb(s->base+Hoststatus) & Host_busy) == 0)
98 			break;
99 		sched();
100 	}
101 	if(tries >= 1000000){
102 		/* try aborting current transaction */
103 		outb(s->base+Hostcontrol, Kill);
104 		for(tries = 0; tries < 1000000; tries++){
105 			if((inb(s->base+Hoststatus) & Host_busy) == 0)
106 				break;
107 			sched();
108 		}
109 		if(tries >= 1000000){
110 			snprint(err, sizeof(err), "SMBus jammed: %2.2ux", inb(s->base+Hoststatus));
111 			error(err);
112 		}
113 	}
114 
115 	/* set up for transaction */
116 	outb(s->base+Hostaddress, (addr<<1)|proto[type].rw);
117 	if(proto[type].cmd)
118 		outb(s->base+Hostcommand, cmd);
119 	if(proto[type].rw != Read){
120 		switch(proto[type].len){
121 		case 2:
122 			outb(s->base+Hostdata1, data[1]);
123 			/* fall through */
124 		case 1:
125 			outb(s->base+Hostdata0, data[0]);
126 			break;
127 		}
128 	}
129 
130 
131 	/* reset the completion/error bits and start transaction */
132 	outb(s->base+Hoststatus, Failed|Bus_error|Dev_error|Host_complete);
133 	outb(s->base+Hostcontrol, Start|proto[type].proto);
134 
135 	/* wait for completion */
136 	status = 0;
137 	for(tries = 0; tries < 1000000; tries++){
138 		status = inb(s->base+Hoststatus);
139 		if(status & (Failed|Bus_error|Dev_error|Host_complete))
140 			break;
141 		sched();
142 	}
143 	if((status & Host_complete) == 0){
144 		snprint(err, sizeof(err), "SMBus request failed: %2.2ux", status);
145 		error(err);
146 	}
147 
148 	/* get results */
149 	if(proto[type].rw == Read){
150 		switch(proto[type].len){
151 		case 2:
152 			data[1] = inb(s->base+Hostdata1);
153 			/* fall through */
154 		case 1:
155 			data[0] = inb(s->base+Hostdata0);
156 			break;
157 		}
158 	}
159 	qunlock(s);
160 	poperror();
161 }
162 
163 static SMBus smbusproto =
164 {
165 	.transact = transact,
166 };
167 
168 /*
169  *  return 0 if this is a piix4 with an smbus interface
170  */
171 SMBus*
piix4smbus(void)172 piix4smbus(void)
173 {
174 	Pcidev *p;
175 	static SMBus *s;
176 
177 	if(s != nil)
178 		return s;
179 
180 	p = pcimatch(nil, IntelVendID, Piix4PMID);
181 	if(p == nil)
182 		return nil;
183 
184 	s = smalloc(sizeof(*s));
185 	memmove(s, &smbusproto, sizeof(*s));
186 	s->arg = p;
187 
188 	/* disable the smbus */
189 	pcicfgw8(p, SMBconfig, IRQ9enable|0);
190 
191 	/* see if bios gave us a viable port space */
192 	s->base = pcicfgr32(p, SMBbase) & ~1;
193 print("SMB base from bios is 0x%lux\n", s->base);
194 	if(ioalloc(s->base, 0xd, 0, "piix4smbus") < 0){
195 		s->base = ioalloc(-1, 0xd, 2, "piix4smbus");
196 		if(s->base < 0){
197 			free(s);
198 			print("piix4smbus: can't allocate io port\n");
199 			return nil;
200 		}
201 print("SMB base ialloc is 0x%lux\n", s->base);
202 		pcicfgw32(p, SMBbase, s->base|1);
203 	}
204 
205 	/* disable SMBus interrupts, abort any transaction in progress */
206 	outb(s->base+Hostcontrol, Kill);
207 	outb(s->base+Slavecontrol, 0);
208 
209 	/* enable the smbus */
210 	pcicfgw8(p, SMBconfig, IRQ9enable|SMBenable);
211 
212 	return s;
213 }
214