diff options
Diffstat (limited to 'drivers/i2c/busses/i2c-i801.c')
-rw-r--r-- | drivers/i2c/busses/i2c-i801.c | 85 |
1 files changed, 78 insertions, 7 deletions
diff --git a/drivers/i2c/busses/i2c-i801.c b/drivers/i2c/busses/i2c-i801.c index 003196fffd2..898dcf9c7ad 100644 --- a/drivers/i2c/busses/i2c-i801.c +++ b/drivers/i2c/busses/i2c-i801.c | |||
@@ -166,6 +166,13 @@ struct i801_priv { | |||
166 | /* isr processing */ | 166 | /* isr processing */ |
167 | wait_queue_head_t waitq; | 167 | wait_queue_head_t waitq; |
168 | u8 status; | 168 | u8 status; |
169 | |||
170 | /* Command state used by isr for byte-by-byte block transactions */ | ||
171 | u8 cmd; | ||
172 | bool is_read; | ||
173 | int count; | ||
174 | int len; | ||
175 | u8 *data; | ||
169 | }; | 176 | }; |
170 | 177 | ||
171 | static struct pci_driver i801_driver; | 178 | static struct pci_driver i801_driver; |
@@ -373,14 +380,60 @@ static int i801_block_transaction_by_block(struct i801_priv *priv, | |||
373 | return 0; | 380 | return 0; |
374 | } | 381 | } |
375 | 382 | ||
383 | static void i801_isr_byte_done(struct i801_priv *priv) | ||
384 | { | ||
385 | if (priv->is_read) { | ||
386 | /* For SMBus block reads, length is received with first byte */ | ||
387 | if (((priv->cmd & 0x1c) == I801_BLOCK_DATA) && | ||
388 | (priv->count == 0)) { | ||
389 | priv->len = inb_p(SMBHSTDAT0(priv)); | ||
390 | if (priv->len < 1 || priv->len > I2C_SMBUS_BLOCK_MAX) { | ||
391 | dev_err(&priv->pci_dev->dev, | ||
392 | "Illegal SMBus block read size %d\n", | ||
393 | priv->len); | ||
394 | /* FIXME: Recover */ | ||
395 | priv->len = I2C_SMBUS_BLOCK_MAX; | ||
396 | } else { | ||
397 | dev_dbg(&priv->pci_dev->dev, | ||
398 | "SMBus block read size is %d\n", | ||
399 | priv->len); | ||
400 | } | ||
401 | priv->data[-1] = priv->len; | ||
402 | } | ||
403 | |||
404 | /* Read next byte */ | ||
405 | if (priv->count < priv->len) | ||
406 | priv->data[priv->count++] = inb(SMBBLKDAT(priv)); | ||
407 | else | ||
408 | dev_dbg(&priv->pci_dev->dev, | ||
409 | "Discarding extra byte on block read\n"); | ||
410 | |||
411 | /* Set LAST_BYTE for last byte of read transaction */ | ||
412 | if (priv->count == priv->len - 1) | ||
413 | outb_p(priv->cmd | SMBHSTCNT_LAST_BYTE, | ||
414 | SMBHSTCNT(priv)); | ||
415 | } else if (priv->count < priv->len - 1) { | ||
416 | /* Write next byte, except for IRQ after last byte */ | ||
417 | outb_p(priv->data[++priv->count], SMBBLKDAT(priv)); | ||
418 | } | ||
419 | |||
420 | /* Clear BYTE_DONE to continue with next byte */ | ||
421 | outb_p(SMBHSTSTS_BYTE_DONE, SMBHSTSTS(priv)); | ||
422 | } | ||
423 | |||
376 | /* | 424 | /* |
377 | * i801 signals transaction completion with one of these interrupts: | 425 | * There are two kinds of interrupts: |
378 | * INTR - Success | 426 | * |
379 | * DEV_ERR - Invalid command, NAK or communication timeout | 427 | * 1) i801 signals transaction completion with one of these interrupts: |
380 | * BUS_ERR - SMI# transaction collision | 428 | * INTR - Success |
381 | * FAILED - transaction was canceled due to a KILL request | 429 | * DEV_ERR - Invalid command, NAK or communication timeout |
382 | * When any of these occur, update ->status and wake up the waitq. | 430 | * BUS_ERR - SMI# transaction collision |
383 | * ->status must be cleared before kicking off the next transaction. | 431 | * FAILED - transaction was canceled due to a KILL request |
432 | * When any of these occur, update ->status and wake up the waitq. | ||
433 | * ->status must be cleared before kicking off the next transaction. | ||
434 | * | ||
435 | * 2) For byte-by-byte (I2C read/write) transactions, one BYTE_DONE interrupt | ||
436 | * occurs for each byte of a byte-by-byte to prepare the next byte. | ||
384 | */ | 437 | */ |
385 | static irqreturn_t i801_isr(int irq, void *dev_id) | 438 | static irqreturn_t i801_isr(int irq, void *dev_id) |
386 | { | 439 | { |
@@ -397,6 +450,9 @@ static irqreturn_t i801_isr(int irq, void *dev_id) | |||
397 | if (status != 0x42) | 450 | if (status != 0x42) |
398 | dev_dbg(&priv->pci_dev->dev, "irq: status = %02x\n", status); | 451 | dev_dbg(&priv->pci_dev->dev, "irq: status = %02x\n", status); |
399 | 452 | ||
453 | if (status & SMBHSTSTS_BYTE_DONE) | ||
454 | i801_isr_byte_done(priv); | ||
455 | |||
400 | /* | 456 | /* |
401 | * Clear irq sources and report transaction result. | 457 | * Clear irq sources and report transaction result. |
402 | * ->status must be cleared before the next transaction is started. | 458 | * ->status must be cleared before the next transaction is started. |
@@ -443,6 +499,21 @@ static int i801_block_transaction_byte_by_byte(struct i801_priv *priv, | |||
443 | else | 499 | else |
444 | smbcmd = I801_BLOCK_DATA; | 500 | smbcmd = I801_BLOCK_DATA; |
445 | 501 | ||
502 | if (priv->features & FEATURE_IRQ) { | ||
503 | priv->is_read = (read_write == I2C_SMBUS_READ); | ||
504 | if (len == 1 && priv->is_read) | ||
505 | smbcmd |= SMBHSTCNT_LAST_BYTE; | ||
506 | priv->cmd = smbcmd | SMBHSTCNT_INTREN; | ||
507 | priv->len = len; | ||
508 | priv->count = 0; | ||
509 | priv->data = &data->block[1]; | ||
510 | |||
511 | outb_p(priv->cmd | SMBHSTCNT_START, SMBHSTCNT(priv)); | ||
512 | wait_event(priv->waitq, (status = priv->status)); | ||
513 | priv->status = 0; | ||
514 | return i801_check_post(priv, status); | ||
515 | } | ||
516 | |||
446 | for (i = 1; i <= len; i++) { | 517 | for (i = 1; i <= len; i++) { |
447 | if (i == len && read_write == I2C_SMBUS_READ) | 518 | if (i == len && read_write == I2C_SMBUS_READ) |
448 | smbcmd |= SMBHSTCNT_LAST_BYTE; | 519 | smbcmd |= SMBHSTCNT_LAST_BYTE; |