All of lore.kernel.org
 help / color / mirror / Atom feed
From: minyard@acm.org (Corey Minyard)
To: Bukie Mabayoje <bukiemab@gte.net>
Cc: Mark Studebaker <mds4@verizon.net>,
	Sensors <sensors@Stimpy.netroedge.com>,
	lkml <linux-kernel@vger.kernel.org>,
	Frodo Looijaard <frodol@dds.nl>,
	Philip Edelbrock <phil@netroedge.com>
Subject: [PATCH] Updates for the i801 driver to support the I2C non-blocking
Date: Thu, 19 May 2005 06:25:33 +0000	[thread overview]
Message-ID: <41FA5633.2050202@acm.org> (raw)
In-Reply-To: <41F9E183.5A9B1BA2@gte.net>

Here's the changes required for the i801 driver.  See the previous post 
for the patch to add a non-blocking interface to the I2C driver.

Like the core I2C changes, this is mostly breaking the functions into 
smaller pieces and calling them from the appropriate places.

-Corey
-------------- next part --------------
Index: linux-2.6.11-rc2/drivers/i2c/busses/i2c-i801.c
=================================--- linux-2.6.11-rc2.orig/drivers/i2c/busses/i2c-i801.c	2005-01-27 13:09:12.000000000 -0600
+++ linux-2.6.11-rc2/drivers/i2c/busses/i2c-i801.c	2005-01-27 13:46:23.000000000 -0600
@@ -79,7 +79,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 +106,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 +147,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 +191,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 +285,347 @@
 
 	/* 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.command = 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.command = 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 after a timeout.  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;
+	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 (command = I2C_SMBUS_I2C_BLOCK_DATA) {
-		if (read_write = I2C_SMBUS_WRITE) {
+	i801_check_hststs(adap, entry, d);
+	if (entry->result)
+		return;
+
+	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));
+
+	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.command = 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, &hostc);
+			pci_read_config_byte(I801_dev, SMBHSTCFG, &d->hostc);
 			pci_write_config_byte(I801_dev, SMBHSTCFG,
-					      hostc | SMBHSTCFG_I2C_EN);
+					      d->hostc | SMBHSTCFG_I2C_EN);
 		} else {
 			dev_err(&I801_dev->dev,
 				"I2C_SMBUS_I2C_BLOCK_READ not DB!\n");
-			return -1;
+			entry->result = -EINVAL;
+			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);
+	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.command != 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");
-		}
+	/* Decrement timeout */
+	entry->time_left -= us_since_last_poll;
 
-		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;
-		}
+	/* Wait a jiffie normally. */
+	entry->call_again_us = RETRY_TIME_US;
 
-		/* 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);
+	if (d->block) {
+		if (d->wait_intr) {
+			i801_block_poll_wait_intr(adap, entry, d);
+		} else {
+			i801_block_poll(adap, entry, d);
 		}
-		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));
-
-		if (result < 0)
-			goto END;
+		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));
-
-		if (timeout >= MAX_TIMEOUT) {
-			dev_dbg(&I801_dev->dev, "PEC Timeout!\n");
-		}
-		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;
+	if (d->finished)
+		i2c_op_done(adap, entry);
 }
 
-/* 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)
+/* 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)
 {
-	int hwpec = 0;
-	int block = 0;
-	int ret, xact = 0;
+	struct i801_i2c_data *d = adap->algo_data;
+
+	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();
-	}
-
-#ifdef HAVE_PEC
-	if(isich4 && hwpec) {
-		if(size != I2C_SMBUS_QUICK &&
-		   size != I2C_SMBUS_I2C_BLOCK_DATA)
-			outb_p(0, SMBAUXCTL);
+	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);
 	}
-#endif
 
-	if(block)
-		return ret;
-	if(ret)
-		return -1;
-	if ((read_write = I2C_SMBUS_WRITE) || (xact = I801_QUICK))
-		return 0;
+	entry->call_again_us = RETRY_TIME_US;
+	entry->time_left = MAX_TIMEOUT_US;
 
-	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 +645,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 +654,7 @@
 	.owner		= THIS_MODULE,
 	.class		= I2C_CLASS_HWMON,
 	.algo		= &smbus_algorithm,
+	.algo_data      = &i801_data,
 	.name		= "unset",
 };
 
@@ -563,7 +673,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: Bukie Mabayoje <bukiemab@gte.net>
Cc: Mark Studebaker <mds4@verizon.net>,
	Sensors <sensors@Stimpy.netroedge.com>,
	lkml <linux-kernel@vger.kernel.org>,
	Frodo Looijaard <frodol@dds.nl>,
	Philip Edelbrock <phil@netroedge.com>
Subject: [PATCH] Updates for the i801 driver to support the I2C non-blocking interface
Date: Fri, 28 Jan 2005 09:11:47 -0600	[thread overview]
Message-ID: <41FA5633.2050202@acm.org> (raw)
In-Reply-To: <41F9E183.5A9B1BA2@gte.net>

[-- Attachment #1: Type: text/plain, Size: 279 bytes --]

Here's the changes required for the i801 driver.  See the previous post 
for the patch to add a non-blocking interface to the I2C driver.

Like the core I2C changes, this is mostly breaking the functions into 
smaller pieces and calling them from the appropriate places.

-Corey

[-- Attachment #2: i2c_async_i801.diff --]
[-- Type: text/plain, Size: 22341 bytes --]

Index: linux-2.6.11-rc2/drivers/i2c/busses/i2c-i801.c
===================================================================
--- linux-2.6.11-rc2.orig/drivers/i2c/busses/i2c-i801.c	2005-01-27 13:09:12.000000000 -0600
+++ linux-2.6.11-rc2/drivers/i2c/busses/i2c-i801.c	2005-01-27 13:46:23.000000000 -0600
@@ -79,7 +79,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 +106,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 +147,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 +191,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 +285,347 @@
 
 	/* 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.command == 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.command == 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 after a timeout.  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;
+	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 (command == I2C_SMBUS_I2C_BLOCK_DATA) {
-		if (read_write == I2C_SMBUS_WRITE) {
+	i801_check_hststs(adap, entry, d);
+	if (entry->result)
+		return;
+
+	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));
+
+	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.command == 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, &hostc);
+			pci_read_config_byte(I801_dev, SMBHSTCFG, &d->hostc);
 			pci_write_config_byte(I801_dev, SMBHSTCFG,
-					      hostc | SMBHSTCFG_I2C_EN);
+					      d->hostc | SMBHSTCFG_I2C_EN);
 		} else {
 			dev_err(&I801_dev->dev,
 				"I2C_SMBUS_I2C_BLOCK_READ not DB!\n");
-			return -1;
+			entry->result = -EINVAL;
+			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);
+	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.command != 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");
-		}
+	/* Decrement timeout */
+	entry->time_left -= us_since_last_poll;
 
