Skip to content

Commit

Permalink
---
Browse files Browse the repository at this point in the history
yaml
---
r: 101313
b: refs/heads/master
c: cf898dc
h: refs/heads/master
i:
  101311: 00b4a66
v: v3
  • Loading branch information
Jean Delvare authored and Jean Delvare committed Jul 14, 2008
1 parent 8765b47 commit c256262
Show file tree
Hide file tree
Showing 2 changed files with 103 additions and 99 deletions.
2 changes: 1 addition & 1 deletion [refs]
Original file line number Diff line number Diff line change
@@ -1,2 +1,2 @@
---
refs/heads/master: 2b73809d06649fe6c7f4294b051ca4934a34bb91
refs/heads/master: cf898dc5e9dfd1487b28ca0176b68722f05d4d48
200 changes: 102 additions & 98 deletions trunk/drivers/i2c/busses/i2c-i801.c
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@
Copyright (c) 1998 - 2002 Frodo Looijaard <frodol@dds.nl>,
Philip Edelbrock <phil@netroedge.com>, and Mark D. Studebaker
<mdsxyz123@yahoo.com>
Copyright (C) 2007 Jean Delvare <khali@linux-fr.org>
Copyright (C) 2007, 2008 Jean Delvare <khali@linux-fr.org>
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
Expand Down Expand Up @@ -121,6 +121,10 @@
#define SMBHSTSTS_INTR 0x02
#define SMBHSTSTS_HOST_BUSY 0x01

#define STATUS_FLAGS (SMBHSTSTS_BYTE_DONE | SMBHSTSTS_FAILED | \
SMBHSTSTS_BUS_ERR | SMBHSTSTS_DEV_ERR | \
SMBHSTSTS_INTR)

static unsigned long i801_smba;
static unsigned char i801_original_hstcfg;
static struct pci_driver i801_driver;
Expand All @@ -132,72 +136,114 @@ static struct pci_dev *I801_dev;
#define FEATURE_I2C_BLOCK_READ (1 << 3)
static unsigned int i801_features;

