FreeBSD/Linux Kernel Cross Reference
sys/pc/piix4smbus.c
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
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*
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 }
Cache object: 2c3044dddfffb55a1a432faeeed65653
|