linux-kernel.vger.kernel.org archive mirror
 help / color / mirror / Atom feed
* Adding an async I2C interface
@ 2005-01-24 15:05 Corey Minyard
  2005-01-28  0:18 ` Mark Studebaker
  0 siblings, 1 reply; 7+ messages in thread
From: Corey Minyard @ 2005-01-24 15:05 UTC (permalink / raw)
  To: Sensors, lkml

I have an IPMI interface driver that sits on top of the I2C code.  I'd
like to get it into the mainstream kernel, but I have a few problems
to solve first before I can do that.  The I2C code is synchronous and
must run from a task context.  The IPMI driver has certain
operations that occur at panic time, including:

   * Storing panic information in IPMI's system event log
   * Extending the watchdog timer so it doesn't go off during panic
     operations (like kernel coredumps).
   * Powering the system off

I can't really put the IPMI SMB interface into the kernel until I can
do those operations.  Also, I understand that some vendors put RTC
chips onto the I2C bus and this must be accessed outside task context,
too.  I would really like add asynchronous interface to the I2C bus
drivers.  I propose:

   * Adding an async send interface to the busses that does a callback
     when the operation is complete.
   * Adding a poll interface to the busses.  The I2C core code could
     call this if a synchronous call is made from task context (much
     like all the current drivers do right now).  For asyncronous
     operation, the I2C core code would call it from a timer
     interrupt.  If the driver supported interrupts, polling from the
     timer interrupt would not be necessary.
   * Add async operations for the user to call, including access to the
     polling code.
   * If the driver didn't support an async send, it would work as it
     does today and the async calls would return ENOSYS.

This way, the bus drivers on I2C could be converted on a
driver-by-driver basis.  The IPMI code could query to see if the
driver supported async operations.  And the RTC code could use it,
too.

Is this ok with the I2C community?  I would do the base work and
convert over a few drivers.

Thanks,

-Corey

^ permalink raw reply	[flat|nested] 7+ messages in thread

* Re: Adding an async I2C interface
  2005-01-24 15:05 Adding an async I2C interface Corey Minyard
@ 2005-01-28  0:18 ` Mark Studebaker
  2005-01-28  4:43   ` Corey Minyard
  0 siblings, 1 reply; 7+ messages in thread
From: Mark Studebaker @ 2005-01-28  0:18 UTC (permalink / raw)
  To: Corey Minyard; +Cc: Sensors, lkml

is there a way to do this solely in i2c-core without having to
add support to all the drivers?

Corey Minyard wrote:
> I have an IPMI interface driver that sits on top of the I2C code.  I'd
> like to get it into the mainstream kernel, but I have a few problems
> to solve first before I can do that.  The I2C code is synchronous and
> must run from a task context.  The IPMI driver has certain
> operations that occur at panic time, including:
> 
>   * Storing panic information in IPMI's system event log
>   * Extending the watchdog timer so it doesn't go off during panic
>     operations (like kernel coredumps).
>   * Powering the system off
> 
> I can't really put the IPMI SMB interface into the kernel until I can
> do those operations.  Also, I understand that some vendors put RTC
> chips onto the I2C bus and this must be accessed outside task context,
> too.  I would really like add asynchronous interface to the I2C bus
> drivers.  I propose:
> 
>   * Adding an async send interface to the busses that does a callback
>     when the operation is complete.
>   * Adding a poll interface to the busses.  The I2C core code could
>     call this if a synchronous call is made from task context (much
>     like all the current drivers do right now).  For asyncronous
>     operation, the I2C core code would call it from a timer
>     interrupt.  If the driver supported interrupts, polling from the
>     timer interrupt would not be necessary.
>   * Add async operations for the user to call, including access to the
>     polling code.
>   * If the driver didn't support an async send, it would work as it
>     does today and the async calls would return ENOSYS.
> 
> This way, the bus drivers on I2C could be converted on a
> driver-by-driver basis.  The IPMI code could query to see if the
> driver supported async operations.  And the RTC code could use it,
> too.
> 
> Is this ok with the I2C community?  I would do the base work and
> convert over a few drivers.
> 
> Thanks,
> 
> -Corey
> 
> 


^ permalink raw reply	[flat|nested] 7+ messages in thread

* Re: Adding an async I2C interface
  2005-01-28  0:18 ` Mark Studebaker
@ 2005-01-28  4:43   ` Corey Minyard
       [not found]     ` <41F9E183.5A9B1BA2@gte.net>
  0 siblings, 1 reply; 7+ messages in thread
From: Corey Minyard @ 2005-01-28  4:43 UTC (permalink / raw)
  To: Mark Studebaker; +Cc: Sensors, lkml

Mark Studebaker wrote:

> is there a way to do this solely in i2c-core without having to
> add support to all the drivers?

Yes and no.  In order to support this async operation, the driver cannot 
block and do things like msleep() or schedule().  It has to start the 
operation, return, and either let polling or an interrupt drive the 
continued operation.  Thus for async operations the driver has to be 
modified.  However, if async operation is not required, the driver can 
stay as is.

I've been working on this and will probably have a patch tomorrow.  I've 
modified the piix4 and the i801 drivers, I probably won't do any more 
myself unless the need arises, since I can't test any others.  Note that 
this still supports the old driver interface, so no drivers need to be 
rewritten.  That way, they only need to be modified if something needs 
the async interface.  So drivers that have an RTC on them or that 
support IPMI BMCs could be rewritten, but nothing else needs to be done.

I've also noticed a somewhat cavalier attitude in this code with respect 
to return values.  I've cleaned some of that up so return values are not 
just -1 on error, but are proper errno values.  However, I've only fixed 
the core code and the drivers I've worked on.

Thanks,

-Corey

>
> Corey Minyard wrote:
>
>> I have an IPMI interface driver that sits on top of the I2C code.  I'd
>> like to get it into the mainstream kernel, but I have a few problems
>> to solve first before I can do that.  The I2C code is synchronous and
>> must run from a task context.  The IPMI driver has certain
>> operations that occur at panic time, including:
>>
>>   * Storing panic information in IPMI's system event log
>>   * Extending the watchdog timer so it doesn't go off during panic
>>     operations (like kernel coredumps).
>>   * Powering the system off
>>
>> I can't really put the IPMI SMB interface into the kernel until I can
>> do those operations.  Also, I understand that some vendors put RTC
>> chips onto the I2C bus and this must be accessed outside task context,
>> too.  I would really like add asynchronous interface to the I2C bus
>> drivers.  I propose:
>>
>>   * Adding an async send interface to the busses that does a callback
>>     when the operation is complete.
>>   * Adding a poll interface to the busses.  The I2C core code could
>>     call this if a synchronous call is made from task context (much
>>     like all the current drivers do right now).  For asyncronous
>>     operation, the I2C core code would call it from a timer
>>     interrupt.  If the driver supported interrupts, polling from the
>>     timer interrupt would not be necessary.
>>   * Add async operations for the user to call, including access to the
>>     polling code.
>>   * If the driver didn't support an async send, it would work as it
>>     does today and the async calls would return ENOSYS.
>>
>> This way, the bus drivers on I2C could be converted on a
>> driver-by-driver basis.  The IPMI code could query to see if the
>> driver supported async operations.  And the RTC code could use it,
>> too.
>>
>> Is this ok with the I2C community?  I would do the base work and
>> convert over a few drivers.
>>
>> Thanks,
>>
>> -Corey
>>
>>


^ permalink raw reply	[flat|nested] 7+ messages in thread

* Re: Adding an async I2C interface
       [not found]     ` <41F9E183.5A9B1BA2@gte.net>
@ 2005-01-28  7:23       ` Bukie Mabayoje
  2005-01-28 14:02       ` Corey Minyard
                         ` (2 subsequent siblings)
  3 siblings, 0 replies; 7+ messages in thread
From: Bukie Mabayoje @ 2005-01-28  7:23 UTC (permalink / raw)
  To: lkml

