diff options
Diffstat (limited to 'drivers/i2c')
-rw-r--r-- | drivers/i2c/busses/i2c-i801.c | 151 |
1 files changed, 100 insertions, 51 deletions
diff --git a/drivers/i2c/busses/i2c-i801.c b/drivers/i2c/busses/i2c-i801.c index d5e12a4f01b0..8f5c686123b8 100644 --- a/drivers/i2c/busses/i2c-i801.c +++ b/drivers/i2c/busses/i2c-i801.c | |||
@@ -26,8 +26,8 @@ | |||
26 | 82801AB 2423 | 26 | 82801AB 2423 |
27 | 82801BA 2443 | 27 | 82801BA 2443 |
28 | 82801CA/CAM 2483 | 28 | 82801CA/CAM 2483 |
29 | 82801DB 24C3 (HW PEC supported, 32 byte buffer not supported) | 29 | 82801DB 24C3 (HW PEC supported) |
30 | 82801EB 24D3 (HW PEC supported, 32 byte buffer not supported) | 30 | 82801EB 24D3 (HW PEC supported) |
31 | 6300ESB 25A4 | 31 | 6300ESB 25A4 |
32 | ICH6 266A | 32 | ICH6 266A |
33 | ICH7 27DA | 33 | ICH7 27DA |
@@ -114,7 +114,7 @@ static struct pci_driver i801_driver; | |||
114 | static struct pci_dev *I801_dev; | 114 | static struct pci_dev *I801_dev; |
115 | static int isich4; | 115 | static int isich4; |
116 | 116 | ||
117 | static int i801_transaction(void) | 117 | static int i801_transaction(int xact) |
118 | { | 118 | { |
119 | int temp; | 119 | int temp; |
120 | int result = 0; | 120 | int result = 0; |
@@ -139,7 +139,9 @@ static int i801_transaction(void) | |||
139 | } | 139 | } |
140 | } | 140 | } |
141 | 141 | ||
142 | outb_p(inb(SMBHSTCNT) | I801_START, SMBHSTCNT); | 142 | /* the current contents of SMBHSTCNT can be overwritten, since PEC, |
143 | * INTREN, SMBSCMD are passed in xact */ | ||
144 | outb_p(xact | I801_START, SMBHSTCNT); | ||
143 | 145 | ||
144 | /* We will always wait for a fraction of a second! */ | 146 | /* We will always wait for a fraction of a second! */ |
145 | do { | 147 | do { |
@@ -207,44 +209,52 @@ static void i801_wait_hwpec(void) | |||
207 | outb_p(temp, SMBHSTSTS); | 209 | outb_p(temp, SMBHSTSTS); |
208 | } | 210 | } |
209 | 211 | ||
210 | /* All-inclusive block transaction function */ | 212 | static int i801_block_transaction_by_block(union i2c_smbus_data *data, |
211 | static int i801_block_transaction(union i2c_smbus_data *data, char read_write, | 213 | char read_write, int hwpec) |
212 | int command, int hwpec) | 214 | { |
215 | int i, len; | ||
216 | |||
217 | inb_p(SMBHSTCNT); /* reset the data buffer index */ | ||
218 | |||
219 | /* Use 32-byte buffer to process this transaction */ | ||
220 | if (read_write == I2C_SMBUS_WRITE) { | ||
221 | len = data->block[0]; | ||
222 | outb_p(len, SMBHSTDAT0); | ||
223 | for (i = 0; i < len; i++) | ||
224 | outb_p(data->block[i+1], SMBBLKDAT); | ||
225 | } | ||
226 | |||
227 | if (i801_transaction(I801_BLOCK_DATA | ENABLE_INT9 | | ||
228 | I801_PEC_EN * hwpec)) | ||
229 | return -1; | ||
230 | |||
231 | if (read_write == I2C_SMBUS_READ) { | ||
232 | len = inb_p(SMBHSTDAT0); | ||
233 | if (len < 1 || len > I2C_SMBUS_BLOCK_MAX) | ||
234 | return -1; | ||
235 | |||
236 | data->block[0] = len; | ||
237 | for (i = 0; i < len; i++) | ||
238 | data->block[i + 1] = inb_p(SMBBLKDAT); | ||
239 | } | ||
240 | return 0; | ||
241 | } | ||
242 | |||
243 | static int i801_block_transaction_byte_by_byte(union i2c_smbus_data *data, | ||
244 | char read_write, int hwpec) | ||
213 | { | 245 | { |
214 | int i, len; | 246 | int i, len; |
215 | int smbcmd; | 247 | int smbcmd; |
216 | int temp; | 248 | int temp; |
217 | int result = 0; | 249 | int result = 0; |
218 | int timeout; | 250 | int timeout; |
219 | unsigned char hostc, errmask; | 251 | unsigned char errmask; |
220 | 252 | ||
221 | if (command == I2C_SMBUS_I2C_BLOCK_DATA) { | 253 | len = data->block[0]; |
222 | if (read_write == I2C_SMBUS_WRITE) { | ||
223 | /* set I2C_EN bit in configuration register */ | ||
224 | pci_read_config_byte(I801_dev, SMBHSTCFG, &hostc); | ||
225 | pci_write_config_byte(I801_dev, SMBHSTCFG, | ||
226 | hostc | SMBHSTCFG_I2C_EN); | ||
227 | } else { | ||
228 | dev_err(&I801_dev->dev, | ||
229 | "I2C_SMBUS_I2C_BLOCK_READ not DB!\n"); | ||
230 | return -1; | ||
231 | } | ||
232 | } | ||
233 | 254 | ||
234 | if (read_write == I2C_SMBUS_WRITE) { | 255 | if (read_write == I2C_SMBUS_WRITE) { |
235 | len = data->block[0]; | ||
236 | if (len < 1) | ||
237 | len = 1; | ||
238 | if (len > 32) | ||
239 | len = 32; | ||
240 | outb_p(len, SMBHSTDAT0); | 256 | outb_p(len, SMBHSTDAT0); |
241 | outb_p(data->block[1], SMBBLKDAT); | 257 | outb_p(data->block[1], SMBBLKDAT); |
242 | } else { | ||
243 | len = 32; /* max for reads */ | ||
244 | } | ||
245 | |||
246 | if(isich4 && command != I2C_SMBUS_I2C_BLOCK_DATA) { | ||
247 | /* set 32 byte buffer */ | ||
248 | } | 258 | } |
249 | 259 | ||
250 | for (i = 1; i <= len; i++) { | 260 | for (i = 1; i <= len; i++) { |
@@ -277,14 +287,11 @@ static int i801_block_transaction(union i2c_smbus_data *data, char read_write, | |||
277 | if (((temp = inb_p(SMBHSTSTS)) & errmask) != 0x00) { | 287 | if (((temp = inb_p(SMBHSTSTS)) & errmask) != 0x00) { |
278 | dev_err(&I801_dev->dev, | 288 | dev_err(&I801_dev->dev, |
279 | "Reset failed! (%02x)\n", temp); | 289 | "Reset failed! (%02x)\n", temp); |
280 | result = -1; | 290 | return -1; |
281 | goto END; | ||
282 | } | 291 | } |
283 | if (i != 1) { | 292 | if (i != 1) |
284 | /* if die in middle of block transaction, fail */ | 293 | /* if die in middle of block transaction, fail */ |
285 | result = -1; | 294 | return -1; |
286 | goto END; | ||
287 | } | ||
288 | } | 295 | } |
289 | 296 | ||
290 | if (i == 1) | 297 | if (i == 1) |
@@ -326,10 +333,8 @@ static int i801_block_transaction(union i2c_smbus_data *data, char read_write, | |||
326 | 333 | ||
327 | if (i == 1 && read_write == I2C_SMBUS_READ) { | 334 | if (i == 1 && read_write == I2C_SMBUS_READ) { |
328 | len = inb_p(SMBHSTDAT0); | 335 | len = inb_p(SMBHSTDAT0); |
329 | if (len < 1 || len > I2C_SMBUS_BLOCK_MAX) { | 336 | if (len < 1 || len > I2C_SMBUS_BLOCK_MAX) |
330 | result = -1; | 337 | return -1; |
331 | goto END; | ||
332 | } | ||
333 | data->block[0] = len; | 338 | data->block[0] = len; |
334 | } | 339 | } |
335 | 340 | ||
@@ -352,14 +357,58 @@ static int i801_block_transaction(union i2c_smbus_data *data, char read_write, | |||
352 | inb_p(SMBHSTDAT0), inb_p(SMBBLKDAT)); | 357 | inb_p(SMBHSTDAT0), inb_p(SMBBLKDAT)); |
353 | 358 | ||
354 | if (result < 0) | 359 | if (result < 0) |
355 | goto END; | 360 | return result; |
356 | } | 361 | } |
362 | return result; | ||
363 | } | ||
357 | 364 | ||
358 | if (hwpec) | 365 | static int i801_set_block_buffer_mode(void) |
366 | { | ||
367 | outb_p(inb_p(SMBAUXCTL) | SMBAUXCTL_E32B, SMBAUXCTL); | ||
368 | if ((inb_p(SMBAUXCTL) & SMBAUXCTL_E32B) == 0) | ||
369 | return -1; | ||
370 | return 0; | ||
371 | } | ||
372 | |||
373 | /* Block transaction function */ | ||
374 | static int i801_block_transaction(union i2c_smbus_data *data, char read_write, | ||
375 | int command, int hwpec) | ||
376 | { | ||
377 | int result = 0; | ||
378 | unsigned char hostc; | ||
379 | |||
380 | if (command == I2C_SMBUS_I2C_BLOCK_DATA) { | ||
381 | if (read_write == I2C_SMBUS_WRITE) { | ||
382 | /* set I2C_EN bit in configuration register */ | ||
383 | pci_read_config_byte(I801_dev, SMBHSTCFG, &hostc); | ||
384 | pci_write_config_byte(I801_dev, SMBHSTCFG, | ||
385 | hostc | SMBHSTCFG_I2C_EN); | ||
386 | } else { | ||
387 | dev_err(&I801_dev->dev, | ||
388 | "I2C_SMBUS_I2C_BLOCK_READ not DB!\n"); | ||
389 | return -1; | ||
390 | } | ||
391 | } | ||
392 | |||
393 | if (read_write == I2C_SMBUS_WRITE) { | ||
394 | if (data->block[0] < 1) | ||
395 | data->block[0] = 1; | ||
396 | if (data->block[0] > I2C_SMBUS_BLOCK_MAX) | ||
397 | data->block[0] = I2C_SMBUS_BLOCK_MAX; | ||
398 | } else { | ||
399 | data->block[0] = 32; /* max for reads */ | ||
400 | } | ||
401 | |||
402 | if (isich4 && i801_set_block_buffer_mode() == 0 ) | ||
403 | result = i801_block_transaction_by_block(data, read_write, | ||
404 | hwpec); | ||
405 | else | ||
406 | result = i801_block_transaction_byte_by_byte(data, read_write, | ||
407 | hwpec); | ||
408 | |||
409 | if (result == 0 && hwpec) | ||
359 | i801_wait_hwpec(); | 410 | i801_wait_hwpec(); |
360 | 411 | ||
361 | result = 0; | ||
362 | END: | ||
363 | if (command == I2C_SMBUS_I2C_BLOCK_DATA) { | 412 | if (command == I2C_SMBUS_I2C_BLOCK_DATA) { |
364 | /* restore saved configuration register value */ | 413 | /* restore saved configuration register value */ |
365 | pci_write_config_byte(I801_dev, SMBHSTCFG, hostc); | 414 | pci_write_config_byte(I801_dev, SMBHSTCFG, hostc); |
@@ -431,15 +480,15 @@ static s32 i801_access(struct i2c_adapter * adap, u16 addr, | |||
431 | 480 | ||
432 | if(block) | 481 | if(block) |
433 | ret = i801_block_transaction(data, read_write, size, hwpec); | 482 | ret = i801_block_transaction(data, read_write, size, hwpec); |
434 | else { | 483 | else |
435 | outb_p(xact | ENABLE_INT9, SMBHSTCNT); | 484 | ret = i801_transaction(xact | ENABLE_INT9); |
436 | ret = i801_transaction(); | ||
437 | } | ||
438 | 485 | ||
439 | /* Some BIOSes don't like it when PEC is enabled at reboot or resume | 486 | /* Some BIOSes don't like it when PEC is enabled at reboot or resume |
440 | time, so we forcibly disable it after every transaction. */ | 487 | time, so we forcibly disable it after every transaction. Turn off |
488 | E32B for the same reason. */ | ||
441 | if (hwpec) | 489 | if (hwpec) |
442 | outb_p(inb_p(SMBAUXCTL) & (~SMBAUXCTL_CRC), SMBAUXCTL); | 490 | outb_p(inb_p(SMBAUXCTL) & ~(SMBAUXCTL_CRC | SMBAUXCTL_E32B), |
491 | SMBAUXCTL); | ||
443 | 492 | ||
444 | if(block) | 493 | if(block) |
445 | return ret; | 494 | return ret; |