On Tue, Jul 3, 2012 at 4:19 PM, Jean Delvare <khali@xxxxxxxxxxxx> wrote: > > Come up with a consistent, driver-wide strategy for event polling. For > intermediate steps of byte-by-byte block transactions, check for > BYTE_DONE or any error flag being set. At the end of every transaction, > check for both BUSY being cleared and INTR or any error flag being set. > This avoids having to wait twice for the same event. > > Signed-off-by: Jean Delvare <khali@xxxxxxxxxxxx> > Cc: Daniel Kurtz <djkurtz@xxxxxxxxxxxx> > --- > Daniel, this is what I had in mind. This applies on top of your first 6 > patches (i.e. all the preliminary cleanup patches, not the interrupt > patches.) It solves the performance regression problem and makes the > code look better IMHO. Tested OK on ICH10 and ICH3-M. What do you > think? Comments and suggestions for improvements are very welcome. > > It may be possible to combine i801_wait_intr() and > i801_wait_byte_done() into a single function, as they do pretty much > the same, but I'll only do that if it doesn't hurt readability. My > first attempt was horrible. > > drivers/i2c/busses/i2c-i801.c | 88 ++++++++++++++++++++--------------------- > 1 file changed, 43 insertions(+), 45 deletions(-) > > --- linux-3.5-rc5.orig/drivers/i2c/busses/i2c-i801.c 2012-07-02 18:09:59.000000000 +0200 > +++ linux-3.5-rc5/drivers/i2c/busses/i2c-i801.c 2012-07-03 10:07:42.254632389 +0200 > @@ -207,13 +207,13 @@ static int i801_check_pre(struct i801_pr > } > > /* Convert the status register to an error code, and clear it. */ > -static int i801_check_post(struct i801_priv *priv, int status, int timeout) > +static int i801_check_post(struct i801_priv *priv, int status) > { > int result = 0; > + int clear; > > /* If the SMBus is still busy, we give up */ > - if (timeout) { > - dev_err(&priv->pci_dev->dev, "Transaction timeout\n"); > + if (unlikely(status < 0)) { > /* try to stop the current command */ > dev_dbg(&priv->pci_dev->dev, "Terminating the current operation\n"); > outb_p(inb_p(SMBHSTCNT(priv)) | SMBHSTCNT_KILL, > @@ -245,42 +245,60 @@ static int i801_check_post(struct i801_p > dev_dbg(&priv->pci_dev->dev, "Lost arbitration\n"); > } > > - if (result) { > - /* Clear error flags */ > - outb_p(status & STATUS_FLAGS, SMBHSTSTS(priv)); > - status = inb_p(SMBHSTSTS(priv)) & STATUS_FLAGS; > - if (status) { > - dev_warn(&priv->pci_dev->dev, "Failed clearing status " > - "flags at end of transaction (%02x)\n", > - status); > - } > - } > + /* Clear status flags except BYTE_DONE, to be cleared by caller */ > + clear = STATUS_ERROR_FLAGS; > + if (!(status & SMBHSTSTS_BYTE_DONE)) > + clear |= SMBHSTSTS_INTR; > + outb_p(status & clear, SMBHSTSTS(priv)); This becomes simpler (see below...), since status is already masked by (STATUS_ERROR_FLAGS | SMBHSTSTS_INTR). + outb_p(status, SMBHSTSTS(priv)); > > return result; > } > > -/* wait for INTR bit as advised by Intel */ > -static void i801_wait_intr(struct i801_priv *priv) > +/* Wait for BUSY being cleared and either INTR or an error flag being set */ > +static int i801_wait_intr(struct i801_priv *priv) > { > int timeout = 0; > int status; > > + /* We will always wait for a fraction of a second! */ > do { > usleep_range(250, 500); > status = inb_p(SMBHSTSTS(priv)); > - } while ((!(status & SMBHSTSTS_INTR)) && (timeout++ < MAX_RETRIES)); > + } while (((status & SMBHSTSTS_HOST_BUSY) || > + !(status & (STATUS_ERROR_FLAGS | SMBHSTSTS_INTR))) && > + (timeout++ < MAX_RETRIES)); >From your experiments, I think you saw that the final status (ERROR | INTR) will be set before or at the same time as HOST_BUSY -> 0, so the loop need only check HOST_BUSY, and then just return the final status bits of the transfer (ie, ERROR_FLAGS | INTR). So, the function name could be something like: i801_wait_xfer_status() > - if (timeout > MAX_RETRIES) > + if (timeout > MAX_RETRIES) { > dev_dbg(&priv->pci_dev->dev, "INTR Timeout!\n"); > + return -ETIMEDOUT; > + } > + return status; > +} > > - outb_p(status & STATUS_FLAGS, SMBHSTSTS(priv)); > +/* Wait for either BYTE_DONE or an error flag being set */ > +static int i801_wait_byte_done(struct i801_priv *priv) > +{ > + int timeout = 0; > + int status; > + > + /* We will always wait for a fraction of a second! */ > + do { > + usleep_range(250, 500); > + status = inb_p(SMBHSTSTS(priv)); > + } while (!(status & (STATUS_ERROR_FLAGS | SMBHSTSTS_BYTE_DONE)) && > + (timeout++ < MAX_RETRIES)); > + > + if (timeout > MAX_RETRIES) { > + dev_dbg(&priv->pci_dev->dev, "BYTE_DONE Timeout!\n"); > + return -ETIMEDOUT; > + } > + return status; > } For i801_wait_byte_done(), on success I would return 0, if an error was set, return (status & ERROR_FLAG), and of course return -ETIMEDOUT on timeout. Then, we only need to call i801_check_post() if i801_wait_byte_done() != 0... see below... > > static int i801_transaction(struct i801_priv *priv, int xact) > { > int status; > int result; > - int timeout = 0; > > result = i801_check_pre(priv); > if (result < 0) > @@ -290,19 +308,8 @@ static int i801_transaction(struct i801_ > * SMBSCMD are passed in xact */ > outb_p(xact | SMBHSTCNT_START, SMBHSTCNT(priv)); > > - /* We will always wait for a fraction of a second! */ > - do { > - usleep_range(250, 500); > - status = inb_p(SMBHSTSTS(priv)); > - } while ((status & SMBHSTSTS_HOST_BUSY) && (timeout++ < MAX_RETRIES)); > - > - result = i801_check_post(priv, status, timeout > MAX_RETRIES); > - if (result < 0) > - return result; > - > - i801_wait_intr(priv); > - > - return 0; > + status = i801_wait_intr(priv); > + return i801_check_post(priv, status); > } > > static int i801_block_transaction_by_block(struct i801_priv *priv, > @@ -353,7 +360,6 @@ static int i801_block_transaction_byte_b > int smbcmd; > int status; > int result; > - int timeout; > > result = i801_check_pre(priv); > if (result < 0) > @@ -381,15 +387,8 @@ static int i801_block_transaction_byte_b > outb_p(inb(SMBHSTCNT(priv)) | SMBHSTCNT_START, > SMBHSTCNT(priv)); > > - /* We will always wait for a fraction of a second! */ > - timeout = 0; > - do { > - usleep_range(250, 500); > - status = inb_p(SMBHSTSTS(priv)); > - } while (!(status & (SMBHSTSTS_BYTE_DONE | STATUS_ERROR_FLAGS)) > - && (timeout++ < MAX_RETRIES)); > - > - result = i801_check_post(priv, status, timeout > MAX_RETRIES); + status = i801_wait_byte_done(priv); + if (status) + goto done; > @@ -421,9 +420,8 @@ static int i801_block_transaction_byte_b > outb_p(SMBHSTSTS_BYTE_DONE, SMBHSTSTS(priv)); > } > > - i801_wait_intr(priv); > - > - return 0; > + status = i801_wait_intr(priv); done: > + return i801_check_post(priv, status); > } > > static int i801_set_block_buffer_mode(struct i801_priv *priv) > > > -- > Jean Delvare While we are at this, let's clean up the "Illegal SMBus block read size" error path, as well. Why not use the "KILL" bit to terminate the transaction? -Daniel -- To unsubscribe from this list: send the line "unsubscribe linux-i2c" in the body of a message to majordomo@xxxxxxxxxxxxxxx More majordomo info at http://vger.kernel.org/majordomo-info.html