aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/i2c
diff options
context:
space:
mode:
authorDaniel Kurtz <djkurtz@chromium.org>2012-07-24 08:13:59 -0400
committerJean Delvare <khali@endymion.delvare>2012-07-24 08:13:59 -0400
commitd3ff6ce40031e8401eab60c3de7db9b1f3f4c08b (patch)
tree4fbbf4ae24c37638bb51001600caa2843139ddbf /drivers/i2c
parent29b608540b030d38a978c972cbe99d40efdb7267 (diff)
i2c-i801: Enable IRQ for byte_by_byte transactions
Byte-by-byte transactions are used primarily for accessing I2C devices with an SMBus controller. For these transactions, for each byte that is read or written, the SMBus controller generates a BYTE_DONE IRQ. The isr reads/writes the next byte, and clears the IRQ flag to start the next byte. On the penultimate IRQ, the isr also sets the LAST_BYTE flag. There is no locking around the cmd/len/count/data variables, since the I2C adapter lock ensures there is never multiple simultaneous transactions for the same device, and the driver thread never accesses these variables while interrupts might be occurring. The end result is faster I2C block read and write transactions. Note: This patch has only been tested and verified by doing I2C read and write block transfers on Cougar Point 6 Series PCH, as well as I2C read block transfers on ICH5. Signed-off-by: Daniel Kurtz <djkurtz@chromium.org> Signed-off-by: Jean Delvare <khali@linux-fr.org>
Diffstat (limited to 'drivers/i2c')
-rw-r--r--drivers/i2c/busses/i2c-i801.c85
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
171static struct pci_driver i801_driver; 178static 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
383static 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 */
385static irqreturn_t i801_isr(int irq, void *dev_id) 438static 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;