The Linux Kernel Mailing List
 help / color / mirror / Atom feed
* [PATCH] I2C patch 4 - Add a timer to the I2C core
@ 2005-02-24 23:31 Corey Minyard
  0 siblings, 0 replies; only message in thread
From: Corey Minyard @ 2005-02-24 23:31 UTC (permalink / raw)
  To: Greg KH, 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_add_timer.diff --]
[-- Type: text/plain, Size: 6196 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-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
@@ -33,6 +33,12 @@
 #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);
@@ -143,6 +149,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.
@@ -185,6 +204,7 @@
 	struct i2c_driver *driver;
 	struct i2c_client *client;
 	int res = 0;
+	unsigned long flags;
 
 	down(&core_lists);
 
@@ -237,6 +257,17 @@
 	device_unregister(&adap->dev);
 	list_del(&adap->list);
 
+	/* Stop the timer and free its memory */
+	spin_lock_irqsave(&adap->timer->lock, flags);
+	if (i2c_stop_timer(adap)) {
+		spin_unlock_irqrestore(&adap->timer->lock, flags);
+		kfree(adap->timer);
+	} else {
+		adap->timer->deleted = 1;
+		spin_unlock_irqrestore(&adap->timer->lock, flags);
+	}
+	adap->timer = NULL;
+
 	/* wait for sysfs to drop all references */
 	wait_for_completion(&adap->dev_released);
 	wait_for_completion(&adap->class_dev_released);
@@ -583,6 +614,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.
  * ----------------------------------------------------
  */
@@ -1425,6 +1533,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-rc4/include/linux/i2c.h
===================================================================
--- linux-2.6.11-rc4.orig/include/linux/i2c.h
+++ linux-2.6.11-rc4/include/linux/i2c.h
@@ -35,6 +35,7 @@
 #include <linux/completion.h>
 #include <linux/list.h>
 #include <linux/spinlock.h>
+#include <linux/timer.h>
 #include <asm/semaphore.h>
 #include <asm/atomic.h>
 
@@ -244,6 +245,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.
  */
@@ -265,6 +281,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] only message in thread

only message in thread, other threads:[~2005-02-24 23:40 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:31 [PATCH] I2C patch 4 - Add a timer to the I2C core Corey Minyard

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox