* [PATCH] Add a non-blocking interface to the I2C code, part 4
2005-05-19 6:25 ` Corey Minyard
@ 2005-05-19 6:25 ` Corey Minyard
-1 siblings, 0 replies; 18+ messages in thread
From: Corey Minyard @ 2005-03-02 20:02 UTC (permalink / raw)
To: Greg KH, lkml, Sensors
[-- Attachment #1: Type: text/plain, Size: 45 bytes --]
See part 1 for details on what this does...
[-- Attachment #2: i2c_add_timer.diff --]
[-- Type: text/plain, Size: 6329 bytes --]
Add a timer to the I2C layer. This doesn't do much until the
non-blocking code shows up.
Signed-off-by: Corey Minyard <minyard@acm.org>
Index: linux-2.6.11-rc5-mm1/drivers/i2c/i2c-core.c
===================================================================
--- linux-2.6.11-rc5-mm1.orig/drivers/i2c/i2c-core.c
+++ linux-2.6.11-rc5-mm1/drivers/i2c/i2c-core.c
@@ -30,9 +30,16 @@
#include <linux/init.h>
#include <linux/idr.h>
#include <linux/seq_file.h>
+#include <linux/rcupdate.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);
static DECLARE_MUTEX(core_lists);
@@ -174,6 +181,19 @@
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.
@@ -216,6 +236,7 @@
struct i2c_driver *driver;
struct i2c_client *client;
int res = 0;
+ unsigned long flags;
down(&core_lists);
@@ -260,6 +281,19 @@
}
}
+ /* 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);
+ }
+ /* Wait to make sure the timer is done. */
+ synchronize_kernel();
+ adap->timer = NULL;
+
/* clean up the sysfs representation */
init_completion(&adap->dev_released);
init_completion(&adap->class_dev_released);
@@ -582,6 +616,83 @@
module_exit(i2c_exit);
/* ----------------------------------------------------
+ * Timer operations
+ * ----------------------------------------------------
+ */
+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) t;
+ 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) {
+ /* Poll will go here. */
+
+ if (!entry_completed(entry))
+ 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);
+}
+
+/* ----------------------------------------------------
* the functional interface to the i2c busses.
* ----------------------------------------------------
*/
@@ -1419,6 +1530,21 @@
if (atomic_dec_and_test(&e->completed)) {
/* We are the lucky winner! We get to clean up the
entry. */
+ if (e->use_timer) {
+ unsigned long flags;
+ 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++;
+ else
+ t->running = 0;
+ spin_unlock_irqrestore(&t->lock, flags);
+ }
if (e->complete)
e->complete(adap, e);
}
Index: linux-2.6.11-rc5-mm1/include/linux/i2c.h
===================================================================
--- linux-2.6.11-rc5-mm1.orig/include/linux/i2c.h
+++ linux-2.6.11-rc5-mm1/include/linux/i2c.h
@@ -36,6 +36,7 @@
#include <linux/kref.h>
#include <linux/list.h>
#include <linux/spinlock.h>
+#include <linux/timer.h>
#include <asm/semaphore.h>
#include <asm/atomic.h>
@@ -242,6 +243,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.
*/
@@ -263,6 +279,11 @@
struct semaphore bus_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;
struct device dev; /* the adapter device */
^ permalink raw reply [flat|nested] 18+ messages in thread* [PATCH] Add a non-blocking interface to the I2C code, part 4
@ 2005-05-19 6:25 ` Corey Minyard
0 siblings, 0 replies; 18+ messages in thread
From: Corey Minyard @ 2005-05-19 6:25 UTC (permalink / raw)
To: Greg KH, lkml, Sensors
See part 1 for details on what this does...
-------------- next part --------------
Add a timer to the I2C layer. This doesn't do much until the
non-blocking code shows up.
Signed-off-by: Corey Minyard <minyard@acm.org>
Index: linux-2.6.11-rc5-mm1/drivers/i2c/i2c-core.c
=================================--- linux-2.6.11-rc5-mm1.orig/drivers/i2c/i2c-core.c
+++ linux-2.6.11-rc5-mm1/drivers/i2c/i2c-core.c
@@ -30,9 +30,16 @@
#include <linux/init.h>
#include <linux/idr.h>
#include <linux/seq_file.h>
+#include <linux/rcupdate.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);
static DECLARE_MUTEX(core_lists);
@@ -174,6 +181,19 @@
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.
@@ -216,6 +236,7 @@
struct i2c_driver *driver;
struct i2c_client *client;
int res = 0;
+ unsigned long flags;
down(&core_lists);
@@ -260,6 +281,19 @@
}
}
+ /* 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);
+ }
+ /* Wait to make sure the timer is done. */
+ synchronize_kernel();
+ adap->timer = NULL;
+
/* clean up the sysfs representation */
init_completion(&adap->dev_released);
init_completion(&adap->class_dev_released);
@@ -582,6 +616,83 @@
module_exit(i2c_exit);
/* ----------------------------------------------------
+ * Timer operations
+ * ----------------------------------------------------
+ */
+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) t;
+ 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) {
+ /* Poll will go here. */
+
+ if (!entry_completed(entry))
+ 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);
+}
+
+/* ----------------------------------------------------
* the functional interface to the i2c busses.
* ----------------------------------------------------
*/
@@ -1419,6 +1530,21 @@
if (atomic_dec_and_test(&e->completed)) {
/* We are the lucky winner! We get to clean up the
entry. */
+ if (e->use_timer) {
+ unsigned long flags;
+ 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++;
+ else
+ t->running = 0;
+ spin_unlock_irqrestore(&t->lock, flags);
+ }
if (e->complete)
e->complete(adap, e);
}
Index: linux-2.6.11-rc5-mm1/include/linux/i2c.h
=================================--- linux-2.6.11-rc5-mm1.orig/include/linux/i2c.h
+++ linux-2.6.11-rc5-mm1/include/linux/i2c.h
@@ -36,6 +36,7 @@
#include <linux/kref.h>
#include <linux/list.h>
#include <linux/spinlock.h>
+#include <linux/timer.h>
#include <asm/semaphore.h>
#include <asm/atomic.h>
@@ -242,6 +243,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.
*/
@@ -263,6 +279,11 @@
struct semaphore bus_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;
struct device dev; /* the adapter device */
^ permalink raw reply [flat|nested] 18+ messages in thread
* [PATCH] Add a non-blocking interface to the I2C code, part 7
2005-05-19 6:25 ` Corey Minyard
@ 2005-05-19 6:25 ` Corey Minyard
-1 siblings, 0 replies; 18+ messages in thread
From: Corey Minyard @ 2005-03-02 20:04 UTC (permalink / raw)
To: Greg KH, lkml, Sensors
[-- Attachment #1: Type: text/plain, Size: 45 bytes --]
See part 1 for details on what this does...
[-- Attachment #2: i2c_nonblock_i801.diff --]
[-- Type: text/plain, Size: 23685 bytes --]
This patch modifies the I801 SMBus driver to use the non-blocking
interface.
Signed-off-by: Corey Minyard <minyard@acm.org>
Index: linux-2.6.11-rc5-mm1/drivers/i2c/busses/i2c-i801.c
===================================================================
--- linux-2.6.11-rc5-mm1.orig/drivers/i2c/busses/i2c-i801.c
+++ linux-2.6.11-rc5-mm1/drivers/i2c/busses/i2c-i801.c
@@ -40,6 +40,14 @@
/* Note: we assume there can only be one I801, with one SMBus interface */
+/* Another note: This interface is extremely sensitive to timing and
+ failure handling. If you don't wait at least one jiffie after
+ starting the transaction before checking things, you will screw it
+ up. If you don't wait a jiffie after the final check, you will
+ screw it up. If you screw it up by these manners or by abandoning
+ an operation in progress, the I2C bus is likely stuck and won't
+ work any more. Gotta love this hardware. */
+
#include <linux/config.h>
#include <linux/module.h>
#include <linux/pci.h>
@@ -79,7 +87,8 @@
#define SMBHSTCFG_I2C_EN 4
/* Other settings */
-#define MAX_TIMEOUT 100
+#define MAX_TIMEOUT_US 100000
+#define RETRY_TIME_US 500 /* Retry minimum is 500us */
#define ENABLE_INT9 0 /* set to 0x01 to enable - untested */
/* I801 command constants */
@@ -105,21 +114,31 @@
"Forcibly enable the I801 at the given address. "
"EXTREMELY DANGEROUS!");
-static int i801_transaction(void);
-static int i801_block_transaction(union i2c_smbus_data *data,
- char read_write, int command);
-
static unsigned short i801_smba;
static struct pci_dev *I801_dev;
static int isich4;
+struct i801_i2c_data
+{
+ int i;
+ int len;
+ unsigned char hostc;
+ int block;
+ int hwpec;
+ int xact;
+ int hststs;
+ int wait_intr;
+ int finished;
+};
+struct i801_i2c_data i801_data;
+
static int i801_setup(struct pci_dev *dev)
{
int error_return = 0;
unsigned char temp;
/* Note: we keep on searching until we have found 'function 3' */
- if(PCI_FUNC(dev->devfn) != 3)
+ if (PCI_FUNC(dev->devfn) != 3)
return -ENODEV;
I801_dev = dev;
@@ -136,7 +155,7 @@
} else {
pci_read_config_word(I801_dev, SMBBA, &i801_smba);
i801_smba &= 0xfff0;
- if(i801_smba == 0) {
+ if (i801_smba == 0) {
dev_err(&dev->dev, "SMB base address uninitialized"
"- upgrade BIOS or use force_addr=0xaddr\n");
return -ENODEV;
@@ -180,12 +199,93 @@
return error_return;
}
-static int i801_transaction(void)
+static void i801_check_hststs(struct i2c_adapter *adap,
+ struct i2c_op_q_entry *entry,
+ struct i801_i2c_data *d)
+{
+ if (d->hststs & 0x10) {
+ entry->result = -EIO;
+ dev_dbg(&I801_dev->dev,
+ "Error: Failed bus transaction\n");
+ } else if (d->hststs & 0x08) {
+ entry->result = -EIO;
+ dev_err(&I801_dev->dev, "Bus collision!\n");
+ /* Clock stops and slave is stuck in mid-transmission */
+ } else if (d->hststs & 0x04) {
+ entry->result = -EIO;
+ dev_dbg(&I801_dev->dev, "Error: no response!\n");
+ }
+}
+
+static void i801_finish(struct i2c_adapter *adap,
+ struct i2c_op_q_entry *entry,
+ struct i801_i2c_data *d)
+{
+ d->finished = 1;
+
+#ifdef HAVE_PEC
+ if (isich4 && d->hwpec) {
+ if (entry->smbus.size != I2C_SMBUS_QUICK &&
+ entry->smbus.size != I2C_SMBUS_I2C_BLOCK_DATA)
+ outb_p(0, SMBAUXCTL);
+ }
+#endif
+
+ if (d->block || (entry->result < 0) ||
+ ((entry->smbus.read_write == I2C_SMBUS_WRITE)
+ || (d->xact == I801_QUICK)))
+ return;
+
+ switch (d->xact & 0x7f) {
+ case I801_BYTE: /* Result put in SMBHSTDAT0 */
+ case I801_BYTE_DATA:
+ entry->smbus.data->byte = inb_p(SMBHSTDAT0);
+ break;
+ case I801_WORD_DATA:
+ entry->smbus.data->word = inb_p(SMBHSTDAT0)
+ + (inb_p(SMBHSTDAT1) << 8);
+ break;
+ }
+}
+
+static void i801_transaction_final_check(struct i2c_adapter *adap,
+ struct i2c_op_q_entry *entry,
+ struct i801_i2c_data *d)
+{
+ i801_check_hststs(adap, entry, d);
+
+ if ((inb_p(SMBHSTSTS) & 0x1f) != 0x00)
+ outb_p(inb(SMBHSTSTS), SMBHSTSTS);
+
+ if ((d->hststs = (0x1f & inb_p(SMBHSTSTS))) != 0x00) {
+ dev_dbg(&I801_dev->dev, "Failed reset at end of transaction"
+ "(%02x)\n", d->hststs);
+ }
+ dev_dbg(&I801_dev->dev, "Transaction (post): CNT=%02x, CMD=%02x, "
+ "ADD=%02x, DAT0=%02x, DAT1=%02x\n", inb_p(SMBHSTCNT),
+ inb_p(SMBHSTCMD), inb_p(SMBHSTADD), inb_p(SMBHSTDAT0),
+ inb_p(SMBHSTDAT1));
+}
+
+static void i801_transaction_poll(struct i2c_adapter *adap,
+ struct i2c_op_q_entry *entry,
+ struct i801_i2c_data *d)
{
- int temp;
- int result = 0;
- int timeout = 0;
+ d->hststs = inb_p(SMBHSTSTS);
+ if (!(d->hststs & 0x01)) {
+ i801_transaction_final_check(adap, entry, d);
+ i801_finish(adap, entry, d);
+ } else if (entry->time_left <= 0) {
+ dev_dbg(&I801_dev->dev, "SMBus Timeout!\n");
+ entry->result = -EIO;
+ i801_transaction_final_check(adap, entry, d);
+ }
+}
+static void i801_transaction_start(struct i2c_adapter *adap,
+ struct i2c_op_q_entry *entry,
+ struct i801_i2c_data *d)
+{
dev_dbg(&I801_dev->dev, "Transaction (pre): CNT=%02x, CMD=%02x,"
"ADD=%02x, DAT0=%02x, DAT1=%02x\n", inb_p(SMBHSTCNT),
inb_p(SMBHSTCMD), inb_p(SMBHSTADD), inb_p(SMBHSTDAT0),
@@ -193,331 +293,362 @@
/* Make sure the SMBus host is ready to start transmitting */
/* 0x1f = Failed, Bus_Err, Dev_Err, Intr, Host_Busy */
- if ((temp = (0x1f & inb_p(SMBHSTSTS))) != 0x00) {
+ if ((d->hststs = (0x1f & inb_p(SMBHSTSTS))) != 0x00) {
dev_dbg(&I801_dev->dev, "SMBus busy (%02x). Resetting... \n",
- temp);
- outb_p(temp, SMBHSTSTS);
- if ((temp = (0x1f & inb_p(SMBHSTSTS))) != 0x00) {
- dev_dbg(&I801_dev->dev, "Failed! (%02x)\n", temp);
- return -1;
+ d->hststs);
+ outb_p(d->hststs, SMBHSTSTS);
+ if ((d->hststs = (0x1f & inb_p(SMBHSTSTS))) != 0x00) {
+ dev_dbg(&I801_dev->dev, "Failed! (%02x)\n", d->hststs);
+ entry->result = -EIO;
+ return;
} else {
dev_dbg(&I801_dev->dev, "Successfull!\n");
}
}
outb_p(inb(SMBHSTCNT) | I801_START, SMBHSTCNT);
+}
- /* We will always wait for a fraction of a second! */
- do {
- msleep(1);
- temp = inb_p(SMBHSTSTS);
- } while ((temp & 0x01) && (timeout++ < MAX_TIMEOUT));
-
- /* If the SMBus is still busy, we give up */
- if (timeout >= MAX_TIMEOUT) {
- dev_dbg(&I801_dev->dev, "SMBus Timeout!\n");
- result = -1;
+static void i801_block_finish(struct i2c_adapter *adap,
+ struct i2c_op_q_entry *entry,
+ struct i801_i2c_data *d)
+{
+ if (entry->smbus.size == I2C_SMBUS_I2C_BLOCK_DATA) {
+ /* restore saved configuration register value */
+ pci_write_config_byte(I801_dev, SMBHSTCFG, d->hostc);
}
- if (temp & 0x10) {
- result = -1;
- dev_dbg(&I801_dev->dev, "Error: Failed bus transaction\n");
- }
+ i801_finish(adap, entry, d);
+}
- if (temp & 0x08) {
- result = -1;
- dev_err(&I801_dev->dev, "Bus collision! SMBus may be locked "
- "until next hard reset. (sorry!)\n");
- /* Clock stops and slave is stuck in mid-transmission */
- }
+static void i801_block_poll_wait_intr(struct i2c_adapter *adap,
+ struct i2c_op_q_entry *entry,
+ struct i801_i2c_data *d)
+{
+#ifdef HAVE_PEC
+ if (entry->result >= 0 &&
+ isich4 &&
+ entry->smbus.size == I2C_SMBUS_BLOCK_DATA_PEC)
+ {
+ /* wait for INTR bit as advised by Intel */
+ d->hststs = inb_p(SMBHSTSTS);
+ if (d->hststs & 0x02) {
+ outb_p(d->hststs, SMBHSTSTS);
+ i801_block_finish(adap, entry, d);
+ } else if (entry->time_left <= 0) {
+ /* Timed out */
+ outb_p(d->hststs, SMBHSTSTS);
+ entry->result = -EIO;
+ dev_dbg(&I801_dev->dev, "PEC Timeout!\n");
+ }
+ } else
+#endif
+ i801_block_finish(adap, entry, d);
+}
- if (temp & 0x04) {
- result = -1;
- dev_dbg(&I801_dev->dev, "Error: no response!\n");
+static void i801_block_next_byte(struct i2c_adapter *adap,
+ struct i2c_op_q_entry *entry,
+ struct i801_i2c_data *d)
+{
+ int smbcmd;
+ unsigned char errmask;
+
+ if (d->i > d->len) {
+ d->wait_intr = 1;
+ entry->time_left = MAX_TIMEOUT_US;
+ i801_block_poll_wait_intr(adap, entry, d);
+ return;
}
- if ((inb_p(SMBHSTSTS) & 0x1f) != 0x00)
- outb_p(inb(SMBHSTSTS), SMBHSTSTS);
+ if (d->i == d->len && entry->smbus.read_write == I2C_SMBUS_READ)
+ smbcmd = I801_BLOCK_LAST;
+ else
+ smbcmd = I801_BLOCK_DATA;
+ outb_p(smbcmd | ENABLE_INT9, SMBHSTCNT);
- if ((temp = (0x1f & inb_p(SMBHSTSTS))) != 0x00) {
- dev_dbg(&I801_dev->dev, "Failed reset at end of transaction"
- "(%02x)\n", temp);
+ dev_dbg(&I801_dev->dev, "Block (pre %d): CNT=%02x, CMD=%02x, "
+ "ADD=%02x, DAT0=%02x, BLKDAT=%02x\n", d->i,
+ inb_p(SMBHSTCNT), inb_p(SMBHSTCMD), inb_p(SMBHSTADD),
+ inb_p(SMBHSTDAT0), inb_p(SMBBLKDAT));
+
+ /* Make sure the SMBus host is ready to start transmitting */
+ d->hststs = inb_p(SMBHSTSTS);
+ if (d->i == 1) {
+ /* Erronenous conditions before transaction:
+ * Byte_Done, Failed, Bus_Err, Dev_Err, Intr, Host_Busy */
+ errmask=0x9f;
+ } else {
+ /* Erronenous conditions during transaction:
+ * Failed, Bus_Err, Dev_Err, Intr */
+ errmask=0x1e;
+ }
+ if (d->hststs & errmask) {
+ dev_dbg(&I801_dev->dev, "SMBus busy (%02x). "
+ "Resetting... \n", d->hststs);
+ outb_p(d->hststs, SMBHSTSTS);
+ if (((d->hststs = inb_p(SMBHSTSTS)) & errmask) != 0x00) {
+ dev_err(&I801_dev->dev,
+ "Reset failed! (%02x)\n", d->hststs);
+ entry->result = -EIO;
+ return;
+ }
+ if (d->i != 1) {
+ /* if die in middle of block transaction, fail */
+ entry->result = -EIO;
+ return;
+ }
}
- dev_dbg(&I801_dev->dev, "Transaction (post): CNT=%02x, CMD=%02x, "
- "ADD=%02x, DAT0=%02x, DAT1=%02x\n", inb_p(SMBHSTCNT),
- inb_p(SMBHSTCMD), inb_p(SMBHSTADD), inb_p(SMBHSTDAT0),
- inb_p(SMBHSTDAT1));
- return result;
+
+ if (d->i == 1)
+ outb_p(inb(SMBHSTCNT) | I801_START, SMBHSTCNT);
}
-/* All-inclusive block transaction function */
-static int i801_block_transaction(union i2c_smbus_data *data, char read_write,
- int command)
+/* Called on timer ticks. This checks the result of the
+ transaction. */
+static void i801_block_poll(struct i2c_adapter *adap,
+ struct i2c_op_q_entry *entry,
+ struct i801_i2c_data *d)
{
- int i, len;
- int smbcmd;
- int temp;
- int result = 0;
- int timeout;
- unsigned char hostc, errmask;
-
- if (command == I2C_SMBUS_I2C_BLOCK_DATA) {
- if (read_write == I2C_SMBUS_WRITE) {
- /* set I2C_EN bit in configuration register */
- pci_read_config_byte(I801_dev, SMBHSTCFG, &hostc);
- pci_write_config_byte(I801_dev, SMBHSTCFG,
- hostc | SMBHSTCFG_I2C_EN);
- } else {
- dev_err(&I801_dev->dev,
- "I2C_SMBUS_I2C_BLOCK_READ not DB!\n");
- return -1;
+ d->hststs = inb_p(SMBHSTSTS);
+ if (!(d->hststs & 0x80)) {
+ /* Not ready yet */
+ if (entry->time_left <= 0) {
+ dev_dbg(&I801_dev->dev, "SMBus Timeout!\n");
+ entry->result = -EIO;
}
+ return;
}
- if (read_write == I2C_SMBUS_WRITE) {
- len = data->block[0];
- if (len < 1)
- len = 1;
- if (len > 32)
- len = 32;
- outb_p(len, SMBHSTDAT0);
- outb_p(data->block[1], SMBBLKDAT);
+ i801_check_hststs(adap, entry, d);
+ if (d->i == 1 && entry->smbus.read_write == I2C_SMBUS_READ) {
+ d->len = inb_p(SMBHSTDAT0);
+ if (d->len < 1)
+ d->len = 1;
+ if (d->len > 32)
+ d->len = 32;
+ entry->smbus.data->block[0] = d->len;
+ }
+
+ /* Retrieve/store value in SMBBLKDAT */
+ if (entry->smbus.read_write == I2C_SMBUS_READ)
+ entry->smbus.data->block[d->i] = inb_p(SMBBLKDAT);
+ if (entry->smbus.read_write == I2C_SMBUS_WRITE && d->i+1 <= d->len)
+ outb_p(entry->smbus.data->block[d->i+1], SMBBLKDAT);
+ if ((d->hststs & 0x9e) != 0x00)
+ outb_p(d->hststs, SMBHSTSTS); /* signals SMBBLKDAT ready */
+
+ if ((d->hststs = (0x1e & inb_p(SMBHSTSTS))) != 0x00) {
+ dev_dbg(&I801_dev->dev,
+ "Bad status (%02x) at end of transaction\n",
+ d->hststs);
+ }
+ dev_dbg(&I801_dev->dev, "Block (post %d): CNT=%02x, CMD=%02x, "
+ "ADD=%02x, DAT0=%02x, BLKDAT=%02x\n", d->i,
+ inb_p(SMBHSTCNT), inb_p(SMBHSTCMD), inb_p(SMBHSTADD),
+ inb_p(SMBHSTDAT0), inb_p(SMBBLKDAT));
+
+ if (entry->result)
+ return;
+
+ (d->i)++;
+ i801_block_next_byte(adap, entry, d);
+}
+
+static void i801_block_start(struct i2c_adapter *adap,
+ struct i2c_op_q_entry *entry,
+ struct i801_i2c_data *d)
+{
+ if (entry->smbus.read_write == I2C_SMBUS_WRITE) {
+ d->len = entry->smbus.data->block[0];
+ if (d->len < 1)
+ d->len = 1;
+ if (d->len > 32)
+ d->len = 32;
+ outb_p(d->len, SMBHSTDAT0);
+ outb_p(entry->smbus.data->block[1], SMBBLKDAT);
} else {
- len = 32; /* max for reads */
+ d->len = 32; /* max for reads */
}
- if(isich4 && command != I2C_SMBUS_I2C_BLOCK_DATA) {
+ if(isich4 && entry->smbus.size != I2C_SMBUS_I2C_BLOCK_DATA) {
/* set 32 byte buffer */
}
- for (i = 1; i <= len; i++) {
- if (i == len && read_write == I2C_SMBUS_READ)
- smbcmd = I801_BLOCK_LAST;
- else
- smbcmd = I801_BLOCK_DATA;
- outb_p(smbcmd | ENABLE_INT9, SMBHSTCNT);
-
- dev_dbg(&I801_dev->dev, "Block (pre %d): CNT=%02x, CMD=%02x, "
- "ADD=%02x, DAT0=%02x, BLKDAT=%02x\n", i,
- inb_p(SMBHSTCNT), inb_p(SMBHSTCMD), inb_p(SMBHSTADD),
- inb_p(SMBHSTDAT0), inb_p(SMBBLKDAT));
-
- /* Make sure the SMBus host is ready to start transmitting */
- temp = inb_p(SMBHSTSTS);
- if (i == 1) {
- /* Erronenous conditions before transaction:
- * Byte_Done, Failed, Bus_Err, Dev_Err, Intr, Host_Busy */
- errmask=0x9f;
- } else {
- /* Erronenous conditions during transaction:
- * Failed, Bus_Err, Dev_Err, Intr */
- errmask=0x1e;
- }
- if (temp & errmask) {
- dev_dbg(&I801_dev->dev, "SMBus busy (%02x). "
- "Resetting... \n", temp);
- outb_p(temp, SMBHSTSTS);
- if (((temp = inb_p(SMBHSTSTS)) & errmask) != 0x00) {
- dev_err(&I801_dev->dev,
- "Reset failed! (%02x)\n", temp);
- result = -1;
- goto END;
- }
- if (i != 1) {
- /* if die in middle of block transaction, fail */
- result = -1;
- goto END;
- }
- }
+ d->i = 1;
+ i801_block_next_byte(adap, entry, d);
+}
- if (i == 1)
- outb_p(inb(SMBHSTCNT) | I801_START, SMBHSTCNT);
+/* General poll routine. Called periodically by the i2c code. */
+static void i801_poll(struct i2c_adapter *adap,
+ struct i2c_op_q_entry *entry,
+ unsigned int us_since_last_poll)
+{
+ struct i801_i2c_data *d = entry->data;
- /* We will always wait for a fraction of a second! */
- timeout = 0;
- do {
- temp = inb_p(SMBHSTSTS);
- msleep(1);
- }
- while ((!(temp & 0x80))
- && (timeout++ < MAX_TIMEOUT));
+ dev_dbg(&I801_dev->dev, "Poll call for %p %p at %ld\n", adap, entry,
+ jiffies);
- /* If the SMBus is still busy, we give up */
- if (timeout >= MAX_TIMEOUT) {
- result = -1;
- dev_dbg(&I801_dev->dev, "SMBus Timeout!\n");
- }
+ if (!d)
+ /* The entry hasn't been started yet. */
+ return;
- if (temp & 0x10) {
- result = -1;
- dev_dbg(&I801_dev->dev,
- "Error: Failed bus transaction\n");
- } else if (temp & 0x08) {
- result = -1;
- dev_err(&I801_dev->dev, "Bus collision!\n");
- } else if (temp & 0x04) {
- result = -1;
- dev_dbg(&I801_dev->dev, "Error: no response!\n");
- }
+ if (d->finished) {
+ /* We delay an extra poll to keep the hardware happy.
+ Otherwise the hardware is not ready when we start
+ the next operation. */
+ i2c_op_done(adap, entry);
+ return;
+ }
- if (i == 1 && read_write == I2C_SMBUS_READ) {
- len = inb_p(SMBHSTDAT0);
- if (len < 1)
- len = 1;
- if (len > 32)
- len = 32;
- data->block[0] = len;
- }
+ /* Decrement timeout */
+ entry->time_left -= us_since_last_poll;
- /* Retrieve/store value in SMBBLKDAT */
- if (read_write == I2C_SMBUS_READ)
- data->block[i] = inb_p(SMBBLKDAT);
- if (read_write == I2C_SMBUS_WRITE && i+1 <= len)
- outb_p(data->block[i+1], SMBBLKDAT);
- if ((temp & 0x9e) != 0x00)
- outb_p(temp, SMBHSTSTS); /* signals SMBBLKDAT ready */
-
- if ((temp = (0x1e & inb_p(SMBHSTSTS))) != 0x00) {
- dev_dbg(&I801_dev->dev,
- "Bad status (%02x) at end of transaction\n",
- temp);
- }
- dev_dbg(&I801_dev->dev, "Block (post %d): CNT=%02x, CMD=%02x, "
- "ADD=%02x, DAT0=%02x, BLKDAT=%02x\n", i,
- inb_p(SMBHSTCNT), inb_p(SMBHSTCMD), inb_p(SMBHSTADD),
- inb_p(SMBHSTDAT0), inb_p(SMBBLKDAT));
+ /* Wait a jiffie normally. */
+ entry->call_again_us = RETRY_TIME_US;
- if (result < 0)
- goto END;
+ if (d->block) {
+ if (d->wait_intr) {
+ i801_block_poll_wait_intr(adap, entry, d);
+ } else {
+ i801_block_poll(adap, entry, d);
+ }
+ if (entry->result < 0)
+ /* Error, finish the transaction */
+ i801_block_finish(adap, entry, d);
+ } else {
+ i801_transaction_poll(adap, entry, d);
+ if (entry->result < 0)
+ /* Error, finish the transaction */
+ i801_finish(adap, entry, d);
}
+}
-#ifdef HAVE_PEC
- if(isich4 && command == I2C_SMBUS_BLOCK_DATA_PEC) {
- /* wait for INTR bit as advised by Intel */
- timeout = 0;
- do {
- temp = inb_p(SMBHSTSTS);
- msleep(1);
- } while ((!(temp & 0x02))
- && (timeout++ < MAX_TIMEOUT));
+/* Start a general SMBUS transaction on the i801. Figure out what
+ kind of transaction it is, set it up, and start it. */
+static void i801_start(struct i2c_adapter *adap,
+ struct i2c_op_q_entry *entry)
+{
+ struct i801_i2c_data *d = adap->algo_data;
- if (timeout >= MAX_TIMEOUT) {
- dev_dbg(&I801_dev->dev, "PEC Timeout!\n");
+ dev_dbg(&I801_dev->dev, "start call for %p %p at %ld\n", adap, entry,
+ jiffies);
+
+ if (entry->smbus.size == I2C_SMBUS_I2C_BLOCK_DATA) {
+ if (entry->smbus.read_write == I2C_SMBUS_WRITE) {
+ /* set I2C_EN bit in configuration register */
+ pci_read_config_byte(I801_dev, SMBHSTCFG, &d->hostc);
+ pci_write_config_byte(I801_dev, SMBHSTCFG,
+ d->hostc | SMBHSTCFG_I2C_EN);
+ } else {
+ dev_err(&I801_dev->dev,
+ "I2C_SMBUS_I2C_BLOCK_READ not DB!\n");
+ entry->result = -EINVAL;
+ return;
}
- outb_p(temp, SMBHSTSTS);
- }
-#endif
- result = 0;
-END:
- if (command == I2C_SMBUS_I2C_BLOCK_DATA) {
- /* restore saved configuration register value */
- pci_write_config_byte(I801_dev, SMBHSTCFG, hostc);
}
- return result;
-}
-/* Return -1 on error. */
-static s32 i801_access(struct i2c_adapter * adap, u16 addr,
- unsigned short flags, char read_write, u8 command,
- int size, union i2c_smbus_data * data)
-{
- int hwpec = 0;
- int block = 0;
- int ret, xact = 0;
+ d->block = 0;
+ d->hwpec = 0;
+ d->xact = 0;
+ d->wait_intr = 0;
+ d->finished = 0;
#ifdef HAVE_PEC
- if(isich4)
- hwpec = (flags & I2C_CLIENT_PEC) != 0;
+ if (isich4)
+ d->hwpec = (entry->smbus.flags & I2C_CLIENT_PEC) != 0;
#endif
- switch (size) {
+ switch (entry->smbus.size) {
case I2C_SMBUS_QUICK:
- outb_p(((addr & 0x7f) << 1) | (read_write & 0x01),
+ outb_p(((entry->smbus.addr & 0x7f) << 1)
+ | (entry->smbus.read_write & 0x01),
SMBHSTADD);
- xact = I801_QUICK;
+ d->xact = I801_QUICK;
break;
case I2C_SMBUS_BYTE:
- outb_p(((addr & 0x7f) << 1) | (read_write & 0x01),
+ outb_p(((entry->smbus.addr & 0x7f) << 1)
+ | (entry->smbus.read_write & 0x01),
SMBHSTADD);
- if (read_write == I2C_SMBUS_WRITE)
- outb_p(command, SMBHSTCMD);
- xact = I801_BYTE;
+ if (entry->smbus.read_write == I2C_SMBUS_WRITE)
+ outb_p(entry->smbus.command, SMBHSTCMD);
+ d->xact = I801_BYTE;
break;
case I2C_SMBUS_BYTE_DATA:
- outb_p(((addr & 0x7f) << 1) | (read_write & 0x01),
+ outb_p(((entry->smbus.addr & 0x7f) << 1)
+ | (entry->smbus.read_write & 0x01),
SMBHSTADD);
- outb_p(command, SMBHSTCMD);
- if (read_write == I2C_SMBUS_WRITE)
- outb_p(data->byte, SMBHSTDAT0);
- xact = I801_BYTE_DATA;
+ outb_p(entry->smbus.command, SMBHSTCMD);
+ if (entry->smbus.read_write == I2C_SMBUS_WRITE)
+ outb_p(entry->smbus.data->byte, SMBHSTDAT0);
+ d->xact = I801_BYTE_DATA;
break;
case I2C_SMBUS_WORD_DATA:
- outb_p(((addr & 0x7f) << 1) | (read_write & 0x01),
+ outb_p(((entry->smbus.addr & 0x7f) << 1)
+ | (entry->smbus.read_write & 0x01),
SMBHSTADD);
- outb_p(command, SMBHSTCMD);
- if (read_write == I2C_SMBUS_WRITE) {
- outb_p(data->word & 0xff, SMBHSTDAT0);
- outb_p((data->word & 0xff00) >> 8, SMBHSTDAT1);
+ outb_p(entry->smbus.command, SMBHSTCMD);
+ if (entry->smbus.read_write == I2C_SMBUS_WRITE) {
+ outb_p(entry->smbus.data->word & 0xff, SMBHSTDAT0);
+ outb_p((entry->smbus.data->word & 0xff00) >> 8,
+ SMBHSTDAT1);
}
- xact = I801_WORD_DATA;
+ d->xact = I801_WORD_DATA;
break;
case I2C_SMBUS_BLOCK_DATA:
case I2C_SMBUS_I2C_BLOCK_DATA:
#ifdef HAVE_PEC
case I2C_SMBUS_BLOCK_DATA_PEC:
- if(hwpec && size == I2C_SMBUS_BLOCK_DATA)
- size = I2C_SMBUS_BLOCK_DATA_PEC;
+ if (d->hwpec && entry->smbus.size == I2C_SMBUS_BLOCK_DATA)
+ entry->smbus.size = I2C_SMBUS_BLOCK_DATA_PEC;
#endif
- outb_p(((addr & 0x7f) << 1) | (read_write & 0x01),
+ outb_p(((entry->smbus.addr & 0x7f) << 1)
+ | (entry->smbus.read_write & 0x01),
SMBHSTADD);
- outb_p(command, SMBHSTCMD);
- block = 1;
+ outb_p(entry->smbus.command, SMBHSTCMD);
+ d->block = 1;
break;
case I2C_SMBUS_PROC_CALL:
default:
- dev_err(&I801_dev->dev, "Unsupported transaction %d\n", size);
- return -1;
+ dev_err(&I801_dev->dev, "Unsupported transaction %d\n",
+ entry->smbus.size);
+ entry->result = -EINVAL;
+ return;
}
#ifdef HAVE_PEC
- if(isich4 && hwpec) {
- if(size != I2C_SMBUS_QUICK &&
- size != I2C_SMBUS_I2C_BLOCK_DATA)
+ if (isich4 && d->hwpec) {
+ if (entry->smbus.size != I2C_SMBUS_QUICK &&
+ entry->smbus.size != I2C_SMBUS_I2C_BLOCK_DATA)
outb_p(1, SMBAUXCTL); /* enable HW PEC */
}
#endif
- if(block)
- ret = i801_block_transaction(data, read_write, size);
- else {
- outb_p(xact | ENABLE_INT9, SMBHSTCNT);
- ret = i801_transaction();
+ if (d->block) {
+ i801_block_start(adap, entry, d);
+ if (entry->result < 0)
+ /* Error, finish the transaction */
+ i801_block_finish(adap, entry, d);
+ } else {
+ outb_p(d->xact | ENABLE_INT9, SMBHSTCNT);
+ i801_transaction_start(adap, entry, d);
+ if (entry->result < 0)
+ /* Error, finish the transaction */
+ i801_finish(adap, entry, d);
}
-#ifdef HAVE_PEC
- if(isich4 && hwpec) {
- if(size != I2C_SMBUS_QUICK &&
- size != I2C_SMBUS_I2C_BLOCK_DATA)
- outb_p(0, SMBAUXCTL);
- }
+ /* Wait extra long here, we want at least 2 ticks to guarantee
+ we wait >= 1 tick. But we want to wait at least 100us no
+ matter what. */
+#if (1000000 / HZ) < 100
+ entry->call_again_us = 200;
+#else
+ entry->call_again_us = (1000000 / HZ) * 2;
#endif
+ entry->time_left = MAX_TIMEOUT_US;
- if(block)
- return ret;
- if(ret)
- return -1;
- if ((read_write == I2C_SMBUS_WRITE) || (xact == I801_QUICK))
- return 0;
-
- switch (xact & 0x7f) {
- case I801_BYTE: /* Result put in SMBHSTDAT0 */
- case I801_BYTE_DATA:
- data->byte = inb_p(SMBHSTDAT0);
- break;
- case I801_WORD_DATA:
- data->word = inb_p(SMBHSTDAT0) + (inb_p(SMBHSTDAT1) << 8);
- break;
- }
- return 0;
+ if (d->finished)
+ i2c_op_done(adap, entry);
+ else
+ entry->data = d;
}
@@ -537,7 +668,8 @@
static struct i2c_algorithm smbus_algorithm = {
.name = "Non-I2C SMBus adapter",
.id = I2C_ALGO_SMBUS,
- .smbus_xfer = i801_access,
+ .smbus_start = i801_start,
+ .poll = i801_poll,
.functionality = i801_func,
};
@@ -545,6 +677,7 @@
.owner = THIS_MODULE,
.class = I2C_CLASS_HWMON,
.algo = &smbus_algorithm,
+ .algo_data = &i801_data,
.name = "unset",
};
@@ -563,7 +696,8 @@
MODULE_DEVICE_TABLE (pci, i801_ids);
-static int __devinit i801_probe(struct pci_dev *dev, const struct pci_device_id *id)
+static int __devinit i801_probe(struct pci_dev *dev,
+ const struct pci_device_id *id)
{
if (i801_setup(dev)) {
^ permalink raw reply [flat|nested] 18+ messages in thread* [PATCH] Add a non-blocking interface to the I2C code, part 7
@ 2005-05-19 6:25 ` Corey Minyard
0 siblings, 0 replies; 18+ messages in thread
From: Corey Minyard @ 2005-05-19 6:25 UTC (permalink / raw)
To: Greg KH, lkml, Sensors
See part 1 for details on what this does...
-------------- next part --------------
This patch modifies the I801 SMBus driver to use the non-blocking
interface.
Signed-off-by: Corey Minyard <minyard@acm.org>
Index: linux-2.6.11-rc5-mm1/drivers/i2c/busses/i2c-i801.c
=================================--- linux-2.6.11-rc5-mm1.orig/drivers/i2c/busses/i2c-i801.c
+++ linux-2.6.11-rc5-mm1/drivers/i2c/busses/i2c-i801.c
@@ -40,6 +40,14 @@
/* Note: we assume there can only be one I801, with one SMBus interface */
+/* Another note: This interface is extremely sensitive to timing and
+ failure handling. If you don't wait at least one jiffie after
+ starting the transaction before checking things, you will screw it
+ up. If you don't wait a jiffie after the final check, you will
+ screw it up. If you screw it up by these manners or by abandoning
+ an operation in progress, the I2C bus is likely stuck and won't
+ work any more. Gotta love this hardware. */
+
#include <linux/config.h>
#include <linux/module.h>
#include <linux/pci.h>
@@ -79,7 +87,8 @@
#define SMBHSTCFG_I2C_EN 4
/* Other settings */
-#define MAX_TIMEOUT 100
+#define MAX_TIMEOUT_US 100000
+#define RETRY_TIME_US 500 /* Retry minimum is 500us */
#define ENABLE_INT9 0 /* set to 0x01 to enable - untested */
/* I801 command constants */
@@ -105,21 +114,31 @@
"Forcibly enable the I801 at the given address. "
"EXTREMELY DANGEROUS!");
-static int i801_transaction(void);
-static int i801_block_transaction(union i2c_smbus_data *data,
- char read_write, int command);
-
static unsigned short i801_smba;
static struct pci_dev *I801_dev;
static int isich4;
+struct i801_i2c_data
+{
+ int i;
+ int len;
+ unsigned char hostc;
+ int block;
+ int hwpec;
+ int xact;
+ int hststs;
+ int wait_intr;
+ int finished;
+};
+struct i801_i2c_data i801_data;
+
static int i801_setup(struct pci_dev *dev)
{
int error_return = 0;
unsigned char temp;
/* Note: we keep on searching until we have found 'function 3' */
- if(PCI_FUNC(dev->devfn) != 3)
+ if (PCI_FUNC(dev->devfn) != 3)
return -ENODEV;
I801_dev = dev;
@@ -136,7 +155,7 @@
} else {
pci_read_config_word(I801_dev, SMBBA, &i801_smba);
i801_smba &= 0xfff0;
- if(i801_smba = 0) {
+ if (i801_smba = 0) {
dev_err(&dev->dev, "SMB base address uninitialized"
"- upgrade BIOS or use force_addr=0xaddr\n");
return -ENODEV;
@@ -180,12 +199,93 @@
return error_return;
}
-static int i801_transaction(void)
+static void i801_check_hststs(struct i2c_adapter *adap,
+ struct i2c_op_q_entry *entry,
+ struct i801_i2c_data *d)
+{
+ if (d->hststs & 0x10) {
+ entry->result = -EIO;
+ dev_dbg(&I801_dev->dev,
+ "Error: Failed bus transaction\n");
+ } else if (d->hststs & 0x08) {
+ entry->result = -EIO;
+ dev_err(&I801_dev->dev, "Bus collision!\n");
+ /* Clock stops and slave is stuck in mid-transmission */
+ } else if (d->hststs & 0x04) {
+ entry->result = -EIO;
+ dev_dbg(&I801_dev->dev, "Error: no response!\n");
+ }
+}
+
+static void i801_finish(struct i2c_adapter *adap,
+ struct i2c_op_q_entry *entry,
+ struct i801_i2c_data *d)
+{
+ d->finished = 1;
+
+#ifdef HAVE_PEC
+ if (isich4 && d->hwpec) {
+ if (entry->smbus.size != I2C_SMBUS_QUICK &&
+ entry->smbus.size != I2C_SMBUS_I2C_BLOCK_DATA)
+ outb_p(0, SMBAUXCTL);
+ }
+#endif
+
+ if (d->block || (entry->result < 0) ||
+ ((entry->smbus.read_write = I2C_SMBUS_WRITE)
+ || (d->xact = I801_QUICK)))
+ return;
+
+ switch (d->xact & 0x7f) {
+ case I801_BYTE: /* Result put in SMBHSTDAT0 */
+ case I801_BYTE_DATA:
+ entry->smbus.data->byte = inb_p(SMBHSTDAT0);
+ break;
+ case I801_WORD_DATA:
+ entry->smbus.data->word = inb_p(SMBHSTDAT0)
+ + (inb_p(SMBHSTDAT1) << 8);
+ break;
+ }
+}
+
+static void i801_transaction_final_check(struct i2c_adapter *adap,
+ struct i2c_op_q_entry *entry,
+ struct i801_i2c_data *d)
+{
+ i801_check_hststs(adap, entry, d);
+
+ if ((inb_p(SMBHSTSTS) & 0x1f) != 0x00)
+ outb_p(inb(SMBHSTSTS), SMBHSTSTS);
+
+ if ((d->hststs = (0x1f & inb_p(SMBHSTSTS))) != 0x00) {
+ dev_dbg(&I801_dev->dev, "Failed reset at end of transaction"
+ "(%02x)\n", d->hststs);
+ }
+ dev_dbg(&I801_dev->dev, "Transaction (post): CNT=%02x, CMD=%02x, "
+ "ADD=%02x, DAT0=%02x, DAT1=%02x\n", inb_p(SMBHSTCNT),
+ inb_p(SMBHSTCMD), inb_p(SMBHSTADD), inb_p(SMBHSTDAT0),
+ inb_p(SMBHSTDAT1));
+}
+
+static void i801_transaction_poll(struct i2c_adapter *adap,
+ struct i2c_op_q_entry *entry,
+ struct i801_i2c_data *d)
{
- int temp;
- int result = 0;
- int timeout = 0;
+ d->hststs = inb_p(SMBHSTSTS);
+ if (!(d->hststs & 0x01)) {
+ i801_transaction_final_check(adap, entry, d);
+ i801_finish(adap, entry, d);
+ } else if (entry->time_left <= 0) {
+ dev_dbg(&I801_dev->dev, "SMBus Timeout!\n");
+ entry->result = -EIO;
+ i801_transaction_final_check(adap, entry, d);
+ }
+}
+static void i801_transaction_start(struct i2c_adapter *adap,
+ struct i2c_op_q_entry *entry,
+ struct i801_i2c_data *d)
+{
dev_dbg(&I801_dev->dev, "Transaction (pre): CNT=%02x, CMD=%02x,"
"ADD=%02x, DAT0=%02x, DAT1=%02x\n", inb_p(SMBHSTCNT),
inb_p(SMBHSTCMD), inb_p(SMBHSTADD), inb_p(SMBHSTDAT0),
@@ -193,331 +293,362 @@
/* Make sure the SMBus host is ready to start transmitting */
/* 0x1f = Failed, Bus_Err, Dev_Err, Intr, Host_Busy */
- if ((temp = (0x1f & inb_p(SMBHSTSTS))) != 0x00) {
+ if ((d->hststs = (0x1f & inb_p(SMBHSTSTS))) != 0x00) {
dev_dbg(&I801_dev->dev, "SMBus busy (%02x). Resetting... \n",
- temp);
- outb_p(temp, SMBHSTSTS);
- if ((temp = (0x1f & inb_p(SMBHSTSTS))) != 0x00) {
- dev_dbg(&I801_dev->dev, "Failed! (%02x)\n", temp);
- return -1;
+ d->hststs);
+ outb_p(d->hststs, SMBHSTSTS);
+ if ((d->hststs = (0x1f & inb_p(SMBHSTSTS))) != 0x00) {
+ dev_dbg(&I801_dev->dev, "Failed! (%02x)\n", d->hststs);
+ entry->result = -EIO;
+ return;
} else {
dev_dbg(&I801_dev->dev, "Successfull!\n");
}
}
outb_p(inb(SMBHSTCNT) | I801_START, SMBHSTCNT);
+}
- /* We will always wait for a fraction of a second! */
- do {
- msleep(1);
- temp = inb_p(SMBHSTSTS);
- } while ((temp & 0x01) && (timeout++ < MAX_TIMEOUT));
-
- /* If the SMBus is still busy, we give up */
- if (timeout >= MAX_TIMEOUT) {
- dev_dbg(&I801_dev->dev, "SMBus Timeout!\n");
- result = -1;
+static void i801_block_finish(struct i2c_adapter *adap,
+ struct i2c_op_q_entry *entry,
+ struct i801_i2c_data *d)
+{
+ if (entry->smbus.size = I2C_SMBUS_I2C_BLOCK_DATA) {
+ /* restore saved configuration register value */
+ pci_write_config_byte(I801_dev, SMBHSTCFG, d->hostc);
}
- if (temp & 0x10) {
- result = -1;
- dev_dbg(&I801_dev->dev, "Error: Failed bus transaction\n");
- }
+ i801_finish(adap, entry, d);
+}
- if (temp & 0x08) {
- result = -1;
- dev_err(&I801_dev->dev, "Bus collision! SMBus may be locked "
- "until next hard reset. (sorry!)\n");
- /* Clock stops and slave is stuck in mid-transmission */
- }
+static void i801_block_poll_wait_intr(struct i2c_adapter *adap,
+ struct i2c_op_q_entry *entry,
+ struct i801_i2c_data *d)
+{
+#ifdef HAVE_PEC
+ if (entry->result >= 0 &&
+ isich4 &&
+ entry->smbus.size = I2C_SMBUS_BLOCK_DATA_PEC)
+ {
+ /* wait for INTR bit as advised by Intel */
+ d->hststs = inb_p(SMBHSTSTS);
+ if (d->hststs & 0x02) {
+ outb_p(d->hststs, SMBHSTSTS);
+ i801_block_finish(adap, entry, d);
+ } else if (entry->time_left <= 0) {
+ /* Timed out */
+ outb_p(d->hststs, SMBHSTSTS);
+ entry->result = -EIO;
+ dev_dbg(&I801_dev->dev, "PEC Timeout!\n");
+ }
+ } else
+#endif
+ i801_block_finish(adap, entry, d);
+}
- if (temp & 0x04) {
- result = -1;
- dev_dbg(&I801_dev->dev, "Error: no response!\n");
+static void i801_block_next_byte(struct i2c_adapter *adap,
+ struct i2c_op_q_entry *entry,
+ struct i801_i2c_data *d)
+{
+ int smbcmd;
+ unsigned char errmask;
+
+ if (d->i > d->len) {
+ d->wait_intr = 1;
+ entry->time_left = MAX_TIMEOUT_US;
+ i801_block_poll_wait_intr(adap, entry, d);
+ return;
}
- if ((inb_p(SMBHSTSTS) & 0x1f) != 0x00)
- outb_p(inb(SMBHSTSTS), SMBHSTSTS);
+ if (d->i = d->len && entry->smbus.read_write = I2C_SMBUS_READ)
+ smbcmd = I801_BLOCK_LAST;
+ else
+ smbcmd = I801_BLOCK_DATA;
+ outb_p(smbcmd | ENABLE_INT9, SMBHSTCNT);
- if ((temp = (0x1f & inb_p(SMBHSTSTS))) != 0x00) {
- dev_dbg(&I801_dev->dev, "Failed reset at end of transaction"
- "(%02x)\n", temp);
+ dev_dbg(&I801_dev->dev, "Block (pre %d): CNT=%02x, CMD=%02x, "
+ "ADD=%02x, DAT0=%02x, BLKDAT=%02x\n", d->i,
+ inb_p(SMBHSTCNT), inb_p(SMBHSTCMD), inb_p(SMBHSTADD),
+ inb_p(SMBHSTDAT0), inb_p(SMBBLKDAT));
+
+ /* Make sure the SMBus host is ready to start transmitting */
+ d->hststs = inb_p(SMBHSTSTS);
+ if (d->i = 1) {
+ /* Erronenous conditions before transaction:
+ * Byte_Done, Failed, Bus_Err, Dev_Err, Intr, Host_Busy */
+ errmask=0x9f;
+ } else {
+ /* Erronenous conditions during transaction:
+ * Failed, Bus_Err, Dev_Err, Intr */
+ errmask=0x1e;
+ }
+ if (d->hststs & errmask) {
+ dev_dbg(&I801_dev->dev, "SMBus busy (%02x). "
+ "Resetting... \n", d->hststs);
+ outb_p(d->hststs, SMBHSTSTS);
+ if (((d->hststs = inb_p(SMBHSTSTS)) & errmask) != 0x00) {
+ dev_err(&I801_dev->dev,
+ "Reset failed! (%02x)\n", d->hststs);
+ entry->result = -EIO;
+ return;
+ }
+ if (d->i != 1) {
+ /* if die in middle of block transaction, fail */
+ entry->result = -EIO;
+ return;
+ }
}
- dev_dbg(&I801_dev->dev, "Transaction (post): CNT=%02x, CMD=%02x, "
- "ADD=%02x, DAT0=%02x, DAT1=%02x\n", inb_p(SMBHSTCNT),
- inb_p(SMBHSTCMD), inb_p(SMBHSTADD), inb_p(SMBHSTDAT0),
- inb_p(SMBHSTDAT1));
- return result;
+
+ if (d->i = 1)
+ outb_p(inb(SMBHSTCNT) | I801_START, SMBHSTCNT);
}
-/* All-inclusive block transaction function */
-static int i801_block_transaction(union i2c_smbus_data *data, char read_write,
- int command)
+/* Called on timer ticks. This checks the result of the
+ transaction. */
+static void i801_block_poll(struct i2c_adapter *adap,
+ struct i2c_op_q_entry *entry,
+ struct i801_i2c_data *d)
{
- int i, len;
- int smbcmd;
- int temp;
- int result = 0;
- int timeout;
- unsigned char hostc, errmask;
-
- if (command = I2C_SMBUS_I2C_BLOCK_DATA) {
- if (read_write = I2C_SMBUS_WRITE) {
- /* set I2C_EN bit in configuration register */
- pci_read_config_byte(I801_dev, SMBHSTCFG, &hostc);
- pci_write_config_byte(I801_dev, SMBHSTCFG,
- hostc | SMBHSTCFG_I2C_EN);
- } else {
- dev_err(&I801_dev->dev,
- "I2C_SMBUS_I2C_BLOCK_READ not DB!\n");
- return -1;
+ d->hststs = inb_p(SMBHSTSTS);
+ if (!(d->hststs & 0x80)) {
+ /* Not ready yet */
+ if (entry->time_left <= 0) {
+ dev_dbg(&I801_dev->dev, "SMBus Timeout!\n");
+ entry->result = -EIO;
}
+ return;
}
- if (read_write = I2C_SMBUS_WRITE) {
- len = data->block[0];
- if (len < 1)
- len = 1;
- if (len > 32)
- len = 32;
- outb_p(len, SMBHSTDAT0);
- outb_p(data->block[1], SMBBLKDAT);
+ i801_check_hststs(adap, entry, d);
+ if (d->i = 1 && entry->smbus.read_write = I2C_SMBUS_READ) {
+ d->len = inb_p(SMBHSTDAT0);
+ if (d->len < 1)
+ d->len = 1;
+ if (d->len > 32)
+ d->len = 32;
+ entry->smbus.data->block[0] = d->len;
+ }
+
+ /* Retrieve/store value in SMBBLKDAT */
+ if (entry->smbus.read_write = I2C_SMBUS_READ)
+ entry->smbus.data->block[d->i] = inb_p(SMBBLKDAT);
+ if (entry->smbus.read_write = I2C_SMBUS_WRITE && d->i+1 <= d->len)
+ outb_p(entry->smbus.data->block[d->i+1], SMBBLKDAT);
+ if ((d->hststs & 0x9e) != 0x00)
+ outb_p(d->hststs, SMBHSTSTS); /* signals SMBBLKDAT ready */
+
+ if ((d->hststs = (0x1e & inb_p(SMBHSTSTS))) != 0x00) {
+ dev_dbg(&I801_dev->dev,
+ "Bad status (%02x) at end of transaction\n",
+ d->hststs);
+ }
+ dev_dbg(&I801_dev->dev, "Block (post %d): CNT=%02x, CMD=%02x, "
+ "ADD=%02x, DAT0=%02x, BLKDAT=%02x\n", d->i,
+ inb_p(SMBHSTCNT), inb_p(SMBHSTCMD), inb_p(SMBHSTADD),
+ inb_p(SMBHSTDAT0), inb_p(SMBBLKDAT));
+
+ if (entry->result)
+ return;
+
+ (d->i)++;
+ i801_block_next_byte(adap, entry, d);
+}
+
+static void i801_block_start(struct i2c_adapter *adap,
+ struct i2c_op_q_entry *entry,
+ struct i801_i2c_data *d)
+{
+ if (entry->smbus.read_write = I2C_SMBUS_WRITE) {
+ d->len = entry->smbus.data->block[0];
+ if (d->len < 1)
+ d->len = 1;
+ if (d->len > 32)
+ d->len = 32;
+ outb_p(d->len, SMBHSTDAT0);
+ outb_p(entry->smbus.data->block[1], SMBBLKDAT);
} else {
- len = 32; /* max for reads */
+ d->len = 32; /* max for reads */
}
- if(isich4 && command != I2C_SMBUS_I2C_BLOCK_DATA) {
+ if(isich4 && entry->smbus.size != I2C_SMBUS_I2C_BLOCK_DATA) {
/* set 32 byte buffer */
}
- for (i = 1; i <= len; i++) {
- if (i = len && read_write = I2C_SMBUS_READ)
- smbcmd = I801_BLOCK_LAST;
- else
- smbcmd = I801_BLOCK_DATA;
- outb_p(smbcmd | ENABLE_INT9, SMBHSTCNT);
-
- dev_dbg(&I801_dev->dev, "Block (pre %d): CNT=%02x, CMD=%02x, "
- "ADD=%02x, DAT0=%02x, BLKDAT=%02x\n", i,
- inb_p(SMBHSTCNT), inb_p(SMBHSTCMD), inb_p(SMBHSTADD),
- inb_p(SMBHSTDAT0), inb_p(SMBBLKDAT));
-
- /* Make sure the SMBus host is ready to start transmitting */
- temp = inb_p(SMBHSTSTS);
- if (i = 1) {
- /* Erronenous conditions before transaction:
- * Byte_Done, Failed, Bus_Err, Dev_Err, Intr, Host_Busy */
- errmask=0x9f;
- } else {
- /* Erronenous conditions during transaction:
- * Failed, Bus_Err, Dev_Err, Intr */
- errmask=0x1e;
- }
- if (temp & errmask) {
- dev_dbg(&I801_dev->dev, "SMBus busy (%02x). "
- "Resetting... \n", temp);
- outb_p(temp, SMBHSTSTS);
- if (((temp = inb_p(SMBHSTSTS)) & errmask) != 0x00) {
- dev_err(&I801_dev->dev,
- "Reset failed! (%02x)\n", temp);
- result = -1;
- goto END;
- }
- if (i != 1) {
- /* if die in middle of block transaction, fail */
- result = -1;
- goto END;
- }
- }
+ d->i = 1;
+ i801_block_next_byte(adap, entry, d);
+}
- if (i = 1)
- outb_p(inb(SMBHSTCNT) | I801_START, SMBHSTCNT);
+/* General poll routine. Called periodically by the i2c code. */
+static void i801_poll(struct i2c_adapter *adap,
+ struct i2c_op_q_entry *entry,
+ unsigned int us_since_last_poll)
+{
+ struct i801_i2c_data *d = entry->data;
- /* We will always wait for a fraction of a second! */
- timeout = 0;
- do {
- temp = inb_p(SMBHSTSTS);
- msleep(1);
- }
- while ((!(temp & 0x80))
- && (timeout++ < MAX_TIMEOUT));
+ dev_dbg(&I801_dev->dev, "Poll call for %p %p at %ld\n", adap, entry,
+ jiffies);
- /* If the SMBus is still busy, we give up */
- if (timeout >= MAX_TIMEOUT) {
- result = -1;
- dev_dbg(&I801_dev->dev, "SMBus Timeout!\n");
- }
+ if (!d)
+ /* The entry hasn't been started yet. */
+ return;
- if (temp & 0x10) {
- result = -1;
- dev_dbg(&I801_dev->dev,
- "Error: Failed bus transaction\n");
- } else if (temp & 0x08) {
- result = -1;
- dev_err(&I801_dev->dev, "Bus collision!\n");
- } else if (temp & 0x04) {
- result = -1;
- dev_dbg(&I801_dev->dev, "Error: no response!\n");
- }
+ if (d->finished) {
+ /* We delay an extra poll to keep the hardware happy.
+ Otherwise the hardware is not ready when we start
+ the next operation. */
+ i2c_op_done(adap, entry);
+ return;
+ }
- if (i = 1 && read_write = I2C_SMBUS_READ) {
- len = inb_p(SMBHSTDAT0);
- if (len < 1)
- len = 1;
- if (len > 32)
- len = 32;
- data->block[0] = len;
- }
+ /* Decrement timeout */
+ entry->time_left -= us_since_last_poll;
- /* Retrieve/store value in SMBBLKDAT */
- if (read_write = I2C_SMBUS_READ)
- data->block[i] = inb_p(SMBBLKDAT);
- if (read_write = I2C_SMBUS_WRITE && i+1 <= len)
- outb_p(data->block[i+1], SMBBLKDAT);
- if ((temp & 0x9e) != 0x00)
- outb_p(temp, SMBHSTSTS); /* signals SMBBLKDAT ready */
-
- if ((temp = (0x1e & inb_p(SMBHSTSTS))) != 0x00) {
- dev_dbg(&I801_dev->dev,
- "Bad status (%02x) at end of transaction\n",
- temp);
- }
- dev_dbg(&I801_dev->dev, "Block (post %d): CNT=%02x, CMD=%02x, "
- "ADD=%02x, DAT0=%02x, BLKDAT=%02x\n", i,
- inb_p(SMBHSTCNT), inb_p(SMBHSTCMD), inb_p(SMBHSTADD),
- inb_p(SMBHSTDAT0), inb_p(SMBBLKDAT));
+ /* Wait a jiffie normally. */
+ entry->call_again_us = RETRY_TIME_US;
- if (result < 0)
- goto END;
+ if (d->block) {
+ if (d->wait_intr) {
+ i801_block_poll_wait_intr(adap, entry, d);
+ } else {
+ i801_block_poll(adap, entry, d);
+ }
+ if (entry->result < 0)
+ /* Error, finish the transaction */
+ i801_block_finish(adap, entry, d);
+ } else {
+ i801_transaction_poll(adap, entry, d);
+ if (entry->result < 0)
+ /* Error, finish the transaction */
+ i801_finish(adap, entry, d);
}
+}
-#ifdef HAVE_PEC
- if(isich4 && command = I2C_SMBUS_BLOCK_DATA_PEC) {
- /* wait for INTR bit as advised by Intel */
- timeout = 0;
- do {
- temp = inb_p(SMBHSTSTS);
- msleep(1);
- } while ((!(temp & 0x02))
- && (timeout++ < MAX_TIMEOUT));
+/* Start a general SMBUS transaction on the i801. Figure out what
+ kind of transaction it is, set it up, and start it. */
+static void i801_start(struct i2c_adapter *adap,
+ struct i2c_op_q_entry *entry)
+{
+ struct i801_i2c_data *d = adap->algo_data;
- if (timeout >= MAX_TIMEOUT) {
- dev_dbg(&I801_dev->dev, "PEC Timeout!\n");
+ dev_dbg(&I801_dev->dev, "start call for %p %p at %ld\n", adap, entry,
+ jiffies);
+
+ if (entry->smbus.size = I2C_SMBUS_I2C_BLOCK_DATA) {
+ if (entry->smbus.read_write = I2C_SMBUS_WRITE) {
+ /* set I2C_EN bit in configuration register */
+ pci_read_config_byte(I801_dev, SMBHSTCFG, &d->hostc);
+ pci_write_config_byte(I801_dev, SMBHSTCFG,
+ d->hostc | SMBHSTCFG_I2C_EN);
+ } else {
+ dev_err(&I801_dev->dev,
+ "I2C_SMBUS_I2C_BLOCK_READ not DB!\n");
+ entry->result = -EINVAL;
+ return;
}
- outb_p(temp, SMBHSTSTS);
- }
-#endif
- result = 0;
-END:
- if (command = I2C_SMBUS_I2C_BLOCK_DATA) {
- /* restore saved configuration register value */
- pci_write_config_byte(I801_dev, SMBHSTCFG, hostc);
}
- return result;
-}
-/* Return -1 on error. */
-static s32 i801_access(struct i2c_adapter * adap, u16 addr,
- unsigned short flags, char read_write, u8 command,
- int size, union i2c_smbus_data * data)
-{
- int hwpec = 0;
- int block = 0;
- int ret, xact = 0;
+ d->block = 0;
+ d->hwpec = 0;
+ d->xact = 0;
+ d->wait_intr = 0;
+ d->finished = 0;
#ifdef HAVE_PEC
- if(isich4)
- hwpec = (flags & I2C_CLIENT_PEC) != 0;
+ if (isich4)
+ d->hwpec = (entry->smbus.flags & I2C_CLIENT_PEC) != 0;
#endif
- switch (size) {
+ switch (entry->smbus.size) {
case I2C_SMBUS_QUICK:
- outb_p(((addr & 0x7f) << 1) | (read_write & 0x01),
+ outb_p(((entry->smbus.addr & 0x7f) << 1)
+ | (entry->smbus.read_write & 0x01),
SMBHSTADD);
- xact = I801_QUICK;
+ d->xact = I801_QUICK;
break;
case I2C_SMBUS_BYTE:
- outb_p(((addr & 0x7f) << 1) | (read_write & 0x01),
+ outb_p(((entry->smbus.addr & 0x7f) << 1)
+ | (entry->smbus.read_write & 0x01),
SMBHSTADD);
- if (read_write = I2C_SMBUS_WRITE)
- outb_p(command, SMBHSTCMD);
- xact = I801_BYTE;
+ if (entry->smbus.read_write = I2C_SMBUS_WRITE)
+ outb_p(entry->smbus.command, SMBHSTCMD);
+ d->xact = I801_BYTE;
break;
case I2C_SMBUS_BYTE_DATA:
- outb_p(((addr & 0x7f) << 1) | (read_write & 0x01),
+ outb_p(((entry->smbus.addr & 0x7f) << 1)
+ | (entry->smbus.read_write & 0x01),
SMBHSTADD);
- outb_p(command, SMBHSTCMD);
- if (read_write = I2C_SMBUS_WRITE)
- outb_p(data->byte, SMBHSTDAT0);
- xact = I801_BYTE_DATA;
+ outb_p(entry->smbus.command, SMBHSTCMD);
+ if (entry->smbus.read_write = I2C_SMBUS_WRITE)
+ outb_p(entry->smbus.data->byte, SMBHSTDAT0);
+ d->xact = I801_BYTE_DATA;
break;
case I2C_SMBUS_WORD_DATA:
- outb_p(((addr & 0x7f) << 1) | (read_write & 0x01),
+ outb_p(((entry->smbus.addr & 0x7f) << 1)
+ | (entry->smbus.read_write & 0x01),
SMBHSTADD);
- outb_p(command, SMBHSTCMD);
- if (read_write = I2C_SMBUS_WRITE) {
- outb_p(data->word & 0xff, SMBHSTDAT0);
- outb_p((data->word & 0xff00) >> 8, SMBHSTDAT1);
+ outb_p(entry->smbus.command, SMBHSTCMD);
+ if (entry->smbus.read_write = I2C_SMBUS_WRITE) {
+ outb_p(entry->smbus.data->word & 0xff, SMBHSTDAT0);
+ outb_p((entry->smbus.data->word & 0xff00) >> 8,
+ SMBHSTDAT1);
}
- xact = I801_WORD_DATA;
+ d->xact = I801_WORD_DATA;
break;
case I2C_SMBUS_BLOCK_DATA:
case I2C_SMBUS_I2C_BLOCK_DATA:
#ifdef HAVE_PEC
case I2C_SMBUS_BLOCK_DATA_PEC:
- if(hwpec && size = I2C_SMBUS_BLOCK_DATA)
- size = I2C_SMBUS_BLOCK_DATA_PEC;
+ if (d->hwpec && entry->smbus.size = I2C_SMBUS_BLOCK_DATA)
+ entry->smbus.size = I2C_SMBUS_BLOCK_DATA_PEC;
#endif
- outb_p(((addr & 0x7f) << 1) | (read_write & 0x01),
+ outb_p(((entry->smbus.addr & 0x7f) << 1)
+ | (entry->smbus.read_write & 0x01),
SMBHSTADD);
- outb_p(command, SMBHSTCMD);
- block = 1;
+ outb_p(entry->smbus.command, SMBHSTCMD);
+ d->block = 1;
break;
case I2C_SMBUS_PROC_CALL:
default:
- dev_err(&I801_dev->dev, "Unsupported transaction %d\n", size);
- return -1;
+ dev_err(&I801_dev->dev, "Unsupported transaction %d\n",
+ entry->smbus.size);
+ entry->result = -EINVAL;
+ return;
}
#ifdef HAVE_PEC
- if(isich4 && hwpec) {
- if(size != I2C_SMBUS_QUICK &&
- size != I2C_SMBUS_I2C_BLOCK_DATA)
+ if (isich4 && d->hwpec) {
+ if (entry->smbus.size != I2C_SMBUS_QUICK &&
+ entry->smbus.size != I2C_SMBUS_I2C_BLOCK_DATA)
outb_p(1, SMBAUXCTL); /* enable HW PEC */
}
#endif
- if(block)
- ret = i801_block_transaction(data, read_write, size);
- else {
- outb_p(xact | ENABLE_INT9, SMBHSTCNT);
- ret = i801_transaction();
+ if (d->block) {
+ i801_block_start(adap, entry, d);
+ if (entry->result < 0)
+ /* Error, finish the transaction */
+ i801_block_finish(adap, entry, d);
+ } else {
+ outb_p(d->xact | ENABLE_INT9, SMBHSTCNT);
+ i801_transaction_start(adap, entry, d);
+ if (entry->result < 0)
+ /* Error, finish the transaction */
+ i801_finish(adap, entry, d);
}
-#ifdef HAVE_PEC
- if(isich4 && hwpec) {
- if(size != I2C_SMBUS_QUICK &&
- size != I2C_SMBUS_I2C_BLOCK_DATA)
- outb_p(0, SMBAUXCTL);
- }
+ /* Wait extra long here, we want at least 2 ticks to guarantee
+ we wait >= 1 tick. But we want to wait at least 100us no
+ matter what. */
+#if (1000000 / HZ) < 100
+ entry->call_again_us = 200;
+#else
+ entry->call_again_us = (1000000 / HZ) * 2;
#endif
+ entry->time_left = MAX_TIMEOUT_US;
- if(block)
- return ret;
- if(ret)
- return -1;
- if ((read_write = I2C_SMBUS_WRITE) || (xact = I801_QUICK))
- return 0;
-
- switch (xact & 0x7f) {
- case I801_BYTE: /* Result put in SMBHSTDAT0 */
- case I801_BYTE_DATA:
- data->byte = inb_p(SMBHSTDAT0);
- break;
- case I801_WORD_DATA:
- data->word = inb_p(SMBHSTDAT0) + (inb_p(SMBHSTDAT1) << 8);
- break;
- }
- return 0;
+ if (d->finished)
+ i2c_op_done(adap, entry);
+ else
+ entry->data = d;
}
@@ -537,7 +668,8 @@
static struct i2c_algorithm smbus_algorithm = {
.name = "Non-I2C SMBus adapter",
.id = I2C_ALGO_SMBUS,
- .smbus_xfer = i801_access,
+ .smbus_start = i801_start,
+ .poll = i801_poll,
.functionality = i801_func,
};
@@ -545,6 +677,7 @@
.owner = THIS_MODULE,
.class = I2C_CLASS_HWMON,
.algo = &smbus_algorithm,
+ .algo_data = &i801_data,
.name = "unset",
};
@@ -563,7 +696,8 @@
MODULE_DEVICE_TABLE (pci, i801_ids);
-static int __devinit i801_probe(struct pci_dev *dev, const struct pci_device_id *id)
+static int __devinit i801_probe(struct pci_dev *dev,
+ const struct pci_device_id *id)
{
if (i801_setup(dev)) {
^ permalink raw reply [flat|nested] 18+ messages in thread
* [PATCH] Add a non-blocking interface to the I2C code, part 6
2005-05-19 6:25 ` Corey Minyard
@ 2005-05-19 6:25 ` Corey Minyard
-1 siblings, 0 replies; 18+ messages in thread
From: Corey Minyard @ 2005-03-02 20:04 UTC (permalink / raw)
To: Greg KH, lkml, Sensors
[-- Attachment #1: Type: text/plain, Size: 45 bytes --]
See part 1 for details on what this does...
[-- Attachment #2: i2c_add_read_block.diff --]
[-- Type: text/plain, Size: 1881 bytes --]
This adds back in the i2c_smbus_read_block_data() function
which is needed by the IPMI SMB driver (coming soon).
Signed-off-by: Corey Minyard <minyard@acm.org>
Index: linux-2.6.11-rc5-mm1/drivers/i2c/i2c-core.c
===================================================================
--- linux-2.6.11-rc5-mm1.orig/drivers/i2c/i2c-core.c
+++ linux-2.6.11-rc5-mm1/drivers/i2c/i2c-core.c
@@ -1250,6 +1250,23 @@
}
/* Returns the number of read bytes */
+s32 i2c_smbus_read_block_data(struct i2c_client *client, u8 command,
+ u8 *values)
+{
+ union i2c_smbus_data data;
+ int i;
+ if (i2c_smbus_xfer(client->adapter,client->addr,client->flags,
+ I2C_SMBUS_READ,command,
+ I2C_SMBUS_BLOCK_DATA,&data))
+ return -1;
+ else {
+ for (i = 1; i <= data.block[0]; i++)
+ values[i-1] = data.block[i];
+ return data.block[0];
+ }
+}
+
+/* Returns the number of read bytes */
s32 i2c_smbus_read_i2c_block_data(struct i2c_client *client, u8 command, u8 *values)
{
union i2c_smbus_data data;
@@ -1731,6 +1748,7 @@
EXPORT_SYMBOL(i2c_smbus_read_word_data);
EXPORT_SYMBOL(i2c_smbus_write_word_data);
EXPORT_SYMBOL(i2c_smbus_write_block_data);
+EXPORT_SYMBOL(i2c_smbus_read_block_data);
EXPORT_SYMBOL(i2c_smbus_read_i2c_block_data);
EXPORT_SYMBOL(i2c_non_blocking_capable);
Index: linux-2.6.11-rc5-mm1/include/linux/i2c.h
===================================================================
--- linux-2.6.11-rc5-mm1.orig/include/linux/i2c.h
+++ linux-2.6.11-rc5-mm1/include/linux/i2c.h
@@ -99,6 +99,8 @@
extern s32 i2c_smbus_write_block_data(struct i2c_client * client,
u8 command, u8 length,
u8 *values);
+extern s32 i2c_smbus_read_block_data(struct i2c_client * client,
+ u8 command, u8 *values);
extern s32 i2c_smbus_read_i2c_block_data(struct i2c_client * client,
u8 command, u8 *values);
^ permalink raw reply [flat|nested] 18+ messages in thread* [PATCH] Add a non-blocking interface to the I2C code, part 6
@ 2005-05-19 6:25 ` Corey Minyard
0 siblings, 0 replies; 18+ messages in thread
From: Corey Minyard @ 2005-05-19 6:25 UTC (permalink / raw)
To: Greg KH, lkml, Sensors
See part 1 for details on what this does...
-------------- next part --------------
This adds back in the i2c_smbus_read_block_data() function
which is needed by the IPMI SMB driver (coming soon).
Signed-off-by: Corey Minyard <minyard@acm.org>
Index: linux-2.6.11-rc5-mm1/drivers/i2c/i2c-core.c
=================================--- linux-2.6.11-rc5-mm1.orig/drivers/i2c/i2c-core.c
+++ linux-2.6.11-rc5-mm1/drivers/i2c/i2c-core.c
@@ -1250,6 +1250,23 @@
}
/* Returns the number of read bytes */
+s32 i2c_smbus_read_block_data(struct i2c_client *client, u8 command,
+ u8 *values)
+{
+ union i2c_smbus_data data;
+ int i;
+ if (i2c_smbus_xfer(client->adapter,client->addr,client->flags,
+ I2C_SMBUS_READ,command,
+ I2C_SMBUS_BLOCK_DATA,&data))
+ return -1;
+ else {
+ for (i = 1; i <= data.block[0]; i++)
+ values[i-1] = data.block[i];
+ return data.block[0];
+ }
+}
+
+/* Returns the number of read bytes */
s32 i2c_smbus_read_i2c_block_data(struct i2c_client *client, u8 command, u8 *values)
{
union i2c_smbus_data data;
@@ -1731,6 +1748,7 @@
EXPORT_SYMBOL(i2c_smbus_read_word_data);
EXPORT_SYMBOL(i2c_smbus_write_word_data);
EXPORT_SYMBOL(i2c_smbus_write_block_data);
+EXPORT_SYMBOL(i2c_smbus_read_block_data);
EXPORT_SYMBOL(i2c_smbus_read_i2c_block_data);
EXPORT_SYMBOL(i2c_non_blocking_capable);
Index: linux-2.6.11-rc5-mm1/include/linux/i2c.h
=================================--- linux-2.6.11-rc5-mm1.orig/include/linux/i2c.h
+++ linux-2.6.11-rc5-mm1/include/linux/i2c.h
@@ -99,6 +99,8 @@
extern s32 i2c_smbus_write_block_data(struct i2c_client * client,
u8 command, u8 length,
u8 *values);
+extern s32 i2c_smbus_read_block_data(struct i2c_client * client,
+ u8 command, u8 *values);
extern s32 i2c_smbus_read_i2c_block_data(struct i2c_client * client,
u8 command, u8 *values);
^ permalink raw reply [flat|nested] 18+ messages in thread
* [PATCH] Add a non-blocking interface to the I2C code, part 5
2005-05-19 6:25 ` Corey Minyard
@ 2005-05-19 6:25 ` Corey Minyard
-1 siblings, 0 replies; 18+ messages in thread
From: Corey Minyard @ 2005-03-02 20:02 UTC (permalink / raw)
To: Greg KH, lkml, Sensors
[-- Attachment #1: Type: text/plain, Size: 45 bytes --]
See part 1 for details on what this does...
[-- Attachment #2: i2c_add_non_blocking.diff --]
[-- Type: text/plain, Size: 9218 bytes --]
This patch adds a non-blocking interface to the I2C code. This
is needed by some RTC drivers on the I2C bus and is needed by
the IPMI code so it can do things at panic time.
The non-blocking interface requires changes to the driver
below it. The current drivers will work as-is, but will not
be able to use the non-blocking interfaces.
Signed-off-by: Corey Minyard <minyard@acm.org>
Index: linux-2.6.11-rc5-mm1/drivers/i2c/i2c-core.c
===================================================================
--- linux-2.6.11-rc5-mm1.orig/drivers/i2c/i2c-core.c
+++ linux-2.6.11-rc5-mm1/drivers/i2c/i2c-core.c
@@ -680,7 +680,8 @@
return;
if (sequence_match) {
- /* Poll will go here. */
+ /* This is the one we expected, call the poll routine. */
+ adap->algo->poll(adap, entry, t->next_call_time);
if (!entry_completed(entry))
i2c_start_timer(adap, entry);
@@ -697,13 +698,102 @@
* ----------------------------------------------------
*/
+/* 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) && entry->use_timer)
+ i2c_start_timer(adap, entry);
+}
+
+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;
+ atomic_set(&entry->completed, 1);
+ entry->result = 0;
+ entry->use_timer = 0; /* We poll it directly. */
+ entry->data = NULL;
+ kref_init(&entry->usecount);
+ 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;
+ atomic_set(&entry->completed, 0);
+ list_del(&entry->link);
+ }
+ spin_unlock_irqrestore(&adap->q_lock, flags);
+ }
+
+ /* Once the operation is started, we will not
+ interrupt it. */
+ while (!entry_completed(entry)) {
+ 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(entry))
+ break;
+ algo->poll(adap, entry, timeout * USEC_PER_JIFFIE);
+ }
+}
+
static void i2c_transfer_entry(struct i2c_adapter * adap,
struct i2c_op_q_entry * entry)
{
entry->xfer_type = I2C_OP_I2C;
entry->complete = NULL;
kref_init(&entry->usecount);
- if (adap->algo->master_xfer) {
+ if (adap->algo->master_start) {
+ i2c_perform_op_wait(adap, entry);
+ } else if (adap->algo->master_xfer) {
dev_dbg(&adap->dev, "master_xfer: with %d msgs.\n",
entry->i2c.num);
@@ -1412,7 +1502,9 @@
i2c_smbus_format_entry(adap, entry);
- if (algo->smbus_xfer) {
+ 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,
@@ -1436,6 +1528,63 @@
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;
+ atomic_set(&entry->completed, 1);
+ entry->result = 0;
+ entry->use_timer = 1; /* Let the timer code poll it. */
+ entry->data = NULL;
+ kref_init(&entry->usecount);
+
+ 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. */
u32 i2c_get_functionality (struct i2c_adapter *adap)
@@ -1513,7 +1662,7 @@
entry->handler(entry);
if (new_entry) {
- /* start entry will go here. */
+ i2c_start_entry(adap, new_entry);
if (new_entry->start)
complete(new_entry->start);
/* Do tail recursion ourself. */
@@ -1584,6 +1733,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-rc5-mm1/include/linux/i2c.h
===================================================================
--- linux-2.6.11-rc5-mm1.orig/include/linux/i2c.h
+++ linux-2.6.11-rc5-mm1/include/linux/i2c.h
@@ -102,6 +102,23 @@
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. */
+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
@@ -231,6 +248,31 @@
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);
int (*slave_recv)(struct i2c_adapter *,char*,int);
^ permalink raw reply [flat|nested] 18+ messages in thread* [PATCH] Add a non-blocking interface to the I2C code, part 5
@ 2005-05-19 6:25 ` Corey Minyard
0 siblings, 0 replies; 18+ messages in thread
From: Corey Minyard @ 2005-05-19 6:25 UTC (permalink / raw)
To: Greg KH, lkml, Sensors
See part 1 for details on what this does...
-------------- next part --------------
This patch adds a non-blocking interface to the I2C code. This
is needed by some RTC drivers on the I2C bus and is needed by
the IPMI code so it can do things at panic time.
The non-blocking interface requires changes to the driver
below it. The current drivers will work as-is, but will not
be able to use the non-blocking interfaces.
Signed-off-by: Corey Minyard <minyard@acm.org>
Index: linux-2.6.11-rc5-mm1/drivers/i2c/i2c-core.c
=================================--- linux-2.6.11-rc5-mm1.orig/drivers/i2c/i2c-core.c
+++ linux-2.6.11-rc5-mm1/drivers/i2c/i2c-core.c
@@ -680,7 +680,8 @@
return;
if (sequence_match) {
- /* Poll will go here. */
+ /* This is the one we expected, call the poll routine. */
+ adap->algo->poll(adap, entry, t->next_call_time);
if (!entry_completed(entry))
i2c_start_timer(adap, entry);
@@ -697,13 +698,102 @@
* ----------------------------------------------------
*/
+/* 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) && entry->use_timer)
+ i2c_start_timer(adap, entry);
+}
+
+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;
+ atomic_set(&entry->completed, 1);
+ entry->result = 0;
+ entry->use_timer = 0; /* We poll it directly. */
+ entry->data = NULL;
+ kref_init(&entry->usecount);
+ 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;
+ atomic_set(&entry->completed, 0);
+ list_del(&entry->link);
+ }
+ spin_unlock_irqrestore(&adap->q_lock, flags);
+ }
+
+ /* Once the operation is started, we will not
+ interrupt it. */
+ while (!entry_completed(entry)) {
+ 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(entry))
+ break;
+ algo->poll(adap, entry, timeout * USEC_PER_JIFFIE);
+ }
+}
+
static void i2c_transfer_entry(struct i2c_adapter * adap,
struct i2c_op_q_entry * entry)
{
entry->xfer_type = I2C_OP_I2C;
entry->complete = NULL;
kref_init(&entry->usecount);
- if (adap->algo->master_xfer) {
+ if (adap->algo->master_start) {
+ i2c_perform_op_wait(adap, entry);
+ } else if (adap->algo->master_xfer) {
dev_dbg(&adap->dev, "master_xfer: with %d msgs.\n",
entry->i2c.num);
@@ -1412,7 +1502,9 @@
i2c_smbus_format_entry(adap, entry);
- if (algo->smbus_xfer) {
+ 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,
@@ -1436,6 +1528,63 @@
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;
+ atomic_set(&entry->completed, 1);
+ entry->result = 0;
+ entry->use_timer = 1; /* Let the timer code poll it. */
+ entry->data = NULL;
+ kref_init(&entry->usecount);
+
+ 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. */
u32 i2c_get_functionality (struct i2c_adapter *adap)
@@ -1513,7 +1662,7 @@
entry->handler(entry);
if (new_entry) {
- /* start entry will go here. */
+ i2c_start_entry(adap, new_entry);
if (new_entry->start)
complete(new_entry->start);
/* Do tail recursion ourself. */
@@ -1584,6 +1733,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-rc5-mm1/include/linux/i2c.h
=================================--- linux-2.6.11-rc5-mm1.orig/include/linux/i2c.h
+++ linux-2.6.11-rc5-mm1/include/linux/i2c.h
@@ -102,6 +102,23 @@
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. */
+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
@@ -231,6 +248,31 @@
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);
int (*slave_recv)(struct i2c_adapter *,char*,int);
^ permalink raw reply [flat|nested] 18+ messages in thread
* [PATCH] Add a non-blocking interface to the I2C code, part3
2005-05-19 6:25 ` Corey Minyard
@ 2005-05-19 6:25 ` Corey Minyard
-1 siblings, 0 replies; 18+ messages in thread
From: Corey Minyard @ 2005-03-02 20:01 UTC (permalink / raw)
To: Greg KH, lkml, Sensors
[-- Attachment #1: Type: text/plain, Size: 45 bytes --]
See part 1 for details on what this does...
[-- Attachment #2: i2c_opq_handling.diff --]
[-- Type: text/plain, Size: 6012 bytes --]
This patch adds handling of the op q to the I2C main code
in preparation for the non-blocking changes.
Signed-off-by: Corey Minyard <minyard@acm.org>
Index: linux-2.6.11-rc5-mm1/drivers/i2c/i2c-core.c
===================================================================
--- linux-2.6.11-rc5-mm1.orig/drivers/i2c/i2c-core.c
+++ linux-2.6.11-rc5-mm1/drivers/i2c/i2c-core.c
@@ -85,6 +85,8 @@
complete(&adap->dev_released);
}
+#define entry_completed(e) (atomic_read(&(e)->completed) <= 0)
+
static struct device_driver i2c_adapter_driver = {
.name = "i2c_adapter",
.bus = &i2c_bus_type,
@@ -165,6 +167,8 @@
}
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);
@@ -587,6 +591,7 @@
{
entry->xfer_type = I2C_OP_I2C;
entry->complete = NULL;
+ kref_init(&entry->usecount);
if (adap->algo->master_xfer) {
dev_dbg(&adap->dev, "master_xfer: with %d msgs.\n",
entry->i2c.num);
@@ -1284,6 +1289,7 @@
entry = kmalloc(sizeof(*entry), GFP_KERNEL);
if (!entry)
return -ENOMEM;
+ kref_init(&entry->usecount);
entry->xfer_type = I2C_OP_SMBUS;
entry->smbus.addr = addr;
@@ -1335,6 +1341,91 @@
return (func & adap_func) == func;
}
+/* ----------------------------------------------------
+ * Entry handling
+ * ----------------------------------------------------
+ */
+
+/* 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 (!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))
+ entry = NULL;
+ else
+ kref_get(&entry->usecount);
+ }
+ 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;
+}
+
+static void i2c_op_release(struct kref *ref)
+{
+ /* Nothing to do here, all handling is from the kref_put return
+ code. */
+}
+
+void i2c_entry_put(struct i2c_adapter * adap,
+ struct i2c_op_q_entry * entry)
+{
+ unsigned long flags;
+ struct i2c_op_q_entry * new_entry;
+
+ restart:
+ pr_debug("i2c_put %p %p\n", adap, entry);
+
+ spin_lock_irqsave(&adap->q_lock, flags);
+ if (kref_put(&entry->usecount, i2c_op_release)) {
+ 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) {
+ /* start entry will go here. */
+ if (new_entry->start)
+ complete(new_entry->start);
+ /* Do tail recursion ourself. */
+ entry = new_entry;
+ goto restart;
+ }
+ } else
+ spin_unlock_irqrestore(&adap->q_lock, flags);
+}
+
+void i2c_op_done(struct i2c_adapter *adap, struct i2c_op_q_entry *e)
+{
+ pr_debug("i2c_op_done: %p %p\n", adap, e);
+ if (atomic_dec_and_test(&e->completed)) {
+ /* We are the lucky winner! We get to clean up the
+ entry. */
+ if (e->complete)
+ e->complete(adap, e);
+ }
+
+ i2c_entry_put(adap, e);
+}
+
EXPORT_SYMBOL(i2c_add_adapter);
EXPORT_SYMBOL(i2c_del_adapter);
EXPORT_SYMBOL(i2c_add_driver);
@@ -1346,6 +1437,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);
Index: linux-2.6.11-rc5-mm1/include/linux/i2c.h
===================================================================
--- linux-2.6.11-rc5-mm1.orig/include/linux/i2c.h
+++ linux-2.6.11-rc5-mm1/include/linux/i2c.h
@@ -34,6 +34,8 @@
#include <linux/device.h> /* for struct device */
#include <linux/completion.h>
#include <linux/kref.h>
+#include <linux/list.h>
+#include <linux/spinlock.h>
#include <asm/semaphore.h>
#include <asm/atomic.h>
@@ -182,6 +184,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
@@ -229,6 +258,9 @@
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;
int timeout;
^ permalink raw reply [flat|nested] 18+ messages in thread* [PATCH] Add a non-blocking interface to the I2C code, part3
@ 2005-05-19 6:25 ` Corey Minyard
0 siblings, 0 replies; 18+ messages in thread
From: Corey Minyard @ 2005-05-19 6:25 UTC (permalink / raw)
To: Greg KH, lkml, Sensors
See part 1 for details on what this does...
-------------- next part --------------
This patch adds handling of the op q to the I2C main code
in preparation for the non-blocking changes.
Signed-off-by: Corey Minyard <minyard@acm.org>
Index: linux-2.6.11-rc5-mm1/drivers/i2c/i2c-core.c
=================================--- linux-2.6.11-rc5-mm1.orig/drivers/i2c/i2c-core.c
+++ linux-2.6.11-rc5-mm1/drivers/i2c/i2c-core.c
@@ -85,6 +85,8 @@
complete(&adap->dev_released);
}
+#define entry_completed(e) (atomic_read(&(e)->completed) <= 0)
+
static struct device_driver i2c_adapter_driver = {
.name = "i2c_adapter",
.bus = &i2c_bus_type,
@@ -165,6 +167,8 @@
}
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);
@@ -587,6 +591,7 @@
{
entry->xfer_type = I2C_OP_I2C;
entry->complete = NULL;
+ kref_init(&entry->usecount);
if (adap->algo->master_xfer) {
dev_dbg(&adap->dev, "master_xfer: with %d msgs.\n",
entry->i2c.num);
@@ -1284,6 +1289,7 @@
entry = kmalloc(sizeof(*entry), GFP_KERNEL);
if (!entry)
return -ENOMEM;
+ kref_init(&entry->usecount);
entry->xfer_type = I2C_OP_SMBUS;
entry->smbus.addr = addr;
@@ -1335,6 +1341,91 @@
return (func & adap_func) = func;
}
+/* ----------------------------------------------------
+ * Entry handling
+ * ----------------------------------------------------
+ */
+
+/* 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 (!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))
+ entry = NULL;
+ else
+ kref_get(&entry->usecount);
+ }
+ 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;
+}
+
+static void i2c_op_release(struct kref *ref)
+{
+ /* Nothing to do here, all handling is from the kref_put return
+ code. */
+}
+
+void i2c_entry_put(struct i2c_adapter * adap,
+ struct i2c_op_q_entry * entry)
+{
+ unsigned long flags;
+ struct i2c_op_q_entry * new_entry;
+
+ restart:
+ pr_debug("i2c_put %p %p\n", adap, entry);
+
+ spin_lock_irqsave(&adap->q_lock, flags);
+ if (kref_put(&entry->usecount, i2c_op_release)) {
+ 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) {
+ /* start entry will go here. */
+ if (new_entry->start)
+ complete(new_entry->start);
+ /* Do tail recursion ourself. */
+ entry = new_entry;
+ goto restart;
+ }
+ } else
+ spin_unlock_irqrestore(&adap->q_lock, flags);
+}
+
+void i2c_op_done(struct i2c_adapter *adap, struct i2c_op_q_entry *e)
+{
+ pr_debug("i2c_op_done: %p %p\n", adap, e);
+ if (atomic_dec_and_test(&e->completed)) {
+ /* We are the lucky winner! We get to clean up the
+ entry. */
+ if (e->complete)
+ e->complete(adap, e);
+ }
+
+ i2c_entry_put(adap, e);
+}
+
EXPORT_SYMBOL(i2c_add_adapter);
EXPORT_SYMBOL(i2c_del_adapter);
EXPORT_SYMBOL(i2c_add_driver);
@@ -1346,6 +1437,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);
Index: linux-2.6.11-rc5-mm1/include/linux/i2c.h
=================================--- linux-2.6.11-rc5-mm1.orig/include/linux/i2c.h
+++ linux-2.6.11-rc5-mm1/include/linux/i2c.h
@@ -34,6 +34,8 @@
#include <linux/device.h> /* for struct device */
#include <linux/completion.h>
#include <linux/kref.h>
+#include <linux/list.h>
+#include <linux/spinlock.h>
#include <asm/semaphore.h>
#include <asm/atomic.h>
@@ -182,6 +184,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
@@ -229,6 +258,9 @@
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;
int timeout;
^ permalink raw reply [flat|nested] 18+ messages in thread
* [PATCH] Add a non-blocking interface to the I2C code, part 2
2005-05-19 6:25 ` Corey Minyard
@ 2005-05-19 6:25 ` Corey Minyard
-1 siblings, 0 replies; 18+ messages in thread
From: Corey Minyard @ 2005-03-02 20:00 UTC (permalink / raw)
To: Greg KH, lkml, Sensors
[-- Attachment #1: Type: text/plain, Size: 44 bytes --]
See part 1 for details on what this does...
[-- Attachment #2: i2c_add_op_q.diff --]
[-- Type: text/plain, Size: 18015 bytes --]
This patch adds an operation queue type and converts over the passing
around of data to use the operation queue. The op queue entry will be
used for queueing in the non-blocking case.
Signed-off-by: Corey Minyard <minyard@acm.org>
Index: linux-2.6.11-rc5-mm1/drivers/i2c/i2c-core.c
===================================================================
--- linux-2.6.11-rc5-mm1.orig/drivers/i2c/i2c-core.c
+++ linux-2.6.11-rc5-mm1/drivers/i2c/i2c-core.c
@@ -582,24 +582,45 @@
* ----------------------------------------------------
*/
-int i2c_transfer(struct i2c_adapter * adap, struct i2c_msg *msgs, int num)
+static void i2c_transfer_entry(struct i2c_adapter * adap,
+ struct i2c_op_q_entry * entry)
{
- int ret;
-
+ entry->xfer_type = I2C_OP_I2C;
+ entry->complete = NULL;
if (adap->algo->master_xfer) {
- dev_dbg(&adap->dev, "master_xfer: with %d msgs.\n", num);
+ 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);
+ entry->result = adap->algo->master_xfer(adap, entry->i2c.msgs,
+ entry->i2c.num);
up(&adap->bus_lock);
-
- return ret;
+ if (entry->complete)
+ entry->complete(adap, entry);
} else {
dev_dbg(&adap->dev, "I2C level transfers not supported\n");
- return -ENOSYS;
+ entry->result = -ENOSYS;
}
}
+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;
+
+ i2c_transfer_entry(adap, entry);
+ rv = entry->result;
+ kfree(entry);
+ return rv;
+}
+
int i2c_master_send(struct i2c_client *client,const char *buf ,int count)
{
int ret;
@@ -1038,188 +1059,182 @@
}
}
-static int i2c_smbus_complete_entry(struct i2c_adapter * adap, u16 addr,
- unsigned short flags, char read_write,
- u8 command, int size,
- union i2c_smbus_data * data,
- int swpec, int partial,
- int result)
-{
- if (result < 0)
- return result;
-
- if(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 -EINVAL;
- }
+static void i2c_smbus_complete_entry(struct i2c_adapter * adap,
+ struct i2c_op_q_entry * entry)
+{
+ if (entry->result < 0)
+ return;
- return 0;
+ if(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, u16 addr,
- unsigned short *flags, char read_write,
- u8 command, int *size,
- union i2c_smbus_data * data,
- int *swpec, int *partial)
-{
- *swpec = 0;
- *partial = 0;
- *flags &= I2C_M_TEN | I2C_CLIENT_PEC;
- if((*flags & I2C_CLIENT_PEC) &&
+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))) {
- *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) {
- unsigned char *sdata = data->block;
- i2c_smbus_add_pec(addr, command, I2C_SMBUS_BLOCK_DATA,
- data);
- *partial = sdata[sdata[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);
+ 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 int i2c_smbus_emu_complete(struct i2c_adapter * adap, u16 addr,
- unsigned short flags, char read_write,
- u8 command, int size,
- union i2c_smbus_data * data,
- struct i2c_msg *msg,
- int swpec, int partial,
- int result)
+static void i2c_smbus_emu_complete(struct i2c_adapter * adap,
+ struct i2c_op_q_entry * entry)
{
- unsigned char *msgbuf0 = msg[0].buf;
- unsigned char *msgbuf1 = msg[1].buf;
+ unsigned char *msgbuf0 = entry->i2c.msgs[0].buf;
+ unsigned char *msgbuf1 = entry->i2c.msgs[1].buf;
int i;
+ if (entry->result < 0)
+ return;
- if (result < 0)
- return result;
+ if (entry->smbus.read_write != I2C_SMBUS_READ)
+ return;
- if (read_write != I2C_SMBUS_READ)
- return result;
-
- switch(size) {
+ switch(entry->smbus.size) {
case I2C_SMBUS_BYTE:
- data->byte = msgbuf0[0];
+ entry->smbus.data->byte = msgbuf0[0];
break;
case I2C_SMBUS_BYTE_DATA:
- data->byte = msgbuf1[0];
+ entry->smbus.data->byte = msgbuf1[0];
break;
case I2C_SMBUS_WORD_DATA:
case I2C_SMBUS_PROC_CALL:
- data->word = msgbuf1[0]|(msgbuf1[1] << 8);
+ entry->smbus.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;
+ entry->smbus.data->block[0] = I2C_SMBUS_I2C_BLOCK_MAX;
for (i = 0; i < I2C_SMBUS_I2C_BLOCK_MAX; i++)
- data->block[i+1] = msgbuf1[i];
+ entry->smbus.data->block[i+1] = msgbuf1[i];
break;
}
- return i2c_smbus_complete_entry(adap, addr, flags,
- read_write, command,
- size, data, swpec, partial, result);
+ entry->xfer_type = I2C_OP_SMBUS;
+ i2c_smbus_complete_entry(adap, entry);
}
-static int i2c_smbus_emu_format(struct i2c_adapter *adap, u16 addr,
- unsigned short flags, char read_write,
- u8 command, int size,
- union i2c_smbus_data * data,
- struct i2c_msg *msg)
+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 = msg[0].buf;
- int num = read_write == I2C_SMBUS_READ?2:1;
+ unsigned char *msgbuf0 = entry->msgbuf0;
+ int num = entry->smbus.read_write == I2C_SMBUS_READ?2:1;
+ struct i2c_msg *msg = entry->msg;
int i;
- msg[0].addr = addr;
- msg[0].flags = flags;
+ entry->i2c.msgs = msg;
+ entry->i2c.msgs[0].buf = msgbuf0;
+ entry->i2c.msgs[1].buf = entry->msgbuf1;
+
+ msg[0].addr = entry->smbus.addr;
+ msg[0].flags = entry->smbus.flags;
msg[0].len = 1;
- msg[1].addr = addr;
- msg[1].flags = flags | I2C_M_RD;
+ msg[1].addr = entry->smbus.addr;
+ msg[1].flags = entry->smbus.flags | I2C_M_RD;
msg[1].len = 0;
- msgbuf0[0] = command;
- switch(size) {
+ 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)
+ 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) {
+ 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(&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:
@@ -1228,28 +1243,32 @@
"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(&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(&adap->dev,
"smbus_access called with invalid size (%d)\n",
- size);
+ entry->smbus.size);
return -1;
}
+ entry->xfer_type = I2C_OP_I2C;
+ entry->i2c.msgs = msg;
+ entry->i2c.num = num;
+ entry->complete = i2c_smbus_emu_complete;
return 0;
}
@@ -1257,46 +1276,46 @@
char read_write, u8 command, int size,
union i2c_smbus_data * data)
{
+ struct i2c_op_q_entry *entry;
struct i2c_algorithm *algo = adap->algo;
int result;
- int swpec, partial;
- i2c_smbus_format_entry(adap, addr, &flags,
- read_write, command,
- &size, data, &swpec, &partial);
+ 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_xfer) {
down(&adap->bus_lock);
- result = adap->algo->smbus_xfer(adap, addr, flags,
- read_write, command,
- size, data);
+ 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);
- result = i2c_smbus_complete_entry(adap, addr, flags,
- read_write, command,
- size, data, swpec, partial,
- result);
+ if (entry->complete)
+ entry->complete(adap, entry);
} else {
- unsigned char msgbuf0[34];
- unsigned char msgbuf1[34];
- struct i2c_msg msg[2];
-
- msg[0].buf = msgbuf0;
- msg[1].buf = msgbuf1;
- if (i2c_smbus_emu_format(adap, addr, flags,
- read_write, command,
- size, data, msg))
- result = -EINVAL;
- else {
- result = i2c_transfer(adap, msg, 2);
- result = i2c_smbus_emu_complete(adap, addr, flags,
- read_write, command,
- size, data, msg,
- swpec, partial,
- result);
- }
+ if (i2c_smbus_emu_format(adap, entry))
+ entry->result = -EINVAL;
+ else
+ i2c_transfer_entry(adap, entry);
}
+ result = entry->result;
+ kfree(entry);
return result;
}
Index: linux-2.6.11-rc5-mm1/include/linux/i2c.h
===================================================================
--- linux-2.6.11-rc5-mm1.orig/include/linux/i2c.h
+++ linux-2.6.11-rc5-mm1/include/linux/i2c.h
@@ -32,7 +32,10 @@
#include <linux/types.h>
#include <linux/i2c-id.h>
#include <linux/device.h> /* for struct device */
+#include <linux/completion.h>
+#include <linux/kref.h>
#include <asm/semaphore.h>
+#include <asm/atomic.h>
/* --- General options ------------------------------------------------ */
@@ -43,6 +46,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
@@ -226,7 +230,6 @@
/* data fields that are valid for all devices */
struct semaphore bus_lock;
- struct semaphore clist_lock;
int timeout;
int retries;
@@ -239,6 +242,7 @@
#endif /* def CONFIG_PROC_FS */
int nr;
+ struct semaphore clist_lock;
struct list_head clients;
struct list_head list;
char name[I2C_NAME_SIZE];
@@ -392,6 +396,92 @@
__u8 *buf; /* pointer to msg data */
};
+/*
+ * Hold a queue of I2C operations to perform, and used to pass data
+ * around.
+ */
+typedef void (*i2c_op_done_cb)(struct i2c_op_q_entry *entry);
+
+#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;
+ atomic_t completed;
+ unsigned int started : 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. */
+ struct kref 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
^ permalink raw reply [flat|nested] 18+ messages in thread* [PATCH] Add a non-blocking interface to the I2C code, part 2
@ 2005-05-19 6:25 ` Corey Minyard
0 siblings, 0 replies; 18+ messages in thread
From: Corey Minyard @ 2005-05-19 6:25 UTC (permalink / raw)
To: Greg KH, lkml, Sensors
See part 1 for details on what this does...
-------------- next part --------------
This patch adds an operation queue type and converts over the passing
around of data to use the operation queue. The op queue entry will be
used for queueing in the non-blocking case.
Signed-off-by: Corey Minyard <minyard@acm.org>
Index: linux-2.6.11-rc5-mm1/drivers/i2c/i2c-core.c
=================================--- linux-2.6.11-rc5-mm1.orig/drivers/i2c/i2c-core.c
+++ linux-2.6.11-rc5-mm1/drivers/i2c/i2c-core.c
@@ -582,24 +582,45 @@
* ----------------------------------------------------
*/
-int i2c_transfer(struct i2c_adapter * adap, struct i2c_msg *msgs, int num)
+static void i2c_transfer_entry(struct i2c_adapter * adap,
+ struct i2c_op_q_entry * entry)
{
- int ret;
-
+ entry->xfer_type = I2C_OP_I2C;
+ entry->complete = NULL;
if (adap->algo->master_xfer) {
- dev_dbg(&adap->dev, "master_xfer: with %d msgs.\n", num);
+ 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);
+ entry->result = adap->algo->master_xfer(adap, entry->i2c.msgs,
+ entry->i2c.num);
up(&adap->bus_lock);
-
- return ret;
+ if (entry->complete)
+ entry->complete(adap, entry);
} else {
dev_dbg(&adap->dev, "I2C level transfers not supported\n");
- return -ENOSYS;
+ entry->result = -ENOSYS;
}
}
+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;
+
+ i2c_transfer_entry(adap, entry);
+ rv = entry->result;
+ kfree(entry);
+ return rv;
+}
+
int i2c_master_send(struct i2c_client *client,const char *buf ,int count)
{
int ret;
@@ -1038,188 +1059,182 @@
}
}
-static int i2c_smbus_complete_entry(struct i2c_adapter * adap, u16 addr,
- unsigned short flags, char read_write,
- u8 command, int size,
- union i2c_smbus_data * data,
- int swpec, int partial,
- int result)
-{
- if (result < 0)
- return result;
-
- if(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 -EINVAL;
- }
+static void i2c_smbus_complete_entry(struct i2c_adapter * adap,
+ struct i2c_op_q_entry * entry)
+{
+ if (entry->result < 0)
+ return;
- return 0;
+ if(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, u16 addr,
- unsigned short *flags, char read_write,
- u8 command, int *size,
- union i2c_smbus_data * data,
- int *swpec, int *partial)
-{
- *swpec = 0;
- *partial = 0;
- *flags &= I2C_M_TEN | I2C_CLIENT_PEC;
- if((*flags & I2C_CLIENT_PEC) &&
+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))) {
- *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) {
- unsigned char *sdata = data->block;
- i2c_smbus_add_pec(addr, command, I2C_SMBUS_BLOCK_DATA,
- data);
- *partial = sdata[sdata[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);
+ 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 int i2c_smbus_emu_complete(struct i2c_adapter * adap, u16 addr,
- unsigned short flags, char read_write,
- u8 command, int size,
- union i2c_smbus_data * data,
- struct i2c_msg *msg,
- int swpec, int partial,
- int result)
+static void i2c_smbus_emu_complete(struct i2c_adapter * adap,
+ struct i2c_op_q_entry * entry)
{
- unsigned char *msgbuf0 = msg[0].buf;
- unsigned char *msgbuf1 = msg[1].buf;
+ unsigned char *msgbuf0 = entry->i2c.msgs[0].buf;
+ unsigned char *msgbuf1 = entry->i2c.msgs[1].buf;
int i;
+ if (entry->result < 0)
+ return;
- if (result < 0)
- return result;
+ if (entry->smbus.read_write != I2C_SMBUS_READ)
+ return;
- if (read_write != I2C_SMBUS_READ)
- return result;
-
- switch(size) {
+ switch(entry->smbus.size) {
case I2C_SMBUS_BYTE:
- data->byte = msgbuf0[0];
+ entry->smbus.data->byte = msgbuf0[0];
break;
case I2C_SMBUS_BYTE_DATA:
- data->byte = msgbuf1[0];
+ entry->smbus.data->byte = msgbuf1[0];
break;
case I2C_SMBUS_WORD_DATA:
case I2C_SMBUS_PROC_CALL:
- data->word = msgbuf1[0]|(msgbuf1[1] << 8);
+ entry->smbus.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;
+ entry->smbus.data->block[0] = I2C_SMBUS_I2C_BLOCK_MAX;
for (i = 0; i < I2C_SMBUS_I2C_BLOCK_MAX; i++)
- data->block[i+1] = msgbuf1[i];
+ entry->smbus.data->block[i+1] = msgbuf1[i];
break;
}
- return i2c_smbus_complete_entry(adap, addr, flags,
- read_write, command,
- size, data, swpec, partial, result);
+ entry->xfer_type = I2C_OP_SMBUS;
+ i2c_smbus_complete_entry(adap, entry);
}
-static int i2c_smbus_emu_format(struct i2c_adapter *adap, u16 addr,
- unsigned short flags, char read_write,
- u8 command, int size,
- union i2c_smbus_data * data,
- struct i2c_msg *msg)
+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 = msg[0].buf;
- int num = read_write = I2C_SMBUS_READ?2:1;
+ unsigned char *msgbuf0 = entry->msgbuf0;
+ int num = entry->smbus.read_write = I2C_SMBUS_READ?2:1;
+ struct i2c_msg *msg = entry->msg;
int i;
- msg[0].addr = addr;
- msg[0].flags = flags;
+ entry->i2c.msgs = msg;
+ entry->i2c.msgs[0].buf = msgbuf0;
+ entry->i2c.msgs[1].buf = entry->msgbuf1;
+
+ msg[0].addr = entry->smbus.addr;
+ msg[0].flags = entry->smbus.flags;
msg[0].len = 1;
- msg[1].addr = addr;
- msg[1].flags = flags | I2C_M_RD;
+ msg[1].addr = entry->smbus.addr;
+ msg[1].flags = entry->smbus.flags | I2C_M_RD;
msg[1].len = 0;
- msgbuf0[0] = command;
- switch(size) {
+ 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)
+ 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) {
+ 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(&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:
@@ -1228,28 +1243,32 @@
"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(&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(&adap->dev,
"smbus_access called with invalid size (%d)\n",
- size);
+ entry->smbus.size);
return -1;
}
+ entry->xfer_type = I2C_OP_I2C;
+ entry->i2c.msgs = msg;
+ entry->i2c.num = num;
+ entry->complete = i2c_smbus_emu_complete;
return 0;
}
@@ -1257,46 +1276,46 @@
char read_write, u8 command, int size,
union i2c_smbus_data * data)
{
+ struct i2c_op_q_entry *entry;
struct i2c_algorithm *algo = adap->algo;
int result;
- int swpec, partial;
- i2c_smbus_format_entry(adap, addr, &flags,
- read_write, command,
- &size, data, &swpec, &partial);
+ 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_xfer) {
down(&adap->bus_lock);
- result = adap->algo->smbus_xfer(adap, addr, flags,
- read_write, command,
- size, data);
+ 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);
- result = i2c_smbus_complete_entry(adap, addr, flags,
- read_write, command,
- size, data, swpec, partial,
- result);
+ if (entry->complete)
+ entry->complete(adap, entry);
} else {
- unsigned char msgbuf0[34];
- unsigned char msgbuf1[34];
- struct i2c_msg msg[2];
-
- msg[0].buf = msgbuf0;
- msg[1].buf = msgbuf1;
- if (i2c_smbus_emu_format(adap, addr, flags,
- read_write, command,
- size, data, msg))
- result = -EINVAL;
- else {
- result = i2c_transfer(adap, msg, 2);
- result = i2c_smbus_emu_complete(adap, addr, flags,
- read_write, command,
- size, data, msg,
- swpec, partial,
- result);
- }
+ if (i2c_smbus_emu_format(adap, entry))
+ entry->result = -EINVAL;
+ else
+ i2c_transfer_entry(adap, entry);
}
+ result = entry->result;
+ kfree(entry);
return result;
}
Index: linux-2.6.11-rc5-mm1/include/linux/i2c.h
=================================--- linux-2.6.11-rc5-mm1.orig/include/linux/i2c.h
+++ linux-2.6.11-rc5-mm1/include/linux/i2c.h
@@ -32,7 +32,10 @@
#include <linux/types.h>
#include <linux/i2c-id.h>
#include <linux/device.h> /* for struct device */
+#include <linux/completion.h>
+#include <linux/kref.h>
#include <asm/semaphore.h>
+#include <asm/atomic.h>
/* --- General options ------------------------------------------------ */
@@ -43,6 +46,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
@@ -226,7 +230,6 @@
/* data fields that are valid for all devices */
struct semaphore bus_lock;
- struct semaphore clist_lock;
int timeout;
int retries;
@@ -239,6 +242,7 @@
#endif /* def CONFIG_PROC_FS */
int nr;
+ struct semaphore clist_lock;
struct list_head clients;
struct list_head list;
char name[I2C_NAME_SIZE];
@@ -392,6 +396,92 @@
__u8 *buf; /* pointer to msg data */
};
+/*
+ * Hold a queue of I2C operations to perform, and used to pass data
+ * around.
+ */
+typedef void (*i2c_op_done_cb)(struct i2c_op_q_entry *entry);
+
+#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;
+ atomic_t completed;
+ unsigned int started : 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. */
+ struct kref 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
^ permalink raw reply [flat|nested] 18+ messages in thread
* [PATCH] Add a non-blocking interface to the I2C code, part 1
@ 2005-05-19 6:25 ` Corey Minyard
0 siblings, 0 replies; 18+ messages in thread
From: Corey Minyard @ 2005-03-02 19:58 UTC (permalink / raw)
To: Greg KH; +Cc: lkml, Sensors
[-- Attachment #1: Type: text/plain, Size: 962 bytes --]
This is the next try at adding the non-blocking interface to the I2C code,
broken into finer patches and a few problems fixed from the previous
release.
The IPMI SMBus driver needs a non-blocking interface; it needs to do
things to the system at times when it cannot schedule, such as at
panic or shutdown time. This interface lets it perform I2C operations
and for polling. This also may be useful for systems that put the
RTC out on the SMBus, or other things that may need to be accessed
where running from a task context is not an option, or not simple.
Note that the I2C bus driver needs to be changed to support this. The
old interface to the I2C code is still supported, so no drivers need
to change until they need the new interface. Currently, only the
i801 driver is updated.
-Corey
[-- Attachment #2: i2c_breakup_formatting.diff --]
[-- Type: text/plain, Size: 12508 bytes --]
This patch reorganizes the I2C SMBus formatting code to make it more
suitable for the upcoming non-blocking changes.
Signed-off-by: Corey Minyard <minyard@acm.org>
Index: linux-2.6.11-rc4/drivers/i2c/i2c-core.c
===================================================================
--- linux-2.6.11-rc4.orig/drivers/i2c/i2c-core.c
+++ linux-2.6.11-rc4/drivers/i2c/i2c-core.c
@@ -606,27 +606,20 @@
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;
+ 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);
-
- 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 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)
@@ -634,31 +627,25 @@
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);
+
+ 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;
}
@@ -1051,31 +1038,135 @@
}
}
-/* 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];
+static int i2c_smbus_complete_entry(struct i2c_adapter * adap, u16 addr,
+ unsigned short flags, char read_write,
+ u8 command, int size,
+ union i2c_smbus_data * data,
+ int swpec, int partial,
+ int result)
+{
+ if (result < 0)
+ return result;
+
+ if(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 -EINVAL;
+ }
+
+ return 0;
+}
+
+static void i2c_smbus_format_entry(struct i2c_adapter * adap, u16 addr,
+ unsigned short *flags, char read_write,
+ u8 command, int *size,
+ union i2c_smbus_data * data,
+ int *swpec, int *partial)
+{
+ *swpec = 0;
+ *partial = 0;
+ *flags &= I2C_M_TEN | I2C_CLIENT_PEC;
+ if((*flags & I2C_CLIENT_PEC) &&
+ !(i2c_check_functionality(adap, 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) {
+ unsigned char *sdata = data->block;
+ i2c_smbus_add_pec(addr, command, I2C_SMBUS_BLOCK_DATA,
+ data);
+ *partial = sdata[sdata[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);
+ }
+}
+
+static int i2c_smbus_emu_complete(struct i2c_adapter * adap, u16 addr,
+ unsigned short flags, char read_write,
+ u8 command, int size,
+ union i2c_smbus_data * data,
+ struct i2c_msg *msg,
+ int swpec, int partial,
+ int result)
+{
+ unsigned char *msgbuf0 = msg[0].buf;
+ unsigned char *msgbuf1 = msg[1].buf;
+ int i;
+
+
+ if (result < 0)
+ return result;
+
+ if (read_write != I2C_SMBUS_READ)
+ return result;
+
+ 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;
+ }
+
+ return i2c_smbus_complete_entry(adap, addr, flags,
+ read_write, command,
+ size, data, swpec, partial, result);
+}
+
+static int i2c_smbus_emu_format(struct i2c_adapter *adap, u16 addr,
+ unsigned short flags, char read_write,
+ u8 command, int size,
+ union i2c_smbus_data * data,
+ struct i2c_msg *msg)
+{
+ /* 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 = msg[0].buf;
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 }
- };
int i;
+ msg[0].addr = addr;
+ msg[0].flags = flags;
+ msg[0].len = 1;
+ msg[1].addr = addr;
+ msg[1].flags = flags | I2C_M_RD;
+ msg[1].len = 0;
+
msgbuf0[0] = command;
switch(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 = (flags |
+ ((read_write==I2C_SMBUS_READ)
+ ? I2C_M_RD : 0));
num = 1;
break;
case I2C_SMBUS_BYTE:
@@ -1113,13 +1204,14 @@
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 "
+ dev_err(&adap->dev, "Block read not supported "
"under I2C emulation!\n");
return -1;
} else {
msg[0].len = 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]);
return -1;
@@ -1132,7 +1224,7 @@
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:
@@ -1141,7 +1233,8 @@
} else {
msg[0].len = 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]);
return -1;
@@ -1151,84 +1244,61 @@
}
break;
default:
- dev_err(&adapter->dev, "smbus_access called with invalid size (%d)\n",
+ dev_err(&adap->dev,
+ "smbus_access called with invalid size (%d)\n",
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;
- }
return 0;
}
-
-s32 i2c_smbus_xfer(struct i2c_adapter * adapter, u16 addr, unsigned short flags,
+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_algorithm *algo = adap->algo;
+ int result;
+ int swpec, partial;
+
+
+ i2c_smbus_format_entry(adap, addr, &flags,
+ read_write, command,
+ &size, data, &swpec, &partial);
+
+ if (algo->smbus_xfer) {
+ down(&adap->bus_lock);
+ result = adap->algo->smbus_xfer(adap, addr, flags,
+ read_write, command,
+ size, data);
+ up(&adap->bus_lock);
+ result = i2c_smbus_complete_entry(adap, addr, flags,
+ read_write, command,
+ size, data, swpec, partial,
+ result);
+ } else {
+ unsigned char msgbuf0[34];
+ unsigned char msgbuf1[34];
+ struct i2c_msg msg[2];
+
+ msg[0].buf = msgbuf0;
+ msg[1].buf = msgbuf1;
+ if (i2c_smbus_emu_format(adap, addr, flags,
+ read_write, command,
+ size, data, msg))
+ result = -EINVAL;
+ else {
+ result = i2c_transfer(adap, msg, 2);
+ result = i2c_smbus_emu_complete(adap, addr, flags,
+ read_write, command,
+ size, data, msg,
+ swpec, partial,
+ result);
+ }
}
- return res;
-}
+ return result;
+}
/* You should always define `functionality'; the 'else' is just for
backward compatibility. */
^ permalink raw reply [flat|nested] 18+ messages in thread* [PATCH] Add a non-blocking interface to the I2C code, part 1
@ 2005-05-19 6:25 ` Corey Minyard
0 siblings, 0 replies; 18+ messages in thread
From: Corey Minyard @ 2005-05-19 6:25 UTC (permalink / raw)
To: Greg KH; +Cc: lkml, Sensors
This is the next try at adding the non-blocking interface to the I2C code,
broken into finer patches and a few problems fixed from the previous
release.
The IPMI SMBus driver needs a non-blocking interface; it needs to do
things to the system at times when it cannot schedule, such as at
panic or shutdown time. This interface lets it perform I2C operations
and for polling. This also may be useful for systems that put the
RTC out on the SMBus, or other things that may need to be accessed
where running from a task context is not an option, or not simple.
Note that the I2C bus driver needs to be changed to support this. The
old interface to the I2C code is still supported, so no drivers need
to change until they need the new interface. Currently, only the
i801 driver is updated.
-Corey
-------------- next part --------------
This patch reorganizes the I2C SMBus formatting code to make it more
suitable for the upcoming non-blocking changes.
Signed-off-by: Corey Minyard <minyard@acm.org>
Index: linux-2.6.11-rc4/drivers/i2c/i2c-core.c
=================================--- linux-2.6.11-rc4.orig/drivers/i2c/i2c-core.c
+++ linux-2.6.11-rc4/drivers/i2c/i2c-core.c
@@ -606,27 +606,20 @@
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;
+ 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);
-
- 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 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)
@@ -634,31 +627,25 @@
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);
+
+ 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;
}
@@ -1051,31 +1038,135 @@
}
}
-/* 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];
+static int i2c_smbus_complete_entry(struct i2c_adapter * adap, u16 addr,
+ unsigned short flags, char read_write,
+ u8 command, int size,
+ union i2c_smbus_data * data,
+ int swpec, int partial,
+ int result)
+{
+ if (result < 0)
+ return result;
+
+ if(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 -EINVAL;
+ }
+
+ return 0;
+}
+
+static void i2c_smbus_format_entry(struct i2c_adapter * adap, u16 addr,
+ unsigned short *flags, char read_write,
+ u8 command, int *size,
+ union i2c_smbus_data * data,
+ int *swpec, int *partial)
+{
+ *swpec = 0;
+ *partial = 0;
+ *flags &= I2C_M_TEN | I2C_CLIENT_PEC;
+ if((*flags & I2C_CLIENT_PEC) &&
+ !(i2c_check_functionality(adap, 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) {
+ unsigned char *sdata = data->block;
+ i2c_smbus_add_pec(addr, command, I2C_SMBUS_BLOCK_DATA,
+ data);
+ *partial = sdata[sdata[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);
+ }
+}
+
+static int i2c_smbus_emu_complete(struct i2c_adapter * adap, u16 addr,
+ unsigned short flags, char read_write,
+ u8 command, int size,
+ union i2c_smbus_data * data,
+ struct i2c_msg *msg,
+ int swpec, int partial,
+ int result)
+{
+ unsigned char *msgbuf0 = msg[0].buf;
+ unsigned char *msgbuf1 = msg[1].buf;
+ int i;
+
+
+ if (result < 0)
+ return result;
+
+ if (read_write != I2C_SMBUS_READ)
+ return result;
+
+ 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;
+ }
+
+ return i2c_smbus_complete_entry(adap, addr, flags,
+ read_write, command,
+ size, data, swpec, partial, result);
+}
+
+static int i2c_smbus_emu_format(struct i2c_adapter *adap, u16 addr,
+ unsigned short flags, char read_write,
+ u8 command, int size,
+ union i2c_smbus_data * data,
+ struct i2c_msg *msg)
+{
+ /* 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 = msg[0].buf;
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 }
- };
int i;
+ msg[0].addr = addr;
+ msg[0].flags = flags;
+ msg[0].len = 1;
+ msg[1].addr = addr;
+ msg[1].flags = flags | I2C_M_RD;
+ msg[1].len = 0;
+
msgbuf0[0] = command;
switch(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 = (flags |
+ ((read_write=I2C_SMBUS_READ)
+ ? I2C_M_RD : 0));
num = 1;
break;
case I2C_SMBUS_BYTE:
@@ -1113,13 +1204,14 @@
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 "
+ dev_err(&adap->dev, "Block read not supported "
"under I2C emulation!\n");
return -1;
} else {
msg[0].len = 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]);
return -1;
@@ -1132,7 +1224,7 @@
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:
@@ -1141,7 +1233,8 @@
} else {
msg[0].len = 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]);
return -1;
@@ -1151,84 +1244,61 @@
}
break;
default:
- dev_err(&adapter->dev, "smbus_access called with invalid size (%d)\n",
+ dev_err(&adap->dev,
+ "smbus_access called with invalid size (%d)\n",
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;
- }
return 0;
}
-
-s32 i2c_smbus_xfer(struct i2c_adapter * adapter, u16 addr, unsigned short flags,
+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_algorithm *algo = adap->algo;
+ int result;
+ int swpec, partial;
+
+
+ i2c_smbus_format_entry(adap, addr, &flags,
+ read_write, command,
+ &size, data, &swpec, &partial);
+
+ if (algo->smbus_xfer) {
+ down(&adap->bus_lock);
+ result = adap->algo->smbus_xfer(adap, addr, flags,
+ read_write, command,
+ size, data);
+ up(&adap->bus_lock);
+ result = i2c_smbus_complete_entry(adap, addr, flags,
+ read_write, command,
+ size, data, swpec, partial,
+ result);
+ } else {
+ unsigned char msgbuf0[34];
+ unsigned char msgbuf1[34];
+ struct i2c_msg msg[2];
+
+ msg[0].buf = msgbuf0;
+ msg[1].buf = msgbuf1;
+ if (i2c_smbus_emu_format(adap, addr, flags,
+ read_write, command,
+ size, data, msg))
+ result = -EINVAL;
+ else {
+ result = i2c_transfer(adap, msg, 2);
+ result = i2c_smbus_emu_complete(adap, addr, flags,
+ read_write, command,
+ size, data, msg,
+ swpec, partial,
+ result);
+ }
}
- return res;
-}
+ return result;
+}
/* You should always define `functionality'; the 'else' is just for
backward compatibility. */
^ permalink raw reply [flat|nested] 18+ messages in thread* Re: [PATCH] Add a non-blocking interface to the I2C code, part 1
2005-05-19 6:25 ` Corey Minyard
@ 2005-05-19 6:25 ` Greg KH
-1 siblings, 0 replies; 18+ messages in thread
From: Greg KH @ 2005-03-17 7:09 UTC (permalink / raw)
To: Corey Minyard; +Cc: lkml, Sensors
On Wed, Mar 02, 2005 at 01:58:51PM -0600, Corey Minyard wrote:
> This patch reorganizes the I2C SMBus formatting code to make it more
> suitable for the upcoming non-blocking changes.
You are changing too much stuff here to claim it's just a
reorganization:
- variable name changes for no reason
- coding style changes (improper ones at that)
- logic changes.
What exactly are you doing with this patch, and why?
thanks,
greg k-h
^ permalink raw reply [flat|nested] 18+ messages in thread
* [PATCH] Add a non-blocking interface to the I2C code, part 1
@ 2005-05-19 6:25 ` Greg KH
0 siblings, 0 replies; 18+ messages in thread
From: Greg KH @ 2005-05-19 6:25 UTC (permalink / raw)
To: Corey Minyard; +Cc: lkml, Sensors
On Wed, Mar 02, 2005 at 01:58:51PM -0600, Corey Minyard wrote:
> This patch reorganizes the I2C SMBus formatting code to make it more
> suitable for the upcoming non-blocking changes.
You are changing too much stuff here to claim it's just a
reorganization:
- variable name changes for no reason
- coding style changes (improper ones at that)
- logic changes.
What exactly are you doing with this patch, and why?
thanks,
greg k-h
^ permalink raw reply [flat|nested] 18+ messages in thread
* Re: [PATCH] Add a non-blocking interface to the I2C code, part 1
2005-05-19 6:25 ` Greg KH
@ 2005-05-19 6:25 ` Corey Minyard
-1 siblings, 0 replies; 18+ messages in thread
From: Corey Minyard @ 2005-03-18 16:13 UTC (permalink / raw)
To: Greg KH; +Cc: lkml, Sensors
Greg KH wrote:
>On Wed, Mar 02, 2005 at 01:58:51PM -0600, Corey Minyard wrote:
>
>
>>This patch reorganizes the I2C SMBus formatting code to make it more
>>suitable for the upcoming non-blocking changes.
>>
>>
>
>You are changing too much stuff here to claim it's just a
>reorganization:
> - variable name changes for no reason
>
>
Well, yes. Both "adap" and "adapter" are used to refer to the same
thing in the file; "adap" seemed to be the most common usage so I chose
that for the new functions I added. In one place I changed adapter to
adap. I also changed "res" to "result". I can fix those. Or I can
submit a patch first that renames the adap and adapter to make the usage
consistent (I would prefer adapter).
> - coding style changes (improper ones at that)
>
>
I don't see that. The ugliest thing about this is the functions that
take the massive numbers of parameters, but that goes away in the next
patch which puts all the data into a single data structure and passes it
around. The code here was also very inconsistent about use of spaces,
like x(a,b,c) vs x(a, b, c), "struct a *b" vs "struct a * b", "a=b" vs
"a = b". It's hard to know what was right. The changes I made in these
respects was to try to make it use the usage most common in the file.
If you like, I can do a pass and make everything consistent in the file
as part of the previous patch I talked about.
> - logic changes.
>
>
I tried very hard not to make logic changes. Now I see there were two
places where the function checked client->adapter->algo->master_xfer
then called i2c_transfer(), which did the same check and returned the
same error if it was NULL. I removed the redundant check. That belongs
in a separate patch. I couldn't find any others.
>What exactly are you doing with this patch, and why?
>
>
The i2c main functions do the following:
Format the data for transmission
Send the data to the next layer down for handling
Clean up the results
The original code did all this in single big functions. This patch
breaks the formatting and cleanup operations into separate functions.
Beyond one big function being ugly, the non-blocking code needs this
because it needs to perform these separately. When you start the
operation, the non-blocking code needs to do the format then return.
Later on, when the operation is complete, the thread of execution
handling the completion will do the cleanup.
-Corey
^ permalink raw reply [flat|nested] 18+ messages in thread
* [PATCH] Add a non-blocking interface to the I2C code, part 1
@ 2005-05-19 6:25 ` Corey Minyard
0 siblings, 0 replies; 18+ messages in thread
From: Corey Minyard @ 2005-05-19 6:25 UTC (permalink / raw)
To: Greg KH; +Cc: lkml, Sensors
Greg KH wrote:
>On Wed, Mar 02, 2005 at 01:58:51PM -0600, Corey Minyard wrote:
>
>
>>This patch reorganizes the I2C SMBus formatting code to make it more
>>suitable for the upcoming non-blocking changes.
>>
>>
>
>You are changing too much stuff here to claim it's just a
>reorganization:
> - variable name changes for no reason
>
>
Well, yes. Both "adap" and "adapter" are used to refer to the same
thing in the file; "adap" seemed to be the most common usage so I chose
that for the new functions I added. In one place I changed adapter to
adap. I also changed "res" to "result". I can fix those. Or I can
submit a patch first that renames the adap and adapter to make the usage
consistent (I would prefer adapter).
> - coding style changes (improper ones at that)
>
>
I don't see that. The ugliest thing about this is the functions that
take the massive numbers of parameters, but that goes away in the next
patch which puts all the data into a single data structure and passes it
around. The code here was also very inconsistent about use of spaces,
like x(a,b,c) vs x(a, b, c), "struct a *b" vs "struct a * b", "a=b" vs
"a = b". It's hard to know what was right. The changes I made in these
respects was to try to make it use the usage most common in the file.
If you like, I can do a pass and make everything consistent in the file
as part of the previous patch I talked about.
> - logic changes.
>
>
I tried very hard not to make logic changes. Now I see there were two
places where the function checked client->adapter->algo->master_xfer
then called i2c_transfer(), which did the same check and returned the
same error if it was NULL. I removed the redundant check. That belongs
in a separate patch. I couldn't find any others.
>What exactly are you doing with this patch, and why?
>
>
The i2c main functions do the following:
Format the data for transmission
Send the data to the next layer down for handling
Clean up the results
The original code did all this in single big functions. This patch
breaks the formatting and cleanup operations into separate functions.
Beyond one big function being ugly, the non-blocking code needs this
because it needs to perform these separately. When you start the
operation, the non-blocking code needs to do the format then return.
Later on, when the operation is complete, the thread of execution
handling the completion will do the cleanup.
-Corey
^ permalink raw reply [flat|nested] 18+ messages in thread
end of thread, other threads:[~2005-05-19 6:25 UTC | newest]
Thread overview: 18+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2005-03-02 20:02 [PATCH] Add a non-blocking interface to the I2C code, part 4 Corey Minyard
2005-05-19 6:25 ` Corey Minyard
-- strict thread matches above, loose matches on Subject: below --
2005-03-02 20:04 [PATCH] Add a non-blocking interface to the I2C code, part 7 Corey Minyard
2005-05-19 6:25 ` Corey Minyard
2005-03-02 20:04 [PATCH] Add a non-blocking interface to the I2C code, part 6 Corey Minyard
2005-05-19 6:25 ` Corey Minyard
2005-03-02 20:02 [PATCH] Add a non-blocking interface to the I2C code, part 5 Corey Minyard
2005-05-19 6:25 ` Corey Minyard
2005-03-02 20:01 [PATCH] Add a non-blocking interface to the I2C code, part3 Corey Minyard
2005-05-19 6:25 ` Corey Minyard
2005-03-02 20:00 [PATCH] Add a non-blocking interface to the I2C code, part 2 Corey Minyard
2005-05-19 6:25 ` Corey Minyard
2005-03-02 19:58 [PATCH] Add a non-blocking interface to the I2C code, part 1 Corey Minyard
2005-05-19 6:25 ` Corey Minyard
2005-03-17 7:09 ` Greg KH
2005-05-19 6:25 ` Greg KH
2005-03-18 16:13 ` Corey Minyard
2005-05-19 6:25 ` Corey Minyard
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.