Commit 6cad93c4 authored by Jean Delvare's avatar Jean Delvare Committed by Jean Delvare

i2c-i801: Consolidate polling

(Based on earlier work by Daniel Kurtz.)

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
(regardless of PEC being used), check for both BUSY being cleared and
INTR or any error flag being set. This ensures proper action for all
transaction types.
Signed-off-by: default avatarJean Delvare <khali@linux-fr.org>
Cc: Daniel Kurtz <djkurtz@chromium.org>
parent 37af8711
...@@ -206,13 +206,17 @@ static int i801_check_pre(struct i801_priv *priv) ...@@ -206,13 +206,17 @@ static int i801_check_pre(struct i801_priv *priv)
return 0; return 0;
} }
/* Convert the status register to an error code, and clear it. */ /*
static int i801_check_post(struct i801_priv *priv, int status, int timeout) * Convert the status register to an error code, and clear it.
* Note that status only contains the bits we want to clear, not the
* actual register value.
*/
static int i801_check_post(struct i801_priv *priv, int status)
{ {
int result = 0; int result = 0;
/* If the SMBus is still busy, we give up */ /* If the SMBus is still busy, we give up */
if (timeout) { if (unlikely(status < 0)) {
dev_err(&priv->pci_dev->dev, "Transaction timeout\n"); dev_err(&priv->pci_dev->dev, "Transaction timeout\n");
/* try to stop the current command */ /* try to stop the current command */
dev_dbg(&priv->pci_dev->dev, "Terminating the current operation\n"); dev_dbg(&priv->pci_dev->dev, "Terminating the current operation\n");
...@@ -245,64 +249,68 @@ static int i801_check_post(struct i801_priv *priv, int status, int timeout) ...@@ -245,64 +249,68 @@ static int i801_check_post(struct i801_priv *priv, int status, int timeout)
dev_dbg(&priv->pci_dev->dev, "Lost arbitration\n"); dev_dbg(&priv->pci_dev->dev, "Lost arbitration\n");
} }
if (result) { /* Clear status flags except BYTE_DONE, to be cleared by caller */
/* Clear error flags */ outb_p(status, SMBHSTSTS(priv));
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);
}
}
return result; return result;
} }
static int i801_transaction(struct i801_priv *priv, int xact) /* Wait for BUSY being cleared and either INTR or an error flag being set */
static int i801_wait_intr(struct i801_priv *priv)
{ {
int status;
int result;
int timeout = 0; int timeout = 0;
int status;
result = i801_check_pre(priv);
if (result < 0)
return result;
/* the current contents of SMBHSTCNT can be overwritten, since PEC,
* SMBSCMD are passed in xact */
outb_p(xact | SMBHSTCNT_START, SMBHSTCNT(priv));
/* We will always wait for a fraction of a second! */ /* We will always wait for a fraction of a second! */
do { do {
usleep_range(250, 500); usleep_range(250, 500);
status = inb_p(SMBHSTSTS(priv)); status = inb_p(SMBHSTSTS(priv));
} while ((status & SMBHSTSTS_HOST_BUSY) && (timeout++ < MAX_RETRIES)); } while (((status & SMBHSTSTS_HOST_BUSY) ||
!(status & (STATUS_ERROR_FLAGS | SMBHSTSTS_INTR))) &&
result = i801_check_post(priv, status, timeout > MAX_RETRIES); (timeout++ < MAX_RETRIES));
if (result < 0)
return result;
outb_p(SMBHSTSTS_INTR, SMBHSTSTS(priv)); if (timeout > MAX_RETRIES) {
return 0; dev_dbg(&priv->pci_dev->dev, "INTR Timeout!\n");
return -ETIMEDOUT;
}
return status & (STATUS_ERROR_FLAGS | SMBHSTSTS_INTR);
} }
/* wait for INTR bit as advised by Intel */ /* Wait for either BYTE_DONE or an error flag being set */
static void i801_wait_hwpec(struct i801_priv *priv) static int i801_wait_byte_done(struct i801_priv *priv)
{ {
int timeout = 0; int timeout = 0;
int status; int status;
/* We will always wait for a fraction of a second! */
do { do {
usleep_range(250, 500); usleep_range(250, 500);
status = inb_p(SMBHSTSTS(priv)); status = inb_p(SMBHSTSTS(priv));
} while ((!(status & SMBHSTSTS_INTR)) } while (!(status & (STATUS_ERROR_FLAGS | SMBHSTSTS_BYTE_DONE)) &&
&& (timeout++ < MAX_RETRIES)); (timeout++ < MAX_RETRIES));
if (timeout > MAX_RETRIES) if (timeout > MAX_RETRIES) {
dev_dbg(&priv->pci_dev->dev, "PEC Timeout!\n"); dev_dbg(&priv->pci_dev->dev, "BYTE_DONE Timeout!\n");
return -ETIMEDOUT;
}
return status & STATUS_ERROR_FLAGS;
}
static int i801_transaction(struct i801_priv *priv, int xact)
{
int status;
int result;
result = i801_check_pre(priv);
if (result < 0)
return result;
/* the current contents of SMBHSTCNT can be overwritten, since PEC,
* SMBSCMD are passed in xact */
outb_p(xact | SMBHSTCNT_START, SMBHSTCNT(priv));
outb_p(status & STATUS_FLAGS, SMBHSTSTS(priv)); status = i801_wait_intr(priv);
return i801_check_post(priv, status);
} }
static int i801_block_transaction_by_block(struct i801_priv *priv, static int i801_block_transaction_by_block(struct i801_priv *priv,
...@@ -353,7 +361,6 @@ static int i801_block_transaction_byte_by_byte(struct i801_priv *priv, ...@@ -353,7 +361,6 @@ static int i801_block_transaction_byte_by_byte(struct i801_priv *priv,
int smbcmd; int smbcmd;
int status; int status;
int result; int result;
int timeout;
result = i801_check_pre(priv); result = i801_check_pre(priv);
if (result < 0) if (result < 0)
...@@ -381,17 +388,9 @@ static int i801_block_transaction_byte_by_byte(struct i801_priv *priv, ...@@ -381,17 +388,9 @@ static int i801_block_transaction_byte_by_byte(struct i801_priv *priv,
outb_p(inb(SMBHSTCNT(priv)) | SMBHSTCNT_START, outb_p(inb(SMBHSTCNT(priv)) | SMBHSTCNT_START,
SMBHSTCNT(priv)); SMBHSTCNT(priv));
/* We will always wait for a fraction of a second! */ status = i801_wait_byte_done(priv);
timeout = 0; if (status)
do { goto exit;
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);
if (result < 0)
return result;
if (i == 1 && read_write == I2C_SMBUS_READ if (i == 1 && read_write == I2C_SMBUS_READ
&& command != I2C_SMBUS_I2C_BLOCK_DATA) { && command != I2C_SMBUS_I2C_BLOCK_DATA) {
...@@ -418,10 +417,12 @@ static int i801_block_transaction_byte_by_byte(struct i801_priv *priv, ...@@ -418,10 +417,12 @@ static int i801_block_transaction_byte_by_byte(struct i801_priv *priv,
outb_p(data->block[i+1], SMBBLKDAT(priv)); outb_p(data->block[i+1], SMBBLKDAT(priv));
/* signals SMBBLKDAT ready */ /* signals SMBBLKDAT ready */
outb_p(SMBHSTSTS_BYTE_DONE | SMBHSTSTS_INTR, SMBHSTSTS(priv)); outb_p(SMBHSTSTS_BYTE_DONE, SMBHSTSTS(priv));
} }
return 0; status = i801_wait_intr(priv);
exit:
return i801_check_post(priv, status);
} }
static int i801_set_block_buffer_mode(struct i801_priv *priv) static int i801_set_block_buffer_mode(struct i801_priv *priv)
...@@ -476,9 +477,6 @@ static int i801_block_transaction(struct i801_priv *priv, ...@@ -476,9 +477,6 @@ static int i801_block_transaction(struct i801_priv *priv,
read_write, read_write,
command, hwpec); command, hwpec);
if (result == 0 && hwpec)
i801_wait_hwpec(priv);
if (command == I2C_SMBUS_I2C_BLOCK_DATA if (command == I2C_SMBUS_I2C_BLOCK_DATA
&& read_write == I2C_SMBUS_WRITE) { && read_write == I2C_SMBUS_WRITE) {
/* restore saved configuration register value */ /* restore saved configuration register value */
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment