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

i2c: i801: improve interrupt handler

Not sure if it can happen, but better play safe: If SMBHSTSTS_BYTE_DONE
and an error flag is set, then don't trust the result and skip calling
i801_isr_byte_done(). In addition clear status bit SMBHSTSTS_BYTE_DONE
in the main interrupt handler, this allows to simplify the code a
little.

Signed-off-by: Heiner Kallweit <hkallweit1@gmail.com>
Reviewed-by: Jean Delvare <jdelvare@suse.de>
Signed-off-by: Wolfram Sang <wsa@kernel.org>

authored by

Heiner Kallweit and committed by
Wolfram Sang
c467d919 fce55da3

+8 -17
+8 -17
drivers/i2c/busses/i2c-i801.c
··· 558 558 /* Write next byte, except for IRQ after last byte */ 559 559 outb_p(priv->data[++priv->count], SMBBLKDAT(priv)); 560 560 } 561 - 562 - /* Clear BYTE_DONE to continue with next byte */ 563 - outb_p(SMBHSTSTS_BYTE_DONE, SMBHSTSTS(priv)); 564 561 } 565 562 566 563 static irqreturn_t i801_host_notify_isr(struct i801_priv *priv) ··· 587 590 * BUS_ERR - SMI# transaction collision 588 591 * FAILED - transaction was canceled due to a KILL request 589 592 * When any of these occur, update ->status and signal completion. 590 - * ->status must be cleared before kicking off the next transaction. 591 593 * 592 594 * 2) For byte-by-byte (I2C read/write) transactions, one BYTE_DONE interrupt 593 595 * occurs for each byte of a byte-by-byte to prepare the next byte. ··· 611 615 } 612 616 613 617 status = inb_p(SMBHSTSTS(priv)); 614 - if (status & SMBHSTSTS_BYTE_DONE) 618 + if ((status & (SMBHSTSTS_BYTE_DONE | STATUS_ERROR_FLAGS)) == SMBHSTSTS_BYTE_DONE) 615 619 i801_isr_byte_done(priv); 616 620 617 621 /* 618 - * Clear remaining IRQ sources: Completion of last command, errors 619 - * and the SMB_ALERT signal. SMB_ALERT status is set after signal 620 - * assertion independently of the interrupt generation being blocked 621 - * or not so clear it always when the status is set. 622 + * Clear IRQ sources: SMB_ALERT status is set after signal assertion 623 + * independently of the interrupt generation being blocked or not 624 + * so clear it always when the status is set. 622 625 */ 623 - status &= SMBHSTSTS_INTR | STATUS_ERROR_FLAGS | SMBHSTSTS_SMBALERT_STS; 624 - if (status) 625 - outb_p(status, SMBHSTSTS(priv)); 626 - status &= ~SMBHSTSTS_SMBALERT_STS; /* SMB_ALERT not reported */ 627 - /* 628 - * Report transaction result. 629 - * ->status must be cleared before the next transaction is started. 630 - */ 626 + status &= STATUS_FLAGS | SMBHSTSTS_SMBALERT_STS; 627 + outb_p(status, SMBHSTSTS(priv)); 628 + 629 + status &= STATUS_ERROR_FLAGS | SMBHSTSTS_INTR; 631 630 if (status) { 632 631 priv->status = status; 633 632 complete(&priv->done);