Re: [PATCH] i2c-i801: Consolidate polling

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

 



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


[Index of Archives]     [Linux GPIO]     [Linux SPI]     [Linux Hardward Monitoring]     [LM Sensors]     [Linux USB Devel]     [Linux Media]     [Video for Linux]     [Linux Audio Users]     [Yosemite News]     [Linux Kernel]     [Linux SCSI]

  Powered by Linux