Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

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>

authored by

Daniel Kurtz and committed by
Jean Delvare
d3ff6ce4 29b60854

+78 -7
+78 -7
drivers/i2c/busses/i2c-i801.c
··· 166 166 /* isr processing */ 167 167 wait_queue_head_t waitq; 168 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 178 static struct pci_driver i801_driver; ··· 380 373 return 0; 381 374 } 382 375 376 + static void i801_isr_byte_done(struct i801_priv *priv) 377 + { 378 + if (priv->is_read) { 379 + /* For SMBus block reads, length is received with first byte */ 380 + if (((priv->cmd & 0x1c) == I801_BLOCK_DATA) && 381 + (priv->count == 0)) { 382 + priv->len = inb_p(SMBHSTDAT0(priv)); 383 + if (priv->len < 1 || priv->len > I2C_SMBUS_BLOCK_MAX) { 384 + dev_err(&priv->pci_dev->dev, 385 + "Illegal SMBus block read size %d\n", 386 + priv->len); 387 + /* FIXME: Recover */ 388 + priv->len = I2C_SMBUS_BLOCK_MAX; 389 + } else { 390 + dev_dbg(&priv->pci_dev->dev, 391 + "SMBus block read size is %d\n", 392 + priv->len); 393 + } 394 + priv->data[-1] = priv->len; 395 + } 396 + 397 + /* Read next byte */ 398 + if (priv->count < priv->len) 399 + priv->data[priv->count++] = inb(SMBBLKDAT(priv)); 400 + else 401 + dev_dbg(&priv->pci_dev->dev, 402 + "Discarding extra byte on block read\n"); 403 + 404 + /* Set LAST_BYTE for last byte of read transaction */ 405 + if (priv->count == priv->len - 1) 406 + outb_p(priv->cmd | SMBHSTCNT_LAST_BYTE, 407 + SMBHSTCNT(priv)); 408 + } else if (priv->count < priv->len - 1) { 409 + /* Write next byte, except for IRQ after last byte */ 410 + outb_p(priv->data[++priv->count], SMBBLKDAT(priv)); 411 + } 412 + 413 + /* Clear BYTE_DONE to continue with next byte */ 414 + outb_p(SMBHSTSTS_BYTE_DONE, SMBHSTSTS(priv)); 415 + } 416 + 383 417 /* 384 - * i801 signals transaction completion with one of these interrupts: 385 - * INTR - Success 386 - * DEV_ERR - Invalid command, NAK or communication timeout 387 - * BUS_ERR - SMI# transaction collision 388 - * FAILED - transaction was canceled due to a KILL request 389 - * When any of these occur, update ->status and wake up the waitq. 390 - * ->status must be cleared before kicking off the next transaction. 418 + * There are two kinds of interrupts: 419 + * 420 + * 1) i801 signals transaction completion with one of these interrupts: 421 + * INTR - Success 422 + * DEV_ERR - Invalid command, NAK or communication timeout 423 + * BUS_ERR - SMI# transaction collision 424 + * FAILED - transaction was canceled due to a KILL request 425 + * When any of these occur, update ->status and wake up the waitq. 426 + * ->status must be cleared before kicking off the next transaction. 427 + * 428 + * 2) For byte-by-byte (I2C read/write) transactions, one BYTE_DONE interrupt 429 + * occurs for each byte of a byte-by-byte to prepare the next byte. 391 430 */ 392 431 static irqreturn_t i801_isr(int irq, void *dev_id) 393 432 { ··· 449 396 status = inb_p(SMBHSTSTS(priv)); 450 397 if (status != 0x42) 451 398 dev_dbg(&priv->pci_dev->dev, "irq: status = %02x\n", status); 399 + 400 + if (status & SMBHSTSTS_BYTE_DONE) 401 + i801_isr_byte_done(priv); 452 402 453 403 /* 454 404 * Clear irq sources and report transaction result. ··· 498 442 smbcmd = I801_I2C_BLOCK_DATA; 499 443 else 500 444 smbcmd = I801_BLOCK_DATA; 445 + 446 + if (priv->features & FEATURE_IRQ) { 447 + priv->is_read = (read_write == I2C_SMBUS_READ); 448 + if (len == 1 && priv->is_read) 449 + smbcmd |= SMBHSTCNT_LAST_BYTE; 450 + priv->cmd = smbcmd | SMBHSTCNT_INTREN; 451 + priv->len = len; 452 + priv->count = 0; 453 + priv->data = &data->block[1]; 454 + 455 + outb_p(priv->cmd | SMBHSTCNT_START, SMBHSTCNT(priv)); 456 + wait_event(priv->waitq, (status = priv->status)); 457 + priv->status = 0; 458 + return i801_check_post(priv, status); 459 + } 501 460 502 461 for (i = 1; i <= len; i++) { 503 462 if (i == len && read_write == I2C_SMBUS_READ)