-
Notifications
You must be signed in to change notification settings - Fork 2
/
Copy pathsmbus.cs
309 lines (254 loc) · 12.7 KB
/
smbus.cs
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
using System.Threading;
using OpenLibSys;
namespace SetFSB{
public class smBus{
private const byte I801_BLOCK_DATA = 0x14;
private const byte I801_BLOCK_LAST = 0x34;
private const byte I801_START = 0x40;
private const uint ICH7_PCI_ID = 0x27da;
private const uint ICH8_PCI_ID = 0x283e;
private const uint ICH9_PCI_ID = 0x2930;
private const uint INTEL_PCI_VENDOR_ID = 0x8086;
private const uint MAX_TIMEOUT = 100;
private const byte PCI_BASE_ADDRESS_4 = 0x20;
private const uint PCI_BASE_ADDRESS_SPACE_IO = 0x01;
private const byte PCI_COMMAND = 0x04; /* 16 bits */
private const ushort PCI_COMMAND_IO = 0x1; /* Enable response in I/O space */
private const byte PCI_DEVICE_ID = 0x02;
private const byte PCI_VENDOR_ID = 0x00;
private const ushort SMBAUXCTL = SMBUS_IO_BASE + 13;
private const uint SMBAUXCTL_CRC = 1;
private const uint SMBAUXCTL_E32B = 2;
private const ushort SMBBLKDAT = SMBUS_IO_BASE + 7;
private const ushort SMBHSTADD = SMBUS_IO_BASE + 4;
private const byte SMBHSTCFG = 0x40;
private const byte SMBHSTCFG_HST_EN = 1;
private const ushort SMBHSTCMD = SMBUS_IO_BASE + 3;
private const ushort SMBHSTCNT = SMBUS_IO_BASE + 2;
private const byte SMBHSTCNT_KILL = 2;
private const ushort SMBHSTDAT0 = SMBUS_IO_BASE + 5;
private const ushort SMBHSTSTS = SMBUS_IO_BASE;
private const byte SMBHSTSTS_BUS_ERR = 0x08;
private const byte SMBHSTSTS_BYTE_DONE = 0x80;
private const byte SMBHSTSTS_DEV_ERR = 0x04;
private const byte SMBHSTSTS_FAILED = 0x10;
private const byte SMBHSTSTS_HOST_BUSY = 0x1;
private const byte SMBHSTSTS_INTR = 0x02;
private const byte SMBHSTSTS_INUSE_STS = 0x40;
private const ushort SMBUS_IO_BASE = 0x400;
private const byte STATUS_FLAGS = SMBHSTSTS_BYTE_DONE | SMBHSTSTS_FAILED | SMBHSTSTS_BUS_ERR | SMBHSTSTS_DEV_ERR | SMBHSTSTS_INTR;
private static Ols ols;
public smBus(Ols openLibSys){
ols = openLibSys;
}
private static uint grub_pci_make_address(uint bus, uint device, uint function){
return (bus << 8) | (device << 3) | (function);
}
public void enable_smbus(){
// some BIOSes disable SMBus. Turn it back on, even though the ICH8 manual says
// you shouldn't, maybe because they never saw the need and don't to proper reinitialization,
// leaving the device in an inconsistent state?
uint rcba = ols.ReadPciConfigDword(grub_pci_make_address(0, 0x1f, 0), 0xf0);
// if RCBA isn't memory mapped, there's little we can do - we could
// set the bit that turns on the mapping, but that's chipset specific
// and usually is locked (D_LCK bit set)
if ((rcba & 1) != 0u) // if mapped
{
/** does not work in c#
rcba &= 0xffffc000;
//grub_printf("rcba: %x\n", rcba);
int func_disable = (int) (rcba + ICH_FUNC_DISABLE);
func_disable &= ~(1 << (int) SMBUS_DISABLE_BIT); // enable SMBUS
* does not work in c# */
}
/* Set the SMBus device statically. */
uint dev = grub_pci_make_address(0x0, 0x1f, 0x3);
if (ols.ReadPciConfigWord(dev, PCI_VENDOR_ID) != INTEL_PCI_VENDOR_ID){
//grub_printf("unsupported SMBus controller\n");
return;
}
/* Check to make sure we've got the right device. */
uint pci_id = ols.ReadPciConfigWord(dev, PCI_DEVICE_ID);
if (pci_id != ICH9_PCI_ID && pci_id != ICH8_PCI_ID && pci_id != ICH7_PCI_ID){
//grub_printf("unsupported SMBus controller\n");
return;
}
/* Set SMBus I/O base. grub_pci_write */
ols.WritePciConfigDword(dev, PCI_BASE_ADDRESS_4, SMBUS_IO_BASE | PCI_BASE_ADDRESS_SPACE_IO);
/* Set SMBus enable, disable I2C_EN, disable SMB_SMI_EN grub_pci_write_byte*/
ols.WritePciConfigByte(dev, SMBHSTCFG, SMBHSTCFG_HST_EN);
/* Set SMBus I/O space enable. grub_pci_write_word*/
ols.WritePciConfigWord(dev, PCI_COMMAND, PCI_COMMAND_IO);
/* Disable interrupt generation. grub_outb*/
ols.WriteIoPortByte(SMBHSTCNT, 0);
/* Clear any lingering errors, so transactions can run. grub_outb*/
ols.WriteIoPortByte(SMBHSTSTS, ols.ReadIoPortByte(SMBHSTSTS));
// disable block mode & packet checking
ols.WriteIoPortByte (SMBAUXCTL,(byte) (ols.ReadIoPortByte(SMBAUXCTL) & ~(SMBAUXCTL_E32B | SMBAUXCTL_CRC)));
// comment this out to use byte by byte block operations
ols.WriteIoPortByte(SMBAUXCTL, (byte) (ols.ReadIoPortByte(SMBAUXCTL) | SMBAUXCTL_E32B));
}
public int smbus_read_block_data(ushort device, byte command, byte[] values){
int result;
enable_smbus();
if ((result = smbus_wait_until_ready()) < 0)
return result;
// are these 2 lines really needed? - not in Linux driver
//ols.WriteIoPortByte(SMBHSTSTS, ols.ReadIoPortByte(SMBHSTSTS));
//while ((ols.ReadIoPortByte(SMBHSTSTS) & SMBHSTSTS_INUSE_STS) == 0){
// Thread.Sleep(0);
//}
ols.WriteIoPortByte(SMBHSTADD, (byte) (((device & 0x7f) << 1) | 0x1));
//ols.WriteIoPortByte(SMBHSTADD, (byte) ((device & 0x7f) << 1));
ols.WriteIoPortByte(SMBHSTCMD, command);
if ((ols.ReadIoPortByte(SMBAUXCTL) & SMBAUXCTL_E32B) != 0u)
return i801_read_block_as_block(values);
else
return i801_read_block_byte_by_byte(values);
}
private static int i801_read_block_byte_by_byte(byte[] buf){
int result, i, size = 32;
if ((result = i801_check_pre()) < 0)
return result;
for (i = 0; i < size; ++i){
byte smbcmd = ((i == (size - 1)) ? I801_BLOCK_LAST : I801_BLOCK_DATA);
ols.WriteIoPortByte(SMBHSTCNT, smbcmd);
if (i == 0)
ols.WriteIoPortByte(SMBHSTCNT, (byte) (ols.ReadIoPortByte(SMBHSTCNT) | I801_START));
ushort tries = 0;
ushort status;
do{
Thread.Sleep(1);
status = ols.ReadIoPortByte(SMBHSTSTS);
} while ((status & SMBHSTSTS_BYTE_DONE) == 0 && (tries++ < MAX_TIMEOUT));
if ((result = i801_check_post(status, tries > MAX_TIMEOUT ? 1 : 0)) < 0)
return result;
if (i == 0)
size = ols.ReadIoPortByte(SMBHSTDAT0);
buf[i] = ols.ReadIoPortByte(SMBBLKDAT);
ols.WriteIoPortByte(SMBHSTSTS, (byte) (SMBHSTSTS_BYTE_DONE | SMBHSTSTS_INTR));
}
return size;
}
private static int i801_read_block_as_block(byte[] buf){
int i, status;
ols.ReadIoPortByte(SMBHSTCNT); // reset data buffer index
if ((status = i801_transaction(I801_BLOCK_DATA)) < 0)
return status;
int size = ols.ReadIoPortByte(SMBHSTDAT0);
for (i = 0; i < size; ++i)
buf[i] = ols.ReadIoPortByte(SMBBLKDAT);
return size;
}
public int smbus_write_block_data(ushort device, byte command, byte size, byte[] values){
int result;
if ((result = smbus_wait_until_ready()) < 0)
return result;
// are these 2 lines really needed? - not in Linux driver
ols.WriteIoPortByte(SMBHSTSTS, ols.ReadIoPortByte(SMBHSTSTS));
while ((ols.ReadIoPortByte(SMBHSTSTS) & SMBHSTSTS_INUSE_STS) == 0){}
ols.WriteIoPortByte(SMBHSTADD, (byte) ((device & 0x7f) << 1));
ols.WriteIoPortByte(SMBHSTCMD, command);
if ((ols.ReadIoPortByte(SMBAUXCTL) & SMBAUXCTL_E32B) != 0u)
return i801_write_block_as_block(values, size);
else
return i801_write_block_byte_by_byte(values, size);
}
private static int smbus_wait_until_ready(){
byte mbyte;
uint tries = 0;
do{
Thread.Sleep(1);
mbyte = ols.ReadIoPortByte(SMBHSTSTS);
} while (mbyte != 0 && (tries++ < MAX_TIMEOUT));
return tries > MAX_TIMEOUT ? -1 : 0;
}
private static int i801_write_block_as_block(byte[] buf, byte size){
uint i;
int status;
ols.ReadIoPortByte(SMBHSTCNT); // reset data buffer index
// Use 32-byte buffer to process this transaction
ols.WriteIoPortByte(SMBHSTDAT0, size);
for (i = 0; i < size; ++i)
ols.WriteIoPortByte(SMBBLKDAT, buf[i]);
if ((status = i801_transaction(I801_BLOCK_DATA)) < 0)
return status;
return 0;
}
private static int i801_write_block_byte_by_byte(byte[] buf, byte size){
uint i;
int result;
if ((result = i801_check_pre()) < 0)
return result;
ols.WriteIoPortByte(SMBHSTDAT0, size);
ols.WriteIoPortByte(SMBBLKDAT, buf[0]);
for (i = 0; i < size; ++i){
ols.WriteIoPortByte(SMBHSTCNT, I801_BLOCK_DATA);
if (i == 0)
ols.WriteIoPortByte(SMBHSTCNT, (byte) (ols.ReadIoPortByte(SMBHSTCNT) | I801_START));
byte tries = 0;
int status;
do{
Thread.Sleep(1);
status = ols.ReadIoPortByte(SMBHSTSTS);
} while ((status & SMBHSTSTS_BYTE_DONE) == 0 && (tries++ < MAX_TIMEOUT));
if ((result = i801_check_post(status, tries > MAX_TIMEOUT ? 1 : 0)) < 0)
return result;
if ((i + 1) < size)
ols.WriteIoPortByte(SMBBLKDAT, buf[i + 1]);
ols.WriteIoPortByte(SMBHSTSTS, (byte) (SMBHSTSTS_BYTE_DONE | SMBHSTSTS_INTR));
}
return 0;
}
private static int i801_transaction(int xact){
int result = i801_check_pre(), status, tries = 0;
if (result < 0)
return result;
ols.WriteIoPortByte(SMBHSTCNT, (byte) (xact | I801_START));
do{
Thread.Sleep(1);
status = ols.ReadIoPortByte(SMBHSTSTS);
} while (((status & SMBHSTSTS_HOST_BUSY) != 0l) && (tries++ < MAX_TIMEOUT));
if ((result = i801_check_post(status, tries > MAX_TIMEOUT ? 1 : 0)) < 0)
return result;
ols.WriteIoPortByte(SMBHSTSTS, SMBHSTSTS_INTR);
return 0;
}
private static int i801_check_post(int status, int timeout){
int error = 0;
if (timeout != 0){
// abort transaction
ols.WriteIoPortByte(SMBHSTCNT, (byte) (ols.ReadIoPortByte(SMBHSTCNT) | SMBHSTCNT_KILL));
Thread.Sleep(1);
ols.WriteIoPortByte(SMBHSTCNT, (byte) (ols.ReadIoPortByte(SMBHSTCNT) & (~SMBHSTCNT_KILL)));
ols.WriteIoPortByte(SMBHSTSTS, STATUS_FLAGS);
return -2;
}
if ((status & SMBHSTSTS_FAILED) != 0l)
error = -3;
if ((status & SMBHSTSTS_DEV_ERR) != 0l)
error = -4;
if ((status & SMBHSTSTS_BUS_ERR) != 0l)
error = -5;
if (error != 0){
// clear error flags
ols.WriteIoPortByte(SMBHSTSTS, (byte) (status & STATUS_FLAGS));
}
return error;
}
private static int i801_check_pre(){
int status = ols.ReadIoPortByte(SMBHSTSTS);
if ((status & SMBHSTSTS_HOST_BUSY) != 0l)
return -10;
status &= STATUS_FLAGS;
if (status != 0u){
// clear status flags (some devices expect this as an acknowledge
ols.WriteIoPortByte(SMBHSTSTS, (byte) status);
status = ols.ReadIoPortByte(SMBHSTSTS) & STATUS_FLAGS;
if (status != 0u)
return -20;
}
return 0;
}
}
}