> I will be glad to work with on this,  I have some exposure to the BMC. See text below in blue.
>
> bukie
>
> Corey Minyard wrote:
>
>> Mark Studebaker wrote:
>>
>> > is there a way to do this solely in i2c-core without having to
>> > add support to all the drivers?
>>
>> Yes and no.  In order to support this async operation, the driver cannot
>> block and do things like msleep() or schedule().
>
> The Interface driver must be a user space driver.
>
>> It has to start the
>> operation, return, and either let polling or an interrupt drive the
>> continued operation.  Thus for async operations the driver has to be
>> modified.  However, if async operation is not required, the driver can
>> stay as is.
>>
>> I've been working on this and will probably have a patch tomorrow.  I've
>> modified the piix4 and the i801 drivers, I probably won't do any more
>> myself unless the need arises, since I can't test any others.  Note that
>> this still supports the old driver interface, so no drivers need to be
>> rewritten.  That way, they only need to be modified if something needs
>> the async interface.  So drivers that have an RTC on them or that
>> support IPMI BMCs could be rewritten, but nothing else needs to be done.
>>
>> I've also noticed a somewhat cavalier attitude in this code with respect
>> to return values.  I've cleaned some of that up so return values are not
>> just -1 on error, but are proper errno values.  However, I've only fixed
>> the core code and the drivers I've worked on.
>>
>> Thanks,
>>
>> -Corey
>>
>> >
>> > Corey Minyard wrote:
>> >
>> >> I have an IPMI interface driver that sits on top of the I2C code.  I'd
>> >> like to get it into the mainstream kernel, but I have a few problems
>> >> to solve first before I can do that.  The I2C code is synchronous and
>> >> must run from a task context.
>
> I am not sure what you mean that the I2C code is synchronous. I2C bus is Asynchronous which means that the data clock is not included in the data. The Sender and Receiver agrees on the timing parameters prior to the bus transaction.
>
>> The IPMI driver has certain
>> >> operations that occur at panic time, including:
>> >>
>> >>   * Storing panic information in IPMI's system event log
>> >>   * Extending the watchdog timer so it doesn't go off during panic
>> >>     operations (like kernel coredumps).
>> >>   * Powering the system off
>> >>
>
> Is this driver compliant with the IPMI spec? Because the above should be a sensor that must be enable or disable. A driver should not make sure a decision by itself.
>
>>
>> >> I can't really put the IPMI SMB interface into the kernel until I can
>> >> do those operations.  Also, I understand that some vendors put RTC
>> >> chips onto the I2C bus and this must be accessed outside task context,
>> >> too.
>
> What the vendor put on the board doesn't matter with respect to IPMI. What matter is that you have access to the Master where the slave you talking to is connect on the I2C bus.
>
>> I would really like add asynchronous interface to the I2C bus
>> >> drivers.
>
> Do you mean a  blocking and non blocking I/O?
>
>> I propose:
>> >>
>> >>   * Adding an async send interface to the busses that does a callback
>> >>     when the operation is complete.
>
> Okay, you are doing a non-blocking I/O. So what happens when another process tries to access the I2C bus before the bus transaction is completed. Some thing (mainly the app) needs to tell the driver not to share the resource while a transaction is in progress.
>
>>
>> >>   * Adding a poll interface to the busses.  The I2C core code could
>> >>     call this if a synchronous call is made from task context (much
>> >>     like all the current drivers do right now).  For asyncronous
>> >>     operation, the I2C core code would call it from a timer
>> >>     interrupt.  If the driver supported interrupts, polling from the
>> >>     timer interrupt would not be necessary.
>
> I think this should be done in the Interface code because the Interface code will be running in the user space and have access to the operating system facility.
>
>>
>> >>   * Add async operations for the user to call, including access to the
>> >>     polling code.
>
> The driver can make itself blocking  and non blocking
>
>>
>> >>   * If the driver didn't support an async send, it would work as it
>> >>     does today and the async calls would return ENOSYS.
>
> Not needed, it will be addressed by the blocking and non-block implementation of the driver.
>
>>
>> >>
>> >> This way, the bus drivers on I2C could be converted on a
>> >> driver-by-driver basis.  The IPMI code could query to see if the
>> >> driver supported async operations.  And the RTC code could use it,
>> >> too.
>> >>
>> >> Is this ok with the I2C community?  I would do the base work and
>> >> convert over a few drivers.
>> >>
>> >> Thanks,
>> >>
>> >> -Corey
>> >>
>> >>
>>
>> -
>> To unsubscribe from this list: send the line "unsubscribe linux-kernel" in
>> the body of a message to majordomo@vger.kernel.org
>> More majordomo info at  http://vger.kernel.org/majordomo-info.html
>> Please read the FAQ at  http://www.tux.org/lkml/
>

^ permalink raw reply	[flat|nested] 7+ messages in thread

* Re: Adding an async I2C interface
       [not found]     ` <41F9E183.5A9B1BA2@gte.net>
  2005-01-28  7:23       ` Bukie Mabayoje
@ 2005-01-28 14:02       ` Corey Minyard
  2005-01-28 15:08       ` [PATCH] Add a non-blocking " Corey Minyard
  2005-01-28 15:11       ` [PATCH] Updates for the i801 driver to support the I2C non-blocking interface Corey Minyard
  3 siblings, 0 replies; 7+ messages in thread
From: Corey Minyard @ 2005-01-28 14:02 UTC (permalink / raw)
  To: Bukie Mabayoje; +Cc: Mark Studebaker, Sensors, lkml

Bukie Mabayoje wrote:

> I will be glad to work with on this,  I have some exposure to the BMC. 
> See text below in blue.
>
> bukie
>
> Corey Minyard wrote:
>
>> Mark Studebaker wrote:
>>
>> > is there a way to do this solely in i2c-core without having to
>> > add support to all the drivers?
>>
>> Yes and no.  In order to support this async operation, the driver cannot
>> block and do things like msleep() or schedule().
>>
> The Interface driver must be a user space driver.

That is precisely the problem.  What happens if you need to get to the 
i2c bus but you are not running from a task context?

>> It has to start the
>> operation, return, and either let polling or an interrupt drive the
>> continued operation.  Thus for async operations the driver has to be
>> modified.  However, if async operation is not required, the driver can
>> stay as is.
>>
>> I've been working on this and will probably have a patch tomorrow.  I've
>> modified the piix4 and the i801 drivers, I probably won't do any more
>> myself unless the need arises, since I can't test any others.  Note that
>> this still supports the old driver interface, so no drivers need to be
>> rewritten.  That way, they only need to be modified if something needs
>> the async interface.  So drivers that have an RTC on them or that
>> support IPMI BMCs could be rewritten, but nothing else needs to be done.
>>
>> I've also noticed a somewhat cavalier attitude in this code with respect
>> to return values.  I've cleaned some of that up so return values are not
>> just -1 on error, but are proper errno values.  However, I've only fixed
>> the core code and the drivers I've worked on.
>>
>> Thanks,
>>
>> -Corey
>>
>> >
>> > Corey Minyard wrote:
>> >
>> >> I have an IPMI interface driver that sits on top of the I2C code.  
>> I'd
>> >> like to get it into the mainstream kernel, but I have a few problems
>> >> to solve first before I can do that.  The I2C code is synchronous and
>> >> must run from a task context.
>>
> I am not sure what you mean that the I2C code is synchronous. I2C bus 
> is Asynchronous which means that the data clock is not included in the 
> data. The Sender and Receiver agrees on the timing parameters prior to 
> the bus transaction.

I mean the driver, not the hardware.  By the I2C driver being 
synchronous, I mean that you call a function, the whole operation occurs 
in the function, and then the function returns the result.  By 
asynchronous, I mean that you tell the driver to start an operation and 
the driver starts it and immediately returns.  It then uses interrupts, 
timers, or polling calls to drive the operation of the interface.  When 
the operation is done, the results are reported back through a callback 
function provided when the driver started.

Plus there are SMB alerts which the hardware can generate 
asynchronously.  The I2C driver has no concept of that right now.

>> The IPMI driver has certain
>> >> operations that occur at panic time, including:
>> >>
>> >>   * Storing panic information in IPMI's system event log
>> >>   * Extending the watchdog timer so it doesn't go off during panic
>> >>     operations (like kernel coredumps).
>> >>   * Powering the system off
>> >>
>
> Is this driver compliant with the IPMI spec? Because the above should 
> be a sensor that must be enable or disable. A driver should not make 
> sure a decision by itself.

There are two parts here.  There is the I2C driver and the IPMI driver 
that sits on top of it.  I'm really only taking about the I2C driver 
here, I need asynchronous operation from the I2C driver to do those 
things in the IPMI driver.

>>  
>> >> I can't really put the IPMI SMB interface into the kernel until I can
>> >> do those operations.  Also, I understand that some vendors put RTC
>> >> chips onto the I2C bus and this must be accessed outside task 
>> context,
>> >> too.
>
> What the vendor put on the board doesn't matter with respect to IPMI. 
> What matter is that you have access to the Master where the slave you 
> talking to is connect on the I2C bus.
>
>> I would really like add asynchronous interface to the I2C bus
>> >> drivers.
>
> Do you mean a  blocking and non blocking I/O?

Yes, that is a better term.  I'll switch to using that.

-Corey


^ permalink raw reply	[flat|nested] 7+ messages in thread

* [PATCH] Add a non-blocking I2C interface
       [not found]     ` <41F9E183.5A9B1BA2@gte.net>
  2005-01-28  7:23       ` Bukie Mabayoje
  2005-01-28 14:02       ` Corey Minyard
@ 2005-01-28 15:08       ` Corey Minyard
  2005-01-28 15:11       ` [PATCH] Updates for the i801 driver to support the I2C non-blocking interface Corey Minyard
  3 siblings, 0 replies; 7+ messages in thread
From: Corey Minyard @ 2005-01-28 15:08 UTC (permalink / raw)
  To: Bukie Mabayoje; +Cc: Mark Studebaker, Sensors, lkml

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

Here's the code that I have so far for adding a non-blocking interface 
to the I2C interface.  I've debated whether to do this as a patch or 
just post the files, because the patch is about half the size of the 
files.  I've decided on the diff for now, it seems to be fairly 
readable.  This is relative to 2.6.11-rc2.

I have not extensively tested this patch.  I've done eeprom operations 
on the piix4 and i801 chips both with and without updating the those 
interface for non-blocking operation.  I'm going to test the block 
transfers with the IPMI driver when I get access to a system (this 
weekend).  This is posted for comment.

This patch doesn't actually change much.  It adds a queue entry that is 
used to pass around the message data (and is obviously used for queuing 
:).  But this mostly is using the queue entry for data, breaking the 
operations into smaller functions, and doing the polling in the i2c-core 
code instead of the drivers themselves (for non-blocking capable 
drivers).  Function for unchanged drivers should be unchanged.

