* [PATCH] I2C patch 3 - Add handling for I2C operation queue entries
@ 2005-02-24 23:29 Corey Minyard
0 siblings, 0 replies; only message in thread
From: Corey Minyard @ 2005-02-24 23:29 UTC (permalink / raw)
To: Greg KH; +Cc: Andrew Morton, lkml
[-- Attachment #1: Type: text/plain, Size: 128 bytes --]
This is one in a series of patches for adding a non-blocking interface
to the I2C driver for supporting the IPMI SMBus driver.
[-- Attachment #2: i2c_opq_handling.diff --]
[-- Type: text/plain, Size: 5984 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-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
@@ -54,6 +54,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,
@@ -134,6 +136,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);
@@ -1334,6 +1338,100 @@
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 {
+ int val = atomic_add_return(1, &entry->usecount);
+
+ /* This is subtle. If we increment the
+ usecount and the value is 1, that means it
+ was zero before. This means the put()
+ routine has decremented the value to zero
+ and is between tha decrement and the
+ spinlock. In this case, return NULL
+ because the current list head is complete
+ and the next list item is not yet
+ started. */
+ if (val == 1) {
+ atomic_dec(&entry->usecount);
+ entry = NULL;
+ }
+ }
+ }
+ pr_debug("_i2c_entry_get %p %p\n", adap, entry);
+ return entry;
+}
+
+struct i2c_op_q_entry *i2c_entry_get(struct i2c_adapter * adap)
+{
+ unsigned long flags;
+ struct i2c_op_q_entry * entry;
+
+ spin_lock_irqsave(&adap->q_lock, flags);
+ entry = _i2c_entry_get(adap);
+ spin_unlock_irqrestore(&adap->q_lock, flags);
+ return entry;
+}
+
+void i2c_entry_put(struct i2c_adapter * adap,
+ struct i2c_op_q_entry * entry)
+{
+ unsigned long flags;
+ struct i2c_op_q_entry * new_entry = NULL;
+
+ restart:
+ pr_debug("i2c_put %p %p\n", adap, entry);
+ /* Subtle reasons why we don't need a lock before the dec, see
+ the get routine for more details. */
+ if (atomic_dec_and_test(&entry->usecount)) {
+ spin_lock_irqsave(&adap->q_lock, flags);
+ list_del(&entry->link);
+
+ /* Get the next entry to start. */
+ new_entry = _i2c_entry_get(adap);
+ spin_unlock_irqrestore(&adap->q_lock, flags);
+
+ entry->handler(entry);
+
+ if (new_entry) {
+ /* start entry will go here. */
+ if (new_entry->start)
+ complete(new_entry->start);
+ /* Do tail recursion ourself. */
+ entry = new_entry;
+ goto restart;
+ }
+ }
+}
+
+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);
@@ -1345,6 +1443,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-rc4/include/linux/i2c.h
===================================================================
--- linux-2.6.11-rc4.orig/include/linux/i2c.h
+++ linux-2.6.11-rc4/include/linux/i2c.h
@@ -33,6 +33,8 @@
#include <linux/i2c-id.h>
#include <linux/device.h> /* for struct device */
#include <linux/completion.h>
+#include <linux/list.h>
+#include <linux/spinlock.h>
#include <asm/semaphore.h>
#include <asm/atomic.h>
@@ -184,6 +186,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
@@ -231,6 +260,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] only message in thread
only message in thread, other threads:[~2005-02-24 23:34 UTC | newest]
Thread overview: (only message) (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2005-02-24 23:29 [PATCH] I2C patch 3 - Add handling for I2C operation queue entries 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.