mirror of
				git://git.openwrt.org/openwrt/openwrt.git
				synced 2025-10-30 21:44:27 -04: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
 | |
| @@ -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
 |