This does not implement SMB Alert, which should improve the performace 
of the IPMI SMB driver.  I'll do that in the future and just poll the 
interface for now.

I'll post the changes to the i801 driver next.

This patch requires the fixes to the completion code that Mike Waychison 
posted a few days ago 
(http://marc.theaimsgroup.com/?l=linux-kernel&m=110669761400454&w=2).  
Otherwise the wait_for_completion_interruptible() and 
wait_for_completion_timeout() are broken.

In case you missed the previous discussion, I need a non-blocking 
interface in the I2C code for use by the IPMI SMB driver.  The driver 
needs to be able to do things like store panic information in the SEL, 
ping the watchdog timer while in a panic, and power the system off.  The 
current I2C driver requires a task context, but you can't exactly do 
things at panic time with a task context.

-Corey

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

Index: linux-2.6.11-rc2/drivers/i2c/i2c-core.c
===================================================================
--- linux-2.6.11-rc2.orig/drivers/i2c/i2c-core.c	2005-01-26 15:59:53.000000000 -0600
+++ linux-2.6.11-rc2/drivers/i2c/i2c-core.c	2005-01-28 08:54:00.000000000 -0600
@@ -30,8 +30,14 @@
 #include <linux/init.h>
 #include <linux/idr.h>
 #include <linux/seq_file.h>
+#include <linux/completion.h>
 #include <asm/uaccess.h>
 
+static int i2c_stop_timer(struct i2c_adapter * adap);
+static void i2c_start_timer(struct i2c_adapter * adap,
+			    struct i2c_op_q_entry * entry);
+
+#define USEC_PER_JIFFIE (1000000 / HZ)
 
 static LIST_HEAD(adapters);
 static LIST_HEAD(drivers);
@@ -134,11 +140,26 @@
 	}
 
 	adap->nr =  id & MAX_ID_MASK;
+	spin_lock_init(&adap->q_lock);
+	INIT_LIST_HEAD(&adap->q);
 	init_MUTEX(&adap->bus_lock);
 	init_MUTEX(&adap->clist_lock);
 	list_add_tail(&adap->list,&adapters);
 	INIT_LIST_HEAD(&adap->clients);
 
+	adap->timer = kmalloc(sizeof(*adap->timer), GFP_KERNEL);
+	if (!adap->timer) {
+		res = -ENOMEM;
+		goto out_unlock;
+	}
+		
+	init_timer(&adap->timer->timer);
+	spin_lock_init(&adap->timer->lock);
+	adap->timer->deleted = 0;
+	adap->timer->running = 0;
+	adap->timer->next_call_time = 0;
+	adap->timer->adapter = adap;
+
 	/* Add the adapter to the driver core.
 	 * If the parent pointer is not set up,
 	 * we add this adapter to the host bus.
@@ -181,6 +202,7 @@
 	struct i2c_driver *driver;
 	struct i2c_client *client;
 	int res = 0;
+	unsigned long flags;
 
 	down(&core_lists);
 
@@ -233,6 +255,17 @@
 	device_unregister(&adap->dev);
 	list_del(&adap->list);
 
+	/* Stop the timer and free its memory */
+	spin_lock_irqsave(&adap->timer->lock, flags);
+	if (i2c_stop_timer(adap)) {
+		spin_unlock_irqrestore(&adap->timer->lock, flags);
+		kfree(adap->timer);
+	} else {
+		adap->timer->deleted = 1;
+		spin_unlock_irqrestore(&adap->timer->lock, flags);
+	}
+	adap->timer = NULL;
+
 	/* wait for sysfs to drop all references */
 	wait_for_completion(&adap->dev_released);
 	wait_for_completion(&adap->class_dev_released);
@@ -583,15 +616,283 @@
  * ----------------------------------------------------
  */
 
