mirror of
				git://git.openwrt.org/openwrt/openwrt.git
				synced 2025-11-04 06:54:27 -05:00 
			
		
		
		
	
		
			
				
	
	
		
			383 lines
		
	
	
		
			13 KiB
		
	
	
	
		
			Diff
		
	
	
	
	
	
			
		
		
	
	
			383 lines
		
	
	
		
			13 KiB
		
	
	
	
		
			Diff
		
	
	
	
	
	
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
 | 
						|
@@ -527,6 +527,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.
 | 
						|
 	 */
 | 
						|
@@ -666,6 +670,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
 | 
						|
@@ -698,33 +731,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);
 | 
						|
+
 | 
						|
 
 | 
						|
 /*-------------------------------------------------------------------------*/
 | 
						|
 
 | 
						|
@@ -738,6 +806,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
 | 
						|
@@ -761,21 +855,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
 | 
						|
@@ -262,6 +262,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
 | 
						|
@@ -542,6 +549,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);
 | 
						|
 
 | 
						|
 /*---------------------------------------------------------------------------*/
 | 
						|
 
 | 
						|
@@ -551,6 +560,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
 |