linux-i2c.vger.kernel.org archive mirror
 help / color / mirror / Atom feed
From: Daniel Kurtz <djkurtz@chromium.org>
To: khali@linux-fr.org, ben-linux@fluff.org, seth.heasley@intel.com,
	ben@decadent.org.uk, David.Woodhouse@intel.com
Cc: linux-i2c@vger.kernel.org, linux-kernel@vger.kernel.org,
	olofj@chromium.org, dlaurie@chromium.org, bleung@chromium.org,
	Daniel Kurtz <djkurtz@chromium.org>
Subject: [PATCH 3/3] i2c: i801: enable irq for byte_by_byte transactions
Date: Wed, 14 Dec 2011 15:56:32 +0800	[thread overview]
Message-ID: <1323849392-20413-4-git-send-email-djkurtz@chromium.org> (raw)
In-Reply-To: <1323849392-20413-1-git-send-email-djkurtz@chromium.org>

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 a dramatic speed up in emulated i2c-over smbus 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.

Signed-off-by: Daniel Kurtz <djkurtz@chromium.org>
---
 drivers/i2c/busses/i2c-i801.c |   49 ++++++++++++++++++++++++++++++++++++++--
 1 files changed, 46 insertions(+), 3 deletions(-)

diff --git a/drivers/i2c/busses/i2c-i801.c b/drivers/i2c/busses/i2c-i801.c
index 3abc624..1b6673b 100644
--- a/drivers/i2c/busses/i2c-i801.c
+++ b/drivers/i2c/busses/i2c-i801.c
@@ -166,6 +166,13 @@ struct i801_priv {
 	wait_queue_head_t waitq;
 	spinlock_t lock;
 	u8 status;
+
+	/* Command state used during by isr */
+	u8 cmd;
+	bool is_read;
+	int count;
+	int len;
+	u8 *data;
 };
 
 static struct pci_driver i801_driver;
@@ -379,6 +386,7 @@ static irqreturn_t i801_isr(int irq, void *dev_id)
 {
 	struct i801_priv *priv = dev_id;
 	u8 pcists, hststs;
+	u8 cmd;
 	unsigned long flags;
 
 	/* Confirm this is our interrupt */
@@ -394,16 +402,35 @@ static irqreturn_t i801_isr(int irq, void *dev_id)
 	/* Only process irq sources */
 	hststs &= STATUS_FLAGS;
 
-	/* Clear and report irq sources */
+	if (hststs & SMBHSTSTS_BYTE_DONE) {
+		if (priv->is_read) {
+			priv->data[priv->count++] = inb(SMBBLKDAT(priv));
+
+			/* Set LAST_BYTE for last byte of read transaction */
+			cmd = priv->cmd;
+			if (priv->count == priv->len - 1)
+				cmd |= I801_LAST_BYTE;
+			outb_p(cmd | I801_START, SMBHSTCNT(priv));
+		} else if (priv->count < priv->len - 1) {
+			outb(priv->data[++priv->count], SMBBLKDAT(priv));
+			outb_p(priv->cmd | I801_START, SMBHSTCNT(priv));
+		}
+
+		/* Clear BYTE_DONE to start next transaction. */
+		outb(SMBHSTSTS_BYTE_DONE, SMBHSTSTS(priv));
+
+		/* Clear BYTE_DONE so it does not wake_up waitq */
+		hststs &= ~SMBHSTSTS_BYTE_DONE;
+	}
+
+	/* Clear and report other irq sources */
 	if (hststs) {
 		outb(hststs, SMBHSTSTS(priv));
-
 		spin_lock_irqsave(&priv->lock, flags);
 		priv->status |= hststs;
 		spin_unlock_irqrestore(&priv->lock, flags);
 		wake_up(&priv->waitq);
 	}
-
 	return IRQ_HANDLED;
 }
 
@@ -439,6 +466,22 @@ static int i801_block_transaction_byte_by_byte(struct i801_priv *priv,
 	else
 		smbcmd = I801_BLOCK_DATA;
 
+	if (priv->features & FEATURE_IRQ) {
+		priv->is_read = (read_write == I2C_SMBUS_READ);
+		if (len == 1 && priv->is_read)
+			smbcmd |= I801_LAST_BYTE;
+		priv->cmd = smbcmd | I801_INTREN;
+		priv->len = len;
+		priv->count = 0;
+		priv->data = &data->block[1];
+
+		outb(priv->cmd | I801_START, SMBHSTCNT(priv));
+		timeout = wait_event_timeout(priv->waitq,
+					     (status = i801_get_status(priv)),
+					     IRQ_TIMEOUT);
+		return i801_check_post(priv, status, timeout == 0);
+	}
+
 	for (i = 1; i <= len; i++) {
 		if (i == len && read_write == I2C_SMBUS_READ)
 			smbcmd |= I801_LAST_BYTE;
-- 
1.7.3.1

      parent reply	other threads:[~2011-12-14  7:56 UTC|newest]

Thread overview: 4+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2011-12-14  7:56 [PATCH 0/3] i2c: i801: enable irq Daniel Kurtz
     [not found] ` <1323849392-20413-1-git-send-email-djkurtz-F7+t8E8rja9g9hUCZPvPmw@public.gmane.org>
2011-12-14  7:56   ` [PATCH 1/3] i2c: i801: refactor i801_block_transaction_byte_by_byte Daniel Kurtz
2011-12-14  7:56   ` [PATCH 2/3] i2c: i801: enable irq for i801 smbus transactions Daniel Kurtz
2011-12-14  7:56 ` Daniel Kurtz [this message]

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=1323849392-20413-4-git-send-email-djkurtz@chromium.org \
    --to=djkurtz@chromium.org \
    --cc=David.Woodhouse@intel.com \
    --cc=ben-linux@fluff.org \
    --cc=ben@decadent.org.uk \
    --cc=bleung@chromium.org \
    --cc=dlaurie@chromium.org \
    --cc=khali@linux-fr.org \
    --cc=linux-i2c@vger.kernel.org \
    --cc=linux-kernel@vger.kernel.org \
    --cc=olofj@chromium.org \
    --cc=seth.heasley@intel.com \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).