-int i2c_transfer(struct i2c_adapter * adap, struct i2c_msg msgs[],int num)
+/* Must be called with the q_lock held. */
+static void i2c_start_entry(struct i2c_adapter * adap,
+			    struct i2c_op_q_entry * entry)
+{
+	entry->started = 1;
+	switch (entry->xfer_type) {
+	case I2C_OP_I2C:
+		adap->algo->master_start(adap, entry);
+		break;
+	case I2C_OP_SMBUS:
+		adap->algo->smbus_start(adap, entry);
+		break;
+	default:
+		entry->result = -EINVAL;
+		i2c_op_done(adap, entry);
+	}
+
+	if (!entry->completed && entry->use_timer)
+		i2c_start_timer(adap, entry);
+}
+
+/* Must be called with q lock held. */
+static void i2c_entry_inc(struct i2c_adapter * adapter,
+			  struct i2c_op_q_entry * entry)
 {
-	int ret;
+	atomic_inc(&entry->usecount);
+}
+
+/* Get the first entry off the head of the queue and lock it there.
+   The entry is guaranteed to remain first in the list and the handler
+   not be called until i2c_entry_put() is called. */
+static struct i2c_op_q_entry *_i2c_entry_get(struct i2c_adapter * adap)
+{
+	struct i2c_op_q_entry * entry = NULL;
 
-	if (adap->algo->master_xfer) {
- 	 	dev_dbg(&adap->dev, "master_xfer: with %d msgs.\n", num);
+	if (!list_empty(&adap->q)) {
+		struct list_head * link = adap->q.next;
+		entry = list_entry(link, struct i2c_op_q_entry, link);
+		if (entry->completed)
+			entry = NULL;
+		else
+			i2c_entry_inc(adap, entry);
+	}
+	pr_debug("_i2c_entry_get %p %p\n", adap, entry);
+	return entry;
+}
+
+struct i2c_op_q_entry *i2c_entry_get(struct i2c_adapter * adap)
+{
+	unsigned long flags;
+	struct i2c_op_q_entry * entry;
+
+	spin_lock_irqsave(&adap->q_lock, flags);
+	entry = _i2c_entry_get(adap);
+	spin_unlock_irqrestore(&adap->q_lock, flags);
+	return entry;
+}
+
+void i2c_entry_put(struct i2c_adapter * adap,
+		   struct i2c_op_q_entry * entry)
+{
+	unsigned long flags;
+	struct i2c_op_q_entry * new_entry = NULL;
+
+ restart:
+	pr_debug("i2c_put %p %p\n", adap, entry);
+	if (atomic_dec_and_test(&entry->usecount)) {
+		spin_lock_irqsave(&adap->q_lock, flags);
+		list_del(&entry->link);
+
+		/* Get the next entry to start. */
+		new_entry = _i2c_entry_get(adap);
+		spin_unlock_irqrestore(&adap->q_lock, flags);
+
+		entry->handler(entry);
+
+
+		if (new_entry) {
+			i2c_start_entry(adap, new_entry);
+			if (new_entry->start)
+				complete(new_entry->start);
+			/* Do tail recursion ourself. */
+			entry = new_entry;
+			goto restart;
+		}
+	}
+}
+
+static void i2c_handle_timer(unsigned long data);
+
+static void i2c_start_timer(struct i2c_adapter * adap,
+			    struct i2c_op_q_entry * entry)
+{
+	unsigned int wait_jiffies;
+	struct i2c_timer *t = adap->timer;
+	unsigned long flags;
+
+	wait_jiffies = ((entry->call_again_us + USEC_PER_JIFFIE - 1)
+			/ USEC_PER_JIFFIE);
+	if (wait_jiffies == 0)
+		wait_jiffies = 1;
+	/* This won't be polled from the user code, so
+	   start a timer to poll it. */
+	spin_lock_irqsave(&t->lock, flags);
+	if (! t->running) {
+		t->timer.expires = jiffies + wait_jiffies;
+		t->timer.data = (unsigned long) adap;
+		t->timer.function = i2c_handle_timer;
+		t->running = 1;
+		t->next_call_time = wait_jiffies * USEC_PER_JIFFIE;
+		add_timer(&t->timer);
+		t->sequence = adap->timer_sequence;
+	}
+	spin_unlock_irqrestore(&t->lock, flags);
+}
+
+/* Returns true if the timer is stopped (or was not running), false if
+   not.  Must be called with the timer lock held. */
+static int i2c_stop_timer(struct i2c_adapter * adap)
+{
+	return (!adap->timer->running || del_timer(&adap->timer->timer));
+}
+
+static void i2c_handle_timer(unsigned long data)
+{
+	struct i2c_timer      * t = (void *) data;
+	struct i2c_adapter    * adap;
+	unsigned long         flags;
+	struct i2c_op_q_entry * entry;
+	unsigned int          sequence_match;
+
+	spin_lock_irqsave(&t->lock, flags);
+	if (t->deleted) {
+		spin_unlock_irqrestore(&t->lock, flags);
+		kfree(t);
+		return;
+	}
+
+	adap = t->adapter;
+	t->running = 0;
+	sequence_match = adap->timer_sequence == t->sequence;
+	spin_unlock_irqrestore(&t->lock, flags);
+
+	entry = i2c_entry_get(adap);
+	pr_debug("i2c_handle_timer: %p %p\n", adap, entry);
+	if (!entry)
+		return;
+
+	if (sequence_match) {
+		/* This is the one we expected, call the poll routine. */
+		adap->algo->poll(adap, entry, t->next_call_time);
+
+		if (!entry->completed)
+			i2c_start_timer(adap, entry);
+	} else if (entry->use_timer)
+		/* We raced in timer deletion, just restart the
+		   timer if necessary. */
+		i2c_start_timer(adap, entry);
+
+	i2c_entry_put(adap, entry);
+}
+
+void i2c_op_done(struct i2c_adapter *adap, struct i2c_op_q_entry *e)
+{
+	unsigned long         flags;
+	int                   did_complete = 0;
+
+	pr_debug("i2c_op_done: %p %p\n", adap, e);
+	spin_lock_irqsave(&adap->q_lock, flags);
+	if (! e->completed) {
+		e->completed = 1;
+		did_complete = 1;
+	}
+	spin_unlock_irqrestore(&adap->q_lock, flags);
+
+	if (did_complete) {
+		if (e->use_timer) {
+			struct i2c_timer *t = adap->timer;
+			spin_lock_irqsave(&t->lock, flags);
+			if (!i2c_stop_timer(adap))
+				/* If we are unable to stop the timer, that
+				   means the timer has gone off but has not
+				   yet run the first part of the handler call.
+				   Increment the sequence so the timer handler
+				   can detect this. */
+				adap->timer_sequence++;
+			spin_unlock_irqrestore(&t->lock, flags);
+		}
+		if (e->complete)
+			e->complete(adap, e);
+	}
+
+	i2c_entry_put(adap, e);
+}
+
+static void i2c_wait_complete(struct i2c_op_q_entry * entry)
+{
+	struct completion *done = entry->handler_data;
+	pr_debug("i2c_wait_complete %p\n", entry);
+	complete(done);
+}
+
+static void i2c_perform_op_wait(struct i2c_adapter * adap,
+				struct i2c_op_q_entry * entry)
+{
+	struct completion    done;
+	unsigned long        flags;
+	struct i2c_algorithm *algo = adap->algo;
+
+	pr_debug("i2c_perform_op_wait %p %p\n", adap, entry);
+	init_completion(&done);
+	entry->start = NULL;
+	entry->handler = i2c_wait_complete;
+	entry->handler_data = &done;
+	entry->started = 0;
+	entry->completed = 0;
+	entry->result = 0;
+	entry->use_timer = 0; /* We poll it directly. */
+	entry->data = NULL;
+	atomic_set(&entry->usecount, 1);
+	spin_lock_irqsave(&adap->q_lock, flags);
+	list_add_tail(&entry->link, &adap->q);
+	if (adap->q.next == &entry->link) {
+		/* Added to the list head, start it */
+		spin_unlock_irqrestore(&adap->q_lock, flags);
+		i2c_start_entry(adap, entry);
+	} else {
+		struct completion start;
+		init_completion(&start);
+		entry->start = &start;
+		spin_unlock_irqrestore(&adap->q_lock, flags);
+
+		wait_for_completion_interruptible(&start);
+
+		spin_lock_irqsave(&adap->q_lock, flags);
+		if (!entry->started) {
+			/* Operation was interrupted.  There
+			   is a race, we can't use the
+			   wait_for_completion return code. */
+			entry->result = -ERESTARTSYS;
+			entry->completed = 1;
+			list_del(&entry->link);
+		}
+		spin_unlock_irqrestore(&adap->q_lock, flags);
+	}
+
+	/* Once the operation is started, we will not
+	   interrupt it. */
+	while (!entry->completed) {
+		unsigned int timeout = entry->call_again_us;
+		timeout += (USEC_PER_JIFFIE - 1);
+		timeout /= USEC_PER_JIFFIE;
+		if (timeout == 0)
+			timeout = 1;
+		wait_for_completion_timeout(&done, timeout);
+		if (entry->completed)
+			break;
+		algo->poll(adap, entry, timeout * USEC_PER_JIFFIE);
+	}
+}
+
+static int i2c_transfer_entry(struct i2c_adapter * adap,
+			      struct i2c_op_q_entry * entry)
+{
+	entry->xfer_type = I2C_OP_I2C;
+	entry->complete = NULL;
+	if (adap->algo->master_start) {
+		i2c_perform_op_wait(adap, entry);
+		return entry->result;
+	} else if (adap->algo->master_xfer) {
+		int ret;
+ 	 	dev_dbg(&adap->dev, "master_xfer: with %d msgs.\n",
+			entry->i2c.num);
 
 		down(&adap->bus_lock);
-		ret = adap->algo->master_xfer(adap,msgs,num);
+		ret = adap->algo->master_xfer(adap, entry->i2c.msgs,
+					      entry->i2c.num);
 		up(&adap->bus_lock);
 
 		return ret;
@@ -601,33 +902,45 @@
 	}
 }
 
+int i2c_transfer(struct i2c_adapter * adap, struct i2c_msg msgs[],int num)
+{
+	struct i2c_op_q_entry *entry;
+	int                   rv;
+
+	entry = kmalloc(sizeof(*entry), GFP_KERNEL);
+	if (!entry)
+	    return -ENOMEM;
+
+	entry->i2c.msgs = msgs;
+	entry->i2c.num = num;
+
+	rv = i2c_transfer_entry(adap, entry);
+	kfree(entry);
+	return rv;
+}
+
 int i2c_master_send(struct i2c_client *client,const char *buf ,int count)
 {
 	int ret;
 	struct i2c_adapter *adap=client->adapter;
 	struct i2c_msg msg;
 
-	if (client->adapter->algo->master_xfer) {
-		msg.addr   = client->addr;
-		msg.flags = client->flags & I2C_M_TEN;
-		msg.len = count;
-		msg.buf = (char *)buf;
-	
-		dev_dbg(&client->adapter->dev, "master_send: writing %d bytes.\n",
-			count);
+	msg.addr   = client->addr;
+	msg.flags = client->flags & I2C_M_TEN;
+	msg.len = count;
+	msg.buf = (char *)buf;
 	
-		down(&adap->bus_lock);
-		ret = adap->algo->master_xfer(adap,&msg,1);
-		up(&adap->bus_lock);
+	dev_dbg(&client->adapter->dev, "master_send: writing %d bytes.\n",
+		count);
 
-		/* if everything went ok (i.e. 1 msg transmitted), return #bytes
-		 * transmitted, else error code.
-		 */
-		return (ret == 1 )? count : ret;
-	} else {
-		dev_err(&client->adapter->dev, "I2C level transfers not supported\n");
-		return -ENOSYS;
-	}
+	ret = i2c_transfer(adap, &msg, 1);
+	if (ret < 0)
+	    return ret;
+
+	/* if everything went ok (i.e. 1 msg transmitted), return #bytes
+	 * transmitted, else error code.
+	 */
+	return (ret == 1 )? count : ret;
 }
 
 int i2c_master_recv(struct i2c_client *client, char *buf ,int count)
@@ -635,31 +948,27 @@
 	struct i2c_adapter *adap=client->adapter;
 	struct i2c_msg msg;
 	int ret;
-	if (client->adapter->algo->master_xfer) {
-		msg.addr   = client->addr;
-		msg.flags = client->flags & I2C_M_TEN;
-		msg.flags |= I2C_M_RD;
-		msg.len = count;
-		msg.buf = buf;
 
-		dev_dbg(&client->adapter->dev, "master_recv: reading %d bytes.\n",
-			count);
-	
-		down(&adap->bus_lock);
-		ret = adap->algo->master_xfer(adap,&msg,1);
-		up(&adap->bus_lock);
+	msg.addr   = client->addr;
+	msg.flags = client->flags & I2C_M_TEN;
+	msg.flags |= I2C_M_RD;
+	msg.len = count;
+	msg.buf = buf;
+
+	dev_dbg(&client->adapter->dev, "master_recv: reading %d bytes.\n",
+		count);
 	
-		dev_dbg(&client->adapter->dev, "master_recv: return:%d (count:%d, addr:0x%02x)\n",
-			ret, count, client->addr);
+	ret = i2c_transfer(adap, &msg, 1);
+	if (ret < 0)
+	    return ret;
+
+	dev_dbg(&client->adapter->dev, "master_recv: return:%d (count:%d, addr:0x%02x)\n",
+		ret, count, client->addr);
 	
-		/* if everything went ok (i.e. 1 msg transmitted), return #bytes
-	 	* transmitted, else error code.
-	 	*/
-		return (ret == 1 )? count : ret;
-	} else {
-		dev_err(&client->adapter->dev, "I2C level transfers not supported\n");
-		return -ENOSYS;
-	}
+	/* if everything went ok (i.e. 1 msg transmitted), return #bytes
+	 * transmitted, else error code.
+	 */
+	return (ret == 1 )? count : ret;
 }
 
 
@@ -1037,7 +1346,8 @@
 }
 
 /* Returns the number of read bytes */
-s32 i2c_smbus_read_i2c_block_data(struct i2c_client *client, u8 command, u8 *values)
+s32 i2c_smbus_read_i2c_block_data(struct i2c_client *client, u8 command,
+				  u8 *values)
 {
 	union i2c_smbus_data data;
 	int i;
@@ -1052,184 +1362,333 @@
 	}
 }
 
