Messages in this thread Patch in this message | | | Date | Thu, 24 Feb 2005 17:31:13 -0600 | From | Corey Minyard <> | Subject | [PATCH] I2C patch 4 - Add a timer to the I2C core |
| |
This is one in a series of patches for adding a non-blocking interface to the I2C driver for supporting the IPMI SMBus driver. 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 */ | |