diff options
author | juhosg <juhosg@3c298f89-4303-0410-b956-a3cf2f4a3e73> | 2012-05-05 11:43:34 +0000 |
---|---|---|
committer | juhosg <juhosg@3c298f89-4303-0410-b956-a3cf2f4a3e73> | 2012-05-05 11:43:34 +0000 |
commit | a6a62009fb32d1363fcb02402f0e400360287591 (patch) | |
tree | 64eadb3d1c8cc4f6fe4cb32dab5dd57935e70e09 /target/linux/generic/patches-2.6.32/910-backport-spi-bus-locking-api.patch | |
parent | e3bebfe1fbdc9582045b24d51b45214bd835f983 (diff) |
linux/2.6.32: R.I.P.
git-svn-id: svn://svn.openwrt.org/openwrt/trunk@31597 3c298f89-4303-0410-b956-a3cf2f4a3e73
Diffstat (limited to 'target/linux/generic/patches-2.6.32/910-backport-spi-bus-locking-api.patch')
-rw-r--r-- | target/linux/generic/patches-2.6.32/910-backport-spi-bus-locking-api.patch | 382 |
1 files changed, 0 insertions, 382 deletions
diff --git a/target/linux/generic/patches-2.6.32/910-backport-spi-bus-locking-api.patch b/target/linux/generic/patches-2.6.32/910-backport-spi-bus-locking-api.patch deleted file mode 100644 index 23becab917..0000000000 --- a/target/linux/generic/patches-2.6.32/910-backport-spi-bus-locking-api.patch +++ /dev/null @@ -1,382 +0,0 @@ -From cf32b71e981ca63e8f349d8585ca2a3583b556e0 Mon Sep 17 00:00:00 2001 -From: Ernst Schwab <eschwab@online.de> -Date: Mon, 28 Jun 2010 17:49:29 -0700 -Subject: [PATCH] spi/mmc_spi: SPI bus locking API, using mutex - -SPI bus locking API to allow exclusive access to the SPI bus, especially, but -not limited to, for the mmc_spi driver. - -Coded according to an outline from Grant Likely; here is his -specification (accidentally swapped function names corrected): - -It requires 3 things to be added to struct spi_master. -- 1 Mutex -- 1 spin lock -- 1 flag. - -The mutex protects spi_sync, and provides sleeping "for free" -The spinlock protects the atomic spi_async call. -The flag is set when the lock is obtained, and checked while holding -the spinlock in spi_async(). If the flag is checked, then spi_async() -must fail immediately. - -The current runtime API looks like this: -spi_async(struct spi_device*, struct spi_message*); -spi_sync(struct spi_device*, struct spi_message*); - -The API needs to be extended to this: -spi_async(struct spi_device*, struct spi_message*) -spi_sync(struct spi_device*, struct spi_message*) -spi_bus_lock(struct spi_master*) /* although struct spi_device* might -be easier */ -spi_bus_unlock(struct spi_master*) -spi_async_locked(struct spi_device*, struct spi_message*) -spi_sync_locked(struct spi_device*, struct spi_message*) - -Drivers can only call the last two if they already hold the spi_master_lock(). - -spi_bus_lock() obtains the mutex, obtains the spin lock, sets the -flag, and releases the spin lock before returning. It doesn't even -need to sleep while waiting for "in-flight" spi_transactions to -complete because its purpose is to guarantee no additional -transactions are added. It does not guarantee that the bus is idle. - -spi_bus_unlock() clears the flag and releases the mutex, which will -wake up any waiters. - -The difference between spi_async() and spi_async_locked() is that the -locked version bypasses the check of the lock flag. Both versions -need to obtain the spinlock. - -The difference between spi_sync() and spi_sync_locked() is that -spi_sync() must hold the mutex while enqueuing a new transfer. -spi_sync_locked() doesn't because the mutex is already held. Note -however that spi_sync must *not* continue to hold the mutex while -waiting for the transfer to complete, otherwise only one transfer -could be queued up at a time! - -Almost no code needs to be written. The current spi_async() and -spi_sync() can probably be renamed to __spi_async() and __spi_sync() -so that spi_async(), spi_sync(), spi_async_locked() and -spi_sync_locked() can just become wrappers around the common code. - -spi_sync() is protected by a mutex because it can sleep -spi_async() needs to be protected with a flag and a spinlock because -it can be called atomically and must not sleep - -Signed-off-by: Ernst Schwab <eschwab@online.de> -[grant.likely@secretlab.ca: use spin_lock_irqsave()] -Signed-off-by: Grant Likely <grant.likely@secretlab.ca> -Tested-by: Matt Fleming <matt@console-pimps.org> -Tested-by: Antonio Ospite <ospite@studenti.unina.it> ---- - drivers/spi/spi.c | 225 ++++++++++++++++++++++++++++++++++++++++------- - include/linux/spi/spi.h | 12 +++ - 2 files changed, 204 insertions(+), 33 deletions(-) - ---- a/drivers/spi/spi.c -+++ b/drivers/spi/spi.c -@@ -524,6 +524,10 @@ int spi_register_master(struct spi_maste - dynamic = 1; - } - -+ spin_lock_init(&master->bus_lock_spinlock); -+ mutex_init(&master->bus_lock_mutex); -+ master->bus_lock_flag = 0; -+ - /* register the device, then userspace will see it. - * registration fails if the bus ID is in use. - */ -@@ -663,6 +667,35 @@ int spi_setup(struct spi_device *spi) - } - EXPORT_SYMBOL_GPL(spi_setup); - -+static int __spi_async(struct spi_device *spi, struct spi_message *message) -+{ -+ struct spi_master *master = spi->master; -+ -+ /* Half-duplex links include original MicroWire, and ones with -+ * only one data pin like SPI_3WIRE (switches direction) or where -+ * either MOSI or MISO is missing. They can also be caused by -+ * software limitations. -+ */ -+ if ((master->flags & SPI_MASTER_HALF_DUPLEX) -+ || (spi->mode & SPI_3WIRE)) { -+ struct spi_transfer *xfer; -+ unsigned flags = master->flags; -+ -+ list_for_each_entry(xfer, &message->transfers, transfer_list) { -+ if (xfer->rx_buf && xfer->tx_buf) -+ return -EINVAL; -+ if ((flags & SPI_MASTER_NO_TX) && xfer->tx_buf) -+ return -EINVAL; -+ if ((flags & SPI_MASTER_NO_RX) && xfer->rx_buf) -+ return -EINVAL; -+ } -+ } -+ -+ message->spi = spi; -+ message->status = -EINPROGRESS; -+ return master->transfer(spi, message); -+} -+ - /** - * spi_async - asynchronous SPI transfer - * @spi: device with which data will be exchanged -@@ -695,33 +728,68 @@ EXPORT_SYMBOL_GPL(spi_setup); - int spi_async(struct spi_device *spi, struct spi_message *message) - { - struct spi_master *master = spi->master; -+ int ret; -+ unsigned long flags; - -- /* Half-duplex links include original MicroWire, and ones with -- * only one data pin like SPI_3WIRE (switches direction) or where -- * either MOSI or MISO is missing. They can also be caused by -- * software limitations. -- */ -- if ((master->flags & SPI_MASTER_HALF_DUPLEX) -- || (spi->mode & SPI_3WIRE)) { -- struct spi_transfer *xfer; -- unsigned flags = master->flags; -+ spin_lock_irqsave(&master->bus_lock_spinlock, flags); - -- list_for_each_entry(xfer, &message->transfers, transfer_list) { -- if (xfer->rx_buf && xfer->tx_buf) -- return -EINVAL; -- if ((flags & SPI_MASTER_NO_TX) && xfer->tx_buf) -- return -EINVAL; -- if ((flags & SPI_MASTER_NO_RX) && xfer->rx_buf) -- return -EINVAL; -- } -- } -+ if (master->bus_lock_flag) -+ ret = -EBUSY; -+ else -+ ret = __spi_async(spi, message); - -- message->spi = spi; -- message->status = -EINPROGRESS; -- return master->transfer(spi, message); -+ spin_unlock_irqrestore(&master->bus_lock_spinlock, flags); -+ -+ return ret; - } - EXPORT_SYMBOL_GPL(spi_async); - -+/** -+ * spi_async_locked - version of spi_async with exclusive bus usage -+ * @spi: device with which data will be exchanged -+ * @message: describes the data transfers, including completion callback -+ * Context: any (irqs may be blocked, etc) -+ * -+ * This call may be used in_irq and other contexts which can't sleep, -+ * as well as from task contexts which can sleep. -+ * -+ * The completion callback is invoked in a context which can't sleep. -+ * Before that invocation, the value of message->status is undefined. -+ * When the callback is issued, message->status holds either zero (to -+ * indicate complete success) or a negative error code. After that -+ * callback returns, the driver which issued the transfer request may -+ * deallocate the associated memory; it's no longer in use by any SPI -+ * core or controller driver code. -+ * -+ * Note that although all messages to a spi_device are handled in -+ * FIFO order, messages may go to different devices in other orders. -+ * Some device might be higher priority, or have various "hard" access -+ * time requirements, for example. -+ * -+ * On detection of any fault during the transfer, processing of -+ * the entire message is aborted, and the device is deselected. -+ * Until returning from the associated message completion callback, -+ * no other spi_message queued to that device will be processed. -+ * (This rule applies equally to all the synchronous transfer calls, -+ * which are wrappers around this core asynchronous primitive.) -+ */ -+int spi_async_locked(struct spi_device *spi, struct spi_message *message) -+{ -+ struct spi_master *master = spi->master; -+ int ret; -+ unsigned long flags; -+ -+ spin_lock_irqsave(&master->bus_lock_spinlock, flags); -+ -+ ret = __spi_async(spi, message); -+ -+ spin_unlock_irqrestore(&master->bus_lock_spinlock, flags); -+ -+ return ret; -+ -+} -+EXPORT_SYMBOL_GPL(spi_async_locked); -+ - - /*-------------------------------------------------------------------------*/ - -@@ -735,6 +803,32 @@ static void spi_complete(void *arg) - complete(arg); - } - -+static int __spi_sync(struct spi_device *spi, struct spi_message *message, -+ int bus_locked) -+{ -+ DECLARE_COMPLETION_ONSTACK(done); -+ int status; -+ struct spi_master *master = spi->master; -+ -+ message->complete = spi_complete; -+ message->context = &done; -+ -+ if (!bus_locked) -+ mutex_lock(&master->bus_lock_mutex); -+ -+ status = spi_async_locked(spi, message); -+ -+ if (!bus_locked) -+ mutex_unlock(&master->bus_lock_mutex); -+ -+ if (status == 0) { -+ wait_for_completion(&done); -+ status = message->status; -+ } -+ message->context = NULL; -+ return status; -+} -+ - /** - * spi_sync - blocking/synchronous SPI data transfers - * @spi: device with which data will be exchanged -@@ -758,21 +852,86 @@ static void spi_complete(void *arg) - */ - int spi_sync(struct spi_device *spi, struct spi_message *message) - { -- DECLARE_COMPLETION_ONSTACK(done); -- int status; -- -- message->complete = spi_complete; -- message->context = &done; -- status = spi_async(spi, message); -- if (status == 0) { -- wait_for_completion(&done); -- status = message->status; -- } -- message->context = NULL; -- return status; -+ return __spi_sync(spi, message, 0); - } - EXPORT_SYMBOL_GPL(spi_sync); - -+/** -+ * spi_sync_locked - version of spi_sync with exclusive bus usage -+ * @spi: device with which data will be exchanged -+ * @message: describes the data transfers -+ * Context: can sleep -+ * -+ * This call may only be used from a context that may sleep. The sleep -+ * is non-interruptible, and has no timeout. Low-overhead controller -+ * drivers may DMA directly into and out of the message buffers. -+ * -+ * This call should be used by drivers that require exclusive access to the -+ * SPI bus. It has to be preceeded by a spi_bus_lock call. The SPI bus must -+ * be released by a spi_bus_unlock call when the exclusive access is over. -+ * -+ * It returns zero on success, else a negative error code. -+ */ -+int spi_sync_locked(struct spi_device *spi, struct spi_message *message) -+{ -+ return __spi_sync(spi, message, 1); -+} -+EXPORT_SYMBOL_GPL(spi_sync_locked); -+ -+/** -+ * spi_bus_lock - obtain a lock for exclusive SPI bus usage -+ * @master: SPI bus master that should be locked for exclusive bus access -+ * Context: can sleep -+ * -+ * This call may only be used from a context that may sleep. The sleep -+ * is non-interruptible, and has no timeout. -+ * -+ * This call should be used by drivers that require exclusive access to the -+ * SPI bus. The SPI bus must be released by a spi_bus_unlock call when the -+ * exclusive access is over. Data transfer must be done by spi_sync_locked -+ * and spi_async_locked calls when the SPI bus lock is held. -+ * -+ * It returns zero on success, else a negative error code. -+ */ -+int spi_bus_lock(struct spi_master *master) -+{ -+ unsigned long flags; -+ -+ mutex_lock(&master->bus_lock_mutex); -+ -+ spin_lock_irqsave(&master->bus_lock_spinlock, flags); -+ master->bus_lock_flag = 1; -+ spin_unlock_irqrestore(&master->bus_lock_spinlock, flags); -+ -+ /* mutex remains locked until spi_bus_unlock is called */ -+ -+ return 0; -+} -+EXPORT_SYMBOL_GPL(spi_bus_lock); -+ -+/** -+ * spi_bus_unlock - release the lock for exclusive SPI bus usage -+ * @master: SPI bus master that was locked for exclusive bus access -+ * Context: can sleep -+ * -+ * This call may only be used from a context that may sleep. The sleep -+ * is non-interruptible, and has no timeout. -+ * -+ * This call releases an SPI bus lock previously obtained by an spi_bus_lock -+ * call. -+ * -+ * It returns zero on success, else a negative error code. -+ */ -+int spi_bus_unlock(struct spi_master *master) -+{ -+ master->bus_lock_flag = 0; -+ -+ mutex_unlock(&master->bus_lock_mutex); -+ -+ return 0; -+} -+EXPORT_SYMBOL_GPL(spi_bus_unlock); -+ - /* portable code must never pass more than 32 bytes */ - #define SPI_BUFSIZ max(32,SMP_CACHE_BYTES) - ---- a/include/linux/spi/spi.h -+++ b/include/linux/spi/spi.h -@@ -261,6 +261,13 @@ struct spi_master { - #define SPI_MASTER_NO_RX BIT(1) /* can't do buffer read */ - #define SPI_MASTER_NO_TX BIT(2) /* can't do buffer write */ - -+ /* lock and mutex for SPI bus locking */ -+ spinlock_t bus_lock_spinlock; -+ struct mutex bus_lock_mutex; -+ -+ /* flag indicating that the SPI bus is locked for exclusive use */ -+ bool bus_lock_flag; -+ - /* Setup mode and clock, etc (spi driver may call many times). - * - * IMPORTANT: this may be called when transfers to another -@@ -541,6 +548,8 @@ static inline void spi_message_free(stru - - extern int spi_setup(struct spi_device *spi); - extern int spi_async(struct spi_device *spi, struct spi_message *message); -+extern int spi_async_locked(struct spi_device *spi, -+ struct spi_message *message); - - /*---------------------------------------------------------------------------*/ - -@@ -550,6 +559,9 @@ extern int spi_async(struct spi_device * - */ - - extern int spi_sync(struct spi_device *spi, struct spi_message *message); -+extern int spi_sync_locked(struct spi_device *spi, struct spi_message *message); -+extern int spi_bus_lock(struct spi_master *master); -+extern int spi_bus_unlock(struct spi_master *master); - - /** - * spi_write - SPI synchronous write |