-/* Simulate a SMBus command using the i2c protocol 
-   No checking of parameters is done!  */
-static s32 i2c_smbus_xfer_emulated(struct i2c_adapter * adapter, u16 addr, 
-                                   unsigned short flags,
-                                   char read_write, u8 command, int size, 
-                                   union i2c_smbus_data * data)
-{
-	/* So we need to generate a series of msgs. In the case of writing, we
-	  need to use only one message; when reading, we need two. We initialize
-	  most things with sane defaults, to keep the code below somewhat
-	  simpler. */
-	unsigned char msgbuf0[34];
-	unsigned char msgbuf1[34];
-	int num = read_write == I2C_SMBUS_READ?2:1;
-	struct i2c_msg msg[2] = { { addr, flags, 1, msgbuf0 }, 
-	                          { addr, flags | I2C_M_RD, 0, msgbuf1 }
-	                        };
+
+static void i2c_smbus_complete_entry(struct i2c_adapter * adap,
+				     struct i2c_op_q_entry * entry)
+{
+	if (entry->result < 0)
+		return;
+
+	if(entry->result >= 0 && entry->swpec &&
+	   entry->smbus.size != I2C_SMBUS_QUICK &&
+	   entry->smbus.size != I2C_SMBUS_I2C_BLOCK_DATA &&
+	   (entry->smbus.read_write == I2C_SMBUS_READ ||
+	    entry->smbus.size == I2C_SMBUS_PROC_CALL_PEC ||
+	    entry->smbus.size == I2C_SMBUS_BLOCK_PROC_CALL_PEC)) {
+		if(i2c_smbus_check_pec(entry->smbus.addr,
+				       entry->smbus.command,
+				       entry->smbus.size,
+				       entry->partial,
+				       entry->smbus.data))
+			entry->result = -EINVAL;
+	}
+}
+
+static void i2c_smbus_format_entry(struct i2c_adapter * adap,
+				   struct i2c_op_q_entry * entry)
+{
+	entry->swpec = 0;
+	entry->partial = 0;
+	entry->smbus.flags &= I2C_M_TEN | I2C_CLIENT_PEC;
+	if((entry->smbus.flags & I2C_CLIENT_PEC) &&
+	   !(i2c_check_functionality(adap, I2C_FUNC_SMBUS_HWPEC_CALC))) {
+		entry->swpec = 1;
+		if(entry->smbus.read_write == I2C_SMBUS_READ &&
+		   entry->smbus.size == I2C_SMBUS_BLOCK_DATA)
+			entry->smbus.size = I2C_SMBUS_BLOCK_DATA_PEC;
+		else if(entry->smbus.size == I2C_SMBUS_PROC_CALL)
+			entry->smbus.size = I2C_SMBUS_PROC_CALL_PEC;
+		else if(entry->smbus.size == I2C_SMBUS_BLOCK_PROC_CALL) {
+			unsigned char *data = entry->smbus.data->block;
+			i2c_smbus_add_pec(entry->smbus.addr,
+					  entry->smbus.command,
+		                          I2C_SMBUS_BLOCK_DATA,
+					  entry->smbus.data);
+			entry->partial = data[data[0] + 1];
+			entry->smbus.size = I2C_SMBUS_BLOCK_PROC_CALL_PEC;
+		} else if(entry->smbus.read_write == I2C_SMBUS_WRITE &&
+		          entry->smbus.size != I2C_SMBUS_QUICK &&
+		          entry->smbus.size != I2C_SMBUS_I2C_BLOCK_DATA)
+			entry->smbus.size =
+				i2c_smbus_add_pec(entry->smbus.addr,
+						  entry->smbus.command,
+						  entry->smbus.size,
+						  entry->smbus.data);
+	}
+
+	entry->complete = i2c_smbus_complete_entry;
+}
+
+static void i2c_smbus_emu_complete(struct i2c_adapter * adap,
+				   struct i2c_op_q_entry * entry)
+{
+	unsigned char *msgbuf0 = entry->i2c.msgs[0].buf;
+	unsigned char *msgbuf1 = entry->i2c.msgs[1].buf;
 	int i;
 
-	msgbuf0[0] = command;
-	switch(size) {
+	if (entry->smbus.read_write != I2C_SMBUS_READ)
+		return;
+
+	switch(entry->smbus.size) {
+	case I2C_SMBUS_BYTE:
+		entry->smbus.data->byte = msgbuf0[0];
+		break;
+	case I2C_SMBUS_BYTE_DATA:
+		entry->smbus.data->byte = msgbuf1[0];
+		break;
+	case I2C_SMBUS_WORD_DATA: 
+	case I2C_SMBUS_PROC_CALL:
+		entry->smbus.data->word = msgbuf1[0]|(msgbuf1[1] << 8);
+		break;
+	case I2C_SMBUS_I2C_BLOCK_DATA:
+		/* fixed at 32 for now */
+		entry->smbus.data->block[0] = I2C_SMBUS_I2C_BLOCK_MAX;
+		for (i = 0; i < I2C_SMBUS_I2C_BLOCK_MAX; i++)
+			entry->smbus.data->block[i+1] = msgbuf1[i];
+		break;
+	}
+
+	entry->xfer_type = I2C_OP_SMBUS;
+	i2c_smbus_complete_entry(adap, entry);
+}
+
+static int i2c_smbus_emu_format(struct i2c_adapter *adap,
+				struct i2c_op_q_entry * entry)
+{
+	/* So we need to generate a series of msgs. In the case of
+	   writing, we need to use only one message; when reading, we
+	   need two. We initialize most things with sane defaults, to
+	   keep the code below somewhat simpler. */
+	unsigned char *msgbuf0 = entry->msgbuf0;
+	unsigned char *msgbuf1 = entry->msgbuf1;
+	int num = entry->smbus.read_write == I2C_SMBUS_READ?2:1;
+	struct i2c_msg *msg = entry->msg;
+	int i;
+
+	entry->i2c.msgs = msg;
+	entry->i2c.msgs[0].buf = msgbuf0;
+	entry->i2c.msgs[1].buf = msgbuf1;
+
+	msg[0].addr = entry->smbus.addr;
+	msg[0].flags = entry->smbus.flags;
+	msg[0].len = 1;
+	msg[1].addr = entry->smbus.addr;
+	msg[1].flags = entry->smbus.flags | I2C_M_RD;
+	msg[1].len = 1;
+
+	msgbuf0[0] = entry->smbus.command;
+	switch(entry->smbus.size) {
 	case I2C_SMBUS_QUICK:
 		msg[0].len = 0;
 		/* Special case: The read/write field is used as data */
-		msg[0].flags = flags | (read_write==I2C_SMBUS_READ)?I2C_M_RD:0;
+		msg[0].flags = (entry->smbus.flags |
+				((entry->smbus.read_write==I2C_SMBUS_READ)
+				 ? I2C_M_RD : 0));
 		num = 1;
 		break;
 	case I2C_SMBUS_BYTE:
-		if (read_write == I2C_SMBUS_READ) {
+		if (entry->smbus.read_write == I2C_SMBUS_READ) {
 			/* Special case: only a read! */
-			msg[0].flags = I2C_M_RD | flags;
+			msg[0].flags = I2C_M_RD | entry->smbus.flags;
 			num = 1;
 		}
 		break;
 	case I2C_SMBUS_BYTE_DATA:
-		if (read_write == I2C_SMBUS_READ)
+		if (entry->smbus.read_write == I2C_SMBUS_READ)
 			msg[1].len = 1;
 		else {
 			msg[0].len = 2;
-			msgbuf0[1] = data->byte;
+			msgbuf0[1] = entry->smbus.data->byte;
 		}
 		break;
 	case I2C_SMBUS_WORD_DATA:
-		if (read_write == I2C_SMBUS_READ)
+		if (entry->smbus.read_write == I2C_SMBUS_READ)
 			msg[1].len = 2;
 		else {
 			msg[0].len=3;
-			msgbuf0[1] = data->word & 0xff;
-			msgbuf0[2] = (data->word >> 8) & 0xff;
+			msgbuf0[1] = entry->smbus.data->word & 0xff;
+			msgbuf0[2] = (entry->smbus.data->word >> 8) & 0xff;
 		}
 		break;
 	case I2C_SMBUS_PROC_CALL:
 		num = 2; /* Special case */
-		read_write = I2C_SMBUS_READ;
+		entry->smbus.read_write = I2C_SMBUS_READ;
 		msg[0].len = 3;
 		msg[1].len = 2;
-		msgbuf0[1] = data->word & 0xff;
-		msgbuf0[2] = (data->word >> 8) & 0xff;
+		msgbuf0[1] = entry->smbus.data->word & 0xff;
+		msgbuf0[2] = (entry->smbus.data->word >> 8) & 0xff;
 		break;
 	case I2C_SMBUS_BLOCK_DATA:
 	case I2C_SMBUS_BLOCK_DATA_PEC:
-		if (read_write == I2C_SMBUS_READ) {
-			dev_err(&adapter->dev, "Block read not supported "
+		if (entry->smbus.read_write == I2C_SMBUS_READ) {
+			dev_err(&adap->dev, "Block read not supported "
 			       "under I2C emulation!\n");
 			return -1;
 		} else {
-			msg[0].len = data->block[0] + 2;
+			msg[0].len = entry->smbus.data->block[0] + 2;
 			if (msg[0].len > I2C_SMBUS_BLOCK_MAX + 2) {
-				dev_err(&adapter->dev, "smbus_access called with "
+				dev_err(&adap->dev,
+					"smbus_access called with "
 				       "invalid block write size (%d)\n",
-				       data->block[0]);
+				       entry->smbus.data->block[0]);
 				return -1;
 			}
-			if(size == I2C_SMBUS_BLOCK_DATA_PEC)
+			if(entry->smbus.size == I2C_SMBUS_BLOCK_DATA_PEC)
 				(msg[0].len)++;
 			for (i = 1; i <= msg[0].len; i++)
-				msgbuf0[i] = data->block[i-1];
+				msgbuf0[i] = entry->smbus.data->block[i-1];
 		}
 		break;
 	case I2C_SMBUS_BLOCK_PROC_CALL:
 	case I2C_SMBUS_BLOCK_PROC_CALL_PEC:
-		dev_dbg(&adapter->dev, "Block process call not supported "
+		dev_dbg(&adap->dev, "Block process call not supported "
 		       "under I2C emulation!\n");
 		return -1;
 	case I2C_SMBUS_I2C_BLOCK_DATA:
-		if (read_write == I2C_SMBUS_READ) {
+		if (entry->smbus.read_write == I2C_SMBUS_READ) {
 			msg[1].len = I2C_SMBUS_I2C_BLOCK_MAX;
 		} else {
-			msg[0].len = data->block[0] + 1;
+			msg[0].len = entry->smbus.data->block[0] + 1;
 			if (msg[0].len > I2C_SMBUS_I2C_BLOCK_MAX + 1) {
-				dev_err(&adapter->dev, "i2c_smbus_xfer_emulated called with "
+				dev_err(&adap->dev,
+					"i2c_smbus_xfer_emulated called with "
 				       "invalid block write size (%d)\n",
-				       data->block[0]);
+				       entry->smbus.data->block[0]);
 				return -1;
 			}
-			for (i = 1; i <= data->block[0]; i++)
-				msgbuf0[i] = data->block[i];
+			for (i = 1; i <= entry->smbus.data->block[0]; i++)
+				msgbuf0[i] = entry->smbus.data->block[i];
 		}
 		break;
 	default:
-		dev_err(&adapter->dev, "smbus_access called with invalid size (%d)\n",
-		       size);
+		dev_err(&adap->dev,
+			"smbus_access called with invalid size (%d)\n",
+		       entry->smbus.size);
 		return -1;
 	}
 
-	if (i2c_transfer(adapter, msg, num) < 0)
-		return -1;
-
-	if (read_write == I2C_SMBUS_READ)
-		switch(size) {
-			case I2C_SMBUS_BYTE:
-				data->byte = msgbuf0[0];
-				break;
-			case I2C_SMBUS_BYTE_DATA:
-				data->byte = msgbuf1[0];
-				break;
-			case I2C_SMBUS_WORD_DATA: 
-			case I2C_SMBUS_PROC_CALL:
-				data->word = msgbuf1[0] | (msgbuf1[1] << 8);
-				break;
-			case I2C_SMBUS_I2C_BLOCK_DATA:
-				/* fixed at 32 for now */
-				data->block[0] = I2C_SMBUS_I2C_BLOCK_MAX;
-				for (i = 0; i < I2C_SMBUS_I2C_BLOCK_MAX; i++)
-					data->block[i+1] = msgbuf1[i];
-				break;
-		}
+	entry->xfer_type = I2C_OP_I2C;
+	entry->i2c.msgs = msg;
+	entry->i2c.num = num;
+	entry->complete = i2c_smbus_emu_complete;
 	return 0;
 }
 
+/* Simulate a SMBus command using the i2c protocol 
+   No checking of parameters is done!  */
+static s32 i2c_smbus_xfer_emulated(struct i2c_adapter * adap,
+				   struct i2c_op_q_entry * entry)
+
+{
+	if (i2c_smbus_emu_format(adap, entry))
+		return -EINVAL;
+
+	if (i2c_transfer_entry(adap, entry) < 0)
+		return -EINVAL;
 
-s32 i2c_smbus_xfer(struct i2c_adapter * adapter, u16 addr, unsigned short flags,
+	return entry->result;
+}
+
+s32 i2c_smbus_xfer(struct i2c_adapter * adap, u16 addr, unsigned short flags,
                    char read_write, u8 command, int size, 
                    union i2c_smbus_data * data)
 {
-	s32 res;
-	int swpec = 0;
-	u8 partial = 0;
-
-	flags &= I2C_M_TEN | I2C_CLIENT_PEC;
-	if((flags & I2C_CLIENT_PEC) &&
-	   !(i2c_check_functionality(adapter, I2C_FUNC_SMBUS_HWPEC_CALC))) {
-		swpec = 1;
-		if(read_write == I2C_SMBUS_READ &&
-		   size == I2C_SMBUS_BLOCK_DATA)
-			size = I2C_SMBUS_BLOCK_DATA_PEC;
-		else if(size == I2C_SMBUS_PROC_CALL)
-			size = I2C_SMBUS_PROC_CALL_PEC;
-		else if(size == I2C_SMBUS_BLOCK_PROC_CALL) {
-			i2c_smbus_add_pec(addr, command,
-		                          I2C_SMBUS_BLOCK_DATA, data);
-			partial = data->block[data->block[0] + 1];
-			size = I2C_SMBUS_BLOCK_PROC_CALL_PEC;
-		} else if(read_write == I2C_SMBUS_WRITE &&
-		          size != I2C_SMBUS_QUICK &&
-		          size != I2C_SMBUS_I2C_BLOCK_DATA)
-			size = i2c_smbus_add_pec(addr, command, size, data);
-	}
-
-	if (adapter->algo->smbus_xfer) {
-		down(&adapter->bus_lock);
-		res = adapter->algo->smbus_xfer(adapter,addr,flags,read_write,
-		                                command,size,data);
-		up(&adapter->bus_lock);
-	} else
-		res = i2c_smbus_xfer_emulated(adapter,addr,flags,read_write,
-	                                      command,size,data);
-
-	if(res >= 0 && swpec &&
-	   size != I2C_SMBUS_QUICK && size != I2C_SMBUS_I2C_BLOCK_DATA &&
-	   (read_write == I2C_SMBUS_READ || size == I2C_SMBUS_PROC_CALL_PEC ||
-	    size == I2C_SMBUS_BLOCK_PROC_CALL_PEC)) {
-		if(i2c_smbus_check_pec(addr, command, size, partial, data))
-			return -1;
+	struct i2c_op_q_entry *entry;
+	struct i2c_algorithm  *algo = adap->algo;
+	int                   result;
+
+
+	entry = kmalloc(sizeof(*entry), GFP_KERNEL);
+	if (!entry)
+	    return -ENOMEM;
+
+	entry->xfer_type = I2C_OP_SMBUS;
+	entry->smbus.addr = addr;
+	entry->smbus.flags = flags;
+	entry->smbus.read_write = read_write;
+	entry->smbus.command = command;
+	entry->smbus.size = size;
+	entry->smbus.data = data;
+
+	i2c_smbus_format_entry(adap, entry);
+
+	if (algo->smbus_start) {
+		i2c_perform_op_wait(adap, entry);
+	} else if (algo->smbus_xfer) {
+		down(&adap->bus_lock);
+		entry->result = adap->algo->smbus_xfer(adap,
+						       entry->smbus.addr,
+						       entry->smbus.flags,
+						       entry->smbus.read_write,
+						       entry->smbus.command,
+						       entry->smbus.size,
+						       entry->smbus.data);
+		up(&adap->bus_lock);
+	} else {
+		i2c_smbus_xfer_emulated(adap, entry);
 	}
-	return res;
+
+	result = entry->result;
+	kfree(entry);
+	return result;
 }
 
+int i2c_non_blocking_capable(struct i2c_adapter *adap)
+{
+	return adap->algo->poll != NULL;
+}
+
+void i2c_poll(struct i2c_client *client,
+	      unsigned int us_since_last_call)
+{
+	struct i2c_adapter *adap = client->adapter;
+	struct i2c_op_q_entry *entry;
+    
+	entry = i2c_entry_get(adap);
+	if (!entry)
+		return;
+	adap->algo->poll(adap, entry, us_since_last_call);
+	i2c_entry_put(adap, entry);
+}
+
+int i2c_non_blocking_op(struct i2c_client *client,
+			struct i2c_op_q_entry *entry)
+{
+	unsigned long      flags;
+	struct i2c_adapter *adap = client->adapter;
+
+	if (!i2c_non_blocking_capable(adap))
+		return -ENOSYS;
+
+	entry->smbus.addr = client->addr;
+	entry->smbus.flags = client->flags;
+
+	if (entry->xfer_type == I2C_OP_SMBUS) {
+		i2c_smbus_format_entry(adap, entry);
+		if (!adap->algo->smbus_start) {
+			if (i2c_smbus_emu_format(adap, entry))
+				return -EINVAL;
+		}
+	}
+
+	entry->start = NULL;
+	entry->started = 0;
+	entry->completed = 0;
+	entry->result = 0;
+	entry->use_timer = 1; /* Let the timer code poll it. */
+	entry->data = NULL;
+	atomic_set(&entry->usecount, 1);
+
+	spin_lock_irqsave(&adap->q_lock, flags);
+	list_add_tail(&entry->link, &adap->q);
+	if (adap->q.next == &entry->link) {
+		/* Added to the list head, start it */
+		spin_unlock_irqrestore(&adap->q_lock, flags);
+		i2c_start_entry(adap, entry);
+	} else
+		spin_unlock_irqrestore(&adap->q_lock, flags);
+	return 0;
+}
 
 /* You should always define `functionality'; the 'else' is just for
    backward compatibility. */ 
@@ -1258,6 +1717,7 @@
 EXPORT_SYMBOL(i2c_clients_command);
 EXPORT_SYMBOL(i2c_check_addr);
 
+EXPORT_SYMBOL(i2c_op_done);
 EXPORT_SYMBOL(i2c_master_send);
 EXPORT_SYMBOL(i2c_master_recv);
 EXPORT_SYMBOL(i2c_control);
@@ -1278,6 +1738,10 @@
 EXPORT_SYMBOL(i2c_smbus_write_block_data);
 EXPORT_SYMBOL(i2c_smbus_read_i2c_block_data);
 
+EXPORT_SYMBOL(i2c_non_blocking_capable);
+EXPORT_SYMBOL(i2c_poll);
+EXPORT_SYMBOL(i2c_non_blocking_op);
+
 EXPORT_SYMBOL(i2c_get_functionality);
 EXPORT_SYMBOL(i2c_check_functionality);
 
Index: linux-2.6.11-rc2/include/linux/i2c.h
===================================================================
--- linux-2.6.11-rc2.orig/include/linux/i2c.h	2005-01-26 15:59:53.000000000 -0600
+++ linux-2.6.11-rc2/include/linux/i2c.h	2005-01-28 08:53:31.000000000 -0600
@@ -32,7 +32,11 @@
 #include <linux/types.h>
 #include <linux/i2c-id.h>
 #include <linux/device.h>	/* for struct device */
+#include <linux/list.h>
+#include <linux/spinlock.h>
+#include <linux/timer.h>
 #include <asm/semaphore.h>
+#include <asm/atomic.h>
 
 /* --- General options ------------------------------------------------	*/
 
@@ -43,6 +47,7 @@
 struct i2c_driver;
 struct i2c_client_address_data;
 union i2c_smbus_data;
+struct i2c_op_q_entry;
 
 /*
  * The master routines are the ones normally used to transmit data to devices
@@ -55,7 +60,7 @@
 
 /* Transfer num messages.
  */
-extern int i2c_transfer(struct i2c_adapter *adap, struct i2c_msg msg[],int num);
+extern int i2c_transfer(struct i2c_adapter *adap, struct i2c_msg *msg,int num);
 
 /*
  * Some adapter types (i.e. PCF 8584 based ones) may support slave behaviuor. 
@@ -95,6 +100,25 @@
 extern s32 i2c_smbus_read_i2c_block_data(struct i2c_client * client,
 					 u8 command, u8 *values);
 
+/* Non-blocking interface.  The user should fill out the public
+   portions of the entry structure.  All data in the entry structure
+   should be guaranteed to be available until the handler callback is
+   called with the entry. */
+typedef void (*i2c_op_done_cb)(struct i2c_op_q_entry *entry);
+
+extern int i2c_non_blocking_op(struct i2c_client *client,
+			       struct i2c_op_q_entry *entry);
+
+/* Can the adapter do non-blocking operations? */
+extern int i2c_non_blocking_capable(struct i2c_adapter *adap);
+
+/* Poll the i2c interface.  This should only be called in a situation
+   where scheduling and interrupts are off.  You should put the amount
+   of microseconds between calls in us_since_last_call. */
+extern void i2c_poll(struct i2c_client *client,
+		     unsigned int us_since_last_call);
+
+
 /*
  * A driver is capable of handling one or more physical devices present on
  * I2C adapters. This information is used to inform the driver of adapter
@@ -181,6 +205,33 @@
 }
 
 /*
+ * About locking and the non-blocking interface.
+ *
+ * The poll operations are called single-threaded (along with the
+ * xxx_start operations), so if the driver is only polled then there
+ * is no need to do any locking.  If you are using interrupts, then
+ * the timer operations and interrupts can race and you need to lock
+ * appropriately.
+ * 
+ * i2c_op_done() can be called multiple times on the same entry (as
+ * long as each one has a get operation).  This handles poll and
+ * interrupt races calling i2c_op_done().  It will do the right thing.
+ */
+
+/* Called from an non-blocking interface to get the current working
+   entry.  Returns NULL if there is none.  This is primarily for
+   interrupt handlers to determine what they should be working on.
+   Note that if you call i2c_entry_get() and get a non-null entry, you
+   must call i2c_entry_put() on it. */
+struct i2c_op_q_entry *i2c_entry_get(struct i2c_adapter * adap);
+void i2c_entry_put(struct i2c_adapter * adap,
+		   struct i2c_op_q_entry * entry);
+
+/* Called from an non-blocking interface to report that an operation
+   has completed.  Can be called from interrupt context. */
+void i2c_op_done(struct i2c_adapter *adap, struct i2c_op_q_entry *entry);
+
+/*
  * The following structs are for those who like to implement new bus drivers:
  * i2c_algorithm is the interface to a class of hardware solutions which can
  * be addressed using the same bus algorithms - i.e. bit-banging or the PCF8584
@@ -190,15 +241,40 @@
 	char name[32];				/* textual description 	*/
 	unsigned int id;
 
-	/* If an adapter algorithm can't to I2C-level access, set master_xfer
+	/* If an adapter algorithm can't do I2C-level access, set master_xfer
 	   to NULL. If an adapter algorithm can do SMBus access, set 
 	   smbus_xfer. If set to NULL, the SMBus protocol is simulated
 	   using common I2C messages */
-	int (*master_xfer)(struct i2c_adapter *adap,struct i2c_msg msgs[], 
-	                   int num);
-	int (*smbus_xfer) (struct i2c_adapter *adap, u16 addr, 
-	                   unsigned short flags, char read_write,
-	                   u8 command, int size, union i2c_smbus_data * data);
+	int (*master_xfer)(struct i2c_adapter *adap,struct i2c_msg *msgs, 
+			   int num);
+	int (*smbus_xfer)(struct i2c_adapter *adap, u16 addr, 
+			  unsigned short flags, char read_write,
+			  u8 command, int size, union i2c_smbus_data * data);
+
+	/* These are like the previous calls, but they will only start
+	   the operation.  The poll call will be called periodically
+	   to drive the operation of the bus.  Each of these calls
+	   should set the result on an error, and set the timeout as
+	   necessary.  Note that even interrupt driven drivers need to
+	   poll so they can time out operations.  When the operation
+	   is complete, these should call i2c_op_done().  Note that
+	   all the data structures passed in are guaranteed to be kept
+	   around until the operation completes.  These may be called
+	   from interrupt context.  If the start operation fails, this
+	   should set the result and call i2c_op_done(). */
+	void (*master_start)(struct i2c_adapter    *adap,
+			     struct i2c_op_q_entry *entry);
+	void (*smbus_start)(struct i2c_adapter    *adap,
+			    struct i2c_op_q_entry *entry);
+	/* us_since_last_poll is the amount of time since the last
+	   time poll was called. Note that this may be *less* than the
+	   time you requested, so always use this number and don't
+	   assume it's the one you gave it.  This time is approximate
+	   and is only guaranteed to be >= the time since the last
+	   poll.  The value may be zero. */
+	void (*poll)(struct i2c_adapter *adap,
+		     struct i2c_op_q_entry *entry,
+		     unsigned int us_since_last_poll);
 
 	/* --- these optional/future use for some adapter types.*/
 	int (*slave_send)(struct i2c_adapter *,char*,int);
@@ -212,6 +288,21 @@
 };
 
 /*
+ * The timer has it's own separately allocated data structure because
+ * it needs to be able to exist even if the adapter is deleted (due to
+ * timer cancellation races).
+ */
+struct i2c_timer {
+	spinlock_t lock;
+	int deleted;
+	struct timer_list timer;
+	int running;
+	unsigned int next_call_time;
+	struct i2c_adapter *adapter;
+	unsigned int sequence;
+};
+
+/*
  * i2c_adapter is the structure used to identify a physical i2c bus along
  * with the access algorithms necessary to access it.
  */
@@ -228,8 +319,15 @@
 	int (*client_unregister)(struct i2c_client *);
 
 	/* data fields that are valid for all devices	*/
+	struct list_head q;
+	spinlock_t q_lock;
+
 	struct semaphore bus_lock;
-	struct semaphore clist_lock;
+
+	/* Used to time non-blocking operations.  The sequence is used
+	   to handle race conditions in the timer handler. */
+	struct i2c_timer *timer;
+	unsigned int timer_sequence;
 
 	int timeout;
 	int retries;
@@ -242,9 +340,14 @@
 #endif /* def CONFIG_PROC_FS */
 
 	int nr;
+
+	struct semaphore clist_lock;
 	struct list_head clients;
+
 	struct list_head list;
+
 	char name[I2C_NAME_SIZE];
+
 	struct completion dev_released;
 	struct completion class_dev_released;
 };
@@ -395,6 +498,90 @@
  	__u8 *buf;		/* pointer to msg data			*/
 };
 
+/*
+ * Hold a queue of I2C operations to perform, and used to pass data
+ * around.
+ */
+#define I2C_OP_I2C	0
+#define I2C_OP_SMBUS	1
+struct i2c_op_q_entry {
+	/* The result will be set to the result of the operation when
+	   it completes. */
+	s32 result;
+
+	/**************************************************************/
+	/* Public interface.  The user should set these up (and the
+	   proper structure below). */
+	int            xfer_type;
+
+	/* Handler may be called from interrupt context, so be
+	   careful. */
+	i2c_op_done_cb handler;
+	void           *handler_data;
+
+	/* Note that this is not a union because an smbus operation
+	   may be converted into an i2c operation (thus both
+	   structures will be used).  The data in these may be changd
+	   by the driver. */
+	struct {
+		struct i2c_msg *msgs;
+		int num;
+	} i2c;
+	struct {
+		/* Addr and flags are filled in by the non-blocking
+		   send routine that takes a client. */
+		u16 addr;
+		unsigned short flags;
+
+		char read_write;
+		u8 command;
+
+		/* Note that the size is *not* the length of the data.
+		   It is the transaction type, like I2C_SMBUS_QUICK
+		   and the ones after that below.  If this is a block
+		   transaction, the length of the rest of the data is
+		   in the first byte of the data, for both transmit
+		   and receive. */
+		int size;
+		union i2c_smbus_data *data;
+	} smbus;
+
+	/**************************************************************/
+	/* For use by the bus interface.  The bus interface sets the
+	   timeout in microseconds until the next poll operation.
+	   This *must* be set in the start operation.  The time_left
+	   and data can be used for anything the bus interface likes.
+	   data will be set to NULL before being started so the bus
+	   interface can use that to tell if it has been set up
+	   yet. */
+	unsigned int call_again_us;
+	long         time_left;
+	void         *data;
+
+	/**************************************************************/
+	/* Internals */
+	struct list_head  link;
+	struct completion *start;
+	unsigned int      started : 1;
+	unsigned int      completed : 1;
+	unsigned int      use_timer : 1;
+	u8                swpec;
+	u8                partial;
+	void (*complete)(struct i2c_adapter    *adap,
+			 struct i2c_op_q_entry *entry);
+
+	/* It's wierd, but we use a usecount to track if an q entry is
+	   in use and when it should be reported back to the user. */
+	atomic_t usecount;
+
+	/* These are here for SMBus emulation over I2C.  I don't like
+	   them taking this much room in the data structure, but they
+	   need to be available in this case. */
+	unsigned char msgbuf0[34];
+	unsigned char msgbuf1[34];
+	struct i2c_msg msg[2];
+};
+
 /* To determine what functionality is present */
 
 #define I2C_FUNC_I2C			0x00000001
@@ -423,22 +610,22 @@
 #define I2C_FUNC_SMBUS_READ_BLOCK_DATA_PEC  0x40000000 /* SMBus 2.0 */
 #define I2C_FUNC_SMBUS_WRITE_BLOCK_DATA_PEC 0x80000000 /* SMBus 2.0 */
 
-#define I2C_FUNC_SMBUS_BYTE I2C_FUNC_SMBUS_READ_BYTE | \
-                            I2C_FUNC_SMBUS_WRITE_BYTE
-#define I2C_FUNC_SMBUS_BYTE_DATA I2C_FUNC_SMBUS_READ_BYTE_DATA | \
-                                 I2C_FUNC_SMBUS_WRITE_BYTE_DATA
-#define I2C_FUNC_SMBUS_WORD_DATA I2C_FUNC_SMBUS_READ_WORD_DATA | \
-                                 I2C_FUNC_SMBUS_WRITE_WORD_DATA
-#define I2C_FUNC_SMBUS_BLOCK_DATA I2C_FUNC_SMBUS_READ_BLOCK_DATA | \
-                                  I2C_FUNC_SMBUS_WRITE_BLOCK_DATA
-#define I2C_FUNC_SMBUS_I2C_BLOCK I2C_FUNC_SMBUS_READ_I2C_BLOCK | \
-                                  I2C_FUNC_SMBUS_WRITE_I2C_BLOCK
-#define I2C_FUNC_SMBUS_I2C_BLOCK_2 I2C_FUNC_SMBUS_READ_I2C_BLOCK_2 | \
-                                   I2C_FUNC_SMBUS_WRITE_I2C_BLOCK_2
-#define I2C_FUNC_SMBUS_BLOCK_DATA_PEC I2C_FUNC_SMBUS_READ_BLOCK_DATA_PEC | \
-                                      I2C_FUNC_SMBUS_WRITE_BLOCK_DATA_PEC
-#define I2C_FUNC_SMBUS_WORD_DATA_PEC  I2C_FUNC_SMBUS_READ_WORD_DATA_PEC | \
-                                      I2C_FUNC_SMBUS_WRITE_WORD_DATA_PEC
+#define I2C_FUNC_SMBUS_BYTE (I2C_FUNC_SMBUS_READ_BYTE | \
+                             I2C_FUNC_SMBUS_WRITE_BYTE)
+#define I2C_FUNC_SMBUS_BYTE_DATA (I2C_FUNC_SMBUS_READ_BYTE_DATA | \
+                                  I2C_FUNC_SMBUS_WRITE_BYTE_DATA)
+#define I2C_FUNC_SMBUS_WORD_DATA (I2C_FUNC_SMBUS_READ_WORD_DATA | \
+                                  I2C_FUNC_SMBUS_WRITE_WORD_DATA)
+#define I2C_FUNC_SMBUS_BLOCK_DATA (I2C_FUNC_SMBUS_READ_BLOCK_DATA | \
+                                   I2C_FUNC_SMBUS_WRITE_BLOCK_DATA)
+#define I2C_FUNC_SMBUS_I2C_BLOCK (I2C_FUNC_SMBUS_READ_I2C_BLOCK | \
+                                  I2C_FUNC_SMBUS_WRITE_I2C_BLOCK)
+#define I2C_FUNC_SMBUS_I2C_BLOCK_2 (I2C_FUNC_SMBUS_READ_I2C_BLOCK_2 | \
+                                    I2C_FUNC_SMBUS_WRITE_I2C_BLOCK_2)
+#define I2C_FUNC_SMBUS_BLOCK_DATA_PEC (I2C_FUNC_SMBUS_READ_BLOCK_DATA_PEC | \
+                                       I2C_FUNC_SMBUS_WRITE_BLOCK_DATA_PEC)
+#define I2C_FUNC_SMBUS_WORD_DATA_PEC  (I2C_FUNC_SMBUS_READ_WORD_DATA_PEC | \
+                                       I2C_FUNC_SMBUS_WRITE_WORD_DATA_PEC)
 
 #define I2C_FUNC_SMBUS_READ_BYTE_PEC		I2C_FUNC_SMBUS_READ_BYTE_DATA
 #define I2C_FUNC_SMBUS_WRITE_BYTE_PEC		I2C_FUNC_SMBUS_WRITE_BYTE_DATA