static int i801_transaction(int xact)
/* Make sure the SMBus host is ready to start transmitting.
Return 0 if it is, -EBUSY if it is not. */
static int i801_check_pre(void)
{
int status;
int result = 0;
int timeout = 0;

/* Make sure the SMBus host is ready to start transmitting */
/* 0x1f = Failed, Bus_Err, Dev_Err, Intr, Host_Busy */
if ((status = (0x1f & inb_p(SMBHSTSTS))) != 0x00) {
dev_dbg(&I801_dev->dev, "SMBus busy (%02x). Resetting...\n",
status = inb_p(SMBHSTSTS);
if (status & SMBHSTSTS_HOST_BUSY) {
dev_err(&I801_dev->dev, "SMBus is busy, can't use it!\n");
return -EBUSY;
}

status &= STATUS_FLAGS;
if (status) {
dev_dbg(&I801_dev->dev, "Clearing status flags (%02x)\n",
status);
outb_p(status, SMBHSTSTS);
if ((status = (0x1f & inb_p(SMBHSTSTS))) != 0x00) {
dev_dbg(&I801_dev->dev, "Failed! (%02x)\n", status);
status = inb_p(SMBHSTSTS) & STATUS_FLAGS;
if (status) {
dev_err(&I801_dev->dev,
"Failed clearing status flags (%02x)\n",
status);
return -EBUSY;
} else {
dev_dbg(&I801_dev->dev, "Successful!\n");
}
}

/* the current contents of SMBHSTCNT can be overwritten, since PEC,
* INTREN, SMBSCMD are passed in xact */
outb_p(xact | I801_START, SMBHSTCNT);
return 0;
}

/* We will always wait for a fraction of a second! */
do {
msleep(1);
status = inb_p(SMBHSTSTS);
} while ((status & SMBHSTSTS_HOST_BUSY) && (timeout++ < MAX_TIMEOUT));
/* Convert the status register to an error code, and clear it. */
static int i801_check_post(int status, int timeout)
{
int result = 0;

/* If the SMBus is still busy, we give up */
if (timeout >= MAX_TIMEOUT) {
dev_dbg(&I801_dev->dev, "SMBus Timeout!\n");
result = -ETIMEDOUT;
if (timeout) {
dev_err(&I801_dev->dev, "Transaction timeout\n");
/* try to stop the current command */
dev_dbg(&I801_dev->dev, "Terminating the current operation\n");
outb_p(inb_p(SMBHSTCNT) | SMBHSTCNT_KILL, SMBHSTCNT);
msleep(1);
outb_p(inb_p(SMBHSTCNT) & (~SMBHSTCNT_KILL), SMBHSTCNT);

/* Check if it worked */
status = inb_p(SMBHSTSTS);
if ((status & SMBHSTSTS_HOST_BUSY) ||
!(status & SMBHSTSTS_FAILED))
dev_err(&I801_dev->dev,
"Failed terminating the transaction\n");
outb_p(STATUS_FLAGS, SMBHSTSTS);
return -ETIMEDOUT;
}

if (status & SMBHSTSTS_FAILED) {
result = -EIO;
dev_dbg(&I801_dev->dev, "Error: Failed bus transaction\n");
dev_err(&I801_dev->dev, "Transaction failed\n");
}
if (status & SMBHSTSTS_DEV_ERR) {
result = -ENXIO;
dev_dbg(&I801_dev->dev, "No response\n");
}

if (status & SMBHSTSTS_BUS_ERR) {
result = -EAGAIN;
dev_dbg(&I801_dev->dev, "Lost arbitration\n");
}

if (status & SMBHSTSTS_DEV_ERR) {
result = -ENXIO;
dev_dbg(&I801_dev->dev, "Error: no response!\n");
if (result) {
/* Clear error flags */
outb_p(status & STATUS_FLAGS, SMBHSTSTS);
status = inb_p(SMBHSTSTS) & STATUS_FLAGS;
if (status) {
dev_warn(&I801_dev->dev, "Failed clearing status "
"flags at end of transaction (%02x)\n",
status);
}
}

if ((inb_p(SMBHSTSTS) & 0x1f) != 0x00)
outb_p(inb(SMBHSTSTS), SMBHSTSTS);

if ((status = (0x1f & inb_p(SMBHSTSTS))) != 0x00) {
dev_dbg(&I801_dev->dev, "Failed reset at end of transaction "
"(%02x)\n", status);
}
return result;
}

static int i801_transaction(int xact)
{
int status;
int result;
int timeout = 0;

result = i801_check_pre();
if (result < 0)
return result;

/* the current contents of SMBHSTCNT can be overwritten, since PEC,
* INTREN, SMBSCMD are passed in xact */
outb_p(xact | I801_START, SMBHSTCNT);

/* We will always wait for a fraction of a second! */
do {
msleep(1);
status = inb_p(SMBHSTSTS);
} while ((status & SMBHSTSTS_HOST_BUSY) && (timeout++ < MAX_TIMEOUT));

result = i801_check_post(status, timeout >= MAX_TIMEOUT);
if (result < 0)
return result;

outb_p(SMBHSTSTS_INTR, SMBHSTSTS);
return 0;
}

/* wait for INTR bit as advised by Intel */
static void i801_wait_hwpec(void)
{
Expand Down Expand Up @@ -256,9 +302,12 @@ static int i801_block_transaction_byte_by_byte(union i2c_smbus_data *data,
int i, len;
int smbcmd;
int status;
int result = 0;
int result;
int timeout;
unsigned char errmask;

result = i801_check_pre();
if (result < 0)
return result;

len = data->block[0];

Expand All @@ -282,31 +331,6 @@ static int i801_block_transaction_byte_by_byte(union i2c_smbus_data *data,
}
outb_p(smbcmd | ENABLE_INT9, SMBHSTCNT);

/* Make sure the SMBus host is ready to start transmitting */
status = inb_p(SMBHSTSTS);
if (i == 1) {
/* Erroneous conditions before transaction:
* Byte_Done, Failed, Bus_Err, Dev_Err, Intr, Host_Busy */
errmask = 0x9f;
} else {
/* Erroneous conditions during transaction:
* Failed, Bus_Err, Dev_Err, Intr */
errmask = 0x1e;
}
if (status & errmask) {
dev_dbg(&I801_dev->dev, "SMBus busy (%02x). "
"Resetting...\n", status);
outb_p(status, SMBHSTSTS);
if (((status = inb_p(SMBHSTSTS)) & errmask) != 0x00) {
dev_err(&I801_dev->dev,
"Reset failed! (%02x)\n", status);
return -EBUSY;
}
if (i != 1)
/* if die in middle of block transaction, fail */
return -EIO;
}

if (i == 1)
outb_p(inb(SMBHSTCNT) | I801_START, SMBHSTCNT);

Expand All @@ -319,36 +343,23 @@ static int i801_block_transaction_byte_by_byte(union i2c_smbus_data *data,
while ((!(status & SMBHSTSTS_BYTE_DONE))
&& (timeout++ < MAX_TIMEOUT));

/* If the SMBus is still busy, we give up */
if (timeout >= MAX_TIMEOUT) {
/* try to stop the current command */
dev_dbg(&I801_dev->dev, "Terminating the current "
"operation\n");
outb_p(inb_p(SMBHSTCNT) | SMBHSTCNT_KILL, SMBHSTCNT);
msleep(1);
outb_p(inb_p(SMBHSTCNT) & (~SMBHSTCNT_KILL),
SMBHSTCNT);
result = -ETIMEDOUT;
dev_dbg(&I801_dev->dev, "SMBus Timeout!\n");
}

if (status & SMBHSTSTS_FAILED) {
result = -EIO;
dev_dbg(&I801_dev->dev,
"Error: Failed bus transaction\n");
} else if (status & SMBHSTSTS_BUS_ERR) {
result = -EAGAIN;
dev_dbg(&I801_dev->dev, "Lost arbitration\n");
} else if (status & SMBHSTSTS_DEV_ERR) {
result = -ENXIO;
dev_dbg(&I801_dev->dev, "Error: no response!\n");
}
result = i801_check_post(status, timeout >= MAX_TIMEOUT);
if (result < 0)
return result;

if (i == 1 && read_write == I2C_SMBUS_READ
&& command != I2C_SMBUS_I2C_BLOCK_DATA) {
len = inb_p(SMBHSTDAT0);
if (len < 1 || len > I2C_SMBUS_BLOCK_MAX)
if (len < 1 || len > I2C_SMBUS_BLOCK_MAX) {
dev_err(&I801_dev->dev,
"Illegal SMBus block read size %d\n",
len);
/* Recover */
while (inb_p(SMBHSTSTS) & SMBHSTSTS_HOST_BUSY)
outb_p(SMBHSTSTS_BYTE_DONE, SMBHSTSTS);
outb_p(SMBHSTSTS_INTR, SMBHSTSTS);
return -EPROTO;
}
data->block[0] = len;
}

Expand All @@ -357,19 +368,12 @@ static int i801_block_transaction_byte_by_byte(union i2c_smbus_data *data,
data->block[i] = inb_p(SMBBLKDAT);
if (read_write == I2C_SMBUS_WRITE && i+1 <= len)
outb_p(data->block[i+1], SMBBLKDAT);
if ((status & 0x9e) != 0x00)
outb_p(status, SMBHSTSTS); /* signals SMBBLKDAT ready */

if ((status = (0x1e & inb_p(SMBHSTSTS))) != 0x00) {
dev_dbg(&I801_dev->dev,
"Bad status (%02x) at end of transaction\n",
status);
}

if (result < 0)
return result;
/* signals SMBBLKDAT ready */
outb_p(SMBHSTSTS_BYTE_DONE | SMBHSTSTS_INTR, SMBHSTSTS);
}
return result;

return 0;
}

static int i801_set_block_buffer_mode(void)
Expand Down

0 comments on commit c256262

Please sign in to comment.