-		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;
-		}
+	/* Wait a jiffie normally. */
+	entry->call_again_us = RETRY_TIME_US;
 
-		/* 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);
+	if (d->block) {
+		if (d->wait_intr) {
+			i801_block_poll_wait_intr(adap, entry, d);
+		} else {
+			i801_block_poll(adap, entry, d);
 		}
-		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));
-
-		if (result < 0)
-			goto END;
+		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));
-
-		if (timeout >= MAX_TIMEOUT) {
-			dev_dbg(&I801_dev->dev, "PEC Timeout!\n");
-		}
-		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;
+	if (d->finished)
+		i2c_op_done(adap, entry);
 }
 
-/* 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)
+/* 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)
 {
-	int hwpec = 0;
-	int block = 0;
-	int ret, xact = 0;
+	struct i801_i2c_data *d = adap->algo_data;
+
+	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();
-	}
-
-#ifdef HAVE_PEC
-	if(isich4 && hwpec) {
-		if(size != I2C_SMBUS_QUICK &&
-		   size != I2C_SMBUS_I2C_BLOCK_DATA)
-			outb_p(0, SMBAUXCTL);
+	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);
 	}
-#endif
 
-	if(block)
-		return ret;
-	if(ret)
-		return -1;
-	if ((read_write == I2C_SMBUS_WRITE) || (xact == I801_QUICK))
-		return 0;
+	entry->call_again_us = RETRY_TIME_US;
+	entry->time_left = MAX_TIMEOUT_US;
 
-	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 +645,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 +654,7 @@
 	.owner		= THIS_MODULE,
 	.class		= I2C_CLASS_HWMON,
 	.algo		= &smbus_algorithm,
+	.algo_data      = &i801_data,
 	.name		= "unset",
 };
 
@@ -563,7 +673,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)) {

  parent reply	other threads:[~2005-05-19  6:25 UTC|newest]

Thread overview: 14+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2005-01-24 15:05 Adding an async I2C interface Corey Minyard
2005-05-19  6:25 ` Corey Minyard
2005-01-28  0:18 ` Mark Studebaker
2005-05-19  6:25   ` Mark Studebaker
2005-01-28  4:43   ` Corey Minyard
2005-05-19  6:25     ` Corey Minyard
2005-05-19  6:25 ` Bukie Mabayoje
2005-01-28  7:23   ` Bukie Mabayoje
2005-01-28 14:02   ` Corey Minyard
2005-05-19  6:25     ` Corey Minyard
2005-01-28 15:08   ` [PATCH] Add a non-blocking " Corey Minyard
2005-05-19  6:25     ` Corey Minyard
2005-01-28 15:11   ` Corey Minyard [this message]
2005-05-19  6:25     ` [PATCH] Updates for the i801 driver to support the I2C non-blocking 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=41FA5633.2050202@acm.org \
    --to=minyard@acm.org \
    --cc=bukiemab@gte.net \
    --cc=frodol@dds.nl \
    --cc=linux-kernel@vger.kernel.org \
    --cc=mds4@verizon.net \
    --cc=phil@netroedge.com \
    --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.