From: minyard@acm.org (Corey Minyard)
To: Greg KH <greg@kroah.com>, lkml <linux-kernel@vger.kernel.org>,
Sensors <sensors@stimpy.netroedge.com>
Subject: [PATCH] Add a non-blocking interface to the I2C code, part 7
Date: Thu, 19 May 2005 06:25:40 +0000 [thread overview]
Message-ID: <42261C60.1050806@acm.org> (raw)
In-Reply-To: <42261AFB.40001@acm.org>
See part 1 for details on what this does...
-------------- next part --------------
This patch modifies the I801 SMBus driver to use the non-blocking
interface.
Signed-off-by: Corey Minyard <minyard@acm.org>
Index: linux-2.6.11-rc5-mm1/drivers/i2c/busses/i2c-i801.c
=================================--- linux-2.6.11-rc5-mm1.orig/drivers/i2c/busses/i2c-i801.c
+++ linux-2.6.11-rc5-mm1/drivers/i2c/busses/i2c-i801.c
@@ -40,6 +40,14 @@
/* Note: we assume there can only be one I801, with one SMBus interface */
+/* Another note: This interface is extremely sensitive to timing and
+ failure handling. If you don't wait at least one jiffie after
+ starting the transaction before checking things, you will screw it
+ up. If you don't wait a jiffie after the final check, you will
+ screw it up. If you screw it up by these manners or by abandoning
+ an operation in progress, the I2C bus is likely stuck and won't
+ work any more. Gotta love this hardware. */
+
#include <linux/config.h>
#include <linux/module.h>
#include <linux/pci.h>
@@ -79,7 +87,8 @@
#define SMBHSTCFG_I2C_EN 4
/* Other settings */
-#define MAX_TIMEOUT 100
+#define MAX_TIMEOUT_US 100000
+#define RETRY_TIME_US 500 /* Retry minimum is 500us */
#define ENABLE_INT9 0 /* set to 0x01 to enable - untested */
/* I801 command constants */
@@ -105,21 +114,31 @@
"Forcibly enable the I801 at the given address. "
"EXTREMELY DANGEROUS!");
-static int i801_transaction(void);
-static int i801_block_transaction(union i2c_smbus_data *data,
- char read_write, int command);
-
static unsigned short i801_smba;
static struct pci_dev *I801_dev;
static int isich4;
+struct i801_i2c_data
+{
+ int i;
+ int len;
+ unsigned char hostc;
+ int block;
+ int hwpec;
+ int xact;
+ int hststs;
+ int wait_intr;
+ int finished;
+};
+struct i801_i2c_data i801_data;
+
static int i801_setup(struct pci_dev *dev)
{
int error_return = 0;
unsigned char temp;
/* Note: we keep on searching until we have found 'function 3' */
- if(PCI_FUNC(dev->devfn) != 3)
+ if (PCI_FUNC(dev->devfn) != 3)
return -ENODEV;
I801_dev = dev;
@@ -136,7 +155,7 @@
} else {
pci_read_config_word(I801_dev, SMBBA, &i801_smba);
i801_smba &= 0xfff0;
- if(i801_smba = 0) {
+ if (i801_smba = 0) {
dev_err(&dev->dev, "SMB base address uninitialized"
"- upgrade BIOS or use force_addr=0xaddr\n");
return -ENODEV;
@@ -180,12 +199,93 @@
return error_return;
}
-static int i801_transaction(void)
+static void i801_check_hststs(struct i2c_adapter *adap,
+ struct i2c_op_q_entry *entry,
+ struct i801_i2c_data *d)
+{
+ if (d->hststs & 0x10) {
+ entry->result = -EIO;
+ dev_dbg(&I801_dev->dev,
+ "Error: Failed bus transaction\n");
+ } else if (d->hststs & 0x08) {
+ entry->result = -EIO;
+ dev_err(&I801_dev->dev, "Bus collision!\n");
+ /* Clock stops and slave is stuck in mid-transmission */
+ } else if (d->hststs & 0x04) {
+ entry->result = -EIO;
+ dev_dbg(&I801_dev->dev, "Error: no response!\n");
+ }
+}
+
+static void i801_finish(struct i2c_adapter *adap,
+ struct i2c_op_q_entry *entry,
+ struct i801_i2c_data *d)
+{
+ d->finished = 1;
+
+#ifdef HAVE_PEC
+ if (isich4 && d->hwpec) {
+ if (entry->smbus.size != I2C_SMBUS_QUICK &&
+ entry->smbus.size != I2C_SMBUS_I2C_BLOCK_DATA)
+ outb_p(0, SMBAUXCTL);
+ }
+#endif
+
+ if (d->block || (entry->result < 0) ||
+ ((entry->smbus.read_write = I2C_SMBUS_WRITE)
+ || (d->xact = I801_QUICK)))
+ return;
+
+ switch (d->xact & 0x7f) {
+ case I801_BYTE: /* Result put in SMBHSTDAT0 */
+ case I801_BYTE_DATA:
+ entry->smbus.data->byte = inb_p(SMBHSTDAT0);
+ break;
+ case I801_WORD_DATA:
+ entry->smbus.data->word = inb_p(SMBHSTDAT0)
+ + (inb_p(SMBHSTDAT1) << 8);
+ break;
+ }
+}
+
+static void i801_transaction_final_check(struct i2c_adapter *adap,
+ struct i2c_op_q_entry *entry,
+ struct i801_i2c_data *d)
+{
+ i801_check_hststs(adap, entry, d);
+
+ if ((inb_p(SMBHSTSTS) & 0x1f) != 0x00)
+ outb_p(inb(SMBHSTSTS), SMBHSTSTS);
+
+ if ((d->hststs = (0x1f & inb_p(SMBHSTSTS))) != 0x00) {
+ dev_dbg(&I801_dev->dev, "Failed reset at end of transaction"
+ "(%02x)\n", d->hststs);
+ }
+ dev_dbg(&I801_dev->dev, "Transaction (post): CNT=%02x, CMD=%02x, "
+ "ADD=%02x, DAT0=%02x, DAT1=%02x\n", inb_p(SMBHSTCNT),
+ inb_p(SMBHSTCMD), inb_p(SMBHSTADD), inb_p(SMBHSTDAT0),
+ inb_p(SMBHSTDAT1));
+}
+
+static void i801_transaction_poll(struct i2c_adapter *adap,
+ struct i2c_op_q_entry *entry,
+ struct i801_i2c_data *d)
{
- int temp;
- int result = 0;
- int timeout = 0;
+ d->hststs = inb_p(SMBHSTSTS);
+ if (!(d->hststs & 0x01)) {
+ i801_transaction_final_check(adap, entry, d);
+ i801_finish(adap, entry, d);
+ } else if (entry->time_left <= 0) {
+ dev_dbg(&I801_dev->dev, "SMBus Timeout!\n");
+ entry->result = -EIO;
+ i801_transaction_final_check(adap, entry, d);
+ }
+}
+static void i801_transaction_start(struct i2c_adapter *adap,
+ struct i2c_op_q_entry *entry,
+ struct i801_i2c_data *d)
+{
dev_dbg(&I801_dev->dev, "Transaction (pre): CNT=%02x, CMD=%02x,"
"ADD=%02x, DAT0=%02x, DAT1=%02x\n", inb_p(SMBHSTCNT),
inb_p(SMBHSTCMD), inb_p(SMBHSTADD), inb_p(SMBHSTDAT0),
@@ -193,331 +293,362 @@
/* Make sure the SMBus host is ready to start transmitting */
/* 0x1f = Failed, Bus_Err, Dev_Err, Intr, Host_Busy */
- if ((temp = (0x1f & inb_p(SMBHSTSTS))) != 0x00) {
+ if ((d->hststs = (0x1f & inb_p(SMBHSTSTS))) != 0x00) {
dev_dbg(&I801_dev->dev, "SMBus busy (%02x). Resetting... \n",
- temp);
- outb_p(temp, SMBHSTSTS);
- if ((temp = (0x1f & inb_p(SMBHSTSTS))) != 0x00) {
- dev_dbg(&I801_dev->dev, "Failed! (%02x)\n", temp);
- return -1;
+ d->hststs);
+ outb_p(d->hststs, SMBHSTSTS);
+ if ((d->hststs = (0x1f & inb_p(SMBHSTSTS))) != 0x00) {
+ dev_dbg(&I801_dev->dev, "Failed! (%02x)\n", d->hststs);
+ entry->result = -EIO;
+ return;
} else {
dev_dbg(&I801_dev->dev, "Successfull!\n");
}
}
outb_p(inb(SMBHSTCNT) | I801_START, SMBHSTCNT);
+}
- /* We will always wait for a fraction of a second! */
- do {
- msleep(1);
- temp = inb_p(SMBHSTSTS);
- } while ((temp & 0x01) && (timeout++ < MAX_TIMEOUT));
-
- /* If the SMBus is still busy, we give up */
- if (timeout >= MAX_TIMEOUT) {
- dev_dbg(&I801_dev->dev, "SMBus Timeout!\n");
- result = -1;
+static void i801_block_finish(struct i2c_adapter *adap,
+ struct i2c_op_q_entry *entry,
+ struct i801_i2c_data *d)
+{
+ if (entry->smbus.size = I2C_SMBUS_I2C_BLOCK_DATA) {
+ /* restore saved configuration register value */
+ pci_write_config_byte(I801_dev, SMBHSTCFG, d->hostc);
}
- if (temp & 0x10) {
- result = -1;
- dev_dbg(&I801_dev->dev, "Error: Failed bus transaction\n");
- }
+ i801_finish(adap, entry, d);
+}
- if (temp & 0x08) {
- result = -1;
- dev_err(&I801_dev->dev, "Bus collision! SMBus may be locked "
- "until next hard reset. (sorry!)\n");
- /* Clock stops and slave is stuck in mid-transmission */
- }
+static void i801_block_poll_wait_intr(struct i2c_adapter *adap,
+ struct i2c_op_q_entry *entry,
+ struct i801_i2c_data *d)
+{
+#ifdef HAVE_PEC
+ if (entry->result >= 0 &&
+ isich4 &&
+ entry->smbus.size = I2C_SMBUS_BLOCK_DATA_PEC)
+ {
+ /* wait for INTR bit as advised by Intel */
+ d->hststs = inb_p(SMBHSTSTS);
+ if (d->hststs & 0x02) {
+ outb_p(d->hststs, SMBHSTSTS);
+ i801_block_finish(adap, entry, d);
+ } else if (entry->time_left <= 0) {
+ /* Timed out */
+ outb_p(d->hststs, SMBHSTSTS);
+ entry->result = -EIO;
+ dev_dbg(&I801_dev->dev, "PEC Timeout!\n");
+ }
+ } else
+#endif
+ i801_block_finish(adap, entry, d);
+}
- if (temp & 0x04) {
- result = -1;
- dev_dbg(&I801_dev->dev, "Error: no response!\n");
+static void i801_block_next_byte(struct i2c_adapter *adap,
+ struct i2c_op_q_entry *entry,
+ struct i801_i2c_data *d)
+{
+ int smbcmd;
+ unsigned char errmask;
+
+ if (d->i > d->len) {
+ d->wait_intr = 1;
+ entry->time_left = MAX_TIMEOUT_US;
+ i801_block_poll_wait_intr(adap, entry, d);
+ return;
}
- if ((inb_p(SMBHSTSTS) & 0x1f) != 0x00)
- outb_p(inb(SMBHSTSTS), SMBHSTSTS);
+ if (d->i = d->len && entry->smbus.read_write = I2C_SMBUS_READ)
+ smbcmd = I801_BLOCK_LAST;
+ else
+ smbcmd = I801_BLOCK_DATA;
+ outb_p(smbcmd | ENABLE_INT9, SMBHSTCNT);
- if ((temp = (0x1f & inb_p(SMBHSTSTS))) != 0x00) {
- dev_dbg(&I801_dev->dev, "Failed reset at end of transaction"
- "(%02x)\n", temp);
+ dev_dbg(&I801_dev->dev, "Block (pre %d): CNT=%02x, CMD=%02x, "
+ "ADD=%02x, DAT0=%02x, BLKDAT=%02x\n", d->i,
+ inb_p(SMBHSTCNT), inb_p(SMBHSTCMD), inb_p(SMBHSTADD),
+ inb_p(SMBHSTDAT0), inb_p(SMBBLKDAT));
+
+ /* Make sure the SMBus host is ready to start transmitting */
+ d->hststs = inb_p(SMBHSTSTS);
+ if (d->i = 1) {
+ /* Erronenous conditions before transaction:
+ * Byte_Done, Failed, Bus_Err, Dev_Err, Intr, Host_Busy */
+ errmask=0x9f;
+ } else {
+ /* Erronenous conditions during transaction:
+ * Failed, Bus_Err, Dev_Err, Intr */
+ errmask=0x1e;
+ }
+ if (d->hststs & errmask) {
+ dev_dbg(&I801_dev->dev, "SMBus busy (%02x). "
+ "Resetting... \n", d->hststs);
+ outb_p(d->hststs, SMBHSTSTS);
+ if (((d->hststs = inb_p(SMBHSTSTS)) & errmask) != 0x00) {
+ dev_err(&I801_dev->dev,
+ "Reset failed! (%02x)\n", d->hststs);
+ entry->result = -EIO;
+ return;
+ }
+ if (d->i != 1) {
+ /* if die in middle of block transaction, fail */
+ entry->result = -EIO;
+ return;
+ }
}
- dev_dbg(&I801_dev->dev, "Transaction (post): CNT=%02x, CMD=%02x, "
- "ADD=%02x, DAT0=%02x, DAT1=%02x\n", inb_p(SMBHSTCNT),
- inb_p(SMBHSTCMD), inb_p(SMBHSTADD), inb_p(SMBHSTDAT0),
- inb_p(SMBHSTDAT1));
- return result;
+
+ if (d->i = 1)
+ outb_p(inb(SMBHSTCNT) | I801_START, SMBHSTCNT);
}
-/* All-inclusive block transaction function */
-static int i801_block_transaction(union i2c_smbus_data *data, char read_write,
- int command)
+/* Called on timer ticks. This checks the result of the
+ transaction. */
+static void i801_block_poll(struct i2c_adapter *adap,
+ struct i2c_op_q_entry *entry,
+ struct i801_i2c_data *d)
{
- int i, len;
- int smbcmd;
- int temp;
- int result = 0;
- int timeout;
- unsigned char hostc, errmask;
-
- if (command = I2C_SMBUS_I2C_BLOCK_DATA) {
- if (read_write = I2C_SMBUS_WRITE) {
- /* set I2C_EN bit in configuration register */
- pci_read_config_byte(I801_dev, SMBHSTCFG, &hostc);
- pci_write_config_byte(I801_dev, SMBHSTCFG,
- hostc | SMBHSTCFG_I2C_EN);
- } else {
- dev_err(&I801_dev->dev,
- "I2C_SMBUS_I2C_BLOCK_READ not DB!\n");
- return -1;
+ d->hststs = inb_p(SMBHSTSTS);
+ if (!(d->hststs & 0x80)) {
+ /* Not ready yet */
+ if (entry->time_left <= 0) {
+ dev_dbg(&I801_dev->dev, "SMBus Timeout!\n");
+ entry->result = -EIO;
}
+ return;
}
- if (read_write = I2C_SMBUS_WRITE) {
- len = data->block[0];
- if (len < 1)
- len = 1;
- if (len > 32)
- len = 32;
- outb_p(len, SMBHSTDAT0);
- outb_p(data->block[1], SMBBLKDAT);
+ i801_check_hststs(adap, entry, d);
+ if (d->i = 1 && entry->smbus.read_write = I2C_SMBUS_READ) {
+ d->len = inb_p(SMBHSTDAT0);
+ if (d->len < 1)
+ d->len = 1;
+ if (d->len > 32)
+ d->len = 32;
+ entry->smbus.data->block[0] = d->len;
+ }
+
+ /* Retrieve/store value in SMBBLKDAT */
+ if (entry->smbus.read_write = I2C_SMBUS_READ)
+ entry->smbus.data->block[d->i] = inb_p(SMBBLKDAT);
+ if (entry->smbus.read_write = I2C_SMBUS_WRITE && d->i+1 <= d->len)
+ outb_p(entry->smbus.data->block[d->i+1], SMBBLKDAT);
+ if ((d->hststs & 0x9e) != 0x00)
+ outb_p(d->hststs, SMBHSTSTS); /* signals SMBBLKDAT ready */
+
+ if ((d->hststs = (0x1e & inb_p(SMBHSTSTS))) != 0x00) {
+ dev_dbg(&I801_dev->dev,
+ "Bad status (%02x) at end of transaction\n",
+ d->hststs);
+ }
+ dev_dbg(&I801_dev->dev, "Block (post %d): CNT=%02x, CMD=%02x, "
+ "ADD=%02x, DAT0=%02x, BLKDAT=%02x\n", d->i,
+ inb_p(SMBHSTCNT), inb_p(SMBHSTCMD), inb_p(SMBHSTADD),
+ inb_p(SMBHSTDAT0), inb_p(SMBBLKDAT));
+
+ if (entry->result)
+ return;
+
+ (d->i)++;
+ i801_block_next_byte(adap, entry, d);
+}
+
+static void i801_block_start(struct i2c_adapter *adap,
+ struct i2c_op_q_entry *entry,
+ struct i801_i2c_data *d)
+{
+ if (entry->smbus.read_write = I2C_SMBUS_WRITE) {
+ d->len = entry->smbus.data->block[0];
+ if (d->len < 1)
+ d->len = 1;
+ if (d->len > 32)
+ d->len = 32;
+ outb_p(d->len, SMBHSTDAT0);
+ outb_p(entry->smbus.data->block[1], SMBBLKDAT);
} else {
- len = 32; /* max for reads */
+ d->len = 32; /* max for reads */
}
- if(isich4 && command != I2C_SMBUS_I2C_BLOCK_DATA) {
+ if(isich4 && entry->smbus.size != I2C_SMBUS_I2C_BLOCK_DATA) {
/* set 32 byte buffer */
}
- for (i = 1; i <= len; i++) {
- if (i = len && read_write = I2C_SMBUS_READ)
- smbcmd = I801_BLOCK_LAST;
- else
- smbcmd = I801_BLOCK_DATA;
- outb_p(smbcmd | ENABLE_INT9, SMBHSTCNT);
-
- dev_dbg(&I801_dev->dev, "Block (pre %d): CNT=%02x, CMD=%02x, "
- "ADD=%02x, DAT0=%02x, BLKDAT=%02x\n", i,
- inb_p(SMBHSTCNT), inb_p(SMBHSTCMD), inb_p(SMBHSTADD),
- inb_p(SMBHSTDAT0), inb_p(SMBBLKDAT));
-
- /* Make sure the SMBus host is ready to start transmitting */
- temp = inb_p(SMBHSTSTS);
- if (i = 1) {
- /* Erronenous conditions before transaction:
- * Byte_Done, Failed, Bus_Err, Dev_Err, Intr, Host_Busy */
- errmask=0x9f;
- } else {
- /* Erronenous conditions during transaction:
- * Failed, Bus_Err, Dev_Err, Intr */
- errmask=0x1e;
- }
- if (temp & errmask) {
- dev_dbg(&I801_dev->dev, "SMBus busy (%02x). "
- "Resetting... \n", temp);
- outb_p(temp, SMBHSTSTS);
- if (((temp = inb_p(SMBHSTSTS)) & errmask) != 0x00) {
- dev_err(&I801_dev->dev,
- "Reset failed! (%02x)\n", temp);
- result = -1;
- goto END;
- }
- if (i != 1) {
- /* if die in middle of block transaction, fail */
- result = -1;
- goto END;
- }
- }
+ d->i = 1;
+ i801_block_next_byte(adap, entry, d);
+}
- if (i = 1)
- outb_p(inb(SMBHSTCNT) | I801_START, SMBHSTCNT);
+/* General poll routine. Called periodically by the i2c code. */
+static void i801_poll(struct i2c_adapter *adap,
+ struct i2c_op_q_entry *entry,
+ unsigned int us_since_last_poll)
+{
+ struct i801_i2c_data *d = entry->data;
- /* We will always wait for a fraction of a second! */
- timeout = 0;
- do {
- temp = inb_p(SMBHSTSTS);
- msleep(1);
- }
- while ((!(temp & 0x80))
- && (timeout++ < MAX_TIMEOUT));
+ dev_dbg(&I801_dev->dev, "Poll call for %p %p at %ld\n", adap, entry,
+ jiffies);
- /* If the SMBus is still busy, we give up */
- if (timeout >= MAX_TIMEOUT) {
- result = -1;
- dev_dbg(&I801_dev->dev, "SMBus Timeout!\n");
- }
+ if (!d)
+ /* The entry hasn't been started yet. */
+ return;
- if (temp & 0x10) {
- result = -1;
- dev_dbg(&I801_dev->dev,
- "Error: Failed bus transaction\n");
- } else if (temp & 0x08) {
- result = -1;
- dev_err(&I801_dev->dev, "Bus collision!\n");
- } else if (temp & 0x04) {
- result = -1;
- dev_dbg(&I801_dev->dev, "Error: no response!\n");
- }
+ if (d->finished) {
+ /* We delay an extra poll to keep the hardware happy.
+ Otherwise the hardware is not ready when we start
+ the next operation. */
+ i2c_op_done(adap, entry);
+ return;
+ }
- if (i = 1 && read_write = I2C_SMBUS_READ) {
- len = inb_p(SMBHSTDAT0);
- if (len < 1)
- len = 1;
- if (len > 32)
- len = 32;
- data->block[0] = len;
- }
+ /* Decrement timeout */
+ entry->time_left -= us_since_last_poll;
- /* Retrieve/store value in SMBBLKDAT */
- if (read_write = I2C_SMBUS_READ)
- data->block[i] = inb_p(SMBBLKDAT);
- if (read_write = I2C_SMBUS_WRITE && i+1 <= len)
- outb_p(data->block[i+1], SMBBLKDAT);
- if ((temp & 0x9e) != 0x00)
- outb_p(temp, SMBHSTSTS); /* signals SMBBLKDAT ready */
-
- if ((temp = (0x1e & inb_p(SMBHSTSTS))) != 0x00) {
- dev_dbg(&I801_dev->dev,
- "Bad status (%02x) at end of transaction\n",
- temp);
- }
- dev_dbg(&I801_dev->dev, "Block (post %d): CNT=%02x, CMD=%02x, "
- "ADD=%02x, DAT0=%02x, BLKDAT=%02x\n", i,
- inb_p(SMBHSTCNT), inb_p(SMBHSTCMD), inb_p(SMBHSTADD),
- inb_p(SMBHSTDAT0), inb_p(SMBBLKDAT));
+ /* Wait a jiffie normally. */
+ entry->call_again_us = RETRY_TIME_US;
- if (result < 0)
- goto END;
+ if (d->block) {
+ if (d->wait_intr) {
+ i801_block_poll_wait_intr(adap, entry, d);
+ } else {
+ i801_block_poll(adap, entry, d);
+ }
+ if (entry->result < 0)
+ /* Error, finish the transaction */
+ i801_block_finish(adap, entry, d);
+ } else {
+ i801_transaction_poll(adap, entry, d);
+ if (entry->result < 0)
+ /* Error, finish the transaction */
+ i801_finish(adap, entry, d);
}
+}
-#ifdef HAVE_PEC
- if(isich4 && command = I2C_SMBUS_BLOCK_DATA_PEC) {
- /* wait for INTR bit as advised by Intel */
- timeout = 0;
- do {
- temp = inb_p(SMBHSTSTS);
- msleep(1);
- } while ((!(temp & 0x02))
- && (timeout++ < MAX_TIMEOUT));
+/* Start a general SMBUS transaction on the i801. Figure out what
+ kind of transaction it is, set it up, and start it. */
+static void i801_start(struct i2c_adapter *adap,
+ struct i2c_op_q_entry *entry)
+{
+ struct i801_i2c_data *d = adap->algo_data;
- if (timeout >= MAX_TIMEOUT) {
- dev_dbg(&I801_dev->dev, "PEC Timeout!\n");
+ dev_dbg(&I801_dev->dev, "start call for %p %p at %ld\n", adap, entry,
+ jiffies);
+
+ if (entry->smbus.size = I2C_SMBUS_I2C_BLOCK_DATA) {
+ if (entry->smbus.read_write = I2C_SMBUS_WRITE) {
+ /* set I2C_EN bit in configuration register */
+ pci_read_config_byte(I801_dev, SMBHSTCFG, &d->hostc);
+ pci_write_config_byte(I801_dev, SMBHSTCFG,
+ d->hostc | SMBHSTCFG_I2C_EN);
+ } else {
+ dev_err(&I801_dev->dev,
+ "I2C_SMBUS_I2C_BLOCK_READ not DB!\n");
+ entry->result = -EINVAL;
+ return;
}
- outb_p(temp, SMBHSTSTS);
- }
-#endif
- result = 0;
-END:
- if (command = I2C_SMBUS_I2C_BLOCK_DATA) {
- /* restore saved configuration register value */
- pci_write_config_byte(I801_dev, SMBHSTCFG, hostc);
}
- return result;
-}
-/* Return -1 on error. */
-static s32 i801_access(struct i2c_adapter * adap, u16 addr,
- unsigned short flags, char read_write, u8 command,
- int size, union i2c_smbus_data * data)
-{
- int hwpec = 0;
- int block = 0;
- int ret, xact = 0;
+ d->block = 0;
+ d->hwpec = 0;
+ d->xact = 0;
+ d->wait_intr = 0;
+ d->finished = 0;
#ifdef HAVE_PEC
- if(isich4)
- hwpec = (flags & I2C_CLIENT_PEC) != 0;
+ if (isich4)
+ d->hwpec = (entry->smbus.flags & I2C_CLIENT_PEC) != 0;
#endif
- switch (size) {
+ switch (entry->smbus.size) {
case I2C_SMBUS_QUICK:
- outb_p(((addr & 0x7f) << 1) | (read_write & 0x01),
+ outb_p(((entry->smbus.addr & 0x7f) << 1)
+ | (entry->smbus.read_write & 0x01),
SMBHSTADD);
- xact = I801_QUICK;
+ d->xact = I801_QUICK;
break;
case I2C_SMBUS_BYTE:
- outb_p(((addr & 0x7f) << 1) | (read_write & 0x01),
+ outb_p(((entry->smbus.addr & 0x7f) << 1)
+ | (entry->smbus.read_write & 0x01),
SMBHSTADD);
- if (read_write = I2C_SMBUS_WRITE)
- outb_p(command, SMBHSTCMD);
- xact = I801_BYTE;
+ if (entry->smbus.read_write = I2C_SMBUS_WRITE)
+ outb_p(entry->smbus.command, SMBHSTCMD);
+ d->xact = I801_BYTE;
break;
case I2C_SMBUS_BYTE_DATA:
- outb_p(((addr & 0x7f) << 1) | (read_write & 0x01),
+ outb_p(((entry->smbus.addr & 0x7f) << 1)
+ | (entry->smbus.read_write & 0x01),
SMBHSTADD);
- outb_p(command, SMBHSTCMD);
- if (read_write = I2C_SMBUS_WRITE)
- outb_p(data->byte, SMBHSTDAT0);
- xact = I801_BYTE_DATA;
+ outb_p(entry->smbus.command, SMBHSTCMD);
+ if (entry->smbus.read_write = I2C_SMBUS_WRITE)
+ outb_p(entry->smbus.data->byte, SMBHSTDAT0);
+ d->xact = I801_BYTE_DATA;
break;
case I2C_SMBUS_WORD_DATA:
- outb_p(((addr & 0x7f) << 1) | (read_write & 0x01),
+ outb_p(((entry->smbus.addr & 0x7f) << 1)
+ | (entry->smbus.read_write & 0x01),
SMBHSTADD);
- outb_p(command, SMBHSTCMD);
- if (read_write = I2C_SMBUS_WRITE) {
- outb_p(data->word & 0xff, SMBHSTDAT0);
- outb_p((data->word & 0xff00) >> 8, SMBHSTDAT1);
+ outb_p(entry->smbus.command, SMBHSTCMD);
+ if (entry->smbus.read_write = I2C_SMBUS_WRITE) {
+ outb_p(entry->smbus.data->word & 0xff, SMBHSTDAT0);
+ outb_p((entry->smbus.data->word & 0xff00) >> 8,
+ SMBHSTDAT1);
}
- xact = I801_WORD_DATA;
+ d->xact = I801_WORD_DATA;
break;
case I2C_SMBUS_BLOCK_DATA:
case I2C_SMBUS_I2C_BLOCK_DATA:
#ifdef HAVE_PEC
case I2C_SMBUS_BLOCK_DATA_PEC:
- if(hwpec && size = I2C_SMBUS_BLOCK_DATA)
- size = I2C_SMBUS_BLOCK_DATA_PEC;
+ if (d->hwpec && entry->smbus.size = I2C_SMBUS_BLOCK_DATA)
+ entry->smbus.size = I2C_SMBUS_BLOCK_DATA_PEC;
#endif
- outb_p(((addr & 0x7f) << 1) | (read_write & 0x01),
+ outb_p(((entry->smbus.addr & 0x7f) << 1)
+ | (entry->smbus.read_write & 0x01),
SMBHSTADD);
- outb_p(command, SMBHSTCMD);
- block = 1;
+ outb_p(entry->smbus.command, SMBHSTCMD);
+ d->block = 1;
break;
case I2C_SMBUS_PROC_CALL:
default:
- dev_err(&I801_dev->dev, "Unsupported transaction %d\n", size);
- return -1;
+ dev_err(&I801_dev->dev, "Unsupported transaction %d\n",
+ entry->smbus.size);
+ entry->result = -EINVAL;
+ return;
}
#ifdef HAVE_PEC
- if(isich4 && hwpec) {
- if(size != I2C_SMBUS_QUICK &&
- size != I2C_SMBUS_I2C_BLOCK_DATA)
+ if (isich4 && d->hwpec) {
+ if (entry->smbus.size != I2C_SMBUS_QUICK &&
+ entry->smbus.size != I2C_SMBUS_I2C_BLOCK_DATA)
outb_p(1, SMBAUXCTL); /* enable HW PEC */
}
#endif
- if(block)
- ret = i801_block_transaction(data, read_write, size);
- else {
- outb_p(xact | ENABLE_INT9, SMBHSTCNT);
- ret = i801_transaction();
+ if (d->block) {
+ i801_block_start(adap, entry, d);
+ if (entry->result < 0)
+ /* Error, finish the transaction */
+ i801_block_finish(adap, entry, d);
+ } else {
+ outb_p(d->xact | ENABLE_INT9, SMBHSTCNT);
+ i801_transaction_start(adap, entry, d);
+ if (entry->result < 0)
+ /* Error, finish the transaction */
+ i801_finish(adap, entry, d);
}
-#ifdef HAVE_PEC
- if(isich4 && hwpec) {
- if(size != I2C_SMBUS_QUICK &&
- size != I2C_SMBUS_I2C_BLOCK_DATA)
- outb_p(0, SMBAUXCTL);
- }
+ /* Wait extra long here, we want at least 2 ticks to guarantee
+ we wait >= 1 tick. But we want to wait at least 100us no
+ matter what. */
+#if (1000000 / HZ) < 100
+ entry->call_again_us = 200;
+#else
+ entry->call_again_us = (1000000 / HZ) * 2;
#endif
+ entry->time_left = MAX_TIMEOUT_US;
- if(block)
- return ret;
- if(ret)
- return -1;
- if ((read_write = I2C_SMBUS_WRITE) || (xact = I801_QUICK))
- return 0;
-
- switch (xact & 0x7f) {
- case I801_BYTE: /* Result put in SMBHSTDAT0 */
- case I801_BYTE_DATA:
- data->byte = inb_p(SMBHSTDAT0);
- break;
- case I801_WORD_DATA:
- data->word = inb_p(SMBHSTDAT0) + (inb_p(SMBHSTDAT1) << 8);
- break;
- }
- return 0;
+ if (d->finished)
+ i2c_op_done(adap, entry);
+ else
+ entry->data = d;
}
@@ -537,7 +668,8 @@
static struct i2c_algorithm smbus_algorithm = {
.name = "Non-I2C SMBus adapter",
.id = I2C_ALGO_SMBUS,
- .smbus_xfer = i801_access,
+ .smbus_start = i801_start,
+ .poll = i801_poll,
.functionality = i801_func,
};
@@ -545,6 +677,7 @@
.owner = THIS_MODULE,
.class = I2C_CLASS_HWMON,
.algo = &smbus_algorithm,
+ .algo_data = &i801_data,
.name = "unset",
};
@@ -563,7 +696,8 @@
MODULE_DEVICE_TABLE (pci, i801_ids);
-static int __devinit i801_probe(struct pci_dev *dev, const struct pci_device_id *id)
+static int __devinit i801_probe(struct pci_dev *dev,
+ const struct pci_device_id *id)
{
if (i801_setup(dev)) {
WARNING: multiple messages have this Message-ID (diff)
From: Corey Minyard <minyard@acm.org>
To: Greg KH <greg@kroah.com>, lkml <linux-kernel@vger.kernel.org>,
Sensors <sensors@stimpy.netroedge.com>
Subject: [PATCH] Add a non-blocking interface to the I2C code, part 7
Date: Wed, 02 Mar 2005 14:04:48 -0600 [thread overview]
Message-ID: <42261C60.1050806@acm.org> (raw)
[-- Attachment #1: Type: text/plain, Size: 45 bytes --]
See part 1 for details on what this does...
[-- Attachment #2: i2c_nonblock_i801.diff --]
[-- Type: text/plain, Size: 23685 bytes --]
This patch modifies the I801 SMBus driver to use the non-blocking
interface.
Signed-off-by: Corey Minyard <minyard@acm.org>
Index: linux-2.6.11-rc5-mm1/drivers/i2c/busses/i2c-i801.c
===================================================================
--- linux-2.6.11-rc5-mm1.orig/drivers/i2c/busses/i2c-i801.c
+++ linux-2.6.11-rc5-mm1/drivers/i2c/busses/i2c-i801.c
@@ -40,6 +40,14 @@
/* Note: we assume there can only be one I801, with one SMBus interface */
+/* Another note: This interface is extremely sensitive to timing and
+ failure handling. If you don't wait at least one jiffie after
+ starting the transaction before checking things, you will screw it
+ up. If you don't wait a jiffie after the final check, you will
+ screw it up. If you screw it up by these manners or by abandoning
+ an operation in progress, the I2C bus is likely stuck and won't
+ work any more. Gotta love this hardware. */
+
#include <linux/config.h>
#include <linux/module.h>
#include <linux/pci.h>
@@ -79,7 +87,8 @@
#define SMBHSTCFG_I2C_EN 4
/* Other settings */
-#define MAX_TIMEOUT 100
+#define MAX_TIMEOUT_US 100000
+#define RETRY_TIME_US 500 /* Retry minimum is 500us */
#define ENABLE_INT9 0 /* set to 0x01 to enable - untested */
/* I801 command constants */
@@ -105,21 +114,31 @@
"Forcibly enable the I801 at the given address. "
"EXTREMELY DANGEROUS!");
-static int i801_transaction(void);
-static int i801_block_transaction(union i2c_smbus_data *data,
- char read_write, int command);
-
static unsigned short i801_smba;
static struct pci_dev *I801_dev;
static int isich4;
+struct i801_i2c_data
+{
+ int i;
+ int len;
+ unsigned char hostc;
+ int block;
+ int hwpec;
+ int xact;
+ int hststs;
+ int wait_intr;
+ int finished;
+};
+struct i801_i2c_data i801_data;
+
static int i801_setup(struct pci_dev *dev)
{
int error_return = 0;
unsigned char temp;
/* Note: we keep on searching until we have found 'function 3' */
- if(PCI_FUNC(dev->devfn) != 3)
+ if (PCI_FUNC(dev->devfn) != 3)
return -ENODEV;
I801_dev = dev;
@@ -136,7 +155,7 @@
} else {
pci_read_config_word(I801_dev, SMBBA, &i801_smba);
i801_smba &= 0xfff0;
- if(i801_smba == 0) {
+ if (i801_smba == 0) {
dev_err(&dev->dev, "SMB base address uninitialized"
"- upgrade BIOS or use force_addr=0xaddr\n");
return -ENODEV;
@@ -180,12 +199,93 @@
return error_return;
}
-static int i801_transaction(void)
+static void i801_check_hststs(struct i2c_adapter *adap,
+ struct i2c_op_q_entry *entry,
+ struct i801_i2c_data *d)
+{
+ if (d->hststs & 0x10) {
+ entry->result = -EIO;
+ dev_dbg(&I801_dev->dev,
+ "Error: Failed bus transaction\n");
+ } else if (d->hststs & 0x08) {
+ entry->result = -EIO;
+ dev_err(&I801_dev->dev, "Bus collision!\n");
+ /* Clock stops and slave is stuck in mid-transmission */
+ } else if (d->hststs & 0x04) {
+ entry->result = -EIO;
+ dev_dbg(&I801_dev->dev, "Error: no response!\n");
+ }
+}
+
+static void i801_finish(struct i2c_adapter *adap,
+ struct i2c_op_q_entry *entry,
+ struct i801_i2c_data *d)
+{
+ d->finished = 1;
+
+#ifdef HAVE_PEC
+ if (isich4 && d->hwpec) {
+ if (entry->smbus.size != I2C_SMBUS_QUICK &&
+ entry->smbus.size != I2C_SMBUS_I2C_BLOCK_DATA)
+ outb_p(0, SMBAUXCTL);
+ }
+#endif
+
+ if (d->block || (entry->result < 0) ||
+ ((entry->smbus.read_write == I2C_SMBUS_WRITE)
+ || (d->xact == I801_QUICK)))
+ return;
+
+ switch (d->xact & 0x7f) {
+ case I801_BYTE: /* Result put in SMBHSTDAT0 */
+ case I801_BYTE_DATA:
+ entry->smbus.data->byte = inb_p(SMBHSTDAT0);
+ break;
+ case I801_WORD_DATA:
+ entry->smbus.data->word = inb_p(SMBHSTDAT0)
+ + (inb_p(SMBHSTDAT1) << 8);
+ break;
+ }
+}
+
+static void i801_transaction_final_check(struct i2c_adapter *adap,
+ struct i2c_op_q_entry *entry,
+ struct i801_i2c_data *d)
+{
+ i801_check_hststs(adap, entry, d);
+
+ if ((inb_p(SMBHSTSTS) & 0x1f) != 0x00)
+ outb_p(inb(SMBHSTSTS), SMBHSTSTS);
+
+ if ((d->hststs = (0x1f & inb_p(SMBHSTSTS))) != 0x00) {
+ dev_dbg(&I801_dev->dev, "Failed reset at end of transaction"
+ "(%02x)\n", d->hststs);
+ }
+ dev_dbg(&I801_dev->dev, "Transaction (post): CNT=%02x, CMD=%02x, "
+ "ADD=%02x, DAT0=%02x, DAT1=%02x\n", inb_p(SMBHSTCNT),
+ inb_p(SMBHSTCMD), inb_p(SMBHSTADD), inb_p(SMBHSTDAT0),
+ inb_p(SMBHSTDAT1));
+}
+
+static void i801_transaction_poll(struct i2c_adapter *adap,
+ struct i2c_op_q_entry *entry,
+ struct i801_i2c_data *d)
{
- int temp;
- int result = 0;
- int timeout = 0;
+ d->hststs = inb_p(SMBHSTSTS);
+ if (!(d->hststs & 0x01)) {
+ i801_transaction_final_check(adap, entry, d);
+ i801_finish(adap, entry, d);
+ } else if (entry->time_left <= 0) {
+ dev_dbg(&I801_dev->dev, "SMBus Timeout!\n");
+ entry->result = -EIO;
+ i801_transaction_final_check(adap, entry, d);
+ }
+}
+static void i801_transaction_start(struct i2c_adapter *adap,
+ struct i2c_op_q_entry *entry,
+ struct i801_i2c_data *d)
+{
dev_dbg(&I801_dev->dev, "Transaction (pre): CNT=%02x, CMD=%02x,"
"ADD=%02x, DAT0=%02x, DAT1=%02x\n", inb_p(SMBHSTCNT),
inb_p(SMBHSTCMD), inb_p(SMBHSTADD), inb_p(SMBHSTDAT0),
@@ -193,331 +293,362 @@
/* Make sure the SMBus host is ready to start transmitting */
/* 0x1f = Failed, Bus_Err, Dev_Err, Intr, Host_Busy */
- if ((temp = (0x1f & inb_p(SMBHSTSTS))) != 0x00) {
+ if ((d->hststs = (0x1f & inb_p(SMBHSTSTS))) != 0x00) {
dev_dbg(&I801_dev->dev, "SMBus busy (%02x). Resetting... \n",
- temp);
- outb_p(temp, SMBHSTSTS);
- if ((temp = (0x1f & inb_p(SMBHSTSTS))) != 0x00) {
- dev_dbg(&I801_dev->dev, "Failed! (%02x)\n", temp);
- return -1;
+ d->hststs);
+ outb_p(d->hststs, SMBHSTSTS);
+ if ((d->hststs = (0x1f & inb_p(SMBHSTSTS))) != 0x00) {
+ dev_dbg(&I801_dev->dev, "Failed! (%02x)\n", d->hststs);
+ entry->result = -EIO;
+ return;
} else {
dev_dbg(&I801_dev->dev, "Successfull!\n");
}
}
outb_p(inb(SMBHSTCNT) | I801_START, SMBHSTCNT);
+}
- /* We will always wait for a fraction of a second! */
- do {
- msleep(1);
- temp = inb_p(SMBHSTSTS);
- } while ((temp & 0x01) && (timeout++ < MAX_TIMEOUT));
-
- /* If the SMBus is still busy, we give up */
- if (timeout >= MAX_TIMEOUT) {
- dev_dbg(&I801_dev->dev, "SMBus Timeout!\n");
- result = -1;
+static void i801_block_finish(struct i2c_adapter *adap,
+ struct i2c_op_q_entry *entry,
+ struct i801_i2c_data *d)
+{
+ if (entry->smbus.size == I2C_SMBUS_I2C_BLOCK_DATA) {
+ /* restore saved configuration register value */
+ pci_write_config_byte(I801_dev, SMBHSTCFG, d->hostc);
}
- if (temp & 0x10) {
- result = -1;
- dev_dbg(&I801_dev->dev, "Error: Failed bus transaction\n");
- }
+ i801_finish(adap, entry, d);
+}
- if (temp & 0x08) {
- result = -1;
- dev_err(&I801_dev->dev, "Bus collision! SMBus may be locked "
- "until next hard reset. (sorry!)\n");
- /* Clock stops and slave is stuck in mid-transmission */
- }
+static void i801_block_poll_wait_intr(struct i2c_adapter *adap,
+ struct i2c_op_q_entry *entry,
+ struct i801_i2c_data *d)
+{
+#ifdef HAVE_PEC
+ if (entry->result >= 0 &&
+ isich4 &&
+ entry->smbus.size == I2C_SMBUS_BLOCK_DATA_PEC)
+ {
+ /* wait for INTR bit as advised by Intel */
+ d->hststs = inb_p(SMBHSTSTS);
+ if (d->hststs & 0x02) {
+ outb_p(d->hststs, SMBHSTSTS);
+ i801_block_finish(adap, entry, d);
+ } else if (entry->time_left <= 0) {
+ /* Timed out */
+ outb_p(d->hststs, SMBHSTSTS);
+ entry->result = -EIO;
+ dev_dbg(&I801_dev->dev, "PEC Timeout!\n");
+ }
+ } else
+#endif
+ i801_block_finish(adap, entry, d);
+}
- if (temp & 0x04) {
- result = -1;
- dev_dbg(&I801_dev->dev, "Error: no response!\n");
+static void i801_block_next_byte(struct i2c_adapter *adap,
+ struct i2c_op_q_entry *entry,
+ struct i801_i2c_data *d)
+{
+ int smbcmd;
+ unsigned char errmask;
+
+ if (d->i > d->len) {
+ d->wait_intr = 1;
+ entry->time_left = MAX_TIMEOUT_US;
+ i801_block_poll_wait_intr(adap, entry, d);
+ return;
}
- if ((inb_p(SMBHSTSTS) & 0x1f) != 0x00)
- outb_p(inb(SMBHSTSTS), SMBHSTSTS);
+ if (d->i == d->len && entry->smbus.read_write == I2C_SMBUS_READ)
+ smbcmd = I801_BLOCK_LAST;
+ else
+ smbcmd = I801_BLOCK_DATA;
+ outb_p(smbcmd | ENABLE_INT9, SMBHSTCNT);
- if ((temp = (0x1f & inb_p(SMBHSTSTS))) != 0x00) {
- dev_dbg(&I801_dev->dev, "Failed reset at end of transaction"
- "(%02x)\n", temp);
+ dev_dbg(&I801_dev->dev, "Block (pre %d): CNT=%02x, CMD=%02x, "
+ "ADD=%02x, DAT0=%02x, BLKDAT=%02x\n", d->i,
+ inb_p(SMBHSTCNT), inb_p(SMBHSTCMD), inb_p(SMBHSTADD),
+ inb_p(SMBHSTDAT0), inb_p(SMBBLKDAT));
+
+ /* Make sure the SMBus host is ready to start transmitting */
+ d->hststs = inb_p(SMBHSTSTS);
+ if (d->i == 1) {
+ /* Erronenous conditions before transaction:
+ * Byte_Done, Failed, Bus_Err, Dev_Err, Intr, Host_Busy */
+ errmask=0x9f;
+ } else {
+ /* Erronenous conditions during transaction:
+ * Failed, Bus_Err, Dev_Err, Intr */
+ errmask=0x1e;
+ }
+ if (d->hststs & errmask) {
+ dev_dbg(&I801_dev->dev, "SMBus busy (%02x). "
+ "Resetting... \n", d->hststs);
+ outb_p(d->hststs, SMBHSTSTS);
+ if (((d->hststs = inb_p(SMBHSTSTS)) & errmask) != 0x00) {
+ dev_err(&I801_dev->dev,
+ "Reset failed! (%02x)\n", d->hststs);
+ entry->result = -EIO;
+ return;
+ }
+ if (d->i != 1) {
+ /* if die in middle of block transaction, fail */
+ entry->result = -EIO;
+ return;
+ }
}
- dev_dbg(&I801_dev->dev, "Transaction (post): CNT=%02x, CMD=%02x, "
- "ADD=%02x, DAT0=%02x, DAT1=%02x\n", inb_p(SMBHSTCNT),
- inb_p(SMBHSTCMD), inb_p(SMBHSTADD), inb_p(SMBHSTDAT0),
- inb_p(SMBHSTDAT1));
- return result;
+
+ if (d->i == 1)
+ outb_p(inb(SMBHSTCNT) | I801_START, SMBHSTCNT);
}
-/* All-inclusive block transaction function */
-static int i801_block_transaction(union i2c_smbus_data *data, char read_write,
- int command)
+/* Called on timer ticks. This checks the result of the
+ transaction. */
+static void i801_block_poll(struct i2c_adapter *adap,
+ struct i2c_op_q_entry *entry,
+ struct i801_i2c_data *d)
{
- int i, len;
- int smbcmd;
- int temp;
- int result = 0;
- int timeout;
- unsigned char hostc, errmask;
-
- if (command == I2C_SMBUS_I2C_BLOCK_DATA) {
- if (read_write == I2C_SMBUS_WRITE) {
- /* set I2C_EN bit in configuration register */
- pci_read_config_byte(I801_dev, SMBHSTCFG, &hostc);
- pci_write_config_byte(I801_dev, SMBHSTCFG,
- hostc | SMBHSTCFG_I2C_EN);
- } else {
- dev_err(&I801_dev->dev,
- "I2C_SMBUS_I2C_BLOCK_READ not DB!\n");
- return -1;
+ d->hststs = inb_p(SMBHSTSTS);
+ if (!(d->hststs & 0x80)) {
+ /* Not ready yet */
+ if (entry->time_left <= 0) {
+ dev_dbg(&I801_dev->dev, "SMBus Timeout!\n");
+ entry->result = -EIO;
}
+ return;
}
- if (read_write == I2C_SMBUS_WRITE) {
- len = data->block[0];
- if (len < 1)
- len = 1;
- if (len > 32)
- len = 32;
- outb_p(len, SMBHSTDAT0);
- outb_p(data->block[1], SMBBLKDAT);
+ i801_check_hststs(adap, entry, d);
+ if (d->i == 1 && entry->smbus.read_write == I2C_SMBUS_READ) {
+ d->len = inb_p(SMBHSTDAT0);
+ if (d->len < 1)
+ d->len = 1;
+ if (d->len > 32)
+ d->len = 32;
+ entry->smbus.data->block[0] = d->len;
+ }
+
+ /* Retrieve/store value in SMBBLKDAT */
+ if (entry->smbus.read_write == I2C_SMBUS_READ)
+ entry->smbus.data->block[d->i] = inb_p(SMBBLKDAT);
+ if (entry->smbus.read_write == I2C_SMBUS_WRITE && d->i+1 <= d->len)
+ outb_p(entry->smbus.data->block[d->i+1], SMBBLKDAT);
+ if ((d->hststs & 0x9e) != 0x00)
+ outb_p(d->hststs, SMBHSTSTS); /* signals SMBBLKDAT ready */
+
+ if ((d->hststs = (0x1e & inb_p(SMBHSTSTS))) != 0x00) {
+ dev_dbg(&I801_dev->dev,
+ "Bad status (%02x) at end of transaction\n",
+ d->hststs);
+ }
+ dev_dbg(&I801_dev->dev, "Block (post %d): CNT=%02x, CMD=%02x, "
+ "ADD=%02x, DAT0=%02x, BLKDAT=%02x\n", d->i,
+ inb_p(SMBHSTCNT), inb_p(SMBHSTCMD), inb_p(SMBHSTADD),
+ inb_p(SMBHSTDAT0), inb_p(SMBBLKDAT));
+
+ if (entry->result)
+ return;
+
+ (d->i)++;
+ i801_block_next_byte(adap, entry, d);
+}
+
+static void i801_block_start(struct i2c_adapter *adap,
+ struct i2c_op_q_entry *entry,
+ struct i801_i2c_data *d)
+{
+ if (entry->smbus.read_write == I2C_SMBUS_WRITE) {
+ d->len = entry->smbus.data->block[0];
+ if (d->len < 1)
+ d->len = 1;
+ if (d->len > 32)
+ d->len = 32;
+ outb_p(d->len, SMBHSTDAT0);
+ outb_p(entry->smbus.data->block[1], SMBBLKDAT);
} else {
- len = 32; /* max for reads */
+ d->len = 32; /* max for reads */
}
- if(isich4 && command != I2C_SMBUS_I2C_BLOCK_DATA) {
+ if(isich4 && entry->smbus.size != I2C_SMBUS_I2C_BLOCK_DATA) {
/* set 32 byte buffer */
}
- for (i = 1; i <= len; i++) {
- if (i == len && read_write == I2C_SMBUS_READ)
- smbcmd = I801_BLOCK_LAST;
- else
- smbcmd = I801_BLOCK_DATA;
- outb_p(smbcmd | ENABLE_INT9, SMBHSTCNT);
-
- dev_dbg(&I801_dev->dev, "Block (pre %d): CNT=%02x, CMD=%02x, "
- "ADD=%02x, DAT0=%02x, BLKDAT=%02x\n", i,
- inb_p(SMBHSTCNT), inb_p(SMBHSTCMD), inb_p(SMBHSTADD),
- inb_p(SMBHSTDAT0), inb_p(SMBBLKDAT));
-
- /* Make sure the SMBus host is ready to start transmitting */
- temp = inb_p(SMBHSTSTS);
- if (i == 1) {
- /* Erronenous conditions before transaction:
- * Byte_Done, Failed, Bus_Err, Dev_Err, Intr, Host_Busy */
- errmask=0x9f;
- } else {
- /* Erronenous conditions during transaction:
- * Failed, Bus_Err, Dev_Err, Intr */
- errmask=0x1e;
- }
- if (temp & errmask) {
- dev_dbg(&I801_dev->dev, "SMBus busy (%02x). "
- "Resetting... \n", temp);
- outb_p(temp, SMBHSTSTS);
- if (((temp = inb_p(SMBHSTSTS)) & errmask) != 0x00) {
- dev_err(&I801_dev->dev,
- "Reset failed! (%02x)\n", temp);
- result = -1;
- goto END;
- }
- if (i != 1) {
- /* if die in middle of block transaction, fail */
- result = -1;
- goto END;
- }
- }
+ d->i = 1;
+ i801_block_next_byte(adap, entry, d);
+}
- if (i == 1)
- outb_p(inb(SMBHSTCNT) | I801_START, SMBHSTCNT);
+/* General poll routine. Called periodically by the i2c code. */
+static void i801_poll(struct i2c_adapter *adap,
+ struct i2c_op_q_entry *entry,
+ unsigned int us_since_last_poll)
+{
+ struct i801_i2c_data *d = entry->data;
- /* We will always wait for a fraction of a second! */
- timeout = 0;
- do {
- temp = inb_p(SMBHSTSTS);
- msleep(1);
- }
- while ((!(temp & 0x80))
- && (timeout++ < MAX_TIMEOUT));
+ dev_dbg(&I801_dev->dev, "Poll call for %p %p at %ld\n", adap, entry,
+ jiffies);
- /* If the SMBus is still busy, we give up */
- if (timeout >= MAX_TIMEOUT) {
- result = -1;
- dev_dbg(&I801_dev->dev, "SMBus Timeout!\n");
- }
+ if (!d)
+ /* The entry hasn't been started yet. */
+ return;
- if (temp & 0x10) {
- result = -1;
- dev_dbg(&I801_dev->dev,
- "Error: Failed bus transaction\n");
- } else if (temp & 0x08) {
- result = -1;
- dev_err(&I801_dev->dev, "Bus collision!\n");
- } else if (temp & 0x04) {
- result = -1;
- dev_dbg(&I801_dev->dev, "Error: no response!\n");
- }
+ if (d->finished) {
+ /* We delay an extra poll to keep the hardware happy.
+ Otherwise the hardware is not ready when we start
+ the next operation. */
+ i2c_op_done(adap, entry);
+ return;
+ }
- if (i == 1 && read_write == I2C_SMBUS_READ) {
- len = inb_p(SMBHSTDAT0);
- if (len < 1)
- len = 1;
- if (len > 32)
- len = 32;
- data->block[0] = len;
- }
+ /* Decrement timeout */
+ entry->time_left -= us_since_last_poll;
- /* Retrieve/store value in SMBBLKDAT */
- if (read_write == I2C_SMBUS_READ)
- data->block[i] = inb_p(SMBBLKDAT);
- if (read_write == I2C_SMBUS_WRITE && i+1 <= len)
- outb_p(data->block[i+1], SMBBLKDAT);
- if ((temp & 0x9e) != 0x00)
- outb_p(temp, SMBHSTSTS); /* signals SMBBLKDAT ready */
-
- if ((temp = (0x1e & inb_p(SMBHSTSTS))) != 0x00) {
- dev_dbg(&I801_dev->dev,
- "Bad status (%02x) at end of transaction\n",
- temp);
- }
- dev_dbg(&I801_dev->dev, "Block (post %d): CNT=%02x, CMD=%02x, "
- "ADD=%02x, DAT0=%02x, BLKDAT=%02x\n", i,
- inb_p(SMBHSTCNT), inb_p(SMBHSTCMD), inb_p(SMBHSTADD),
- inb_p(SMBHSTDAT0), inb_p(SMBBLKDAT));
+ /* Wait a jiffie normally. */
+ entry->call_again_us = RETRY_TIME_US;
- if (result < 0)
- goto END;
+ if (d->block) {
+ if (d->wait_intr) {
+ i801_block_poll_wait_intr(adap, entry, d);
+ } else {
+ i801_block_poll(adap, entry, d);
+ }
+ if (entry->result < 0)
+ /* Error, finish the transaction */
+ i801_block_finish(adap, entry, d);
+ } else {
+ i801_transaction_poll(adap, entry, d);
+ if (entry->result < 0)
+ /* Error, finish the transaction */
+ i801_finish(adap, entry, d);
}
+}
-#ifdef HAVE_PEC
- if(isich4 && command == I2C_SMBUS_BLOCK_DATA_PEC) {
- /* wait for INTR bit as advised by Intel */
- timeout = 0;
- do {
- temp = inb_p(SMBHSTSTS);
- msleep(1);
- } while ((!(temp & 0x02))
- && (timeout++ < MAX_TIMEOUT));
+/* Start a general SMBUS transaction on the i801. Figure out what
+ kind of transaction it is, set it up, and start it. */
+static void i801_start(struct i2c_adapter *adap,
+ struct i2c_op_q_entry *entry)
+{
+ struct i801_i2c_data *d = adap->algo_data;
- if (timeout >= MAX_TIMEOUT) {
- dev_dbg(&I801_dev->dev, "PEC Timeout!\n");
+ dev_dbg(&I801_dev->dev, "start call for %p %p at %ld\n", adap, entry,
+ jiffies);
+
+ if (entry->smbus.size == I2C_SMBUS_I2C_BLOCK_DATA) {
+ if (entry->smbus.read_write == I2C_SMBUS_WRITE) {
+ /* set I2C_EN bit in configuration register */
+ pci_read_config_byte(I801_dev, SMBHSTCFG, &d->hostc);
+ pci_write_config_byte(I801_dev, SMBHSTCFG,
+ d->hostc | SMBHSTCFG_I2C_EN);
+ } else {
+ dev_err(&I801_dev->dev,
+ "I2C_SMBUS_I2C_BLOCK_READ not DB!\n");
+ entry->result = -EINVAL;
+ return;
}
- outb_p(temp, SMBHSTSTS);
- }
-#endif
- result = 0;
-END:
- if (command == I2C_SMBUS_I2C_BLOCK_DATA) {
- /* restore saved configuration register value */
- pci_write_config_byte(I801_dev, SMBHSTCFG, hostc);
}
- return result;
-}
-/* Return -1 on error. */
-static s32 i801_access(struct i2c_adapter * adap, u16 addr,
- unsigned short flags, char read_write, u8 command,
- int size, union i2c_smbus_data * data)
-{
- int hwpec = 0;
- int block = 0;
- int ret, xact = 0;
+ d->block = 0;
+ d->hwpec = 0;
+ d->xact = 0;
+ d->wait_intr = 0;
+ d->finished = 0;
#ifdef HAVE_PEC
- if(isich4)
- hwpec = (flags & I2C_CLIENT_PEC) != 0;
+ if (isich4)
+ d->hwpec = (entry->smbus.flags & I2C_CLIENT_PEC) != 0;
#endif
- switch (size) {
+ switch (entry->smbus.size) {
case I2C_SMBUS_QUICK:
- outb_p(((addr & 0x7f) << 1) | (read_write & 0x01),
+ outb_p(((entry->smbus.addr & 0x7f) << 1)
+ | (entry->smbus.read_write & 0x01),
SMBHSTADD);
- xact = I801_QUICK;
+ d->xact = I801_QUICK;
break;
case I2C_SMBUS_BYTE:
- outb_p(((addr & 0x7f) << 1) | (read_write & 0x01),
+ outb_p(((entry->smbus.addr & 0x7f) << 1)
+ | (entry->smbus.read_write & 0x01),
SMBHSTADD);
- if (read_write == I2C_SMBUS_WRITE)
- outb_p(command, SMBHSTCMD);
- xact = I801_BYTE;
+ if (entry->smbus.read_write == I2C_SMBUS_WRITE)
+ outb_p(entry->smbus.command, SMBHSTCMD);
+ d->xact = I801_BYTE;
break;
case I2C_SMBUS_BYTE_DATA:
- outb_p(((addr & 0x7f) << 1) | (read_write & 0x01),
+ outb_p(((entry->smbus.addr & 0x7f) << 1)
+ | (entry->smbus.read_write & 0x01),
SMBHSTADD);
- outb_p(command, SMBHSTCMD);
- if (read_write == I2C_SMBUS_WRITE)
- outb_p(data->byte, SMBHSTDAT0);
- xact = I801_BYTE_DATA;
+ outb_p(entry->smbus.command, SMBHSTCMD);
+ if (entry->smbus.read_write == I2C_SMBUS_WRITE)
+ outb_p(entry->smbus.data->byte, SMBHSTDAT0);
+ d->xact = I801_BYTE_DATA;
break;
case I2C_SMBUS_WORD_DATA:
- outb_p(((addr & 0x7f) << 1) | (read_write & 0x01),
+ outb_p(((entry->smbus.addr & 0x7f) << 1)
+ | (entry->smbus.read_write & 0x01),
SMBHSTADD);
- outb_p(command, SMBHSTCMD);
- if (read_write == I2C_SMBUS_WRITE) {
- outb_p(data->word & 0xff, SMBHSTDAT0);
- outb_p((data->word & 0xff00) >> 8, SMBHSTDAT1);
+ outb_p(entry->smbus.command, SMBHSTCMD);
+ if (entry->smbus.read_write == I2C_SMBUS_WRITE) {
+ outb_p(entry->smbus.data->word & 0xff, SMBHSTDAT0);
+ outb_p((entry->smbus.data->word & 0xff00) >> 8,
+ SMBHSTDAT1);
}
- xact = I801_WORD_DATA;
+ d->xact = I801_WORD_DATA;
break;
case I2C_SMBUS_BLOCK_DATA:
case I2C_SMBUS_I2C_BLOCK_DATA:
#ifdef HAVE_PEC
case I2C_SMBUS_BLOCK_DATA_PEC:
- if(hwpec && size == I2C_SMBUS_BLOCK_DATA)
- size = I2C_SMBUS_BLOCK_DATA_PEC;
+ if (d->hwpec && entry->smbus.size == I2C_SMBUS_BLOCK_DATA)
+ entry->smbus.size = I2C_SMBUS_BLOCK_DATA_PEC;
#endif
- outb_p(((addr & 0x7f) << 1) | (read_write & 0x01),
+ outb_p(((entry->smbus.addr & 0x7f) << 1)
+ | (entry->smbus.read_write & 0x01),
SMBHSTADD);
- outb_p(command, SMBHSTCMD);
- block = 1;
+ outb_p(entry->smbus.command, SMBHSTCMD);
+ d->block = 1;
break;
case I2C_SMBUS_PROC_CALL:
default:
- dev_err(&I801_dev->dev, "Unsupported transaction %d\n", size);
- return -1;
+ dev_err(&I801_dev->dev, "Unsupported transaction %d\n",
+ entry->smbus.size);
+ entry->result = -EINVAL;
+ return;
}
#ifdef HAVE_PEC
- if(isich4 && hwpec) {
- if(size != I2C_SMBUS_QUICK &&
- size != I2C_SMBUS_I2C_BLOCK_DATA)
+ if (isich4 && d->hwpec) {
+ if (entry->smbus.size != I2C_SMBUS_QUICK &&
+ entry->smbus.size != I2C_SMBUS_I2C_BLOCK_DATA)
outb_p(1, SMBAUXCTL); /* enable HW PEC */
}
#endif
- if(block)
- ret = i801_block_transaction(data, read_write, size);
- else {
- outb_p(xact | ENABLE_INT9, SMBHSTCNT);
- ret = i801_transaction();
+ if (d->block) {
+ i801_block_start(adap, entry, d);
+ if (entry->result < 0)
+ /* Error, finish the transaction */
+ i801_block_finish(adap, entry, d);
+ } else {
+ outb_p(d->xact | ENABLE_INT9, SMBHSTCNT);
+ i801_transaction_start(adap, entry, d);
+ if (entry->result < 0)
+ /* Error, finish the transaction */
+ i801_finish(adap, entry, d);
}
-#ifdef HAVE_PEC
- if(isich4 && hwpec) {
- if(size != I2C_SMBUS_QUICK &&
- size != I2C_SMBUS_I2C_BLOCK_DATA)
- outb_p(0, SMBAUXCTL);
- }
+ /* Wait extra long here, we want at least 2 ticks to guarantee
+ we wait >= 1 tick. But we want to wait at least 100us no
+ matter what. */
+#if (1000000 / HZ) < 100
+ entry->call_again_us = 200;
+#else
+ entry->call_again_us = (1000000 / HZ) * 2;
#endif
+ entry->time_left = MAX_TIMEOUT_US;
- if(block)
- return ret;
- if(ret)
- return -1;
- if ((read_write == I2C_SMBUS_WRITE) || (xact == I801_QUICK))
- return 0;
-
- switch (xact & 0x7f) {
- case I801_BYTE: /* Result put in SMBHSTDAT0 */
- case I801_BYTE_DATA:
- data->byte = inb_p(SMBHSTDAT0);
- break;
- case I801_WORD_DATA:
- data->word = inb_p(SMBHSTDAT0) + (inb_p(SMBHSTDAT1) << 8);
- break;
- }
- return 0;
+ if (d->finished)
+ i2c_op_done(adap, entry);
+ else
+ entry->data = d;
}
@@ -537,7 +668,8 @@
static struct i2c_algorithm smbus_algorithm = {
.name = "Non-I2C SMBus adapter",
.id = I2C_ALGO_SMBUS,
- .smbus_xfer = i801_access,
+ .smbus_start = i801_start,
+ .poll = i801_poll,
.functionality = i801_func,
};
@@ -545,6 +677,7 @@
.owner = THIS_MODULE,
.class = I2C_CLASS_HWMON,
.algo = &smbus_algorithm,
+ .algo_data = &i801_data,
.name = "unset",
};
@@ -563,7 +696,8 @@
MODULE_DEVICE_TABLE (pci, i801_ids);
-static int __devinit i801_probe(struct pci_dev *dev, const struct pci_device_id *id)
+static int __devinit i801_probe(struct pci_dev *dev,
+ const struct pci_device_id *id)
{
if (i801_setup(dev)) {
next parent reply other threads:[~2005-05-19 6:25 UTC|newest]
Thread overview: 18+ messages / expand[flat|nested] mbox.gz Atom feed top
2005-03-02 20:04 Corey Minyard [this message]
2005-05-19 6:25 ` [PATCH] Add a non-blocking interface to the I2C code, part 7 Corey Minyard
-- strict thread matches above, loose matches on Subject: below --
2005-03-02 20:04 [PATCH] Add a non-blocking interface to the I2C code, part 6 Corey Minyard
2005-05-19 6:25 ` Corey Minyard
2005-03-02 20:02 [PATCH] Add a non-blocking interface to the I2C code, part 5 Corey Minyard
2005-05-19 6:25 ` Corey Minyard
2005-03-02 20:02 [PATCH] Add a non-blocking interface to the I2C code, part 4 Corey Minyard
2005-05-19 6:25 ` Corey Minyard
2005-03-02 20:01 [PATCH] Add a non-blocking interface to the I2C code, part3 Corey Minyard
2005-05-19 6:25 ` Corey Minyard
2005-03-02 20:00 [PATCH] Add a non-blocking interface to the I2C code, part 2 Corey Minyard
2005-05-19 6:25 ` Corey Minyard
2005-03-02 19:58 [PATCH] Add a non-blocking interface to the I2C code, part 1 Corey Minyard
2005-05-19 6:25 ` Corey Minyard
2005-03-17 7:09 ` Greg KH
2005-05-19 6:25 ` Greg KH
2005-03-18 16:13 ` Corey Minyard
2005-05-19 6:25 ` Corey Minyard
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=42261C60.1050806@acm.org \
--to=minyard@acm.org \
--cc=greg@kroah.com \
--cc=linux-kernel@vger.kernel.org \
--cc=sensors@stimpy.netroedge.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 an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.