@@ -447,14 +634,14 @@
 #define I2C_FUNC_SMBUS_BYTE_PEC			I2C_FUNC_SMBUS_BYTE_DATA
 #define I2C_FUNC_SMBUS_BYTE_DATA_PEC		I2C_FUNC_SMBUS_WORD_DATA
 
-#define I2C_FUNC_SMBUS_EMUL I2C_FUNC_SMBUS_QUICK | \
-                            I2C_FUNC_SMBUS_BYTE | \
-                            I2C_FUNC_SMBUS_BYTE_DATA | \
-                            I2C_FUNC_SMBUS_WORD_DATA | \
-                            I2C_FUNC_SMBUS_PROC_CALL | \
-                            I2C_FUNC_SMBUS_WRITE_BLOCK_DATA | \
-                            I2C_FUNC_SMBUS_WRITE_BLOCK_DATA_PEC | \
-                            I2C_FUNC_SMBUS_I2C_BLOCK
+#define I2C_FUNC_SMBUS_EMUL (I2C_FUNC_SMBUS_QUICK | \
+                             I2C_FUNC_SMBUS_BYTE | \
+                             I2C_FUNC_SMBUS_BYTE_DATA | \
+                             I2C_FUNC_SMBUS_WORD_DATA | \
+                             I2C_FUNC_SMBUS_PROC_CALL | \
+                             I2C_FUNC_SMBUS_WRITE_BLOCK_DATA | \
+                             I2C_FUNC_SMBUS_WRITE_BLOCK_DATA_PEC | \
+                             I2C_FUNC_SMBUS_I2C_BLOCK)
 
 /* 
  * Data for SMBus Messages 

^ permalink raw reply	[flat|nested] 7+ messages in thread

* [PATCH] Updates for the i801 driver to support the I2C non-blocking interface
       [not found]     ` <41F9E183.5A9B1BA2@gte.net>
                         ` (2 preceding siblings ...)
  2005-01-28 15:08       ` [PATCH] Add a non-blocking " Corey Minyard
@ 2005-01-28 15:11       ` Corey Minyard
  3 siblings, 0 replies; 7+ messages in thread
From: Corey Minyard @ 2005-01-28 15:11 UTC (permalink / raw)
  To: Bukie Mabayoje
  Cc: Mark Studebaker, Sensors, lkml, Frodo Looijaard, Philip Edelbrock

[-- 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)) {

^ permalink raw reply	[flat|nested] 7+ messages in thread

end of thread, other threads:[~2005-01-28 15:16 UTC | newest]

Thread overview: 7+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2005-01-24 15:05 Adding an async I2C interface Corey Minyard
2005-01-28  0:18 ` Mark Studebaker
2005-01-28  4:43   ` Corey Minyard
     [not found]     ` <41F9E183.5A9B1BA2@gte.net>
2005-01-28  7:23       ` Bukie Mabayoje
2005-01-28 14:02       ` Corey Minyard
2005-01-28 15:08       ` [PATCH] Add a non-blocking " Corey Minyard
2005-01-28 15:11       ` [PATCH] Updates for the i801 driver to support the I2C non-blocking interface Corey Minyard

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).